michael@0: #!/usr/bin/python michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: import os michael@0: import os.path michael@0: import shutil michael@0: import subprocess michael@0: import platform michael@0: import sys michael@0: import json michael@0: import collections michael@0: import argparse michael@0: michael@0: michael@0: def check_run(args): michael@0: r = subprocess.call(args) michael@0: assert r == 0 michael@0: michael@0: michael@0: def run_in(path, args): michael@0: d = os.getcwd() michael@0: os.chdir(path) michael@0: check_run(args) michael@0: os.chdir(d) michael@0: michael@0: michael@0: def patch(patch, srcdir): michael@0: patch = os.path.realpath(patch) michael@0: check_run(['patch', '-d', srcdir, '-p1', '-i', patch, '--fuzz=0', michael@0: '-s']) michael@0: michael@0: michael@0: def build_package(package_source_dir, package_build_dir, configure_args, michael@0: make_args): michael@0: if not os.path.exists(package_build_dir): michael@0: os.mkdir(package_build_dir) michael@0: run_in(package_build_dir, michael@0: ["%s/configure" % package_source_dir] + configure_args) michael@0: run_in(package_build_dir, ["make", "-j4"] + make_args) michael@0: run_in(package_build_dir, ["make", "install"]) michael@0: michael@0: michael@0: def with_env(env, f): michael@0: old_env = os.environ.copy() michael@0: os.environ.update(env) michael@0: f() michael@0: os.environ.clear() michael@0: os.environ.update(old_env) michael@0: michael@0: michael@0: def build_tar_package(tar, name, base, directory): michael@0: name = os.path.realpath(name) michael@0: run_in(base, [tar, "-cjf", name, directory]) michael@0: michael@0: michael@0: def svn_co(url, directory, revision): michael@0: check_run(["svn", "co", "-r", revision, url, directory]) michael@0: michael@0: michael@0: def build_one_stage(env, stage_dir, llvm_source_dir, gcc_toolchain_dir): michael@0: def f(): michael@0: build_one_stage_aux(stage_dir, llvm_source_dir, gcc_toolchain_dir) michael@0: with_env(env, f) michael@0: michael@0: michael@0: def build_tooltool_manifest(llvm_revision): michael@0: basedir = os.path.split(os.path.realpath(sys.argv[0]))[0] michael@0: tooltool = basedir + '/tooltool.py' michael@0: setup = basedir + '/setup.sh' michael@0: manifest = 'clang.manifest' michael@0: check_run(['python', tooltool, '-m', manifest, 'add', michael@0: setup, 'clang.tar.bz2']) michael@0: data = json.load(file(manifest), object_pairs_hook=collections.OrderedDict) michael@0: data = [{'clang_version': 'r%s' % llvm_revision}] + data michael@0: out = file(manifest, 'w') michael@0: json.dump(data, out, indent=0) michael@0: out.write('\n') michael@0: michael@0: assert data[2]['filename'] == 'clang.tar.bz2' michael@0: os.rename('clang.tar.bz2', data[2]['digest']) michael@0: michael@0: michael@0: def get_platform(): michael@0: p = platform.system() michael@0: if p == "Darwin": michael@0: return "macosx64" michael@0: elif p == "Linux": michael@0: if platform.processor() == "x86_64": michael@0: return "linux64" michael@0: else: michael@0: return "linux32" michael@0: else: michael@0: raise NotImplementedError("Not supported platform") michael@0: michael@0: michael@0: def is_darwin(): michael@0: return platform.system() == "Darwin" michael@0: michael@0: michael@0: def build_one_stage_aux(stage_dir, llvm_source_dir, gcc_toolchain_dir): michael@0: os.mkdir(stage_dir) michael@0: michael@0: build_dir = stage_dir + "/build" michael@0: inst_dir = stage_dir + "/clang" michael@0: michael@0: targets = ["x86", "x86_64"] michael@0: # The Darwin equivalents of binutils appear to have intermittent problems michael@0: # with objects in compiler-rt that are compiled for arm. Since the arm michael@0: # support is only necessary for iOS (which we don't support), only enable michael@0: # arm support on Linux. michael@0: if not is_darwin(): michael@0: targets.append("arm") michael@0: michael@0: configure_opts = ["--enable-optimized", michael@0: "--enable-targets=" + ",".join(targets), michael@0: "--disable-assertions", michael@0: "--prefix=%s" % inst_dir, michael@0: "--with-gcc-toolchain=%s" % gcc_toolchain_dir, michael@0: "--disable-compiler-version-checks"] michael@0: build_package(llvm_source_dir, build_dir, configure_opts, []) michael@0: michael@0: if __name__ == "__main__": michael@0: # The directories end up in the debug info, so the easy way of getting michael@0: # a reproducible build is to run it in a know absolute directory. michael@0: # We use a directory in /builds/slave because the mozilla infrastructure michael@0: # cleans it up automatically. michael@0: base_dir = "/builds/slave/moz-toolchain" michael@0: michael@0: source_dir = base_dir + "/src" michael@0: build_dir = base_dir + "/build" michael@0: michael@0: llvm_source_dir = source_dir + "/llvm" michael@0: clang_source_dir = source_dir + "/clang" michael@0: compiler_rt_source_dir = source_dir + "/compiler-rt" michael@0: michael@0: gcc_dir = "/tools/gcc-4.7.3-0moz1" michael@0: michael@0: if is_darwin(): michael@0: os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7' michael@0: michael@0: parser = argparse.ArgumentParser() michael@0: parser.add_argument('-c', '--config', required=True, michael@0: type=argparse.FileType('r'), michael@0: help="Clang configuration file") michael@0: michael@0: args = parser.parse_args() michael@0: config = json.load(args.config) michael@0: llvm_revision = config["llvm_revision"] michael@0: llvm_repo = config["llvm_repo"] michael@0: clang_repo = config["clang_repo"] michael@0: compiler_repo = config["compiler_repo"] michael@0: michael@0: if not os.path.exists(source_dir): michael@0: os.makedirs(source_dir) michael@0: svn_co(llvm_repo, llvm_source_dir, llvm_revision) michael@0: svn_co(clang_repo, clang_source_dir, llvm_revision) michael@0: svn_co(compiler_repo, compiler_rt_source_dir, llvm_revision) michael@0: os.symlink("../../clang", llvm_source_dir + "/tools/clang") michael@0: os.symlink("../../compiler-rt", michael@0: llvm_source_dir + "/projects/compiler-rt") michael@0: for p in config.get("patches", {}).get(get_platform(), []): michael@0: patch(p, source_dir) michael@0: michael@0: if os.path.exists(build_dir): michael@0: shutil.rmtree(build_dir) michael@0: os.makedirs(build_dir) michael@0: michael@0: stage1_dir = build_dir + '/stage1' michael@0: stage1_inst_dir = stage1_dir + '/clang' michael@0: michael@0: if is_darwin(): michael@0: extra_cflags = "" michael@0: extra_cxxflags = "" michael@0: cc = "/usr/bin/clang" michael@0: cxx = "/usr/bin/clang++" michael@0: else: michael@0: extra_cflags = "-static-libgcc" michael@0: extra_cxxflags = "-static-libgcc -static-libstdc++" michael@0: cc = gcc_dir + "/bin/gcc" michael@0: cxx = gcc_dir + "/bin/g++" michael@0: michael@0: if os.environ.has_key('LD_LIBRARY_PATH'): michael@0: os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']); michael@0: else: michael@0: os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir michael@0: michael@0: build_one_stage({"CC": cc, "CXX": cxx}, stage1_dir, llvm_source_dir, gcc_dir) michael@0: michael@0: stage2_dir = build_dir + '/stage2' michael@0: build_one_stage( michael@0: {"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags, michael@0: "CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags}, michael@0: stage2_dir, llvm_source_dir, gcc_dir) michael@0: michael@0: build_tar_package("tar", "clang.tar.bz2", stage2_dir, "clang") michael@0: build_tooltool_manifest(llvm_revision)