123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607(******************************************************************************)(* OASIS: architecture for building OCaml libraries and applications *)(* *)(* Copyright (C) 2011-2016, Sylvain Le Gall *)(* Copyright (C) 2008-2011, OCamlCore SARL *)(* *)(* This library is free software; you can redistribute it and/or modify it *)(* under the terms of the GNU Lesser General Public License as published by *)(* the Free Software Foundation; either version 2.1 of the License, or (at *)(* your option) any later version, with the OCaml static compilation *)(* exception. *)(* *)(* This library is distributed in the hope that it will be useful, but *)(* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY *)(* or FITNESS FOR A PARTICULAR PURPOSE. See the file COPYING for more *)(* details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this library; if not, write to the Free Software Foundation, *)(* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *)(******************************************************************************)(** Install using internal scheme
@author Sylvain Le Gall
*)(* TODO: rewrite this module with OASISFileSystem. *)openBaseEnvopenBaseStandardVaropenBaseMessageopenOASISTypesopenOASISFindlibopenOASISGettextopenOASISUtilsletexec_hook=ref(fun(cs,bs,exec)->cs,bs,exec)letlib_hook=ref(fun(cs,bs,dn,lib)->cs,bs,dn,lib,[])letobj_hook=ref(fun(cs,bs,dn,obj)->cs,bs,dn,obj,[])letdoc_hook=ref(fun(cs,doc)->cs,doc)letinstall_file_ev="install-file"letinstall_dir_ev="install-dir"letinstall_findlib_ev="install-findlib"(* TODO: this can be more generic and used elsewhere. *)letwin32_max_command_line_length=8000letsplit_install_commandocamlfindfindlib_namemetafiles=ifSys.os_type="Win32"then(* Arguments for the first command: *)letfirst_args=["install";findlib_name;meta]in(* Arguments for remaining commands: *)letother_args=["install";findlib_name;"-add"]in(* Extract as much files as possible from [files], [len] is
the current command line length: *)letrecget_fileslenaccfiles=matchfileswith|[]->(List.revacc,[])|file::rest->letlen=len+1+String.lengthfileiniflen>win32_max_command_line_lengththen(List.revacc,files)elseget_fileslen(file::acc)restin(* Split the command into several commands. *)letrecsplitargsfiles=matchfileswith|[]->[]|_->(* Length of "ocamlfind install <lib> [META|-add]" *)letlen=List.fold_left(funlenarg->len+1(* for the space *)+String.lengtharg)(String.lengthocamlfind)argsinmatchget_fileslen[]fileswith|([],_)->failwith(s_"Command line too long.")|(firsts,others)->letcmd=args@firstsin(* Use -add for remaining commands: *)let()=letfindlib_ge_132=OASISVersion.comparator_apply(OASISVersion.version_of_string(BaseStandardVar.findlib_version()))(OASISVersion.VGreaterEqual(OASISVersion.version_of_string"1.3.2"))inifnotfindlib_ge_132thenfailwithf(f_"Installing the library %s require to use the \
flag '-add' of ocamlfind because the command \
line is too long. This flag is only available \
for findlib 1.3.2. Please upgrade findlib from \
%s to 1.3.2")findlib_name(BaseStandardVar.findlib_version())inletcmds=splitother_argsothersincmd::cmdsin(* The first command does not use -add: *)splitfirst_argsfileselse["install"::findlib_name::meta::files]letinstall=letin_destdir=tryletdestdir=destdir()in(* Practically speaking destdir is prepended
* at the beginning of the target filename
*)funfn->destdir^fnwithPropList.Not_set_->funfn->fninletinstall_file~ctxt?(prepend_destdir=true)?tgt_fnsrc_fileenvdir=lettgt_dir=ifprepend_destdirthenin_destdir(envdir())elseenvdir()inlettgt_file=Filename.concattgt_dir(matchtgt_fnwith|Somefn->fn|None->Filename.basenamesrc_file)in(* Create target directory if needed *)OASISFileUtil.mkdir_parent~ctxt(fundn->info(f_"Creating directory '%s'")dn;BaseLog.register~ctxtinstall_dir_evdn)(Filename.dirnametgt_file);(* Really install files *)info(f_"Copying file '%s' to '%s'")src_filetgt_file;OASISFileUtil.cp~ctxtsrc_filetgt_file;BaseLog.register~ctxtinstall_file_evtgt_filein(* Install the files for a library. *)letinstall_lib_files~ctxtfindlib_namefiles=letfindlib_dir=letdn=letfindlib_destdir=OASISExec.run_read_one_line~ctxt(ocamlfind())["printconf";"destdir"]inFilename.concatfindlib_destdirfindlib_nameinfun()->dninlet()=ifnot(OASISFileUtil.file_exists_case(findlib_dir()))thenfailwithf(f_"Directory '%s' doesn't exist for findlib library %s")(findlib_dir())findlib_nameinletfdirfile=letbasename=Filename.basenamefileinlettgt_fn=Filename.concatdirbasenamein(* Destdir is already include in printconf. *)install_file~ctxt~prepend_destdir:false~tgt_fnfilefindlib_dirinList.iter(fun(dir,files)->List.iter(fdir)files)files;in(* Install data into defined directory *)letinstall_data~ctxtsrcdirlsttgtdir=lettgtdir=OASISHostPath.of_unix(var_expandtgtdir)inList.iter(fun(src,tgt_opt)->letreal_srcs=OASISFileUtil.glob~ctxt:!BaseContext.default(Filename.concatsrcdirsrc)inifreal_srcs=[]thenfailwithf(f_"Wildcard '%s' doesn't match any files")src;List.iter(funfn->install_file~ctxtfn(fun()->matchtgt_optwith|Somes->OASISHostPath.of_unix(var_expands)|None->tgtdir))real_srcs)lstinletmake_fnamesmodulsufx=List.fold_rightbeginfunsufxaccu->(OASISString.capitalize_asciimodul^sufx)::(OASISString.uncapitalize_asciimodul^sufx)::accuendsufx[]in(** Install all libraries *)letinstall_libs~ctxtpkg=letfind_first_existing_files_in_pathbslst=letpath=OASISHostPath.of_unixbs.bs_pathinList.findOASISFileUtil.file_exists_case(List.map(Filename.concatpath)lst)inletfiles_of_modulesnew_filestypcsbsmodules=List.fold_left(funaccmodul->begintry(* Add uncompiled header from the source tree *)[find_first_existing_files_in_pathbs(make_fnamesmodul[".mli";".ml"])]withNot_found->warning(f_"Cannot find source header for module %s \
in %s %s")typmodulcs.cs_name;[]end@List.fold_left(funaccfn->tryfind_first_existing_files_in_pathbs[fn]::accwithNot_found->acc)acc(make_fnamesmodul[".annot";".cmti";".cmt"]))new_filesmodulesinletfiles_of_build_section(f_data,new_files)typcsbs=letextra_files=List.map(funfn->tryfind_first_existing_files_in_pathbs[fn]withNot_found->failwithf(f_"Cannot find extra findlib file %S in %s %s ")fntypcs.cs_name)bs.bs_findlib_extra_filesinletf_data()=(* Install data associated with the library *)install_data~ctxtbs.bs_pathbs.bs_data_files(Filename.concat(datarootdir())pkg.name);f_data()inf_data,new_files@extra_filesinletfiles_of_library(f_data,acc)data_lib=letcs,bs,lib,dn,lib_extra=!lib_hookdata_libinifvar_choosebs.bs_install&&BaseBuilt.is_built~ctxtBaseBuilt.BLibcs.cs_namethenbegin(* Start with lib_extra *)letnew_files=lib_extrainletnew_files=files_of_modulesnew_files"library"csbslib.lib_modulesinletf_data,new_files=files_of_build_section(f_data,new_files)"library"csbsinletnew_files=(* Get generated files *)BaseBuilt.fold~ctxtBaseBuilt.BLibcs.cs_name(funaccfn->fn::acc)new_filesinletacc=(dn,new_files)::accinletf_data()=(* Install data associated with the library *)install_data~ctxtbs.bs_pathbs.bs_data_files(Filename.concat(datarootdir())pkg.name);f_data()in(f_data,acc)endelsebegin(f_data,acc)endandfiles_of_object(f_data,acc)data_obj=letcs,bs,obj,dn,obj_extra=!obj_hookdata_objinifvar_choosebs.bs_install&&BaseBuilt.is_built~ctxtBaseBuilt.BObjcs.cs_namethenbegin(* Start with obj_extra *)letnew_files=obj_extrainletnew_files=files_of_modulesnew_files"object"csbsobj.obj_modulesinletf_data,new_files=files_of_build_section(f_data,new_files)"object"csbsinletnew_files=(* Get generated files *)BaseBuilt.fold~ctxtBaseBuilt.BObjcs.cs_name(funaccfn->fn::acc)new_filesinletacc=(dn,new_files)::accinletf_data()=(* Install data associated with the object *)install_data~ctxtbs.bs_pathbs.bs_data_files(Filename.concat(datarootdir())pkg.name);f_data()in(f_data,acc)endelsebegin(f_data,acc)endin(* Install one group of library *)letinstall_group_libgrp=(* Iterate through all group nodes *)letrecinstall_group_lib_auxdata_and_filesgrp=letdata_and_files,children=matchgrpwith|Container(_,children)->data_and_files,children|Package(_,cs,bs,`Librarylib,dn,children)->files_of_librarydata_and_files(cs,bs,lib,dn),children|Package(_,cs,bs,`Objectobj,dn,children)->files_of_objectdata_and_files(cs,bs,obj,dn),childreninList.fold_leftinstall_group_lib_auxdata_and_fileschildrenin(* Findlib name of the root library *)letfindlib_name=findlib_of_groupgrpin(* Determine root library *)letroot_lib=root_of_groupgrpin(* All files to install for this library *)letf_data,files=install_group_lib_aux(ignore,[])grpin(* Really install, if there is something to install *)iffiles=[]thenbeginwarning(f_"Nothing to install for findlib library '%s'")findlib_nameendelsebeginletmeta=(* Search META file *)let_,bs,_=root_libinletres=Filename.concatbs.bs_path"META"inifnot(OASISFileUtil.file_exists_caseres)thenfailwithf(f_"Cannot find file '%s' for findlib library %s")resfindlib_name;resinletfiles=(* Make filename shorter to avoid hitting command max line length
* too early, esp. on Windows.
*)(* TODO: move to OASISHostPath as make_relative. *)letremove_prefixpn=letplen=String.lengthpinletnlen=String.lengthninifplen<=nlen&&String.subn0plen=pthenbeginletfn_sep=ifSys.os_type="Win32"then'\\'else'/'inletcutpoint=plen+(ifplen<nlen&&n.[plen]=fn_septhen1else0)inString.subncutpoint(nlen-cutpoint)endelsebeginnendinList.map(fun(dir,fn)->(dir,List.map(remove_prefix(Sys.getcwd()))fn))filesinletocamlfind=ocamlfind()inletnodir_files,dir_files=List.fold_left(fun(nodir,dir)(dn,lst)->matchdnwith|Somedn->nodir,(dn,lst)::dir|None->lst@nodir,dir)([],[])(List.revfiles)ininfo(f_"Installing findlib library '%s'")findlib_name;List.iter(OASISExec.run~ctxtocamlfind)(split_install_commandocamlfindfindlib_namemetanodir_files);install_lib_files~ctxtfindlib_namedir_files;BaseLog.register~ctxtinstall_findlib_evfindlib_nameend;(* Install data files *)f_data();inletgroup_libs,_,_=findlib_mappingpkgin(* We install libraries in groups *)List.iterinstall_group_libgroup_libsinletinstall_execs~ctxtpkg=letinstall_execdata_exec=letcs,bs,_=!exec_hookdata_execinifvar_choosebs.bs_install&&BaseBuilt.is_built~ctxtBaseBuilt.BExeccs.cs_namethenbeginletexec_libdir()=Filename.concat(libdir())pkg.nameinBaseBuilt.fold~ctxtBaseBuilt.BExeccs.cs_name(fun()fn->install_file~ctxt~tgt_fn:(cs.cs_name^ext_program())fnbindir)();BaseBuilt.fold~ctxtBaseBuilt.BExecLibcs.cs_name(fun()fn->install_file~ctxtfnexec_libdir)();install_data~ctxtbs.bs_pathbs.bs_data_files(Filename.concat(datarootdir())pkg.name)endinList.iter(function|Executable(cs,bs,exec)->install_exec(cs,bs,exec)|_->())pkg.sectionsinletinstall_docs~ctxtpkg=letinstall_docdata=letcs,doc=!doc_hookdatainifvar_choosedoc.doc_install&&BaseBuilt.is_built~ctxtBaseBuilt.BDoccs.cs_namethenbeginlettgt_dir=OASISHostPath.of_unix(var_expanddoc.doc_install_dir)inBaseBuilt.fold~ctxtBaseBuilt.BDoccs.cs_name(fun()fn->install_file~ctxtfn(fun()->tgt_dir))();install_data~ctxtFilename.current_dir_namedoc.doc_data_filesdoc.doc_install_direndinList.iter(function|Doc(cs,doc)->install_doc(cs,doc)|_->())pkg.sectionsinfun~ctxtpkg_->install_libs~ctxtpkg;install_execs~ctxtpkg;install_docs~ctxtpkg(* Uninstall already installed data *)letuninstall~ctxt__=letuninstall_aux(ev,data)=ifev=install_file_evthenbeginifOASISFileUtil.file_exists_casedatathenbegininfo(f_"Removing file '%s'")data;Sys.removedataendelsebeginwarning(f_"File '%s' doesn't exist anymore")dataendendelseifev=install_dir_evthenbeginifSys.file_existsdata&&Sys.is_directorydatathenbeginifSys.readdirdata=[||]thenbegininfo(f_"Removing directory '%s'")data;OASISFileUtil.rmdir~ctxtdataendelsebeginwarning(f_"Directory '%s' is not empty (%s)")data(String.concat", "(Array.to_list(Sys.readdirdata)))endendelsebeginwarning(f_"Directory '%s' doesn't exist anymore")dataendendelseifev=install_findlib_evthenbegininfo(f_"Removing findlib library '%s'")data;OASISExec.run~ctxt(ocamlfind())["remove";data]endelsebeginfailwithf(f_"Unknown log event '%s'")ev;end;BaseLog.unregister~ctxtevdatain(* We process event in reverse order *)List.iteruninstall_aux(List.rev(BaseLog.filter~ctxt[install_file_ev;install_dir_ev]));List.iteruninstall_aux(List.rev(BaseLog.filter~ctxt[install_findlib_ev]))(* END EXPORT *)openOASISPluginopenInternalIdletplugin=`Install,name,Someversionletinit()=letself_id,_=Install.createpluginin(* Installation *)letdoit_installplugin_ctxt_=plugin_ctxt,{chng_moduls=[InternalData.internalsys_ml];chng_main=OASISDataNotation.funcinstall"InternalInstallPlugin.install";chng_clean=None;chng_distclean=None;}in(* Uninstall *)letdoit_uninstallplugin_ctxt_=plugin_ctxt,{chng_moduls=[InternalData.internalsys_ml];chng_main=OASISDataNotation.funcuninstall"InternalInstallPlugin.uninstall";chng_clean=None;chng_distclean=None;}inInternalId.init();Install.register_actself_id(doit_install,doit_uninstall)