Skip to content

Commit

Permalink
Merge pull request #751 from jphickey/fix-746-mission-deps-cleanup
Browse files Browse the repository at this point in the history
Fix #746, CMake mission dependency cleanup
  • Loading branch information
astrogeco authored Jul 1, 2020
2 parents 5d9e5fe + 09cbe08 commit b764b05
Show file tree
Hide file tree
Showing 14 changed files with 365 additions and 243 deletions.
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ initialize_globals()

# Load the target configuration information (used by all builds)
# This is at the top level so all vars set in here will become globals.
# The "defaults" file is included first, which the user-supplied targets
# file may override as necessary.
include("cmake/mission_defaults.cmake")
include(${MISSION_DEFS}/targets.cmake)

# Scan the list of targets and organize by target system type.
Expand Down
2 changes: 1 addition & 1 deletion cmake/Makefile.sample
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
# Establish default values for critical variables. Any of these may be overridden
# on the command line or via the make environment configuration in an IDE
O ?= build
ARCH ?= native
ARCH ?= native/default_cpu1
BUILDTYPE ?= debug
INSTALLPREFIX ?= /exe
DESTDIR ?= $(O)
Expand Down
99 changes: 38 additions & 61 deletions cmake/arch_build.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,19 @@ function(prepare)

# Truncate the global TGTSYS_LIST to be only the target architecture
set(TGTSYS_LIST ${TARGETSYSTEM} PARENT_SCOPE)

# set the BUILD_CONFIG variable from the cached data
set(BUILD_CONFIG ${BUILD_CONFIG_${TARGETSYSTEM}})
list(REMOVE_AT BUILD_CONFIG 0)
set(BUILD_CONFIG ${BUILD_CONFIG} PARENT_SCOPE)

# Pull in any application-specific platform-scope configuration
# This may include user configuration files such as cfe_platform_cfg.h,
# or any other configuration/preparation that needs to happen at
# platform/arch scope.
foreach(DEP_NAME ${MISSION_DEPS})
include("${${DEP_NAME}_MISSION_DIR}/arch_build.cmake" OPTIONAL)
endforeach(DEP_NAME ${MISSION_DEPS})

endfunction(prepare)

Expand Down Expand Up @@ -344,7 +357,7 @@ function(process_arch SYSVAR)
include_directories(${CMAKE_BINARY_DIR}/inc)

# Configure OSAL target first, as it also determines important compiler flags
add_subdirectory(${MISSION_SOURCE_DIR}/osal osal)
add_subdirectory("${osal_MISSION_DIR}" osal)

# The OSAL displays its selected OS, so it is logical to display the selected PSP
# This can help with debugging if things go wrong.
Expand Down Expand Up @@ -393,55 +406,40 @@ function(process_arch SYSVAR)
set(INSTALL_SUBDIR cf)
endif (NOT INSTALL_SUBDIR)

# Add any dependencies which MIGHT be required for subsequent apps/libs/tools
# The cfe-core and osal are handled explicitly since these have special extra config
foreach(DEP ${MISSION_DEPS})
if (NOT DEP STREQUAL "cfe-core" AND
NOT DEP STREQUAL "osal")
add_subdirectory(${${DEP}_MISSION_DIR} ${DEP})
endif()
endforeach(DEP ${MISSION_DEPS})
# Add all core modules
# The osal is handled explicitly (above) since this has special extra config
foreach(DEP ${MISSION_CORE_MODULES})
if(NOT DEP STREQUAL "osal")
message(STATUS "Building Core Module: ${DEP}")
add_subdirectory("${${DEP}_MISSION_DIR}" ${DEP})
endif(NOT DEP STREQUAL "osal")
endforeach(DEP ${MISSION_CORE_MODULES})

# For the PSP it may define the FSW as either
# "psp-${CFE_SYSTEM_PSPNAME}" or just simply "psp"
if (NOT TARGET psp)
add_library(psp ALIAS psp-${CFE_SYSTEM_PSPNAME})
endif (NOT TARGET psp)

# Clear the app lists
set(ARCH_APP_SRCS)
foreach(APP ${TGTSYS_${SYSVAR}_APPS})
set(TGTLIST_${APP})
endforeach()
foreach(DRV ${TGTSYS_${SYSVAR}_DRIVERS})
set(TGTLIST_DRV_${DRV})
endforeach()

