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)
26 # Warn user if both COMPONENT_SRCDIRS and COMPONENT_SRCS are set
28 message(WARNING "COMPONENT_SRCDIRS and COMPONENT_SRCS are both set, COMPONENT_SRCS will be ignored")
31 set(COMPONENT_SRCS "")
33 foreach(dir ${COMPONENT_SRCDIRS})
34 get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
35 if(NOT IS_DIRECTORY ${abs_dir})
36 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' does not exist")
39 file(GLOB matches "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S")
42 set(COMPONENT_SRCS "${COMPONENT_SRCS};${matches}")
44 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' has no source files")
49 # Remove COMPONENT_SRCEXCLUDE matches
50 foreach(exclude ${COMPONENT_SRCEXCLUDE})
51 get_filename_component(exclude "${exclude}" ABSOLUTE ${component_dir})
52 foreach(src ${COMPONENT_SRCS})
53 get_filename_component(abs_src "${src}" ABSOLUTE ${component_dir})
54 if("${exclude}" STREQUAL "${abs_src}") # compare as canonical paths
55 list(REMOVE_ITEM COMPONENT_SRCS "${src}")
60 # add as a PUBLIC library (if there are source files) or INTERFACE (if header only)
61 if(COMPONENT_SRCS OR embed_binaries)
62 add_library(${component} STATIC ${COMPONENT_SRCS})
63 set(include_type PUBLIC)
65 add_library(${component} INTERFACE) # header-only component
66 set(include_type INTERFACE)
69 # binaries to embed directly in library
70 spaces2list(COMPONENT_EMBED_FILES)
71 spaces2list(COMPONENT_EMBED_TXTFILES)
72 foreach(embed_data ${COMPONENT_EMBED_FILES} ${COMPONENT_EMBED_TXTFILES})
73 if(embed_data IN_LIST COMPONENT_EMBED_TXTFILES)
74 set(embed_type "TEXT")
76 set(embed_type "BINARY")
78 target_add_binary_data("${component}" "${embed_data}" "${embed_type}")
81 # add component public includes
82 foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS})
83 get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
84 if(NOT IS_DIRECTORY ${abs_dir})
85 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
86 "COMPONENT_ADD_INCLUDEDIRS entry '${include_dir}' not found")
88 target_include_directories(${component} ${include_type} ${abs_dir})
91 # add component private includes
92 foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS})
93 if(${include_type} STREQUAL INTERFACE)
94 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} "
95 "sets no component source files but sets COMPONENT_PRIV_INCLUDEDIRS")
98 get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
99 if(NOT IS_DIRECTORY ${abs_dir})
100 message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
101 "COMPONENT_PRIV_INCLUDEDIRS entry '${include_dir}' does not exist")
103 target_include_directories(${component} PRIVATE ${abs_dir})
107 function(register_config_only_component)
108 get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
109 get_filename_component(component ${component_dir} NAME)
114 function(add_component_dependencies target dep dep_type)
115 get_target_property(target_type ${target} TYPE)
116 get_target_property(target_imported ${target} IMPORTED)
118 if(${target_type} STREQUAL STATIC_LIBRARY OR ${target_type} STREQUAL EXECUTABLE)
120 # Add all compile options exported by dep into target
121 target_include_directories(${target} ${dep_type}
122 $<TARGET_PROPERTY:${dep},INTERFACE_INCLUDE_DIRECTORIES>)
123 target_compile_definitions(${target} ${dep_type}
124 $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_DEFINITIONS>)
125 target_compile_options(${target} ${dep_type}
126 $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_OPTIONS>)
131 function(components_finish_registration)
133 # have the executable target depend on all components in the build
134 set_target_properties(${CMAKE_PROJECT_NAME}.elf PROPERTIES INTERFACE_COMPONENT_REQUIRES "${BUILD_COMPONENTS}")
136 spaces2list(COMPONENT_REQUIRES_COMMON)
138 # each component should see the include directories of its requirements
140 # (we can't do this until all components are registered and targets exist in cmake, as we have
141 # a circular requirements graph...)
142 foreach(a ${BUILD_COMPONENTS})
144 get_component_requirements("${a}" a_deps a_priv_deps)
145 list(APPEND a_priv_deps ${COMPONENT_REQUIRES_COMMON})
147 add_component_dependencies(${a} ${b} PUBLIC)
150 foreach(b ${a_priv_deps})
151 add_component_dependencies(${a} ${b} PRIVATE)
154 get_target_property(a_type ${a} TYPE)
155 if(${a_type} MATCHES .+_LIBRARY)
156 set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}")
161 # Add each component library's link-time dependencies (which are otherwise ignored) to the executable
162 # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least).
163 # (maybe this should probably be something CMake does, but it doesn't do it...)
164 foreach(component ${BUILD_COMPONENTS})
165 if(TARGET ${component})
166 get_target_property(imported ${component} IMPORTED)
167 get_target_property(type ${component} TYPE)
169 if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE)
170 get_target_property(link_depends "${component}" LINK_DEPENDS)
172 set_property(TARGET ${CMAKE_PROJECT_NAME}.elf APPEND PROPERTY LINK_DEPENDS "${link_depends}")
179 target_link_libraries(${CMAKE_PROJECT_NAME}.elf ${COMPONENT_LIBRARIES})
181 message(STATUS "Component libraries: ${COMPONENT_LIBRARIES}")