| # 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.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) |
| 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') |