# INCLUDE_REFACTOR: apps and the PSP like to #include cfe_platform_cfg.h -- they shouldn't
# This will become unnecessary when dependency refactoring is merged in, but for now
# they need to be able to find it. Remove the next line once refactoring is merged.
# Also do not do this if more than one CPU shares this architecture - this hack can only
# be done if a 1:1 mapping between cpus and architectures (so all apps are rebuilt per-cpu)
list(LENGTH TGTSYS_${SYSVAR} ARCHLEN)
if (ARCHLEN EQUAL 1)
include_directories(${CMAKE_BINARY_DIR}/cfe_core_default_${TGT${TGTSYS_${SYSVAR}}_NAME}/inc)
endif (ARCHLEN EQUAL 1)

# Process each PSP module that is referenced on this system architecture (any cpu)
foreach(PSPMOD ${TGTSYS_${SYSVAR}_PSPMODULES})
message(STATUS "Building PSP Module: ${PSPMOD}")
add_subdirectory(${${PSPMOD}_MISSION_DIR} psp/${PSPMOD})
add_subdirectory("${${PSPMOD}_MISSION_DIR}" psp/${PSPMOD})
endforeach()

# Process each app that is used on this system architecture
set(APP_INSTALL_LIST)
foreach(APP ${TGTSYS_${SYSVAR}_STATICAPPS})
message(STATUS "Building Static App: ${APP}")
add_subdirectory(${${APP}_MISSION_DIR} apps/${APP})
add_subdirectory("${${APP}_MISSION_DIR}" apps/${APP})
endforeach()

# Configure the selected PSP
# The naming convention allows more than one PSP per arch,
# however in practice this gets too complicated so it is
# currently a 1:1 relationship. This may change at some point.
add_subdirectory(${MISSION_SOURCE_DIR}/psp psp/${CFE_SYSTEM_PSPNAME})

# Process each target that shares this system architecture
# First Pass: Assemble the list of apps that should be compiled
foreach(APP ${TGTSYS_${SYSVAR}_APPS})
set(TGTLIST_${APP})
endforeach()

foreach(TGTID ${TGTSYS_${SYSVAR}})

set(TGTNAME ${TGT${TGTID}_NAME})
Expand All @@ -461,36 +459,15 @@ function(process_arch SYSVAR)
foreach(APP ${TGTSYS_${SYSVAR}_APPS})
set(APP_INSTALL_LIST ${TGTLIST_${APP}})
message(STATUS "Building App: ${APP} install=${APP_INSTALL_LIST}")
add_subdirectory(${${APP}_MISSION_DIR} apps/${APP})
add_subdirectory("${${APP}_MISSION_DIR}" apps/${APP})
endforeach()

# If unit test is enabled, build a generic ut stub library for CFE
if (ENABLE_UNIT_TESTS)
add_subdirectory(${cfe-core_MISSION_DIR}/ut-stubs ut_cfe_core_stubs)
endif (ENABLE_UNIT_TESTS)

# Process each target that shares this system architecture
# Second Pass: Build cfe-core and link final target executable
# Second Pass: Build and link final target executable
foreach(TGTID ${TGTSYS_${SYSVAR}})

set(TGTNAME ${TGT${TGTID}_NAME})
set(TGTPLATFORM ${TGT${TGTID}_PLATFORM})
if(NOT TGTPLATFORM)
set(TGTPLATFORM "default" ${TGTNAME})
endif(NOT TGTPLATFORM)

string(REPLACE ";" "_" CFE_CORE_TARGET "cfe_core_${TGTPLATFORM}")
if (NOT TARGET ${CFE_CORE_TARGET})

# Generate wrapper file for the requisite cfe_platform_cfg.h file
generate_config_includefile("${CFE_CORE_TARGET}/inc/cfe_msgids.h" msgids.h ${TGTPLATFORM})
generate_config_includefile("${CFE_CORE_TARGET}/inc/cfe_platform_cfg.h" platform_cfg.h ${TGTPLATFORM})

# Actual core library is a subdirectory
add_subdirectory(${MISSION_SOURCE_DIR}/cfe/fsw/cfe-core ${CFE_CORE_TARGET})

endif (NOT TARGET ${CFE_CORE_TARGET})

set(TGTNAME ${TGT${TGTID}_NAME})

# Target to generate the actual executable file
add_subdirectory(cmake/target ${TGTNAME})

Expand Down
5 changes: 5 additions & 0 deletions cmake/cfe_generated_file.h.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@GENERATED_FILE_HEADER@

@GENERATED_FILE_CONTENT@

@GENERATED_FILE_TRAILER@
173 changes: 119 additions & 54 deletions cmake/global_functions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,49 @@
#
##################################################################

include(CMakeParseArguments)

