From be36d42da56de341eebe2f99f6ea4eea2b59a155 Mon Sep 17 00:00:00 2001 From: wangchengdong Date: Mon, 25 Aug 2025 12:03:17 +0800 Subject: [PATCH] Cmake: add defconfig preprocess capability in Cmake build environment(recursively expand #include) Add: cmake/nuttx_process_config.cmake tools/process_config.py Update nuttx/CMakeLists.txt to call process_config defined ini nuttx_process_config.cmake to process defconfig before actually using it --- CMakeLists.txt | 16 +++++ cmake/nuttx_process_config.cmake | 48 ++++++++++++++ tools/process_config.py | 105 +++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 cmake/nuttx_process_config.cmake create mode 100644 tools/process_config.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bc93fc9d2..0c575829a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,6 +158,22 @@ if(NOT EXISTS "${NUTTX_DEFCONFIG}") message(FATAL_ERROR "No config file found at ${NUTTX_DEFCONFIG}") endif() +# Process initial defconfig ################################################### +# Process initial defconfig to recursively expand #include in it + +include(nuttx_process_config) +get_filename_component(NUTTX_DEFCONFIG_DIR "${NUTTX_DEFCONFIG}" DIRECTORY) +process_config( + ${CMAKE_BINARY_DIR}/.defconfig.processed + ${NUTTX_DEFCONFIG} + INCLUDE_PATHS + ${NUTTX_DEFCONFIG_DIR}/../../common/configs + ${NUTTX_DEFCONFIG_DIR}/../common + ${NUTTX_DEFCONFIG_DIR} + ${NUTTX_DIR}/../apps + ${NUTTX_DIR}/../nuttx-apps) +set(NUTTX_DEFCONFIG ${CMAKE_BINARY_DIR}/.defconfig.processed) + # Generate initial .config ################################################### # This is needed right before any other configure step so that we can source # Kconfig variables into CMake variables diff --git a/cmake/nuttx_process_config.cmake b/cmake/nuttx_process_config.cmake new file mode 100644 index 0000000000..ceeef73fdb --- /dev/null +++ b/cmake/nuttx_process_config.cmake @@ -0,0 +1,48 @@ +# ############################################################################## +# cmake/nuttx_process_config.cmake +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you 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. +# +# ############################################################################## + +function(process_config OUTPUT INPUT) + set(options) + set(oneValueArgs) + set(multiValueArgs INCLUDE_PATHS) + cmake_parse_arguments(PARSE_ARGV 2 PROCESS_INCLUDES "${options}" + "${oneValueArgs}" "${multiValueArgs}") + + find_package(Python3 REQUIRED COMPONENTS Interpreter) + + set(include_args "") + foreach(path IN LISTS PROCESS_INCLUDES_INCLUDE_PATHS) + list(APPEND include_args "${path}") + endforeach() + + message(STATUS "Processing includes: ${INPUT} → ${OUTPUT}") + execute_process( + COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/process_config.py + ${OUTPUT} ${INPUT} ${include_args} + RESULT_VARIABLE result + OUTPUT_VARIABLE out + ERROR_VARIABLE err) + + if(NOT result EQUAL 0) + message(FATAL_ERROR "Failed to process includes:\n${err}") + endif() +endfunction() diff --git a/tools/process_config.py b/tools/process_config.py new file mode 100644 index 0000000000..dc2894f21d --- /dev/null +++ b/tools/process_config.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 +# tools/process_config.py +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you 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 re +import sys +from pathlib import Path + + +def expand_file(input_path, include_paths, processed=None): + """ + Recursively expand the file, returning its contents in order as a list of lines. + """ + if processed is None: + processed = set() + + input_path = Path(input_path).resolve() + if input_path in processed: + return [] # Already processed, avoid duplicate includes + processed.add(input_path) + + expanded_lines = [] + + with input_path.open("r", encoding="utf-8") as f: + lines = f.readlines() + + for line in lines: + line_strip = line.strip() + match = re.match(r'#include\s*[<"]([^">]+)[">]', line_strip) + if match: + include_file = match.group(1) + found = False + + # Check the current directory first + + direct_path = input_path.parent / include_file + if direct_path.exists(): + expanded_lines.extend( + expand_file(direct_path, include_paths, processed) + ) + found = True + else: + # Then check in the include paths + + for path in include_paths: + candidate = Path(path) / include_file + if candidate.exists(): + expanded_lines.extend( + expand_file(candidate, include_paths, processed) + ) + found = True + break + + if not found: + print( + f'ERROR: Cannot find "{include_file}" from {input_path}', + file=sys.stderr, + ) + sys.exit(1) + else: + expanded_lines.append(line) + + expanded_lines.append("\n") # Keep separation between files + return expanded_lines + + +def process_file(output_path, input_path, include_paths): + lines = expand_file(input_path, include_paths) + with open(output_path, "w", encoding="utf-8") as out: + out.writelines(lines) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + print( + "Usage: process_includes.py [include_paths...]", + file=sys.stderr, + ) + sys.exit(1) + + output_file = Path(sys.argv[1]) + input_file = sys.argv[2] + include_dirs = sys.argv[3:] + + if output_file.exists(): + output_file.unlink() + + process_file(output_file, input_file, include_dirs)