blob: 44a002cbe818e595b07ba5d537eb276dea724bd7 [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Recipe for building goma toolchain images."""
from recipe_engine.config import List
from recipe_engine.recipe_api import Property
DEPS = [
'fuchsia/docker',
'fuchsia/gcloud',
'fuchsia/git',
'fuchsia/goma',
'fuchsia/gsutil',
'recipe_engine/archive',
'recipe_engine/buildbucket',
'recipe_engine/cipd',
'recipe_engine/context',
'recipe_engine/file',
'recipe_engine/json',
'recipe_engine/path',
'recipe_engine/platform',
'recipe_engine/properties',
'recipe_engine/python',
'recipe_engine/raw_io',
'recipe_engine/step',
'recipe_engine/time',
'recipe_engine/url',
]
PROPERTIES = {
'package':
Property(
kind=str,
help='cipd path of the toolchain package',
default='fuchsia/third_party/clang'),
'version':
Property(
kind=str,
help='cipd version of the toolchain package',
default='git_revision:1475fad1d0d45572471575cdda8fed6eeab5b0ec'),
'toolchain':
Property(kind=str, help='type of the toolchain', default='clang'),
'platforms':
Property(
kind=List(str),
help='toolchain host platform',
default=['linux-amd64', 'mac-amd64']),
'domain':
Property(
kind=str,
help='domain name of container registry',
default='gcr.io'),
'project':
Property(
kind=str, help='name of goma cloud project',
default='goma-fuchsia'),
'root':
Property(
kind=str,
help='root directory name for container registry',
default='fuchsia_linux'),
'update_goma_base_images':
Property(
kind=bool,
help='Update goma base images even if they already exist',
default=False),
}
GOMATOOLS_VERSION = 'git_revision:22684ee9174606073a2604e1d58fbf03ba3ad564'
def ensure_gomatools(api, gomatools_dir):
with api.step.nest('ensure gomatools'):
pkgs = api.cipd.EnsureFile()
pkgs.add_package(
'fuchsia_internal/third_party/goma/server/gomatools/linux-amd64',
GOMATOOLS_VERSION)
api.cipd.ensure(gomatools_dir, pkgs)
def generate_time_stamp(api):
return "{:%Y%m%d_%H%M%S}".format(api.time.utcnow())
def build_goma_base_image(api, dockerfile, image, timestamp=None):
api.file.copy('copy dockerfile', api.resource(dockerfile),
api.path['cleanup'].join(dockerfile))
tags = [image]
if timestamp:
tags.append(image + ':' + timestamp)
with api.context(cwd=api.path['cleanup']):
api.docker.build(
dockerfile=api.path['cleanup'].join(dockerfile),
tags=tags,
)
api.docker('push', image)
def ensure_goma_base_images(api,
domain,
project_id,
root,
timestamp=None,
update_if_exist=False):
"""Ensure the base goma images in container registry.
E.g. ensure_goma_base_images(api,
'gcr.io',\
'goma-fuchsia',\
'fuchsia_linux')
will create docker images at
gcr.io/goma-fuchsia/fuchsia_linux/base,
gcr.io/goma-fuchsia/fuchsia_linux/runtime,
gcr.io/goma-fuchsia/fuchsia_linux/remoteexec-platform,
using 'debian:buster-slim' as bottom layer image.
Args:
* domain (str) - The domain name of the container register.
* project (str) - The gcloud project ID.
* root (str) - The name of container registry root.
* update_if_exist (str) - Update the container if it exists.
"""
with api.step.nest('ensure goma base images'):
# Build goma base image.
goma_base_image = api.url.join(domain, project_id, root, 'base')
if not api.gcloud.container_image_exists(
goma_base_image) or update_if_exist:
build_goma_base_image(api, 'Dockerfile_goma_base', goma_base_image,
timestamp)
# Build goma runtime image.
runtime_image = api.url.join(domain, project_id, root, 'runtime')
if not api.gcloud.container_image_exists(runtime_image) or update_if_exist:
api.docker('pull', goma_base_image)
api.docker('tag', goma_base_image, 'base')
build_goma_base_image(api, 'Dockerfile_goma_runtime', runtime_image,
timestamp)
# pull-toolchain-runtime
api.docker('pull', runtime_image)
api.docker('tag', runtime_image, 'toolchain/runtime')
# Build goma remoteexec-platform
remote_image = api.url.join(domain, project_id, root, 'remoteexec-platform')
if not api.gcloud.container_image_exists(remote_image) or update_if_exist:
build_goma_base_image(api, 'Dockerfile_goma_remoteexec_platform',
remote_image, timestamp)
def push_toolchain_to_goma(api,
goma_temp_dir,
project_id,
root,
toolchain_tag,
container_tag,
container_version='latest',
stamp=None):
"""Push and register the toolchain docker image with Goma.
E.g. push_toolchain_to_goma(goma_temp_dir,\
'goma-fuchsia',\
'fuchsia_linux',\
'clang-some_hash-linux-amd64',\
'gcr.io/goma-fuchsia/fuchsia_linux/clang-some_hash-linux-amd64',\
'latest',
'20190909_122500')
will copy the toolchain binaries from docker image
'gcr.io/goma-fuchsia/fuchsia_linux/clang-some_hash-linux-amd64:latest'
to appropriate gs bucket of the Goma gcloud project and
register it with Goma server.
Args:
* goma_temp_dir (str) - The temp directory for Goma resources.
* project_id (str) - The gcloud project ID.
* root (str) - The root path of all toolchains. E.g. 'fuchsia_linux'.
* toolchain_tag (str) - Toolchain version and platform tag.
* container_tag (str) - Toolchain container tag.
* container_version (str) - The container image version. 'latest' by default.
* stamp (str) - Time stamp string.
"""
with api.step.nest('push toolchain to goma'):
# URI = ${domain}/${project_id}/${root}/${toolchain}-${toolchain_version}:${container_version}
# e.g. URI = gcr.io/goma-fuchsia/fuchsia_linux/clang-0a5975a5bcd59cb07efdc86b47498fbb7c095ab:latest
if stamp is None:
stamp = generate_time_stamp(api) # pragma: no cover
# extract-goma-files
with api.docker.create(container_tag + ':' +
container_version) as temp_copy:
api.docker.copy(temp_copy, ['/var/cache/goma', '/etc/goma/descriptors'],
goma_temp_dir)
api.file.move('rename cmdstorage', goma_temp_dir.join('goma', 'cmdstorage'),
goma_temp_dir.join('goma', 'sha256'))
api.file.remove(
'remove dockerfile if it exists',
goma_temp_dir.join('Dockerfile_fuchsia_toolchain_push_test'))
api.file.copy('copy dockerfile',
api.resource('Dockerfile_fuchsia_toolchain_push_test'),
goma_temp_dir)
# build-test-image
local_toolchain_tag = api.url.join(root, toolchain_tag)
# Set env
with api.context(cwd=goma_temp_dir):
api.docker.build(
dockerfile=goma_temp_dir.join(
"Dockerfile_fuchsia_toolchain_push_test"),
tags=['%s_test' % container_tag,
'%s_test' % local_toolchain_tag],
)
# run test
api.docker('run', '%s_test' % local_toolchain_tag)
# store-files
api.gsutil.upload(
bucket='%s-files' % project_id,
src=goma_temp_dir.join('goma', 'sha256'),
dst='',
recursive=True,
multithreaded=True,
)
api.gsutil.upload(
bucket='%s-toolchain-config' % project_id,
src=goma_temp_dir.join('descriptors'),
dst='%s/%s/' % (root, toolchain_tag),
recursive=True,
multithreaded=True,
)
api.file.remove('remove seq file', goma_temp_dir.join('seq'))
api.file.write_text('write seq file', goma_temp_dir.join('seq'), stamp)
api.gsutil.upload(
bucket='%s-toolchain-config' % project_id,
src=goma_temp_dir.join('seq'),
dst='%s/seq' % root,
recursive=False,
multithreaded=True,
)
def build_and_push_goma_toolchain(api, toolchain, version, domain, project,
root, platform, goma_temp_dir, timestamp):
toolchain_tag = '%s-%s-%s' % (toolchain, version, platform)
container_tag = api.url.join(domain, project, root, toolchain_tag)
with api.step.nest('copy goma docker configurations'):
dockerfile_platform = platform.replace('-', '_')
dockerfile = 'Dockerfile_fuchsia_toolchain_{}'.format(dockerfile_platform)
api.file.copy('copy dockerfile', api.resource(dockerfile),
goma_temp_dir.join(dockerfile))
setupfile = 'setup_fuchsia_{}_{}'.format(toolchain, dockerfile_platform)
# On CI bots, file.copy complains error 13 if the setup file already
# exists. Remove it first to avoid this issue.
api.file.remove('remove existing setup file', goma_temp_dir.join('setup'))
api.file.copy('copy toolchain setup file', api.resource(setupfile),
goma_temp_dir.join('setup'))
with api.context(cwd=goma_temp_dir):
api.docker.build(
dockerfile=dockerfile,
tags=[container_tag, container_tag + ':' + timestamp],
build_args=['TOOLCHAIN=%s' % toolchain])
api.docker('push', container_tag)
api.docker('push', container_tag + ':' + timestamp)
push_toolchain_to_goma(
api,
goma_temp_dir,
project,
root,
toolchain_tag,
container_tag,
stamp=timestamp)
def RunSteps(api, package, version, toolchain, platforms, domain, project, root,
update_goma_base_images):
preprocess_platforms = platforms[:]
if 'mac-amd64' in preprocess_platforms and 'linux-amd64' not in preprocess_platforms:
# To cross compile mac target on goma linux workers, both mac and linux
# toolchains are required.
preprocess_platforms.append('linux-amd64')
pkgs = api.cipd.EnsureFile()
for platform in preprocess_platforms:
pkgs.add_package(api.url.join(package, platform), version, platform)
toolchain_dir = api.path['cleanup'].join('toolchain')
with api.step.nest('ensure toolchain'):
api.cipd.ensure(toolchain_dir, pkgs)
timestamp = generate_time_stamp(api)
if version.startswith('git_revision:'):
version = version[len('git_revision:'):]
goma_temp_dir = api.path['cleanup'].join('goma')
ensure_gomatools(api, goma_temp_dir)
with api.step.nest('preprocess toolchain'):
if toolchain == 'clang':
dest_dir = api.path['cleanup'].join('toolchain_copy')
api.python(
'preprocess clang',
script=api.resource('preprocess_clang.py'),
args=[
toolchain_dir,
dest_dir,
] + ['--platform=%s' % platform for platform in preprocess_platforms],
)
toolchain_dir = dest_dir
for platform in preprocess_platforms:
api.archive.package(toolchain_dir.join(platform)).archive(
'pack toolchain package for %s' % platform,
goma_temp_dir.join('{}-{}.tgz'.format(toolchain, platform)))
ensure_goma_base_images(api, domain, project, root, timestamp,
update_goma_base_images)
api.docker(
'pull',
api.url.join(domain, project, root, 'base'),
step_name='pull base')
api.docker(
'tag',
api.url.join(domain, project, root, 'base'),
'base',
step_name='tag base')
try:
for platform in platforms:
with api.step.nest('build toolchain image for %s' % platform):
build_and_push_goma_toolchain(api, toolchain, version, domain, project,
root, platform, goma_temp_dir, timestamp)
finally:
api.docker.cleanup()
def GenTests(api):
default_properties = api.properties(
package='fuchsia/third_party/clang',
version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e',
toolchain='clang',
domain='gcr.io',
project='goma-fuchsia',
root='fuchsia_linux')
properties_missing_platform = api.properties(
package='fuchsia/third_party/clang/linux-amd64',
version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e',
toolchain='clang',
domain='gcr.io',
project='goma-fuchsia',
root='fuchsia_linux')
wrong_platform_properties = api.properties(
package='fuchsia/third_party/clang',
version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e',
toolchain='clang',
platforms=['linux_amd64'],
domain='gcr.io',
project='goma-fuchsia',
root='fuchsia_linux')
cross_compile_properties = api.properties(
package='fuchsia/third_party/clang',
version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e',
toolchain='clang',
platforms=['mac-amd64'],
domain='gcr.io',
project='goma-fuchsia',
root='fuchsia_linux')
yield api.test('default') + default_properties + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')
yield api.test(
'base image missing'
) + default_properties + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration'
) + api.step_data(
'ensure goma base images.check existence of gcr.io/goma-fuchsia/fuchsia_linux/runtime',
retcode=1
) + api.step_data(
'ensure goma base images.check existence of gcr.io/goma-fuchsia/fuchsia_linux/base',
retcode=1
) + api.step_data(
'ensure goma base images.check existence of gcr.io/goma-fuchsia/fuchsia_linux/remoteexec-platform',
retcode=1)
yield api.test(
'wrong_platform') + wrong_platform_properties + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')
yield api.test(
'cross_compile') + cross_compile_properties + api.buildbucket.try_build(
git_repo='https://fuchsia.googlesource.com/integration')