components.cmake 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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)
  4. set(result "")
  5. foreach(path ${component_paths})
  6. get_filename_component(name "${path}" NAME)
  7. if("${name}" IN_LIST components)
  8. list(APPEND result "${name}")
  9. endif()
  10. endforeach()
  11. set("${result_path}" "${result}" PARENT_SCOPE)
  12. endfunction()
  13. # Add a component to the build, using the COMPONENT variables defined
  14. # in the parent
  15. #
  16. function(register_component)
  17. get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
  18. get_filename_component(component ${component_dir} NAME)
  19. spaces2list(COMPONENT_SRCDIRS)
  20. spaces2list(COMPONENT_ADD_INCLUDEDIRS)
  21. spaces2list(COMPONENT_SRCEXCLUDE)
  22. # Add to COMPONENT_SRCS by globbing in COMPONENT_SRCDIRS
  23. if(NOT COMPONENT_SRCS)
  24. foreach(dir ${COMPONENT_SRCDIRS})
  25. get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir})
  26. if(NOT IS_DIRECTORY ${abs_dir})
  27. message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' does not exist")
  28. endif()
  29. file(GLOB matches "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S")
  30. if(matches)
  31. list(SORT matches)
  32. set(COMPONENT_SRCS "${COMPONENT_SRCS};${matches}")
  33. else()
  34. message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' has no source files")
  35. endif()
  36. endforeach()
  37. endif()
  38. # Remove COMPONENT_SRCEXCLUDE matches
  39. foreach(exclude ${COMPONENT_SRCEXCLUDE})
  40. get_filename_component(exclude "${exclude}" ABSOLUTE ${component_dir})
  41. foreach(src ${COMPONENT_SRCS})
  42. get_filename_component(abs_src "${src}" ABSOLUTE ${component_dir})
  43. if("${exclude}" STREQUAL "${abs_src}") # compare as canonical paths
  44. list(REMOVE_ITEM COMPONENT_SRCS "${src}")
  45. endif()
  46. endforeach()
  47. endforeach()
  48. # add as a PUBLIC library (if there are source files) or INTERFACE (if header only)
  49. if(COMPONENT_SRCS OR embed_binaries)
  50. add_library(${component} STATIC ${COMPONENT_SRCS})
  51. set(include_type PUBLIC)
  52. else()
  53. add_library(${component} INTERFACE) # header-only component
  54. set(include_type INTERFACE)
  55. endif()
  56. # binaries to embed directly in library
  57. spaces2list(COMPONENT_EMBED_FILES)
  58. spaces2list(COMPONENT_EMBED_TXTFILES)
  59. foreach(embed_data ${COMPONENT_EMBED_FILES} ${COMPONENT_EMBED_TXTFILES})
  60. if(embed_data IN_LIST COMPONENT_EMBED_TXTFILES)
  61. set(embed_type "TEXT")
  62. else()
  63. set(embed_type "BINARY")
  64. endif()
  65. target_add_binary_data("${component}" "${embed_data}" "${embed_type}")
  66. endforeach()
  67. # add component public includes
  68. foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS})
  69. get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
  70. if(NOT IS_DIRECTORY ${abs_dir})
  71. message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
  72. "COMPONENT_ADD_INCLUDEDIRS entry '${include_dir}' not found")
  73. endif()
  74. target_include_directories(${component} ${include_type} ${abs_dir})
  75. endforeach()
  76. # add component private includes
  77. foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS})
  78. if(${include_type} STREQUAL INTERFACE)
  79. message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} "
  80. "sets no component source files but sets COMPONENT_PRIV_INCLUDEDIRS")
  81. endif()
  82. get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir})
  83. if(NOT IS_DIRECTORY ${abs_dir})
  84. message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: "
  85. "COMPONENT_PRIV_INCLUDEDIRS entry '${include_dir}' does not exist")
  86. endif()
  87. target_include_directories(${component} PRIVATE ${abs_dir})
  88. endforeach()
  89. endfunction()
  90. function(register_config_only_component)
  91. get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY)
  92. get_filename_component(component ${component_dir} NAME)
  93. # No-op for now...
  94. endfunction()
  95. function(add_component_dependencies target dep dep_type)
  96. get_target_property(target_type ${target} TYPE)
  97. get_target_property(target_imported ${target} IMPORTED)
  98. if(${target_type} STREQUAL STATIC_LIBRARY OR ${target_type} STREQUAL EXECUTABLE)
  99. if(TARGET ${dep})
  100. # Add all compile options exported by dep into target
  101. target_include_directories(${target} ${dep_type}
  102. $<TARGET_PROPERTY:${dep},INTERFACE_INCLUDE_DIRECTORIES>)
  103. target_compile_definitions(${target} ${dep_type}
  104. $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_DEFINITIONS>)
  105. target_compile_options(${target} ${dep_type}
  106. $<TARGET_PROPERTY:${dep},INTERFACE_COMPILE_OPTIONS>)
  107. endif()
  108. endif()
  109. endfunction()
  110. function(components_finish_registration)
  111. # have the executable target depend on all components in the build
  112. set_target_properties(${CMAKE_PROJECT_NAME}.elf PROPERTIES INTERFACE_COMPONENT_REQUIRES "${BUILD_COMPONENTS}")
  113. spaces2list(COMPONENT_REQUIRES_COMMON)
  114. # each component should see the include directories of its requirements
  115. #
  116. # (we can't do this until all components are registered and targets exist in cmake, as we have
  117. # a circular requirements graph...)
  118. foreach(a ${BUILD_COMPONENTS})
  119. if(TARGET ${a})
  120. get_component_requirements("${a}" a_deps a_priv_deps)
  121. list(APPEND a_priv_deps ${COMPONENT_REQUIRES_COMMON})
  122. foreach(b ${a_deps})
  123. add_component_dependencies(${a} ${b} PUBLIC)
  124. endforeach()
  125. foreach(b ${a_priv_deps})
  126. add_component_dependencies(${a} ${b} PRIVATE)
  127. endforeach()
  128. get_target_property(a_type ${a} TYPE)
  129. if(${a_type} MATCHES .+_LIBRARY)
  130. set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}")
  131. endif()
  132. endif()
  133. endforeach()
  134. # Add each component library's link-time dependencies (which are otherwise ignored) to the executable
  135. # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least).
  136. # (maybe this should probably be something CMake does, but it doesn't do it...)
  137. foreach(component ${BUILD_COMPONENTS})
  138. if(TARGET ${component})
  139. get_target_property(imported ${component} IMPORTED)
  140. get_target_property(type ${component} TYPE)
  141. if(NOT imported)
  142. if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE)
  143. get_target_property(link_depends "${component}" LINK_DEPENDS)
  144. if(link_depends)
  145. set_property(TARGET ${CMAKE_PROJECT_NAME}.elf APPEND PROPERTY LINK_DEPENDS "${link_depends}")
  146. endif()
  147. endif()
  148. endif()
  149. endif()
  150. endforeach()
  151. target_link_libraries(${CMAKE_PROJECT_NAME}.elf ${COMPONENT_LIBRARIES})
  152. message(STATUS "Component libraries: ${COMPONENT_LIBRARIES}")
  153. endfunction()