##// END OF EJS Templates
pyoxidizer: add arch to PyOxidizer MSIs...
Gregory Szorc -
r48699:3962503c stable
parent child Browse files
Show More
@@ -1,316 +1,320 b''
1 1 # The following variables can be passed in as parameters:
2 2 #
3 3 # VERSION
4 4 # Version string of program being produced.
5 5 #
6 6 # MSI_NAME
7 7 # Root name of MSI installer.
8 8 #
9 9 # EXTRA_MSI_FEATURES
10 10 # ; delimited string of extra features to advertise in the built MSA.
11 11 #
12 12 # SIGNING_PFX_PATH
13 13 # Path to code signing certificate to use.
14 14 #
15 15 # SIGNING_PFX_PASSWORD
16 16 # Password to code signing PFX file defined by SIGNING_PFX_PATH.
17 17 #
18 18 # SIGNING_SUBJECT_NAME
19 19 # String fragment in code signing certificate subject name used to find
20 20 # code signing certificate in Windows certificate store.
21 21 #
22 22 # TIME_STAMP_SERVER_URL
23 23 # URL of time-stamp token authority (RFC 3161) servers to stamp code signatures.
24 24
25 25 ROOT = CWD + "/../.."
26 26
27 27 VERSION = VARS.get("VERSION", "5.8")
28 28 MSI_NAME = VARS.get("MSI_NAME", "mercurial")
29 29 EXTRA_MSI_FEATURES = VARS.get("EXTRA_MSI_FEATURES")
30 30 SIGNING_PFX_PATH = VARS.get("SIGNING_PFX_PATH")
31 31 SIGNING_PFX_PASSWORD = VARS.get("SIGNING_PFX_PASSWORD", "")
32 32 SIGNING_SUBJECT_NAME = VARS.get("SIGNING_SUBJECT_NAME")
33 33 TIME_STAMP_SERVER_URL = VARS.get("TIME_STAMP_SERVER_URL", "http://timestamp.digicert.com")
34 34
35 35 IS_WINDOWS = "windows" in BUILD_TARGET_TRIPLE
36 36
37 37 # Code to run in Python interpreter.
38 38 RUN_CODE = """
39 39 import os
40 40 import sys
41 41 extra_path = os.environ.get('PYTHONPATH')
42 42 if extra_path is not None:
43 43 # extensions and hooks expect a working python environment
44 44 # We do not prepend the values because the Mercurial library wants to be in
45 45 # the front of the sys.path to avoid picking up other installations.
46 46 sys.path.extend(extra_path.split(os.pathsep))
47 47 # Add user site to sys.path to load extensions without the full path
48 48 if os.name == 'nt':
49 49 vi = sys.version_info
50 50 appdata = os.environ.get('APPDATA')
51 51 if appdata:
52 52 sys.path.append(
53 53 os.path.join(
54 54 appdata,
55 55 'Python',
56 56 'Python%d%d' % (vi[0], vi[1]),
57 57 'site-packages',
58 58 )
59 59 )
60 60 import hgdemandimport;
61 61 hgdemandimport.enable();
62 62 from mercurial import dispatch;
63 63 dispatch.run();
64 64 """
65 65
66 66 set_build_path(ROOT + "/build/pyoxidizer")
67 67
68 68 def make_distribution():
69 69 return default_python_distribution(python_version = "3.9")
70 70
71 71 def resource_callback(policy, resource):
72 72 if not IS_WINDOWS:
73 73 resource.add_location = "in-memory"
74 74 return
75 75
76 76 # We use a custom resource routing policy to influence where things are loaded
77 77 # from.
78 78 #
79 79 # For Python modules and resources, we load from memory if they are in
80 80 # the standard library and from the filesystem if not. This is because
81 81 # parts of Mercurial and some 3rd party packages aren't yet compatible
82 82 # with memory loading.
83 83 #
84 84 # For Python extension modules, we load from the filesystem because
85 85 # this yields greatest compatibility.
86 86 if type(resource) in ("PythonModuleSource", "PythonPackageResource", "PythonPackageDistributionResource"):
87 87 if resource.is_stdlib:
88 88 resource.add_location = "in-memory"
89 89 else:
90 90 resource.add_location = "filesystem-relative:lib"
91 91
92 92 elif type(resource) == "PythonExtensionModule":
93 93 resource.add_location = "filesystem-relative:lib"
94 94
95 95 def make_exe(dist):
96 96 """Builds a Rust-wrapped Mercurial binary."""
97 97 packaging_policy = dist.make_python_packaging_policy()
98 98
99 99 # Extension may depend on any Python functionality. Include all
100 100 # extensions.
101 101 packaging_policy.extension_module_filter = "all"
102 102 packaging_policy.resources_location = "in-memory"
103 103 if IS_WINDOWS:
104 104 packaging_policy.resources_location_fallback = "filesystem-relative:lib"
105 105 packaging_policy.register_resource_callback(resource_callback)
106 106
107 107 config = dist.make_python_interpreter_config()
108 108 config.allocator_backend = "default"
109 109 config.run_command = RUN_CODE
110 110
111 111 # We want to let the user load extensions from the file system
112 112 config.filesystem_importer = True
113 113
114 114 # We need this to make resourceutil happy, since it looks for sys.frozen.
115 115 config.sys_frozen = True
116 116 config.legacy_windows_stdio = True
117 117
118 118 exe = dist.to_python_executable(
119 119 name = "hg",
120 120 packaging_policy = packaging_policy,
121 121 config = config,
122 122 )
123 123
124 124 # Add Mercurial to resources.
125 125 exe.add_python_resources(exe.pip_install(["--verbose", ROOT]))
126 126
127 127 # On Windows, we install extra packages for convenience.
128 128 if IS_WINDOWS:
129 129 exe.add_python_resources(
130 130 exe.pip_install(["-r", ROOT + "/contrib/packaging/requirements-windows-py3.txt"]),
131 131 )
132 132 extra_packages = VARS.get("extra_py_packages", "")
133 133 if extra_packages:
134 134 for extra in extra_packages.split(","):
135 135 extra_src, pkgs = extra.split("=")
136 136 pkgs = pkgs.split(":")
137 137 exe.add_python_resources(exe.read_package_root(extra_src, pkgs))
138 138
139 139 return exe
140 140
141 141 def make_manifest(dist, exe):
142 142 m = FileManifest()
143 143 m.add_python_resource(".", exe)
144 144
145 145 return m
146 146
147 147
148 148 # This adjusts the InstallManifest produced from exe generation to provide
149 149 # additional files found in a Windows install layout.
150 150 def make_windows_install_layout(manifest):
151 151 # Copy various files to new install locations. This can go away once
152 152 # we're using the importlib resource reader.
153 153 RECURSIVE_COPIES = {
154 154 "lib/mercurial/locale/": "locale/",
155 155 "lib/mercurial/templates/": "templates/",
156 156 }
157 157 for (search, replace) in RECURSIVE_COPIES.items():
158 158 for path in manifest.paths():
159 159 if path.startswith(search):
160 160 new_path = path.replace(search, replace)
161 161 print("copy %s to %s" % (path, new_path))
162 162 file = manifest.get_file(path)
163 163 manifest.add_file(file, path = new_path)
164 164
165 165 # Similar to above, but with filename pattern matching.
166 166 # lib/mercurial/helptext/**/*.txt -> helptext/
167 167 # lib/mercurial/defaultrc/*.rc -> defaultrc/
168 168 for path in manifest.paths():
169 169 if path.startswith("lib/mercurial/helptext/") and path.endswith(".txt"):
170 170 new_path = path[len("lib/mercurial/"):]
171 171 elif path.startswith("lib/mercurial/defaultrc/") and path.endswith(".rc"):
172 172 new_path = path[len("lib/mercurial/"):]
173 173 else:
174 174 continue
175 175
176 176 print("copying %s to %s" % (path, new_path))
177 177 manifest.add_file(manifest.get_file(path), path = new_path)
178 178
179 179 extra_install_files = VARS.get("extra_install_files", "")
180 180 if extra_install_files:
181 181 for extra in extra_install_files.split(","):
182 182 print("adding extra files from %s" % extra)
183 183 # TODO: I expected a ** glob to work, but it didn't.
184 184 #
185 185 # TODO: I know this has forward-slash paths. As far as I can tell,
186 186 # backslashes don't ever match glob() expansions in
187 187 # tugger-starlark, even on Windows.
188 188 manifest.add_manifest(glob(include=[extra + "/*/*"], strip_prefix=extra+"/"))
189 189
190 190 # We also install a handful of additional files.
191 191 EXTRA_CONTRIB_FILES = [
192 192 "bash_completion",
193 193 "hgweb.fcgi",
194 194 "hgweb.wsgi",
195 195 "logo-droplets.svg",
196 196 "mercurial.el",
197 197 "mq.el",
198 198 "tcsh_completion",
199 199 "tcsh_completion_build.sh",
200 200 "xml.rnc",
201 201 "zsh_completion",
202 202 ]
203 203
204 204 for f in EXTRA_CONTRIB_FILES:
205 205 manifest.add_file(FileContent(path = ROOT + "/contrib/" + f), directory = "contrib")
206 206
207 207 # Individual files with full source to destination path mapping.
208 208 EXTRA_FILES = {
209 209 "contrib/hgk": "contrib/hgk.tcl",
210 210 "contrib/win32/postinstall.txt": "ReleaseNotes.txt",
211 211 "contrib/win32/ReadMe.html": "ReadMe.html",
212 212 "doc/style.css": "doc/style.css",
213 213 "COPYING": "Copying.txt",
214 214 }
215 215
216 216 for source, dest in EXTRA_FILES.items():
217 217 print("adding extra file %s" % dest)
218 218 manifest.add_file(FileContent(path = ROOT + "/" + source), path = dest)
219 219
220 220 # And finally some wildcard matches.
221 221 manifest.add_manifest(glob(
222 222 include = [ROOT + "/contrib/vim/*"],
223 223 strip_prefix = ROOT + "/"
224 224 ))
225 225 manifest.add_manifest(glob(
226 226 include = [ROOT + "/doc/*.html"],
227 227 strip_prefix = ROOT + "/"
228 228 ))
229 229
230 230 # But we don't ship hg-ssh on Windows, so exclude its documentation.
231 231 manifest.remove("doc/hg-ssh.8.html")
232 232
233 233 return manifest
234 234
235 235
236 236 def make_msi(manifest):
237 237 manifest = make_windows_install_layout(manifest)
238 238
239 239 if "x86_64" in BUILD_TARGET_TRIPLE:
240 240 platform = "x64"
241 241 else:
242 242 platform = "x86"
243 243
244 244 manifest.add_file(
245 245 FileContent(path = ROOT + "/contrib/packaging/wix/COPYING.rtf"),
246 246 path = "COPYING.rtf",
247 247 )
248 248 manifest.remove("Copying.txt")
249 249 manifest.add_file(
250 250 FileContent(path = ROOT + "/contrib/win32/mercurial.ini"),
251 251 path = "defaultrc/mercurial.rc",
252 252 )
253 253 manifest.add_file(
254 254 FileContent(filename = "editor.rc", content = "[ui]\neditor = notepad\n"),
255 255 path = "defaultrc/editor.rc",
256 256 )
257 257
258 wix = WiXInstaller("hg", "%s-%s.msi" % (MSI_NAME, VERSION), arch = platform)
258 wix = WiXInstaller(
259 "hg",
260 "%s-%s-%s.msi" % (MSI_NAME, VERSION, platform),
261 arch = platform,
262 )
259 263
260 264 # Materialize files in the manifest to the install layout.
261 265 wix.add_install_files(manifest)
262 266
263 267 # From mercurial.wxs.
264 268 wix.install_files_root_directory_id = "INSTALLDIR"
265 269
266 270 # Pull in our custom .wxs files.
267 271 defines = {
268 272 "PyOxidizer": "1",
269 273 "Platform": platform,
270 274 "Version": VERSION,
271 275 "Comments": "Installs Mercurial version %s" % VERSION,
272 276 "PythonVersion": "3",
273 277 "MercurialHasLib": "1",
274 278 }
275 279
276 280 if EXTRA_MSI_FEATURES:
277 281 defines["MercurialExtraFeatures"] = EXTRA_MSI_FEATURES
278 282
279 283 wix.add_wxs_file(
280 284 ROOT + "/contrib/packaging/wix/mercurial.wxs",
281 285 preprocessor_parameters=defines,
282 286 )
283 287
284 288 # Our .wxs references to other files. Pull those into the build environment.
285 289 for f in ("defines.wxi", "guids.wxi", "COPYING.rtf"):
286 290 wix.add_build_file(f, ROOT + "/contrib/packaging/wix/" + f)
287 291
288 292 wix.add_build_file("mercurial.ico", ROOT + "/contrib/win32/mercurial.ico")
289 293
290 294 return wix
291 295
292 296
293 297 def register_code_signers():
294 298 if not IS_WINDOWS:
295 299 return
296 300
297 301 if SIGNING_PFX_PATH:
298 302 signer = code_signer_from_pfx_file(SIGNING_PFX_PATH, SIGNING_PFX_PASSWORD)
299 303 elif SIGNING_SUBJECT_NAME:
300 304 signer = code_signer_from_windows_store_subject(SIGNING_SUBJECT_NAME)
301 305 else:
302 306 signer = None
303 307
304 308 if signer:
305 309 signer.set_time_stamp_server(TIME_STAMP_SERVER_URL)
306 310 signer.activate()
307 311
308 312
309 313 register_code_signers()
310 314
311 315 register_target("distribution", make_distribution)
312 316 register_target("exe", make_exe, depends = ["distribution"])
313 317 register_target("app", make_manifest, depends = ["distribution", "exe"], default = True)
314 318 register_target("msi", make_msi, depends = ["app"])
315 319
316 320 resolve_targets()
General Comments 0
You need to be logged in to leave comments. Login now