From 41f1f648f63928f6691abe3384bbc2ccb0fd2075 Mon Sep 17 00:00:00 2001 From: Carles Cufi Date: Mon, 17 Jun 2019 11:47:11 +0200 Subject: [PATCH] west: runners: Guess build folder When using a build folder format with build.dir-fmt that includes any parameters that need resolving, the west runners cannot find the folder since the required information (board, source dir or app) is not available. Add a very simple heuristic to support the case where a build folder starts with a hardcoded prefix (for example 'build/') and a single build is present under that prefix. The heuristic is gated behind a new configuration option: build.guess-dir Signed-off-by: Carles Cufi --- doc/guides/west/build-flash-debug.rst | 10 ++++++ scripts/west_commands/build_helpers.py | 44 ++++++++++++++++++++------ scripts/west_commands/run_common.py | 5 ++- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/doc/guides/west/build-flash-debug.rst b/doc/guides/west/build-flash-debug.rst index 0a2560069e..655a364467 100644 --- a/doc/guides/west/build-flash-debug.rst +++ b/doc/guides/west/build-flash-debug.rst @@ -219,6 +219,16 @@ You can :ref:`configure ` ``west build`` using these options. - String, default ``Ninja``. The `CMake Generator`_ to use to create a build system. (To set a generator for a single build, see the :ref:`above example `) + * - ``build.guess-dir`` + - String, instructs west whether to try to guess what build folder to use + when ``build.dir-fmt`` is in use and not enough information is available + to resolve the build folder name. Can take these values: + + - ``never`` (default): Never try to guess, bail out instead and + require the user to provide a build folder with ``-d``. + - ``runners``: Try to guess the folder when using any of the 'runner' + commands. These are typically all commands that invoke an external + tool, such as ``flash`` and ``debug``. * - ``build.pristine`` - String. Controls the way in which ``west build`` may clean the build folder before building. Can take the following values: diff --git a/scripts/west_commands/build_helpers.py b/scripts/west_commands/build_helpers.py index 5cc0f6ac51..5281dfdacc 100644 --- a/scripts/west_commands/build_helpers.py +++ b/scripts/west_commands/build_helpers.py @@ -12,6 +12,7 @@ See build.py for the build command itself. import zcmake import os +from pathlib import Path from west import log from west.configuration import config from west.util import escapes_directory @@ -28,7 +29,7 @@ build.dir-fmt configuration variable is set) and the current directory are checked, in that order. If one is a Zephyr build directory, it is used. '''.format(DEFAULT_BUILD_DIR) -def _resolve_build_dir(fmt, cwd, **kwargs): +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 @@ -40,9 +41,38 @@ def _resolve_build_dir(fmt, cwd, **kwargs): # no meaningful relative path possible kwargs['source_dir'] = '' - return fmt.format(**kwargs) + try: + return fmt.format(**kwargs) + except KeyError: + if not guess: + return None -def find_build_dir(dir, **kwargs): + # 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: + 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. The default build directory is computed by reading the build.dir-fmt @@ -60,12 +90,8 @@ def find_build_dir(dir, **kwargs): else: cwd = os.getcwd() default = config.get('build', 'dir-fmt', fallback=DEFAULT_BUILD_DIR) - try: - default = _resolve_build_dir(default, cwd, **kwargs) - log.dbg('config dir-fmt: {}'.format(default), - level=log.VERBOSE_EXTREME) - except KeyError: - default = None + default = _resolve_build_dir(default, guess, cwd, **kwargs) + log.dbg('config dir-fmt: {}'.format(default), level=log.VERBOSE_EXTREME) if default and is_zephyr_build(default): build_dir = default elif is_zephyr_build(cwd): diff --git a/scripts/west_commands/run_common.py b/scripts/west_commands/run_common.py index 9b8888460d..ea86e3927f 100644 --- a/scripts/west_commands/run_common.py +++ b/scripts/west_commands/run_common.py @@ -19,6 +19,7 @@ from west import util from build_helpers import find_build_dir, is_zephyr_build, \ FIND_BUILD_DIR_DESCRIPTION from west.commands import CommandError +from west.configuration import config from runners import get_runner_cls, ZephyrBinaryRunner, MissingProgram @@ -170,7 +171,9 @@ def _build_dir(args, die_if_none=True): if args.build_dir: return args.build_dir - dir = find_build_dir(None) + guess = config.get('build', 'guess-dir', fallback='never') + guess = True if guess == 'runners' else False + dir = find_build_dir(None, guess) if dir and is_zephyr_build(dir): return dir