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