summaryrefslogtreecommitdiffstats
path: root/build/build-infer/build-infer.py
blob: e9a4804ce08f66c6e4c8570f803fa1bf201b3a9c (plain)
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"),
            ],
        )