diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /js/src/make-source-package.py | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/make-source-package.py')
-rwxr-xr-x | js/src/make-source-package.py | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/js/src/make-source-package.py b/js/src/make-source-package.py new file mode 100755 index 0000000000..3ea6b7e310 --- /dev/null +++ b/js/src/make-source-package.py @@ -0,0 +1,491 @@ +#!/usr/bin/env -S python3 -B +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this file, +# You can obtain one at http://mozilla.org/MPL/2.0/. + +import argparse +import enum +import logging +import os +import shutil +import subprocess +import sys +from pathlib import Path + +logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO) + + +def find_command(names): + """Search for command in `names`, and returns the first one that exists.""" + + for name in names: + path = shutil.which(name) + if path is not None: + return name + + return None + + +def assert_command(env_var, name): + """Assert that the command is not empty + The command name comes from either environment variable or find_command. + """ + if not name: + logging.error("{} command not found".format(env_var)) + sys.exit(1) + + +def parse_version(topsrc_dir): + """Parse milestone.txt and return the entire milestone and major version.""" + milestone_file = topsrc_dir / "config" / "milestone.txt" + if not milestone_file.is_file(): + return ("", "", "") + + with milestone_file.open("r") as f: + for line in f: + line = line.strip() + if not line: + continue + if line.startswith("#"): + continue + + v = line.split(".") + return tuple((v + ["", ""])[:3]) + + return ("", "", "") + + +tmp_dir = Path("/tmp") + +tar = os.environ.get("TAR", find_command(["tar"])) +assert_command("TAR", tar) + +rsync = os.environ.get("RSYNC", find_command(["rsync"])) +assert_command("RSYNC", rsync) + +m4 = os.environ.get("M4", find_command(["m4"])) +assert_command("M4", m4) + +awk = os.environ.get("AWK", find_command(["awk"])) +assert_command("AWK", awk) + +src_dir = Path(os.environ.get("SRC_DIR", Path(__file__).parent.absolute())) +mozjs_name = os.environ.get("MOZJS_NAME", "mozjs") +staging_dir = Path(os.environ.get("STAGING", tmp_dir / "mozjs-src-pkg")) +dist_dir = Path(os.environ.get("DIST", tmp_dir)) +topsrc_dir = src_dir.parent.parent.absolute() + +parsed_major_version, parsed_minor_version, parsed_patch_version = parse_version( + topsrc_dir +) + +major_version = os.environ.get("MOZJS_MAJOR_VERSION", parsed_major_version) +minor_version = os.environ.get("MOZJS_MINOR_VERSION", parsed_minor_version) +patch_version = os.environ.get("MOZJS_PATCH_VERSION", parsed_patch_version) +alpha = os.environ.get("MOZJS_ALPHA", "") + +version = "{}-{}.{}.{}".format( + mozjs_name, major_version, minor_version, patch_version or alpha or "0" +) +target_dir = staging_dir / version +package_name = "{}.tar.xz".format(version) +package_file = dist_dir / package_name +tar_opts = ["-Jcf"] + +# Given there might be some external program that reads the following output, +# use raw `print`, instead of logging. +print("Environment:") +print(" TAR = {}".format(tar)) +print(" RSYNC = {}".format(rsync)) +print(" M4 = {}".format(m4)) +print(" AWK = {}".format(awk)) +print(" STAGING = {}".format(staging_dir)) +print(" DIST = {}".format(dist_dir)) +print(" SRC_DIR = {}".format(src_dir)) +print(" MOZJS_NAME = {}".format(mozjs_name)) +print(" MOZJS_MAJOR_VERSION = {}".format(major_version)) +print(" MOZJS_MINOR_VERSION = {}".format(minor_version)) +print(" MOZJS_PATCH_VERSION = {}".format(patch_version)) +print(" MOZJS_ALPHA = {}".format(alpha)) +print("") + +rsync_filter_list = """ +# Top-level config and build files + ++ /aclocal.m4 ++ /client.mk ++ /configure.py ++ /LICENSE ++ /mach ++ /Makefile.in ++ /moz.build ++ /moz.configure ++ /test.mozbuild ++ /.babel-eslint.rc.js ++ /.eslintignore ++ /.eslintrc.js ++ /.flake8 ++ /.gitignore ++ /.hgignore ++ /.lldbinit ++ /.prettierignore ++ /.prettierrc ++ /.ycm_extra_conf.py + +# Additional libraries (optionally) used by SpiderMonkey + ++ /mfbt/** ++ /nsprpub/** + ++ /intl/bidi/** + +- /intl/icu/source/data +- /intl/icu/source/test +- /intl/icu/source/tools ++ /intl/icu/** + ++ /intl/icu_capi/** ++ /intl/icu_segmenter_data/** + +- /intl/components/gtest ++ /intl/components/** + ++ /memory/replace/dmd/dmd.py ++ /memory/build/** ++ /memory/moz.build ++ /memory/mozalloc/** + ++ /modules/fdlibm/** ++ /modules/zlib/** + ++ /mozglue/baseprofiler/** ++ /mozglue/build/** ++ /mozglue/interposers/** ++ /mozglue/misc/** ++ /mozglue/moz.build ++ /mozglue/static/** + ++ /tools/rb/fix_stacks.py ++ /tools/fuzzing/moz.build ++ /tools/fuzzing/interface/** ++ /tools/fuzzing/registry/** ++ /tools/fuzzing/libfuzzer/** ++ /tools/fuzzing/*.mozbuild + +# Build system and dependencies + ++ /Cargo.lock ++ /build/** ++ /config/** ++ /python/** + ++ /.cargo/config.in + ++ /third_party/function2/** +- /third_party/python/gyp ++ /third_party/python/** ++ /third_party/rust/** ++ /third_party/gemmology/** ++ /third_party/xsimd/** ++ /layout/tools/reftest/reftest/** + ++ /testing/mach_commands.py ++ /testing/moz.build ++ /testing/mozbase/** ++ /testing/performance/** ++ /testing/test/** ++ /testing/web-platform/*.ini ++ /testing/web-platform/*.py ++ /testing/web-platform/meta/streams/** ++ /testing/web-platform/mozilla/** ++ /testing/web-platform/tests/resources/** ++ /testing/web-platform/tests/streams/** ++ /testing/web-platform/tests/tools/** + ++ /toolkit/crashreporter/tools/symbolstore.py ++ /toolkit/mozapps/installer/package-name.mk + ++ /xpcom/geckoprocesstypes_generator/** + +# List of prefs. ++ /modules/libpref/init/StaticPrefList.yaml + +# SpiderMonkey itself + ++ /js/src/** ++ /js/app.mozbuild ++ /js/*.configure ++ /js/examples/** ++ /js/public/** + ++ */ +- /** +""" + +INSTALL_CONTENT = """\ +Documentation for SpiderMonkey is available at: + + https://spidermonkey.dev/ + +In particular, it points to build documentation at + + https://firefox-source-docs.mozilla.org/js/build.html + +Note that the libraries produced by the build system include symbols, +causing the binaries to be extremely large. It is highly suggested that `strip` +be run over the binaries before deploying them. + +Building with default options may be performed as follows: + + ./mach build + +This will produce a debug build (much more suitable for developing against the +SpiderMonkey JSAPI). To produce an optimized build: + + export MOZCONFIG=$(pwd)/mozconfig.opt + ./mach build + +You may edit the mozconfig and mozconfig.opt files to configure your own build +appropriately. +""" + +MOZCONFIG_DEBUG_CONTENT = """\ +# Much slower when running, but adds assertions that are much better for +# developing against the JSAPI. +ac_add_options --enable-debug + +# Much faster when running, worse for debugging. +ac_add_options --enable-optimize + +mk_add_options MOZ_OBJDIR=obj-debug +""" + +MOZCONFIG_OPT_CONTENT = """\ +# Much faster when running, but very error-prone to develop against because +# this will skip all the assertions critical to using the JSAPI properly. +ac_add_options --disable-debug + +# Much faster when running, worse for debugging. +ac_add_options --enable-optimize + +mk_add_options MOZ_OBJDIR=obj-opt +""" + +README_CONTENT = """\ +This directory contains SpiderMonkey {major_version}. + +This release is based on a revision of Mozilla {major_version}: + https://hg.mozilla.org/releases/ +The changes in the patches/ directory were applied. + +See https://spidermonkey.dev/ for documentation, examples, and release notes. +""".format( + major_version=major_version +) + + +def is_mozjs_cargo_member(line): + """Checks if the line in workspace.members is mozjs-related""" + + return '"js/' in line + + +def is_mozjs_crates_io_local_patch(line): + """Checks if the line in patch.crates-io is mozjs-related""" + + return any( + f'path = "{p}' in line for p in ("js", "build", "third_party/rust", "intl") + ) + + +def clean(): + """Remove temporary directory and package file.""" + logging.info("Cleaning {} and {} ...".format(package_file, target_dir)) + if package_file.exists(): + package_file.unlink() + if target_dir.exists(): + shutil.rmtree(str(target_dir)) + + +def assert_clean(): + """Assert that target directory does not contain generated files.""" + makefile_file = target_dir / "js" / "src" / "Makefile" + if makefile_file.exists(): + logging.error("found js/src/Makefile. Please clean before packaging.") + sys.exit(1) + + +def create_target_dir(): + if target_dir.exists(): + logging.warning("dist tree {} already exists!".format(target_dir)) + else: + target_dir.mkdir(parents=True) + + +def sync_files(): + # Output of the command should directly go to stdout/stderr. + p = subprocess.Popen( + [ + str(rsync), + "--delete-excluded", + "--prune-empty-dirs", + "--quiet", + "--recursive", + "{}/".format(topsrc_dir), + "{}/".format(target_dir), + "--filter=. -", + ], + stdin=subprocess.PIPE, + ) + + p.communicate(rsync_filter_list.encode()) + + if p.returncode != 0: + sys.exit(p.returncode) + + +def copy_cargo_toml(): + cargo_toml_file = topsrc_dir / "Cargo.toml" + target_cargo_toml_file = target_dir / "Cargo.toml" + + with cargo_toml_file.open("r") as f: + + class State(enum.Enum): + BEFORE_MEMBER = 1 + INSIDE_MEMBER = 2 + AFTER_MEMBER = 3 + INSIDE_PATCH = 4 + AFTER_PATCH = 5 + + content = "" + state = State.BEFORE_MEMBER + for line in f: + if state == State.BEFORE_MEMBER: + if line.strip() == "members = [": + state = State.INSIDE_MEMBER + elif state == State.INSIDE_MEMBER: + if line.strip() == "]": + state = State.AFTER_MEMBER + elif not is_mozjs_cargo_member(line): + continue + elif state == State.AFTER_MEMBER: + if line.strip() == "[patch.crates-io]": + state = State.INSIDE_PATCH + elif state == State.INSIDE_PATCH: + if line.startswith("["): + state = State.AFTER_PATCH + if "path = " in line: + if not is_mozjs_crates_io_local_patch(line): + continue + + content += line + + with target_cargo_toml_file.open("w") as f: + f.write(content) + + +def generate_configure(): + """Generate configure files to avoid build dependency on autoconf-2.13""" + + src_old_configure_in_file = topsrc_dir / "js" / "src" / "old-configure.in" + dest_old_configure_file = target_dir / "js" / "src" / "old-configure" + + js_src_dir = topsrc_dir / "js" / "src" + + env = os.environ.copy() + env["M4"] = m4 + env["AWK"] = awk + env["AC_MACRODIR"] = topsrc_dir / "build" / "autoconf" + + with dest_old_configure_file.open("w") as f: + subprocess.run( + [ + "sh", + str(topsrc_dir / "build" / "autoconf" / "autoconf.sh"), + "--localdir={}".format(js_src_dir), + str(src_old_configure_in_file), + ], + stdout=f, + check=True, + env=env, + ) + + +def copy_file(filename, content): + """Copy an existing file from the staging area, or create a new file + with the given contents if it does not exist.""" + + staging_file = staging_dir / filename + target_file = target_dir / filename + + if staging_file.exists(): + shutil.copy2(str(staging_file), str(target_file)) + else: + with target_file.open("w") as f: + f.write(content) + + +def copy_patches(): + """Copy patches dir, if it exists.""" + + staging_patches_dir = staging_dir / "patches" + top_patches_dir = topsrc_dir / "patches" + target_patches_dir = target_dir / "patches" + + if staging_patches_dir.is_dir(): + shutil.copytree(str(staging_patches_dir), str(target_patches_dir)) + elif top_patches_dir.is_dir(): + shutil.copytree(str(top_patches_dir), str(target_patches_dir)) + + +def remove_python_cache(): + """Remove *.pyc and *.pyo files if any.""" + for f in target_dir.glob("**/*.pyc"): + f.unlink() + for f in target_dir.glob("**/*.pyo"): + f.unlink() + + +def stage(): + """Stage source tarball content.""" + logging.info("Staging source tarball in {}...".format(target_dir)) + + create_target_dir() + sync_files() + copy_cargo_toml() + generate_configure() + copy_file("INSTALL", INSTALL_CONTENT) + copy_file("README", README_CONTENT) + copy_file("mozconfig", MOZCONFIG_DEBUG_CONTENT) + copy_file("mozconfig.opt", MOZCONFIG_OPT_CONTENT) + copy_patches() + remove_python_cache() + + +def create_tar(): + """Roll the tarball.""" + + logging.info("Packaging source tarball at {}...".format(package_file)) + + subprocess.run( + [str(tar)] + tar_opts + [str(package_file), "-C", str(staging_dir), version], + check=True, + ) + + +def build(): + assert_clean() + stage() + create_tar() + + +parser = argparse.ArgumentParser(description="Make SpiderMonkey source package") +subparsers = parser.add_subparsers(dest="COMMAND") +subparser_update = subparsers.add_parser("clean", help="") +subparser_update = subparsers.add_parser("build", help="") +args = parser.parse_args() + +if args.COMMAND == "clean": + clean() +elif not args.COMMAND or args.COMMAND == "build": + build() |