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