| # 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.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'), |
| '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'), |
| 'platform': |
| Property(kind=str, help='toolchain host platform', default=None), |
| '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) |
| # 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, |
| domain, |
| project_id, |
| root, |
| toolchain, |
| toolchain_version, |
| container_version='latest', |
| stamp=None): |
| """Push and register the toolchain docker image with Goma. |
| |
| E.g. push_toolchain_to_goma('gcr.io',\ |
| goma_temp_dir,\ |
| 'goma-fuchsia',\ |
| 'fuchsia_linux',\ |
| 'clang',\ |
| 'some_hash',\ |
| 'latest', |
| '20190909_122500') |
| will copy the toolchain binaries from docker image |
| 'gcr.io/goma-fuchsia/fuchsia_linux/clang-some_hash:latest' |
| to appropriate gs bucket of the Goma gcloud project and |
| register it with Goma server. |
| |
| Args: |
| * domain (str) - The domain name of the clontainer register. |
| * project_id (str) - The gcloud project ID. |
| * root (str) - The root path of all toolchains. E.g. 'fuchsia_linux'. |
| * toolchain (str) - The name of the toolchain. |
| * toolchain_version (str) - The version string of the toolchain. |
| Usually it is the commit hash. |
| * 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 |
| runtime_image = api.url.join(domain, project_id, root, 'runtime') |
| if stamp is None: |
| stamp = generate_time_stamp(api) # pragma: no cover |
| # extract-goma-files |
| toolchain_tag = '%s-%s' % (toolchain, toolchain_version) |
| toolchain_image = api.url.join(domain, project_id, root, toolchain_tag) |
| with api.docker.create(toolchain_image + ':' + |
| 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')) |
| |
| # pull-toolchain-runtime |
| api.docker('pull', runtime_image) |
| api.docker('tag', runtime_image, 'toolchain/runtime') |
| |
| 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' % toolchain_image, |
| '%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 RunSteps(api, package, version, toolchain, platform, domain, project, root, |
| update_goma_base_images): |
| assert package.endswith('${platform}') |
| # TODO: factor this out into a host_build recipe module. |
| host_platform = '%s-%s' % (api.platform.name.replace('win', 'windows'), { |
| 'intel': { |
| 32: '386', |
| 64: 'amd64', |
| }, |
| 'arm': { |
| 32: 'armv6', |
| 64: 'arm64', |
| }, |
| }[api.platform.arch][api.platform.bits]) |
| target_platform = platform or host_platform |
| pkgs = api.cipd.EnsureFile() |
| platforms = [target_platform] |
| if target_platform == 'mac-amd64': |
| # Cross compilation is required, packing |
| # linux toolchain as well. |
| platforms.append('linux-amd64') |
| |
| for item in platforms: |
| pkgs.add_package(package.replace('${platform}', item), version, item) |
| 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') |
| py_args = [ |
| str(toolchain_dir), |
| str(dest_dir), |
| ] |
| for item in platforms: |
| py_args.append('--platform=%s' % item) |
| api.python( |
| 'minimize clang', |
| script=api.resource('preprocess_clang.py'), |
| args=py_args, |
| ) |
| toolchain_dir = dest_dir |
| for item in platforms: |
| api.archive.package(toolchain_dir.join(item)).archive( |
| 'pack toolchain package', |
| goma_temp_dir.join('{}-{}.tgz'.format(toolchain, item))) |
| |
| toolchain_tag = '%s-%s-%s' % (toolchain, version, target_platform) |
| container_tag = api.url.join(domain, project, root, toolchain_tag) |
| |
| with api.step.nest('build toolchain image'): |
| try: |
| 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') |
| |
| with api.step.nest('copy goma docker configurations'): |
| dockerfile_platform = target_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, |
| domain, |
| project, |
| root, |
| toolchain, |
| version + '-' + target_platform, |
| stamp=timestamp) |
| finally: |
| api.docker.cleanup() |
| |
| |
| def GenTests(api): |
| default_properties = api.properties( |
| package='fuchsia/third_party/clang/${platform}', |
| 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/${platform}', |
| version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e', |
| toolchain='clang', |
| platform='linux_amd64', |
| domain='gcr.io', |
| project='goma-fuchsia', |
| root='fuchsia_linux') |
| |
| cross_compile_properties = api.properties( |
| package='fuchsia/third_party/clang/${platform}', |
| version='git_revision:56e9b608ad3a42d59cd4521b0eb495388261c67e', |
| toolchain='clang', |
| platform='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( |
| 'build toolchain image.ensure goma base images.check container existence', |
| retcode=1 |
| ) + api.step_data( |
| 'build toolchain image.ensure goma base images.check container existence (2)', |
| retcode=1 |
| ) + api.step_data( |
| 'build toolchain image.ensure goma base images.check container existence (3)', |
| retcode=1) |
| |
| yield api.test('missing_platform' |
| ) + properties_missing_platform + api.buildbucket.try_build( |
| git_repo='https://fuchsia.googlesource.com/integration' |
| ) + api.expect_exception('AssertionError') |
| |
| 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') |