mirror of
https://github.com/facebook/sapling.git
synced 2024-12-26 14:34:34 +03:00
d2520b62e2
Summary: This should fix the Travis CI builds. It adds rust toolchain support inside docker and sets the required THRIFT env variable. Pull Request resolved: https://github.com/facebookexperimental/rust-shed/pull/3 Reviewed By: krallin Differential Revision: D18905608 Pulled By: lukaspiatkowski fbshipit-source-id: 5db1eff6f215a6617d8acaa0c99a62d45225956b
192 lines
7.5 KiB
Python
192 lines
7.5 KiB
Python
#!/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
|
|
'''
|
|
|
|
Extends FBCodeBuilder to produce Docker context directories.
|
|
|
|
In order to get the largest iteration-time savings from Docker's build
|
|
caching, you will want to:
|
|
- Use fine-grained steps as appropriate (e.g. separate make & make install),
|
|
- Start your action sequence with the lowest-risk steps, and with the steps
|
|
that change the least often, and
|
|
- Put the steps that you are debugging towards the very end.
|
|
|
|
'''
|
|
import logging
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
|
|
from fbcode_builder import FBCodeBuilder
|
|
from shell_quoting import (
|
|
raw_shell, shell_comment, shell_join, ShellQuoted, path_join
|
|
)
|
|
from utils import recursively_flatten_list, run_command
|
|
|
|
|
|
class DockerFBCodeBuilder(FBCodeBuilder):
|
|
|
|
def _user(self):
|
|
return self.option('user', 'root')
|
|
|
|
def _change_user(self):
|
|
return ShellQuoted('USER {u}').format(u=self._user())
|
|
|
|
def setup(self):
|
|
# Please add RPM-based OSes here as appropriate.
|
|
#
|
|
# To allow exercising non-root installs -- we change users after the
|
|
# system packages are installed. TODO: For users not defined in the
|
|
# image, we should probably `useradd`.
|
|
return self.step(
|
|
"Setup",
|
|
[
|
|
# Docker's FROM does not understand shell quoting.
|
|
ShellQuoted("FROM {}".format(self.option("os_image"))),
|
|
# /bin/sh syntax is a pain
|
|
ShellQuoted('SHELL ["/bin/bash", "-c"]'),
|
|
]
|
|
+ self.install_debian_deps()
|
|
+ [self._change_user()]
|
|
+ [self.workdir(self.option("prefix"))]
|
|
+ self.create_python_venv()
|
|
+ self.python_venv()
|
|
+ self.rust_toolchain(),
|
|
)
|
|
|
|
def python_venv(self):
|
|
# To both avoid calling venv activate on each RUN command AND to ensure
|
|
# it is present when the resulting container is run add to PATH
|
|
actions = []
|
|
if self.option("PYTHON_VENV", "OFF") == "ON":
|
|
actions = ShellQuoted('ENV PATH={p}:$PATH').format(
|
|
p=path_join(self.option('prefix'), "venv", "bin"))
|
|
return(actions)
|
|
|
|
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 run(self, shell_cmd):
|
|
return ShellQuoted('RUN {cmd}').format(cmd=shell_cmd)
|
|
|
|
def set_env(self, key, value):
|
|
return ShellQuoted("ENV {key}={val}").format(key=key, val=value)
|
|
|
|
def workdir(self, dir):
|
|
return [
|
|
# As late as Docker 1.12.5, this results in `build` being owned
|
|
# by root:root -- the explicit `mkdir` works around the bug:
|
|
# USER nobody
|
|
# WORKDIR build
|
|
ShellQuoted('USER root'),
|
|
ShellQuoted('RUN mkdir -p {d} && chown {u} {d}').format(
|
|
d=dir, u=self._user()
|
|
),
|
|
self._change_user(),
|
|
ShellQuoted('WORKDIR {dir}').format(dir=dir),
|
|
]
|
|
|
|
def comment(self, comment):
|
|
# This should not be a command since we don't want comment changes
|
|
# to invalidate the Docker build cache.
|
|
return shell_comment(comment)
|
|
|
|
def copy_local_repo(self, repo_dir, dest_name):
|
|
fd, archive_path = tempfile.mkstemp(
|
|
prefix='local_repo_{0}_'.format(dest_name),
|
|
suffix='.tgz',
|
|
dir=os.path.abspath(self.option('docker_context_dir')),
|
|
)
|
|
os.close(fd)
|
|
run_command('tar', 'czf', archive_path, '.', cwd=repo_dir)
|
|
return [
|
|
ShellQuoted('ADD {archive} {dest_name}').format(
|
|
archive=os.path.basename(archive_path), dest_name=dest_name
|
|
),
|
|
# Docker permissions make very little sense... see also workdir()
|
|
ShellQuoted('USER root'),
|
|
ShellQuoted('RUN chown -R {u} {d}').format(
|
|
d=dest_name, u=self._user()
|
|
),
|
|
self._change_user(),
|
|
]
|
|
|
|
def _render_impl(self, steps):
|
|
return raw_shell(shell_join('\n', recursively_flatten_list(steps)))
|
|
|
|
def debian_ccache_setup_steps(self):
|
|
source_ccache_tgz = self.option('ccache_tgz', '')
|
|
if not source_ccache_tgz:
|
|
logging.info('Docker ccache not enabled')
|
|
return []
|
|
|
|
dest_ccache_tgz = os.path.join(
|
|
self.option('docker_context_dir'), 'ccache.tgz'
|
|
)
|
|
|
|
try:
|
|
try:
|
|
os.link(source_ccache_tgz, dest_ccache_tgz)
|
|
except OSError:
|
|
logging.exception(
|
|
'Hard-linking {s} to {d} failed, falling back to copy'
|
|
.format(s=source_ccache_tgz, d=dest_ccache_tgz)
|
|
)
|
|
shutil.copyfile(source_ccache_tgz, dest_ccache_tgz)
|
|
except Exception:
|
|
logging.exception(
|
|
'Failed to copy or link {s} to {d}, aborting'
|
|
.format(s=source_ccache_tgz, d=dest_ccache_tgz)
|
|
)
|
|
raise
|
|
|
|
return [
|
|
# Separate layer so that in development we avoid re-downloads.
|
|
self.run(ShellQuoted('apt-get install -yq ccache')),
|
|
ShellQuoted('ADD ccache.tgz /'),
|
|
ShellQuoted(
|
|
# Set CCACHE_DIR before the `ccache` invocations below.
|
|
'ENV CCACHE_DIR=/ccache '
|
|
# No clang support for now, so it's easiest to hardcode gcc.
|
|
'CC="ccache gcc" CXX="ccache g++" '
|
|
# Always log for ease of debugging. For real FB projects,
|
|
# this log is several megabytes, so dumping it to stdout
|
|
# would likely exceed the Travis log limit of 4MB.
|
|
#
|
|
# On a local machine, `docker cp` will get you the data. To
|
|
# get the data out from Travis, I would compress and dump
|
|
# uuencoded bytes to the log -- for Bistro this was about
|
|
# 600kb or 8000 lines:
|
|
#
|
|
# apt-get install sharutils
|
|
# bzip2 -9 < /tmp/ccache.log | uuencode -m ccache.log.bz2
|
|
'CCACHE_LOGFILE=/tmp/ccache.log'
|
|
),
|
|
self.run(ShellQuoted(
|
|
# Future: Skipping this part made this Docker step instant,
|
|
# saving ~1min of build time. It's unclear if it is the
|
|
# chown or the du, but probably the chown -- since a large
|
|
# part of the cost is incurred at image save time.
|
|
#
|
|
# ccache.tgz may be empty, or may have the wrong
|
|
# permissions.
|
|
'mkdir -p /ccache && time chown -R nobody /ccache && '
|
|
'time du -sh /ccache && '
|
|
# Reset stats so `docker_build_with_ccache.sh` can print
|
|
# useful values at the end of the run.
|
|
'echo === Prev run stats === && ccache -s && ccache -z && '
|
|
# Record the current time to let travis_build.sh figure out
|
|
# the number of bytes in the cache that are actually used --
|
|
# this is crucial for tuning the maximum cache size.
|
|
'date +%s > /FBCODE_BUILDER_CCACHE_START_TIME && '
|
|
# The build running as `nobody` should be able to write here
|
|
'chown nobody /tmp/ccache.log'
|
|
)),
|
|
]
|