2019-01-23 16:31:06 +01:00
|
|
|
# Copyright (c) 2018 Foundries.io
|
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
2020-03-04 15:14:43 +01:00
|
|
|
import pathlib
|
2019-12-09 20:05:11 +01:00
|
|
|
import shlex
|
cmake: west: invoke west using same python as rest of build system
When running CMake, then Python3 will be used.
This is detected through FindPython3, with a preference for using the
python or python3 in path, if any of those matches the required Python
minimal version in Zephyr.
It is also possible for users to specify a different Python, as example
by using:
`cmake -DPYTHON_PREFER=/usr/bin/python3.x`
However, when running `west` as native command, then west will be
invoked on linux based on the python defined in:
`west` launcher, which could be: `#!/usr/bin/python3.y`
Thus there could be mismatch in Pythons used for `west` and the python
used for other scripts.
This is even worse on windows, where a user might experience:
```
>.\opt\bin\Scripts\west.exe --version
Traceback (most recent call last):
File "C:\Python37\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
...
File "C:\Python37\lib\socket.py", line 49, in <module>
import _socket
ImportError: Module use of python38.dll conflicts with this version of
Python.
```
when testing out a newer Python, but the python in path is still a 3.7.
By importing `west` into zephyr_module.py and by using, as example
`python -c "from west.util import west_topdir; print(topdir())"`
we ensure the same python is used in all python scripts.
Also it allows the user to control the python to use for west.
It also ensures that the west version being tested, is also the version
being used, where old code would test the version imported by python,
but using the west in path (which could be a different version)
If the west version installed in the current Python, and west invocation
is using a different Python interpreter, then an additional help text
is printed, to easier assist users with debugging.
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
2020-06-08 21:09:15 +02:00
|
|
|
import sys
|
2021-05-29 13:53:41 +02:00
|
|
|
import yaml
|
2019-01-23 16:31:06 +01:00
|
|
|
|
|
|
|
from west import log
|
2019-04-14 21:52:45 +02:00
|
|
|
from west.configuration import config
|
2019-05-04 14:44:56 +02:00
|
|
|
from zcmake import DEFAULT_CMAKE_GENERATOR, run_cmake, run_build, CMakeCache
|
2019-05-04 21:16:15 +02:00
|
|
|
from build_helpers import is_zephyr_build, find_build_dir, \
|
2019-06-05 16:04:29 +02:00
|
|
|
FIND_BUILD_DIR_DESCRIPTION
|
2019-01-30 21:53:40 +01:00
|
|
|
|
2019-04-26 21:53:02 +02:00
|
|
|
from zephyr_ext_common import Forceable
|
2019-01-30 21:53:40 +01:00
|
|
|
|
2019-02-21 15:58:19 +01:00
|
|
|
_ARG_SEPARATOR = '--'
|
2019-01-23 16:31:06 +01:00
|
|
|
|
2021-11-22 13:35:06 +01:00
|
|
|
SYSBUILD_PROJ_DIR = pathlib.Path(__file__).resolve().parent.parent.parent \
|
|
|
|
/ pathlib.Path('share/sysbuild')
|
|
|
|
|
2019-05-02 00:48:04 +02:00
|
|
|
BUILD_USAGE = '''\
|
2022-02-23 13:27:14 +01:00
|
|
|
west build [-h] [-b BOARD[@REV]]] [-d BUILD_DIR]
|
2019-05-02 00:48:04 +02:00
|
|
|
[-t TARGET] [-p {auto, always, never}] [-c] [--cmake-only]
|
2019-05-04 21:32:36 +02:00
|
|
|
[-n] [-o BUILD_OPT] [-f]
|
2021-11-22 13:35:06 +01:00
|
|
|
[--sysbuild | --no-sysbuild]
|
2019-05-04 21:32:36 +02:00
|
|
|
[source_dir] -- [cmake_opt [cmake_opt ...]]
|
2019-05-02 00:48:04 +02:00
|
|
|
'''
|
|
|
|
|
2020-06-25 21:58:58 +02:00
|
|
|
BUILD_DESCRIPTION = f'''\
|
2019-01-23 16:31:06 +01:00
|
|
|
Convenience wrapper for building Zephyr applications.
|
|
|
|
|
2020-06-25 21:58:58 +02:00
|
|
|
{FIND_BUILD_DIR_DESCRIPTION}
|
|
|
|
|
2019-02-21 15:58:19 +01:00
|
|
|
positional arguments:
|
2020-06-25 21:58:58 +02:00
|
|
|
source_dir application source directory
|
|
|
|
cmake_opt extra options to pass to cmake; implies -c
|
|
|
|
(these must come after "--" as shown above)
|
2019-02-21 15:58:19 +01:00
|
|
|
'''
|
2019-01-23 16:31:06 +01:00
|
|
|
|
2020-06-25 21:58:58 +02:00
|
|
|
PRISTINE_DESCRIPTION = """\
|
|
|
|
A "pristine" build directory is empty. The -p option controls
|
|
|
|
whether the build directory is made pristine before the build
|
|
|
|
is done. A bare '--pristine' with no value is the same as
|
|
|
|
--pristine=always. Setting --pristine=auto uses heuristics to
|
|
|
|
guess if a pristine build may be necessary."""
|
|
|
|
|
2019-06-03 06:48:52 +02:00
|
|
|
def _banner(msg):
|
|
|
|
log.inf('-- west build: ' + msg, colorize=True)
|
|
|
|
|
2019-05-04 21:14:28 +02:00
|
|
|
def config_get(option, fallback):
|
|
|
|
return config.get('build', option, fallback=fallback)
|
|
|
|
|
|
|
|
def config_getboolean(option, fallback):
|
|
|
|
return config.getboolean('build', option, fallback=fallback)
|
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
class AlwaysIfMissing(argparse.Action):
|
|
|
|
|
2019-05-04 21:16:15 +02:00
|
|
|
def __call__(self, parser, namespace, values, option_string=None):
|
|
|
|
setattr(namespace, self.dest, values or 'always')
|
2019-01-30 21:53:40 +01:00
|
|
|
|
|
|
|
class Build(Forceable):
|
2019-01-23 16:31:06 +01:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
super(Build, self).__init__(
|
|
|
|
'build',
|
2019-01-28 18:45:02 +01:00
|
|
|
# Keep this in sync with the string in west-commands.yml.
|
2019-01-23 16:31:06 +01:00
|
|
|
'compile a Zephyr application',
|
|
|
|
BUILD_DESCRIPTION,
|
2019-02-21 15:58:19 +01:00
|
|
|
accepts_unknown_args=True)
|
2019-01-23 16:31:06 +01:00
|
|
|
|
|
|
|
self.source_dir = None
|
|
|
|
'''Source directory for the build, or None on error.'''
|
|
|
|
|
|
|
|
self.build_dir = None
|
|
|
|
'''Final build directory used to run the build, or None on error.'''
|
|
|
|
|
|
|
|
self.created_build_dir = False
|
|
|
|
'''True if the build directory was created; False otherwise.'''
|
|
|
|
|
|
|
|
self.run_cmake = False
|
|
|
|
'''True if CMake was run; False otherwise.
|
|
|
|
|
|
|
|
Note: this only describes CMake runs done by this command. The
|
|
|
|
build system generated by CMake may also update itself due to
|
|
|
|
internal logic.'''
|
|
|
|
|
|
|
|
self.cmake_cache = None
|
|
|
|
'''Final parsed CMake cache for the build, or None on error.'''
|
|
|
|
|
|
|
|
def do_add_parser(self, parser_adder):
|
|
|
|
parser = parser_adder.add_parser(
|
|
|
|
self.name,
|
|
|
|
help=self.help,
|
|
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
2019-02-21 15:58:19 +01:00
|
|
|
description=self.description,
|
2019-05-02 00:48:04 +02:00
|
|
|
usage=BUILD_USAGE)
|
2019-01-23 16:31:06 +01:00
|
|
|
|
boards/shields: re-work handling in cmake and west
Remove the boards and shields lists from the 'usage' target output.
That might have been readable at some point long ago in Zephyr's
history, when only a few boards were available, but right now it's
obscuring the high level targets we really want 'usage' to print.
Instead, add 'boards' and 'shields' targets which the user can run to
get those lists, and reference them from the 'usage' output. This
makes 'usage' squintable again. We use the new list_boards.py script
from the 'boards' target.
Reference the 'help' target from 'usage' as well, and drop the
recommendation that people run '--target help' from the 'west build
--help' output for the 'west build --target' option. The canonical
place to look is 'usage' now.
Use the new list_boards.py code from 'west boards' as well, which
allows us to add the board's directory as a format string key, in
addition to its name and architecture.
Keep west-completion.bash up to date. While doing that, I noticed that
a bunch of references to this file refer to a stale location, so fix
those too.
Finally, the 'usage' output is what we print for a failed board or
shield lookup, so that needs to be updated also. Handle that by
invoking boards.cmake and a new shields.cmake in CMake script mode to
print the relevant output.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-12-10 00:53:00 +01:00
|
|
|
# Remember to update west-completion.bash if you add or remove
|
2019-01-23 16:31:06 +01:00
|
|
|
# flags
|
|
|
|
|
2022-02-23 13:27:14 +01:00
|
|
|
parser.add_argument('-b', '--board',
|
|
|
|
help='board to build for with optional board revision')
|
2019-02-21 15:58:19 +01:00
|
|
|
# Hidden option for backwards compatibility
|
|
|
|
parser.add_argument('-s', '--source-dir', help=argparse.SUPPRESS)
|
2019-01-23 16:31:06 +01:00
|
|
|
parser.add_argument('-d', '--build-dir',
|
2020-06-25 21:58:58 +02:00
|
|
|
help='build directory to create or use')
|
2019-01-30 21:53:40 +01:00
|
|
|
self.add_force_arg(parser)
|
2020-06-25 21:58:58 +02:00
|
|
|
|
|
|
|
group = parser.add_argument_group('cmake and build tool')
|
|
|
|
group.add_argument('-c', '--cmake', action='store_true',
|
|
|
|
help='force a cmake run')
|
|
|
|
group.add_argument('--cmake-only', action='store_true',
|
|
|
|
help="just run cmake; don't build (implies -c)")
|
|
|
|
group.add_argument('-t', '--target',
|
boards/shields: re-work handling in cmake and west
Remove the boards and shields lists from the 'usage' target output.
That might have been readable at some point long ago in Zephyr's
history, when only a few boards were available, but right now it's
obscuring the high level targets we really want 'usage' to print.
Instead, add 'boards' and 'shields' targets which the user can run to
get those lists, and reference them from the 'usage' output. This
makes 'usage' squintable again. We use the new list_boards.py script
from the 'boards' target.
Reference the 'help' target from 'usage' as well, and drop the
recommendation that people run '--target help' from the 'west build
--help' output for the 'west build --target' option. The canonical
place to look is 'usage' now.
Use the new list_boards.py code from 'west boards' as well, which
allows us to add the board's directory as a format string key, in
addition to its name and architecture.
Keep west-completion.bash up to date. While doing that, I noticed that
a bunch of references to this file refer to a stale location, so fix
those too.
Finally, the 'usage' output is what we print for a failed board or
shield lookup, so that needs to be updated also. Handle that by
invoking boards.cmake and a new shields.cmake in CMake script mode to
print the relevant output.
Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
2020-12-10 00:53:00 +01:00
|
|
|
help='''run build system target TARGET
|
|
|
|
(try "-t usage")''')
|
2021-05-29 13:53:41 +02:00
|
|
|
group.add_argument('-T', '--test-item',
|
|
|
|
help='''Build based on test data in testcase.yaml
|
|
|
|
or sample.yaml''')
|
2020-06-25 21:58:58 +02:00
|
|
|
group.add_argument('-o', '--build-opt', default=[], action='append',
|
|
|
|
help='''options to pass to the build tool
|
|
|
|
(make or ninja); may be given more than once''')
|
|
|
|
group.add_argument('-n', '--just-print', '--dry-run', '--recon',
|
|
|
|
dest='dry_run', action='store_true',
|
|
|
|
help="just print build commands; don't run them")
|
|
|
|
|
2021-11-22 13:35:06 +01:00
|
|
|
group = parser.add_mutually_exclusive_group()
|
|
|
|
group.add_argument('--sysbuild', action='store_true',
|
|
|
|
help='''create multi domain build system''')
|
|
|
|
group.add_argument('--no-sysbuild', action='store_true',
|
|
|
|
help='''do not create multi domain build system
|
|
|
|
(default)''')
|
|
|
|
|
2020-06-25 21:58:58 +02:00
|
|
|
group = parser.add_argument_group('pristine builds',
|
|
|
|
PRISTINE_DESCRIPTION)
|
|
|
|
group.add_argument('-p', '--pristine', choices=['auto', 'always',
|
|
|
|
'never'], action=AlwaysIfMissing, nargs='?',
|
|
|
|
help='pristine build folder setting')
|
|
|
|
|
2019-01-23 16:31:06 +01:00
|
|
|
return parser
|
|
|
|
|
2019-02-21 15:58:19 +01:00
|
|
|
def do_run(self, args, remainder):
|
2019-01-23 16:31:06 +01:00
|
|
|
self.args = args # Avoid having to pass them around
|
2019-05-23 17:31:31 +02:00
|
|
|
self.config_board = config_get('board', None)
|
2019-05-02 01:06:15 +02:00
|
|
|
log.dbg('args: {} remainder: {}'.format(args, remainder),
|
|
|
|
level=log.VERBOSE_EXTREME)
|
2019-02-21 15:58:19 +01:00
|
|
|
# Store legacy -s option locally
|
|
|
|
source_dir = self.args.source_dir
|
|
|
|
self._parse_remainder(remainder)
|
2021-05-29 13:53:41 +02:00
|
|
|
# Parse testcase.yaml or sample.yaml files for additional options.
|
|
|
|
if self.args.test_item:
|
|
|
|
self._parse_test_item()
|
2019-02-21 15:58:19 +01:00
|
|
|
if source_dir:
|
|
|
|
if self.args.source_dir:
|
|
|
|
log.die("source directory specified twice:({} and {})".format(
|
|
|
|
source_dir, self.args.source_dir))
|
|
|
|
self.args.source_dir = source_dir
|
|
|
|
log.dbg('source_dir: {} cmake_opts: {}'.format(self.args.source_dir,
|
2019-05-02 01:06:15 +02:00
|
|
|
self.args.cmake_opts),
|
|
|
|
level=log.VERBOSE_EXTREME)
|
2019-01-23 16:31:06 +01:00
|
|
|
self._sanity_precheck()
|
|
|
|
self._setup_build_dir()
|
2019-04-14 21:52:45 +02:00
|
|
|
|
|
|
|
if args.pristine is not None:
|
|
|
|
pristine = args.pristine
|
|
|
|
else:
|
|
|
|
# Load the pristine={auto, always, never} configuration value
|
2021-01-15 18:17:01 +01:00
|
|
|
pristine = config_get('pristine', 'never')
|
2019-04-14 21:52:45 +02:00
|
|
|
if pristine not in ['auto', 'always', 'never']:
|
2019-05-04 21:16:15 +02:00
|
|
|
log.wrn(
|
|
|
|
'treating unknown build.pristine value "{}" as "never"'.
|
|
|
|
format(pristine))
|
2019-04-14 21:52:45 +02:00
|
|
|
pristine = 'never'
|
|
|
|
self.auto_pristine = (pristine == 'auto')
|
|
|
|
|
|
|
|
log.dbg('pristine: {} auto_pristine: {}'.format(pristine,
|
2019-05-02 01:06:15 +02:00
|
|
|
self.auto_pristine),
|
|
|
|
level=log.VERBOSE_VERY)
|
2019-01-23 16:31:06 +01:00
|
|
|
if is_zephyr_build(self.build_dir):
|
2019-04-14 21:52:45 +02:00
|
|
|
if pristine == 'always':
|
2019-04-18 16:46:12 +02:00
|
|
|
self._run_pristine()
|
2019-01-23 16:31:06 +01:00
|
|
|
self.run_cmake = True
|
2019-04-14 21:52:45 +02:00
|
|
|
else:
|
|
|
|
self._update_cache()
|
2019-05-02 00:48:04 +02:00
|
|
|
if (self.args.cmake or self.args.cmake_opts or
|
|
|
|
self.args.cmake_only):
|
2019-04-14 21:52:45 +02:00
|
|
|
self.run_cmake = True
|
2019-01-23 16:31:06 +01:00
|
|
|
else:
|
|
|
|
self.run_cmake = True
|
2019-06-05 16:04:29 +02:00
|
|
|
self.source_dir = self._find_source_dir()
|
2019-01-23 16:31:06 +01:00
|
|
|
self._sanity_check()
|
|
|
|
|
2019-05-04 21:14:57 +02:00
|
|
|
board, origin = self._find_board()
|
|
|
|
self._run_cmake(board, origin, self.args.cmake_opts)
|
2019-05-02 00:48:04 +02:00
|
|
|
if args.cmake_only:
|
|
|
|
return
|
|
|
|
|
2019-01-23 16:31:06 +01:00
|
|
|
self._sanity_check()
|
|
|
|
self._update_cache()
|
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
self._run_build(args.target)
|
2019-01-23 16:31:06 +01:00
|
|
|
|
2019-05-04 21:14:57 +02:00
|
|
|
def _find_board(self):
|
|
|
|
board, origin = None, None
|
|
|
|
if self.cmake_cache:
|
|
|
|
board, origin = (self.cmake_cache.get('CACHED_BOARD'),
|
|
|
|
'CMakeCache.txt')
|
2021-02-02 18:22:44 +01:00
|
|
|
|
|
|
|
# A malformed CMake cache may exist, but not have a board.
|
|
|
|
# This happens if there's a build error from a previous run.
|
|
|
|
if board is not None:
|
|
|
|
return (board, origin)
|
|
|
|
|
|
|
|
if self.args.board:
|
2019-05-04 21:14:57 +02:00
|
|
|
board, origin = self.args.board, 'command line'
|
|
|
|
elif 'BOARD' in os.environ:
|
|
|
|
board, origin = os.environ['BOARD'], 'env'
|
2019-05-23 17:31:31 +02:00
|
|
|
elif self.config_board is not None:
|
|
|
|
board, origin = self.config_board, 'configfile'
|
2019-05-04 21:14:57 +02:00
|
|
|
return board, origin
|
|
|
|
|
2019-02-21 15:58:19 +01:00
|
|
|
def _parse_remainder(self, remainder):
|
|
|
|
self.args.source_dir = None
|
|
|
|
self.args.cmake_opts = None
|
2021-05-29 13:53:41 +02:00
|
|
|
|
2019-02-21 15:58:19 +01:00
|
|
|
try:
|
|
|
|
# Only one source_dir is allowed, as the first positional arg
|
|
|
|
if remainder[0] != _ARG_SEPARATOR:
|
|
|
|
self.args.source_dir = remainder[0]
|
|
|
|
remainder = remainder[1:]
|
|
|
|
# Only the first argument separator is consumed, the rest are
|
|
|
|
# passed on to CMake
|
|
|
|
if remainder[0] == _ARG_SEPARATOR:
|
|
|
|
remainder = remainder[1:]
|
2019-09-02 15:35:56 +02:00
|
|
|
if remainder:
|
2019-02-21 15:58:19 +01:00
|
|
|
self.args.cmake_opts = remainder
|
|
|
|
except IndexError:
|
|
|
|
return
|
|
|
|
|
2021-05-29 13:53:41 +02:00
|
|
|
def _parse_test_item(self):
|
|
|
|
for yp in ['sample.yaml', 'testcase.yaml']:
|
|
|
|
yf = os.path.join(self.args.source_dir, yp)
|
|
|
|
if not os.path.exists(yf):
|
|
|
|
continue
|
|
|
|
with open(yf, 'r') as stream:
|
|
|
|
try:
|
|
|
|
y = yaml.safe_load(stream)
|
|
|
|
except yaml.YAMLError as exc:
|
|
|
|
log.die(exc)
|
|
|
|
tests = y.get('tests')
|
|
|
|
if not tests:
|
|
|
|
continue
|
|
|
|
item = tests.get(self.args.test_item)
|
|
|
|
if not item:
|
|
|
|
continue
|
|
|
|
|
|
|
|
for data in ['extra_args', 'extra_configs']:
|
|
|
|
extra = item.get(data)
|
|
|
|
if not extra:
|
|
|
|
continue
|
|
|
|
if isinstance(extra, str):
|
|
|
|
arg_list = extra.split(" ")
|
|
|
|
else:
|
|
|
|
arg_list = extra
|
|
|
|
args = ["-D{}".format(arg.replace('"', '')) for arg in arg_list]
|
|
|
|
if self.args.cmake_opts:
|
|
|
|
self.args.cmake_opts.extend(args)
|
|
|
|
else:
|
|
|
|
self.args.cmake_opts = args
|
|
|
|
|
2019-01-23 16:31:06 +01:00
|
|
|
def _sanity_precheck(self):
|
|
|
|
app = self.args.source_dir
|
|
|
|
if app:
|
2019-01-30 21:53:40 +01:00
|
|
|
self.check_force(
|
|
|
|
os.path.isdir(app),
|
|
|
|
'source directory {} does not exist'.format(app))
|
|
|
|
self.check_force(
|
|
|
|
'CMakeLists.txt' in os.listdir(app),
|
|
|
|
"{} doesn't contain a CMakeLists.txt".format(app))
|
2019-01-23 16:31:06 +01:00
|
|
|
|
|
|
|
def _update_cache(self):
|
|
|
|
try:
|
2019-04-26 21:53:02 +02:00
|
|
|
self.cmake_cache = CMakeCache.from_build_dir(self.build_dir)
|
2019-01-23 16:31:06 +01:00
|
|
|
except FileNotFoundError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def _setup_build_dir(self):
|
|
|
|
# Initialize build_dir and created_build_dir attributes.
|
2019-01-30 21:53:40 +01:00
|
|
|
# If we created the build directory, we must run CMake.
|
2019-01-23 16:31:06 +01:00
|
|
|
log.dbg('setting up build directory', level=log.VERBOSE_EXTREME)
|
2019-06-05 16:04:29 +02:00
|
|
|
# The CMake Cache has not been loaded yet, so this is safe
|
2019-09-05 13:44:09 +02:00
|
|
|
board, _ = self._find_board()
|
2019-06-05 16:04:29 +02:00
|
|
|
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')
|
2019-01-23 16:31:06 +01:00
|
|
|
|
|
|
|
if os.path.exists(build_dir):
|
|
|
|
if not os.path.isdir(build_dir):
|
|
|
|
log.die('build directory {} exists and is not a directory'.
|
|
|
|
format(build_dir))
|
|
|
|
else:
|
|
|
|
os.makedirs(build_dir, exist_ok=False)
|
|
|
|
self.created_build_dir = True
|
|
|
|
self.run_cmake = True
|
|
|
|
|
|
|
|
self.build_dir = build_dir
|
|
|
|
|
2019-06-05 16:04:29 +02:00
|
|
|
def _find_source_dir(self):
|
2019-01-23 16:31:06 +01:00
|
|
|
# Initialize source_dir attribute, either from command line argument,
|
|
|
|
# implicitly from the build directory's CMake cache, or using the
|
|
|
|
# default (current working directory).
|
|
|
|
log.dbg('setting up source directory', level=log.VERBOSE_EXTREME)
|
|
|
|
if self.args.source_dir:
|
|
|
|
source_dir = self.args.source_dir
|
|
|
|
elif self.cmake_cache:
|
2019-04-11 01:09:17 +02:00
|
|
|
source_dir = self.cmake_cache.get('CMAKE_HOME_DIRECTORY')
|
2019-01-23 16:31:06 +01:00
|
|
|
if not source_dir:
|
2019-04-11 01:09:17 +02:00
|
|
|
# This really ought to be there. The build directory
|
|
|
|
# must be corrupted somehow. Let's see what we can do.
|
|
|
|
log.die('build directory', self.build_dir,
|
|
|
|
'CMake cache has no CMAKE_HOME_DIRECTORY;',
|
|
|
|
'please give a source_dir')
|
2019-01-23 16:31:06 +01:00
|
|
|
else:
|
|
|
|
source_dir = os.getcwd()
|
2019-06-05 16:04:29 +02:00
|
|
|
return os.path.abspath(source_dir)
|
2019-01-23 16:31:06 +01:00
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
def _sanity_check_source_dir(self):
|
2019-01-23 16:31:06 +01:00
|
|
|
if self.source_dir == self.build_dir:
|
|
|
|
# There's no forcing this.
|
|
|
|
log.die('source and build directory {} cannot be the same; '
|
|
|
|
'use --build-dir {} to specify a build directory'.
|
|
|
|
format(self.source_dir, self.build_dir))
|
|
|
|
|
|
|
|
srcrel = os.path.relpath(self.source_dir)
|
2019-01-30 21:53:40 +01:00
|
|
|
self.check_force(
|
|
|
|
not is_zephyr_build(self.source_dir),
|
|
|
|
'it looks like {srcrel} is a build directory: '
|
|
|
|
'did you mean --build-dir {srcrel} instead?'.
|
|
|
|
format(srcrel=srcrel))
|
|
|
|
self.check_force(
|
|
|
|
'CMakeLists.txt' in os.listdir(self.source_dir),
|
|
|
|
'source directory "{srcrel}" does not contain '
|
|
|
|
'a CMakeLists.txt; is this really what you '
|
|
|
|
'want to build? (Use -s SOURCE_DIR to specify '
|
|
|
|
'the application source directory)'.
|
|
|
|
format(srcrel=srcrel))
|
2019-04-14 21:52:45 +02:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2019-01-23 16:31:06 +01:00
|
|
|
if not self.cmake_cache:
|
|
|
|
return # That's all we can check without a cache.
|
|
|
|
|
2020-04-11 00:39:55 +02:00
|
|
|
if "CMAKE_PROJECT_NAME" not in self.cmake_cache:
|
|
|
|
# This happens sometimes when a build system is not
|
|
|
|
# completely generated due to an error during the
|
|
|
|
# CMake configuration phase.
|
|
|
|
self.run_cmake = True
|
|
|
|
|
2021-11-22 13:35:06 +01:00
|
|
|
cached_proj = self.cmake_cache.get('APPLICATION_SOURCE_DIR')
|
|
|
|
cached_app = self.cmake_cache.get('APP_DIR')
|
|
|
|
# if APP_DIR is None but APPLICATION_SOURCE_DIR is set, that indicates
|
|
|
|
# an older build folder, this still requires pristine.
|
|
|
|
if cached_app is None and cached_proj:
|
|
|
|
cached_app = cached_proj
|
|
|
|
|
|
|
|
log.dbg('APP_DIR:', cached_app, level=log.VERBOSE_EXTREME)
|
2019-01-23 16:31:06 +01:00
|
|
|
source_abs = (os.path.abspath(self.args.source_dir)
|
|
|
|
if self.args.source_dir else None)
|
|
|
|
cached_abs = os.path.abspath(cached_app) if cached_app else None
|
2019-01-30 21:53:40 +01:00
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
log.dbg('pristine:', self.auto_pristine, level=log.VERBOSE_EXTREME)
|
2020-03-04 15:14:43 +01:00
|
|
|
|
2019-01-30 21:53:40 +01:00
|
|
|
# If the build directory specifies a source app, make sure it's
|
|
|
|
# consistent with --source-dir.
|
|
|
|
apps_mismatched = (source_abs and cached_abs and
|
2021-11-02 18:30:27 +01:00
|
|
|
pathlib.Path(source_abs).resolve() != pathlib.Path(cached_abs).resolve())
|
2020-03-04 15:14:43 +01:00
|
|
|
|
2019-01-30 21:53:40 +01:00
|
|
|
self.check_force(
|
2019-04-14 21:52:45 +02:00
|
|
|
not apps_mismatched or self.auto_pristine,
|
2019-01-30 21:53:40 +01:00
|
|
|
'Build directory "{}" is for application "{}", but source '
|
2019-04-14 21:52:45 +02:00
|
|
|
'directory "{}" was specified; please clean it, use --pristine, '
|
|
|
|
'or use --build-dir to set another build directory'.
|
2019-01-30 21:53:40 +01:00
|
|
|
format(self.build_dir, cached_abs, source_abs))
|
2020-03-04 15:14:43 +01:00
|
|
|
|
2019-01-30 21:53:40 +01:00
|
|
|
if apps_mismatched:
|
2019-01-23 16:31:06 +01:00
|
|
|
self.run_cmake = True # If they insist, we need to re-run cmake.
|
|
|
|
|
2019-05-23 17:31:31 +02:00
|
|
|
# If CACHED_BOARD is not defined, we need some other way to
|
|
|
|
# find the board.
|
2019-01-23 16:31:06 +01:00
|
|
|
cached_board = self.cmake_cache.get('CACHED_BOARD')
|
|
|
|
log.dbg('CACHED_BOARD:', cached_board, level=log.VERBOSE_EXTREME)
|
2019-05-23 17:31:31 +02:00
|
|
|
# If apps_mismatched and self.auto_pristine are true, we will
|
|
|
|
# run pristine on the build, invalidating the cached
|
|
|
|
# board. In that case, we need some way of getting the board.
|
2019-04-14 21:52:45 +02:00
|
|
|
self.check_force((cached_board and
|
|
|
|
not (apps_mismatched and self.auto_pristine))
|
2019-05-23 17:31:31 +02:00
|
|
|
or self.args.board or self.config_board or
|
|
|
|
os.environ.get('BOARD'),
|
|
|
|
'Cached board not defined, please provide it '
|
|
|
|
'(provide --board, set default with '
|
|
|
|
'"west config build.board <BOARD>", or set '
|
|
|
|
'BOARD in the environment)')
|
2019-01-30 21:53:40 +01:00
|
|
|
|
|
|
|
# Check consistency between cached board and --board.
|
|
|
|
boards_mismatched = (self.args.board and cached_board and
|
|
|
|
self.args.board != cached_board)
|
|
|
|
self.check_force(
|
2019-04-14 21:52:45 +02:00
|
|
|
not boards_mismatched or self.auto_pristine,
|
2019-01-30 21:53:40 +01:00
|
|
|
'Build directory {} targets board {}, but board {} was specified. '
|
2019-04-14 21:52:45 +02:00
|
|
|
'(Clean the directory, use --pristine, or use --build-dir to '
|
|
|
|
'specify a different one.)'.
|
2019-01-30 21:53:40 +01:00
|
|
|
format(self.build_dir, cached_board, self.args.board))
|
2019-01-23 16:31:06 +01:00
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
if self.auto_pristine and (apps_mismatched or boards_mismatched):
|
2019-04-18 16:46:12 +02:00
|
|
|
self._run_pristine()
|
2019-04-14 21:52:45 +02:00
|
|
|
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
|
2019-05-04 21:16:15 +02:00
|
|
|
(not source_abs and cached_abs)):
|
2019-06-05 16:04:29 +02:00
|
|
|
self.source_dir = self._find_source_dir()
|
2019-04-14 21:52:45 +02:00
|
|
|
self._sanity_check_source_dir()
|
|
|
|
|
2019-05-04 21:14:57 +02:00
|
|
|
def _run_cmake(self, board, origin, cmake_opts):
|
|
|
|
if board is None and config_getboolean('board_warn', True):
|
|
|
|
log.wrn('This looks like a fresh build and BOARD is unknown;',
|
|
|
|
"so it probably won't work. To fix, use",
|
|
|
|
'--board=<your-board>.')
|
|
|
|
log.inf('Note: to silence the above message, run',
|
|
|
|
"'west config build.board_warn false'")
|
|
|
|
|
2019-01-23 16:31:06 +01:00
|
|
|
if not self.run_cmake:
|
|
|
|
return
|
|
|
|
|
2019-06-03 06:48:52 +02:00
|
|
|
_banner('generating a build system')
|
|
|
|
|
2019-05-04 21:14:57 +02:00
|
|
|
if board is not None and origin != 'CMakeCache.txt':
|
|
|
|
cmake_opts = ['-DBOARD={}'.format(board)]
|
|
|
|
else:
|
|
|
|
cmake_opts = []
|
|
|
|
if self.args.cmake_opts:
|
|
|
|
cmake_opts.extend(self.args.cmake_opts)
|
|
|
|
|
2019-12-09 20:05:11 +01:00
|
|
|
user_args = config_get('cmake-args', None)
|
|
|
|
if user_args:
|
|
|
|
cmake_opts.extend(shlex.split(user_args))
|
|
|
|
|
2021-11-22 13:35:06 +01:00
|
|
|
config_sysbuild = config_getboolean('sysbuild', False)
|
|
|
|
if self.args.sysbuild or (config_sysbuild and not self.args.no_sysbuild):
|
|
|
|
cmake_opts.extend(['-S{}'.format(SYSBUILD_PROJ_DIR),
|
|
|
|
'-DAPP_DIR={}'.format(self.source_dir)])
|
|
|
|
else:
|
|
|
|
# self.args.no_sysbuild == True or config sysbuild False
|
|
|
|
cmake_opts.extend(['-S{}'.format(self.source_dir)])
|
|
|
|
|
2019-04-14 21:51:16 +02:00
|
|
|
# Invoke CMake from the current working directory using the
|
|
|
|
# -S and -B options (officially introduced in CMake 3.13.0).
|
|
|
|
# This is important because users expect invocations like this
|
|
|
|
# to Just Work:
|
2019-01-23 16:31:06 +01:00
|
|
|
#
|
|
|
|
# west build -- -DOVERLAY_CONFIG=relative-path.conf
|
cmake: west: invoke west using same python as rest of build system
When running CMake, then Python3 will be used.
This is detected through FindPython3, with a preference for using the
python or python3 in path, if any of those matches the required Python
minimal version in Zephyr.
It is also possible for users to specify a different Python, as example
by using:
`cmake -DPYTHON_PREFER=/usr/bin/python3.x`
However, when running `west` as native command, then west will be
invoked on linux based on the python defined in:
`west` launcher, which could be: `#!/usr/bin/python3.y`
Thus there could be mismatch in Pythons used for `west` and the python
used for other scripts.
This is even worse on windows, where a user might experience:
```
>.\opt\bin\Scripts\west.exe --version
Traceback (most recent call last):
File "C:\Python37\lib\runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
...
File "C:\Python37\lib\socket.py", line 49, in <module>
import _socket
ImportError: Module use of python38.dll conflicts with this version of
Python.
```
when testing out a newer Python, but the python in path is still a 3.7.
By importing `west` into zephyr_module.py and by using, as example
`python -c "from west.util import west_topdir; print(topdir())"`
we ensure the same python is used in all python scripts.
Also it allows the user to control the python to use for west.
It also ensures that the west version being tested, is also the version
being used, where old code would test the version imported by python,
but using the west in path (which could be a different version)
If the west version installed in the current Python, and west invocation
is using a different Python interpreter, then an additional help text
is printed, to easier assist users with debugging.
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
2020-06-08 21:09:15 +02:00
|
|
|
final_cmake_args = ['-DWEST_PYTHON={}'.format(sys.executable),
|
|
|
|
'-B{}'.format(self.build_dir),
|
2019-05-02 00:53:32 +02:00
|
|
|
'-G{}'.format(config_get('generator',
|
|
|
|
DEFAULT_CMAKE_GENERATOR))]
|
2019-01-23 16:31:06 +01:00
|
|
|
if cmake_opts:
|
|
|
|
final_cmake_args.extend(cmake_opts)
|
2019-05-02 01:24:23 +02:00
|
|
|
run_cmake(final_cmake_args, dry_run=self.args.dry_run)
|
2019-04-14 21:52:45 +02:00
|
|
|
|
2019-04-18 16:46:12 +02:00
|
|
|
def _run_pristine(self):
|
2019-06-03 06:48:52 +02:00
|
|
|
_banner('making build dir {} pristine'.format(self.build_dir))
|
2019-04-18 16:46:12 +02:00
|
|
|
if not is_zephyr_build(self.build_dir):
|
2019-05-04 21:16:15 +02:00
|
|
|
log.die('Refusing to run pristine on a folder that is not a '
|
|
|
|
'Zephyr build system')
|
2019-04-18 16:46:12 +02:00
|
|
|
|
2020-03-10 14:52:35 +01:00
|
|
|
cache = CMakeCache.from_build_dir(self.build_dir)
|
2021-01-18 10:56:25 +01:00
|
|
|
|
|
|
|
app_src_dir = cache.get('APPLICATION_SOURCE_DIR')
|
|
|
|
app_bin_dir = cache.get('APPLICATION_BINARY_DIR')
|
|
|
|
|
|
|
|
cmake_args = [f'-DBINARY_DIR={app_bin_dir}',
|
|
|
|
f'-DSOURCE_DIR={app_src_dir}',
|
|
|
|
'-P', cache['ZEPHYR_BASE'] + '/cmake/pristine.cmake']
|
2019-05-02 01:24:23 +02:00
|
|
|
run_cmake(cmake_args, cwd=self.build_dir, dry_run=self.args.dry_run)
|
2019-04-18 16:46:12 +02:00
|
|
|
|
2019-04-14 21:52:45 +02:00
|
|
|
def _run_build(self, target):
|
2019-06-03 06:48:52 +02:00
|
|
|
if target:
|
|
|
|
_banner('running target {}'.format(target))
|
2020-03-05 23:20:18 +01:00
|
|
|
elif self.run_cmake:
|
2019-06-03 06:48:52 +02:00
|
|
|
_banner('building application')
|
2019-04-14 21:52:45 +02:00
|
|
|
extra_args = ['--target', target] if target else []
|
2019-05-04 21:32:36 +02:00
|
|
|
if self.args.build_opt:
|
|
|
|
extra_args.append('--')
|
|
|
|
extra_args.extend(self.args.build_opt)
|
2019-05-07 02:10:35 +02:00
|
|
|
if self.args.verbose:
|
|
|
|
self._append_verbose_args(extra_args,
|
|
|
|
not bool(self.args.build_opt))
|
2019-05-02 01:24:23 +02:00
|
|
|
run_build(self.build_dir, extra_args=extra_args,
|
|
|
|
dry_run=self.args.dry_run)
|
2019-05-07 02:10:35 +02:00
|
|
|
|
|
|
|
def _append_verbose_args(self, extra_args, add_dashes):
|
|
|
|
# These hacks are only needed for CMake versions earlier than
|
|
|
|
# 3.14. When Zephyr's minimum version is at least that, we can
|
|
|
|
# drop this nonsense and just run "cmake --build BUILD -v".
|
|
|
|
self._update_cache()
|
|
|
|
if not self.cmake_cache:
|
|
|
|
return
|
|
|
|
generator = self.cmake_cache.get('CMAKE_GENERATOR')
|
|
|
|
if not generator:
|
|
|
|
return
|
|
|
|
# Substring matching is for things like "Eclipse CDT4 - Ninja".
|
|
|
|
if 'Ninja' in generator:
|
|
|
|
if add_dashes:
|
|
|
|
extra_args.append('--')
|
|
|
|
extra_args.append('-v')
|
|
|
|
elif generator == 'Unix Makefiles':
|
|
|
|
if add_dashes:
|
|
|
|
extra_args.append('--')
|
|
|
|
extra_args.append('VERBOSE=1')
|