scripts: west commands to support --domain
This commit extends the west commands build, flash, and debug to support --domain when having multiple domains (images) defined in a domains.yaml build file. The domains.yaml uses the following yaml format to specify the build directory of each domain in the multi image build: > default: <domain-n> > domains: > <domain-1>: > build_dir: <build_dir-domain-1> > <domain-2>: > build_dir: <build_dir-domain-2> > ... `west <build|flash|debug>` has been extended to support `--domain <domain>`. `west build` calls CMake to create the build system, and if `--domain` is given, then the build tool will be invoked afterwards for the specified domain. `west flash` will default flash all domains, but `--domain <domain>` argument can be used to select a specific domain to flash, for example: > west flash --domain mcuboot `west debug` only a single domain can be debugged at any given time. If `--domain` is not specified, then the default domain specified in the domains.yml file will be used. Users can still select a different domain, for example with: > west debug --domain mcuboot Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
This commit is contained in:
parent
5fe5d6b43d
commit
8408af6d7c
|
@ -12,7 +12,7 @@ import yaml
|
|||
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, \
|
||||
from build_helpers import is_zephyr_build, find_build_dir, load_domains, \
|
||||
FIND_BUILD_DIR_DESCRIPTION
|
||||
|
||||
from zephyr_ext_common import Forceable
|
||||
|
@ -115,6 +115,9 @@ class Build(Forceable):
|
|||
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('--domain', action='append',
|
||||
help='''execute build tool (make or ninja) only for
|
||||
given domain''')
|
||||
group.add_argument('-t', '--target',
|
||||
help='''run build system target TARGET
|
||||
(try "-t usage")''')
|
||||
|
@ -201,8 +204,9 @@ class Build(Forceable):
|
|||
|
||||
self._sanity_check()
|
||||
self._update_cache()
|
||||
self.domains = load_domains(self.build_dir)
|
||||
|
||||
self._run_build(args.target)
|
||||
self._run_build(args.target, args.domain)
|
||||
|
||||
def _find_board(self):
|
||||
board, origin = None, None
|
||||
|
@ -464,7 +468,7 @@ class Build(Forceable):
|
|||
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)])
|
||||
'-DAPP_DIR:PATH={}'.format(self.source_dir)])
|
||||
else:
|
||||
# self.args.no_sysbuild == True or config sysbuild False
|
||||
cmake_opts.extend(['-S{}'.format(self.source_dir)])
|
||||
|
@ -499,7 +503,7 @@ class Build(Forceable):
|
|||
'-P', cache['ZEPHYR_BASE'] + '/cmake/pristine.cmake']
|
||||
run_cmake(cmake_args, cwd=self.build_dir, dry_run=self.args.dry_run)
|
||||
|
||||
def _run_build(self, target):
|
||||
def _run_build(self, target, domain):
|
||||
if target:
|
||||
_banner('running target {}'.format(target))
|
||||
elif self.run_cmake:
|
||||
|
@ -511,8 +515,23 @@ class Build(Forceable):
|
|||
if self.args.verbose:
|
||||
self._append_verbose_args(extra_args,
|
||||
not bool(self.args.build_opt))
|
||||
run_build(self.build_dir, extra_args=extra_args,
|
||||
dry_run=self.args.dry_run)
|
||||
|
||||
domains = load_domains(self.build_dir)
|
||||
build_dir_list = []
|
||||
|
||||
if domain is None:
|
||||
# If no domain is specified, we just build top build dir as that
|
||||
# will build all domains.
|
||||
build_dir_list = [domains.get_top_build_dir()]
|
||||
else:
|
||||
_banner('building domain(s): {}'.format(' '.join(domain)))
|
||||
domain_list = domains.get_domains(domain)
|
||||
for d in domain_list:
|
||||
build_dir_list.append(d.build_dir)
|
||||
|
||||
for b in build_dir_list:
|
||||
run_build(b, extra_args=extra_args,
|
||||
dry_run=self.args.dry_run)
|
||||
|
||||
def _append_verbose_args(self, extra_args, add_dashes):
|
||||
# These hacks are only needed for CMake versions earlier than
|
||||
|
|
|
@ -16,6 +16,7 @@ from pathlib import Path
|
|||
from west import log
|
||||
from west.configuration import config
|
||||
from west.util import escapes_directory
|
||||
from domains import Domains
|
||||
|
||||
DEFAULT_BUILD_DIR = 'build'
|
||||
'''Name of the default Zephyr build directory.'''
|
||||
|
@ -133,3 +134,19 @@ def is_zephyr_build(path):
|
|||
log.dbg(f'{path} is NOT a valid zephyr build directory',
|
||||
level=log.VERBOSE_EXTREME)
|
||||
return False
|
||||
|
||||
|
||||
def load_domains(path):
|
||||
'''Load domains from a domains.yaml.
|
||||
|
||||
If domains.yaml is not found, then a single 'app' domain referring to the
|
||||
top-level build folder is created and returned.
|
||||
'''
|
||||
domains_file = Path(path) / 'domains.yaml'
|
||||
|
||||
if not domains_file.is_file():
|
||||
return Domains.from_data({'default': 'app',
|
||||
'build_dir': path,
|
||||
'domains': [{'name': 'app', 'build_dir': path}]})
|
||||
|
||||
return Domains.from_file(domains_file)
|
||||
|
|
139
scripts/west_commands/domains.py
Normal file
139
scripts/west_commands/domains.py
Normal file
|
@ -0,0 +1,139 @@
|
|||
# Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''Domain handling for west extension commands.
|
||||
|
||||
This provides parsing of domains yaml file and creation of objects of the
|
||||
Domain class.
|
||||
'''
|
||||
|
||||
import yaml
|
||||
import pykwalify.core
|
||||
from west import log
|
||||
|
||||
DOMAINS_SCHEMA = '''
|
||||
## A pykwalify schema for basic validation of the structure of a
|
||||
## domains YAML file.
|
||||
##
|
||||
# The domains.yaml file is a simple list of domains from a multi image build
|
||||
# along with the default domain to use.
|
||||
type: map
|
||||
mapping:
|
||||
default:
|
||||
required: true
|
||||
type: str
|
||||
build_dir:
|
||||
required: true
|
||||
type: str
|
||||
domains:
|
||||
required: false
|
||||
type: seq
|
||||
sequence:
|
||||
- type: map
|
||||
mapping:
|
||||
name:
|
||||
required: true
|
||||
type: str
|
||||
build_dir:
|
||||
required: true
|
||||
type: str
|
||||
'''
|
||||
|
||||
schema = yaml.safe_load(DOMAINS_SCHEMA)
|
||||
|
||||
|
||||
class Domains:
|
||||
|
||||
def __init__(self, data):
|
||||
self._domains = []
|
||||
self._domain_names = []
|
||||
self._domain_default = []
|
||||
|
||||
self._build_dir = data.get('build_dir')
|
||||
domain_list = data.get('domains')
|
||||
if not domain_list:
|
||||
log.wrn("no domains defined; this probably won't work")
|
||||
|
||||
for d in domain_list:
|
||||
domain = Domain(d['name'], d['build_dir'])
|
||||
self._domains.append(domain)
|
||||
self._domain_names.append(domain.name)
|
||||
if domain.name == data['default']:
|
||||
self._default_domain = domain
|
||||
|
||||
@staticmethod
|
||||
def from_file(domains_file):
|
||||
'''Load domains from domains.yaml.
|
||||
|
||||
Exception raised:
|
||||
- ``FileNotFoundError`` if the domains file is not found.
|
||||
'''
|
||||
try:
|
||||
with open(domains_file, 'r') as f:
|
||||
domains = yaml.safe_load(f.read())
|
||||
except FileNotFoundError:
|
||||
log.die(f'domains.yaml file not found: {domains_file}')
|
||||
|
||||
try:
|
||||
pykwalify.core.Core(source_data=domains, schema_data=schema)\
|
||||
.validate()
|
||||
except pykwalify.errors.SchemaError:
|
||||
log.die(f'ERROR: Malformed yaml in file: {domains_file}')
|
||||
|
||||
return Domains(domains)
|
||||
|
||||
@staticmethod
|
||||
def from_data(domains_data):
|
||||
'''Load domains from domains dictionary.
|
||||
'''
|
||||
return Domains(domains_data)
|
||||
|
||||
def get_domains(self, names=None):
|
||||
ret = []
|
||||
|
||||
if not names:
|
||||
return self._domains
|
||||
|
||||
for n in names:
|
||||
found = False
|
||||
for d in self._domains:
|
||||
if n == d.name:
|
||||
ret.append(d)
|
||||
found = True
|
||||
break
|
||||
# Getting here means the domain was not found.
|
||||
# Todo: throw an error.
|
||||
if not found:
|
||||
log.die(f'domain {n} not found, '
|
||||
f'valid domains are:', *self._domain_names)
|
||||
return ret
|
||||
|
||||
def get_default_domain(self):
|
||||
return self._default_domain
|
||||
|
||||
def get_top_build_dir(self):
|
||||
return self._build_dir
|
||||
|
||||
|
||||
class Domain:
|
||||
|
||||
def __init__(self, name, build_dir):
|
||||
self.name = name
|
||||
self.build_dir = build_dir
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
self._name = value
|
||||
|
||||
@property
|
||||
def build_dir(self):
|
||||
return self._build_dir
|
||||
|
||||
@build_dir.setter
|
||||
def build_dir(self, value):
|
||||
self._build_dir = value
|
|
@ -8,7 +8,8 @@
|
|||
|
||||
from west.commands import WestCommand
|
||||
|
||||
from run_common import add_parser_common, do_run_common
|
||||
from run_common import add_parser_common, do_run_common, get_build_dir
|
||||
from build_helpers import load_domains
|
||||
|
||||
|
||||
class Flash(WestCommand):
|
||||
|
@ -26,4 +27,6 @@ class Flash(WestCommand):
|
|||
return add_parser_common(self, parser_adder)
|
||||
|
||||
def do_run(self, my_args, runner_args):
|
||||
do_run_common(self, my_args, runner_args)
|
||||
build_dir = get_build_dir(my_args)
|
||||
domains = load_domains(build_dir).get_domains(my_args.domain)
|
||||
do_run_common(self, my_args, runner_args, domains=domains)
|
||||
|
|
|
@ -16,7 +16,7 @@ import textwrap
|
|||
import traceback
|
||||
|
||||
from west import log
|
||||
from build_helpers import find_build_dir, is_zephyr_build, \
|
||||
from build_helpers import find_build_dir, is_zephyr_build, load_domains, \
|
||||
FIND_BUILD_DIR_DESCRIPTION
|
||||
from west.commands import CommandError
|
||||
from west.configuration import config
|
||||
|
@ -104,6 +104,8 @@ def add_parser_common(command, parser_adder=None, parser=None):
|
|||
help='override default runner from --build-dir')
|
||||
group.add_argument('--skip-rebuild', action='store_true',
|
||||
help='do not refresh cmake dependencies first')
|
||||
group.add_argument('--domain', action='append',
|
||||
help='execute runner only for given domain')
|
||||
|
||||
group = parser.add_argument_group(
|
||||
'runner configuration',
|
||||
|
@ -145,7 +147,7 @@ def add_parser_common(command, parser_adder=None, parser=None):
|
|||
|
||||
return parser
|
||||
|
||||
def do_run_common(command, user_args, user_runner_args):
|
||||
def do_run_common(command, user_args, user_runner_args, domains=None):
|
||||
# This is the main routine for all the "west flash", "west debug",
|
||||
# etc. commands.
|
||||
|
||||
|
@ -153,13 +155,30 @@ def do_run_common(command, user_args, user_runner_args):
|
|||
dump_context(command, user_args, user_runner_args)
|
||||
return
|
||||
|
||||
command_name = command.name
|
||||
build_dir = get_build_dir(user_args)
|
||||
cache = load_cmake_cache(build_dir, user_args)
|
||||
board = cache['CACHED_BOARD']
|
||||
if not user_args.skip_rebuild:
|
||||
rebuild(command, build_dir, user_args)
|
||||
|
||||
if domains is None:
|
||||
if user_args.domain is None:
|
||||
# No domains are passed down and no domains specified by the user.
|
||||
# So default domain will be used.
|
||||
domains = [load_domains(build_dir).get_default_domain()]
|
||||
else:
|
||||
# No domains are passed down, but user has specified domains to use.
|
||||
# Get the user specified domains.
|
||||
domains = load_domains(build_dir).get_domains(user_args.domain)
|
||||
|
||||
for d in domains:
|
||||
do_run_common_image(command, user_args, user_runner_args, d.build_dir)
|
||||
|
||||
def do_run_common_image(command, user_args, user_runner_args, build_dir=None):
|
||||
command_name = command.name
|
||||
if build_dir is None:
|
||||
build_dir = get_build_dir(user_args)
|
||||
cache = load_cmake_cache(build_dir, user_args)
|
||||
board = cache['CACHED_BOARD']
|
||||
|
||||
# Load runners.yaml.
|
||||
yaml_path = runners_yaml_path(build_dir, board)
|
||||
runners_yaml = load_runners_yaml(yaml_path)
|
||||
|
@ -173,7 +192,9 @@ def do_run_common(command, user_args, user_runner_args):
|
|||
# Set up runner logging to delegate to west.log commands.
|
||||
logger = logging.getLogger('runners')
|
||||
logger.setLevel(LOG_LEVEL)
|
||||
logger.addHandler(WestLogHandler())
|
||||
if not logger.hasHandlers():
|
||||
# Only add a runners log handler if none has been added already.
|
||||
logger.addHandler(WestLogHandler())
|
||||
|
||||
# If the user passed -- to force the parent argument parser to stop
|
||||
# parsing, it will show up here, and needs to be filtered out.
|
||||
|
|
Loading…
Reference in a new issue