# Copyright (C) 2021 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Recipe for building Perfetto.""" from recipe_engine.recipe_api import Property DEPS = [ 'depot_tools/gsutil', 'recipe_engine/buildbucket', 'recipe_engine/cipd', 'recipe_engine/context', 'recipe_engine/file', 'recipe_engine/path', 'recipe_engine/platform', 'recipe_engine/properties', 'recipe_engine/raw_io', 'recipe_engine/step', 'macos_sdk', 'windows_sdk', ] PROPERTIES = { 'repository': Property( kind=str, default='https://android.googlesource.com/platform/external/perfetto' ), } ARTIFACTS = [ { 'name': 'trace_processor_shell' }, { 'name': 'trace_to_text', # trace_to_text is really a host exeutable, doesn't make sense to build # it when cross-compiling . 'exclude_platforms': [ 'android-arm', 'android-arm64', 'android-x86', 'android-x64', 'linux-arm', 'linux-arm64' ] }, { 'name': 'tracebox', 'exclude_platforms': ['windows-amd64'] }, { 'name': 'perfetto' }, { 'name': 'traced' }, { 'name': 'traced_probes', 'exclude_platforms': ['windows-amd64'] }, ] class BuildContext: def __init__(self, src_dir): self.src_dir = src_dir self.git_revision = None self.maybe_git_tag = None def GnArgs(platform): (os, cpu) = platform.split('-') base_args = 'is_debug=false monolithic_binaries=true' if os not in ('android', 'linux', 'mac'): return base_args # No cross-compiling on Windows. cpu = 'x64' if cpu == 'amd64' else cpu # GN calls it "x64". return base_args + ' target_os="{}" target_cpu="{}"'.format(os, cpu) def UploadArtifact(api, ctx, platform, out_dir, artifact): exclude_platforms = artifact.get('exclude_platforms', []) if platform in exclude_platforms: return # We want to use the stripped binaries except on Windows where we don't generate # them. exe_dir = out_dir if api.platform.is_win else out_dir.join('stripped') # Compute the exact artifact path gcs_upload_dir = ctx.maybe_git_tag if ctx.maybe_git_tag else ctx.git_revision artifact_ext = artifact['name'] + ('.exe' if api.platform.is_win else '') source_path = exe_dir.join(artifact_ext) # Upload to GCS bucket. gcs_target_path = '{}/{}/{}'.format(gcs_upload_dir, platform, artifact_ext) api.gsutil.upload(source_path, 'perfetto-luci-artifacts', gcs_target_path) # Create the CIPD package definition from the artifact path. cipd_pkg_name = 'perfetto/{}/{}'.format(artifact['name'], platform) pkg_def = api.cipd.PackageDefinition( package_name=cipd_pkg_name, package_root=exe_dir) pkg_def.add_file(source_path) # Actually build the CIPD pakcage cipd_pkg_file_name = '{}-{}.cipd'.format(artifact['name'], platform) cipd_pkg_file = api.path['cleanup'].join(cipd_pkg_file_name) api.cipd.build_from_pkg( pkg_def=pkg_def, output_package=cipd_pkg_file, ) # If we have a git tag, add that to the CIPD tags. tags = { 'git_revision': ctx.git_revision, } if ctx.maybe_git_tag: tags['git_tag'] = ctx.maybe_git_tag # Upload the package and regisiter with the 'latest' tag. api.cipd.register( package_name=cipd_pkg_name, package_path=cipd_pkg_file, refs=['latest'], tags=tags, ) def BuildForPlatform(api, ctx, platform): out_dir = ctx.src_dir.join('out', platform) # Build Perfetto. # There should be no need for internet access here. with api.context(cwd=ctx.src_dir), api.macos_sdk(), api.windows_sdk(): targets = [ x['name'] for x in ARTIFACTS if platform not in x.get('exclude_platforms', []) ] args = GnArgs(platform) api.step('gn gen', ['python3', 'tools/gn', 'gen', out_dir, '--args={}'.format(args)]) api.step('ninja', ['python3', 'tools/ninja', '-C', out_dir] + targets) # Upload stripped artifacts using gsutil if we're on the official builder. if 'official' not in api.buildbucket.builder_id.builder: return with api.step.nest('Artifact upload'), api.context(cwd=ctx.src_dir): for artifact in ARTIFACTS: UploadArtifact(api, ctx, platform, out_dir, artifact) def RunSteps(api, repository): builder_cache_dir = api.path['cache'].join('builder') src_dir = builder_cache_dir.join('perfetto') # Crate the context we use in all the building stages. ctx = BuildContext(src_dir) # Fetch the Perfetto repo. with api.step.nest('git'), api.context(infra_steps=True): api.file.ensure_directory('ensure source dir', src_dir) api.step('init', ['git', 'init', src_dir]) with api.context(cwd=src_dir): build_input = api.buildbucket.build_input ref = ( build_input.gitiles_commit.ref if build_input.gitiles_commit else 'refs/heads/master') # Fetch tags so `git describe` works. api.step('fetch', ['git', 'fetch', '--tags', repository, ref]) api.step('checkout', ['git', 'checkout', 'FETCH_HEAD']) # Store information about the git revision and the tag if available. ctx.git_revision = api.step( 'rev-parse', ['git', 'rev-parse', 'HEAD'], stdout=api.raw_io.output()).stdout.strip() ctx.maybe_git_tag = ref.replace( 'refs/tags/', '') if ref.startswith('refs/tags/') else None # Pull all deps here. with api.context(cwd=src_dir, infra_steps=True): extra_args = [] if 'android' in api.buildbucket.builder_id.builder: extra_args += ['--android'] elif api.platform.is_linux: # Pull the cross-toolchains for building for linux-arm{,64}. extra_args += ['--linux-arm'] api.step('build-deps', ['python3', 'tools/install-build-deps'] + extra_args) if api.platform.is_win: BuildForPlatform(api, ctx, 'windows-amd64') elif api.platform.is_mac: with api.step.nest('mac-amd64'): BuildForPlatform(api, ctx, 'mac-amd64') with api.step.nest('mac-arm64'): BuildForPlatform(api, ctx, 'mac-arm64') elif 'android' in api.buildbucket.builder_id.builder: with api.step.nest('android-arm'): BuildForPlatform(api, ctx, 'android-arm') with api.step.nest('android-arm64'): BuildForPlatform(api, ctx, 'android-arm64') with api.step.nest('android-x86'): BuildForPlatform(api, ctx, 'android-x86') with api.step.nest('android-x64'): BuildForPlatform(api, ctx, 'android-x64') elif api.platform.is_linux: with api.step.nest('linux-amd64'): BuildForPlatform(api, ctx, 'linux-amd64') with api.step.nest('linux-arm'): BuildForPlatform(api, ctx, 'linux-arm') with api.step.nest('linux-arm64'): BuildForPlatform(api, ctx, 'linux-arm64') def GenTests(api): for target in ('android', 'linux', 'mac', 'win'): host = 'linux' if target == 'android' else target yield (api.test('ci_' + target) + api.platform.name(host) + api.buildbucket.ci_build( project='perfetto', builder='perfetto-official-builder-%s' % target, git_repo='android.googlesource.com/platform/external/perfetto', )) yield (api.test('ci_tag') + api.platform.name('linux') + api.buildbucket.ci_build( project='perfetto', builder='official', git_repo='android.googlesource.com/platform/external/perfetto', git_ref='refs/tags/v13.0')) yield (api.test('unofficial') + api.platform.name('linux') + api.buildbucket.ci_build( project='perfetto', git_repo='android.googlesource.com/platform/external/perfetto'))