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
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);
let bundle = Macos_app_bundle.create ~installer_config ~work_dir in
Macos_app_bundle.copy_bundle_contents bundle ~bundle_dir;
let binary_name = match installer_config.exec_files with
| [] ->
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;
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;
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 bundle ~installer_config;
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
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))