Skip to content

Commit

Permalink
west: build: Add new pristine cmd-line and config option
Browse files Browse the repository at this point in the history
Add a new command-line and build config option, `pristine`, that the
user can pass to `west build` or set in its configuration file(s) in
order to automatically trigger a pristine build on every build or
whenever west considers it required.

The option can take the following values:

- never: Never run the target
- always: Always run the pristine target before building
- auto: Run the pristine target when required

With `auto`, the pristine target will be run when running
west with an existing build folder containing a build system and:

- Selecting a different board from the one currently in the build system
- Selecting a different application from the one currently in the build
  system

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
  • Loading branch information
carlescufi authored and nashif committed Apr 17, 2019
1 parent 3c6584d commit b7c7591
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 16 deletions.
12 changes: 12 additions & 0 deletions doc/guides/west/build-flash-debug.rst
Expand Up @@ -73,6 +73,18 @@ Additionally you can specify the build system target using the ``--target``
You can list all targets with ``ninja help`` (or ``west build -t help``) inside
the build folder.

A clean build can be triggered by using the ``--pristine`` (or ``-p``) option.
This is particularly handy if you want to switch source dirs or boards without
using a different build dir::

west build -b qemu_x86 samples/philosophers
west build -p -b reel_board samples/hello_world

If you are unsure about whether the command-line parameters you supply to
``west build`` require a clean build you can let west decide for you by using
the ``auto`` setting in the ``--pristine`` option::

west build -p auto -b reel_board samples/hello_world

Finally, you can add additional arguments to the CMake invocation performed by
``west build`` by supplying additional parameters (after a ``--``) to the
Expand Down
26 changes: 26 additions & 0 deletions doc/guides/west/config.rst
Expand Up @@ -150,3 +150,29 @@ commands.
environment overrides the value of the ``zephyr.base`` configuration
option. If set to ``"configfile"``, the configuration option wins
instead.

Zephyr Extension Commands Configuration Options
-----------------------------------------------

The following table documents configuration options supported by zephyr's
extension commands (found in :file:`scripts/west_commands`).
.. NOTE: docs authors: keep this table sorted by section, then option.
.. list-table::
:widths: 10 30
:header-rows: 1

* - Option
- Description
* - ``build.pristine``
- String. Controls the way in which ``west build`` may run the ``pristine``
target before building. Can take the following values:

- ``never`` (default): Never automatically run the ``pristine`` target.
- ``auto``: ``west build`` will automatically run the ``pristine``
target before building, if a build system is present and the build
will fail otherwise (e.g. the user has specified a different board or
application from the one previously used to make the build
directory).
- ``always``: Always run the ``pristine`` target before building, if
a build system is present.
96 changes: 80 additions & 16 deletions scripts/west_commands/build.py
Expand Up @@ -7,6 +7,7 @@

from west import log
from west import cmake
from west.configuration import config
from west.build import DEFAULT_CMAKE_GENERATOR, is_zephyr_build

