mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #161 from ARMmbed/config_system
Config system
commit
a7a163b811
|
@ -2,3 +2,4 @@ mbed/docs/*
|
|||
mbed/libraries/*
|
||||
mbed/travis/*
|
||||
mbed/workspace_tools/*
|
||||
tools/*
|
||||
|
|
|
@ -4,6 +4,7 @@ test:
|
|||
- cd ../testproject && mbed compile -m K64F -t GCC_ARM -j 0 --source=. --source=mbed-os/TESTS/integration/basic
|
||||
- cd ../testproject && mbed compile -m K64F -t GCC_ARM -j 0 --tests
|
||||
- cd ../testproject && mbed compile -m LPC1768 -t GCC_ARM -j 0 --source=. --source=mbed-os/TESTS/integration/basic
|
||||
- cd ../testproject && export PYTHONPATH=$(readlink -f mbed-os) && python mbed-os/tools/test/config_test/config_test.py
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
# About the cofiguration system
|
||||
|
||||
The mbed configuration system can be used to customize the compile time configuration of various mbed components (targets, libraries and applications). Each such component can define a number of *configuration parameters*. The values of these configuration parameters can then be *overridden* in various ways. Configuration is defined using [JSON](http://www.json.org/). Some examples of configuration parameters:
|
||||
|
||||
- the sampling period for a data acquisition application.
|
||||
- the default stack size for a newly created OS thread.
|
||||
- the receive buffer size of a serial communication library.
|
||||
- the flash and RAM memory size of a mbed target.
|
||||
|
||||
The configuration system gathers and interprets all the configuration defined in the source tree. The output of the configuration system is a list of macros that are automatically defined when compiling the code.
|
||||
|
||||
# Defining configuration parameters
|
||||
|
||||
The configuration system understands configuration data defined in targets, libraries and applications. While there are some slight differences in the way the configuration system works in these cases, the configuration parameters are always defined in a JSON object called "config". An example is given below:
|
||||
|
||||
```
|
||||
{
|
||||
"config": {
|
||||
"param1": {
|
||||
"help": "The first configuration parameter",
|
||||
"macro_name": "CUSTOM_MACRO_NAME",
|
||||
"value": 0
|
||||
},
|
||||
"param1": {
|
||||
"help": "The second configuration parameter",
|
||||
"required": true
|
||||
},
|
||||
"param3": 10
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The JSON fragment above defines 3 configuration parameters named `param1`, `param2` and `param3`. There are two ways to define a configuration parameter:
|
||||
|
||||
- the short way: by name and value. `param3` above is an example of a short definition for a parameter named `param3` with value `10`.
|
||||
- the long way: by name and description (another JSON object), like `param1` and `param2` above. The JSON description object can have the following keys:
|
||||
- `help`: an optional help message that describes the purpose of the parameter.
|
||||
- `value`: an optional field that defines the value of the parameter.
|
||||
- `required`: an optional key that specifies if the parameter **must** be given a value before compiling the code (`false` by default). It's not possible to compile a source tree with one or more required parameters that don't have a value. Generally, it makes sense to define a required parameter only when it doesn't have a `value` key.
|
||||
- `macro_name`: an optional name for the macro defined at compile time for this configuration parameter. The configuration system will automatically figure out the corresponding macro name for a configuration parameter, but the user can override this automatically computed name by specifying `macro_name`.
|
||||
|
||||
Note that the name of a parameter in `config` can't contain a dot (`.`) character.
|
||||
|
||||
The configuration system automatically appends an *implicit prefix* to the name of each parameter, so you don't have to worry about a name clash if you define a parameter with the same name in a library and a target, for example. The implicit prefix is:
|
||||
|
||||
- **target.** if the parameter is defined in a target.
|
||||
- **app.** if the parameter is defined in the application.
|
||||
- the name of the library followed by a dot (.) if the parameter is defined in a library.
|
||||
|
||||
# Configuration data in libraries
|
||||
|
||||
Each mbed library can have an optional `mbed_lib.json` file located in the root folder of the library that defines its configuration. For a library called `mylib`, the configuration file could look like this:
|
||||
|
||||
```
|
||||
{
|
||||
"name": mylib,
|
||||
"config": {
|
||||
"buffer_size": 1024,
|
||||
"timer_period": {
|
||||
"help": "The timer period (in us)",
|
||||
"macro_name": "INTERNAL_GPTMR_PERIOD",
|
||||
"required": true
|
||||
},
|
||||
"queue_size": {
|
||||
"help": "Size of event queue (entries)",
|
||||
"value": 10
|
||||
}
|
||||
},
|
||||
"macros": ["MYMOD_MACRO1", "MYMOD_MACRO2=\"TEST\""],
|
||||
"target_overrides": {
|
||||
"K64F": {
|
||||
"timer_period": 100,
|
||||
"queue_size": 40
|
||||
},
|
||||
"NXP": {
|
||||
"queue_size": 20,
|
||||
"buffer_size": 128
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this JSON file:
|
||||
|
||||
- `name` is the name of the library. **This is a required field.**
|
||||
- `config` defines the configuration parameters of the library, as explained [here](#defining-configuration-parameters).
|
||||
- `macros` is a list of extra macros that will be defined when compiling a project that includes this library. A macro can be defined without a value (like `MYMOD_MACRO1` above) or with a value (like `MYMOD_MACRO2` above).
|
||||
- `target_overrides` is a dictionary with target-specific values for the configuration parameters.
|
||||
|
||||
`target_overrides` is used to override the values of the parameters depending on the current compilation target. The keys in `target_overrides` are matched against toolchain *labels* (a description of mbed targets can be found [here](mbed_targets.md)). If a key inside `target_overrides` matches one of the target labels, the parameter values are changed according to the value of the key. In the example above:
|
||||
|
||||
- `config` is always processed first, independent of the target. `config` might define values for some of the parameters. In this case, `buffer_size` will be set to 1024, `queue_size` will be set to 10 and `timer_period` will not have a value.
|
||||
- if the library is compiled for the `K64F` target, `timer_period` will be set to 100 and `queue_size` will be set to 40, since they are overridden by the `K64F` key in `target_overrides`. `buffer_size` will be set to 1024, as defined in `config`.
|
||||
- assuming that `NXP` is a label defined by **all** NXP based targets, if the library is compiled for **any** `NXP` target (like `LPC1768` or `LPC11U24`), `buffer_size` will be set to 128 and `queue_size` will be set to 20, while `timer_period` will not have a value (since it doesn't get one neither in `config`, nor in the `NXP` override).
|
||||
- the keys in `target_overrides` are processed in order: if a hypothetical target defines both `K64F` and `NXP` as labels, `timer_period` will be set to 100, `queue_size` will be set to 20 and `buffer_size` will be set to 128.
|
||||
- if the library is compiled for a target that doesn't have `K64F` or `NXP` as labels, the values of the parameters will be the ones set in `config`.
|
||||
|
||||
Except `name`, all the above keys in the JSON file are optional, but if `target_overrides` is defined, `config` must also be defined.
|
||||
|
||||
As explained [here](#defining-configuration-parameters), the parameters have an implicit `mylib.` prefix. Outside `mylib`, `buffer_size` is accessible using the name `mylib.buffer_size`. An application will be able to override the value of this parameter, as described in [this section](#configuration-data-in-applications).
|
||||
|
||||
If the source tree has code for more than one library, each library needs its own `mbed_lib.json` file in its root folder.
|
||||
|
||||
# Configuration data in targets
|
||||
|
||||
Like libraries, targets can define their own configuration data. Additionally, tables can override the configuration of the target(s) they inherit from (for more details about how do define a target and target inheritance, check [this link](mbed_targets.md)). Target configuration data is defined in `targets.json` using `config`, as described [here](#defining-configuration-parameters). An example for a hypothetical `Base` target is given below:
|
||||
|
||||
```
|
||||
"Base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": ["BASE_LABEL"],
|
||||
"config": {
|
||||
"serial_console_speed": {
|
||||
"help": "Baud rate of the serial console",
|
||||
"value": 115200,
|
||||
"macro_name": "MBED_SERIAL_UART_SPEED"
|
||||
},
|
||||
"stack_size": {
|
||||
"help": "Initial stack size of the application",
|
||||
"value": 128
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Similar to libraries, the target defined parameters have an implicit prefix. For a target, the prefix is always called `target` (no matter what the actual target name is), so the above configuration parameters will be accessible outside the definition in `Base` (and any other target) as `target.serial_console_speed` and `target.stack_size`.
|
||||
|
||||
Targets can inherit from other targets, and their configuration data is also inherited. A target that inherits from one or more other targets can add new parameters in its own `config` section and can also override the configuration parameters defined by its parent(s) in a `overrides` section. For example:
|
||||
|
||||
```
|
||||
"Derived": {
|
||||
"inherits": ["Base"],
|
||||
"extra_labels_add": ["NXP"],
|
||||
"config": {
|
||||
"my_own_config": {
|
||||
"help": "My very own configuration parameter",
|
||||
"value": 0
|
||||
}
|
||||
},
|
||||
"overrides": {
|
||||
"stack_size": 256
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`Derived` above defines its own configuration parameter called `my_own_config` and inherits the configuration parameters from `Base`, so its configuration parameters are `serial_console_speed`, `stack_size` and `my_own_config`. It also overrides the value of the `stack_size` parameter defined in `Base`. This means that:
|
||||
|
||||
- when compiling for `Base`, the target will define two configuration parameters: `serial_console_speed` with the value 115200 and `stack_size` with the value 128.
|
||||
- when compiling for `Derived`, the target will define three configuration parameters: `serial_console_speed` with the value 115200, `stack_size` with the value 256 and `my_own_config` with the value 0.
|
||||
|
||||
It is an error for a derived target to re-define a configuration parameter already defined by its parent(s) in its `config` section. It is also an error for a derived target to override a configuration parameter that was not defined by its parent(s) in its `overrides` section.
|
||||
|
||||
# Configuration data in applications
|
||||
|
||||
Like the configuration for targets and libraries, application configuration is optional; if it exists, it must be defined in a `mbed_app.json` file. Unlike library configuration, there can be a single `mbed_app.json` file in the source tree.
|
||||
|
||||
There are quite a few similarities between configuration data in applications and libraries:
|
||||
|
||||
- applications define their configuration parameters in the `config` section of `mbed_app.json`, as explained [here](#defining-configuration-parameters).
|
||||
- applications can specify target-dependent values in their `target_overrides` section, as described in the [library configuration paragraph][#configuration-data-in-libraries) (but see below for differences).
|
||||
- applications can define macros that will be define at compile time by declaring them in `macros`.
|
||||
|
||||
There are also a few differences:
|
||||
|
||||
- applications **can't** have a `name` key in `mbed_app.json`. The prefix for the configuration parameters defined in an application is always `app.`.
|
||||
- applications can also override configuration of libraries and targets in addition to its own configuration in its `target_overrides` section.
|
||||
|
||||
The last point above is important. The application can freely override the configuration of any of the libraries it depends on, as well as the configuration data in targets, so it has complete control over the configuration of the whole build. For an application called myapp that depends on mylib above, the configuration can look like this:
|
||||
|
||||
```
|
||||
{
|
||||
"config": {
|
||||
"welcome_string": {
|
||||
"help": "The string printed on the display on start-up",
|
||||
"value": "\"Hello!\""
|
||||
}
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"target.serial_console_speed": 2400,
|
||||
"mylib.timer_period": 100
|
||||
},
|
||||
"Base": {
|
||||
"target.serial_console_speed": 9600
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`target_overrides` works a lot like it does in libraries, but there are a few differences:
|
||||
|
||||
- since the application can override any configuration parameter, it must specify them using their prefix (like `mylib.timer_period`). If an overridden parameter doesn't have a prefix, it is assumed that it is one of the parameters defined by the application in its own `config` section.
|
||||
- the `*` key in `target_overrides` will match *any* target. It is possible to use the `*` key in a library's `target_overrides` too, but it'd make little sense to do so, since it will always override the values defined in the library's `config` section. In an application it might make sense to use the `*` key, since it can be used to override the configuration defined by the target or the dependent libraries, no matter which target is used for building.
|
||||
|
||||
Other than this, `target_overrides` works exactly like it does for libraries. Keys in `target_overrides` are still processed in the order they are defined, so for the example above, the `*` override is always processed first (since it matches all targets) and then `Base` is only processed for the `Base` target.
|
||||
|
||||
`myapp` above defines its own configuration parameter (`welcome_string`) and overrides the configuration in both the target (`target.serial_console_speed`) and its `mylib` dependency (`mylib.timer_period`):
|
||||
|
||||
- when compiling for `Base`, `app.welcome_string` will be set to `"Hello!"`, `target.serial_console_speed` will be set to 9600 (from the `Base` override) and `mylib.timer_period` will be set to 100 (from the `*` override).
|
||||
- when compiling for `Derived`, `app.welcome_string` will be set to `"Hello!"`, `target.serial_console_speed` will be set to 2400 (from the `*` override) and `mylib.timer_period` will be set to 100 (also from the `*` override).
|
||||
|
||||
It is an error for the application configuration to override configuration parameters that were not defined.
|
||||
|
||||
## Custom targets
|
||||
|
||||
Application configuration can optionally define application-specific targets. These are mbed targets that are needed just to compile this specific application, so it doesn't make sense to add them to the list of official mbed targets; on the contrary, since they're part of `mbed_app.json`, they're versioned together with the application and only known by the application. Application-specific targets are defined with the key `custom_targets` in the `mbed_app.json` file and have the same syntax as a regular target definition, for example:
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"custom_targets": {
|
||||
"k64f_myapp": {
|
||||
"inherits": ["K64F"],
|
||||
"extra_labels_add": ["CUSTOM_K64F_LIB"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This will define a new target named `k64f_myapp` that inherits from the `K64F` mbed target, but with an extra label defined, which will change the way the build system looks for sources in the tree.
|
||||
|
||||
# Configuration data precedence
|
||||
|
||||
The order in which the various bits of configurations are considered is this:
|
||||
|
||||
- the configuration defined by an inherited target overrides the configuration defined by its parent(s), as described [above](#configuration-data-in-targets).
|
||||
- the configuration of the top level application overrides the configuration defined by the target and any of the libraries on which it depends.
|
||||
|
||||
For `myapp` above:
|
||||
|
||||
- the value of `target.serial_console_speed` will be 9600 when compiling for `Base` because of the `Base` override in myapp's `target_overrides`.
|
||||
- the value of `target.serial_console_speed` will be 2400 when compiling for any other target because of the `*` override in myapp's `target_overrides`.
|
||||
- the value of `target.stack_size` will be 256 when compiling for `Derived` and 128 when compiling for `Base` or any other target that derives from `Base` (assuming of course that `Derived` is the only target that redefines `stack_size`).
|
||||
- the value of `mylib.timer_period` will be 100, since that's overridden by the application and thus takes precedence over the values defined in `mylib`.
|
||||
- when compiling for `Base`, the values of `mylib.buffer_size` and `mylib.queue_size` will be 1024 and 10 respectively, as defined in the `config` section of `mylib`.
|
||||
- when compiling for `Derived`, the values of `mylib.buffer_size `and `mylib.queue_size` will be 128 and 20 respectively, since `Derived` defines the `NXP` label and `mylib` defines a specific configuration for this label. Also, since `Derived` has its own `my_own_config` configuration parameter, `target.my_own_config` will also be defined in this case.
|
||||
|
||||
# Using configuration data in the code
|
||||
|
||||
When compiling, the configuration system will automatically generate macro definitions for the configuration parameters and all the macros defined in libraries and the application in their `macros` keys. These definitions will be appended to the compiler's command line. When compiling `myapp` for target `Base`, the following extra macros will be generated (not necessarily in this order):
|
||||
|
||||
```
|
||||
-DMBED_CONF_MYAPP_WELCOME_STRING="Hello!"
|
||||
-DMBED_SERIAL_UART_SPEED=9600
|
||||
-DMBED_CONF_TARGET_STACK_SIZE=128
|
||||
-DINTERNAL_GPTMR_PERIOD=100
|
||||
-DMBED_CONF_MYLIB_BUFFER_SIZE=1024
|
||||
-DMBED_CONF_MYLIB_QUEUE_SIZE=10
|
||||
-DMYMOD_MACRO1
|
||||
-DMYMOD_MACRO2="TEST"
|
||||
```
|
||||
|
||||
When compiling for `Derived`, the following extra macros will be generated:
|
||||
|
||||
```
|
||||
-DMBED_CONF_MYAPP_WELCOME_STRING="Hello!"
|
||||
-DMBED_SERIAL_UART_SPEED=2400
|
||||
-DMBED_CONF_TARGET_STACK_SIZE=256
|
||||
-DMBED_CONF_TARGET_MY_OWN_CONFIG=0
|
||||
-DINTERNAL_GPTMR_PERIOD=100
|
||||
-DMBED_CONF_MYLIB_BUFFER_SIZE=128
|
||||
-DMBED_CONF_MYLIB_QUEUE_SIZE=20
|
||||
-DMYMOD_MACRO1
|
||||
-DMYMOD_MACRO2="TEST"
|
||||
```
|
||||
|
||||
Note that a macro definition will *not* be generated for a parameter that doesn't have a value.
|
||||
|
||||
The names of the macros for the configuration parameter (unless explicitly specified by `macro_name`) are prefixed by **MBED_CONF_**, followed by the full (prefixed) name of the parameter, capitalized and converted to a valid C macro name (if needed).
|
||||
|
|
@ -33,7 +33,7 @@ from tools.libraries import Library
|
|||
from tools.toolchains import TOOLCHAIN_CLASSES
|
||||
from jinja2 import FileSystemLoader
|
||||
from jinja2.environment import Environment
|
||||
|
||||
from tools.config import Config
|
||||
|
||||
def prep_report(report, target_name, toolchain_name, id_name):
|
||||
# Setup report keys
|
||||
|
@ -76,12 +76,65 @@ def add_result_to_report(report, result):
|
|||
result_wrap = { 0: result }
|
||||
report[target][toolchain][id_name].append(result_wrap)
|
||||
|
||||
def get_config(src_path, target, toolchain_name):
|
||||
# Convert src_path to a list if needed
|
||||
src_paths = [src_path] if type(src_path) != ListType else src_path
|
||||
# We need to remove all paths which are repeated to avoid
|
||||
# multiple compilations and linking with the same objects
|
||||
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
|
||||
|
||||
# Create configuration object
|
||||
config = Config(target, src_paths)
|
||||
|
||||
# If the 'target' argument is a string, convert it to a target instance
|
||||
if isinstance(target, str):
|
||||
try:
|
||||
target = TARGET_MAP[target]
|
||||
except KeyError:
|
||||
raise KeyError("Target '%s' not found" % target)
|
||||
|
||||
# Toolchain instance
|
||||
try:
|
||||
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options=None, notify=None, macros=None, silent=True, extra_verbose=False)
|
||||
except KeyError as e:
|
||||
raise KeyError("Toolchain %s not supported" % toolchain_name)
|
||||
|
||||
# Scan src_path for config files
|
||||
resources = toolchain.scan_resources(src_paths[0])
|
||||
for path in src_paths[1:]:
|
||||
resources.add(toolchain.scan_resources(path))
|
||||
|
||||
config.add_config_files(resources.json_files)
|
||||
return config.get_config_data()
|
||||
|
||||
def build_project(src_path, build_path, target, toolchain_name,
|
||||
libraries_paths=None, options=None, linker_script=None,
|
||||
clean=False, notify=None, verbose=False, name=None, macros=None, inc_dirs=None,
|
||||
jobs=1, silent=False, report=None, properties=None, project_id=None, project_description=None, extra_verbose=False):
|
||||
jobs=1, silent=False, report=None, properties=None, project_id=None, project_description=None,
|
||||
extra_verbose=False, config=None):
|
||||
""" This function builds project. Project can be for example one test / UT
|
||||
"""
|
||||
|
||||
# Convert src_path to a list if needed
|
||||
src_paths = [src_path] if type(src_path) != ListType else src_path
|
||||
|
||||
# We need to remove all paths which are repeated to avoid
|
||||
# multiple compilations and linking with the same objects
|
||||
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
|
||||
first_src_path = src_paths[0] if src_paths[0] != "." and src_paths[0] != "./" else getcwd()
|
||||
abs_path = abspath(first_src_path)
|
||||
project_name = basename(normpath(abs_path))
|
||||
|
||||
# If the configuration object was not yet created, create it now
|
||||
config = config or Config(target, src_paths)
|
||||
|
||||
# If the 'target' argument is a string, convert it to a target instance
|
||||
if isinstance(target, str):
|
||||
try:
|
||||
target = TARGET_MAP[target]
|
||||
except KeyError:
|
||||
raise KeyError("Target '%s' not found" % target)
|
||||
|
||||
# Toolchain instance
|
||||
try:
|
||||
toolchain = TOOLCHAIN_CLASSES[toolchain_name](target, options, notify, macros, silent, extra_verbose=extra_verbose)
|
||||
|
@ -91,14 +144,6 @@ def build_project(src_path, build_path, target, toolchain_name,
|
|||
toolchain.VERBOSE = verbose
|
||||
toolchain.jobs = jobs
|
||||
toolchain.build_all = clean
|
||||
src_paths = [src_path] if type(src_path) != ListType else src_path
|
||||
|
||||
# We need to remove all paths which are repeated to avoid
|
||||
# multiple compilations and linking with the same objects
|
||||
src_paths = [src_paths[0]] + list(set(src_paths[1:]))
|
||||
first_src_path = src_paths[0] if src_paths[0] != "." and src_paths[0] != "./" else getcwd()
|
||||
abs_path = abspath(first_src_path)
|
||||
project_name = basename(normpath(abs_path))
|
||||
|
||||
if name is None:
|
||||
# We will use default project name based on project folder name
|
||||
|
@ -148,13 +193,18 @@ def build_project(src_path, build_path, target, toolchain_name,
|
|||
resources.inc_dirs.extend(inc_dirs)
|
||||
else:
|
||||
resources.inc_dirs.append(inc_dirs)
|
||||
|
||||
# Update the configuration with any .json files found while scanning
|
||||
config.add_config_files(resources.json_files)
|
||||
# And add the configuration macros to the toolchain
|
||||
toolchain.add_macros(config.get_config_data_macros())
|
||||
|
||||
# Compile Sources
|
||||
for path in src_paths:
|
||||
src = toolchain.scan_resources(path)
|
||||
objects = toolchain.compile_sources(src, build_path, resources.inc_dirs)
|
||||
resources.objects.extend(objects)
|
||||
|
||||
|
||||
# Link Program
|
||||
res, needed_update = toolchain.link_program(resources, build_path, name)
|
||||
|
||||
|
@ -190,7 +240,6 @@ def build_project(src_path, build_path, target, toolchain_name,
|
|||
# Let Exception propagate
|
||||
raise e
|
||||
|
||||
|
||||
def build_library(src_paths, build_path, target, toolchain_name,
|
||||
dependencies_paths=None, options=None, name=None, clean=False, archive=True,
|
||||
notify=None, verbose=False, macros=None, inc_dirs=None, inc_dirs_ext=None,
|
||||
|
@ -279,6 +328,9 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
|||
else:
|
||||
tmp_path = build_path
|
||||
|
||||
# Handle configuration
|
||||
config = Config(target)
|
||||
|
||||
# Copy headers, objects and static libraries
|
||||
for resource in resources:
|
||||
toolchain.copy_files(resource.headers, build_path, rel_path=resource.base_path)
|
||||
|
@ -286,6 +338,9 @@ def build_library(src_paths, build_path, target, toolchain_name,
|
|||
toolchain.copy_files(resource.libraries, build_path, rel_path=resource.base_path)
|
||||
if resource.linker_script:
|
||||
toolchain.copy_files(resource.linker_script, build_path, rel_path=resource.base_path)
|
||||
config.add_config_files(resource.json_files)
|
||||
|
||||
toolchain.add_macros(config.get_config_data_macros())
|
||||
|
||||
# Compile Sources
|
||||
objects = []
|
||||
|
|
|
@ -0,0 +1,304 @@
|
|||
"""
|
||||
mbed SDK
|
||||
Copyright (c) 2016 ARM Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
# Implementation of mbed configuration mechanism
|
||||
from copy import deepcopy
|
||||
from collections import OrderedDict
|
||||
from tools.utils import json_file_to_dict, ToolException
|
||||
from tools.targets import Target
|
||||
import os
|
||||
|
||||
# Base class for all configuration exceptions
|
||||
class ConfigException(Exception):
|
||||
pass
|
||||
|
||||
# This class keeps information about a single configuration parameter
|
||||
class ConfigParameter:
|
||||
# name: the name of the configuration parameter
|
||||
# data: the data associated with the configuration parameter
|
||||
# unit_name: the unit (target/library/application) that defines this parameter
|
||||
# unit_ kind: the kind of the unit ("target", "library" or "application")
|
||||
def __init__(self, name, data, unit_name, unit_kind):
|
||||
self.name = self.get_full_name(name, unit_name, unit_kind, allow_prefix = False)
|
||||
self.defined_by = self.get_display_name(unit_name, unit_kind)
|
||||
self.set_by = self.defined_by
|
||||
self.help_text = data.get("help", None)
|
||||
self.value = data.get("value", None)
|
||||
self.required = data.get("required", False)
|
||||
self.macro_name = data.get("macro_name", "MBED_CONF_%s" % self.sanitize(self.name.upper()))
|
||||
|
||||
# Return the full (prefixed) name of a parameter.
|
||||
# If the parameter already has a prefix, check if it is valid
|
||||
# name: the simple (unqualified) name of the parameter
|
||||
# unit_name: the unit (target/library/application) that defines this parameter
|
||||
# unit_kind: the kind of the unit ("target", "library" or "application")
|
||||
# label: the name of the label in the 'target_config_overrides' section (optional)
|
||||
# allow_prefix: True to allo the original name to have a prefix, False otherwise
|
||||
@staticmethod
|
||||
def get_full_name(name, unit_name, unit_kind, label = None, allow_prefix = True):
|
||||
if name.find('.') == -1: # the name is not prefixed
|
||||
if unit_kind == "target":
|
||||
prefix = "target."
|
||||
elif unit_kind == "application":
|
||||
prefix = "app."
|
||||
else:
|
||||
prefix = unit_name + '.'
|
||||
return prefix + name
|
||||
# The name has a prefix, so check if it is valid
|
||||
if not allow_prefix:
|
||||
raise ConfigException("Invalid parameter name '%s' in '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
|
||||
temp = name.split(".")
|
||||
# Check if the parameter syntax is correct (must be unit_name.parameter_name)
|
||||
if len(temp) != 2:
|
||||
raise ConfigException("Invalid parameter name '%s' in '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
|
||||
prefix = temp[0]
|
||||
# Check if the given parameter prefix matches the expected prefix
|
||||
if (unit_kind == "library" and prefix != unit_name) or (unit_kind == "target" and prefix != "target"):
|
||||
raise ConfigException("Invalid prefix '%s' for parameter name '%s' in '%s'" % (prefix, name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
|
||||
return name
|
||||
|
||||
# Return the name displayed for a unit when interogating the origin
|
||||
# and the last set place of a parameter
|
||||
# unit_name: the unit (target/library/application) that defines this parameter
|
||||
# unit_kind: the kind of the unit ("target", "library" or "application")
|
||||
# label: the name of the label in the 'target_config_overrides' section (optional)
|
||||
@staticmethod
|
||||
def get_display_name(unit_name, unit_kind, label = None):
|
||||
if unit_kind == "target":
|
||||
return "target:" + unit_name
|
||||
elif unit_kind == "application":
|
||||
return "application%s" % ("[%s]" % label if label else "")
|
||||
else: # library
|
||||
return "library:%s%s" % (unit_name, "[%s]" % label if label else "")
|
||||
|
||||
# "Sanitize" a name so that it is a valid C macro name
|
||||
# Currently it simply replaces '.' and '-' with '_'
|
||||
# name: the un-sanitized name.
|
||||
@staticmethod
|
||||
def sanitize(name):
|
||||
return name.replace('.', '_').replace('-', '_')
|
||||
|
||||
# Sets a value for this parameter, remember the place where it was set
|
||||
# value: the value of the parameter
|
||||
# unit_name: the unit (target/library/application) that defines this parameter
|
||||
# unit_ kind: the kind of the unit ("target", "library" or "application")
|
||||
# label: the name of the label in the 'target_config_overrides' section (optional)
|
||||
def set_value(self, value, unit_name, unit_kind, label = None):
|
||||
self.value = value
|
||||
self.set_by = self.get_display_name(unit_name, unit_kind, label)
|
||||
|
||||
# Return the string representation of this configuration parameter
|
||||
def __str__(self):
|
||||
return '"%s" = %s (set in "%s", defined in "%s")' % (self.name, self.value, self.set_by, self.defined_by)
|
||||
|
||||
# A representation of a configuration macro. It handles both macros without a value (MACRO)
|
||||
# and with a value (MACRO=VALUE)
|
||||
class ConfigMacro:
|
||||
def __init__(self, name, unit_name, unit_kind):
|
||||
self.name = name
|
||||
self.defined_by = ConfigParameter.get_display_name(unit_name, unit_kind)
|
||||
if name.find("=") != -1:
|
||||
tmp = name.split("=")
|
||||
if len(tmp) != 2:
|
||||
raise ValueError("Invalid macro definition '%s' in '%s'" % (name, self.defined_by))
|
||||
self.macro_name = tmp[0]
|
||||
else:
|
||||
self.macro_name = name
|
||||
|
||||
# 'Config' implements the mbed configuration mechanism
|
||||
class Config:
|
||||
# Libraries and applications have different names for their configuration files
|
||||
__mbed_app_config_name = "mbed_app.json"
|
||||
__mbed_lib_config_name = "mbed_lib.json"
|
||||
|
||||
# Allowed keys in configuration dictionaries
|
||||
# (targets can have any kind of keys, so this validation is not applicable to them)
|
||||
__allowed_keys = {
|
||||
"library": set(["name", "config", "target_overrides", "macros", "__config_path"]),
|
||||
"application": set(["config", "custom_targets", "target_overrides", "macros", "__config_path"])
|
||||
}
|
||||
|
||||
# The initialization arguments for Config are:
|
||||
# target: the name of the mbed target used for this configuration instance
|
||||
# top_level_dirs: a list of top level source directories (where mbed_abb_config.json could be found)
|
||||
# __init__ will look for the application configuration file in top_level_dirs.
|
||||
# If found once, it'll parse it and check if it has a custom_targets function.
|
||||
# If it does, it'll update the list of targets if need.
|
||||
# If found more than once, an exception is raised
|
||||
# top_level_dirs can be None (in this case, mbed_app_config.json will not be searched)
|
||||
def __init__(self, target, top_level_dirs = []):
|
||||
app_config_location = None
|
||||
for s in (top_level_dirs or []):
|
||||
full_path = os.path.join(s, self.__mbed_app_config_name)
|
||||
if os.path.isfile(full_path):
|
||||
if app_config_location is not None:
|
||||
raise ConfigException("Duplicate '%s' file in '%s' and '%s'" % (self.__mbed_app_config_name, app_config_location, full_path))
|
||||
else:
|
||||
app_config_location = full_path
|
||||
self.app_config_data = json_file_to_dict(app_config_location) if app_config_location else {}
|
||||
# Check the keys in the application configuration data
|
||||
unknown_keys = set(self.app_config_data.keys()) - self.__allowed_keys["application"]
|
||||
if unknown_keys:
|
||||
raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), self.__mbed_app_config_name))
|
||||
# Update the list of targets with the ones defined in the application config, if applicable
|
||||
Target.add_py_targets(self.app_config_data.get("custom_targets", {}))
|
||||
self.lib_config_data = {}
|
||||
# Make sure that each config is processed only once
|
||||
self.processed_configs = {}
|
||||
self.target = target if isinstance(target, str) else target.name
|
||||
self.target_labels = Target.get_target(self.target).get_labels()
|
||||
|
||||
# Add one or more configuration files
|
||||
def add_config_files(self, flist):
|
||||
for f in flist:
|
||||
if not f.endswith(self.__mbed_lib_config_name):
|
||||
continue
|
||||
full_path = os.path.normpath(os.path.abspath(f))
|
||||
# Check that we didn't already process this file
|
||||
if self.processed_configs.has_key(full_path):
|
||||
continue
|
||||
self.processed_configs[full_path] = True
|
||||
# Read the library configuration and add a "__full_config_path" attribute to it
|
||||
cfg = json_file_to_dict(f)
|
||||
cfg["__config_path"] = full_path
|
||||
# If there's already a configuration for a module with the same name, exit with error
|
||||
if self.lib_config_data.has_key(cfg["name"]):
|
||||
raise ConfigException("Library name '%s' is not unique (defined in '%s' and '%s')" % (cfg["name"], full_path, self.lib_config_data[cfg["name"]]["__config_path"]))
|
||||
self.lib_config_data[cfg["name"]] = cfg
|
||||
|
||||
# Helper function: process a "config_parameters" section in either a target, a library or the application
|
||||
# data: a dictionary with the configuration parameters
|
||||
# params: storage for the discovered configuration parameters
|
||||
# unit_name: the unit (target/library/application) that defines this parameter
|
||||
# unit_kind: the kind of the unit ("target", "library" or "application")
|
||||
def _process_config_parameters(self, data, params, unit_name, unit_kind):
|
||||
for name, v in data.items():
|
||||
full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind)
|
||||
# If the parameter was already defined, raise an error
|
||||
if full_name in params:
|
||||
raise ConfigException("Parameter name '%s' defined in both '%s' and '%s'" % (name, ConfigParameter.get_display_name(unit_name, unit_kind), params[full_name].defined_by))
|
||||
# Otherwise add it to the list of known parameters
|
||||
# If "v" is not a dictionary, this is a shortcut definition, otherwise it is a full definition
|
||||
params[full_name] = ConfigParameter(name, v if isinstance(v, dict) else {"value": v}, unit_name, unit_kind)
|
||||
return params
|
||||
|
||||
# Helper function: process "config_parameters" and "target_config_overrides" in a given dictionary
|
||||
# data: the configuration data of the library/appliation
|
||||
# params: storage for the discovered configuration parameters
|
||||
# unit_name: the unit (library/application) that defines this parameter
|
||||
# unit_kind: the kind of the unit ("library" or "application")
|
||||
def _process_config_and_overrides(self, data, params, unit_name, unit_kind):
|
||||
self._process_config_parameters(data.get("config", {}), params, unit_name, unit_kind)
|
||||
for label, overrides in data.get("target_overrides", {}).items():
|
||||
# If the label is defined by the target or it has the special value "*", process the overrides
|
||||
if (label == '*') or (label in self.target_labels):
|
||||
for name, v in overrides.items():
|
||||
# Get the full name of the parameter
|
||||
full_name = ConfigParameter.get_full_name(name, unit_name, unit_kind, label)
|
||||
# If an attempt is made to override a parameter that isn't defined, raise an error
|
||||
if not full_name in params:
|
||||
raise ConfigException("Attempt to override undefined parameter '%s' in '%s'" % (full_name, ConfigParameter.get_display_name(unit_name, unit_kind, label)))
|
||||
params[full_name].set_value(v, unit_name, unit_kind, label)
|
||||
return params
|
||||
|
||||
# Read and interpret configuration data defined by targets
|
||||
def get_target_config_data(self):
|
||||
# We consider the resolution order for our target and sort it by level reversed,
|
||||
# so that we first look at the top level target (the parent), then its direct children,
|
||||
# then the children's children and so on, until we reach self.target
|
||||
# TODO: this might not work so well in some multiple inheritance scenarios
|
||||
# At each step, look at two keys of the target data:
|
||||
# - config_parameters: used to define new configuration parameters
|
||||
# - config_overrides: used to override already defined configuration parameters
|
||||
params, json_data = {}, Target.get_json_target_data()
|
||||
resolution_order = [e[0] for e in sorted(Target.get_target(self.target).resolution_order, key = lambda e: e[1], reverse = True)]
|
||||
for tname in resolution_order:
|
||||
# Read the target data directly from its description
|
||||
t = json_data[tname]
|
||||
# Process definitions first
|
||||
self._process_config_parameters(t.get("config", {}), params, tname, "target")
|
||||
# Then process overrides
|
||||
for name, v in t.get("overrides", {}).items():
|
||||
full_name = ConfigParameter.get_full_name(name, tname, "target")
|
||||
# If the parameter name is not defined or if there isn't a path from this target to the target where the
|
||||
# parameter was defined in the target inheritance tree, raise an error
|
||||
# We need to use 'defined_by[7:]' to remove the "target:" prefix from defined_by
|
||||
if (not full_name in params) or (not params[full_name].defined_by[7:] in Target.get_target(tname).resolution_order_names):
|
||||
raise ConfigException("Attempt to override undefined parameter '%s' in '%s'" % (name, ConfigParameter.get_display_name(tname, "target")))
|
||||
# Otherwise update the value of the parameter
|
||||
params[full_name].set_value(v, tname, "target")
|
||||
return params
|
||||
|
||||
# Helper function: process a macro definition, checking for incompatible duplicate definitions
|
||||
# mlist: list of macro names to process
|
||||
# macros: dictionary with currently discovered macros
|
||||
# unit_name: the unit (library/application) that defines this macro
|
||||
# unit_kind: the kind of the unit ("library" or "application")
|
||||
def _process_macros(self, mlist, macros, unit_name, unit_kind):
|
||||
for mname in mlist:
|
||||
m = ConfigMacro(mname, unit_name, unit_kind)
|
||||
if (m.macro_name in macros) and (macros[m.macro_name].name != mname):
|
||||
# Found an incompatible definition of the macro in another module, so raise an error
|
||||
full_unit_name = ConfigParameter.get_display_name(unit_name, unit_kind)
|
||||
raise ConfigException("Macro '%s' defined in both '%s' and '%s' with incompatible values" % (m.macro_name, macros[m.macro_name].defined_by, full_unit_name))
|
||||
macros[m.macro_name] = m
|
||||
|
||||
# Read and interpret configuration data defined by libs
|
||||
# It is assumed that "add_config_files" above was already called and the library configuration data
|
||||
# exists in self.lib_config_data
|
||||
def get_lib_config_data(self):
|
||||
all_params, macros = {}, {}
|
||||
for lib_name, lib_data in self.lib_config_data.items():
|
||||
unknown_keys = set(lib_data.keys()) - self.__allowed_keys["library"]
|
||||
if unknown_keys:
|
||||
raise ConfigException("Unknown key(s) '%s' in %s" % (",".join(unknown_keys), lib_name))
|
||||
all_params.update(self._process_config_and_overrides(lib_data, {}, lib_name, "library"))
|
||||
self._process_macros(lib_data.get("macros", []), macros, lib_name, "library")
|
||||
return all_params, macros
|
||||
|
||||
# Read and interpret the configuration data defined by the target
|
||||
# The target can override any configuration parameter, as well as define its own configuration data
|
||||
# params: the dictionary with configuration parameters found so far (in the target and in libraries)
|
||||
# macros: the list of macros defined in the configuration
|
||||
def get_app_config_data(self, params, macros):
|
||||
app_cfg = self.app_config_data
|
||||
# The application can have a "config_parameters" and a "target_config_overrides" section just like a library
|
||||
self._process_config_and_overrides(app_cfg, params, "app", "application")
|
||||
# The application can also defined macros
|
||||
self._process_macros(app_cfg.get("macros", []), macros, "app", "application")
|
||||
|
||||
# Return the configuration data in two parts:
|
||||
# - params: a dictionary with (name, ConfigParam) entries
|
||||
# - macros: the list of macros defined with "macros" in libraries and in the application
|
||||
def get_config_data(self):
|
||||
all_params = self.get_target_config_data()
|
||||
lib_params, macros = self.get_lib_config_data()
|
||||
all_params.update(lib_params)
|
||||
self.get_app_config_data(all_params, macros)
|
||||
return all_params, [m.name for m in macros.values()]
|
||||
|
||||
# Helper: verify if there are any required parameters without a value in 'params'
|
||||
def _check_required_parameters(self, params):
|
||||
for p in params.values():
|
||||
if p.required and (p.value is None):
|
||||
raise ConfigException("Required parameter '%s' defined by '%s' doesn't have a value" % (p.name, p.defined_by))
|
||||
|
||||
# Return the configuration data converted to a list of C macros
|
||||
def get_config_data_macros(self):
|
||||
params, macros = self.get_config_data()
|
||||
self._check_required_parameters(params)
|
||||
return macros + ['%s=%s' % (m.macro_name, m.value) for m in params.values() if m.value is not None]
|
|
@ -247,13 +247,7 @@ if __name__ == '__main__':
|
|||
build_dir = options.build_dir
|
||||
|
||||
try:
|
||||
target = TARGET_MAP[mcu]
|
||||
except KeyError:
|
||||
print "[ERROR] Target %s not supported" % mcu
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
bin_file = build_project(test.source_dir, build_dir, target, toolchain, test.dependencies, options.options,
|
||||
bin_file = build_project(test.source_dir, build_dir, mcu, toolchain, test.dependencies, options.options,
|
||||
linker_script=options.linker_script,
|
||||
clean=options.clean,
|
||||
verbose=options.verbose,
|
||||
|
@ -271,7 +265,7 @@ if __name__ == '__main__':
|
|||
# Import pyserial: https://pypi.python.org/pypi/pyserial
|
||||
from serial import Serial
|
||||
|
||||
sleep(target.program_cycle_s())
|
||||
sleep(TARGET_MAP[mcu].program_cycle_s())
|
||||
|
||||
serial = Serial(options.serial, timeout = 1)
|
||||
if options.baud:
|
||||
|
|
|
@ -37,7 +37,7 @@ from paths import TOOLS_BOOTLOADERS
|
|||
import json
|
||||
import inspect
|
||||
import sys
|
||||
|
||||
from tools.utils import json_file_to_dict
|
||||
|
||||
########################################################################################################################
|
||||
# Generic Target class that reads and interprets the data in targets.json
|
||||
|
@ -60,27 +60,17 @@ class Target:
|
|||
# need to be computed differently than regular attributes
|
||||
__cumulative_attributes = ['extra_labels', 'macros']
|
||||
|
||||
# Utility function: traverse a dictionary and change all the strings in the dictionary to
|
||||
# ASCII from Unicode. Needed because the original mbed target definitions were written in
|
||||
# Python and used only ASCII strings, but the Python JSON decoder always returns Unicode
|
||||
# Based on http://stackoverflow.com/a/13105359
|
||||
@staticmethod
|
||||
def to_ascii(input):
|
||||
if isinstance(input, dict):
|
||||
return dict([(Target.to_ascii(key), Target.to_ascii(value)) for key, value in input.iteritems()])
|
||||
elif isinstance(input, list):
|
||||
return [Target.to_ascii(element) for element in input]
|
||||
elif isinstance(input, unicode):
|
||||
return input.encode('ascii')
|
||||
else:
|
||||
return input
|
||||
# {target_name: target_instance} map for all the targets in the system
|
||||
__target_map = {}
|
||||
|
||||
# List of targets that were added dynamically using "add_py_targets" (see below)
|
||||
__py_targets = set()
|
||||
|
||||
# Load the description of JSON target data
|
||||
@staticmethod
|
||||
@cached
|
||||
def get_json_target_data():
|
||||
with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../mbed/hal/targets.json"), "rt") as f:
|
||||
return Target.to_ascii(json.load(f))
|
||||
return json_file_to_dict(os.path.join(os.path.dirname(os.path.abspath(__file__)), "../mbed/hal/targets.json"))
|
||||
|
||||
# Get the members of this module using Python's "inspect" module
|
||||
@staticmethod
|
||||
|
@ -172,21 +162,58 @@ class Target:
|
|||
return v if attrname != "progen" else self.__add_paths_to_progen(v)
|
||||
|
||||
# Return the value of an attribute
|
||||
# This function only looks for the attribute's value in the cache, the real work of computing the
|
||||
# attribute's value is done in the function above (__getattr_helper)
|
||||
# This function only computes the attribute's value once, then adds it to the instance attributes
|
||||
# (in __dict__), so the next time it is returned directly
|
||||
def __getattr__(self, attrname):
|
||||
if not self.attr_cache.has_key(attrname):
|
||||
self.attr_cache[attrname] = self.__getattr_helper(attrname)
|
||||
return self.attr_cache[attrname]
|
||||
v = self.__getattr_helper(attrname)
|
||||
self.__dict__[attrname] = v
|
||||
return v
|
||||
|
||||
# Add one or more new target(s) represented as a Python dictionary in 'new_targets'
|
||||
# It it an error to add a target with a name that exists in "targets.json"
|
||||
# However, it is OK to add a target that was previously added via "add_py_targets"
|
||||
# (this makes testing easier without changing the regular semantics)
|
||||
@staticmethod
|
||||
def add_py_targets(new_targets):
|
||||
crt_data = Target.get_json_target_data()
|
||||
# First add all elemnts to the internal dictionary
|
||||
for tk, tv in new_targets.items():
|
||||
if crt_data.has_key(tk) and (not tk in Target.__py_targets):
|
||||
raise Exception("Attempt to add target '%s' that already exists" % tk)
|
||||
crt_data[tk] = tv
|
||||
Target.__py_targets.add(tk)
|
||||
# Then create the new instances and update global variables if needed
|
||||
for tk, tv in new_targets.items():
|
||||
# Is the target already created?
|
||||
old_target = Target.__target_map.get(tk, None)
|
||||
# Instantiate this target. If it is public, update the data in
|
||||
# in TARGETS, TARGET_MAP, TARGET_NAMES
|
||||
new_target = Target(tk)
|
||||
if tv.get("public", True):
|
||||
if old_target: # remove the old target from TARGETS and TARGET_NAMES
|
||||
TARGETS.remove(old_target)
|
||||
TARGET_NAMES.remove(tk)
|
||||
# Add the new target
|
||||
TARGETS.append(new_target)
|
||||
TARGET_MAP[tk] = new_target
|
||||
TARGET_NAMES.append(tk)
|
||||
# Update the target cache
|
||||
Target.__target_map[tk] = new_target
|
||||
|
||||
# Return the target instance starting from the target name
|
||||
@staticmethod
|
||||
def get_target(name):
|
||||
if not Target.__target_map.has_key(name):
|
||||
Target.__target_map[name] = Target(name)
|
||||
return Target.__target_map[name]
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
# Compute resolution order once (it will be used later in __getattr__)
|
||||
self.resolution_order = self.__get_resolution_order(self.name, [])
|
||||
|
||||
# Attribute cache: once an attribute's value is computed, don't compute it again
|
||||
self.attr_cache = {}
|
||||
# Create also a list with only the names of the targets in the resolution order
|
||||
self.resolution_order_names = [t[0] for t in self.resolution_order]
|
||||
|
||||
def program_cycle_s(self):
|
||||
try:
|
||||
|
@ -364,7 +391,7 @@ class MCU_NRF51Code:
|
|||
########################################################################################################################
|
||||
|
||||
# Instantiate all public targets
|
||||
TARGETS = [Target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
|
||||
TARGETS = [Target.get_target(name) for name, value in Target.get_json_target_data().items() if value.get("public", True)]
|
||||
|
||||
# Map each target name to its unique instance
|
||||
TARGET_MAP = dict([(t.name, t) for t in TARGETS])
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
"""
|
||||
mbed SDK
|
||||
Copyright (c) 2011-2016 ARM Limited
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
from tools.build_api import get_config
|
||||
from tools.config import ConfigException
|
||||
import os, sys
|
||||
|
||||
# Compare the output of config against a dictionary of known good results
|
||||
def compare_config(cfg, expected):
|
||||
try:
|
||||
for k in cfg:
|
||||
if cfg[k].value != expected[k]:
|
||||
return "'%s': expected '%s', got '%s'" % (k, expected[k], cfg[k].value)
|
||||
except KeyError:
|
||||
return "Unexpected key '%s' in configuration data" % k
|
||||
for k in expected:
|
||||
if k != "desc" and k != "expected_macros" and not k in cfg:
|
||||
return "Expected key '%s' was not found in configuration data" % k
|
||||
return ""
|
||||
|
||||
def test_tree(full_name, name):
|
||||
failed = 0
|
||||
sys.path.append(full_name)
|
||||
if "test_data" in sys.modules:
|
||||
del sys.modules["test_data"]
|
||||
import test_data
|
||||
for target, expected in test_data.expected_results.items():
|
||||
sys.stdout.write("%s:'%s'(%s) " % (name, expected["desc"], target))
|
||||
sys.stdout.flush()
|
||||
err_msg = None
|
||||
try:
|
||||
cfg, macros = get_config(full_name, target, "GCC_ARM")
|
||||
except ConfigException as e:
|
||||
err_msg = e.message
|
||||
if err_msg:
|
||||
if expected.has_key("exception_msg"):
|
||||
if err_msg.find(expected["exception_msg"]) == -1:
|
||||
print "FAILED!"
|
||||
sys.stderr.write(" Unexpected error message!\n")
|
||||
sys.stderr.write(" Expected: '%s'\n" % expected["exception_msg"])
|
||||
sys.stderr.write(" Got: '%s'\n" % err_msg)
|
||||
failed += 1
|
||||
else:
|
||||
print "OK"
|
||||
else:
|
||||
print "FAILED!"
|
||||
sys.stderr.write(" Error while getting configuration!\n")
|
||||
sys.stderr.write(" " + err_msg + "\n")
|
||||
failed += 1
|
||||
else:
|
||||
res = compare_config(cfg, expected)
|
||||
if res:
|
||||
print "FAILED!"
|
||||
sys.stdout.write(" " + res + "\n")
|
||||
failed += 1
|
||||
else:
|
||||
expected_macros = expected.get("expected_macros", None)
|
||||
if expected_macros is not None:
|
||||
if sorted(expected_macros) != sorted(macros):
|
||||
print "FAILED!"
|
||||
sys.stderr.write(" List of macros doesn't match\n")
|
||||
sys.stderr.write(" Expected: '%s'\n" % ",".join(sorted(expected_macros)))
|
||||
sys.stderr.write(" Got: '%s'\n" % ",".join(sorted(expected_macros)))
|
||||
failed += 1
|
||||
else:
|
||||
print "OK"
|
||||
else:
|
||||
print "OK"
|
||||
sys.path.remove(full_name)
|
||||
return failed
|
||||
|
||||
failed = 0
|
||||
root_dir = os.path.abspath(os.path.dirname(__file__))
|
||||
tlist = sorted(os.listdir(root_dir), key = lambda e: int(e[4:]) if e.startswith('test') else -1)
|
||||
for test_name in tlist:
|
||||
full_name = os.path.join(root_dir, test_name)
|
||||
if os.path.isdir(full_name) and test_name.startswith('test'):
|
||||
failed += test_tree(full_name, test_name)
|
||||
sys.exit(failed)
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"b1": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"base1_1": "v_base1_1_b1",
|
||||
"base1_2": "v_base1_2_b1",
|
||||
"base1_3": "v_base1_3_b1"
|
||||
}
|
||||
},
|
||||
"d1": {
|
||||
"inherits": ["b1"],
|
||||
"config": {
|
||||
"derived1": "v_derived1_d1",
|
||||
"derived2": "v_derived2_d1"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_1": "v_base1_1_d1",
|
||||
"base1_2": "v_base1_2_d1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"config": {
|
||||
"base2_1": "v_base2_1_b2",
|
||||
"base2_2": "v_base2_2_b2"
|
||||
}
|
||||
},
|
||||
"f": {
|
||||
"inherits": ["b2", "d1"],
|
||||
"config": {
|
||||
"f1_1": "v_f1_1_f",
|
||||
"f1_2": "v_f1_2_f"
|
||||
},
|
||||
"overrides": {
|
||||
"base2_1": "v_base2_1_f",
|
||||
"base1_1": "v_base1_1_f",
|
||||
"derived2": "v_derived2_f",
|
||||
"f1_1": "v_f1_1_f_override"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
# Test for configuration data defined in targets
|
||||
# A base target (B1) defines 3 configuration parameters (base1_1, base1_2 and base1_3)
|
||||
# A derived target (D1) inherits drom B1 and defines one configuration parameters (derived1 and derived2) and overrides base1_1 and base1_2
|
||||
# Another base target (B2) defines its own configuration parameters (base2_1 and base2_2)
|
||||
# The final target F derives from B2 and D1, defines two configuration paramaters (f1_1 and f1_2)
|
||||
# and overrides base2_1, base1_1, derived2 and its own configuration parameter f1_1 (which is legal)
|
||||
# Final result:
|
||||
# base1_1 must have the value defined in F
|
||||
# base1_2 must have the value defined in D1
|
||||
# base1_3 must have the value defined in B1
|
||||
# derived1 must have the value defined in D1
|
||||
# derived2 must have the value defined in F
|
||||
# base2_1 must have the value defined in F
|
||||
# base2_2 must have the value defined in B2
|
||||
# f1_1 must have the value defined and then overriden in F
|
||||
# f1_2 must have the value defined in F
|
||||
|
||||
expected_results = {
|
||||
"f": {
|
||||
"desc": "test multiple target inheritance",
|
||||
"target.base1_1": "v_base1_1_f",
|
||||
"target.base1_2": "v_base1_2_d1",
|
||||
"target.base1_3": "v_base1_3_b1",
|
||||
"target.derived1": "v_derived1_d1",
|
||||
"target.derived2": "v_derived2_f",
|
||||
"target.base2_1": "v_base2_1_f",
|
||||
"target.base2_2": "v_base2_2_b2",
|
||||
"target.f1_1": "v_f1_1_f_override",
|
||||
"target.f1_2": "v_f1_2_f"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "test with a single base target, no inheritance",
|
||||
"target.base1_1": "v_base1_1_b1",
|
||||
"target.base1_2": "v_base1_2_b1",
|
||||
"target.base1_3": "v_base1_3_b1"
|
||||
},
|
||||
"d1": {
|
||||
"desc": "test single target inheritance",
|
||||
"target.base1_1": "v_base1_1_d1",
|
||||
"target.base1_2": "v_base1_2_d1",
|
||||
"target.base1_3": "v_base1_3_b1",
|
||||
"target.derived1": "v_derived1_d1",
|
||||
"target.derived2": "v_derived2_d1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"b1": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"base1_1": "v_base1_1_b1",
|
||||
"base1_2": "v_base1_2_b1",
|
||||
"base1_3": "v_base1_3_b1"
|
||||
}
|
||||
},
|
||||
"d1": {
|
||||
"inherits": ["b1"],
|
||||
"config": {
|
||||
"derived1": "v_derived1_d1",
|
||||
"derived2": "v_derived2_d1"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_1": "v_base1_1_d1",
|
||||
"base1_2": "v_base1_2_d1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["b1"],
|
||||
"config": {
|
||||
"base2_1": "v_base2_1_b2",
|
||||
"base2_2": "v_base2_2_b2"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_2": "v_base1_2_b2"
|
||||
}
|
||||
},
|
||||
"f": {
|
||||
"inherits": ["d1", "b2"],
|
||||
"config": {
|
||||
"f1_1": "v_f1_1_f",
|
||||
"f1_2": "v_f1_2_f"
|
||||
},
|
||||
"overrides": {
|
||||
"base2_1": "v_base2_1_f",
|
||||
"base1_1": "v_base1_1_f",
|
||||
"derived2": "v_derived2_f",
|
||||
"f1_1": "v_f1_1_f_override"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
# This is similar to test1, but this time B2 also inherits from B1, which allows it to override its config data.
|
||||
# B2 also overrides base1_2, like D1.
|
||||
# The order of inheritace in F is also reversed ([D1, B2] instead of [B2, D1])
|
||||
# Since the last override of base1_2 in inheritance order is in B2, base1_2 must now
|
||||
# have the value that was overriden in B2, not in D1.
|
||||
# This test also shows that multiple inheritance works for a simple diamond shaped inheritance pattern
|
||||
|
||||
expected_results = {
|
||||
"f": {
|
||||
"desc": "test multiple target inheritance (diamond shape)",
|
||||
"target.base1_1": "v_base1_1_f",
|
||||
"target.base1_2": "v_base1_2_b2",
|
||||
"target.base1_3": "v_base1_3_b1",
|
||||
"target.derived1": "v_derived1_d1",
|
||||
"target.derived2": "v_derived2_f",
|
||||
"target.base2_1": "v_base2_1_f",
|
||||
"target.base2_2": "v_base2_2_b2",
|
||||
"target.f1_1": "v_f1_1_f_override",
|
||||
"target.f1_2": "v_f1_2_f"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "another single inheritance test",
|
||||
"target.base1_1": "v_base1_1_b1",
|
||||
"target.base1_2": "v_base1_2_b2",
|
||||
"target.base1_3": "v_base1_3_b1",
|
||||
"target.base2_1": "v_base2_1_b2",
|
||||
"target.base2_2": "v_base2_2_b2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"b1": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"base1_1": "v_base1_1_b1",
|
||||
"base1_2": "v_base1_2_b1",
|
||||
"base1_3": "v_base1_3_b1"
|
||||
}
|
||||
},
|
||||
"d1": {
|
||||
"inherits": ["b1"],
|
||||
"config": {
|
||||
"derived1": "v_derived1_d1",
|
||||
"derived2": "v_derived2_d1"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_1": "v_base1_1_d1",
|
||||
"base1_2": "v_base1_2_d1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"config": {
|
||||
"base2_1": "v_base2_1_b2",
|
||||
"base2_2": "v_base2_2_b2"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_1": "v_base1_1_b2"
|
||||
}
|
||||
},
|
||||
"f": {
|
||||
"inherits": ["b2", "d1"],
|
||||
"config": {
|
||||
"f1_1": "v_f1_1_f",
|
||||
"f1_2": "v_f1_2_f"
|
||||
},
|
||||
"overrides": {
|
||||
"base2_1": "v_base2_1_f",
|
||||
"base1_1": "v_base1_1_f",
|
||||
"derived2": "v_derived2_f",
|
||||
"f1_1": "v_f1_1_f_override"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# Similar to test1, but this time B2 attempt to override base1_1. Since B2 doesn't directly inherit
|
||||
# from B1, this must raise an error
|
||||
|
||||
expected_results = {
|
||||
"f": {
|
||||
"desc": "attempt to override undefined parameter in inherited target",
|
||||
"exception_msg": "Attempt to override undefined parameter 'base1_1' in 'target:b2'"
|
||||
},
|
||||
"d1": {
|
||||
"desc": "single target inheritance again",
|
||||
"target.base1_1": "v_base1_1_d1",
|
||||
"target.base1_2": "v_base1_2_d1",
|
||||
"target.base1_3": "v_base1_3_b1",
|
||||
"target.derived1": "v_derived1_d1",
|
||||
"target.derived2": "v_derived2_d1"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"b1": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"base1_1": "v_base1_1_b1",
|
||||
"base1_2": "v_base1_2_b1",
|
||||
"base1_3": "v_base1_3_b1"
|
||||
}
|
||||
},
|
||||
"d1": {
|
||||
"inherits": ["b1"],
|
||||
"config": {
|
||||
"derived1": "v_derived1_d1",
|
||||
"derived2": "v_derived2_d1"
|
||||
},
|
||||
"overrides": {
|
||||
"base1_1": "v_base1_1_d1",
|
||||
"base1_2": "v_base1_2_d1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"base2_1": "v_base2_1_b2",
|
||||
"base2_2": "v_base2_2_b2",
|
||||
"base1_1": "v_base1_1_b2"
|
||||
}
|
||||
},
|
||||
"f": {
|
||||
"inherits": ["b2", "d1"],
|
||||
"config": {
|
||||
"f1_1": "v_f1_1_f",
|
||||
"f1_2": "v_f1_2_f"
|
||||
},
|
||||
"overrides": {
|
||||
"base2_1": "v_base2_1_f",
|
||||
"base1_1": "v_base1_1_f",
|
||||
"derived2": "v_derived2_f",
|
||||
"f1_1": "v_f1_1_f_override"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# Similar to test1, but this time B2 attempt to define base1_1. Since base1_1
|
||||
# is already defined in B1 and F derives from both B1 and B2, this results
|
||||
# in an error. However, when building for B2 instead of F, defining base1_1
|
||||
# should be OK.
|
||||
|
||||
expected_results = {
|
||||
"f": {
|
||||
"desc": "attempt to redefine parameter in target inheritance tree",
|
||||
"exception_msg": "Parameter name 'base1_1' defined in both 'target:b2' and 'target:b1'"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "it should be OK to define parameters with the same name in non-related targets",
|
||||
"target.base2_1": "v_base2_1_b2",
|
||||
"target.base2_2": "v_base2_2_b2",
|
||||
"target.base1_1": "v_base1_1_b2"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": []
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"]
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"]
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"app1": "v_app1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
},
|
||||
"dummy_label": {
|
||||
"app1": "dummy.app1",
|
||||
"app2": "dummy.app2"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# This tests overriding configuration values based on target labels.
|
||||
# Four targets are defined:
|
||||
# - "base" is the base target, it doesn't define any extra labels
|
||||
# - "b1" inherits from "base" and adds the "b1_label" label
|
||||
# - "b2" inherits from "base" and adds the "b2_label" label
|
||||
# - "both" inherits from both "b1" and "b2", so it inherits both labels
|
||||
|
||||
expected_results = {
|
||||
"b1": {
|
||||
"desc": "override values based on labels (first label)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]"
|
||||
},
|
||||
"both": {
|
||||
"desc": "override values based on labels (both labels)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2[b2_label]"
|
||||
},
|
||||
"base": {
|
||||
"desc": "override values based on labels (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib2[b1_label]",
|
||||
"p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p1": "v_p1_lib2[b2_label]",
|
||||
"p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p2": "v_p2_lib1[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": []
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"]
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"]
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"app1": "v_app1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
# This build on top of test5 by adding a few libraries with their own configurations
|
||||
# and overrides. The same targets are used for building and testing (base, b1, b2, both)
|
||||
|
||||
expected_results = {
|
||||
"base": {
|
||||
"desc": "override values based on labels with libs (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2",
|
||||
"lib2.p2": "v_p2_lib2"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "override values based on labels with libs (first label)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1[b1_label]",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2[b1_label]",
|
||||
"lib2.p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels with libs (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2[b2_label]",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]"
|
||||
},
|
||||
# The values for lib2.p1 and lib2.p2 illustrate how overriding on multiple
|
||||
# labels work. In lib2, both lib2.p1 and lib2.p2 are overriden for both
|
||||
# labels (b1_label and b2_label). However, since "b2_label" is specified
|
||||
# after "b1_label", it sets the final values of the overrides.
|
||||
"both": {
|
||||
"desc": "override values based on labels with libs (both labels)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1[b1_label]",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2[b2_label]",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]"
|
||||
},
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib2[b1_label]",
|
||||
"p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p1": "v_p1_lib2[b2_label]",
|
||||
"p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p2": "v_p2_lib1[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": []
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"]
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"]
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app"
|
||||
},
|
||||
"b1_label": {
|
||||
"app1": "v_app1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
# This build on top of test6 by adding overrides for libs in the application
|
||||
|
||||
expected_results = {
|
||||
"base": {
|
||||
"desc": "override values based on labels with libs (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "override values based on labels with libs (first label)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels with libs (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]"
|
||||
},
|
||||
# The values for lib2.p1 and lib2.p2 illustrate how overriding on multiple
|
||||
# labels work. In lib2, both lib2.p1 and lib2.p2 are overriden for both
|
||||
# labels (b1_label and b2_label). However, since "b2_label" is specified
|
||||
# after "b1_label", it sets the final values of the overrides.
|
||||
"both": {
|
||||
"desc": "override values based on labels with libs (both labels)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib2[b1_label]",
|
||||
"p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p1": "v_p1_lib2[b2_label]",
|
||||
"p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p2": "v_p2_lib1[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"par1": "v_par1_base",
|
||||
"par2": "v_par2_base",
|
||||
"par3": "v_par3_base"
|
||||
}
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"],
|
||||
"overrides": {
|
||||
"par1": "v_par1_b1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"],
|
||||
"overrides": {
|
||||
"par2": "v_par2_b2"
|
||||
}
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"],
|
||||
"config": {
|
||||
"par4": "v_par4_both"
|
||||
},
|
||||
"overrides": {
|
||||
"par3": "v_par3_both"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"target.par1": "v_par1_target_app"
|
||||
},
|
||||
"b1_label": {
|
||||
"app1": "v_app1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
# This build on top of test7 by adding some configuration values in targets
|
||||
# and overriding them in the application
|
||||
|
||||
expected_results = {
|
||||
"base": {
|
||||
"desc": "override values based on labels with libs and target params (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_base",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "override values based on labels with libs and target params (first label)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b1_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_base",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels with libs and target params (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_b2",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"both": {
|
||||
"desc": "override values based on labels with libs and target params (both labels)",
|
||||
"app.app1": "v_app1[b1_label]",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_b2",
|
||||
"target.par3": "v_par3_both",
|
||||
"target.par4": "v_par4_both"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib2[b1_label]",
|
||||
"p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p1": "v_p1_lib2[b2_label]",
|
||||
"p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p2": "v_p2_lib1[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": []
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"]
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"]
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"]
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"app1": "v_app1[b1_label]",
|
||||
"app_wrong": "v_dummy"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
# This build on top of test6 by adding an invalid override in mbed_app_override.json for b1_label.
|
||||
# This will prevent the configuration for working for b1 and both, but it should still
|
||||
# work for base and b2.
|
||||
|
||||
expected_results = {
|
||||
"base": {
|
||||
"desc": "override values based on labels with libs (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2",
|
||||
"lib2.p2": "v_p2_lib2"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "override values based on labels with libs - invalid override (first label)",
|
||||
"exception_msg": "Attempt to override undefined parameter 'app.app_wrong' in 'application[b1_label]"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels with libs (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2[b2_label]",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]"
|
||||
},
|
||||
"both": {
|
||||
"desc": "override values based on labels with libs - invalid override (both labels)",
|
||||
"exception_msg": "Attempt to override undefined parameter 'app.app_wrong' in 'application[b1_label]"
|
||||
},
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib2[b1_label]",
|
||||
"p2": "v_p2_lib2[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p1": "v_p1_lib2[b2_label]",
|
||||
"p2": "v_p2_lib2[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"b1_label": {
|
||||
"p1": "v_p1_lib1[b1_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"p2": "v_p2_lib1[b2_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"custom_targets": {
|
||||
"base": {
|
||||
"core": "Cortex-M0",
|
||||
"extra_labels": [],
|
||||
"config": {
|
||||
"par1": "v_par1_base",
|
||||
"par2": "v_par2_base",
|
||||
"par3": "v_par3_base"
|
||||
}
|
||||
},
|
||||
"b1": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b1_label"],
|
||||
"overrides": {
|
||||
"par1": "v_par1_b1"
|
||||
}
|
||||
},
|
||||
"b2": {
|
||||
"inherits": ["base"],
|
||||
"extra_labels_add": ["b2_label"],
|
||||
"overrides": {
|
||||
"par2": "v_par2_b2"
|
||||
}
|
||||
},
|
||||
"both": {
|
||||
"inherits": ["b1", "b2"],
|
||||
"config": {
|
||||
"par4": "v_par4_both"
|
||||
},
|
||||
"overrides": {
|
||||
"par3": "v_par3_both"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"app1": "v_app1",
|
||||
"app2": "v_app2"
|
||||
},
|
||||
"target_overrides": {
|
||||
"*": {
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"target.par1": "v_par1_target_app"
|
||||
},
|
||||
"b1_label": {
|
||||
"app.app1": "v_app1[b1_label_label]"
|
||||
},
|
||||
"b2_label": {
|
||||
"app2": "v_app2[b2_label]"
|
||||
},
|
||||
"both": {
|
||||
"target.par4": "v_par4_app[both_label]",
|
||||
"lib2.p1": "v_p1_lib2_app[both_label]"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
# This builds on top of test8 by adding target-conditional overrides in mbed_app_config.json.
|
||||
|
||||
expected_results = {
|
||||
"base": {
|
||||
"desc": "override values based on labels with libs, target params and target overrides (no labels)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_base",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"b1": {
|
||||
"desc": "override values based on labels with libs, target params and target overrides (first label)",
|
||||
"app.app1": "v_app1[b1_label_label]",
|
||||
"app.app2": "v_app2",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b1_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_base",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"b2": {
|
||||
"desc": "override values based on labels with libs, target params and target overrides (second label)",
|
||||
"app.app1": "v_app1",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_b2",
|
||||
"target.par3": "v_par3_base"
|
||||
},
|
||||
"both": {
|
||||
"desc": "override values based on labels with libs, target params and target overrides (both labels)",
|
||||
"app.app1": "v_app1[b1_label_label]",
|
||||
"app.app2": "v_app2[b2_label]",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1[b2_label]",
|
||||
"lib1.p3": "v_p3_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app[both_label]",
|
||||
"lib2.p2": "v_p2_lib2[b2_label]",
|
||||
"target.par1": "v_par1_target_app",
|
||||
"target.par2": "v_par2_b2",
|
||||
"target.par3": "v_par3_both",
|
||||
"target.par4": "v_par4_app[both_label]"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
},
|
||||
"target_overrides": {
|
||||
"K64F": {
|
||||
"p1": "v_p1_lib1[b1_label]",
|
||||
"lib2.p1": "dummy"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# Two libraries (lib1 and lib2) define their own configuration parameters
|
||||
# lib1 tries to override a configuration parameter in lib2, which raises an error
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "lib1 trying to override a config parameter in lib2",
|
||||
"exception_msg": "Invalid prefix 'lib2' for parameter name 'lib2.p1' in 'library:lib1[K64F]'"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"config": {
|
||||
"p1": "v_p1_lib2",
|
||||
"p2": "v_p2_lib2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"config": {
|
||||
"p1": "v_p1_lib1",
|
||||
"p2": "v_p2_lib1",
|
||||
"p3": "v_p3_lib1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"target_overrides": {
|
||||
"K64F": {
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib2.p1": "v_p1_lib2_app"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
# Two libraries (lib1 and lib2) define their own configuration parameters
|
||||
# The application config doesn't have any parameters itself, it just overrides the parameter
|
||||
# named p1 from both lib1 and lib2.
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "app without its own parameters overrides parameters in other libs",
|
||||
"lib1.p1": "v_p1_lib1_app",
|
||||
"lib1.p2": "v_p2_lib1",
|
||||
"lib1.p3": "v_p3_lib1",
|
||||
"lib2.p1": "v_p1_lib2_app",
|
||||
"lib2.p2": "v_p2_lib2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "lib1"
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"name": "lib1"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Create two libraries named "lib1", which must raise an error
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "attempt to configure two libraries named 'lib1'",
|
||||
"exception_msg": "Library name 'lib1' is not unique"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"unknown_key": "dummy_value"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Put an invalid key in the application configuration
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "invalid key in mbed_app_config.json",
|
||||
"exception_msg": "Unknown key(s)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"unknown_key": "dummy_value"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Put an invalid key in the library configuration
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "invalid key in mbed_lib_config.json",
|
||||
"exception_msg": "Unknown key(s)"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"macros": ["LIB1_1=1", "LIB1_2"]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"macros": ["LIB2_1=5", "LIB1_2"]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"macros": ["APP1=10", "APP2", "LIB2_1=5"]
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
# Macro test: defined macros in the top level app and 2 libs, check if they
|
||||
# are reported properly.
|
||||
# The app defines one macro with value the same as lib2, while lib2 defined
|
||||
# the same macro without value as lib1. Since the definitions are comptabile,
|
||||
# no error should be raised
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "test macro definitions",
|
||||
"expected_macros": ["APP1=10", "APP2", "LIB1_1=1","LIB1_2", "LIB2_1=5"]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"macros": ["LIB1_1=1", "LIB1_2"]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"macros": ["LIB2_1=5", "LIB1_2"]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"macros": ["APP1=10", "APP2", "LIB2_1=10"]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# Build on top of test16
|
||||
# Adds an invalid macro redefinition in the app
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "test invalid macro re-definition in the app",
|
||||
"exception_msg": "Macro 'LIB2_1' defined in both 'library:lib2' and 'application' with incompatible values"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"macros": ["LIB1_1=1", "LIB1_2"]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "lib2",
|
||||
"macros": ["LIB2_1=5", "LIB1_2=3"]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"macros": ["APP1=10", "APP2", "LIB2_1=5"]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Like test17, but this time the invalid re-definition is in lib2, not in the app
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "test invalid macro re-definition in a library",
|
||||
"exception_msg": "defined in both"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"macros": [],
|
||||
"invalid_key": "invalid_value"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Test that invalid keys in application configuration are not allowed
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "test invalid key in application config",
|
||||
"exception_msg": "Unknown key(s) 'invalid_key'"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "lib1",
|
||||
"macros": [],
|
||||
"invalid_key": "invalid_value"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# Like test19, but this time check invalid key in a library configuration
|
||||
|
||||
expected_results = {
|
||||
"K64F": {
|
||||
"desc": "test invalid key in lib config",
|
||||
"exception_msg": "Unknown key(s) 'invalid_key'"
|
||||
}
|
||||
}
|
|
@ -34,7 +34,6 @@ import tools.hooks as hooks
|
|||
from hashlib import md5
|
||||
import fnmatch
|
||||
|
||||
|
||||
#Disables multiprocessing if set to higher number than the host machine CPUs
|
||||
CPU_COUNT_MIN = 1
|
||||
|
||||
|
@ -55,6 +54,7 @@ def compile_worker(job):
|
|||
'results': results
|
||||
}
|
||||
|
||||
|
||||
class Resources:
|
||||
def __init__(self, base_path=None):
|
||||
self.base_path = base_path
|
||||
|
@ -82,6 +82,7 @@ class Resources:
|
|||
# Other files
|
||||
self.hex_files = []
|
||||
self.bin_files = []
|
||||
self.json_files = []
|
||||
|
||||
def __add__(self, resources):
|
||||
if resources is None:
|
||||
|
@ -118,12 +119,15 @@ class Resources:
|
|||
|
||||
self.hex_files += resources.hex_files
|
||||
self.bin_files += resources.bin_files
|
||||
self.json_files += resources.json_files
|
||||
|
||||
return self
|
||||
|
||||
def relative_to(self, base, dot=False):
|
||||
for field in ['inc_dirs', 'headers', 's_sources', 'c_sources',
|
||||
'cpp_sources', 'lib_dirs', 'objects', 'libraries',
|
||||
'lib_builds', 'lib_refs', 'repo_dirs', 'repo_files', 'hex_files', 'bin_files']:
|
||||
'lib_builds', 'lib_refs', 'repo_dirs', 'repo_files',
|
||||
'hex_files', 'bin_files', 'json_files']:
|
||||
v = [rel_path(f, base, dot) for f in getattr(self, field)]
|
||||
setattr(self, field, v)
|
||||
if self.linker_script is not None:
|
||||
|
@ -132,7 +136,8 @@ class Resources:
|
|||
def win_to_unix(self):
|
||||
for field in ['inc_dirs', 'headers', 's_sources', 'c_sources',
|
||||
'cpp_sources', 'lib_dirs', 'objects', 'libraries',
|
||||
'lib_builds', 'lib_refs', 'repo_dirs', 'repo_files', 'hex_files', 'bin_files']:
|
||||
'lib_builds', 'lib_refs', 'repo_dirs', 'repo_files',
|
||||
'hex_files', 'bin_files', 'json_files']:
|
||||
v = [f.replace('\\', '/') for f in getattr(self, field)]
|
||||
setattr(self, field, v)
|
||||
if self.linker_script is not None:
|
||||
|
@ -164,7 +169,6 @@ class Resources:
|
|||
|
||||
return '\n'.join(s)
|
||||
|
||||
|
||||
# Support legacy build conventions: the original mbed build system did not have
|
||||
# standard labels for the "TARGET_" and "TOOLCHAIN_" specific directories, but
|
||||
# had the knowledge of a list of these directories to be ignored.
|
||||
|
@ -235,7 +239,7 @@ class mbedToolchain:
|
|||
self.CHROOT = None
|
||||
|
||||
self.mp_pool = None
|
||||
|
||||
|
||||
if 'UVISOR_PRESENT=1' in self.macros:
|
||||
self.target.core = re.sub(r"F$", '', self.target.core)
|
||||
|
||||
|
@ -329,6 +333,10 @@ class mbedToolchain:
|
|||
|
||||
return list(set(self.symbols)) # Return only unique symbols
|
||||
|
||||
# Extend the internal list of macros
|
||||
def add_macros(self, new_macros):
|
||||
self.macros.extend(new_macros)
|
||||
|
||||
def get_labels(self):
|
||||
if self.labels is None:
|
||||
toolchain_labels = [c.__name__ for c in getmro(self.__class__)]
|
||||
|
@ -472,6 +480,9 @@ class mbedToolchain:
|
|||
elif ext == '.bin':
|
||||
resources.bin_files.append(file_path)
|
||||
|
||||
elif ext == '.json':
|
||||
resources.json_files.append(file_path)
|
||||
|
||||
return resources
|
||||
|
||||
def scan_repository(self, path):
|
||||
|
|
|
@ -21,7 +21,8 @@ from os import listdir, remove, makedirs
|
|||
from shutil import copyfile
|
||||
from os.path import isdir, join, exists, split, relpath, splitext
|
||||
from subprocess import Popen, PIPE, STDOUT, call
|
||||
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
|
||||
def cmd(l, check=True, verbose=False, shell=False, cwd=None):
|
||||
text = l if shell else ' '.join(l)
|
||||
|
@ -174,3 +175,23 @@ def check_required_modules(required_modules, verbose=True):
|
|||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
# Utility function: traverse a dictionary and change all the strings in the dictionary to
|
||||
# ASCII from Unicode. Useful when reading ASCII JSON data, because the JSON decoder always
|
||||
# returns Unicode string.
|
||||
# Based on http://stackoverflow.com/a/13105359
|
||||
def dict_to_ascii(input):
|
||||
if isinstance(input, dict):
|
||||
return OrderedDict([(dict_to_ascii(key), dict_to_ascii(value)) for key, value in input.iteritems()])
|
||||
elif isinstance(input, list):
|
||||
return [dict_to_ascii(element) for element in input]
|
||||
elif isinstance(input, unicode):
|
||||
return input.encode('ascii')
|
||||
else:
|
||||
return input
|
||||
|
||||
# Read a JSON file and return its Python representation, transforming all the strings from Unicode
|
||||
# to ASCII. The order of keys in the JSON file is preserved.
|
||||
def json_file_to_dict(fname):
|
||||
with open(fname, "rt") as f:
|
||||
return dict_to_ascii(json.load(f, object_pairs_hook=OrderedDict))
|
||||
|
|
Loading…
Reference in New Issue