From efb8551adaa27ca0bf49174f10c05b41f0f80626 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Fri, 7 Apr 2023 11:28:50 -0400 Subject: [PATCH] Fix #2284, improve add_cfe_tables function Improves the functionality and adds additional documentation about how this function is intended to work. The improvements add some flexibility and intelligence and should be backward compatible with existing use cases. The add_cfe_app() function now adds an additional interface target that can be referenced when building tables, and this includes the directory-scope properties from the original app build. Therefore, calls to add_cfe_tables from other (non-app) contexts can get the full set of include directories. This also makes the target name simpler when adding custom properties, it is simply "${APP_NAME}.table" Finally, instead of invoking the table tool (elf2cfetbl) directly in the context of the table build rule, it generates a custom makefile rule instead, which is called from the top-level (mission) scope to do the conversions. --- cmake/arch_build.cmake | 255 +++++++++++++------- cmake/generate_table.cmake | 49 ---- cmake/mission_build.cmake | 42 +++- cmake/tables/elf2cfetbl_rules.mk | 5 + cmake/tables/generate_elf_table_rules.cmake | 34 +++ cmake/tables/table_rule_template.d.in | 10 + cmake/tables/tabletool_rule.mk | 10 + 7 files changed, 272 insertions(+), 133 deletions(-) delete mode 100644 cmake/generate_table.cmake create mode 100644 cmake/tables/elf2cfetbl_rules.mk create mode 100644 cmake/tables/generate_elf_table_rules.cmake create mode 100644 cmake/tables/table_rule_template.d.in create mode 100644 cmake/tables/tabletool_rule.mk diff --git a/cmake/arch_build.cmake b/cmake/arch_build.cmake index f2402d1ce..d1fe5a5dc 100644 --- a/cmake/arch_build.cmake +++ b/cmake/arch_build.cmake @@ -106,6 +106,30 @@ function(add_cfe_app APP_NAME APP_SRC_FILES) cfs_app_do_install(${APP_NAME} ${APP_DYNAMIC_TARGET_LIST}) endif (APP_DYNAMIC_TARGET_LIST) + # Add an interface target is added with a ".table" suffix, + # this should reflect all the INCLUDE_DIRECTORIES and COMPILE_DEFINITIONS that + # were used when compiling the app itself, such that tables (and other items) can be + # added later and reference the same set. + add_library(${APP_NAME}.table INTERFACE) + get_directory_property(CURRENT_INCLUDE_DIRS INCLUDE_DIRECTORIES) + if (NOT CURRENT_INCLUDE_DIRS) + set(CURRENT_INCLUDE_DIRS) # make it empty, not -NOTFOUND + endif() + get_directory_property(CURRENT_COMPILE_DEFS COMPILE_DEFINITIONS) + if (NOT CURRENT_COMPILE_DEFS) + set(CURRENT_COMPILE_DEFS) # make it empty, not -NOTFOUND + endif() + list (APPEND CURRENT_INCLUDE_DIRS + $ + $ + ) + list (APPEND CURRENT_COMPILE_DEFS + $ + $ + ) + target_include_directories(${APP_NAME}.table INTERFACE ${CURRENT_INCLUDE_DIRS}) + target_compile_definitions(${APP_NAME}.table INTERFACE ${CURRENT_COMPILE_DEFS} CFE_APP_NAME="${APP_NAME}") + endfunction(add_cfe_app) ################################################################## @@ -146,99 +170,160 @@ endfunction(add_cfe_app_dependency) # # Simplified routine to add CFS tables to be built with an app # -function(add_cfe_tables APP_NAME TBL_SRC_FILES) +# For apps with just a single table, the TABLE_FQNAME may be the +# same as the app name, which is simple. +# +# For apps with multiple tables, the TABLE_FQNAME may be of the +# form "${APP_NAME}.${TABLE_NAME}" where ${APP_NAME} refers to an +# app target that was registered via the "add_cfe_app" function. +# +# Note that for backward compatibility, any name will be accepted +# for TABLE_FQNAME. However if this function cannot determine which +# app the table is associated with, it will only have a default set +# of INCLUDE_DIRECTORIES when building the C source file(s). By +# associating a table with an app using the conventions above, the +# INCLUDE_DIRECTORIES from the parent app will be used when building the +# tables. +# +# This function produces one or more library targets in CMake, using names +# of the form: "tblobj_${TGT}_{TABLE_FQNAME}" where TGT reflects the name +# of the target from targets.cmake and TABLE_FQNAME reflects the first +# parameter to this function. +# +function(add_cfe_tables TABLE_FQNAME TBL_DEFAULT_SRC_FILES) + + get_filename_component(APP_NAME ${TABLE_FQNAME} NAME_WE) - if (TGTNAME) - set (TABLE_TGTLIST ${TGTNAME}) - elseif (TARGET ${APP_NAME}) + # If "TGTNAME" is set, then use it directly + set(TABLE_TGTLIST ${TGTNAME}) + set(TABLE_TEMPLATE "${CFE_SOURCE_DIR}/cmake/tables/table_rule_template.d.in") + set(TABLE_CMD_BASIC_OPTS + -DTEMPLATE_FILE="${TABLE_TEMPLATE}" + -DAPP_NAME="${APP_NAME}" + ) + + if (INSTALL_SUBDIR) + list(APPEND TABLE_CMD_BASIC_OPTS + -DINSTALL_SUBDIR="${INSTALL_SUBDIR}" + ) + endif() + + if (TARGET ${APP_NAME}.table) + if (NOT TABLE_TGTLIST) set (TABLE_TGTLIST ${TGTLIST_${APP_NAME}}) + endif() + set(TABLE_PARENT_TGT ${APP_NAME}.table) else() - # The first parameter should match the name of an app that was - # previously defined using "add_cfe_app". If target-scope properties - # are used for include directories and compile definitions, this is needed - # to compile tables with the same include path/definitions as the app has. - # However historically this could have been any string, which still works - # if directory-scope properties are used for includes, so this is not - # an error. - message("NOTE: \"${APP_NAME}\" passed to add_cfe_tables is not a previously-defined application target") + # The first parameter should match the name of an app that was + # previously defined using "add_cfe_app". If target-scope properties + # are used for include directories and compile definitions, this is needed + # to compile tables with the same include path/definitions as the app has. + # However historically this could have been any string, which still works + # if directory-scope properties are used for includes, so this is not + # an error. + message("NOTE: \"${APP_NAME}\" passed to add_cfe_tables is not a previously-defined application target") + if (NOT TABLE_TGTLIST) set (TABLE_TGTLIST ${APP_STATIC_TARGET_LIST} ${APP_DYNAMIC_TARGET_LIST}) + endif() + # No (known) parent app, just use core_api in this case. It will only get global-scope includes and defines. + set(TABLE_PARENT_TGT core_api) endif() + set(TABLE_GENSCRIPT "${CFE_SOURCE_DIR}/cmake/tables/generate_elf_table_rules.cmake") + # The table source must be compiled using the same "include_directories" # as any other target, but it uses the "add_custom_command" so there is # no automatic way to do this (at least in the older cmakes) + foreach(TGT ${TABLE_TGTLIST}) + + set(TABLE_CMD_TGT_OPTS + -DTARGET_NAME="${TGT}" + ) + + set(TABLE_LIBNAME "tblobj_${TGT}_${TABLE_FQNAME}") + list(APPEND TABLE_CMD_TGT_OPTS "-DARCHIVE_FILE=\"$\"") + + # Note that the TBL_DEFAULT_SRC_FILES is just a default - we now need + # to find the active source, which typically comes from the MISSION_DEFS dir. + # The TABLE_SELECTED_SRCS will become this list of active/selected source files + set(TABLE_SELECTED_SRCS) + foreach(TBL ${TBL_DEFAULT_SRC_FILES} ${ARGN}) + + # The file source basename (without directory or ext) should be the same as the table + # binary filename with a ".tbl" extension (this is the convention assumed by elf2cfetbl) + get_filename_component(TABLE_SRC_NEEDED ${TBL} NAME) + get_filename_component(TABLE_BASENAME ${TBL} NAME_WE) + + + # Check if an override exists at the mission level (recommended practice) + # This allows a mission to implement a customized table without modifying + # the original - this also makes for easier merging/updating if needed. + # Note this path list is in reverse-priority order, and only a single file + # will be end up being selected. + cfe_locate_implementation_file(TBL_SRC "${TABLE_SRC_NEEDED}" + OPTIONAL + FALLBACK_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${TBL}" + PREFIX ${TGT} + SUBDIR tables + ) + + list(APPEND TABLE_SELECTED_SRCS ${TBL_SRC}) + + if (TBL_SRC) + message(STATUS "Using ${TBL_SRC} as table definition for ${TABLE_BASENAME} on ${TGT}") + else() + message(FATAL_ERROR "No table definition for ${APP_NAME}.${TABLE_BASENAME} on ${TGT} found") + endif() + + # Set a preprocessor macro so when the .c file is compiled it knows what its + # input and (by convention) output name is supposed to be. + if (TABLE_LIBNAME) + set_property(SOURCE "${TBL_SRC}" APPEND PROPERTY COMPILE_DEFINITIONS + CFE_TABLE_NAME=${TABLE_BASENAME} + ) + endif() + + # Note the table is not generated directly here, as it may require the native system compiler, so + # the call to the table tool (eds2cfetbl in this build) is deferred to the parent scope. Instead, this + # generates a file that captures the state (include dirs, source files, targets) for use in a future step. + set(TABLE_RULEFILE "${MISSION_BINARY_DIR}/tables/${TGT}_${TABLE_FQNAME}.${TABLE_BASENAME}.d") + add_custom_command( + OUTPUT "${TABLE_RULEFILE}" + COMMAND ${CMAKE_COMMAND} + ${TABLE_CMD_BASIC_OPTS} + ${TABLE_CMD_TGT_OPTS} + -DOUTPUT_FILE="${TABLE_RULEFILE}" + -DTABLE_NAME="${TABLE_BASENAME}" + -DSOURCES="${TBL_SRC}" + -P "${TABLE_GENSCRIPT}" + WORKING_DIRECTORY + ${WORKING_DIRECTORY} + DEPENDS + ${TABLE_TEMPLATE} + ${TABLE_GENSCRIPT} + ${TABLE_PARENT_TGT} + ) + + # Add a custom target to generate the config file + add_custom_target(generate_table_${TGT}_${APP_NAME}_${TABLE_BASENAME} + DEPENDS "${TABLE_RULEFILE}" + ) + add_dependencies(cfetables generate_table_${TGT}_${APP_NAME}_${TABLE_BASENAME}) + + endforeach() + + if (TABLE_LIBNAME) + # NOTE: On newer CMake versions this should become an OBJECT library which makes this simpler. + # On older versions one may not reference the TARGET_OBJECTS property from the custom command. + # As a workaround this is built into a static library, and then the desired object is extracted + # before passing to elf2cfetbl. It is roundabout but it works. + add_library(${TABLE_LIBNAME} STATIC ${TABLE_SELECTED_SRCS}) + target_compile_definitions(${TABLE_LIBNAME} PRIVATE + CFE_CPU_NAME=${TGT} + ) + target_link_libraries(${TABLE_LIBNAME} ${TABLE_PARENT_TGT}) + endif() - # Create the intermediate table objects using the target compiler, - # then use "elf2cfetbl" to convert to a .tbl file - foreach(TBL ${TBL_SRC_FILES} ${ARGN}) - - # Get name without extension (NAME_WE) and append to list of tables - get_filename_component(TBLWE ${TBL} NAME_WE) - - foreach(TGT ${TABLE_TGTLIST}) - set(TABLE_LIBNAME "${TGT}_${APP_NAME}_${TBLWE}") - set(TABLE_DESTDIR "${CMAKE_CURRENT_BINARY_DIR}/${TABLE_LIBNAME}") - set(TABLE_BINARY "${TABLE_DESTDIR}/${TBLWE}.tbl") - file(MAKE_DIRECTORY ${TABLE_DESTDIR}) - - # Check if an override exists at the mission level (recommended practice) - # This allows a mission to implement a customized table without modifying - # the original - this also makes for easier merging/updating if needed. - if (EXISTS "${MISSION_DEFS}/tables/${TGT}_${TBLWE}.c") - set(TBL_SRC "${MISSION_DEFS}/tables/${TGT}_${TBLWE}.c") - elseif (EXISTS "${MISSION_SOURCE_DIR}/tables/${TGT}_${TBLWE}.c") - set(TBL_SRC "${MISSION_SOURCE_DIR}/tables/${TGT}_${TBLWE}.c") - elseif (EXISTS "${MISSION_DEFS}/${TGT}/tables/${TBLWE}.c") - set(TBL_SRC "${MISSION_DEFS}/${TGT}/tables/${TBLWE}.c") - elseif (EXISTS "${MISSION_DEFS}/tables/${TBLWE}.c") - set(TBL_SRC "${MISSION_DEFS}/tables/${TBLWE}.c") - elseif (EXISTS "${MISSION_SOURCE_DIR}/tables/${TBLWE}.c") - set(TBL_SRC "${MISSION_SOURCE_DIR}/tables/${TBLWE}.c") - elseif (IS_ABSOLUTE "${TBL}") - set(TBL_SRC "${TBL}") - else() - set(TBL_SRC "${CMAKE_CURRENT_SOURCE_DIR}/${TBL}") - endif() - - if (NOT EXISTS "${TBL_SRC}") - message(FATAL_ERROR "ERROR: No source file for table ${TBLWE}") - else() - message("NOTE: Selected ${TBL_SRC} as source for ${APP_NAME}.${TBLWE} on ${TGT}") - - # NOTE: On newer CMake versions this should become an OBJECT library which makes this simpler. - # On older versions one may not reference the TARGET_OBJECTS property from the custom command. - # As a workaround this is built into a static library, and then the desired object is extracted - # before passing to elf2cfetbl. It is roundabout but it works. - add_library(${TABLE_LIBNAME} STATIC ${TBL_SRC}) - target_link_libraries(${TABLE_LIBNAME} PRIVATE core_api) - if (TARGET ${APP_NAME}) - target_include_directories(${TABLE_LIBNAME} PRIVATE $) - target_compile_definitions(${TABLE_LIBNAME} PRIVATE $) - endif() - - # IMPORTANT: This rule assumes that the output filename of elf2cfetbl matches - # the input file name but with a different extension (.o -> .tbl) - # The actual output filename is embedded in the source file (.c), however - # this must match and if it does not the build will break. That's just the - # way it is, because NO make system supports changing rules based on the - # current content of a dependency (rightfully so). - add_custom_command( - OUTPUT ${TABLE_BINARY} - COMMAND ${CMAKE_COMMAND} - -DCMAKE_AR=${CMAKE_AR} - -DTBLTOOL=${MISSION_BINARY_DIR}/tools/elf2cfetbl/elf2cfetbl - -DLIB=$ - -P ${CFE_SOURCE_DIR}/cmake/generate_table.cmake - DEPENDS ${MISSION_BINARY_DIR}/tools/elf2cfetbl/elf2cfetbl ${TABLE_LIBNAME} - WORKING_DIRECTORY ${TABLE_DESTDIR} - ) - - # Add a custom target to invoke the elf2cfetbl tool to generate the tbl file, - # and install that binary file to the staging area. - add_custom_target(${TABLE_LIBNAME}_tbl ALL DEPENDS ${TABLE_BINARY}) - install(FILES ${TABLE_BINARY} DESTINATION ${TGT}/${INSTALL_SUBDIR}) - endif() - endforeach() endforeach() @@ -513,6 +598,10 @@ endfunction(cfs_app_check_intf) # function(prepare) + # "cfetables" is a top level target to build all table files + # all generated table files will be added as dependencies to this target + add_custom_target(cfetables) + # Choose the configuration file to use for OSAL on this system set(OSAL_CONFIGURATION_FILE) foreach(CONFIG ${BUILD_CONFIG_${TARGETSYSTEM}} ${OSAL_SYSTEM_OSCONFIG}) diff --git a/cmake/generate_table.cmake b/cmake/generate_table.cmake deleted file mode 100644 index b817ce6da..000000000 --- a/cmake/generate_table.cmake +++ /dev/null @@ -1,49 +0,0 @@ -################################################################## -# -# Sub-script to generate a table file via elf2cfetbl -# -# This small script runs at build time (as opposed to prep time) -# which converts a static library (.a) with a single object into a -# table (.tbl) file -# -################################################################## - -# -# Required passed in values: -# CMAKE_AR => path to "ar" utility for working with static lib files -# TBLTOOL => path to "elf2cfetbl" utility -# LIB => name of library file to convert -# -# This assumes/requires that the static library has a single object in it. -# Note, In newer versions of CMake an object library can be used. This workaround -# is intended to also be compatible with older CMake versions. -# - -# First run "ar t" to get the object file name, there should be only 1 file. -execute_process(COMMAND ${CMAKE_AR} t "${LIB}" - OUTPUT_VARIABLE OBJNAME - RESULT_VARIABLE RESULT - OUTPUT_STRIP_TRAILING_WHITESPACE -) -if (NOT RESULT EQUAL 0) - message(FATAL_ERROR "Failure running ${CMAKE_AR} t ${LIB}") -endif() - -# Next run "ar x" to extract that file. -execute_process(COMMAND ${CMAKE_AR} x "${LIB}" "${OBJNAME}" - RESULT_VARIABLE RESULT -) -if (NOT RESULT EQUAL 0) - message(FATAL_ERROR "Failure running ${CMAKE_AR} x ${LIB} ${OBJNAME}") -endif() - -# Finally invoke the table tool (elf2cfetbl) on the object -message("Executing Process: ${TBLTOOL} ${OBJNAME}") -execute_process(COMMAND ${TBLTOOL} "${OBJNAME}" - RESULT_VARIABLE RESULT -) -if (NOT RESULT EQUAL 0) - message(FATAL_ERROR "Failure running ${TBLTOOL}") -endif() - -message("Successfully converted ${LIB} to a CFE table") diff --git a/cmake/mission_build.cmake b/cmake/mission_build.cmake index ef3d62a8a..2ee6f7094 100644 --- a/cmake/mission_build.cmake +++ b/cmake/mission_build.cmake @@ -174,14 +174,28 @@ function(prepare) add_definitions(-DSIMULATION=${SIMULATION}) endif (SIMULATION) + # Prepare the table makefile - Ensure the list of tables is initially empty + file(MAKE_DIRECTORY "${MISSION_BINARY_DIR}/tables") + file(WRITE "${MISSION_BINARY_DIR}/tables/Makefile" + "MISSION_BINARY_DIR := ${MISSION_BINARY_DIR}\n" + "TABLE_BINARY_DIR := ${MISSION_BINARY_DIR}/tables\n" + "MISSION_SOURCE_DIR := ${MISSION_SOURCE_DIR}\n" + "MISSION_DEFS := ${MISSION_DEFS}\n\n" + "include \$(wildcard ${CFE_SOURCE_DIR}/cmake/tables/*.mk) \$(wildcard *.d)\n" + ) + # Create custom targets for building and cleaning all architectures # This is required particularly for doing extra stuff in the clean step add_custom_target(mission-all COMMAND $(MAKE) all) add_custom_target(mission-install COMMAND $(MAKE) install) add_custom_target(mission-clean COMMAND $(MAKE) clean) add_custom_target(mission-prebuild) + add_custom_target(mission-cfetables) add_custom_target(doc-prebuild) + add_dependencies(mission-all mission-cfetables) + add_dependencies(mission-install mission-cfetables) + # Locate the source location for all the apps found within the target file # This is done by searching through the list of paths to find a matching name # The environment variable is cached so it will be retained across runs. @@ -379,13 +393,31 @@ function(prepare) include_directories( ${core_api_MISSION_DIR}/fsw/inc ${osal_MISSION_DIR}/src/os/inc - ${psp_MISSION_DIR}/psp/fsw/inc + ${psp_MISSION_DIR}/fsw/inc ) add_subdirectory(${MISSION_SOURCE_DIR}/tools tools) # Add a dependency on the table generator tool as this is required for table builds # The "elf2cfetbl" target should have been added by the "tools" above add_dependencies(mission-prebuild elf2cfetbl) + set(TABLETOOL_EXEC $) + + add_custom_target(tabletool-execute + COMMAND $(MAKE) + CC="${CMAKE_C_COMPILER}" + CFLAGS="${CMAKE_C_FLAGS}" + AR="${CMAKE_AR}" + TBLTOOL="${TABLETOOL_EXEC}" + cfetables + WORKING_DIRECTORY + "${CMAKE_BINARY_DIR}/tables" + DEPENDS + mission-cfetables + ) + add_dependencies(mission-all tabletool-execute) + add_dependencies(mission-install tabletool-execute) + add_dependencies(mission-cfetables mission-prebuild) + install(DIRECTORY ${CMAKE_BINARY_DIR}/tables/staging/ DESTINATION .) # Build version information should be generated as part of the pre-build process add_dependencies(mission-prebuild mission-version) @@ -475,13 +507,21 @@ function(process_arch TARGETSYSTEM) WORKING_DIRECTORY "${ARCH_BINARY_DIR}" ) + add_custom_target(${TARGETSYSTEM}-cfetables + COMMAND + $(MAKE) cfetables + WORKING_DIRECTORY + "${ARCH_BINARY_DIR}" + ) # All subordinate builds depend on the generated files being present first add_dependencies(${TARGETSYSTEM}-install mission-prebuild) add_dependencies(${TARGETSYSTEM}-all mission-prebuild) + add_dependencies(${TARGETSYSTEM}-cfetables mission-prebuild) add_dependencies(mission-all ${TARGETSYSTEM}-all) add_dependencies(mission-clean ${TARGETSYSTEM}-clean) add_dependencies(mission-install ${TARGETSYSTEM}-install) + add_dependencies(mission-cfetables ${TARGETSYSTEM}-cfetables) endfunction(process_arch TARGETSYSTEM) diff --git a/cmake/tables/elf2cfetbl_rules.mk b/cmake/tables/elf2cfetbl_rules.mk new file mode 100644 index 000000000..fa36a6bf0 --- /dev/null +++ b/cmake/tables/elf2cfetbl_rules.mk @@ -0,0 +1,5 @@ +# Rule for traditional CFE table generation via elf2cfetbl + +elf/%.o: + @mkdir -pv $(dir $(@)) + cd $(dir $(@)) && $(AR) x $(abspath $(<)) $(notdir $(@)) diff --git a/cmake/tables/generate_elf_table_rules.cmake b/cmake/tables/generate_elf_table_rules.cmake new file mode 100644 index 000000000..35068e060 --- /dev/null +++ b/cmake/tables/generate_elf_table_rules.cmake @@ -0,0 +1,34 @@ +################################################################## +# +# Sub-script to capture the table compile/generation environment +# +# This small script runs at build time (as opposed to prep time) +# which captures a set of environment metadata +# +# It must be done this way such that generator expressions will +# be evaluated now, during the arch build process, rather than +# deferring the evaluation to the parent build where they may +# have different values. +# +################################################################## + +set(STAGING_DIR staging ${TARGET_NAME} ${INSTALL_SUBDIR}) +string(REPLACE ";" "/" STAGING_DIR "${STAGING_DIR}") + +set(TABLE_BINARY "${STAGING_DIR}/${TABLE_NAME}.tbl") +set(TMP_DIR "elf/${TARGET_NAME}") +set(TABLE_RULES) + +foreach(TBL_SRC ${SOURCES}) + + get_filename_component(DEP_FILE ${TBL_SRC} NAME) + set(DEP_FILE "${TMP_DIR}/${DEP_FILE}.o") + string(APPEND TABLE_RULES + "${DEP_FILE}: ${ARCHIVE_FILE}\n" + "${TABLE_BINARY}: ${DEP_FILE}\n" + "\n" + ) + +endforeach() + +configure_file(${TEMPLATE_FILE} ${OUTPUT_FILE}) diff --git a/cmake/tables/table_rule_template.d.in b/cmake/tables/table_rule_template.d.in new file mode 100644 index 000000000..4fa664818 --- /dev/null +++ b/cmake/tables/table_rule_template.d.in @@ -0,0 +1,10 @@ +# Template for table configuration + +cfetables: ${TABLE_BINARY} + +${TABLE_BINARY}: CFE_TABLE_CPUNAME := ${TARGET_NAME} +${TABLE_BINARY}: CFE_TABLE_APPNAME := ${APP_NAME} +${TABLE_BINARY}: CFE_TABLE_BASENAME := ${TABLE_NAME} + +# Rules to build ${TABLE_BINARY} +${TABLE_RULES} diff --git a/cmake/tables/tabletool_rule.mk b/cmake/tables/tabletool_rule.mk new file mode 100644 index 000000000..0e2e178f2 --- /dev/null +++ b/cmake/tables/tabletool_rule.mk @@ -0,0 +1,10 @@ +# Makefile for EDS-based CFE table generation +.PHONY: cfetables + +cfetables: + @echo "Table build completed" + +#%.tbl: $(TBLTOOL) +staging/%.tbl: + @mkdir -pv $(dir $(@)) + cd $(dir $(@)) && $(TBLTOOL) $(TBLTOOL_FLAGS) $(abspath $(^))