summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Rosenthal <jrosenth@chromium.org>2021-08-20 10:23:55 -0600
committerCommit Bot <commit-bot@chromium.org>2021-08-20 17:36:11 +0000
commit4c1cdd23cb35964608f0a2666f933987b4573899 (patch)
tree52a8b6531745e8893839d2c5a8f36331a882f061
parent93adb802c1a626fbb73d762124e90be4fee08a8b (diff)
downloadchrome-ec-4c1cdd23cb35964608f0a2666f933987b4573899.tar.gz
Reland "zephyr: pull in the version string from zmake"
This reverts commit 93adb802c1a626fbb73d762124e90be4fee08a8b. This commit was reverted due to build failures with the chromeos-zephyr ebuild. The root cause of the build failures was that the ebuild uses different working directories between the configure and build phases, and so the path to the include directory we generate during the configure phase won't work in the build phase. Use the full path here so that we can run the diffrerent phases in different working directories. Diff from original commit: Change-Id: Idcbd0c0546f9fcb12fcd66c617e024b8aa2a893a > --- a/zephyr/zmake/zmake/zmake.py > +++ b/zephyr/zmake/zmake/zmake.py > @@ -235,7 +235,7 @@ class Zmake: > self.logger.info("Clearing old build directory %s", build_dir) > shutil.rmtree(build_dir) > > - generated_include_dir = build_dir / "include" > + generated_include_dir = (build_dir / "include").resolve() > base_config = zmake.build_config.BuildConfig( > environ_defs={"ZEPHYR_BASE": str(zephyr_base), "PATH": "/usr/bin"}, > cmake_defs={ BUG=b:197287679 BRANCH=none TEST=emerge-volteer chromeos-zephyr Signed-off-by: Jack Rosenthal <jrosenth@chromium.org> Change-Id: I80cec7a1622edcecc670b7dbeed56dadcdf71555 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3111305 Commit-Queue: Yuval Peress <peress@chromium.org> Reviewed-by: Yuval Peress <peress@chromium.org>
-rw-r--r--zephyr/CMakeLists.txt30
-rw-r--r--zephyr/zmake/tests/test_util.py15
-rw-r--r--zephyr/zmake/tests/test_version.py81
-rw-r--r--zephyr/zmake/tests/test_zmake.py3
-rw-r--r--zephyr/zmake/zmake/util.py23
-rw-r--r--zephyr/zmake/zmake/version.py43
-rw-r--r--zephyr/zmake/zmake/zmake.py12
7 files changed, 180 insertions, 27 deletions
diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt
index af577cdc34..171ab973d2 100644
--- a/zephyr/CMakeLists.txt
+++ b/zephyr/CMakeLists.txt
@@ -25,33 +25,11 @@ if(NOT EXISTS "${PLATFORM_EC}/zephyr/module.yml")
assert_exists("${PLATFORM_EC}/zephyr/module.yml")
endif()
-if(DEFINED CONFIG_PLATFORM_EC)
- # Build the EC's expected ec_version.h file.
- set(ec_version_file "${CMAKE_BINARY_DIR}/ec/include/generated/ec_version.h")
- # Create the command used to generate the ec_version.h file.
- add_custom_command(
- OUTPUT ${ec_version_file}
- # TODO(b/185249526): zephyr: add project name to the version output
- COMMAND BOARD=${BOARD}_zephyr ${PLATFORM_EC}/util/getversion.sh > ${ec_version_file}
- WORKING_DIRECTORY ${PLATFORM_EC}
- )
- # Create a custom target that will depend on the file.
- add_custom_target(generate_ec_version DEPENDS ${ec_version_file})
-
- # Create a local library (ec_version) that will be a pure interface library
- # and will depend on the custom target that generates the ec_version.h file.
- add_library(ec_version INTERFACE)
- add_dependencies(ec_version generate_ec_version)
-
- # Ensure the EC version file is generated before trying to build the app
- # library, which includes compiling version.c
- add_dependencies(app ec_version)
-
- # Register the library with zephyr so that it generates the file at build
- # time. Also, append the include directory so we can include ec_version.h.
- zephyr_append_cmake_library(ec_version)
- zephyr_include_directories("${CMAKE_BINARY_DIR}/ec/include/generated")
+if(DEFINED ZMAKE_INCLUDE_DIR)
+ zephyr_include_directories("${ZMAKE_INCLUDE_DIR}")
+endif()
+if(DEFINED CONFIG_PLATFORM_EC)
# Add CHROMIUM_EC definition, which is used by ec_commands.h to
# determine that the header is being compiled for the EC instead of
# by another third-party C codebase.
diff --git a/zephyr/zmake/tests/test_util.py b/zephyr/zmake/tests/test_util.py
index 15f92f58ab..0524a153fd 100644
--- a/zephyr/zmake/tests/test_util.py
+++ b/zephyr/zmake/tests/test_util.py
@@ -87,3 +87,18 @@ def test_read_kconfig_autoconf_value(value):
f.write("#define TEST {}".format(value))
read_value = util.read_kconfig_autoconf_value(path, "TEST")
assert int(read_value) == value
+
+
+@pytest.mark.parametrize(
+ ["input_str", "expected_result"],
+ [
+ ("", '""'),
+ ("TROGDOR ABC-123", '"TROGDOR ABC-123"'),
+ ("hello world", '"hello world"'),
+ ("hello\nworld", r'"hello\nworld"'),
+ ('hello"world', r'"hello\"world"'),
+ ("hello\\world", '"hello\\\\world"'),
+ ],
+)
+def test_c_str(input_str, expected_result):
+ assert util.c_str(input_str) == expected_result
diff --git a/zephyr/zmake/tests/test_version.py b/zephyr/zmake/tests/test_version.py
index ff8e366736..8fb4a09435 100644
--- a/zephyr/zmake/tests/test_version.py
+++ b/zephyr/zmake/tests/test_version.py
@@ -2,7 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import datetime
import subprocess
+import unittest.mock as mock
+
+import pytest
import zmake.project
import zmake.version as version
@@ -96,3 +100,80 @@ def test_version_string_static(tmp_path):
version.get_version_string(project, zephyr_base, modules, static=True)
== "prj_v2.6.0-STATIC"
)
+
+
+@pytest.fixture
+def fake_user_hostname():
+ with mock.patch("getpass.getuser", return_value="toukmond", autospec=True):
+ with mock.patch("platform.node", return_value="pokey", autospec=True):
+ yield
+
+
+@pytest.fixture
+def fake_date():
+ fixed_date = datetime.datetime(2021, 6, 28, 3, 18, 53)
+ with mock.patch("datetime.datetime") as mock_datetime:
+ mock_datetime.now.return_value = fixed_date
+ yield
+
+
+HEADER_VERSION_STR = "trogdor_v2.6.1004-cmsis:0dead0,hal_stm32:0beef0,os:ad00da"
+EXPECTED_HEADER = (
+ "/* This file is automatically generated by zmake */\n"
+ '#define VERSION "trogdor_v2.6.1004-cmsis:0dead0,hal_stm32:0beef0,os:ad00da"\n'
+ '#define CROS_EC_VERSION32 "trogdor_v2.6.1004-cmsis:0dead0,"\n'
+ '#define BUILDER "toukmond@pokey"\n'
+ '#define DATE "2021-06-28 03:18:53"\n'
+)
+HEADER_VERSION_STR_STATIC = "trogdor_v2.6.0-STATIC"
+EXPECTED_HEADER_STATIC = (
+ "/* This file is automatically generated by zmake */\n"
+ '#define VERSION "trogdor_v2.6.0-STATIC"\n'
+ '#define CROS_EC_VERSION32 "trogdor_v2.6.0-STATIC"\n'
+ '#define BUILDER "reproducible@build"\n'
+ '#define DATE "STATIC_VERSION_DATE"\n'
+)
+
+
+def test_header_gen(fake_user_hostname, fake_date, tmp_path):
+ # Test the simple case (static=False, no existing header).
+ output_file = tmp_path / "ec_version.h"
+ version.write_version_header(HEADER_VERSION_STR, output_file)
+ assert output_file.read_text() == EXPECTED_HEADER
+
+
+def test_header_gen_reproducible_build(tmp_path):
+ # With static=True this time.
+ output_file = tmp_path / "ec_version.h"
+ version.write_version_header(HEADER_VERSION_STR_STATIC, output_file, static=True)
+ assert output_file.read_text() == EXPECTED_HEADER_STATIC
+
+
+def test_header_gen_exists_not_changed(fake_user_hostname, fake_date, tmp_path):
+ # Test we don't overwrite if no changes needed.
+ output_file = tmp_path / "ec_version.h"
+
+ # First time, write and record mtime.
+ version.write_version_header(HEADER_VERSION_STR, output_file)
+ expected_mtime = output_file.stat().st_mtime
+
+ # Do another write (contents should be unchanged).
+ version.write_version_header(HEADER_VERSION_STR, output_file)
+
+ # Assert we didn't write again.
+ assert output_file.stat().st_mtime == expected_mtime
+
+
+def test_header_gen_exists_needs_changes(fake_user_hostname, fake_date, tmp_path):
+ # Test we overwrite when it exists already and changes are needed.
+ output_file = tmp_path / "ec_version.h"
+
+ # First time, write and save contents.
+ version.write_version_header(HEADER_VERSION_STR, output_file)
+ original_contents = output_file.read_text()
+
+ # Do another write (contents should be changed).
+ version.write_version_header(HEADER_VERSION_STR_STATIC, output_file, static=True)
+
+ # Assert we overwrote.
+ assert output_file.read_text() != original_contents
diff --git a/zephyr/zmake/tests/test_zmake.py b/zephyr/zmake/tests/test_zmake.py
index a5c132dbaa..f7978764f8 100644
--- a/zephyr/zmake/tests/test_zmake.py
+++ b/zephyr/zmake/tests/test_zmake.py
@@ -141,7 +141,8 @@ EXTRAVERSION =
pathlib.Path(tmpname), build_dir=pathlib.Path("build")
)
else:
- zmk.build(pathlib.Path(tmpname))
+ with patch("zmake.version.write_version_header", autospec=True):
+ zmk.build(pathlib.Path(tmpname))
multiproc.wait_for_log_end()
recs = [rec.getMessage() for rec in cap.records]
diff --git a/zephyr/zmake/zmake/util.py b/zephyr/zmake/zmake/util.py
index 18d03dd7f0..455cb7c9d6 100644
--- a/zephyr/zmake/zmake/util.py
+++ b/zephyr/zmake/zmake/util.py
@@ -9,6 +9,29 @@ import re
import shlex
+def c_str(input_str):
+ """Make a string that can be included as a literal in C source code.
+
+ Args:
+ input_str: The string to process.
+
+ Returns:
+ A string which can be included in C source code.
+ """
+
+ def c_chr(char):
+ # Convert a char in a string to the C representation. Per the
+ # C standard, we can use all characters but quote, newline,
+ # and backslash directly with no replacements.
+ return {
+ '"': r"\"",
+ "\n": r"\n",
+ "\\": "\\\\",
+ }.get(char, char)
+
+ return '"{}"'.format("".join(map(c_chr, input_str)))
+
+
def locate_cros_checkout():
"""Find the path to the ChromiumOS checkout.
diff --git a/zephyr/zmake/zmake/version.py b/zephyr/zmake/zmake/version.py
index 404c05cb74..2d505769f2 100644
--- a/zephyr/zmake/zmake/version.py
+++ b/zephyr/zmake/zmake/version.py
@@ -2,7 +2,11 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import datetime
+import getpass
+import io
import os
+import platform
import subprocess
import zmake.util as util
@@ -114,3 +118,42 @@ def get_version_string(project, zephyr_base, modules, static=False):
return "{}_v{}.{}.{}-{}".format(
project_id, major_version, minor_version, num_commits, vcs_hashes
)
+
+
+def write_version_header(version_str, output_path, static=False):
+ """Generate a version header and write it to the specified path.
+
+ Generate a version header in the format expected by the EC build
+ system, and write it out only if the version header does not exist
+ or changes. We don't write in the case that the version header
+ does exist and was unchanged, which allows "zmake build" commands
+ on an unchanged tree to be an effective no-op.
+
+ Args:
+ version_str: The version string to be used in the header, such
+ as one generated by get_version_string.
+ output_path: The file path to write at (a pathlib.Path
+ object).
+ static: If true, generate a header which does not include
+ information like the username, hostname, or date, allowing
+ the build to be reproducible.
+ """
+ output = io.StringIO()
+ output.write("/* This file is automatically generated by zmake */\n")
+
+ def add_def(name, value):
+ output.write("#define {} {}\n".format(name, util.c_str(value)))
+
+ add_def("VERSION", version_str)
+ add_def("CROS_EC_VERSION32", version_str[:31])
+
+ if static:
+ add_def("BUILDER", "reproducible@build")
+ add_def("DATE", "STATIC_VERSION_DATE")
+ else:
+ add_def("BUILDER", "{}@{}".format(getpass.getuser(), platform.node()))
+ add_def("DATE", datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
+
+ contents = output.getvalue()
+ if not output_path.exists() or output_path.read_text() != contents:
+ output_path.write_text(contents)
diff --git a/zephyr/zmake/zmake/zmake.py b/zephyr/zmake/zmake/zmake.py
index 45e23fd904..c6ec3879ea 100644
--- a/zephyr/zmake/zmake/zmake.py
+++ b/zephyr/zmake/zmake/zmake.py
@@ -235,6 +235,7 @@ class Zmake:
self.logger.info("Clearing old build directory %s", build_dir)
shutil.rmtree(build_dir)
+ generated_include_dir = (build_dir / "include").resolve()
base_config = zmake.build_config.BuildConfig(
environ_defs={"ZEPHYR_BASE": str(zephyr_base), "PATH": "/usr/bin"},
cmake_defs={
@@ -242,6 +243,7 @@ class Zmake:
"SYSCALL_INCLUDE_DIRS": str(
self.module_paths["ec"] / "zephyr" / "include" / "drivers"
),
+ "ZMAKE_INCLUDE_DIR": str(generated_include_dir),
},
)
@@ -274,6 +276,8 @@ class Zmake:
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)
for build_name, build_config in project.iter_builds():
@@ -375,6 +379,14 @@ class Zmake:
zmake.modules.locate_from_directory(build_dir / "modules"),
)
+ # The version header needs to generated during the build phase
+ # instead of configure, as the tree may have changed since
+ # configure was run.
+ zmake.version.write_version_header(
+ version_string,
+ build_dir / "include" / "ec_version.h",
+ )
+
for build_name, build_config in project.iter_builds():
with self.jobserver.get_job():
dirs[build_name] = build_dir / "build-{}".format(build_name)