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