1 #!/usr/bin/env -S python3 -B
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 # You can obtain one at http://mozilla.org/MPL/2.0/.
13 from pathlib
import Path
15 logging
.basicConfig(format
="%(levelname)s: %(message)s", level
=logging
.INFO
)
18 def find_command(names
):
19 """Search for command in `names`, and returns the first one that exists."""
22 path
= shutil
.which(name
)
29 def assert_command(env_var
, name
):
30 """Assert that the command is not empty
31 The command name comes from either environment variable or find_command.
34 logging
.error("{} command not found".format(env_var
))
38 def parse_version(topsrc_dir
):
39 """Parse milestone.txt and return the entire milestone and major version."""
40 milestone_file
= topsrc_dir
/ "config" / "milestone.txt"
41 if not milestone_file
.is_file():
44 with milestone_file
.open("r") as f
:
49 if line
.startswith("#"):
53 return tuple((v
+ ["", ""])[:3])
58 tmp_dir
= Path("/tmp")
60 tar
= os
.environ
.get("TAR", find_command(["tar"]))
61 assert_command("TAR", tar
)
63 rsync
= os
.environ
.get("RSYNC", find_command(["rsync"]))
64 assert_command("RSYNC", rsync
)
66 m4
= os
.environ
.get("M4", find_command(["m4"]))
67 assert_command("M4", m4
)
69 awk
= os
.environ
.get("AWK", find_command(["awk"]))
70 assert_command("AWK", awk
)
72 src_dir
= Path(os
.environ
.get("SRC_DIR", Path(__file__
).parent
.absolute()))
73 mozjs_name
= os
.environ
.get("MOZJS_NAME", "mozjs")
74 staging_dir
= Path(os
.environ
.get("STAGING", tmp_dir
/ "mozjs-src-pkg"))
75 dist_dir
= Path(os
.environ
.get("DIST", tmp_dir
))
76 topsrc_dir
= src_dir
.parent
.parent
.absolute()
78 parsed_major_version
, parsed_minor_version
, parsed_patch_version
= parse_version(
82 major_version
= os
.environ
.get("MOZJS_MAJOR_VERSION", parsed_major_version
)
83 minor_version
= os
.environ
.get("MOZJS_MINOR_VERSION", parsed_minor_version
)
84 patch_version
= os
.environ
.get("MOZJS_PATCH_VERSION", parsed_patch_version
)
85 alpha
= os
.environ
.get("MOZJS_ALPHA", "")
87 version
= "{}-{}.{}.{}".format(
88 mozjs_name
, major_version
, minor_version
, patch_version
or alpha
or "0"
90 target_dir
= staging_dir
/ version
91 package_name
= "{}.tar.xz".format(version
)
92 package_file
= dist_dir
/ package_name
95 # Given there might be some external program that reads the following output,
96 # use raw `print`, instead of logging.
98 print(" TAR = {}".format(tar
))
99 print(" RSYNC = {}".format(rsync
))
100 print(" M4 = {}".format(m4
))
101 print(" AWK = {}".format(awk
))
102 print(" STAGING = {}".format(staging_dir
))
103 print(" DIST = {}".format(dist_dir
))
104 print(" SRC_DIR = {}".format(src_dir
))
105 print(" MOZJS_NAME = {}".format(mozjs_name
))
106 print(" MOZJS_MAJOR_VERSION = {}".format(major_version
))
107 print(" MOZJS_MINOR_VERSION = {}".format(minor_version
))
108 print(" MOZJS_PATCH_VERSION = {}".format(patch_version
))
109 print(" MOZJS_ALPHA = {}".format(alpha
))
112 rsync_filter_list
= """
113 # Top-level config and build files
124 + /.babel-eslint.rc.js
133 + /.ycm_extra_conf.py
135 # Additional libraries (optionally) used by SpiderMonkey
140 - /intl/icu/source/data
141 - /intl/icu/source/test
142 - /intl/icu/source/tools
145 + /intl/icu_testdata/**
147 - /intl/components/gtest
148 + /intl/components/**
150 + /memory/replace/dmd/dmd.py
153 + /memory/mozalloc/**
158 + /mozglue/baseprofiler/**
160 + /mozglue/interposers/**
165 + /tools/rb/fix_stacks.py
166 + /tools/fuzzing/moz.build
167 + /tools/fuzzing/interface/**
168 + /tools/fuzzing/registry/**
169 + /tools/fuzzing/libfuzzer/**
170 + /tools/fuzzing/*.mozbuild
172 # Build system and dependencies
181 + /third_party/function2/**
182 - /third_party/python/gyp
183 + /third_party/python/**
184 + /third_party/rust/**
185 + /third_party/gemmology/**
186 + /third_party/xsimd/**
187 + /layout/tools/reftest/reftest/**
189 + /testing/mach_commands.py
191 + /testing/mozbase/**
192 + /testing/performance/**
193 + /testing/web-platform/*.ini
194 + /testing/web-platform/*.py
195 + /testing/web-platform/meta/streams/**
196 + /testing/web-platform/mozilla/**
197 + /testing/web-platform/tests/resources/**
198 + /testing/web-platform/tests/streams/**
199 + /testing/web-platform/tests/tools/**
201 + /toolkit/crashreporter/tools/symbolstore.py
202 + /toolkit/mozapps/installer/package-name.mk
204 + /xpcom/geckoprocesstypes_generator/**
206 # SpiderMonkey itself
218 INSTALL_CONTENT
= """\
219 Documentation for SpiderMonkey is available at:
221 https://spidermonkey.dev/
223 In particular, it points to build documentation at
225 https://firefox-source-docs.mozilla.org/js/build.html
227 Note that the libraries produced by the build system include symbols,
228 causing the binaries to be extremely large. It is highly suggested that `strip`
229 be run over the binaries before deploying them.
231 Building with default options may be performed as follows:
235 This will produce a debug build (much more suitable for developing against the
236 SpiderMonkey JSAPI). To produce an optimized build:
238 export MOZCONFIG=$(pwd)/mozconfig.opt
241 You may edit the mozconfig and mozconfig.opt files to configure your own build
245 MOZCONFIG_DEBUG_CONTENT
= """\
246 # Much slower when running, but adds assertions that are much better for
247 # developing against the JSAPI.
248 ac_add_options --enable-debug
250 # Much faster when running, worse for debugging.
251 ac_add_options --enable-optimize
253 mk_add_options MOZ_OBJDIR=obj-debug
256 MOZCONFIG_OPT_CONTENT
= """\
257 # Much faster when running, but very error-prone to develop against because
258 # this will skip all the assertions critical to using the JSAPI properly.
259 ac_add_options --disable-debug
261 # Much faster when running, worse for debugging.
262 ac_add_options --enable-optimize
264 mk_add_options MOZ_OBJDIR=obj-opt
267 README_CONTENT
= """\
268 This directory contains SpiderMonkey {major_version}.
270 This release is based on a revision of Mozilla {major_version}:
271 https://hg.mozilla.org/releases/
272 The changes in the patches/ directory were applied.
274 See https://spidermonkey.dev/ for documentation, examples, and release notes.
276 major_version
=major_version
280 def is_mozjs_cargo_member(line
):
281 """Checks if the line in workspace.members is mozjs-related"""
283 return '"js/' in line
286 def is_mozjs_crates_io_local_patch(line
):
287 """Checks if the line in patch.crates-io is mozjs-related"""
290 f
'path = "{p}' in line
for p
in ("js", "build", "third_party/rust", "intl")
295 """Remove temporary directory and package file."""
296 logging
.info("Cleaning {} and {} ...".format(package_file
, target_dir
))
297 if package_file
.exists():
298 package_file
.unlink()
299 if target_dir
.exists():
300 shutil
.rmtree(str(target_dir
))
304 """Assert that target directory does not contain generated files."""
305 makefile_file
= target_dir
/ "js" / "src" / "Makefile"
306 if makefile_file
.exists():
307 logging
.error("found js/src/Makefile. Please clean before packaging.")
311 def create_target_dir():
312 if target_dir
.exists():
313 logging
.warning("dist tree {} already exists!".format(target_dir
))
315 target_dir
.mkdir(parents
=True)
319 # Output of the command should directly go to stdout/stderr.
320 p
= subprocess
.Popen(
324 "--prune-empty-dirs",
327 "{}/".format(topsrc_dir
),
328 "{}/".format(target_dir
),
331 stdin
=subprocess
.PIPE
,
334 p
.communicate(rsync_filter_list
.encode())
336 if p
.returncode
!= 0:
337 sys
.exit(p
.returncode
)
340 def copy_cargo_toml():
341 cargo_toml_file
= topsrc_dir
/ "Cargo.toml"
342 target_cargo_toml_file
= target_dir
/ "Cargo.toml"
344 with cargo_toml_file
.open("r") as f
:
346 class State(enum
.Enum
):
354 state
= State
.BEFORE_MEMBER
356 if state
== State
.BEFORE_MEMBER
:
357 if line
.strip() == "members = [":
358 state
= State
.INSIDE_MEMBER
359 elif state
== State
.INSIDE_MEMBER
:
360 if line
.strip() == "]":
361 state
= State
.AFTER_MEMBER
362 elif not is_mozjs_cargo_member(line
):
364 elif state
== State
.AFTER_MEMBER
:
365 if line
.strip() == "[patch.crates-io]":
366 state
= State
.INSIDE_PATCH
367 elif state
== State
.INSIDE_PATCH
:
368 if line
.startswith("["):
369 state
= State
.AFTER_PATCH
370 if "path = " in line
:
371 if not is_mozjs_crates_io_local_patch(line
):
376 with target_cargo_toml_file
.open("w") as f
:
380 def generate_configure():
381 """Generate configure files to avoid build dependency on autoconf-2.13"""
383 src_old_configure_in_file
= topsrc_dir
/ "js" / "src" / "old-configure.in"
384 dest_old_configure_file
= target_dir
/ "js" / "src" / "old-configure"
386 js_src_dir
= topsrc_dir
/ "js" / "src"
388 env
= os
.environ
.copy()
391 env
["AC_MACRODIR"] = topsrc_dir
/ "build" / "autoconf"
393 with dest_old_configure_file
.open("w") as f
:
397 str(topsrc_dir
/ "build" / "autoconf" / "autoconf.sh"),
398 "--localdir={}".format(js_src_dir
),
399 str(src_old_configure_in_file
),
407 def copy_file(filename
, content
):
408 """Copy an existing file from the staging area, or create a new file
409 with the given contents if it does not exist."""
411 staging_file
= staging_dir
/ filename
412 target_file
= target_dir
/ filename
414 if staging_file
.exists():
415 shutil
.copy2(str(staging_file
), str(target_file
))
417 with target_file
.open("w") as f
:
422 """Copy patches dir, if it exists."""
424 staging_patches_dir
= staging_dir
/ "patches"
425 top_patches_dir
= topsrc_dir
/ "patches"
426 target_patches_dir
= target_dir
/ "patches"
428 if staging_patches_dir
.is_dir():
429 shutil
.copytree(str(staging_patches_dir
), str(target_patches_dir
))
430 elif top_patches_dir
.is_dir():
431 shutil
.copytree(str(top_patches_dir
), str(target_patches_dir
))
434 def remove_python_cache():
435 """Remove *.pyc and *.pyo files if any."""
436 for f
in target_dir
.glob("**/*.pyc"):
438 for f
in target_dir
.glob("**/*.pyo"):
443 """Stage source tarball content."""
444 logging
.info("Staging source tarball in {}...".format(target_dir
))
450 copy_file("INSTALL", INSTALL_CONTENT
)
451 copy_file("README", README_CONTENT
)
452 copy_file("mozconfig", MOZCONFIG_DEBUG_CONTENT
)
453 copy_file("mozconfig.opt", MOZCONFIG_OPT_CONTENT
)
455 remove_python_cache()
459 """Roll the tarball."""
461 logging
.info("Packaging source tarball at {}...".format(package_file
))
464 [str(tar
)] + tar_opts
+ [str(package_file
), "-C", str(staging_dir
), version
],
475 parser
= argparse
.ArgumentParser(description
="Make SpiderMonkey source package")
476 subparsers
= parser
.add_subparsers(dest
="COMMAND")
477 subparser_update
= subparsers
.add_parser("clean", help="")
478 subparser_update
= subparsers
.add_parser("build", help="")
479 args
= parser
.parse_args()
481 if args
.COMMAND
== "clean":
483 elif not args
.COMMAND
or args
.COMMAND
== "build":