summaryrefslogtreecommitdiff
path: root/zephyr/zmake
diff options
context:
space:
mode:
Diffstat (limited to 'zephyr/zmake')
-rw-r--r--zephyr/zmake/setup.py4
-rw-r--r--zephyr/zmake/tests/test_project.py208
-rw-r--r--zephyr/zmake/tests/test_toolchains.py15
-rw-r--r--zephyr/zmake/tests/test_util.py52
-rw-r--r--zephyr/zmake/tests/test_version.py15
-rw-r--r--zephyr/zmake/tests/test_zmake.py24
-rw-r--r--zephyr/zmake/zmake/__main__.py19
-rw-r--r--zephyr/zmake/zmake/configlib.py40
-rw-r--r--zephyr/zmake/zmake/project.py185
-rw-r--r--zephyr/zmake/zmake/toolchains.py4
-rw-r--r--zephyr/zmake/zmake/util.py63
-rw-r--r--zephyr/zmake/zmake/version.py7
-rw-r--r--zephyr/zmake/zmake/zmake.py167
13 files changed, 386 insertions, 417 deletions
diff --git a/zephyr/zmake/setup.py b/zephyr/zmake/setup.py
index 4328dc48d7..51001c6695 100644
--- a/zephyr/zmake/setup.py
+++ b/zephyr/zmake/setup.py
@@ -24,8 +24,8 @@ setuptools.setup(
# requirements files see:
# https://packaging.python.org/en/latest/requirements.html
install_requires=[
- "jsonschema>=3.2.0",
- "pyyaml>=3.13",
+ # Required until chroot upgrades to Python 3.7+.
+ "dataclasses>=0.6; python_version < '3.7'",
],
# To provide executable scripts, use entry points in preference to the
# "scripts" keyword. Entry points provide cross-platform support and allow
diff --git a/zephyr/zmake/tests/test_project.py b/zephyr/zmake/tests/test_project.py
index 2442ceedf6..f7688784e7 100644
--- a/zephyr/zmake/tests/test_project.py
+++ b/zephyr/zmake/tests/test_project.py
@@ -11,28 +11,13 @@ import hypothesis.strategies as st
import pytest
import zmake.modules
+import zmake.output_packers
import zmake.project
board_names = st.text(alphabet=set(string.ascii_lowercase) | {"_"}, min_size=1)
sets_of_board_names = st.lists(st.lists(board_names, unique=True))
-class TemporaryProject(tempfile.TemporaryDirectory):
- """A temporary project wrapper.
-
- Args:
- config: The config dictionary to be used with the project.
- """
-
- def __init__(self, config):
- self.config = config
- super().__init__()
-
- def __enter__(self):
- project_path = pathlib.Path(super().__enter__())
- return zmake.project.Project(project_path, config_dict=self.config)
-
-
@hypothesis.given(sets_of_board_names)
@hypothesis.settings(deadline=None)
def test_find_dts_overlays(modules):
@@ -67,21 +52,21 @@ def test_find_dts_overlays(modules):
board_file_mapping[board] = files | {file_name}
for board, expected_dts_files in board_file_mapping.items():
- with TemporaryProject(
- {
- "board": board,
- "output-type": "elf",
- "supported-toolchains": ["llvm"],
- "supported-zephyr-versions": ["v2.6"],
- }
- ) as project:
- config = project.find_dts_overlays(dict(enumerate(module_paths)))
-
- actual_dts_files = set(
- config.cmake_defs.get("DTC_OVERLAY_FILE", "").split(";")
+ project = zmake.project.Project(
+ zmake.project.ProjectConfig(
+ project_name=board,
+ zephyr_board=board,
+ output_packer=zmake.output_packers.ElfPacker,
+ supported_toolchains=["llvm"],
+ project_dir=pathlib.Path("/fakebuild"),
)
+ )
+ config = project.find_dts_overlays(dict(enumerate(module_paths)))
+ actual_dts_files = set(
+ config.cmake_defs.get("DTC_OVERLAY_FILE", "").split(";")
+ )
- assert actual_dts_files == set(map(str, expected_dts_files))
+ assert actual_dts_files == set(map(str, expected_dts_files))
setup_modules_and_dispatch(modules, testcase)
@@ -101,16 +86,17 @@ def test_prune_modules(modules):
for name in zmake.modules.known_modules
}
- with TemporaryProject(
- {
- "board": "native_posix",
- "output-type": "elf",
- "supported-toolchains": ["coreboot-sdk"],
- "supported-zephyr-versions": ["v2.6"],
- "modules": modules,
- }
- ) as project:
- assert set(project.prune_modules(module_paths)) == set(modules)
+ project = zmake.project.Project(
+ zmake.project.ProjectConfig(
+ project_name="prunetest",
+ zephyr_board="native_posix",
+ output_packer=zmake.output_packers.ElfPacker,
+ supported_toolchains=["coreboot-sdk"],
+ project_dir=pathlib.Path("/fake"),
+ modules=modules,
+ ),
+ )
+ assert set(project.prune_modules(module_paths)) == set(modules)
def test_prune_modules_unavailable():
@@ -122,17 +108,18 @@ def test_prune_modules_unavailable():
"hal_stm32": pathlib.Path("/mod/halstm"),
}
- with TemporaryProject(
- {
- "board": "native_posix",
- "output-type": "elf",
- "supported-toolchains": ["coreboot-sdk"],
- "supported-zephyr-versions": ["v2.6"],
- "modules": ["hal_stm32", "cmsis"],
- }
- ) as project:
- with pytest.raises(KeyError):
- project.prune_modules(module_paths)
+ project = zmake.project.Project(
+ zmake.project.ProjectConfig(
+ project_name="prunetest",
+ zephyr_board="native_posix",
+ output_packer=zmake.output_packers.ElfPacker,
+ supported_toolchains=["coreboot-sdk"],
+ project_dir=pathlib.Path("/fake"),
+ modules=["hal_stm32", "cmsis"],
+ ),
+ )
+ with pytest.raises(KeyError):
+ project.prune_modules(module_paths)
def test_find_projects_empty(tmp_path):
@@ -141,33 +128,104 @@ def test_find_projects_empty(tmp_path):
assert len(projects) == 0
-YAML_FILE = """
-supported-zephyr-versions:
- - v2.6
-supported-toolchains:
- - coreboot-sdk
-output-type: npcx
+CONFIG_FILE_1 = """
+register_raw_project(project_name="one", zephyr_board="one")
+register_host_test(test_name="two")
+register_npcx_project(project_name="three", zephyr_board="three")
+register_binman_project(project_name="four", zephyr_board="four")
+"""
+
+CONFIG_FILE_2 = """
+register_raw_project(
+ project_name="five",
+ zephyr_board="foo",
+ dts_overlays=[here / "gpio.dts"],
+)
"""
def test_find_projects(tmp_path):
"""Test the find_projects method when there are projects."""
- dir = tmp_path.joinpath("one")
- dir.mkdir()
- dir.joinpath("zmake.yaml").write_text("board: one\n" + YAML_FILE)
- tmp_path.joinpath("two").mkdir()
- dir = tmp_path.joinpath("two/a")
- dir.mkdir()
- dir.joinpath("zmake.yaml").write_text("board: twoa\nis-test: true\n" + YAML_FILE)
- dir = tmp_path.joinpath("two/b")
- dir.mkdir()
- dir.joinpath("zmake.yaml").write_text("board: twob\n" + YAML_FILE)
- projects = list(zmake.project.find_projects(tmp_path))
- projects.sort(key=lambda x: x.project_dir)
- assert len(projects) == 3
- assert projects[0].project_dir == tmp_path.joinpath("one")
- assert projects[1].project_dir == tmp_path.joinpath("two/a")
- assert projects[2].project_dir == tmp_path.joinpath("two/b")
- assert not projects[0].config.is_test
- assert projects[1].config.is_test
- assert not projects[2].config.is_test
+ cf1_dir = tmp_path / "cf1"
+ cf1_dir.mkdir()
+ (cf1_dir / "BUILD.py").write_text(CONFIG_FILE_1)
+
+ cf2_bb_dir = tmp_path / "cf2_bb"
+ cf2_bb_dir.mkdir()
+ cf2_dir = cf2_bb_dir / "cf2"
+ cf2_dir.mkdir()
+ (cf2_dir / "BUILD.py").write_text(CONFIG_FILE_2)
+
+ projects = zmake.project.find_projects(tmp_path)
+ assert len(projects) == 5
+ assert projects["one"].config.project_dir == cf1_dir
+ assert not projects["one"].config.is_test
+
+ assert projects["test-two"].config.project_dir == cf1_dir
+ assert projects["test-two"].config.zephyr_board == "native_posix"
+ assert projects["test-two"].config.is_test
+
+ assert projects["three"].config.project_dir == cf1_dir
+ assert not projects["three"].config.is_test
+ assert projects["three"].config.zephyr_board == "three"
+
+ assert projects["four"].config.project_dir == cf1_dir
+ assert not projects["four"].config.is_test
+ assert projects["four"].config.zephyr_board == "four"
+
+ assert projects["five"].config.project_dir == cf2_dir
+ assert not projects["five"].config.is_test
+ assert projects["five"].config.zephyr_board == "foo"
+
+
+def test_find_projects_name_conflict(tmp_path):
+ """When two projects define the same name, that should be an error."""
+ cf1_dir = tmp_path / "cf1"
+ cf1_dir.mkdir()
+ (cf1_dir / "BUILD.py").write_text(CONFIG_FILE_2)
+
+ cf2_dir = tmp_path / "cf2"
+ cf2_dir.mkdir()
+ (cf2_dir / "BUILD.py").write_text(CONFIG_FILE_2)
+
+ with pytest.raises(KeyError):
+ zmake.project.find_projects(tmp_path)
+
+
+@pytest.mark.parametrize(
+ ("actual_files", "config_files", "expected_files"),
+ [
+ (["prj_link.conf"], [], []),
+ (["prj.conf"], [], ["prj.conf"]),
+ (
+ ["prj.conf", "cfg.conf"],
+ ["prj.conf", "cfg.conf"],
+ ["prj.conf", "cfg.conf"],
+ ),
+ (
+ ["prj.conf", "prj_samus.conf", "prj_link.conf"],
+ ["prj_link.conf"],
+ ["prj.conf", "prj_link.conf"],
+ ),
+ ],
+)
+def test_kconfig_files(tmp_path, actual_files, config_files, expected_files):
+ for name in actual_files:
+ (tmp_path / name).write_text("")
+
+ project = zmake.project.Project(
+ zmake.project.ProjectConfig(
+ project_name="samus",
+ zephyr_board="lm4",
+ output_packer=zmake.output_packers.RawBinPacker,
+ supported_toolchains=["coreboot-sdk"],
+ project_dir=tmp_path,
+ kconfig_files=[tmp_path / name for name in config_files],
+ ),
+ )
+
+ builds = list(project.iter_builds())
+ assert len(builds) == 1
+
+ _, config = builds[0]
+ assert sorted(f.name for f in config.kconfig_files) == sorted(expected_files)
diff --git a/zephyr/zmake/tests/test_toolchains.py b/zephyr/zmake/tests/test_toolchains.py
index 515f54a112..fb1953052a 100644
--- a/zephyr/zmake/tests/test_toolchains.py
+++ b/zephyr/zmake/tests/test_toolchains.py
@@ -7,6 +7,7 @@ import pathlib
import pytest
+import zmake.output_packers
import zmake.project as project
import zmake.toolchains as toolchains
@@ -62,18 +63,18 @@ def zephyr_exists(mockfs):
@pytest.fixture
def fake_project(tmp_path):
return project.Project(
- tmp_path,
- config_dict={
- "board": "foo",
- "supported-zephyr-versions": ["v2.6"],
- "supported-toolchains": [
+ project.ProjectConfig(
+ project_name="foo",
+ zephyr_board="foo",
+ supported_toolchains=[
"coreboot-sdk",
"host",
"llvm",
"zephyr",
],
- "output-type": "raw",
- },
+ output_packer=zmake.output_packers.RawBinPacker,
+ project_dir=tmp_path,
+ ),
)
diff --git a/zephyr/zmake/tests/test_util.py b/zephyr/zmake/tests/test_util.py
index 0c4cd4dda5..438c5efcf0 100644
--- a/zephyr/zmake/tests/test_util.py
+++ b/zephyr/zmake/tests/test_util.py
@@ -3,7 +3,6 @@
# found in the LICENSE file.
import pathlib
-import re
import tempfile
import hypothesis
@@ -13,57 +12,6 @@ import pytest
import zmake.util as util
# Strategies for use with hypothesis
-relative_path = st.from_regex(
- regex=re.compile(r"\A\w{1,255}(/\w{1,255}){0,15}\Z", re.ASCII)
-)
-
-
-@hypothesis.given(relative_path, relative_path, relative_path)
-@hypothesis.settings(deadline=60000)
-def test_resolve_build_dir_with_build_dir(
- platform_ec_subdir, project_subdir, build_subdir
-):
- with tempfile.TemporaryDirectory() as temp_dir_name:
- platform_ec_dir = pathlib.Path(temp_dir_name) / platform_ec_subdir
- build_dir = util.resolve_build_dir(
- platform_ec_dir=platform_ec_dir,
- project_dir=platform_ec_dir / project_subdir,
- build_dir=platform_ec_dir / build_subdir,
- )
-
- assert build_dir == platform_ec_dir / build_subdir
-
-
-@hypothesis.given(relative_path, relative_path)
-@hypothesis.settings(deadline=60000)
-def test_resolve_build_dir_invalid_project(platform_ec_subdir, project_subdir):
- try:
- with tempfile.TemporaryDirectory() as temp_dir_name:
- platform_ec_dir = pathlib.Path(temp_dir_name) / platform_ec_subdir
- util.resolve_build_dir(
- platform_ec_dir=platform_ec_dir,
- project_dir=platform_ec_dir / project_subdir,
- build_dir=None,
- )
- pytest.fail()
- except Exception:
- pass
-
-
-@hypothesis.given(relative_path, relative_path)
-@hypothesis.settings(deadline=60000)
-def test_resolve_build_dir_from_project(platform_ec_subdir, project_subdir):
- with tempfile.TemporaryDirectory() as temp_dir_name:
- platform_ec_dir = pathlib.Path(temp_dir_name) / platform_ec_subdir
- project_dir = platform_ec_dir / project_subdir
- project_dir.mkdir(parents=True)
- (project_dir / "zmake.yaml").touch()
- build_dir = util.resolve_build_dir(
- platform_ec_dir=platform_ec_dir, project_dir=project_dir, build_dir=None
- )
- assert build_dir == platform_ec_dir / "build" / project_subdir
-
-
version_integers = st.integers(min_value=0)
version_tuples = st.tuples(version_integers, version_integers, version_integers)
diff --git a/zephyr/zmake/tests/test_version.py b/zephyr/zmake/tests/test_version.py
index a238a8ac02..b2c6b43fec 100644
--- a/zephyr/zmake/tests/test_version.py
+++ b/zephyr/zmake/tests/test_version.py
@@ -8,6 +8,7 @@ import unittest.mock as mock
import pytest
+import zmake.output_packers
import zmake.project
import zmake.version as version
@@ -51,13 +52,13 @@ def _setup_example_repos(tmp_path):
project_path.mkdir()
project = zmake.project.Project(
- project_path,
- config_dict={
- "board": "foo",
- "output-type": "raw",
- "supported-toolchains": ["coreboot-sdk"],
- "supported-zephyr-versions": ["v2.6"],
- },
+ zmake.project.ProjectConfig(
+ project_name="prj",
+ zephyr_board="foo",
+ output_packer=zmake.output_packers.RawBinPacker,
+ supported_toolchains=["coreboot-sdk"],
+ project_dir=project_path,
+ ),
)
# Has one commit.
zephyr_base = tmp_path / "zephyr_base"
diff --git a/zephyr/zmake/tests/test_zmake.py b/zephyr/zmake/tests/test_zmake.py
index 163159b9c5..f735d942ed 100644
--- a/zephyr/zmake/tests/test_zmake.py
+++ b/zephyr/zmake/tests/test_zmake.py
@@ -18,6 +18,7 @@ from testfixtures import LogCapture
import zmake.build_config
import zmake.jobserver
import zmake.multiproc as multiproc
+import zmake.output_packers
import zmake.project
import zmake.toolchains
import zmake.zmake as zm
@@ -33,10 +34,14 @@ class FakeProject:
def __init__(self):
self.packer = mock.Mock()
self.packer.pack_firmware = mock.Mock(return_value=[])
- self.project_dir = pathlib.Path("FakeProjectDir")
- self.config = mock.Mock()
- self.config.supported_zephyr_versions = [(2, 5)]
+ self.config = zmake.project.ProjectConfig(
+ project_name="fakeproject",
+ zephyr_board="fakeboard",
+ supported_toolchains=["llvm"],
+ output_packer=zmake.output_packers.ElfPacker,
+ project_dir=pathlib.Path("FakeProjectDir"),
+ )
@staticmethod
def iter_builds():
@@ -123,12 +128,10 @@ def do_test_with_log_level(log_level, use_configure=False, fnames=None):
re.compile(r".*build-rw"): get_test_filepath("rw"),
}
zephyr_base = mock.Mock()
- zephyr_root = mock.Mock()
zmk = zm.Zmake(
jobserver=FakeJobserver(fnames),
zephyr_base=zephyr_base,
- zephyr_root=zephyr_root,
)
with LogCapture(level=log_level) as cap:
@@ -142,13 +145,16 @@ VERSION_TWEAK = 0
EXTRAVERSION =
"""
)
+ (pathlib.Path(tmpname) / "project_name.txt").write_text("fakeproject")
zephyr_base.resolve = mock.Mock(return_value=pathlib.Path(tmpname))
with patch("zmake.version.get_version_string", return_value="123"):
- with patch.object(zmake.project, "Project", return_value=FakeProject()):
+ with patch.object(
+ zmake.project,
+ "find_projects",
+ return_value={"fakeproject": FakeProject()},
+ ):
if use_configure:
- zmk.configure(
- pathlib.Path(tmpname), build_dir=pathlib.Path("build")
- )
+ zmk.configure("fakeproject", build_dir=pathlib.Path("build"))
else:
with patch("zmake.version.write_version_header", autospec=True):
zmk.build(pathlib.Path(tmpname))
diff --git a/zephyr/zmake/zmake/__main__.py b/zephyr/zmake/zmake/__main__.py
index ea639584cc..aef897d1d3 100644
--- a/zephyr/zmake/zmake/__main__.py
+++ b/zephyr/zmake/zmake/__main__.py
@@ -161,21 +161,11 @@ def main(argv=None):
parser.add_argument(
"--zephyr-base", type=pathlib.Path, help="Path to Zephyr OS repository"
)
- parser.add_argument(
- "--zephyr-root",
- type=pathlib.Path,
- help="Path to Zephyr OS repos, must contain subdirs like v1.2",
- )
sub = parser.add_subparsers(dest="subcommand", help="Subcommand")
sub.required = True
configure = sub.add_parser("configure")
- configure.add_argument(
- "--ignore-unsupported-zephyr-version",
- action="store_true",
- help="Don't warn about using an unsupported Zephyr version",
- )
configure.add_argument("-t", "--toolchain", help="Name of toolchain to use")
configure.add_argument(
"--bringup",
@@ -184,6 +174,12 @@ def main(argv=None):
help="Enable bringup debugging features",
)
configure.add_argument(
+ "--allow-warnings",
+ action="store_true",
+ default=False,
+ help="Do not treat warnings as errors",
+ )
+ configure.add_argument(
"-B", "--build-dir", type=pathlib.Path, help="Build directory"
)
configure.add_argument(
@@ -200,7 +196,8 @@ def main(argv=None):
help="Test the .elf file after configuration",
)
configure.add_argument(
- "project_dir", type=pathlib.Path, help="Path to the project to build"
+ "project_name_or_dir",
+ help="Path to the project to build",
)
configure.add_argument(
"-c",
diff --git a/zephyr/zmake/zmake/configlib.py b/zephyr/zmake/zmake/configlib.py
new file mode 100644
index 0000000000..3c6aa649c5
--- /dev/null
+++ b/zephyr/zmake/zmake/configlib.py
@@ -0,0 +1,40 @@
+# Copyright 2021 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""This module defines helpers accessible to all BUILD.py files."""
+
+import zmake.output_packers
+
+
+def _register_project(**kwargs):
+ kwargs.setdefault("project_dir", here) # noqa: F821
+ register_project(**kwargs) # noqa: F821
+
+
+def register_host_project(**kwargs):
+ kwargs.setdefault("zephyr_board", "native_posix")
+ kwargs.setdefault("supported_toolchains", ["llvm", "host"])
+ kwargs.setdefault("output_packer", zmake.output_packers.ElfPacker)
+ _register_project(**kwargs)
+
+
+def register_host_test(test_name, **kwargs):
+ kwargs.setdefault("is_test", True)
+ register_host_project(project_name="test-{}".format(test_name), **kwargs)
+
+
+def register_raw_project(**kwargs):
+ kwargs.setdefault("supported_toolchains", ["coreboot-sdk", "zephyr"])
+ kwargs.setdefault("output_packer", zmake.output_packers.RawBinPacker)
+ _register_project(**kwargs)
+
+
+def register_binman_project(**kwargs):
+ kwargs.setdefault("output_packer", zmake.output_packers.BinmanPacker)
+ register_raw_project(**kwargs)
+
+
+def register_npcx_project(**kwargs):
+ kwargs.setdefault("output_packer", zmake.output_packers.NpcxPacker)
+ register_binman_project(**kwargs)
diff --git a/zephyr/zmake/zmake/project.py b/zephyr/zmake/zmake/project.py
index 7ffe4ebc19..0e2e97fd14 100644
--- a/zephyr/zmake/zmake/project.py
+++ b/zephyr/zmake/zmake/project.py
@@ -3,24 +3,14 @@
# found in the LICENSE file.
"""Module for project config wrapper object."""
+import dataclasses
import logging
import pathlib
-import warnings
-
-import yaml
import zmake.build_config as build_config
+import zmake.configlib as configlib
import zmake.modules as modules
-import zmake.output_packers as packers
import zmake.toolchains as toolchains
-import zmake.util as util
-
-# The version of jsonschema in the chroot has a bunch of
-# DeprecationWarnings that fire when we import it. Suppress these
-# during the import to keep the noise down.
-with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- import jsonschema
def module_dts_overlay_name(modpath, board_name):
@@ -36,121 +26,85 @@ def module_dts_overlay_name(modpath, board_name):
return modpath / "zephyr" / "dts" / "board-overlays" / "{}.dts".format(board_name)
-def find_projects(root_dir):
- """Finds all zmake projects in root_dir.
+def load_config_file(path):
+ """Load a BUILD.py config file and create associated projects.
Args:
- root_dir: the root dir as a pathlib.Path object
+ path: A pathlib.Path to the BUILD.py file.
- Yields:
- Project: The next project found.
+ Returns:
+ A list of Project objects specified by the file.
"""
- logging.info("Finding zmake targets under '%s'.", root_dir)
- for path in pathlib.Path(root_dir).rglob("zmake.yaml"):
- yield Project(path.parent)
+ projects = []
+ def register_project(**kwargs):
+ projects.append(Project(ProjectConfig(**kwargs)))
-class ProjectConfig:
- """An object wrapping zmake.yaml."""
-
- validator = jsonschema.Draft7Validator
- schema = {
- "type": "object",
- "required": [
- "board",
- "output-type",
- "supported-toolchains",
- "supported-zephyr-versions",
- ],
- "properties": {
- "supported-zephyr-versions": {
- "type": "array",
- "items": {
- "type": "string",
- "enum": ["v2.6", "v2.7", "v2.8"],
- },
- "minItems": 1,
- "uniqueItems": True,
- },
- "board": {
- "type": "string",
- },
- "modules": {
- "type": "array",
- "items": {
- "type": "string",
- "enum": list(modules.known_modules),
- },
- },
- "output-type": {
- "type": "string",
- "enum": list(packers.packer_registry),
- },
- "supported-toolchains": {
- "type": "array",
- "items": {
- "type": "string",
- "enum": list(toolchains.support_classes),
- },
- },
- "is-test": {
- "type": "boolean",
- },
- "dts-overlays": {
- "type": "array",
- "items": {
- "type": "string",
- },
- },
- },
+ # The Python environment passed to the config file.
+ config_globals = {
+ "register_project": register_project,
+ "here": path.parent.resolve(),
}
- def __init__(self, config_dict):
- self.validator.check_schema(self.schema)
- jsonschema.validate(config_dict, self.schema, cls=self.validator)
- self.config_dict = config_dict
+ # First, load the global helper functions.
+ code = compile(
+ pathlib.Path(configlib.__file__).read_bytes(),
+ configlib.__file__,
+ "exec",
+ )
+ exec(code, config_globals)
- @property
- def supported_zephyr_versions(self):
- return [
- util.parse_zephyr_version(x)
- for x in self.config_dict["supported-zephyr-versions"]
- ]
+ # Next, load the BUILD.py
+ logging.info("Loading config file %s", path)
+ code = compile(path.read_bytes(), str(path), "exec")
+ exec(code, config_globals)
+ logging.info("Config file %s defines %s projects", path, len(projects))
+ return projects
- @property
- def board(self):
- return self.config_dict["board"]
- @property
- def modules(self):
- return self.config_dict.get("modules", list(modules.known_modules))
+def find_projects(root_dir):
+ """Finds all zmake projects in root_dir.
- @property
- def output_packer(self):
- return packers.packer_registry[self.config_dict["output-type"]]
+ Args:
+ root_dir: the root dir as a pathlib.Path object
- @property
- def supported_toolchains(self):
- return self.config_dict["supported-toolchains"]
+ Returns:
+ A dictionary mapping project names to Project objects.
+ """
+ logging.info("Finding zmake targets under '%s'.", root_dir)
+ found_projects = {}
+ for path in pathlib.Path(root_dir).rglob("BUILD.py"):
+ for project in load_config_file(path):
+ if project.config.project_name in found_projects:
+ raise KeyError(
+ "Duplicate project defined: {} (in {})".format(
+ project.config.project_name, path
+ )
+ )
+ found_projects[project.config.project_name] = project
+ return found_projects
- @property
- def is_test(self):
- return self.config_dict.get("is-test", False)
- @property
- def dts_overlays(self):
- return self.config_dict.get("dts-overlays", [])
+@dataclasses.dataclass
+class ProjectConfig:
+ project_name: str
+ zephyr_board: str
+ supported_toolchains: "list[str]"
+ output_packer: type
+ modules: "list[str]" = dataclasses.field(
+ default_factory=lambda: modules.known_modules,
+ )
+ is_test: bool = dataclasses.field(default=False)
+ dts_overlays: "list[str]" = dataclasses.field(default_factory=list)
+ kconfig_files: "list[pathlib.Path]" = dataclasses.field(default_factory=list)
+ project_dir: pathlib.Path = dataclasses.field(default_factory=pathlib.Path)
class Project:
"""An object encapsulating a project directory."""
- def __init__(self, project_dir, config_dict=None):
- self.project_dir = project_dir.resolve()
- if not config_dict:
- with open(self.project_dir / "zmake.yaml") as f:
- config_dict = yaml.safe_load(f)
- self.config = ProjectConfig(config_dict)
+ def __init__(self, config):
+ self.config = config
self.packer = self.config.output_packer(self)
def iter_builds(self):
@@ -159,10 +113,15 @@ class Project:
Yields:
2-tuples of a build configuration name and a BuildConfig.
"""
- conf = build_config.BuildConfig(cmake_defs={"BOARD": self.config.board})
- prj_conf = self.project_dir / "prj.conf"
+ conf = build_config.BuildConfig(cmake_defs={"BOARD": self.config.zephyr_board})
+
+ kconfig_files = []
+ prj_conf = self.config.project_dir / "prj.conf"
if prj_conf.is_file():
- conf |= build_config.BuildConfig(kconfig_files=[prj_conf])
+ kconfig_files.append(prj_conf)
+ kconfig_files.extend(self.config.kconfig_files)
+ conf |= build_config.BuildConfig(kconfig_files=kconfig_files)
+
for build_name, packer_config in self.packer.configs():
yield build_name, conf | packer_config
@@ -178,11 +137,11 @@ class Project:
"""
overlays = []
for module_path in modules.values():
- dts_path = module_dts_overlay_name(module_path, self.config.board)
+ dts_path = module_dts_overlay_name(module_path, self.config.zephyr_board)
if dts_path.is_file():
overlays.append(dts_path.resolve())
- overlays.extend(self.project_dir / f for f in self.config.dts_overlays)
+ overlays.extend(self.config.dts_overlays)
if overlays:
return build_config.BuildConfig(
@@ -218,7 +177,7 @@ class Project:
except KeyError as e:
raise KeyError(
"The {!r} module is required by the {} project, but is not "
- "available.".format(module, self.project_dir)
+ "available.".format(module, self.config.project_dir)
) from e
return result
diff --git a/zephyr/zmake/zmake/toolchains.py b/zephyr/zmake/zmake/toolchains.py
index 6e25301b7d..671c539c0f 100644
--- a/zephyr/zmake/zmake/toolchains.py
+++ b/zephyr/zmake/zmake/toolchains.py
@@ -26,7 +26,7 @@ class GenericToolchain:
# know if it's installed. Simply return False to indicate not
# installed. An unknown toolchain would only be used if -t
# was manually passed to zmake, and is not valid to put in a
- # zmake.yaml file.
+ # BUILD.py file.
return False
def get_build_config(self):
@@ -107,7 +107,7 @@ class ZephyrToolchain(GenericToolchain):
if not self.zephyr_sdk_install_dir:
raise RuntimeError(
"No installed Zephyr SDK was found"
- " (see docs/zephyr_build.md for documentation)"
+ " (see docs/zephyr/zephyr_build.md for documentation)"
)
tc_vars = {
"ZEPHYR_SDK_INSTALL_DIR": str(self.zephyr_sdk_install_dir),
diff --git a/zephyr/zmake/zmake/util.py b/zephyr/zmake/zmake/util.py
index 0908993267..ee3b245b78 100644
--- a/zephyr/zmake/zmake/util.py
+++ b/zephyr/zmake/zmake/util.py
@@ -64,19 +64,6 @@ def locate_cros_checkout():
raise FileNotFoundError("Unable to locate a ChromiumOS checkout")
-def locate_zephyr_base(zephyr_root, version):
- """Locate the path to the Zephyr RTOS in a ChromiumOS checkout.
-
- Args:
- checkout: The path to the ChromiumOS checkout.
- version: The requested zephyr version, as a tuple of integers.
-
- Returns:
- The path to the Zephyr source.
- """
- return zephyr_root / "v{}.{}".format(*version[:2])
-
-
def read_kconfig_file(path):
"""Parse a Kconfig file.
@@ -132,23 +119,6 @@ def write_kconfig_file(path, config, only_if_changed=True):
f.write("{}={}\n".format(name, value))
-def parse_zephyr_version(version_string):
- """Parse a human-readable version string (e.g., "v2.4") as a tuple.
-
- Args:
- version_string: The human-readable version string.
-
- Returns:
- A 2-tuple or 3-tuple of integers representing the version.
- """
- match = re.fullmatch(r"v?(\d+)[._](\d+)(?:[._](\d+))?", version_string)
- if not match:
- raise ValueError(
- "{} does not look like a Zephyr version.".format(version_string)
- )
- return tuple(int(x) for x in match.groups() if x is not None)
-
-
def read_zephyr_version(zephyr_base):
"""Read the Zephyr version from a Zephyr OS checkout.
@@ -213,36 +183,3 @@ def log_multi_line(logger, level, message):
for line in message.splitlines():
if line:
logger.log(level, line)
-
-
-def resolve_build_dir(platform_ec_dir, project_dir, build_dir):
- """Resolve the build directory using platform/ec/build/... as default.
-
- Args:
- platform_ec_dir: The path to the chromiumos source's platform/ec
- directory.
- project_dir: The directory of the project.
- build_dir: The directory to build in (may be None).
- Returns:
- The resolved build directory (using build_dir if not None).
- """
- if build_dir:
- return build_dir
-
- if not pathlib.Path.exists(project_dir / "zmake.yaml"):
- raise OSError("Invalid configuration")
-
- # Resolve project_dir to absolute path.
- project_dir = project_dir.resolve()
-
- # Compute the path of project_dir relative to platform_ec_dir.
- project_relative_path = pathlib.Path.relative_to(project_dir, platform_ec_dir)
-
- # Make sure that the project_dir is a subdirectory of platform_ec_dir.
- if platform_ec_dir / project_relative_path != project_dir:
- raise OSError(
- "Can't resolve project directory {} which is not a subdirectory"
- " of the platform/ec directory {}".format(project_dir, platform_ec_dir)
- )
-
- return platform_ec_dir / "build" / project_relative_path
diff --git a/zephyr/zmake/zmake/version.py b/zephyr/zmake/zmake/version.py
index 47aba6d804..b2b897cf5b 100644
--- a/zephyr/zmake/zmake/version.py
+++ b/zephyr/zmake/zmake/version.py
@@ -90,7 +90,6 @@ def get_version_string(project, zephyr_base, modules, static=False):
the build for the OS.
"""
major_version, minor_version, *_ = util.read_zephyr_version(zephyr_base)
- project_id = project.project_dir.parts[-1]
num_commits = 0
if static:
@@ -116,7 +115,11 @@ def get_version_string(project, zephyr_base, modules, static=False):
)
return "{}_v{}.{}.{}-{}".format(
- project_id, major_version, minor_version, num_commits, vcs_hashes
+ project.config.project_name,
+ major_version,
+ minor_version,
+ num_commits,
+ vcs_hashes,
)
diff --git a/zephyr/zmake/zmake/zmake.py b/zephyr/zmake/zmake/zmake.py
index 0fa30640c9..d2559a9270 100644
--- a/zephyr/zmake/zmake/zmake.py
+++ b/zephyr/zmake/zmake/zmake.py
@@ -153,16 +153,17 @@ class Zmake:
jobs=0,
modules_dir=None,
zephyr_base=None,
- zephyr_root=None,
):
zmake.multiproc.reset()
self._checkout = checkout
- self._zephyr_base = zephyr_base
- if zephyr_root:
- self._zephyr_root = zephyr_root
+ if zephyr_base:
+ self.zephyr_base = zephyr_base
else:
- self._zephyr_root = (
- self.checkout / "src" / "third_party" / "zephyr" / "main"
+ # TODO(b/205884929): Drop v2.7 from path. This is
+ # intentionally hard-coded here as an intermediate step to
+ # cutting over to the main branch.
+ self.zephyr_base = (
+ self.checkout / "src" / "third_party" / "zephyr" / "main" / "v2.7"
)
if modules_dir:
@@ -188,59 +189,62 @@ class Zmake:
self._checkout = util.locate_cros_checkout()
return self._checkout.resolve()
- def locate_zephyr_base(self, version):
- """Locate the Zephyr OS repository.
-
- Args:
- version: If a Zephyr OS base was not supplied to Zmake,
- which version to search for as a tuple of integers.
- This argument is ignored if a Zephyr base was supplied
- to Zmake.
- Returns:
- A pathlib.Path to the found Zephyr OS repository.
- """
- if self._zephyr_base:
- return self._zephyr_base
-
- return util.locate_zephyr_base(self._zephyr_root, version)
-
def configure(
self,
- project_dir,
+ project_name_or_dir,
build_dir=None,
toolchain=None,
- ignore_unsupported_zephyr_version=False,
build_after_configure=False,
test_after_configure=False,
bringup=False,
coverage=False,
+ allow_warnings=False,
):
- """Set up a build directory to later be built by "zmake build"."""
- project = zmake.project.Project(project_dir)
- supported_versions = project.config.supported_zephyr_versions
-
- zephyr_base = self.locate_zephyr_base(max(supported_versions)).resolve()
-
- # Ignore the patchset from the Zephyr version.
- zephyr_version = util.read_zephyr_version(zephyr_base)[:2]
-
- if (
- not ignore_unsupported_zephyr_version
- and zephyr_version not in supported_versions
- ):
- raise ValueError(
- "The Zephyr OS version (v{}.{}) is not supported by the "
- "project. You may wish to either configure zmake.yaml to "
- "support this version, or pass "
- "--ignore-unsupported-zephyr-version.".format(*zephyr_version)
- )
-
- # Resolve build_dir if needed.
- build_dir = util.resolve_build_dir(
- platform_ec_dir=self.module_paths["ec"],
- project_dir=project_dir,
+ """Locate a project by name or directory and then call _configure."""
+ root_dir = pathlib.Path(project_name_or_dir)
+ if not root_dir.is_dir():
+ root_dir = self.module_paths["ec"] / "zephyr"
+ found_projects = zmake.project.find_projects(root_dir)
+ if len(found_projects) == 1:
+ # Likely passed directory path, wants to build only
+ # project from there.
+ project = next(iter(found_projects.values()))
+ else:
+ try:
+ project = found_projects[project_name_or_dir]
+ except KeyError as e:
+ raise KeyError("No project named {}".format(project_name_or_dir)) from e
+ return self._configure(
+ project=project,
build_dir=build_dir,
+ toolchain=toolchain,
+ build_after_configure=build_after_configure,
+ test_after_configure=test_after_configure,
+ bringup=bringup,
+ coverage=coverage,
+ allow_warnings=allow_warnings,
)
+
+ def _configure(
+ self,
+ project,
+ build_dir=None,
+ toolchain=None,
+ build_after_configure=False,
+ test_after_configure=False,
+ bringup=False,
+ coverage=False,
+ allow_warnings=False,
+ ):
+ """Set up a build directory to later be built by "zmake build"."""
+ # Resolve build_dir if needed.
+ if not build_dir:
+ build_dir = (
+ self.module_paths["ec"]
+ / "build"
+ / "zephyr"
+ / project.config.project_name
+ )
# Make sure the build directory is clean.
if os.path.exists(build_dir):
self.logger.info("Clearing old build directory %s", build_dir)
@@ -248,8 +252,9 @@ class Zmake:
generated_include_dir = (build_dir / "include").resolve()
base_config = zmake.build_config.BuildConfig(
- environ_defs={"ZEPHYR_BASE": str(zephyr_base), "PATH": "/usr/bin"},
+ environ_defs={"ZEPHYR_BASE": str(self.zephyr_base), "PATH": "/usr/bin"},
cmake_defs={
+ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
"DTS_ROOT": str(self.module_paths["ec"] / "zephyr"),
"SYSCALL_INCLUDE_DIRS": str(
self.module_paths["ec"] / "zephyr" / "include" / "drivers"
@@ -267,7 +272,7 @@ class Zmake:
# Symlink the Zephyr base into the build directory so it can
# be used in the build phase.
- util.update_symlink(zephyr_base, build_dir / "zephyr_base")
+ util.update_symlink(self.zephyr_base, build_dir / "zephyr_base")
dts_overlay_config = project.find_dts_overlays(module_paths)
@@ -282,15 +287,21 @@ class Zmake:
base_config |= zmake.build_config.BuildConfig(
kconfig_defs={"CONFIG_COVERAGE": "y"}
)
+ if allow_warnings:
+ base_config |= zmake.build_config.BuildConfig(
+ cmake_defs={"ALLOW_WARNINGS": "ON"}
+ )
if not build_dir.exists():
build_dir = build_dir.mkdir()
if not generated_include_dir.exists():
generated_include_dir.mkdir()
processes = []
- self.logger.info("Building %s in %s.", project_dir, build_dir)
+ self.logger.info("Building %s in %s.", project.config.project_name, build_dir)
for build_name, build_config in project.iter_builds():
- self.logger.info("Configuring %s:%s.", project_dir, build_name)
+ self.logger.info(
+ "Configuring %s:%s.", project.config.project_name, build_name
+ )
config = (
base_config
| toolchain_config
@@ -302,7 +313,7 @@ class Zmake:
kconfig_file = build_dir / "kconfig-{}.conf".format(build_name)
proc = config.popen_cmake(
self.jobserver,
- project_dir,
+ project.config.project_dir,
output_dir,
kconfig_file,
stdin=subprocess.DEVNULL,
@@ -311,7 +322,7 @@ class Zmake:
encoding="utf-8",
errors="replace",
)
- job_id = "{}:{}".format(project_dir, build_name)
+ job_id = "{}:{}".format(project.config.project_name, build_name)
zmake.multiproc.log_output(
self.logger,
logging.DEBUG,
@@ -335,8 +346,10 @@ class Zmake:
if proc.wait():
raise OSError(get_process_failure_msg(proc))
- # Create symlink to project
- util.update_symlink(project_dir, build_dir / "project")
+ # To reconstruct a Project object later, we need to know the
+ # name and project directory.
+ (build_dir / "project_name.txt").write_text(project.config.project_name)
+ util.update_symlink(project.config.project_dir, build_dir / "project")
if test_after_configure:
rv = self.test(build_dir=build_dir)
@@ -393,7 +406,8 @@ class Zmake:
dirs = {}
build_dir = build_dir.resolve()
- project = zmake.project.Project(build_dir / "project")
+ found_projects = zmake.project.find_projects(build_dir / "project")
+ project = found_projects[(build_dir / "project_name.txt").read_text()]
# Compute the version string.
version_string = zmake.version.get_version_string(
@@ -477,7 +491,8 @@ class Zmake:
self.build(build_dir, output_files_out=output_files)
# If the project built but isn't a test, just bail.
- project = zmake.project.Project(build_dir / "project")
+ found_projects = zmake.project.find_projects(build_dir / "project")
+ project = found_projects[(build_dir / "project_name.txt").read_text()]
if not project.config.is_test:
return 0
@@ -514,17 +529,19 @@ class Zmake:
def testall(self):
"""Test all the valid test targets"""
tmp_dirs = []
- for project in zmake.project.find_projects(self.module_paths["ec"] / "zephyr"):
+ for project in zmake.project.find_projects(
+ self.module_paths["ec"] / "zephyr"
+ ).values():
is_test = project.config.is_test
temp_build_dir = tempfile.mkdtemp(
- suffix="-{}".format(os.path.basename(project.project_dir.as_posix())),
+ suffix="-{}".format(project.config.project_name),
prefix="zbuild-",
)
tmp_dirs.append(temp_build_dir)
# Configure and run the test.
self.executor.append(
- func=lambda: self.configure(
- project_dir=project.project_dir,
+ func=lambda: self._configure(
+ project=project,
build_dir=pathlib.Path(temp_build_dir),
build_after_configure=True,
test_after_configure=is_test,
@@ -601,9 +618,9 @@ class Zmake:
return 0
def _coverage_compile_only(self, project, build_dir, lcov_file):
- self.logger.info("Building %s in %s", project.project_dir, build_dir)
- rv = self.configure(
- project_dir=project.project_dir,
+ self.logger.info("Building %s in %s", project.config.project_name, build_dir)
+ rv = self._configure(
+ project=project,
build_dir=build_dir,
build_after_configure=False,
test_after_configure=False,
@@ -628,7 +645,8 @@ class Zmake:
)
# Use ninja to compile the all.libraries target.
- build_project = zmake.project.Project(build_dir / "project")
+ found_projects = zmake.project.find_projects(build_dir / "project")
+ build_project = found_projects[(build_dir / "project_name.txt").read_text()]
procs = []
dirs = {}
@@ -679,10 +697,12 @@ class Zmake:
lcov_file,
is_configured=False,
):
- self.logger.info("Running test %s in %s", project.project_dir, build_dir)
+ self.logger.info(
+ "Running test %s in %s", project.config.project_name, build_dir
+ )
if not is_configured:
- rv = self.configure(
- project_dir=project.project_dir,
+ rv = self._configure(
+ project=project,
build_dir=build_dir,
build_after_configure=True,
test_after_configure=True,
@@ -699,12 +719,11 @@ class Zmake:
"""Builds all targets with coverage enabled, and then runs the tests."""
all_lcov_files = []
root_dir = self.module_paths["ec"] / "zephyr"
- for project in zmake.project.find_projects(root_dir):
+ for project in zmake.project.find_projects(root_dir).values():
is_test = project.config.is_test
- rel_path = project.project_dir.relative_to(root_dir)
- project_build_dir = pathlib.Path(build_dir).joinpath(rel_path)
- lcov_file = pathlib.Path(build_dir).joinpath(
- str(rel_path).replace("/", "_") + ".info"
+ project_build_dir = pathlib.Path(build_dir) / project.config.project_name
+ lcov_file = pathlib.Path(build_dir) / "{}.info".format(
+ project.config.project_name
)
all_lcov_files.append(lcov_file)
if is_test: