Skip to content

Commit

Permalink
Generate include/tf_psa_crypto/version.h
Browse files Browse the repository at this point in the history
Add support for generated include files.

Generate include/tf_psa_crypto/version.h from
include/tf_psa_crypto/version.h.in and CMakeLists.txt. This is pretty
ugly, because the current method for injecting that information relies
heavily on CMake.
  • Loading branch information
gilles-peskine-arm committed Oct 29, 2023
1 parent 7db4888 commit 07608bd
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 7 deletions.
108 changes: 108 additions & 0 deletions scripts/configure_source_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#!/usr/bin/env python3

"""Fill in files with the version information extracted from CMakeLists.txt.
Currently only supports include/tf_psa_crypto/version.h.
"""

# Copyright The Mbed TLS Contributors
# SPDX-License-Identifier: Apache-2.0
#
# 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.

import argparse
import os
import re
from typing import Dict, Pattern


class Configurator:
"""Populate template files in a format that's a subset of CMake's configure_file."""

@staticmethod
def _find_matching_line(filename: str, regex: Pattern) -> bytes:
for line in open(filename, 'rb'):
m = regex.match(line)
if m:
return m.group(1)
raise Exception('No line matching {} found in {}'
.format(regex, filename))

def __init__(self, cmakelists: str,
project_name: str,
variable_prefix: str) -> None:
"""Read information from the given CMakeLists.txt file."""
set_version_re = re.compile(rb'\s*set\s*\(' +
re.escape(project_name.encode()) +
rb'_VERSION\s+(.*?)\s*\)')
version_string = self._find_matching_line(cmakelists, set_version_re)
numbers_re = re.compile(rb'([0-9]+)\.([0-9]+)\.([0-9]+)')
m = numbers_re.match(version_string)
if not m:
raise Exception('Version string "{}" does not have the expected format'
.format(version_string.decode()))
self.variables = {} #type: Dict[bytes, bytes]
prefix = variable_prefix.encode() + b'_VERSION_'
for suffix, value in zip((b'MAJOR', b'MINOR', b'PATCH'),
m.groups()):
self.variables[prefix + suffix] = value
self.variable_re = re.compile(rb'@(' +
rb'|'.join(self.variables.keys()) +
rb')@')

def process_file(self, source_file: str, target_file: str) -> None:
"""Fill the given templated files."""
with open(target_file, 'wb') as out:
for _num, line in enumerate(open(source_file, 'rb'), 1):
line = re.sub(self.variable_re,
lambda m: self.variables[m.group(1)],
line)
out.write(line)

def run(self, source_root: str, target_root: str) -> None:
"""Fill templated files under source_root.
The output goes under the target_root directory.
"""
for path in [
'include/tf_psa_crypto/version.h',
]:
self.process_file(source_root + '/' + path + '.in',
target_root + '/' + path)


def main() -> None:
"""Process the command line and generate output files."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--cmakelists', '-c',
default='CMakeLists.txt',
help='CMakeLists.txt containing the version information')
parser.add_argument('--directory', '-d',
default=os.curdir,
help='Root directory of the output tree (default: current directory)')
parser.add_argument('--project-name',
default='TF_PSA_CRYPTO',
help='Project name in CMakeLists.txt')
parser.add_argument('--variable-prefix',
default='TF-PSA-Crypto',
help='Prefix for the variables containing version numbers')
parser.add_argument('--source', '-s',
default=os.curdir,
help='Root directory of the source tree (default: current directory)')
options = parser.parse_args()
configurator = Configurator(options.cmakelists,
options.project_name, options.variable_prefix)
configurator.run(options.source, options.directory)

if __name__ == '__main__':
main()
45 changes: 38 additions & 7 deletions scripts/make_makefile.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class MakefileMaker:
Typical usage:
MakefileMaker(options, source_path).generate()
"""
#pylint: disable=too-many-public-methods