##################################################################
#
# FUNCTION: generate_c_headerfile
#
# Generates a C header file in the build directory.
# First argument is the file name to write. All remaining arguments will be
# concatenated and written to the file.
#
function(generate_c_headerfile FILE_NAME)

# Determine the absolute location for this wrapper file
# If it is relative path then assume it is relative to ${CMAKE_BINARY_DIR}
# This should not write generated files to ${CMAKE_SOURCE_DIR}
if (NOT IS_ABSOLUTE "${FILE_NAME}")
set(FILE_NAME "${CMAKE_BINARY_DIR}/${FILE_NAME}")
endif (NOT IS_ABSOLUTE "${FILE_NAME}")

# Generate an include guard
get_filename_component(FILE_GUARD "${FILE_NAME}" NAME)
string(REGEX REPLACE "[^A-Za-z0-9]" "_" FILE_GUARD "${FILE_GUARD}")
string(TOUPPER "GENERATED_INCLUDE_${FILE_GUARD}" FILE_GUARD)
set(GENERATED_FILE_HEADER
"/* Generated header file. Do not edit */\n\n"
"#ifndef ${FILE_GUARD}\n"
"#define ${FILE_GUARD}\n\n"
)
set(GENERATED_FILE_TRAILER
"#endif /* ${FILE_GUARD} */\n"
)

# Use configure_file() to write the file, as this automatically
# has the logic to not update the timestamp on the file unless it changes.
string(REPLACE ";" "" GENERATED_FILE_CONTENT "${ARGN}")
string(REPLACE ";" "" GENERATED_FILE_HEADER "${GENERATED_FILE_HEADER}")
string(REPLACE ";" "" GENERATED_FILE_TRAILER "${GENERATED_FILE_TRAILER}")
configure_file(
"${CFE_SOURCE_DIR}/cmake/cfe_generated_file.h.in"
"${FILE_NAME}"
@ONLY)

endfunction(generate_c_headerfile)

##################################################################
#
Expand All @@ -21,58 +64,62 @@
# This also supports "stacking" multiple component files together by specifying more than one
# source file for the wrapper.
#
# In all cases, care must be used when updating such files -- if the timestamp is updated then
# everything that uses it will be rebuilt even if the content did not change. So the file content
# is checked against any pre-existing files, and if the content is the same then the update is
# skipped, preserving the original timestamp.
# This function now accepts named parameters:
# FILE_NAME - the name of the file to write
# FALLBACK_FILE - if no files are found in "defs" using the name match, this file will be used instead.
# MATCH_SUFFIX - the suffix to match in the "defs" directory (optional)
# PREFIXES - a list of prefixes to match in the "defs" directory (optional)
#
function(generate_config_includefile DESTFILE SUFFIX)
function(generate_config_includefile)

# Determine the absolute location for this wrapper file
if (NOT IS_ABSOLUTE ${DESTFILE})
set(DESTFILE ${CMAKE_BINARY_DIR}/${DESTFILE})
endif (NOT IS_ABSOLUTE ${DESTFILE})

set(INCL_LIST)
set(DEST_CONTENTSTR "/* This is a wrapper file generated by the build system - DO NOT EDIT */\n")
foreach(SRC ${ARGN})
set(SRC_LOCAL_PATH "${MISSION_DEFS}/${SRC}_${SUFFIX}")
# only add this if not already included (prevent double inclusion) */
list(FIND INCL_LIST "${SRC_LOCAL_PATH}" INCL_INDX)
if (INCL_INDX LESS 0)
list(APPEND INCL_LIST "${SRC_LOCAL_PATH}")
if (EXISTS "${SRC_LOCAL_PATH}")
file(TO_NATIVE_PATH "${SRC_LOCAL_PATH}" SRC_NATIVE)
set(DEST_CONTENTSTR "${DEST_CONTENTSTR}#include \"${SRC_NATIVE}\"\n")
else()
set(DEST_CONTENTSTR "${DEST_CONTENTSTR}/* ${SRC_LOCAL_PATH} does not exist */\n")
endif (EXISTS "${SRC_LOCAL_PATH}")
endif (INCL_INDX LESS 0)
endforeach(SRC ${SRCFILES})
if (EXISTS "${DESTFILE}")
file(READ "${DESTFILE}" DEST_EXISTING)
else (EXISTS "${DESTFILE}")
set(DEST_EXISTING)
endif (EXISTS "${DESTFILE}")
if (NOT DEST_CONTENTSTR STREQUAL DEST_EXISTING)
file(WRITE "${DESTFILE}" "${DEST_CONTENTSTR}")
endif (NOT DEST_CONTENTSTR STREQUAL DEST_EXISTING)

