1#!/usr/bin/env python3 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 4# file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 6import os 7import subprocess 8import json 9import argparse 10import sys 11import shutil 12from functools import reduce 13 14 15def check_run(args, path): 16 print(" ".join(args) + " in " + path, file=sys.stderr) 17 subprocess.run(args, cwd=path, check=True) 18 19 20def run_in(path, args, extra_env=None): 21 """ 22 Runs the given commands in the directory specified by <path>. 23 """ 24 env = dict(os.environ) 25 env.update(extra_env or {}) 26 check_run(args, path) 27 subprocess.run(args, cwd=path) 28 29 30def build_tar_package(tar, name, base, directories): 31 name = os.path.realpath(name) 32 run_in( 33 base, 34 [tar, "-c", "-%s" % ("J" if ".xz" in name else "j"), "-f", name] + directories, 35 ) 36 37 38def is_git_repo(dir): 39 """Check whether the given directory is a git repository.""" 40 from subprocess import CalledProcessError 41 42 try: 43 check_run(["git", "rev-parse"], dir) 44 return True 45 except CalledProcessError: 46 return False 47 48 49def git_clone(main_dir, url, clone_dir, commit): 50 """ 51 Clones the repository from <url> into <clone_dir>, and brings the 52 repository to the state of <commit>. 53 """ 54 run_in(main_dir, ["git", "clone", url, clone_dir]) 55 run_in(clone_dir, ["git", "checkout", commit]) 56 57 58if __name__ == "__main__": 59 parser = argparse.ArgumentParser() 60 parser.add_argument( 61 "-c", 62 "--config", 63 required=True, 64 type=argparse.FileType("r"), 65 help="Infer configuration file", 66 ) 67 parser.add_argument( 68 "-b", "--base-dir", help="Base directory for code and build artifacts" 69 ) 70 parser.add_argument( 71 "--clean", action="store_true", help="Clean the build directory" 72 ) 73 parser.add_argument( 74 "--skip-tar", action="store_true", help="Skip tar packaging stage" 75 ) 76 77 args = parser.parse_args() 78 79 # The directories end up in the debug info, so the easy way of getting 80 # a reproducible build is to run it in a know absolute directory. 81 # We use a directory that is registered as a volume in the Docker image. 82 if args.base_dir: 83 base_dir = args.base_dir 84 else: 85 base_dir = reduce( 86 os.path.join, [os.sep + "builds", "worker", "workspace", "moz-toolchain"] 87 ) 88 infer_dir = os.path.join(base_dir, "infer") 89 source_dir = os.path.join(infer_dir, "src") 90 build_dir = os.path.join(infer_dir, "build") 91 92 if args.clean: 93 shutil.rmtree(build_dir) 94 os.sys.exit(0) 95 96 config = json.load(args.config) 97 infer_revision = config["infer_revision"] 98 infer_repo = config["infer_repo"] 99 100 for folder in [infer_dir, source_dir, build_dir]: 101 os.makedirs(folder, exist_ok=True) 102 103 # clone infer 104 if not is_git_repo(source_dir): 105 # git doesn't like cloning into a non-empty folder. If src is not a git 106 # repo then just remove it in order to reclone 107 shutil.rmtree(source_dir) 108 git_clone(infer_dir, infer_repo, source_dir, infer_revision) 109 # apply a few patches 110 dir_path = os.path.dirname(os.path.realpath(__file__)) 111 # clean the git directory by reseting all changes 112 git_commands = [["clean", "-f"], ["reset", "--hard"]] 113 for command in git_commands: 114 run_in(source_dir, ["git"] + command) 115 for p in config.get("patches", []): 116 run_in(source_dir, ["git", "apply", os.path.join(dir_path, p)]) 117 # configure opam 118 run_in(source_dir, ["opam", "init", "--no-setup", "--disable-sandboxing"]) 119 # build infer 120 run_in(source_dir, ["./build-infer.sh", "java"], extra_env={"NO_CMAKE_STRIP": "1"}) 121 122 package_name = "infer" 123 infer_package = os.path.join(os.getcwd(), package_name) 124 # We need to create a package with all of the depended libraries injected in it 125 run_in( 126 source_dir, 127 [ 128 "make", 129 "install-with-libs", 130 "BUILD_MODE=opt", 131 "PATCHELF=patchelf", 132 "DESTDIR={}".format(infer_package), 133 "libdir_relative_to_bindir=../lib", 134 ], 135 ) 136 137 infer_package_with_pref = os.path.join(infer_package, "usr") 138 if not args.skip_tar: 139 os.rename( 140 os.path.join(infer_package_with_pref, "local"), 141 os.path.join(infer_package_with_pref, "infer"), 142 ) 143 build_tar_package( 144 "tar", 145 "%s.tar.xz" % (package_name), 146 infer_package_with_pref, 147 [ 148 os.path.join("infer", "bin"), 149 os.path.join("infer", "lib"), 150 os.path.join("infer", "share"), 151 ], 152 ) 153