From 810d20375c2988a228f73081d423f2418507457f Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 13 Apr 2023 13:27:42 -0400 Subject: [PATCH] Fix #2289, implement common search routine for config files Adds a 'cfe_locate_implementation_file()' function that provides a consistent means to find a file within the ${MISSION_DEFS} subdirectory. Similar locate/search logic was necessary in a few places, for generate_config_includefile as well as add_cfe_tables, but these were not consistent in the paths or ordering used. Using this function should make them consistent. --- cmake/global_functions.cmake | 140 +++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 39 deletions(-) diff --git a/cmake/global_functions.cmake b/cmake/global_functions.cmake index d007d614a..9a50fd94d 100644 --- a/cmake/global_functions.cmake +++ b/cmake/global_functions.cmake @@ -3,13 +3,91 @@ # cFS Mission global CMake function definitions # # This is included by the top-level script and can define -# common routines and variables that may be referenced in both +# common routines and variables that may be referenced in both # the mission (non-arch) and arch-specific builds # ################################################################## include(CMakeParseArguments) +################################################################## +# +# FUNCTION: cfe_locate_implementation_file +# +# The CFE/CFS build permits users to modify or tune system behavior by +# providing customized versions of configuration files and tables. +# +# This function locates the preferred version of a file to use, by following +# a priority-based search scheme. Users can put their preferred version of +# a file in their MISSION_DEFS directory, based on a file naming convention. +# +# If no version of the file is found there, a default/fallback version can +# be specified as well, which can be part of the module source. +# +function(cfe_locate_implementation_file OUTPUT_VAR FILE_NAME) + + cmake_parse_arguments(LOCATEIMPL_ARG "OPTIONAL;ALLOW_LIST" "FALLBACK_FILE" "PREFIX;SUBDIR" ${ARGN}) + + # Always check for filename matches directly in the MISSION_DEFS dir + set(IMPL_SEARCH_BASEDIRS "${MISSION_DEFS}/") + # The prefix could also be a subdir, but do not repeat the mission name + foreach (PREFIX ${LOCATEIMPL_ARG_PREFIX}) + if (NOT "${MISSIONCONFIG}" STREQUAL "${PREFIX}") + list(APPEND IMPL_SEARCH_BASEDIRS "${MISSION_DEFS}/${PREFIX}/") + endif() + endforeach() + set(ADD_SUBDIRS) + foreach (SUBDIR ${LOCATEIMPL_ARG_SUBDIR}) + foreach (BASEDIR ${IMPL_SEARCH_BASEDIRS}) + list(APPEND ADD_SUBDIRS "${BASEDIR}${SUBDIR}/") + endforeach() + endforeach() + list(APPEND IMPL_SEARCH_BASEDIRS ${ADD_SUBDIRS}) + + # Build the list of possible locations for this file in REVERSE priority order + set(IMPL_SEARCH_PATH) + if (LOCATEIMPL_ARG_FALLBACK_FILE) + if (IS_ABSOLUTE "${LOCATEIMPL_ARG_FALLBACK_FILE}") + list(APPEND IMPL_SEARCH_PATH "${LOCATEIMPL_ARG_FALLBACK_FILE}") + else() + list(APPEND IMPL_SEARCH_PATH "${CMAKE_CURRENT_LIST_DIR}/${LOCATEIMPL_ARG_FALLBACK_FILE}") + endif() + endif() + + # Check for the existence of the file in each of the dirs + foreach(BASEDIR ${IMPL_SEARCH_BASEDIRS}) + list(APPEND IMPL_SEARCH_PATH "${BASEDIR}${FILE_NAME}") + + # A target-specific prefixed filename gets priority over a direct filename match + # But do not include this variant if the prefix is already part of the basedir + foreach (PREFIX ${LOCATEIMPL_ARG_PREFIX}) + if (NOT "${BASEDIR}" MATCHES "/${PREFIX}/") + list(APPEND IMPL_SEARCH_PATH "${BASEDIR}${PREFIX}_${FILE_NAME}") + endif() + endforeach() + endforeach() + + set(SELECTED_FILE) + foreach (CHECK_FILE ${IMPL_SEARCH_PATH}) + if (EXISTS "${CHECK_FILE}") + list(APPEND SELECTED_FILE ${CHECK_FILE}) + endif() + endforeach() + + if (SELECTED_FILE) + message(STATUS "Using file: ${SELECTED_FILE} for ${FILE_NAME}") + elseif(NOT LOCATEIMPL_ARG_OPTIONAL) + message(FATAL_ERROR "No implementation for ${FILE_NAME} found") + endif() + + # Export the result to the caller + if (NOT LOCATEIMPL_ARG_ALLOW_LIST AND SELECTED_FILE) + list(GET SELECTED_FILE -1 SELECTED_FILE) + endif() + set(${OUTPUT_VAR} ${SELECTED_FILE} PARENT_SCOPE) + +endfunction() + ################################################################## # # FUNCTION: generate_c_headerfile @@ -78,48 +156,32 @@ function(generate_config_includefile) set(GENCONFIG_ARG_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/inc") endif (NOT GENCONFIG_ARG_OUTPUT_DIRECTORY) - set(WRAPPER_FILE_CONTENT) - set(ITEM_FOUND FALSE) + if (IS_CFS_ARCH_BUILD) + set(ARCH_PREFIXES ${BUILD_CONFIG}) + else() + set(ARCH_PREFIXES) + endif() - # Assemble a list of file names to test for - # Check for existence of a file in defs directory with an exact matching name - # Then Check for existence of file(s) with a matching prefix+suffix - set(CHECK_PATH_LIST "${MISSION_DEFS}/${GENCONFIG_ARG_FILE_NAME}") if (GENCONFIG_ARG_MATCH_SUFFIX) - foreach(PREFIX ${GENCONFIG_ARG_PREFIXES} ${GENCONFIG_ARG_UNPARSED_ARGUMENTS}) - list(APPEND CHECK_PATH_LIST "${MISSION_DEFS}/${PREFIX}_${GENCONFIG_ARG_MATCH_SUFFIX}") - endforeach() - endif(GENCONFIG_ARG_MATCH_SUFFIX) - - # Check for existence of files, and add to content if present - # Note that all files are appended/concatenated together. - foreach(SRC_LOCAL_PATH ${CHECK_PATH_LIST}) - if (EXISTS "${SRC_LOCAL_PATH}") - file(TO_NATIVE_PATH "${SRC_LOCAL_PATH}" SRC_NATIVE_PATH) - list(APPEND WRAPPER_FILE_CONTENT "#include \"${SRC_NATIVE_PATH}\"\n") - set(ITEM_FOUND TRUE) - else() - list(APPEND WRAPPER_FILE_CONTENT "/* ${SRC_LOCAL_PATH} does not exist */\n") - endif (EXISTS "${SRC_LOCAL_PATH}") - endforeach() - - # If _no_ files were found in the above loop, - # then check for and use the fallback file. - # (if specified by the caller it should always exist) - # Also produce a message on the console showing whether mission config or fallback was used - if (ITEM_FOUND) - message(STATUS "Generated ${GENCONFIG_ARG_FILE_NAME} from ${MISSION_DEFS} configuration") - elseif (GENCONFIG_ARG_FALLBACK_FILE) - file(TO_NATIVE_PATH "${GENCONFIG_ARG_FALLBACK_FILE}" SRC_NATIVE_PATH) - list(APPEND WRAPPER_FILE_CONTENT - "\n\n/* No configuration for ${GENCONFIG_ARG_FILE_NAME}, using fallback */\n" - "#include \"${GENCONFIG_ARG_FALLBACK_FILE}\"\n") - message(STATUS "Using ${GENCONFIG_ARG_FALLBACK_FILE} for ${GENCONFIG_ARG_FILE_NAME}") + set(TGTFILE ${GENCONFIG_ARG_MATCH_SUFFIX}) else() - message("ERROR: No implementation for ${GENCONFIG_ARG_FILE_NAME} found") - message(FATAL_ERROR "Tested: ${CHECK_PATH_LIST}") + set(TGTFILE ${GENCONFIG_ARG_FILE_NAME}) endif() + # Use the common search function to find the candidate(s) + cfe_locate_implementation_file(SRC_LOCAL_PATH_LIST "${TGTFILE}" + ALLOW_LIST + FALLBACK_FILE "${GENCONFIG_ARG_FALLBACK_FILE}" + PREFIX ${GENCONFIG_ARG_PREFIXES} ${ARCH_PREFIXES} + SUBDIR config + ) + + set(WRAPPER_FILE_CONTENT) + foreach(SELECTED_FILE ${SRC_LOCAL_PATH_LIST}) + file(TO_NATIVE_PATH "${SELECTED_FILE}" SRC_NATIVE_PATH) + list(APPEND WRAPPER_FILE_CONTENT "#include \"${SRC_NATIVE_PATH}\"\n") + endforeach() + # Generate a header file generate_c_headerfile("${GENCONFIG_ARG_OUTPUT_DIRECTORY}/${GENCONFIG_ARG_FILE_NAME}" ${WRAPPER_FILE_CONTENT}) @@ -139,7 +201,7 @@ endfunction(generate_config_includefile) # # This function then sets up the following variables in the global scope: # TGTSYS_LIST: list of CPU architectures used in the build. Note this -# will always contain a "native" target (for tools at least) which +# will always contain a "native" target (for tools at least) which # is forced to be last. # MISSION_APPS: list of all applications in this build # MISSION_PSPMODULES: list of all psp modules in this build