#!/usr/bin/env python # Copyright (c) Facebook, Inc. and its affiliates. from __future__ import absolute_import from __future__ import division from __future__ import print_function from __future__ import unicode_literals """ shell_builder.py allows running the fbcode_builder logic on the host rather than in a container. It emits a bash script with set -exo pipefail configured such that any failing step will cause the script to exit with failure. == How to run it? == cd build python fbcode_builder/shell_builder.py > ~/run.sh bash ~/run.sh """ import distutils.spawn import os from fbcode_builder import FBCodeBuilder from shell_quoting import raw_shell, shell_comment, shell_join, ShellQuoted from utils import recursively_flatten_list class ShellFBCodeBuilder(FBCodeBuilder): def _render_impl(self, steps): return raw_shell(shell_join("\n", recursively_flatten_list(steps))) def set_env(self, key, value): return ShellQuoted("export {key}={val}").format(key=key, val=value) def workdir(self, dir): return [ ShellQuoted("mkdir -p {d} && cd {d}").format(d=dir), ] def run(self, shell_cmd): return ShellQuoted("{cmd}").format(cmd=shell_cmd) def step(self, name, actions): assert "\n" not in name, "Name {0} would span > 1 line".format(name) b = ShellQuoted("") return [ShellQuoted("### {0} ###".format(name)), b] + actions + [b] def setup(self): steps = ( [ ShellQuoted("set -exo pipefail"), ] + self.create_python_venv() + self.python_venv() ) if self.has_option("ccache_dir"): ccache_dir = self.option("ccache_dir") steps += [ ShellQuoted( # Set CCACHE_DIR before the `ccache` invocations below. "export CCACHE_DIR={ccache_dir} " 'CC="ccache ${{CC:-gcc}}" CXX="ccache ${{CXX:-g++}}"' ).format(ccache_dir=ccache_dir) ] return steps def comment(self, comment): return shell_comment(comment) def copy_local_repo(self, dir, dest_name): return [ ShellQuoted("cp -r {dir} {dest_name}").format(dir=dir, dest_name=dest_name), ] def find_project_root(): here = os.path.dirname(os.path.realpath(__file__)) maybe_root = os.path.dirname(os.path.dirname(here)) if os.path.isdir(os.path.join(maybe_root, ".git")): return maybe_root raise RuntimeError( "I expected shell_builder.py to be in the " "build/fbcode_builder subdir of a git repo" ) def persistent_temp_dir(repo_root): escaped = repo_root.replace("/", "sZs").replace("\\", "sZs").replace(":", "") return os.path.join(os.path.expandvars("$HOME"), ".fbcode_builder-" + escaped) if __name__ == "__main__": from utils import read_fbcode_builder_config, build_fbcode_builder_config repo_root = find_project_root() temp = persistent_temp_dir(repo_root) config = read_fbcode_builder_config("fbcode_builder_config.py") builder = ShellFBCodeBuilder(projects_dir=temp) if distutils.spawn.find_executable("ccache"): builder.add_option( "ccache_dir", os.environ.get("CCACHE_DIR", os.path.join(temp, ".ccache")) ) builder.add_option("prefix", os.path.join(temp, "installed")) builder.add_option("make_parallelism", 4) builder.add_option( "{project}:local_repo_dir".format(project=config["github_project"]), repo_root ) make_steps = build_fbcode_builder_config(config) steps = make_steps(builder) print(builder.render(steps))