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>
diff --git a/scripts/west_commands/domains.py b/scripts/west_commands/domains.py
new file mode 100644
index 0000000..9301c76
--- /dev/null
+++ b/scripts/west_commands/domains.py
@@ -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