blob: f81d1ecab7465930ac535721cfa34cad1aba9b77 [file] [log] [blame]
#!/usr/bin/env python
# Copyright 2022 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.
"""Watch build config dataclasses."""
from dataclasses import dataclass, field
from pathlib import Path
import shlex
from typing import List, Optional, TYPE_CHECKING
if TYPE_CHECKING:
from pw_build.project_builder_prefs import ProjectBuilderPrefs
class UnknownBuildSystem(Exception):
"""Exception for requesting build systems other than make, ninja, or bazel.
"""
@dataclass
class BuildCommand:
"""Store details of a single build step."""
build_dir: Path
build_system_command: Optional[str] = None
build_system_extra_args: List[str] = field(default_factory=list)
targets: List[str] = field(default_factory=list)
command_string: str = ''
def __post_init__(self) -> None:
self._expanded_args: List[str] = shlex.split(self.command_string)
def _get_build_system_args(self) -> List[str]:
assert self.build_system_command
if (self.build_system_command.endswith('make')
or self.build_system_command.endswith('ninja')):
return ['-C', str(self.build_dir), *self.targets]
if self.build_system_command.endswith('bazel'):
return ['--output_base', str(self.build_dir), *self.targets]
raise UnknownBuildSystem(
f'\n\nUnknown build system command "{self.build_system_command}" '
f'for build directory "{self.build_dir}".\n'
'Supported commands: ninja, bazel, make')
def get_args(
self,
additional_build_args: Optional[List[str]] = None,
) -> List[str]:
if self.build_system_command:
extra_args = []
extra_args.extend(self.build_system_extra_args)
if additional_build_args:
extra_args.extend(additional_build_args)
command = [
self.build_system_command, *extra_args,
*self._get_build_system_args()
]
return command
return self._expanded_args
def __str__(self) -> str:
return ' '.join(shlex.quote(arg) for arg in self.get_args())
@dataclass
class BuildRecipe:
build_dir: Path
steps: List[BuildCommand]
build_system_command: Optional[str] = None
title: Optional[str] = None
@property
def display_name(self) -> str:
if self.title:
return self.title
return str(self.build_dir)
def targets(self) -> List[str]:
return list(
set(target for step in self.steps for target in step.targets))
def __str__(self) -> str:
message = f"{self.display_name}"
targets = self.targets()
if targets:
message = '{} ({})'.format(message, ' '.join(self.targets()))
return message
def create_build_recipes(prefs: 'ProjectBuilderPrefs') -> List[BuildRecipe]:
"""Create a list of BuildRecipes from ProjectBuilderPrefs."""
build_recipes: List[BuildRecipe] = []
if prefs.run_commands:
for command_str in prefs.run_commands:
build_recipes.append(
BuildRecipe(
build_dir=Path.cwd(),
steps=[
BuildCommand(Path.cwd(), command_string=command_str)
],
title=command_str,
))
for build_dir, targets in prefs.build_directories.items():
steps: List[BuildCommand] = []
build_path = Path(build_dir)
build_system_command, build_system_extra_args = (
prefs.build_system_commands(build_dir))
if not targets:
targets = []
steps.append(
BuildCommand(build_dir=build_path,
build_system_command=build_system_command,
build_system_extra_args=build_system_extra_args,
targets=targets))
build_recipes.append(
BuildRecipe(build_dir=build_path,
steps=steps,
build_system_command=build_system_command))
return build_recipes