Skip to content

Add support for custom_targets.json #160

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ repos:
hooks:
- id: licenseheaders
exclude: \.yaml$|\.yml$
args: ["-t", "ci_scripts/templates/.copyright.tmpl", "-cy", "-f"]
args: ["-s", "ci_scripts/licenseheaders.json", "-t",
"ci_scripts/templates/.copyright.tmpl", "-cy", "-f"]

- repo: https://github.com/psf/black
rev: 19.10b0
Expand Down
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,20 @@ matrix:
- mbedtools compile -t GCC_ARM -m K64F
- ccache -s

- <<: *mbed-tools-test
name: "Test custom target"
env: NAME=test-import-cmd CACHE_NAME=test-import-cmd
script:
- mbedtools new custom-target-test
- cd custom-target-test
- cp $TRAVIS_BUILD_DIR/travis-ci/test-data/custom_targets.json .
- mkdir TARGET_IMAGINARYBOARD
- cp $TRAVIS_BUILD_DIR/travis-ci/test-data/TARGET_IMAGINARYBOARD/* TARGET_IMAGINARYBOARD
- cp mbed-os/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/P* TARGET_IMAGINARYBOARD
- sed -i '/add_subdirectory(${MBED_PATH})/i add_subdirectory(TARGET_IMAGINARYBOARD)' CMakeLists.txt
- mbedtools compile -t GCC_ARM -m IMAGINARYBOARD
- ccache -s

- <<: *mbed-tools-test
name: "Test deploy command checks out tip of default branch"
env: NAME=test-deploy-cmd CACHE_NAME=test-deploy-cmd
Expand Down
15 changes: 15 additions & 0 deletions ci_scripts/licenseheaders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"cmake": {
"extensions": [],
"filenames": ["CMakeLists.txt"],
"keepFirst": "",
"blockCommentStartPattern": "",
"blockCommentEndPattern": "",
"lineCommentStartPattern": "^\\s*#",
"lineCommentEndPattern": "",
"headerStartLine": "",
"headerEndLine": "",
"headerLinePrefix": "# ",
"headerLineSuffix": ""
}
}
1 change: 1 addition & 0 deletions news/20201222090211.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Support custom_targets.json.
20 changes: 3 additions & 17 deletions src/mbed_tools/build/_internal/cmake_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,16 @@
TEMPLATE_NAME = "mbed_config.tmpl"


def generate_mbed_config_cmake_file(mbed_target_name: str, config: Config, toolchain_name: str) -> str:
"""Generate mbed_config.cmake containing the correct definitions for a build.

Args:
mbed_target_name: the target the application is being built for
config: Config object holding information parsed from the mbed config system.
toolchain_name: the toolchain to be used to build the application

Returns:
A string of rendered contents for the file.
"""
return _render_mbed_config_cmake_template(config, toolchain_name, mbed_target_name,)


def _render_mbed_config_cmake_template(config: Config, toolchain_name: str, target_name: str) -> str:
"""Renders the mbed_config template with the relevant information.
def render_mbed_config_cmake_template(config: Config, toolchain_name: str, target_name: str) -> str:
"""Renders the mbed_config jinja template with the target and project config settings.

Args:
config: Config object holding information parsed from the mbed config system.
toolchain_name: Name of the toolchain being used.
target_name: Name of the target.

Returns:
The contents of the rendered CMake file.
The rendered mbed_config template.
"""
env = jinja2.Environment(loader=jinja2.PackageLoader("mbed_tools.build", str(TEMPLATES_DIRECTORY)),)
template = env.get_template(TEMPLATE_NAME)
Expand Down
8 changes: 4 additions & 4 deletions src/mbed_tools/build/_internal/write_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from mbed_tools.build.exceptions import InvalidExportOutputDirectory


def write_file(output_directory: pathlib.Path, file_name: str, file_contents: str) -> None:
"""Writes out a file to a directory.
def write_file(file_path: pathlib.Path, file_contents: str) -> None:
"""Writes out a string to a file.

If the intermediate directories to the output directory don't exist,
this function will create them.
Expand All @@ -19,9 +19,9 @@ def write_file(output_directory: pathlib.Path, file_name: str, file_contents: st
Raises:
InvalidExportOutputDirectory: it's not possible to export to the output directory provided
"""
output_directory = file_path.parent
if output_directory.is_file():
raise InvalidExportOutputDirectory("Output directory cannot be a path to a file.")

output_directory.mkdir(parents=True, exist_ok=True)
output_file = output_directory.joinpath(file_name)
output_file.write_text(file_contents)
file_path.write_text(file_contents)
32 changes: 28 additions & 4 deletions src/mbed_tools/build/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
"""Parses the Mbed configuration system and generates a CMake config script."""
import pathlib

from typing import Any

from mbed_tools.lib.json_helpers import decode_json_file
from mbed_tools.project import MbedProgram
from mbed_tools.targets import get_target_by_name
from mbed_tools.build._internal.cmake_file import generate_mbed_config_cmake_file
from mbed_tools.build._internal.cmake_file import render_mbed_config_cmake_template
from mbed_tools.build._internal.config.assemble_build_config import assemble_config
from mbed_tools.build._internal.write_files import write_file
from mbed_tools.build.exceptions import MbedBuildError


def generate_config(target_name: str, toolchain: str, program: MbedProgram) -> pathlib.Path:
Expand All @@ -23,9 +27,29 @@ def generate_config(target_name: str, toolchain: str, program: MbedProgram) -> p
Returns:
Path to the generated config file.
"""
target_build_attributes = get_target_by_name(target_name, program.mbed_os.targets_json_file)
targets_data = _load_raw_targets_data(program)
target_build_attributes = get_target_by_name(target_name, targets_data)
config = assemble_config(target_build_attributes, program.root, program.files.app_config_file)
cmake_file_contents = generate_mbed_config_cmake_file(target_name, config, toolchain)
cmake_file_contents = render_mbed_config_cmake_template(
target_name=target_name, config=config, toolchain_name=toolchain,
)
cmake_config_file_path = program.files.cmake_config_file
write_file(cmake_config_file_path.parent, cmake_config_file_path.name, cmake_file_contents)
write_file(cmake_config_file_path, cmake_file_contents)
return cmake_config_file_path


def _load_raw_targets_data(program: MbedProgram) -> Any:
targets_data = decode_json_file(program.mbed_os.targets_json_file)
if program.files.custom_targets_json.exists():
custom_targets_data = decode_json_file(program.files.custom_targets_json)
for custom_target in custom_targets_data:
if custom_target in targets_data:
raise MbedBuildError(
f"Error found in {program.files.custom_targets_json}.\n"
f"A target with the name '{custom_target}' already exists in targets.json. "
"Please give your custom target a unique name so it can be identified."
)

targets_data.update(custom_targets_data)

return targets_data
8 changes: 8 additions & 0 deletions src/mbed_tools/cli/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
@click.option(
"--mbed-os-path", type=click.Path(), default=None, help="Path to local Mbed OS directory.",
)
@click.option(
"--custom-targets-json", type=click.Path(), default=None, help="Path to custom_targets.json.",
)
@click.option(
"-f", "--flash", is_flag=True, default=False, help="Flash the binary onto a device",
)
Expand All @@ -60,6 +63,7 @@ def build(
sterm: bool = False,
baudrate: int = 9600,
mbed_os_path: str = None,
custom_targets_json: str = None,
) -> None:
"""Configure and build an Mbed project using CMake and Ninja.

Expand All @@ -73,6 +77,7 @@ def build(
program_path: Path to the Mbed project.
mbed_os_path: the path to the local Mbed OS directory
profile: The Mbed build profile (debug, develop or release).
custom_targets_json: Path to custom_targets.json.
toolchain: The toolchain to use for the build.
mbed_target: The name of the Mbed target to build for.
clean: Perform a clean build.
Expand All @@ -93,6 +98,9 @@ def build(
if any([not mbed_config_file.exists(), not build_tree.exists(), mbed_target, toolchain]):
click.echo("Configuring project and generating build system...")
_validate_target_and_toolchain_args(mbed_target, toolchain)
if custom_targets_json is not None:
program.files.custom_targets_json = pathlib.Path(custom_targets_json)

generate_config(mbed_target.upper(), toolchain, program)
generate_build_system(program.root, build_tree, profile)

Expand Down
3 changes: 2 additions & 1 deletion src/mbed_tools/project/_internal/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def checkout(repo: git.Repo, ref: str, force: bool = False) -> None:
VersionControlError: Check out failed.
"""
try:
repo.git.checkout(f"--force {ref}" if force else str(ref))
git_args = [ref] + ["--force"] if force else [ref]
repo.git.checkout(*git_args)
except git.exc.GitCommandError as err:
raise VersionControlError(f"Failed to check out revision '{ref}'. Error from VCS: {err}")

Expand Down
6 changes: 6 additions & 0 deletions src/mbed_tools/project/_internal/project_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
MBED_OS_REFERENCE_FILE_NAME = "mbed-os.lib"
MBED_OS_DIR_NAME = "mbed-os"
TARGETS_JSON_FILE_PATH = Path("targets", "targets.json")
CUSTOM_TARGETS_JSON_FILE_NAME = "custom_targets.json"

# Information written to mbed-os.lib
MBED_OS_REFERENCE_URL = "https://github.com/ARMmbed/mbed-os"
Expand Down Expand Up @@ -59,6 +60,7 @@ class MbedProgramFiles:
cmakelists_file: Path
cmake_config_file: Path
cmake_build_dir: Path
custom_targets_json: Path

@classmethod
def from_new(cls, root_path: Path) -> "MbedProgramFiles":
Expand All @@ -79,6 +81,7 @@ def from_new(cls, root_path: Path) -> "MbedProgramFiles":
gitignore = root_path / ".gitignore"
cmake_config = root_path / CMAKE_CONFIG_FILE_PATH
cmake_build_dir = root_path / CMAKE_BUILD_DIR
custom_targets_json = root_path / CUSTOM_TARGETS_JSON_FILE_NAME

if mbed_os_ref.exists():
raise ValueError(f"Program already exists at path {root_path}.")
Expand All @@ -94,6 +97,7 @@ def from_new(cls, root_path: Path) -> "MbedProgramFiles":
cmakelists_file=cmakelists_file,
cmake_config_file=cmake_config,
cmake_build_dir=cmake_build_dir,
custom_targets_json=custom_targets_json,
)

@classmethod
Expand All @@ -109,6 +113,7 @@ def from_existing(cls, root_path: Path) -> "MbedProgramFiles":
logger.info("This program does not contain an mbed_app.json config file.")
app_config = None

custom_targets_json = root_path / CUSTOM_TARGETS_JSON_FILE_NAME
mbed_os_file = root_path / MBED_OS_REFERENCE_FILE_NAME

cmakelists_file = root_path / CMAKELISTS_FILE_NAME
Expand All @@ -121,6 +126,7 @@ def from_existing(cls, root_path: Path) -> "MbedProgramFiles":
cmakelists_file=cmakelists_file,
cmake_config_file=root_path / CMAKE_CONFIG_FILE_PATH,
cmake_build_dir=root_path / CMAKE_BUILD_DIR,
custom_targets_json=custom_targets_json,
)


Expand Down
2 changes: 0 additions & 2 deletions src/mbed_tools/project/_internal/templates/CMakeLists.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ add_executable(${APP_TARGET}

mbed_configure_app_target(${APP_TARGET})

mbed_set_mbed_target_linker_script(${APP_TARGET})

project(${APP_TARGET})

target_link_libraries(${APP_TARGET} mbed-os)
Expand Down
38 changes: 7 additions & 31 deletions src/mbed_tools/targets/_internal/target_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
This information is parsed from the targets.json configuration file
found in the mbed-os repo.
"""
import json
import pathlib
from json.decoder import JSONDecodeError
from typing import Dict, Any, Set, Optional, cast
from typing import Dict, Any, Set, Optional

from mbed_tools.lib.exceptions import ToolsError
from mbed_tools.lib.json_helpers import decode_json_file

from mbed_tools.targets._internal.targets_json_parsers.accumulating_attribute_parser import (
get_accumulating_attributes_for_target,
Expand All @@ -39,25 +38,22 @@ class TargetNotFoundError(TargetAttributesError):
"""Target definition not found in targets.json."""


def get_target_attributes(path_to_targets_json: pathlib.Path, target_name: str) -> dict:
def get_target_attributes(targets_json_data: dict, target_name: str) -> dict:
"""Retrieves attribute data taken from targets.json for a single target.

Args:
path_to_targets_json: an absolute or relative path to the location of targets.json.
targets_json_data: target definitions from targets.json
target_name: the name of the target (often a Board's board_type).

Returns:
A dictionary representation of the attributes for the target.

Raises:
FileNotFoundError: path provided does not lead to targets.json
ParsingTargetJSONError: error parsing targets.json
TargetNotFoundError: there is no target attribute data found for that target.
"""
targets_json_path = path_to_targets_json
all_targets_data = _read_json_file(targets_json_path)
target_attributes = _extract_target_attributes(all_targets_data, target_name)
target_attributes["labels"] = get_labels_for_target(all_targets_data, target_name).union(
target_attributes = _extract_target_attributes(targets_json_data, target_name)
target_attributes["labels"] = get_labels_for_target(targets_json_data, target_name).union(
_extract_core_labels(target_attributes.get("core", None))
)
target_attributes["extra_labels"] = set(target_attributes.get("extra_labels", []))
Expand All @@ -70,26 +66,6 @@ def get_target_attributes(path_to_targets_json: pathlib.Path, target_name: str)
return target_attributes


def _read_json_file(path_to_file: pathlib.Path) -> dict:
"""Reads the data from a json file.

Args:
path_to_file: location of the json file.

Returns:
A dictionary representation of all the data in the json file.

Raises:
ParsingTargetJSONError: error parsing targets.json
FileNotFoundError: path provided does not lead to a valid json file
"""
try:
# mypy doesn't recognise that json.loads always returns a dictionary
return cast(dict, json.loads(path_to_file.read_text()))
except JSONDecodeError as json_err:
raise ParsingTargetsJSONError(f"Invalid JSON found in '{path_to_file}'.") from json_err


def _extract_target_attributes(all_targets_data: Dict[str, Any], target_name: str) -> dict:
"""Extracts the definition for a particular target from all the targets in targets.json.

Expand Down Expand Up @@ -127,7 +103,7 @@ def _extract_core_labels(target_core: Optional[str]) -> Set[str]:
if either core is undefined or no labels found for the core.
"""
if target_core:
mbed_os_metadata = _read_json_file(MBED_OS_METADATA_FILE)
mbed_os_metadata = decode_json_file(MBED_OS_METADATA_FILE)
return set(mbed_os_metadata["CORE_LABELS"].get(target_core, []))
return set()

Expand Down
14 changes: 6 additions & 8 deletions src/mbed_tools/targets/get_target.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,38 @@
An instance of `mbed_tools.targets.target.Target`
can be retrieved by calling one of the public functions.
"""
import pathlib

from mbed_tools.targets.exceptions import TargetError
from mbed_tools.targets._internal import target_attributes


def get_target_by_name(name: str, path_to_targets_json: pathlib.Path) -> dict:
def get_target_by_name(name: str, targets_json_data: dict) -> dict:
"""Returns a dictionary of attributes for the target whose name matches the name given.

The target is as defined in the targets.json file found in the Mbed OS library.

Args:
name: the name of the Target to be returned
path_to_targets_json: path to a targets.json file containing target definitions
targets_json_data: target definitions from targets.json

Raises:
TargetError: an error has occurred while fetching target
"""
try:
return target_attributes.get_target_attributes(path_to_targets_json, name)
return target_attributes.get_target_attributes(targets_json_data, name)
except (FileNotFoundError, target_attributes.TargetAttributesError) as e:
raise TargetError(e) from e


def get_target_by_board_type(board_type: str, path_to_targets_json: pathlib.Path) -> dict:
def get_target_by_board_type(board_type: str, targets_json_data: dict) -> dict:
"""Returns the target whose name matches a board's build_type.

The target is as defined in the targets.json file found in the Mbed OS library.

Args:
board_type: a board's board_type (see `mbed_tools.targets.board.Board`)
path_to_targets_json: path to a targets.json file containing target definitions
targets_json_data: target definitions from targets.json

Raises:
TargetError: an error has occurred while fetching target
"""
return get_target_by_name(board_type, path_to_targets_json)
return get_target_by_name(board_type, targets_json_data)
Loading