# 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() list(APPEND include_flags "-I${CMAKE_CURRENT_SOURCE_DIR}") # Add source subdirectory to includes when SOURCES lists a path prefix if(source_dir) list(APPEND include_flags "-I${CMAKE_CURRENT_SOURCE_DIR}/${source_dir}") endif() # 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() set(depfile "${output_file}.d") # Preprocess with -MD so #included .dapss files become build dependencies # (e.g. body specs that #include ../elp-4k-usb-cam.dapss). add_custom_command( OUTPUT ${output_file} COMMAND "${CMAKE_C_COMPILER}" -E -P -x c ${include_flags} -MD -MQ "${output_file}" -MF "${depfile}" -o "${output_file}" "${CMAKE_CURRENT_SOURCE_DIR}/${source_file}" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${source_file}" DEPFILE "${depfile}" 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()