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