west: build: Configurable build folder format

Add the possibility of configuring the build folder format in west's
configuration system.
The build.dir-fmt configuration option controls how west will create
build folders when not specified, the following parameters are currently
accepted:

- board: The board name
- source_dir: The relative path from CWD to the source directory
- app: The name of the source directory

If CWD is below source_dir in the directory hierarchy then source_dir is
set to an empty string.

This means that if one sets:

[build]
dir-fmt = build/{board}/{source_dir}

Then when building samples/hello_world from zephyr's root for the
reel_board the build folder will be:

./build/reel_board/samples/hello_world

but when building it from inside the samples/hello_world folder it will
instead be:

./build/reel_board

Fixes https://github.com/zephyrproject-rtos/west/issues/124

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
Signed-off-by: Marti Bolivar <marti.bolivar@nordicsemi.no>
This commit is contained in:
Carles Cufi 2019-06-05 16:04:29 +02:00 committed by Carles Cufí
parent 49c4b1c303
commit 98980c6b7c
4 changed files with 138 additions and 56 deletions

View file

@ -32,19 +32,15 @@ Building: ``west build``
The ``build`` command helps you build Zephyr applications from source. You can
use :ref:`west config <west-config-cmd>` to configure its behavior.
This command attempts to "do what you mean" when run from a Zephyr application
source or build directory:
Its default behavior tries to "do what you mean":
- When you run ``west build`` in an existing build directory, the board, source
directory, etc. are obtained from the CMake cache, and that build directory
is re-compiled.
- If there is a Zephyr build directory named :file:`build` in your current
working directory, it is incrementally re-compiled. The same is true if you
run ``west build`` from a Zephyr build directory.
- The same is true if a Zephyr build directory named :file:`build` exists in
your current working directory.
- Otherwise, the source directory defaults to the current working directory, so
running ``west build`` from a Zephyr application's source directory compiles
it.
- Otherwise, if you run ``west build`` from a Zephyr application's source
directory and no build directory is found, a new one is created and the
application is compiled in it.
Basics
======
@ -66,15 +62,23 @@ exactly the same name you would supply to CMake if you were to invoke it with:
A build directory named :file:`build` will be created, and the application will
be compiled there after ``west build`` runs CMake to create a build system in
that directory. If you run ``west build`` with an existing build directory, the
application is incrementally re-compiled without re-running CMake (you can
force CMake to run again with ``--cmake``).
that directory. If ``west build`` finds an existing build directory, the
application is incrementally re-compiled there without re-running CMake. You
can force CMake to run again with ``--cmake``.
You don't need to use the ``--board`` option if you've already got an existing
build directory; ``west build`` can figure out the board from the CMake cache.
For new builds, the ``--board`` option, :envvar:`BOARD` environment variable,
or ``build.board`` configuration option are checked (in that order).
Examples
========
Here are some ``west build`` usage examples, grouped by area.
Setting a Default Board
-----------------------
To configure ``west build`` to build for the ``reel_board`` by default::
west config build.board reel_board
@ -82,15 +86,36 @@ To configure ``west build`` to build for the ``reel_board`` by default::
(You can use any other board supported by Zephyr here; it doesn't have to be
``reel_board``.)
To use another build directory, use ``--build-dir`` (or ``-d``)::
.. _west-building-dirs:
west build -b <BOARD> --build-dir path/to/build/directory
Setting Source and Build Directories
------------------------------------
To specify the application source directory explicitly, give its path as a
To set the application source directory explicitly, give its path as a
positional argument::
west build -b <BOARD> path/to/source/directory
To set the build directory explicitly, use ``--build-dir`` (or ``-d``)::
west build -b <BOARD> --build-dir path/to/build/directory
To change the default build directory from :file:`build`, use the
``build.dir-fmt`` configuration option. This lets you name build
directories using format strings, like this::
west config build.dir-fmt "build/{board}/{app}"
With the above, running ``west build -b reel_board samples/hello_world`` will
use build directory :file:`build/reel_board/hello_world`. See
:ref:`west-building-config` for more details on this option.
Controlling the Build System
----------------------------
There are several ways to control the build system generated and used by ``west
build``.
To specify the build system target to run, use ``--target`` (or ``-t``).
For example, on host platforms with QEMU, you can use the ``run`` target to
@ -125,7 +150,6 @@ To let west decide for you if a pristine build is needed, use ``-p auto``::
You can run ``west config build.pristine auto`` to make this setting
permanent.
.. _west-building-generator:
To add additional arguments to the CMake invocation performed by ``west
@ -159,6 +183,8 @@ To force a CMake re-run, use the ``--cmake`` (or ``--c``) option::
west build -c
.. _west-building-config:
Configuration Options
=====================
@ -179,6 +205,16 @@ You can :ref:`configure <west-config-cmd>` ``west build`` using these options.
* - ``build.board_warn``
- Boolean, default ``true``. If ``false``, disables warnings when
``west build`` can't figure out the target board.
* - ``build.dir-fmt``
- String, default ``build``. The build folder format string, used by
west whenever it needs to create or locate a build folder. The currently
available arguments are:
- ``board``: The board name
- ``source_dir``: The relative path from the current working directory
to the source directory. If the current working directory is inside
the source directory this will be set to an empty string.
- ``app``: The name of the source directory.
* - ``build.generator``
- String, default ``Ninja``. The `CMake Generator`_ to use to create a
build system. (To set a generator for a single build, see the
@ -219,10 +255,10 @@ To specify the build directory, use ``--build-dir`` (or ``-d``)::
west flash --build-dir path/to/build/directory
Since the build directory defaults to :file:`build`, if you do not specify
a build directory but a folder named :file:`build` is present, that will be
used, allowing you to flash from outside the :file:`build` folder with no
additional parameters.
If you don't specify the build directory, ``west flash`` searches for one in
:file:`build`, then the current working directory. If you set the
``build.dir-fmt`` configuration option (see :ref:`west-building-dirs`), ``west
flash`` searches there instead of :file:`build`.
Choosing a Runner
=================
@ -315,10 +351,10 @@ To specify the build directory, use ``--build-dir`` (or ``-d``)::
west debug --build-dir path/to/build/directory
west debugserver --build-dir path/to/build/directory
Since the build directory defaults to :file:`build`, if you do not specify
a build directory but a folder named :file:`build` is present, that will be
used, allowing you to debug from outside the :file:`build` folder with no
additional parameters.
If you don't specify the build directory, these commands search for one in
:file:`build`, then the current working directory. If you set the
``build.dir-fmt`` configuration option (see :ref:`west-building-dirs`), ``west
debug`` searches there instead of :file:`build`.
Choosing a Runner
=================

View file

@ -9,7 +9,7 @@ from west import log
from west.configuration import config
from zcmake import DEFAULT_CMAKE_GENERATOR, run_cmake, run_build, CMakeCache
from build_helpers import is_zephyr_build, find_build_dir, \
BUILD_DIR_DESCRIPTION
FIND_BUILD_DIR_DESCRIPTION
from zephyr_ext_common import Forceable
@ -85,8 +85,10 @@ class Build(Forceable):
# Hidden option for backwards compatibility
parser.add_argument('-s', '--source-dir', help=argparse.SUPPRESS)
parser.add_argument('-d', '--build-dir',
help=BUILD_DIR_DESCRIPTION +
" Always created if it doesn't exist.")
help='Build directory. ' +
FIND_BUILD_DIR_DESCRIPTION +
" Otherwise the default build directory is " +
"created and used.")
parser.add_argument('-t', '--target',
help='''Build system target to run''')
parser.add_argument('-p', '--pristine', choices=['auto', 'always',
@ -155,7 +157,7 @@ class Build(Forceable):
self.run_cmake = True
else:
self.run_cmake = True
self._setup_source_dir()
self.source_dir = self._find_source_dir()
self._sanity_check()
board, origin = self._find_board()
@ -218,7 +220,15 @@ class Build(Forceable):
# Initialize build_dir and created_build_dir attributes.
# If we created the build directory, we must run CMake.
log.dbg('setting up build directory', level=log.VERBOSE_EXTREME)
build_dir = find_build_dir(self.args.build_dir)
# The CMake Cache has not been loaded yet, so this is safe
board, origin = self._find_board()
source_dir = self._find_source_dir()
app = os.path.split(source_dir)[1]
build_dir = find_build_dir(self.args.build_dir, board=board,
source_dir=source_dir, app=app)
if not build_dir:
log.die('Unable to determine a default build folder. Check '
'your build.dir-fmt configuration option')
if os.path.exists(build_dir):
if not os.path.isdir(build_dir):
@ -231,7 +241,7 @@ class Build(Forceable):
self.build_dir = build_dir
def _setup_source_dir(self):
def _find_source_dir(self):
# Initialize source_dir attribute, either from command line argument,
# implicitly from the build directory's CMake cache, or using the
# default (current working directory).
@ -248,7 +258,7 @@ class Build(Forceable):
'please give a source_dir')
else:
source_dir = os.getcwd()
self.source_dir = os.path.abspath(source_dir)
return os.path.abspath(source_dir)
def _sanity_check_source_dir(self):
if self.source_dir == self.build_dir:
@ -338,7 +348,7 @@ class Build(Forceable):
# invalidated, reset to CWD and re-run the basic tests.
if ((boards_mismatched and not apps_mismatched) and
(not source_abs and cached_abs)):
self._setup_source_dir()
self.source_dir = self._find_source_dir()
self._sanity_check_source_dir()
def _run_cmake(self, board, origin, cmake_opts):

View file

@ -13,6 +13,8 @@ See build.py for the build command itself.
import zcmake
import os
from west import log
from west.configuration import config
from west.util import escapes_directory
DEFAULT_BUILD_DIR = 'build'
'''Name of the default Zephyr build directory.'''
@ -20,31 +22,61 @@ DEFAULT_BUILD_DIR = 'build'
DEFAULT_CMAKE_GENERATOR = 'Ninja'
'''Name of the default CMake generator.'''
BUILD_DIR_DESCRIPTION = '''\
Build directory. If not given, {}/ is used; otherwise if the current directory
is a Zephyr build directory, it is used.'''.format(DEFAULT_BUILD_DIR)
FIND_BUILD_DIR_DESCRIPTION = '''\
If not given, the default build directory ({}/ unless the
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):
# 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'] = ''
def find_build_dir(dir):
return fmt.format(**kwargs)
def find_build_dir(dir, **kwargs):
'''Heuristic for finding a build directory.
If the given argument is truthy, it is returned. Otherwise if the
DEFAULT_BUILD_DIR is a build directory, it is returned. Next, if
the current working directory is a build directory, it is
returned. Finally, DEFAULT_BUILD_DIR is returned.'''
The default build directory is computed by reading the build.dir-fmt
configuration option, defaulting to DEFAULT_BUILD_DIR if not set. It might
be None if the build.dir-fmt configuration option is set but cannot be
resolved.
If the given argument is truthy, it is returned. Otherwise, if
the default build folder is a build directory, it is returned.
Next, if the current working directory is a build directory, it is
returned. Finally, the default build directory is returned (may be None).
'''
if dir:
build_dir = dir
else:
cwd = os.getcwd()
default = os.path.join(cwd, DEFAULT_BUILD_DIR)
if is_zephyr_build(default):
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
if default and is_zephyr_build(default):
build_dir = default
elif is_zephyr_build(cwd):
build_dir = cwd
else:
build_dir = DEFAULT_BUILD_DIR
return os.path.abspath(build_dir)
build_dir = default
log.dbg('build dir: {}'.format(build_dir), level=log.VERBOSE_EXTREME)
if build_dir:
return os.path.abspath(build_dir)
else:
return None
def is_zephyr_build(path):
'''Return true if and only if `path` appears to be a valid Zephyr

View file

@ -13,7 +13,8 @@ import textwrap
from west import cmake
from west import log
from west import util
from build_helpers import find_build_dir, is_zephyr_build
from build_helpers import find_build_dir, is_zephyr_build, \
FIND_BUILD_DIR_DESCRIPTION
from west.commands import CommandContextError
from runners import get_runner_cls, ZephyrBinaryRunner
@ -43,10 +44,8 @@ def add_parser_common(parser_adder, command):
group = parser.add_argument_group(title='General Options')
group.add_argument('-d', '--build-dir',
help='''Build directory to obtain runner information
from. If not given, this command tries to use build/
and then the current working directory, in that
order.''')
help='Build directory to obtain runner information ' +
'from. ' + FIND_BUILD_DIR_DESCRIPTION)
group.add_argument('-c', '--cmake-cache',
help='''Path to CMake cache file containing runner
configuration (this is generated by the Zephyr
@ -130,12 +129,17 @@ def _build_dir(args, die_if_none=True):
dir = find_build_dir(None)
if is_zephyr_build(dir):
if dir and is_zephyr_build(dir):
return dir
elif die_if_none:
log.die('--build-dir was not given, and neither {} '
'nor {} are zephyr build directories.'.
format(getcwd(), dir))
msg = '--build-dir was not given, '
if dir:
msg = msg + 'and neither {} nor {} are zephyr build directories.'
else:
msg = msg + ('{} is not a build directory and the default build '
'directory cannot be determined. Check your '
'build.dir-fmt configuration option')
log.die(msg.format(getcwd(), dir))
else:
return None