Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file dkml_package_console_create.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388(* Cmdliner 1.0 -> 1.1 deprecated a lot of things. But until Cmdliner 1.1
is in common use in Opam packages we should provide backwards compatibility.
In fact, Diskuv OCaml is not even using Cmdliner 1.1. *)[@@@alert"-deprecated"]openBosopenDkml_install_runner.Error_handling.Monad_syntaxmoduleArg=Cmdliner.ArgmoduleTerm=Cmdliner.Termletgenerate_installer_from_archive_dir~install_direction~archive_dir~work_dir~abi_selector~organization~program_name~program_version~target_dir=(* For Windows create a self-extracting executable.
Since 7zr.exe is used to create a .7z archive, we can only run this on
Windows today.
See CROSSPLATFORM-TODO.md *)letuninstallers=refNonein(ifSys.win32thenmatchabi_selectorwith|Dkml_install_runner.Path_location.AbiabiwhenDkml_install_api.Context.Abi_v2.is_windowsabi->Logs.debug(funl->l"Generating self-extracting executable (SFX)");letinstaller_path=Installer_sfx.generate~install_direction~archive_dir~target_dir~abi_selector~organization~program_name~program_version~work_dirinifinstall_direction=Dkml_install_runner.Path_eval.Global_context.Uninstallthenuninstallers:=Someinstaller_path|_->());(* All operating systems can have an archive *)Logs.debug(funl->l"Generating tar.gz capable archive");let*(),_fl=Installer_archive.generate~install_direction~archive_dir~target_dir~abi_selector~program_name~program_versioninreturn!uninstallersletcreate_forone_abi~abi_selector~install_component_names~uninstall_component_names~organization~program_name~program_version~program_info~opam_context~work_dir~target_dir~runner_admin_exe~runner_user_exe~packager_install_exe~packager_uninstall_exe~packager_setup_bytecode~packager_uninstaller_bytecode=letabi=Dkml_install_runner.Path_location.show_abi_selectorabi_selectorin(* Get Opam sources *)let*opam_staging_files_source,_fl=Dkml_install_runner.Path_location.staging_files_source~staging_default:No_staging_default~opam_context_opt:(Someopam_context)~staging_files_opt:Noneinlet*opam_static_files_source,_fl=Dkml_install_runner.Path_location.static_files_source~static_default:No_static_default~opam_context_opt:(Someopam_context)~static_files_opt:Noneinletcreate_installerinstall_directionarchive_dircomponent_namespackager_entry_exepackager_bytecode=(* Create a temporary archive directory where we'll build the installer.contents
For the benefit of Windows and macOS we keep the directory name ("a") small. *)letarchive_staging_dir=Dkml_install_runner.Cmdliner_runner.staging_default_dir_for_package~archive_dirinletarchive_static_dir=Dkml_install_runner.Cmdliner_runner.static_default_dir_for_package~archive_dirin(* Copy non-component files into archive *)Logs.debug(funl->l"Copying non-component files into archive tree");Populate_archive.populate_archive~archive_dir~abi_selector~runner_admin_exe~runner_user_exe~packager_entry_exe~packager_bytecode;(* Get archive destinations.
The destinations are nothing more than a *_files_source which
allows us to use the same code and context paths that the end-user
machine will use.
*)let*archive_staging_files_dest,_fl=Dkml_install_runner.Path_location.staging_files_source~staging_default:No_staging_default~opam_context_opt:None~staging_files_opt:(Some(Fpath.to_stringarchive_staging_dir))inlet*archive_static_files_dest,_fl=Dkml_install_runner.Path_location.static_files_source~static_default:No_static_default~opam_context_opt:None~static_files_opt:(Some(Fpath.to_stringarchive_static_dir))in(* Copy all components from Opam into archive *)Logs.debug(funl->l"Copying all components from Opam into archive tree");List.iter(funcomponent_name->Populate_archive.populate_archive_component~component_name~abi_selector~opam_staging_files_source~opam_static_files_source~archive_staging_files_dest~archive_static_files_dest)component_names;(* Assemble for one ABI. Return uninstaller, if any *)generate_installer_from_archive_dir~install_direction~archive_dir~work_dir~abi_selector~organization~program_name~program_version~target_dirin(* Separate install and uninstall components.
The install direction will be placed in work/a/i/* and target/i-*.
The uninstall direction will be placed in work/a/u/* and target/u-*.
The target/ directory has by design no subdirectories (aka. it is _flat_)
so that a single release directory can be made.
A flat directory is necessary for GitHub Releases.
Only the i- and u- prefixes distinguish installers from uninstallers. We
didn't use "setup-" and "uninstall-" prefixes because those would conflict
with the probable names of signed Windows executables (which belong to the
same Releases namespace).
The uninstaller is done first because it has to be bundled into
the installer.
*)letget_archive_dirdirection_dir=Fpath.(work_dir/"a"/direction_dir/abi)inletinstall_archive_dir=get_archive_dir"i"inletuninstall_archive_dir=get_archive_dir"u"inLogs.debug(funl->l"Creating uninstaller");let*uninstaller_opt,_fl=create_installerDkml_install_runner.Path_eval.Global_context.Uninstalluninstall_archive_diruninstall_component_namespackager_uninstall_exepackager_uninstaller_bytecodein(match(uninstaller_opt,abi_selector,program_info.Dkml_package_console_common.Author_types.embeds_32bit_uninstaller,program_info.Dkml_package_console_common.Author_types.embeds_64bit_uninstaller)with|Someuninstaller,AbiWindows_x86,true,_|Someuninstaller,AbiWindows_x86_64,_,true->Logs.debug(funl->l"Embedding uninstaller into installer archive tree");Populate_archive.copy_file~src:uninstaller~dst:Fpath.(install_archive_dir/"bin"/"dkml-package-uninstall.exe")|Some_,_,_,_|None,_,_,_->());Logs.debug(funl->l"Creating installer");let*_uninstallers,_fl=create_installerDkml_install_runner.Path_eval.Global_context.Installinstall_archive_dirinstall_component_namespackager_install_exepackager_setup_bytecodeinreturn()letcreate_forall_abi(_log_config:Dkml_install_api.Log_config.t)organizationprogram_nameprogram_infoprogram_versioncomponent_listwork_dirtarget_diropam_contextabisrunner_admin_exerunner_user_exepackager_install_exepackager_uninstall_exepackager_setup_bytecodepackager_uninstaller_bytecode=(* Get component plugins; logging already setup *)letreg=Dkml_install_register.Component_registry.get()in(* Get component names.
Install/uninstall may have different components because
"install_depends_on" and "uninstall_depends_on" component values
may be different.
For example, an uninstaller for Windows may not need to bundle in MSYS2.
*)let*install_component_names,_fl=Dkml_install_register.Component_registry.install_evalreg~selector:(Just_named_components_plus_their_dependenciescomponent_list)~fl:Dkml_install_runner.Error_handling.runner_fatal_log~f:(funcfg->letmoduleCfg=(valcfg:Dkml_install_api.Component_config)inreturnCfg.component_name)inlet*uninstall_component_names,_fl=Dkml_install_register.Component_registry.uninstall_evalreg~selector:(Just_named_components_plus_their_dependenciescomponent_list)~fl:Dkml_install_runner.Error_handling.runner_fatal_log~f:(funcfg->letmoduleCfg=(valcfg:Dkml_install_api.Component_config)inreturnCfg.component_name)in(* IMPORTANT: We always add
{!Dkml_package_console_common.console_required_components} for both
installers and uninstallers
*)letinstall_component_names=List.sort_uniqString.compare(Dkml_package_console_common.console_required_components@install_component_names)inletuninstall_component_names=List.sort_uniqString.compare(Dkml_package_console_common.console_required_components@uninstall_component_names)inLogs.info(funl->l"@[Installers will be created that include the components:@]@ @[<v>%a@]"Fmt.(Dump.liststring)install_component_names);Logs.info(funl->l"@[Uninstallers will be created that include the components:@]@ \
@[<v>%a@]"Fmt.(Dump.liststring)uninstall_component_names);(* Get all ABIs, include Generic *)letabi_selectors=[Dkml_install_runner.Path_location.Generic]@List.map(funv->Dkml_install_runner.Path_location.Abiv)abisinLogs.info(funl->l"Installers and uninstallers will be created for the ABIs:@ %a"Fmt.(Dump.listDkml_install_runner.Path_location.pp_abi_selector)abi_selectors);let*(),_fl=Dkml_install_api.Forward_progress.iter~fl:Dkml_install_runner.Error_handling.runner_fatal_log(funabi_selector->create_forone_abi~abi_selector~install_component_names~uninstall_component_names~organization~program_name~program_version~program_info~opam_context~work_dir:(Fpath.vwork_dir)~target_dir:(Fpath.vtarget_dir)~runner_admin_exe:(Fpath.vrunner_admin_exe)~runner_user_exe:(Fpath.vrunner_user_exe)~packager_install_exe:(Fpath.vpackager_install_exe)~packager_uninstall_exe:(Fpath.vpackager_uninstall_exe)~packager_setup_bytecode:(Fpath.vpackager_setup_bytecode)~packager_uninstaller_bytecode:(Fpath.vpackager_uninstaller_bytecode))abi_selectorsinLogs.info(funl->l"Installers and uninstallers created successfully.");return()letprogram_version_t=letdoc="The version of the program that will be installed"inArg.(required&opt(somestring)None&info["program-version"]~doc)letabis_t=letopenDkml_install_api.Context.Abi_v2inletl=List.map(funv->(to_canonical_stringv,v))Dkml_install_api.Context.Abi_v2.valuesinletdoc="An ABI to build an installer for. Defaults to all of the supported ABIs"inArg.(value&opt_all(enuml)Dkml_install_api.Context.Abi_v2.values&info["abi"]~doc~docv:"ABI")letwork_dir_t=letdoc="A working directory for use generating the installer. It is your \
responsibility to clean it up"inArg.(required&opt(somedir)None&info["work-dir"]~docv:"DIR"~doc)lettarget_dir_t=letdoc="The directory to place the installer and any supporting files"inArg.(required&opt(somedir)None&info["target-dir"]~docv:"DIR"~doc)letwildcard_doc="Any $(dune-context) in the path will expand to either 'default.TARGET_ABI' \
if it is present, or 'default' if it is not present. For example, if you \
have done cross-compilation using opam-monorepo and `dune build -x \
darwin_arm64`, then `_build/install/$(dune-context)/bin/a.exe` will expand \
to `_build/install/default.darwin_arm64/bin/some.executable` if \
some.executable exists, otherwise \
`_build/install/default/bin/some.executable` is used as the path. With this \
mechanism cross-compiled binaries can replace native binaries."letrunner_admin_exe_t=letdoc="The runner_admin.exe. "^wildcard_docinArg.(required&opt(somestring)None&info["runner-admin-exe"]~docv:"EXE"~doc)letrunner_user_exe_t=letdoc="The runner_user.exe. "^wildcard_docinArg.(required&opt(somestring)None&info["runner-user-exe"]~docv:"EXE"~doc)letentry_install_exe_t=letdoc="The setup.exe generated by a (Console, etc.) packager. "^wildcard_docinArg.(required&opt(somestring)None&info["packager-install-exe"]~docv:"EXE"~doc)letentry_uninstall_exe_t=letdoc="The uninstall.exe generated by a (Console, etc.) packager. "^wildcard_docinArg.(required&opt(somestring)None&info["packager-uninstall-exe"]~docv:"EXE"~doc)letsetup_bytecode_t=letdoc="The setup.bc generated by a (Console, etc.) packager. "^wildcard_docinArg.(required&opt(somestring)None&info["packager-setup-bytecode"]~docv:"BYTECODE"~doc)letuninstaller_bytecode_t=letdoc="The uninstaller.bc generated by a (Console, etc.) packager. "^wildcard_docinArg.(required&opt(somestring)None&info["packager-uninstaller-bytecode"]~docv:"BYTECODE"~doc)letopam_context_t=letdoc=Fmt.str"Obtain staging files from an Opam switch. A switch prefix is either the \
$(b,_opam) subdirectory of a local Opam switch or $(b,%s/<switchname>) \
for a global Opam switch. $(opt) is required when there is no \
OPAM_SWITCH_PREFIX environment variable; otherwise the value of \
OPAM_SWITCH_PREFIX is the default for $(opt). The OPAM_SWITCH_PREFIX \
environment variable is set automatically by commands like `%s`."(Cmdliner.Manpage.escape"$OPAMROOT")(Cmdliner.Manpage.escape"(& opam env) -split '\\r?\\n' | ForEach-Object { Invoke-Expression \
$_ }` for Windows PowerShell or `eval $(opam env)")inletinf=Arg.info[Dkml_install_runner.Cmdliner_common.opam_context_args]~docv:"OPAM_SWITCH_PREFIX"~docinletunbackslash=function'\\'->'/'|c->cinmatchOS.Env.var"OPAM_SWITCH_PREFIX"with|Somecurrent_opam_switch_prefix->Arg.(value&optdir(String.mapunbackslashcurrent_opam_switch_prefix)&inf)|None->Arg.(required&opt(somedir)None&inf)letcomponent_list_t=letdoc="A component to add to the installer and uninstaller. All the \
[install_depends_on] components of the specified component are added as \
well to the installer. Similarly all the [uninstall_depends_on] \
components of the specified component are added as well to the \
uninstaller. May be repeated. At least one component must be specified."inArg.(non_empty&opt_allstring[]&info["component"]~doc)(** [create_installers] creates a Console installer for each ABI, and one
Console installer .tar.gz for "generic".
On Windows the installer is a self-extracting 7zip archive that
automatically runs setup.exe.
On Unix the installer is simply a .tar.gz archive.
The generic .tar.gz "installer" is likely unusable since it will not have
any ABI specific files.
*)letcreate_installersorganizationprogram_nameprogram_info=lett=Term.(constcreate_forall_abi$Dkml_install_runner.Cmdliner_runner.setup_log_t$constorganization$constprogram_name$constprogram_info$program_version_t$component_list_t$work_dir_t$target_dir_t$opam_context_t$abis_t$runner_admin_exe_t$runner_user_exe_t$entry_install_exe_t$entry_uninstall_exe_t$setup_bytecode_t$uninstaller_bytecode_t)inDkml_install_runner.Cmdliner_runner.eval_progress(t,Term.info~version:"0.3.1""dkml-install-create-installers")