blob: 6f0262402fc03cae1ad60aed1390e504c48a81dd [file] [log] [blame]
# Copyright 2021 The Pigweed Authors
#
# 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
#
# https://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.
"""Calls to build code."""
from __future__ import annotations
import dataclasses
from typing import TYPE_CHECKING
from PB.recipe_modules.pigweed.build import options as options_pb2
from recipe_engine import config_types, recipe_api
if TYPE_CHECKING: # pragma: no cover
from recipe_engine import step_data
@dataclasses.dataclass
class BuildContext:
_api: recipe_api.RecipeApi
checkout_root: config_types.Path
root: config_types.Path
options: options_pb2.Options
_Command = list[str | config_types.Path]
class BuildApi(recipe_api.RecipeApi):
"""Calls to build code."""
BuildContext = BuildContext
CAS_DIGEST_PROPERTY_NAME: str = 'cas_build_digest'
def create(
self,
checkout_root: config_types.Path,
options: options_pb2.Options,
root: config_types.Path | None = None,
) -> BuildContext:
if not root:
root = checkout_root / 'out'
return BuildContext(self.m, checkout_root, root, options)
def __call__(self, ctx: BuildContext) -> None:
self.install_packages(ctx)
self.gn_gen(ctx)
self.ninja(ctx)
@recipe_api.ignore_warnings('recipe_engine/PYTHON2_DEPRECATED')
def install_packages(self, ctx: BuildContext) -> None:
if not ctx.options.packages:
return
with self.m.step.nest('install packages'):
cmd: _Command = ['python', '-m', 'pw_cli', 'package', 'install']
for package in ctx.options.packages:
self.m.step(package, cmd + [package])
def gn_gen(self, ctx: BuildContext) -> None:
cmd: _Command = ['gn', 'gen']
for gn_arg in ctx.options.gn_args:
cmd.append(f'--args={gn_arg}')
# Infrequently needed but harmless to always add this.
cmd.append('--export-compile-commands')
cmd.append(ctx.root)
with self.m.context(cwd=ctx.checkout_root):
self.m.step('gn gen', cmd)
def get_gn_args(
self,
ctx: BuildContext,
test_data=None,
) -> dict[str, int | str]:
context_kwargs = {'cwd': ctx.checkout_root} if ctx.checkout_root else {}
with self.m.context(**context_kwargs):
cmd: _Command = ['gn', 'args', ctx.root, '--list', '--json']
args = self.m.step(
'all gn args',
cmd,
stdout=self.m.json.output(),
step_test_data=lambda: self.m.json.test_api.output_stream(
test_data or []
),
).stdout
return {x['name']: x for x in args or ()}
def ninja(self, ctx: BuildContext) -> None:
cmd: _Command = ['ninja', '-C', ctx.root]
cmd.extend(ctx.options.ninja_targets)
with self.m.default_timeout():
self.m.step('ninja', cmd)
def archive_to_cas(self, ctx: BuildContext) -> str:
# TODO: b/234879756 - Only archive necessary files.
with self.m.step.nest('archive to cas') as pres:
digest: str = self.m.cas.archive('archive', ctx.root, ctx.root)
pres.properties[self.CAS_DIGEST_PROPERTY_NAME] = digest
return digest
def download_from_cas(
self, ctx: BuildContext, digest: str
) -> step_data.StepData:
return self.m.cas.download('download from cas', digest, ctx.root)