Skip to content
Permalink
Browse files

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 zephyrproject-rtos/west#124

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
Signed-off-by: Marti Bolivar <marti.bolivar@nordicsemi.no>
  • Loading branch information...
carlescufi committed Jun 5, 2019
1 parent 49c4b1c commit 98980c6b7ce4a372fdc75802bb691012ad813d13
@@ -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,31 +62,60 @@ 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

(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
=================
@@ -9,7 +9,7 @@
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 @@ def do_add_parser(self, parser_adder):
# 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 @@ def do_run(self, args, remainder):
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 @@ def _setup_build_dir(self):
# 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 @@ def _setup_build_dir(self):

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 @@ def _setup_source_dir(self):
'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 @@ def _sanity_check(self):
# 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):
@@ -13,38 +13,70 @@
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.'''

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'] = ''

return fmt.format(**kwargs)

def find_build_dir(dir):
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
@@ -13,7 +13,8 @@
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

0 comments on commit 98980c6

Please sign in to comment.
You can’t perform that action at this time.