from zephyr_ext_common import find_build_dir, Forceable, BUILD_DIR_DESCRIPTION
Expand Down Expand Up @@ -52,6 +53,10 @@
cmake_opt Extra options to pass to CMake; implies -c
'''

class AlwaysIfMissing(argparse.Action):

def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values or 'always')

class Build(Forceable):

Expand Down Expand Up @@ -89,7 +94,7 @@ def do_add_parser(self, parser_adder):
formatter_class=argparse.RawDescriptionHelpFormatter,
description=self.description,
usage='''west build [-h] [-b BOARD] [-d BUILD_DIR]
[-t TARGET] [-c] [-f] [source_dir]
[-t TARGET] [-p {auto, always, never}] [-c] [-f] [source_dir]
-- [cmake_opt [cmake_opt ...]]''')

# Remember to update scripts/west-completion.bash if you add or remove
Expand All @@ -106,6 +111,17 @@ def do_add_parser(self, parser_adder):
parser.add_argument('-t', '--target',
help='''Override the build system target (e.g.
'clean', 'pristine', etc.)''')
parser.add_argument('-p', '--pristine', choices=['auto', 'always',
'never'], action=AlwaysIfMissing, nargs='?',
help='''Control whether the pristine target is run
before building if a build system is present in the
build dir. --pristine is the same as
--pristine=always. If set to auto, the pristine
target will be run only if required based on the
existing build system and the options provided.
This allows for reusing a build folder even if it
contains build files for a different board or
application.''')
parser.add_argument('-c', '--cmake', action='store_true',
help='Force CMake to run')
self.add_force_arg(parser)
Expand All @@ -127,10 +143,29 @@ def do_run(self, args, remainder):
self.args.cmake_opts))
self._sanity_precheck()
self._setup_build_dir()

if args.pristine is not None:
pristine = args.pristine
else:
# Load the pristine={auto, always, never} configuration value
pristine = config.get('build', 'pristine', fallback='never')
if pristine not in ['auto', 'always', 'never']:
log.wrn('treating unknown build.pristine value "{}" as "never"'.
format(pristine))
pristine = 'never'
self.auto_pristine = (pristine == 'auto')

log.dbg('pristine: {} auto_pristine: {}'.format(pristine,
self.auto_pristine))
if is_zephyr_build(self.build_dir):
self._update_cache()
if self.args.cmake or self.args.cmake_opts:
if pristine == 'always':
log.inf('Making build dir {} pristine'.format(self.build_dir))
self._run_build('pristine')
self.run_cmake = True
else:
self._update_cache()
if self.args.cmake or self.args.cmake_opts:
self.run_cmake = True
else:
self.run_cmake = True
self._setup_source_dir()
Expand All @@ -154,8 +189,7 @@ def do_run(self, args, remainder):
self._sanity_check()
self._update_cache()

extra_args = ['--target', args.target] if args.target else []
cmake.run_build(self.build_dir, extra_args=extra_args)
self._run_build(args.target)

def _parse_remainder(self, remainder):
self.args.source_dir = None
Expand Down Expand Up @@ -226,10 +260,7 @@ def _setup_source_dir(self):
source_dir = os.getcwd()
self.source_dir = os.path.abspath(source_dir)

def _sanity_check(self):
# Sanity check the build configuration.
# Side effect: may update cmake_cache attribute.
log.dbg('sanity checking the build', level=log.VERBOSE_EXTREME)
def _sanity_check_source_dir(self):
if self.source_dir == self.build_dir:
# There's no forcing this.
log.die('source and build directory {} cannot be the same; '
Expand All @@ -249,6 +280,13 @@ def _sanity_check(self):
'want to build? (Use -s SOURCE_DIR to specify '
'the application source directory)'.
format(srcrel=srcrel))

def _sanity_check(self):
# Sanity check the build configuration.
# Side effect: may update cmake_cache attribute.
log.dbg('sanity checking the build', level=log.VERBOSE_EXTREME)
self._sanity_check_source_dir()

self.check_force(
is_zephyr_build(self.build_dir) or self.args.board,
'this looks like a new or clean build, please provide --board')
Expand All @@ -263,15 +301,16 @@ def _sanity_check(self):
if self.args.source_dir else None)
cached_abs = os.path.abspath(cached_app) if cached_app else None

log.dbg('pristine:', self.auto_pristine, level=log.VERBOSE_EXTREME)
# If the build directory specifies a source app, make sure it's
# consistent with --source-dir.
apps_mismatched = (source_abs and cached_abs and
source_abs != cached_abs)
self.check_force(
not apps_mismatched,
not apps_mismatched or self.auto_pristine,
'Build directory "{}" is for application "{}", but source '
'directory "{}" was specified; please clean it or use --build-dir '
'to set another build directory'.
'directory "{}" was specified; please clean it, use --pristine, '
'or use --build-dir to set another build directory'.
format(self.build_dir, cached_abs, source_abs))
if apps_mismatched:
self.run_cmake = True # If they insist, we need to re-run cmake.
Expand All @@ -280,19 +319,40 @@ def _sanity_check(self):
# command line.
cached_board = self.cmake_cache.get('CACHED_BOARD')
log.dbg('CACHED_BOARD:', cached_board, level=log.VERBOSE_EXTREME)
self.check_force(cached_board or self.args.board,
# If app_mismatched and pristine are true we will run pristine on the
# build, invalidating the cached board. Whenever we run pristine we
# require the user to provide all the require inputs again.
self.check_force((cached_board and
not (apps_mismatched and self.auto_pristine))
or self.args.board,
'Cached board not defined, please provide --board')

# Check consistency between cached board and --board.
boards_mismatched = (self.args.board and cached_board and
self.args.board != cached_board)
self.check_force(
not boards_mismatched,
not boards_mismatched or self.auto_pristine,
'Build directory {} targets board {}, but board {} was specified. '
'(Clean the directory or use --build-dir to specify a different '
'one.)'.
'(Clean the directory, use --pristine, or use --build-dir to '
'specify a different one.)'.
format(self.build_dir, cached_board, self.args.board))

if self.auto_pristine and (apps_mismatched or boards_mismatched):
log.inf('Making build dir {} pristine'.format(self.build_dir))
self._run_build('pristine')
self.cmake_cache = None
log.dbg('run_cmake:', True, level=log.VERBOSE_EXTREME)
self.run_cmake = True

# Tricky corner-case: The user has not specified a build folder but
# there was one in the CMake cache. Since this is going to be
# 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._sanity_check_source_dir()


def _run_cmake(self, cmake_opts):
if not self.run_cmake:
log.dbg('not running cmake; build system is present')
Expand All @@ -312,3 +372,7 @@ def _run_cmake(self, cmake_opts):
if cmake_opts:
final_cmake_args.extend(cmake_opts)
cmake.run_cmake(final_cmake_args)

def _run_build(self, target):
extra_args = ['--target', target] if target else []
cmake.run_build(self.build_dir, extra_args=extra_args)

0 comments on commit b7c7591

Please sign in to comment.