def __init__(self, options, source_path: str) -> None:
"""Initialize a makefile generator.
Expand All @@ -118,6 +119,10 @@ def __init__(self, options, source_path: str) -> None:
self.help = {} #type: Dict[str, str]
# Directories containing targets
self.target_directories = set() #type: Set[str]
# Generated header files:
# {path_in_include_directive: location_under_build}
# This must be populated before calling collect_c_dependencies().
self.generated_header_files = {} #type: Dict[str, str]
# Dependencies of C files ({c_or_h_file: {h_file, ...}}). Paths are
# relative to the source or build directory.
self.c_dependency_cache = {} #type: Dict[str, FrozenSet[str]]
Expand Down Expand Up @@ -200,6 +205,10 @@ def source_file(self, path: Union[pathlib.Path, str]) -> SourceFile:
"""Construct a SourceFile object for the given path."""
return SourceFile(self.source_path, path)

def find_source_file(self, inner_path: str) -> str:
"""Return the path to the given source file in make syntax."""
return self.source_file(inner_path).make_path()

def iterate_source_files(self, *patterns: str) -> Iterator[SourceFile]:
"""List the source files matching any of the specified patterns.
Expand Down Expand Up @@ -251,8 +260,6 @@ def collect_c_dependencies(self, c_file: str,
might be defined: it bases its analysis solely on the textual
presence of "#include".
Note that dependencies in the build tree are not supported yet.
This function uses a cache internally, so repeated calls with
the same argument return almost instantly.
Expand All @@ -273,10 +280,13 @@ def collect_c_dependencies(self, c_file: str,
if m is None:
continue
filename = m.group(1)
for subdir in include_path:
if self.source_path.joinpath(subdir, filename).exists():
dependencies.add('/'.join([subdir, filename]))
break
if filename in self.generated_header_files:
dependencies.add(filename)
else:
for subdir in include_path:
if self.source_path.joinpath(subdir, filename).exists():
dependencies.add('/'.join([subdir, filename]))
break
for dep in frozenset(dependencies):
dependencies |= self.collect_c_dependencies(dep, stack)
frozen = frozenset(dependencies)
Expand All @@ -289,7 +299,10 @@ def targets_for_c(self,
"""Emit targets for a .c source file."""
dep_set = set(deps)
for dep in self.collect_c_dependencies(src.relative_path()):
dep_set.add(self.source_file(dep).make_path())
if dep in self.generated_header_files:
dep_set.add(self.generated_header_files[dep])
else:
dep_set.add(self.find_source_file(dep))
for switch, extension in [
('-c', '$(OBJ_EXT)',),
('-s', '$(ASM_EXT)',),
Expand Down Expand Up @@ -323,6 +336,22 @@ def settings_section(self) -> None:
self.blank_line()
self.target('default', ['lib'], [], phony=True)

def version_h_subsection(self) -> None:
"""Generate the target for <tf_psa_crypto/version.h>."""
location = 'include/tf_psa_crypto/version.h'
self.target(location,
['$(SOURCE_DIR)/CMakeLists.txt',
'$(SOURCE_DIR)/include/tf_psa_crypto/version.h.in'],
[sjoin('$(PYTHON) scripts/configure_source_files.py',
'-c $(SOURCE_DIR)/CMakeLists.txt',
'-s $(SOURCE_DIR)')])
self.generated_header_files['tf_psa_crypto/version.h'] = location
self.c_dependency_cache['tf_psa_crypto/version.h'] = frozenset()

def generated_sources_section(self) -> None:
"""Generate targets to build generated source files."""
self.version_h_subsection()

def library_section(self) -> None:
"""Generate targets to build the library."""
c_files = self.list_source_files('core/*.c', 'drivers/builtin/src/*.c')
Expand Down Expand Up @@ -363,6 +392,8 @@ def output_all(self) -> None:
self.blank_line()
self.settings_section()
self.blank_line()
self.generated_sources_section()
self.blank_line()
self.library_section()
self.blank_line()
self.clean_section()
Expand Down

0 comments on commit 07608bd

Please sign in to comment.