#!/usr/bin/env python3 # 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 os import subprocess import json import argparse import sys import shutil from functools import reduce def check_run(args, path): print(" ".join(args) + " in " + path, file=sys.stderr) subprocess.run(args, cwd=path, check=True) def run_in(path, args, extra_env=None): """ Runs the given commands in the directory specified by . """ env = dict(os.environ) env.update(extra_env or {}) check_run(args, path) subprocess.run(args, cwd=path) def build_tar_package(tar, name, base, directories): name = os.path.realpath(name) run_in( base, [tar, "-c", "-%s" % ("J" if ".xz" in name else "j"), "-f", name] + directories, ) def is_git_repo(dir): """Check whether the given directory is a git repository.""" from subprocess import CalledProcessError try: check_run(["git", "rev-parse"], dir) return True except CalledProcessError: return False def git_clone(main_dir, url, clone_dir, commit): """ Clones the repository from into , and brings the repository to the state of . """ run_in(main_dir, ["git", "clone", url, clone_dir]) run_in(clone_dir, ["git", "checkout", commit]) if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "-c", "--config", required=True, type=argparse.FileType("r"), help="Infer configuration file", ) parser.add_argument( "-b", "--base-dir", help="Base directory for code and build artifacts" ) parser.add_argument( "--clean", action="store_true", help="Clean the build directory" ) parser.add_argument( "--skip-tar", action="store_true", help="Skip tar packaging stage" ) args = parser.parse_args() # The directories end up in the debug info, so the easy way of getting # a reproducible build is to run it in a know absolute directory. # We use a directory that is registered as a volume in the Docker image. if args.base_dir: base_dir = args.base_dir else: base_dir = reduce( os.path.join, [os.sep + "builds", "worker", "workspace", "moz-toolchain"] ) infer_dir = os.path.join(base_dir, "infer") source_dir = os.path.join(infer_dir, "src") build_dir = os.path.join(infer_dir, "build") if args.clean: shutil.rmtree(build_dir) os.sys.exit(0) config = json.load(args.config) infer_revision = config["infer_revision"] infer_repo = config["infer_repo"] for folder in [infer_dir, source_dir, build_dir]: os.makedirs(folder, exist_ok=True) # clone infer if not is_git_repo(source_dir): # git doesn't like cloning into a non-empty folder. If src is not a git # repo then just remove it in order to reclone shutil.rmtree(source_dir) git_clone(infer_dir, infer_repo, source_dir, infer_revision) # apply a few patches dir_path = os.path.dirname(os.path.realpath(__file__)) # clean the git directory by reseting all changes git_commands = [["clean", "-f"], ["reset", "--hard"]] for command in git_commands: run_in(source_dir, ["git"] + command) for p in config.get("patches", []): run_in(source_dir, ["git", "apply", os.path.join(dir_path, p)]) # configure opam run_in(source_dir, ["opam", "init", "--no-setup", "--disable-sandboxing"]) # build infer run_in(source_dir, ["./build-infer.sh", "java"], extra_env={"NO_CMAKE_STRIP": "1"}) package_name = "infer" infer_package = os.path.join(os.getcwd(), package_name) # We need to create a package with all of the depended libraries injected in it run_in( source_dir, [ "make", "install-with-libs", "BUILD_MODE=opt", "PATCHELF=patchelf", "DESTDIR={}".format(infer_package), "libdir_relative_to_bindir=../lib", ], ) infer_package_with_pref = os.path.join(infer_package, "usr") if not args.skip_tar: os.rename( os.path.join(infer_package_with_pref, "local"), os.path.join(infer_package_with_pref, "infer"), ) build_tar_package( "tar", "%s.tar.xz" % (package_name), infer_package_with_pref, [ os.path.join("infer", "bin"), os.path.join("infer", "lib"), os.path.join("infer", "share"), ], )