123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990(** Find and read lambdapi.pkg files. *)openLplibopenExtraopenCommonopenError(** A package configuration file is expected at the root of every package. The
file is used to figure out the module path under which the package must be
placed. This information is also useful for installation. *)(** Pacage configuration file name. *)letpkg_file:string="lambdapi.pkg"(** Configuration file format (using an example).
==== lambdapi.pkg ============
# only two required fields:
package_name = my_package
root_path = contrib.my_pack
# comments not at end of line
undefined = ignored
==============================
*)(** Configuration data read from a file. *)typeconfig_data={package_name:string;root_path:Path.t}(** [read fname] reads configuration data from the file [fname]. The exception
[Fatal] is raised in case of error (non-existing file, bad format). *)letread:string->config_data=funfname->(* Obtaining file lines. *)letlines=letic=tryopen_infnamewithSys_error(_)->fatal_no_pos"Package file [%s] does not exist."fnameinletlines=input_linesicinclose_inic;linesin(* Build a dictionary from the lines. *)lethandle_linedictl=(* Spaces at the begining and end of line are ignored. *)letl=String.trimlin(* Empty lines and comments (lines starting with ['#']) are ignored. *)ifString.lengthl=0||l.[0]='#'thendictelse(* Get key and value (separated by ['=']). *)matchString.split_on_char'='lwith|[k;v]->(String.trimk,String.trimv)::dict|_->fatal_no_pos"Ill-formed package file [%s]."fnameinletdict=List.fold_lefthandle_line[]linesin(* Getting a value given a key. *)letgetk=tryList.assockdictwithNot_found->fatal_no_pos"Ill-formed package file [%s]: missing field [%s]."fnamekin(* Building the configuration. *)letpackage_name=get"package_name"inletroot_path=Parser.path_of_string(get"root_path")in{package_name;root_path}(** [find_config fname] looks for a configuration file above [fname], which is
typically a source file or an object file (it can also be a directory). If
there is no configuration file in the same directory as [fname], then we
look in the parent directory and so on, up to the root or as long as no
[Sys_error] is raised. Note that [fname] is first normalized with a call
to [Filename.realpath]. *)letfind_config:string->stringoption=funfname->letfname=Filename.normalizefnameinletrecfinddir=letfile=Filename.concatdirpkg_fileinmatchSys.file_existsfilewith|true->Some(file)|false->ifdir="/"thenNoneelsefind(Filename.dirnamedir)|exceptionSys_error(_)->Noneinfindfname(** [apply_config fname] attempts to find a configuration file from the
directory or file [fname], and applies the corresponding configuration. *)letapply_config:string->unit=funfname->matchfind_configfnamewith|None->()|Some(cfg_file)->let{root_path;_}=readcfg_fileinletroot=Filename.dirnamecfg_fileinLibrary.add_mapping(root_path,root)