Files
salmanoff/cmake/DAPSS.cmake
T

190 lines
7.6 KiB
CMake

# DAPSS (Device Attachment Pipe Specification Source) preprocessing module
# This module provides functionality to preprocess .dapss files to .daps files
# using the C preprocessor, respecting include directories and target dependencies.
#
# Usage:
# add_daps_target(target_name SOURCES file1.dapss file2.dapss ...)
# register_daps_target(target_name) # In subdirectories
# add_all_daps_dependencies() # In main CMakeLists.txt
# add_daps_clean_target() # Optional: clean_daps_specs removes
# all .daps outputs from add_daps_target.
#
# Examples:
# add_daps_target(device_specs SOURCES devices/avia0.dapss devices/win0.dapss)
# register_daps_target(device_specs)
# add_all_daps_dependencies()
#
# The preprocessed .daps files will be placed in ${CMAKE_CURRENT_BINARY_DIR}/
# Function to add a DAPSS preprocessing target
# Usage: add_daps_target(target_name SOURCES file1.dapss file2.dapss ...)
function(add_daps_target target_name)
set(options)
set(oneValueArgs)
set(multiValueArgs SOURCES)
cmake_parse_arguments(DAPS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(NOT DAPS_SOURCES)
message(FATAL_ERROR "add_daps_target: No SOURCES specified for target ${target_name}")
endif()
# Use binary directory directly for processed files
# This ensures files are created in the same directory as the target
set(output_dir "${CMAKE_CURRENT_BINARY_DIR}")
# List to store all output files
set(output_files)
# Process each source file
foreach(source_file ${DAPS_SOURCES})
# Get the base name without extension
get_filename_component(base_name ${source_file} NAME_WE)
get_filename_component(source_dir ${source_file} DIRECTORY)
# Create output file path
set(output_file "${output_dir}/${base_name}.daps")
list(APPEND output_files ${output_file})
# Get include directories from current directory and target
get_directory_property(include_dirs INCLUDE_DIRECTORIES)
# Build include flags
set(include_flags)
foreach(include_dir ${include_dirs})
list(APPEND include_flags "-I${include_dir}")
endforeach()
# Add current source directory to includes if it's not already there
if(source_dir)
list(APPEND include_flags "-I${source_dir}")
endif()
# Convert list to space-separated string
string(REPLACE ";" " " include_flags_str "${include_flags}")
# Find C compiler if not already set
if(NOT CMAKE_C_COMPILER)
find_program(CMAKE_C_COMPILER gcc cc clang)
if(NOT CMAKE_C_COMPILER)
message(FATAL_ERROR "No C compiler found for DAPSS preprocessing")
endif()
endif()
# Create custom command to preprocess the file
add_custom_command(
OUTPUT ${output_file}
COMMAND sh -c "\"${CMAKE_C_COMPILER}\" -E -P -x c ${include_flags_str} \"${CMAKE_CURRENT_SOURCE_DIR}/${source_file}\" > \"${output_file}\""
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${source_file}
COMMENT "Preprocessing ${source_file} to ${base_name}.daps"
VERBATIM
)
endforeach()
get_property(_daps_all_outputs GLOBAL PROPERTY DAPS_ALL_OUTPUT_FILES)
list(APPEND _daps_all_outputs ${output_files})
set_property(GLOBAL PROPERTY DAPS_ALL_OUTPUT_FILES "${_daps_all_outputs}")
# Create custom target that depends on all output files
add_custom_target(${target_name} DEPENDS ${output_files})
# Make the target part of the ALL target so it gets built by default
# This ensures it gets built when building just this subdirectory
set_target_properties(${target_name} PROPERTIES
FOLDER "${CMAKE_CURRENT_SOURCE_DIR}"
EXCLUDE_FROM_ALL FALSE
)
# Set target properties
set_target_properties(${target_name} PROPERTIES
DAPS_OUTPUT_DIR ${output_dir}
DAPS_OUTPUT_FILES "${output_files}"
)
# Make the target available globally
set(${target_name}_OUTPUT_DIR ${output_dir} PARENT_SCOPE)
set(${target_name}_OUTPUT_FILES "${output_files}" PARENT_SCOPE)
endfunction()
# Function to register a DAPSS target for later dependency addition
# Usage: register_daps_target(target_name)
# This stores the target name in a global property for later use
function(register_daps_target target_name)
# Store the target name in a global property
get_property(registered_targets GLOBAL PROPERTY DAPS_REGISTERED_TARGETS)
list(APPEND registered_targets ${target_name})
set_property(GLOBAL PROPERTY DAPS_REGISTERED_TARGETS ${registered_targets})
message(STATUS "Registered DAPSS target ${target_name} for later dependency addition")
endfunction()
# Function to add all registered DAPSS targets as dependencies
# Usage: add_all_daps_dependencies([TARGET main_target] [CONDITION condition_expression])
# This should be called from the main CMakeLists.txt after all subdirectories are processed
function(add_all_daps_dependencies)
set(options)
set(oneValueArgs TARGET CONDITION)
set(multiValueArgs)
cmake_parse_arguments(DAPS_ALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Default target is PROJECT_NAME
if(DAPS_ALL_TARGET)
set(dep_target ${DAPS_ALL_TARGET})
else()
set(dep_target ${PROJECT_NAME})
endif()
# Get all registered targets
get_property(registered_targets GLOBAL PROPERTY DAPS_REGISTERED_TARGETS)
if(registered_targets)
foreach(target_name ${registered_targets})
if(TARGET ${target_name})
if(DAPS_ALL_CONDITION)
if(${DAPS_ALL_CONDITION})
add_dependencies(${dep_target} ${target_name})
message(STATUS "Added registered DAPSS target ${target_name} as dependency of ${dep_target} (condition: ${DAPS_ALL_CONDITION})")
else()
message(STATUS "Skipped registered DAPSS target ${target_name} (condition: ${DAPS_ALL_CONDITION} not met)")
endif()
else()
add_dependencies(${dep_target} ${target_name})
message(STATUS "Added registered DAPSS target ${target_name} as dependency of ${dep_target}")
endif()
else()
message(WARNING "Registered DAPSS target ${target_name} does not exist")
endif()
endforeach()
else()
message(STATUS "No DAPSS targets registered for dependency addition")
endif()
endfunction()
# Custom target that deletes every generated .daps file from add_daps_target (all subdirs).
# Usage: add_daps_clean_target([NAME clean_daps_specs])
# Call once from the top-level CMakeLists.txt after every add_subdirectory that uses DAPSS.
function(add_daps_clean_target)
set(options)
set(oneValueArgs NAME)
set(multiValueArgs)
cmake_parse_arguments(DC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
if(DC_NAME)
set(_daps_clean_target ${DC_NAME})
else()
set(_daps_clean_target clean_daps_specs)
endif()
if(TARGET ${_daps_clean_target})
message(FATAL_ERROR "add_daps_clean_target: target '${_daps_clean_target}' already exists")
endif()
get_property(_daps_clean_files GLOBAL PROPERTY DAPS_ALL_OUTPUT_FILES)
if(_daps_clean_files)
add_custom_target(${_daps_clean_target}
COMMAND ${CMAKE_COMMAND} -E rm -f ${_daps_clean_files}
COMMENT "Removing generated .daps specification files"
)
else()
add_custom_target(${_daps_clean_target})
endif()
endfunction()