|  | # Copyright 2018 (c) Foundries.io. | 
|  | # | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | '''Common definitions for building Zephyr applications. | 
|  |  | 
|  | This provides some default settings and convenience wrappers for | 
|  | building Zephyr applications needed by multiple commands. | 
|  |  | 
|  | See build.py for the build command itself. | 
|  | ''' | 
|  |  | 
|  | import os | 
|  | import sys | 
|  | from pathlib import Path | 
|  |  | 
|  | import zcmake | 
|  | from west import log | 
|  | from west.configuration import config | 
|  | from west.util import escapes_directory | 
|  |  | 
|  | # Domains.py must be imported from the pylib directory, since | 
|  | # twister also uses the implementation | 
|  | script_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) | 
|  | sys.path.insert(0, os.path.join(script_dir, "pylib/build_helpers/")) | 
|  | from domains import Domains  # noqa: E402 | 
|  |  | 
|  | DEFAULT_BUILD_DIR = 'build' | 
|  | '''Name of the default Zephyr build directory.''' | 
|  |  | 
|  | DEFAULT_CMAKE_GENERATOR = 'Ninja' | 
|  | '''Name of the default CMake generator.''' | 
|  |  | 
|  | FIND_BUILD_DIR_DESCRIPTION = f'''\ | 
|  | If the build directory is not given, the default is {DEFAULT_BUILD_DIR}/ unless the | 
|  | build.dir-fmt configuration variable is set. The current directory is | 
|  | checked after that. If either is a Zephyr build directory, it is used. | 
|  | ''' | 
|  |  | 
|  | def _resolve_build_dir(fmt, guess, cwd, **kwargs): | 
|  | # Remove any None values, we do not want 'None' as a string | 
|  | kwargs = {k: v for k, v in kwargs.items() if v is not None} | 
|  | # Check if source_dir is below cwd first | 
|  | source_dir = kwargs.get('source_dir') | 
|  | if source_dir: | 
|  | if escapes_directory(cwd, source_dir): | 
|  | kwargs['source_dir'] = os.path.relpath(source_dir, cwd) | 
|  | else: | 
|  | # no meaningful relative path possible | 
|  | kwargs['source_dir'] = '' | 
|  |  | 
|  | try: | 
|  | return fmt.format(**kwargs) | 
|  | except KeyError: | 
|  | if not guess: | 
|  | return None | 
|  |  | 
|  | # Guess the build folder by iterating through all sub-folders from the | 
|  | # root of the format string and trying to resolve. If resolving fails, | 
|  | # proceed to iterate over subfolders only if there is a single folder | 
|  | # present on each iteration. | 
|  | parts = Path(fmt).parts | 
|  | b = Path('.') | 
|  | for p in parts: | 
|  | # default to cwd in the first iteration | 
|  | curr = b | 
|  | b = b.joinpath(p) | 
|  | try: | 
|  | # if fmt is an absolute path, the first iteration will always | 
|  | # resolve '/' | 
|  | b = Path(str(b).format(**kwargs)) | 
|  | except KeyError: | 
|  | # Missing key, check sub-folders and match if a single one exists | 
|  | while True: | 
|  | if not curr.exists(): | 
|  | return None | 
|  | dirs = [f for f in curr.iterdir() if f.is_dir()] | 
|  | if len(dirs) != 1: | 
|  | return None | 
|  | curr = dirs[0] | 
|  | if is_zephyr_build(str(curr)): | 
|  | return str(curr) | 
|  | return str(b) | 
|  |  | 
|  | def find_build_dir(dir, guess=False, **kwargs): | 
|  | '''Heuristic for finding a build directory. | 
|  | If `dir` is specified, this directory is returned as the build directory. | 
|  | Otherwise, the default build directory is determined according to the | 
|  | following priorities: | 
|  | 1. Resolved `build.dir-fmt` configuration option (all {args} are resolvable). | 
|  | Return this directory, if it is an already existing build directory. | 
|  | 2. The current working directory, if it is an existing build directory. | 
|  | 3. Resolved `build.dir-fmt` configuration option, no matter if it is an | 
|  | already existing build directory. | 
|  | 4. DEFAULT_BUILD_DIR | 
|  | ''' | 
|  |  | 
|  | build_dir = dir | 
|  | cwd = os.getcwd() | 
|  | dir_fmt = config.get('build', 'dir-fmt', fallback=None) | 
|  | if dir_fmt: | 
|  | log.dbg(f'config dir-fmt: {dir_fmt}', level=log.VERBOSE_EXTREME) | 
|  | dir_fmt = _resolve_build_dir(dir_fmt, guess, cwd, **kwargs) | 
|  | if not build_dir and is_zephyr_build(dir_fmt): | 
|  | build_dir = dir_fmt | 
|  | if not build_dir and is_zephyr_build(cwd): | 
|  | build_dir = cwd | 
|  | if not build_dir and dir_fmt: | 
|  | build_dir = dir_fmt | 
|  | if not build_dir: | 
|  | build_dir = DEFAULT_BUILD_DIR | 
|  | log.dbg(f'build dir: {build_dir}', level=log.VERBOSE_EXTREME) | 
|  | return os.path.abspath(build_dir) | 
|  |  | 
|  | def is_zephyr_build(path): | 
|  | '''Return true if and only if `path` appears to be a valid Zephyr | 
|  | build directory. | 
|  |  | 
|  | "Valid" means the given path is a directory which contains a CMake | 
|  | cache with a 'ZEPHYR_BASE' or 'ZEPHYR_TOOLCHAIN_VARIANT' variable. | 
|  |  | 
|  | (The check for ZEPHYR_BASE introduced sometime after Zephyr 2.4 to | 
|  | fix https://github.com/zephyrproject-rtos/zephyr/issues/28876; we | 
|  | keep support for the second variable around for compatibility with | 
|  | versions 2.2 and earlier, which didn't have ZEPHYR_BASE in cache. | 
|  | The cached ZEPHYR_BASE was added in | 
|  | https://github.com/zephyrproject-rtos/zephyr/pull/23054.) | 
|  | ''' | 
|  | if not path: | 
|  | return False | 
|  |  | 
|  | try: | 
|  | cache = zcmake.CMakeCache.from_build_dir(path) | 
|  | except FileNotFoundError: | 
|  | cache = {} | 
|  |  | 
|  | if 'ZEPHYR_BASE' in cache or 'ZEPHYR_TOOLCHAIN_VARIANT' in cache: | 
|  | log.dbg(f'{path} is a zephyr build directory', | 
|  | level=log.VERBOSE_EXTREME) | 
|  | return True | 
|  |  | 
|  | log.dbg(f'{path} is NOT a valid zephyr build directory', | 
|  | level=log.VERBOSE_EXTREME) | 
|  | return False | 
|  |  | 
|  |  | 
|  | def load_domains(path): | 
|  | '''Load domains from a domains.yaml. | 
|  |  | 
|  | If domains.yaml is not found, then a single 'app' domain referring to the | 
|  | top-level build folder is created and returned. | 
|  | ''' | 
|  | domains_file = Path(path) / 'domains.yaml' | 
|  |  | 
|  | if not domains_file.is_file(): | 
|  | return Domains.from_yaml(f'''\ | 
|  | default: app | 
|  | build_dir: {path} | 
|  | domains: | 
|  | - name: app | 
|  | build_dir: {path} | 
|  | flash_order: | 
|  | - app | 
|  | ''') | 
|  |  | 
|  | return Domains.from_file(domains_file) |