1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
#!/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 <path>.
"""
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 <url> into <clone_dir>, and brings the
repository to the state of <commit>.
"""
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"),
],
)
|