| #!/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 |