scripts: support for build meta file creation
This commit is motivated by the west discussion in:
The commit provides the ability to generate a build meta info file
containing lists of:
- Zephyr: path and revision
- Zephyr modules: name, path, and revision
- West: manifest path
path and revision for each project
For Zephyr or Zephyr modules the revision will be `null` if it is not
under git version control.
If Zephyr, a modules, or a project has uncommitted changes, the revision
will be marked dirty.
If west is not installed or used for the build process, the
west-projects list will be empty.
If a project is both a Zephyr module and a west project it will show up
in both lists.
Similar to Zephyr, which is independently referred as the Zephyr in use
but also listed as west project when west is used.
This is important in case ZEPHYR_BASE was manually set and pointing to
a different Zephyr repository.
The build meta file is not created per default but can be enabled with
the BUILD_OUTPUT_META Kconfig setting.
A project using west and having an extra Zephyr module loaded not
controlled using git can look like:
path: /.../zephyr
revision: 863600cd0e3c0a271e86629c5089821e5e4380cc-dirty
- name: mcuboot
path: /.../bootloader/mcuboot
revision: c61538748ead773ea75a551a7beee299228bdcaf
- name: local_module
path: /.../local_module
revision: null
manifest: /.../zephyr/west.yml
projects:
- path: /.../zephyr
revision: 863600cd0e3c0a271e86629c5089821e5e4380cc-dirty
- path: /.../bootloader/mcuboot
revision: c61538748ead773ea75a551a7beee299228bdcaf
- path: /.../tools/net-tools
revision: f49bd1354616fae4093bf36e5eaee43c51a55127
And without west:
path: /.../zephyr
revision: 863600cd0e3c0a271e86629c5089821e5e4380cc-dirty
- name: hal_nordic
path: /.../modules/hal/nordic
revision: a6e5299041f152da5ae0ab17b2e44e088bb96d6d
(cherry picked from commit fffaf05e5d228ba9baf955b8100e28fa47907c5c)
https://github.com/zephyrproject-rtos/west/issues/548
zephyr:
modules:
west:
zephyr:
modules:
west: null
Signed-off-by: Torsten Rasmussen <Torsten.Rasmussen@nordicsemi.no>
GitOrigin-RevId: fffaf05e5d228ba9baf955b8100e28fa47907c5c
Change-Id: If39aaf0136a0bbb3c6ae850eb425df47eb49914b
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/zephyr/+/3275801
Tested-by: CopyBot Service Account <copybot.service@gmail.com>
Reviewed-by: Tristan Honscheid <honscheid@google.com>
Commit-Queue: Tristan Honscheid <honscheid@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8036c42..78b31aa 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1406,6 +1406,21 @@
)
endif()
+if(CONFIG_BUILD_OUTPUT_META)
+ list(APPEND
+ post_build_commands
+ COMMAND ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/zephyr_module.py
+ ${WEST_ARG}
+ ${ZEPHYR_MODULES_ARG}
+ ${ZEPHYR_EXTRA_MODULES_ARG}
+ --meta-out ${KERNEL_META_NAME}
+ )
+ list(APPEND
+ post_build_byproducts
+ ${KERNEL_META_NAME}
+ )
+endif()
+
# Cleanup intermediate files
if(CONFIG_CLEANUP_INTERMEDIATE_FILES)
list(APPEND
diff --git a/Kconfig.zephyr b/Kconfig.zephyr
index 5372c0b..fbd47d2 100644
--- a/Kconfig.zephyr
+++ b/Kconfig.zephyr
@@ -506,6 +506,15 @@
Generates a file with build information that can be read by
third party Makefile-based build systems.
+config BUILD_OUTPUT_META
+ bool "Create a build meta file"
+ help
+ Create a build meta file in the build directory containing lists of:
+ - Zephyr: path and revision (if git repo)
+ - Zephyr modules: name, path, and revision (if git repo)
+ - West projects: path and revision
+ File extension is .meta
+
endmenu
config EXPERIMENTAL
diff --git a/cmake/app/boilerplate.cmake b/cmake/app/boilerplate.cmake
index 2ccc8c3..bba8129 100644
--- a/cmake/app/boilerplate.cmake
+++ b/cmake/app/boilerplate.cmake
@@ -614,6 +614,7 @@
set(KERNEL_EXE_NAME ${KERNEL_NAME}.exe)
set(KERNEL_STAT_NAME ${KERNEL_NAME}.stat)
set(KERNEL_STRIP_NAME ${KERNEL_NAME}.strip)
+set(KERNEL_META_NAME ${KERNEL_NAME}.meta)
include(${BOARD_DIR}/board.cmake OPTIONAL)
diff --git a/scripts/zephyr_module.py b/scripts/zephyr_module.py
index 9d100ed..7ef5a38 100755
--- a/scripts/zephyr_module.py
+++ b/scripts/zephyr_module.py
@@ -20,6 +20,7 @@
import argparse
import os
import re
+import subprocess
import sys
import yaml
import pykwalify.core
@@ -245,35 +246,106 @@
return out
+def process_meta(zephyr_base, west_projects, modules):
+ # Process zephyr_base, projects, and modules and create a dictionary
+ # with meta information for each input.
+ #
+ # The dictionary will contain meta info in the following lists:
+ # - zephyr: path and revision
+ # - modules: name, path, and revision
+ # - west-projects: path and revision
+ #
+ # returns the dictionary with said lists
+
+ meta = {'zephyr': None, 'modules': None, 'west': None}
+
+ def git_revision(path):
+ rc = subprocess.Popen(['git', 'rev-parse', '--is-inside-work-tree'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=path).wait()
+ if rc == 0:
+ # A git repo.
+ popen = subprocess.Popen(['git', 'rev-parse', 'HEAD'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=path)
+ stdout, stderr = popen.communicate()
+ stdout = stdout.decode('utf-8')
+
+ if not (popen.returncode or stderr):
+ revision = stdout.rstrip()
+
+ rc = subprocess.Popen(['git', 'diff-index', '--quiet', 'HEAD',
+ '--'],
+ stdout=None,
+ stderr=None,
+ cwd=path).wait()
+ if rc:
+ return revision + '-dirty'
+ return revision
+ return None
+
+ meta_project = {'path': zephyr_base,
+ 'revision': git_revision(zephyr_base)}
+ meta['zephyr'] = meta_project
+
+ if west_projects is not None:
+ meta_projects = []
+ for project in west_projects['projects']:
+ project_path = PurePath(project).as_posix()
+ meta_project = {'path': project_path,
+ 'revision': git_revision(project_path)}
+ meta_projects.append(meta_project)
+ meta['west'] = {'manifest': west_projects['manifest'],
+ 'projects': meta_projects}
+
+ meta_projects = []
+ for module in modules:
+ module_path = PurePath(module.project).as_posix()
+ meta_project = {'name': module.meta['name'],
+ 'path': module_path,
+ 'revision': git_revision(module_path)}
+ meta_projects.append(meta_project)
+ meta['modules'] = meta_projects
+
+ return meta
+
+
+def west_projects():
+ manifest_file = None
+ projects = []
+ # West is imported here, as it is optional
+ # (and thus maybe not installed)
+ # if user is providing a specific modules list.
+ from west.manifest import Manifest
+ from west.util import WestNotFound
+ from west.version import __version__ as WestVersion
+ from packaging import version
+ try:
+ manifest = Manifest.from_file()
+ if version.parse(WestVersion) >= version.parse('0.9.0'):
+ projects = [p.posixpath for p in manifest.get_projects([])
+ if manifest.is_active(p)]
+ else:
+ projects = [p.posixpath for p in manifest.get_projects([])]
+ manifest_file = manifest.path
+ return {'manifest': manifest_file, 'projects': projects}
+ except WestNotFound:
+ # Only accept WestNotFound, meaning we are not in a west
+ # workspace. Such setup is allowed, as west may be installed
+ # but the project is not required to use west.
+ pass
+ return None
+
+
def parse_modules(zephyr_base, modules=None, extra_modules=None):
if modules is None:
- # West is imported here, as it is optional
- # (and thus maybe not installed)
- # if user is providing a specific modules list.
- from west.manifest import Manifest
- from west.util import WestNotFound
- from west.version import __version__ as WestVersion
- from packaging import version
- try:
- manifest = Manifest.from_file()
- if version.parse(WestVersion) >= version.parse('0.9.0'):
- projects = [p.posixpath for p in manifest.get_projects([])
- if manifest.is_active(p)]
- else:
- projects = [p.posixpath for p in manifest.get_projects([])]
- except WestNotFound:
- # Only accept WestNotFound, meaning we are not in a west
- # workspace. Such setup is allowed, as west may be installed
- # but the project is not required to use west.
- projects = []
- else:
- projects = modules.copy()
+ modules = []
if extra_modules is None:
extra_modules = []
- projects += extra_modules
-
Module = namedtuple('Module', ['project', 'meta', 'depends'])
# dep_modules is a list of all modules that has an unresolved dependency
dep_modules = []
@@ -282,7 +354,7 @@
# sorted_modules is a topological sorted list of the modules
sorted_modules = []
- for project in projects:
+ for project in modules + extra_modules:
# Avoid including Zephyr base project as module.
if project == zephyr_base:
continue
@@ -340,6 +412,11 @@
parser.add_argument('--cmake-out',
help="""File to write with resulting <name>:<path>
values to use for including in CMake""")
+ parser.add_argument('--meta-out',
+ help="""Write a build meta YaML file containing a list
+ of Zephyr modules and west projects.
+ If a module or project is also a git repository
+ the current SHA revision will also be written.""")
parser.add_argument('--settings-out',
help="""File to write with resulting <name>:<value>
values to use for including in CMake""")
@@ -357,7 +434,15 @@
settings = ""
twister = ""
- modules = parse_modules(args.zephyr_base, args.modules, args.extra_modules)
+ west_proj = None
+ if args.modules is None:
+ west_proj = west_projects()
+ modules = parse_modules(args.zephyr_base,
+ west_proj['projects'] if west_proj else None,
+ args.extra_modules)
+ else:
+ modules = parse_modules(args.zephyr_base, args.modules,
+ args.extra_modules)
for module in modules:
kconfig += process_kconfig(module.project, module.meta)
@@ -391,6 +476,11 @@
with open(args.twister_out, 'w', encoding="utf-8") as fp:
fp.write(twister)
+ if args.meta_out:
+ meta = process_meta(args.zephyr_base, west_proj, modules)
+ with open(args.meta_out, 'w', encoding="utf-8") as fp:
+ fp.write(yaml.dump(meta))
+
if __name__ == "__main__":
main()