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
142 - /intl/icu/source/data
143 - /intl/icu/source/test
144 - /intl/icu/source/tools
148 + /intl/icu_segmenter_data/**
150 - /intl/components/gtest
151 + /intl/components/**
153 + /memory/replace/dmd/dmd.py
156 + /memory/mozalloc/**
161 + /mozglue/baseprofiler/**
163 + /mozglue/interposers/**
168 + /tools/rb/fix_stacks.py
169 + /tools/fuzzing/moz.build
170 + /tools/fuzzing/interface/**
171 + /tools/fuzzing/registry/**
172 + /tools/fuzzing/libfuzzer/**
173 + /tools/fuzzing/*.mozbuild
175 # Build system and dependencies
184 + /third_party/function2/**
185 - /third_party/python/gyp
186 + /third_party/python/**
187 + /third_party/rust/**
188 + /third_party/gemmology/**
189 + /third_party/xsimd/**
190 + /layout/tools/reftest/reftest/**
192 + /testing/mach_commands.py
194 + /testing/mozbase/**
195 + /testing/performance/**
197 + /testing/web-platform/*.ini
198 + /testing/web-platform/*.py
199 + /testing/web-platform/meta/streams/**
200 + /testing/web-platform/mozilla/**
201 + /testing/web-platform/tests/resources/**
202 + /testing/web-platform/tests/streams/**
203 + /testing/web-platform/tests/tools/**
205 + /toolkit/crashreporter/tools/symbolstore.py
206 + /toolkit/mozapps/installer/package-name.mk
208 + /xpcom/geckoprocesstypes_generator/**
211 + /modules/libpref/init/StaticPrefList.yaml
213 # SpiderMonkey itself
225 INSTALL_CONTENT
= """\
226 Documentation for SpiderMonkey is available at:
228 https://spidermonkey.dev/
230 In particular, it points to build documentation at
232 https://firefox-source-docs.mozilla.org/js/build.html
234 Note that the libraries produced by the build system include symbols,
235 causing the binaries to be extremely large. It is highly suggested that `strip`
236 be run over the binaries before deploying them.
238 Building with default options may be performed as follows:
242 This will produce a debug build (much more suitable for developing against the
243 SpiderMonkey JSAPI). To produce an optimized build:
245 export MOZCONFIG=$(pwd)/mozconfig.opt
248 You may edit the mozconfig and mozconfig.opt files to configure your own build
252 MOZCONFIG_DEBUG_CONTENT
= """\
253 # Much slower when running, but adds assertions that are much better for
254 # developing against the JSAPI.
255 ac_add_options --enable-debug
257 # Much faster when running, worse for debugging.
258 ac_add_options --enable-optimize
260 mk_add_options MOZ_OBJDIR=obj-debug
263 MOZCONFIG_OPT_CONTENT
= """\
264 # Much faster when running, but very error-prone to develop against because
265 # this will skip all the assertions critical to using the JSAPI properly.
266 ac_add_options --disable-debug
268 # Much faster when running, worse for debugging.
269 ac_add_options --enable-optimize
271 mk_add_options MOZ_OBJDIR=obj-opt
274 README_CONTENT
= """\
275 This directory contains SpiderMonkey {major_version}.
277 This release is based on a revision of Mozilla {major_version}:
278 https://hg.mozilla.org/releases/
279 The changes in the patches/ directory were applied.
281 See https://spidermonkey.dev/ for documentation, examples, and release notes.
283 major_version
=major_version
287 def is_mozjs_cargo_member(line
):
288 """Checks if the line in workspace.members is mozjs-related"""
290 return '"js/' in line
293 def is_mozjs_crates_io_local_patch(line
):
294 """Checks if the line in patch.crates-io is mozjs-related"""
297 f
'path = "{p}' in line
for p
in ("js", "build", "third_party/rust", "intl")
302 """Remove temporary directory and package file."""
303 logging
.info("Cleaning {} and {} ...".format(package_file
, target_dir
))
304 if package_file
.exists():
305 package_file
.unlink()
306 if target_dir
.exists():
307 shutil
.rmtree(str(target_dir
))
311 """Assert that target directory does not contain generated files."""
312 makefile_file
= target_dir
/ "js" / "src" / "Makefile"
313 if makefile_file
.exists():
314 logging
.error("found js/src/Makefile. Please clean before packaging.")
318 def create_target_dir():
319 if target_dir
.exists():
320 logging
.warning("dist tree {} already exists!".format(target_dir
))
322 target_dir
.mkdir(parents
=True)
326 # Output of the command should directly go to stdout/stderr.
327 p
= subprocess
.Popen(
331 "--prune-empty-dirs",
334 "{}/".format(topsrc_dir
),
335 "{}/".format(target_dir
),
338 stdin
=subprocess
.PIPE
,
341 p
.communicate(rsync_filter_list
.encode())
343 if p
.returncode
!= 0:
344 sys
.exit(p
.returncode
)
347 def copy_cargo_toml():
348 cargo_toml_file
= topsrc_dir
/ "Cargo.toml"
349 target_cargo_toml_file
= target_dir
/ "Cargo.toml"
351 with cargo_toml_file
.open("r") as f
:
353 class State(enum
.Enum
):
361 state
= State
.BEFORE_MEMBER
363 if state
== State
.BEFORE_MEMBER
:
364 if line
.strip() == "members = [":
365 state
= State
.INSIDE_MEMBER
366 elif state
== State
.INSIDE_MEMBER
:
367 if line
.strip() == "]":
368 state
= State
.AFTER_MEMBER
369 elif not is_mozjs_cargo_member(line
):
371 elif state
== State
.AFTER_MEMBER
:
372 if line
.strip() == "[patch.crates-io]":
373 state
= State
.INSIDE_PATCH
374 elif state
== State
.INSIDE_PATCH
:
375 if line
.startswith("["):
376 state
= State
.AFTER_PATCH
377 if "path = " in line
:
378 if not is_mozjs_crates_io_local_patch(line
):
383 with target_cargo_toml_file
.open("w") as f
:
387 def generate_configure():
388 """Generate configure files to avoid build dependency on autoconf-2.13"""
390 src_old_configure_in_file
= topsrc_dir
/ "js" / "src" / "old-configure.in"
391 dest_old_configure_file
= target_dir
/ "js" / "src" / "old-configure"
393 js_src_dir
= topsrc_dir
/ "js" / "src"
395 env
= os
.environ
.copy()
398 env
["AC_MACRODIR"] = topsrc_dir
/ "build" / "autoconf"
400 with dest_old_configure_file
.open("w") as f
:
404 str(topsrc_dir
/ "build" / "autoconf" / "autoconf.sh"),
405 "--localdir={}".format(js_src_dir
),
406 str(src_old_configure_in_file
),
414 def copy_file(filename
, content
):
415 """Copy an existing file from the staging area, or create a new file
416 with the given contents if it does not exist."""
418 staging_file
= staging_dir
/ filename
419 target_file
= target_dir
/ filename
421 if staging_file
.exists():
422 shutil
.copy2(str(staging_file
), str(target_file
))
424 with target_file
.open("w") as f
:
429 """Copy patches dir, if it exists."""
431 staging_patches_dir
= staging_dir
/ "patches"
432 top_patches_dir
= topsrc_dir
/ "patches"
433 target_patches_dir
= target_dir
/ "patches"
435 if staging_patches_dir
.is_dir():
436 shutil
.copytree(str(staging_patches_dir
), str(target_patches_dir
))
437 elif top_patches_dir
.is_dir():
438 shutil
.copytree(str(top_patches_dir
), str(target_patches_dir
))
441 def remove_python_cache():
442 """Remove *.pyc and *.pyo files if any."""
443 for f
in target_dir
.glob("**/*.pyc"):
445 for f
in target_dir
.glob("**/*.pyo"):
450 """Stage source tarball content."""
451 logging
.info("Staging source tarball in {}...".format(target_dir
))
457 copy_file("INSTALL", INSTALL_CONTENT
)
458 copy_file("README", README_CONTENT
)
459 copy_file("mozconfig", MOZCONFIG_DEBUG_CONTENT
)
460 copy_file("mozconfig.opt", MOZCONFIG_OPT_CONTENT
)
462 remove_python_cache()
466 """Roll the tarball."""
468 logging
.info("Packaging source tarball at {}...".format(package_file
))
471 [str(tar
)] + tar_opts
+ [str(package_file
), "-C", str(staging_dir
), version
],
482 parser
= argparse
.ArgumentParser(description
="Make SpiderMonkey source package")
483 subparsers
= parser
.add_subparsers(dest
="COMMAND")
484 subparser_update
= subparsers
.add_parser("clean", help="")
485 subparser_update
= subparsers
.add_parser("build", help="")
486 args
= parser
.parse_args()
488 if args
.COMMAND
== "clean":
490 elif not args
.COMMAND
or args
.COMMAND
== "build":