blob: 642e2012cf696df6f1800af5961a9703d10ea1ec [file] [log] [blame]
# Copyright (c) 2018 Foundries.io
#
# SPDX-License-Identifier: Apache-2.0
'''Helpers shared by multiple west extension command modules.
Note that common helpers used by the flash and debug extension
commands are in run_common -- that's for common code used by
commands which specifically execute runners.'''
import os
from pathlib import Path
import re
from west import log
from west.commands import WestCommand
# This relies on this file being zephyr/scripts/foo/bar.py.
# If you move this file, you'll break it, so be careful.
THIS_ZEPHYR = Path(__file__).parent.parent.parent
ZEPHYR_BASE = Path(os.environ.get('ZEPHYR_BASE', THIS_ZEPHYR))
# FIXME we need a nicer way to handle imports from scripts and cmake than this.
ZEPHYR_SCRIPTS = ZEPHYR_BASE / 'scripts'
ZEPHYR_CMAKE = ZEPHYR_BASE / 'cmake'
class Forceable(WestCommand):
'''WestCommand subclass for commands with a --force option.'''
@staticmethod
def add_force_arg(parser):
'''Add a -f / --force option to the parser.'''
parser.add_argument('-f', '--force', action='store_true',
help='ignore any errors and try to proceed')
def check_force(self, cond, msg):
'''Abort if the command needs to be forced and hasn't been.
The "forced" predicate must be in self.args.forced.
If cond and self.args.force are both False, scream and die
with message msg. Otherwise, return. That is, "cond" is a
condition which means everything is OK; if it's False, only
self.args.force being True can allow execution to proceed.
'''
if not (cond or self.args.force):
log.err(msg)
log.die('refusing to proceed without --force due to above error')
def load_dot_config(path):
'''Load a Kconfig .config output file in 'path' and return a dict
from symbols set in that file to their values.
This parser:
- respects the usual "# CONFIG_FOO is not set" --> CONFIG_FOO=n
semantics
- converts decimal or hexadecimal integer literals to ints
- strips quotes around strings
'''
# You might think it would be easier to create a new
# kconfiglib.Kconfig object and use its load_config() method to
# parse the file instead.
#
# Beware, traveler: that way lies madness.
#
# Zephyr's Kconfig files rely heavily on environment variables to
# manage user-configurable directory locations, and recreating
# that state turns out to be a surprisingly fragile process. It's
# not just 'srctree'; there are various others and they change
# randomly as features get merged.
#
# Nor will pickling the Kconfig object come to your aid. It's a
# deeply recursive data structure which requires a very high
# system recursion limit (100K frames seems to do it) and contains
# references to un-pickle-able values in its attributes.
#
# It's easier and in fact more robust to rely on the fact that the
# .config file contents are just a strictly formatted Makefile
# fragment with some extra "is not set" semantics.
opt_value = re.compile('^(?P<option>CONFIG_[A-Za-z0-9_]+)=(?P<value>.*)$')
not_set = re.compile('^# (?P<option>CONFIG_[A-Za-z0-9_]+) is not set$')
with open(path, 'r') as f:
lines = f.readlines()
ret = {}
for line in lines:
match = opt_value.match(line)
if match:
value = match.group('value')
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
else:
try:
base = 16 if value.startswith('0x') else 10
value = int(value, base=base)
except ValueError:
pass
ret[match.group('option')] = value
match = not_set.match(line)
if match:
ret[match.group('option')] = 'n'
return ret