# Create a copy of the generated file in a location where it can
# be found by an editor such as Eclipse. The user will still have to
# add the path to the IDEs include search path, but this makes it easier
# by at least putting them all in one spot. This is not used for the build.
get_filename_component(DESTBASENAME "${DESTFILE}" NAME)
if (MISSION_BINARY_DIR)
set(EDITOR_COPY_DIR ${MISSION_BINARY_DIR}/editor_inc)
else()
set(EDITOR_COPY_DIR ${CMAKE_BINARY_DIR}/editor_inc)
endif()

file(MAKE_DIRECTORY ${EDITOR_COPY_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DESTFILE} ${EDITOR_COPY_DIR}/${DESTBASENAME})
cmake_parse_arguments(GENCONFIG_ARG "" "OUTPUT_DIRECTORY;FILE_NAME;FALLBACK_FILE;MATCH_SUFFIX" "PREFIXES" ${ARGN} )
if (NOT GENCONFIG_ARG_OUTPUT_DIRECTORY)
set(GENCONFIG_ARG_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/inc")
endif (NOT GENCONFIG_ARG_OUTPUT_DIRECTORY)

set(WRAPPER_FILE_CONTENT)
set(ITEM_FOUND FALSE)

# 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 (NOT ITEM_FOUND AND 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}")
else()
message(STATUS "Generated ${GENCONFIG_ARG_FILE_NAME} from ${MISSION_DEFS} configuration")
endif()

# Generate a header file
generate_c_headerfile("${GENCONFIG_ARG_OUTPUT_DIRECTORY}/${GENCONFIG_ARG_FILE_NAME}" ${WRAPPER_FILE_CONTENT})

endfunction(generate_config_includefile DESTFILE SUFFIX)
endfunction(generate_config_includefile)


##################################################################
Expand Down Expand Up @@ -106,22 +153,40 @@ function(read_targetconfig)
endif()
if (NOT DEFINED TGT${TGTID}_SYSTEM)
set(TGT${TGTID}_SYSTEM "cpu${TGTID}")
set(TGT${TGTID}_SYSTEM "${TGT${TGTID}_SYSTEM}" PARENT_SCOPE)
endif()
if (NOT DEFINED TGT${TGTID}_PLATFORM)
set(TGT${TGTID}_PLATFORM "default" "${TGT${TGTID}_NAME}")
set(TGT${TGTID}_PLATFORM "${TGT${TGTID}_PLATFORM}" PARENT_SCOPE)
endif()

if (SIMULATION)
# if simulation use simulation system architecture for all targets
set(CURRSYS "${SIMULATION}")
set(TOOLCHAIN_NAME "${SIMULATION}")
else (SIMULATION)
# get the target system arch identifier string
set(CURRSYS "${TGT${TGTID}_SYSTEM}")
set(TOOLCHAIN_NAME "${TGT${TGTID}_SYSTEM}")
endif (SIMULATION)

# make sure the string is safe for a variable name
string(REGEX REPLACE "[^A-Za-z0-9]" "_" SYSVAR "${CURRSYS}")
set(BUILD_CONFIG ${TOOLCHAIN_NAME} ${TGT${TGTID}_PLATFORM})

# convert to a the string which is safe for a variable name
string(REGEX REPLACE "[^A-Za-z0-9]" "_" SYSVAR "${BUILD_CONFIG}")

# save the unmodified name for future reference
set(SYSID_${SYSVAR} "${CURRSYS}" PARENT_SCOPE)
set(BUILD_CONFIG_${SYSVAR} "${BUILD_CONFIG}" PARENT_SCOPE)

# if the "global" applist is not empty, append to every CPU applist
if (MISSION_GLOBAL_APPLIST)
list(APPEND TGT${TGTID}_APPLIST ${MISSION_GLOBAL_APPLIST})
set(TGT${TGTID}_APPLIST ${TGT${TGTID}_APPLIST} PARENT_SCOPE)
endif (MISSION_GLOBAL_APPLIST)

if (MISSION_GLOBAL_STATIC_APPLIST)
list(APPEND TGT${TGTID}_STATIC_APPLIST ${MISSION_GLOBAL_STATIC_APPLIST})
set(TGT${TGTID}_STATIC_APPLIST ${TGT${TGTID}_STATIC_APPLIST} PARENT_SCOPE)
endif (MISSION_GLOBAL_STATIC_APPLIST)

# Append to global lists
list(APPEND TGTSYS_LIST "${SYSVAR}")
list(APPEND TGTSYS_${SYSVAR} "${TGTID}")
Expand Down
Loading

0 comments on commit b764b05

Please sign in to comment.