package oui
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
A tool that builds standalone installers for your OCaml applications
Install
dune-project
Dependency
Authors
Maintainers
Sources
oui-0.1.0.tbz
sha256=b806a63c9acd581a00729a48f1faf86b294cc82936fc705fc628a20ede33f977
sha512=e61c35a6e08671634c1edabd7b0add926ac6e0c6c43c9e0a095e89efab7696b6ae5cc1ea0844283a80640c19adb36478bdfb2414bbcab5077ceb7fd16ae45e66
doc/src/oui.lib/pkgbuild_backend.ml.html
Source file pkgbuild_backend.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208(**************************************************************************) (* *) (* Copyright 2025 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) (* exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) open OpamFilename.Op let vars : Installer_config.vars = { install_path = "$INSTALL_PATH" } let create_work_dir () = let tmp_dir = Filename.get_temp_dir_name () in let work_dir_name = Printf.sprintf "oui-macos-%d" (Random.int 1000000) in let work_dir_path = Filename.concat tmp_dir work_dir_name in let work_dir = OpamFilename.Dir.of_string work_dir_path in OpamFilename.mkdir work_dir; work_dir let copy_manpages bundle ~bundle_dir ~manpages = match manpages with | None -> () | Some man_sections -> List.iter (fun (section, files) -> let relative_path = Filename.concat "man" section in let section_dir = Macos_app_bundle.add_subdir bundle ~relative_path in List.iter (fun file_path -> let src = bundle_dir // file_path in if OpamFilename.exists src then let basename = OpamFilename.basename src in let dst = section_dir // OpamFilename.Base.to_string basename in OpamFilename.copy ~src ~dst else OpamConsole.warning "Manpage not found: %s" file_path ) files ) man_sections let create_info_plist bundle ~installer_config = let plist = Info_plist.make_info_plist ~bundle_id:bundle.Macos_app_bundle.bundle_id ~executable:bundle.binary_name ~name:bundle.app_name ~display_name:installer_config.Installer_config.fullname ~version:installer_config.version in let plist_path = bundle.contents // "Info.plist" in Info_plist.save plist plist_path; OpamConsole.msg "Created Info.plist: %s\n" (OpamFilename.to_string plist_path) let handle_dylibs bundle ~binary_dst = OpamConsole.msg "Processing dylib dependencies...\n"; let dylibs = Otool.get_dylibs binary_dst in if List.length dylibs = 0 then OpamConsole.msg " No external dylibs found\n" else List.iter (fun dylib -> ignore (Macos_app_bundle.copy_dylib bundle ~dylib)) dylibs; Install_name_tool.relocate_to_executable_path binary_dst (** Generate install.conf content *) let generate_install_conf ~resources_path ~(installer_config : Installer_config.internal) = let lines = [ Printf.sprintf "version=%s" installer_config.version ] @ (match installer_config.plugin_dirs with | None -> [] | Some pd -> [ Printf.sprintf "plugins=%s/%s" resources_path pd.Installer_config.plugins_dir ; Printf.sprintf "lib=%s/%s" resources_path pd.lib_dir ]) in String.concat "\n" lines ^ "\n" let write_install_conf bundle ~installer_config = let resources_path = Printf.sprintf "/Applications/%s.app/Contents/Resources" (String.capitalize_ascii installer_config.Installer_config.name) in let content = generate_install_conf ~resources_path ~installer_config in let install_conf_path = bundle.Macos_app_bundle.resources // "install.conf" in OpamFilename.write install_conf_path content; OpamConsole.msg "Created install.conf: %s\n" (OpamFilename.to_string install_conf_path) (** Create the .pkg installer from the bundle *) let create_installer ~(installer_config : Installer_config.internal) ~bundle_dir installer = Random.self_init (); let work_dir = create_work_dir () in OpamConsole.msg "Working directory: %s\n" (OpamFilename.Dir.to_string work_dir); (* Create .app bundle structure *) let bundle = Macos_app_bundle.create ~installer_config ~work_dir in (* Copy all bundle contents to Resources *) Macos_app_bundle.copy_bundle_contents bundle ~bundle_dir; (* Install main binary to MacOS directory (if exec_files provided) *) let binary_name = match installer_config.exec_files with | [] -> (* Plugin-only bundle - no main binary *) OpamConsole.msg "No exec_files specified, creating plugin-only package\n"; None | binary :: _ -> let binary_src = bundle_dir // binary.path in let binary_dst = Macos_app_bundle.install_binary bundle ~binary_path:binary_src in handle_dylibs bundle ~binary_dst; (* Sign the binary with ad-hoc signature *) OpamConsole.msg "Signing binary...\n"; Codesign.sign_binary_adhoc binary_dst; Some bundle.binary_name in create_info_plist bundle ~installer_config; copy_manpages bundle ~bundle_dir ~manpages:installer_config.manpages; (* Create symlinks for dune-site relocatable support *) List.iter (fun dir_name -> let src_dir = bundle.resources / dir_name in let link_path = OpamFilename.Dir.to_string bundle.contents ^ "/" ^ dir_name in if OpamFilename.exists_dir src_dir then (OpamConsole.msg "Creating symlink: Contents/%s -> Resources/%s\n" dir_name dir_name; Unix.symlink ("Resources/" ^ dir_name) link_path) else OpamConsole.warning "Directory %s not found in Resources, skipping symlink" dir_name ) installer_config.macos_symlink_dirs; (* Write install.conf for plugin support *) write_install_conf bundle ~installer_config; (* Create postinstall script *) let scripts_dir = work_dir / "scripts" in let has_binary = Option.is_some binary_name in let binary_name_for_scripts = match binary_name with | Some n -> n | None -> installer_config.name in let postinstall_content = Macos_postinstall.generate_postinstall_script ~env:installer_config.environment ~app_name:bundle.app_name ~binary_name:binary_name_for_scripts ~has_binary ~plugins:installer_config.plugins () in let _postinstall_path = Macos_postinstall.save_postinstall_script ~content:postinstall_content ~scripts_dir in (* Create uninstall script in bundle *) let uninstall_content = Macos_postinstall.generate_uninstall_script ~app_name:bundle.app_name ~binary_name:binary_name_for_scripts ~has_binary ~plugins:installer_config.plugins in let _uninstall_path = Macos_postinstall.save_uninstall_script ~content:uninstall_content ~resources_dir:bundle.resources in let component_pkg_path = let base = OpamFilename.chop_extension installer in OpamFilename.add_extension base "-component.pkg" in let install_location = Printf.sprintf "/Applications/%s.app" bundle.app_name in OpamConsole.msg "Creating component package...\n"; let pkgbuild_args : System.pkgbuild_args = { root = bundle.app_bundle_dir; identifier = bundle.bundle_id; version = installer_config.version; install_location; scripts = Some scripts_dir; output = component_pkg_path; } in System.call_unit System.Pkgbuild pkgbuild_args; OpamConsole.msg "Creating final installer package...\n"; let productbuild_args : System.productbuild_args = { package = component_pkg_path; output = installer; } in System.call_unit System.Productbuild productbuild_args; OpamFilename.remove component_pkg_path; OpamFilename.rmdir work_dir; OpamConsole.formatted_msg "Created: %s\n" (OpamConsole.colorise `green (OpamFilename.to_string installer))
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>