1 # Given a list of components in 'component_paths', filter only paths to the components
2 # mentioned in 'components' and return as a list in 'result_paths'
3 function(components_get_paths component_paths components result_paths)
5 foreach(path ${component_paths})
6 get_filename_component(name "${path}" NAME)
7 if("${name}" IN_LIST components)
8 list(APPEND result "${name}")
11 set("${result_path}" "${result}" PARENT_SCOPE)
14 # Add a component to the build, using the COMPONENT variables defined
17 function(register_component)
18 get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
19 get_filename_component(component ${component_dir} NAME)
21 spaces2list(COMPONENT_SRCDIRS)
22 spaces2list(COMPONENT_ADD_INCLUDEDIRS)
23 spaces2list(COMPONENT_SRCEXCLUDE)
25 # Add to COMPONENT_SRCS by globbing in COMPONENT_SRCDIRS
26 if(NOT COMPONENT_SRCS)
27 foreach(dir ${COMPONENT_SRCDIRS})
28 get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
29 if(NOT IS_DIRECTORY ${abs_dir})
30 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' does not exist")
33 file(GLOB matches "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S")
36 set(COMPONENT_SRCS "${COMPONENT_SRCS};${matches}")
38 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' has no source files")
43 # Remove COMPONENT_SRCEXCLUDE matches
44 foreach(exclude ${COMPONENT_SRCEXCLUDE})
45 get_filename_component(exclude "${exclude}" ABSOLUTE ${component_dir})
46 foreach(src ${COMPONENT_SRCS})
47 get_filename_component(abs_src "${src}" ABSOLUTE ${component_dir})
48 if("${exclude}" STREQUAL "${abs_src}") # compare as canonical paths
49 list(REMOVE_ITEM COMPONENT_SRCS src)
54 # add as a PUBLIC library (if there are source files) or INTERFACE (if header only)
55 if(COMPONENT_SRCS OR embed_binaries)
56 add_library(${component} STATIC ${COMPONENT_SRCS})
57 set(include_type PUBLIC)
59 add_library(${component} INTERFACE) # header-only component
60 set(include_type INTERFACE)
63 # binaries to embed directly in library
64 spaces2list(COMPONENT_EMBED_FILES)
65 spaces2list(COMPONENT_EMBED_TXTFILES)
66 foreach(embed_data ${COMPONENT_EMBED_FILES} ${COMPONENT_EMBED_TXTFILES})
67 if(embed_data IN_LIST COMPONENT_EMBED_TXTFILES)
68 set(embed_type "TEXT")
70 set(embed_type "BINARY")
72 target_add_binary_data("${component}" "${embed_data}" "${embed_type}")
75 # add component public includes
76 foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS})
77 get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
78 if(NOT IS_DIRECTORY ${abs_dir})
79 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
80 "COMPONENT_ADD_INCLUDEDIRS entry '${include_dir}' not found")
82 target_include_directories(${component} ${include_type} ${abs_dir})
85 # add component private includes
86 foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS})
87 if(${include_type} STREQUAL INTERFACE)
88 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} "
89 "sets no component source files but sets COMPONENT_PRIV_INCLUDEDIRS")
92 get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
93 if(NOT IS_DIRECTORY ${abs_dir})
94 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
95 "COMPONENT_PRIV_INCLUDEDIRS entry '${include_dir}' does not exist")
97 target_include_directories(${component} PRIVATE ${abs_dir})
101 function(register_config_only_component)
102 get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
103 get_filename_component(component ${component_dir} NAME)
108 function(add_component_dependencies target dep dep_type)
109 get_target_property(target_type ${target} TYPE)
110 get_target_property(target_imported ${target} IMPORTED)
112 if(${target_type} STREQUAL STATIC_LIBRARY OR ${target_type} STREQUAL EXECUTABLE)
114 # Add all compile options exported by dep into target
115 target_include_directories(${target} ${dep_type}
116 $<TARGET_PROPERTY:${dep},INTERFACE_INCLUDE_DIRECTORIES>)
117 target_compile_definitions(${target} ${dep_type}
118 $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_DEFINITIONS>)
119 target_compile_options(${target} ${dep_type}
120 $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_OPTIONS>)
125 function(components_finish_registration)
127 # have the executable target depend on all components in the build
128 set_target_properties(${CMAKE_PROJECT_NAME}.elf PROPERTIES INTERFACE_COMPONENT_REQUIRES "${BUILD_COMPONENTS}")
130 spaces2list(COMPONENT_REQUIRES_COMMON)
132 # each component should see the include directories of its requirements
134 # (we can't do this until all components are registered and targets exist in cmake, as we have
135 # a circular requirements graph...)
136 foreach(a ${BUILD_COMPONENTS})
138 get_component_requirements("${a}" a_deps a_priv_deps)
139 list(APPEND a_priv_deps ${COMPONENT_REQUIRES_COMMON})
141 add_component_dependencies(${a} ${b} PUBLIC)
144 foreach(b ${a_priv_deps})
145 add_component_dependencies(${a} ${b} PRIVATE)
148 get_target_property(a_type ${a} TYPE)
149 if(${a_type} MATCHES .+_LIBRARY)
150 set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}")
155 # Add each component library's link-time dependencies (which are otherwise ignored) to the executable
156 # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least).
157 # (maybe this should probably be something CMake does, but it doesn't do it...)
158 foreach(component ${BUILD_COMPONENTS})
159 if(TARGET ${component})
160 get_target_property(imported ${component} IMPORTED)
161 get_target_property(type ${component} TYPE)
163 if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE)
164 get_target_property(link_depends "${component}" LINK_DEPENDS)
166 set_property(TARGET ${CMAKE_PROJECT_NAME}.elf APPEND PROPERTY LINK_DEPENDS "${link_depends}")
173 target_link_libraries(${CMAKE_PROJECT_NAME}.elf ${COMPONENT_LIBRARIES})
175 message(STATUS "Component libraries: ${COMPONENT_LIBRARIES}")