From 7d3a8760d366cdeb16d1281f657ff6a6acb752dd Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 19:46:57 +0300 Subject: [PATCH 01/17] Started refactoring of libraries sub-system. Most of the changes occurred originally in the 'header-only-lib' branch, but are moved here since they don't belong there. --- cmake/Platform/Arduino.cmake | 4 + cmake/Platform/Libraries/Libraries.cmake | 3 + .../Platform/Libraries/LibrariesFinder.cmake | 70 +++++++++ .../Libraries/LibraryArchitectureParser.cmake | 40 +++++ .../Libraries/LibraryFlagsManager.cmake | 27 ++++ .../Other/ArchitectureValidator.cmake | 29 ++++ .../Targets/ArduinoCMakeLibraryTarget.cmake | 70 ++++----- .../Targets/ArduinoLibraryTarget.cmake | 144 ++---------------- 8 files changed, 220 insertions(+), 167 deletions(-) create mode 100644 cmake/Platform/Libraries/Libraries.cmake create mode 100644 cmake/Platform/Libraries/LibrariesFinder.cmake create mode 100644 cmake/Platform/Libraries/LibraryArchitectureParser.cmake create mode 100644 cmake/Platform/Libraries/LibraryFlagsManager.cmake create mode 100644 cmake/Platform/Other/ArchitectureValidator.cmake diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index 4e77841..1f04d67 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -6,6 +6,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Other) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Properties) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Sketches) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Sources) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Libraries) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Targets) include(MathUtils) @@ -20,6 +21,9 @@ include(TargetFlagsManager) include(SourcesManager) include(SketchManager) include(DefaultsManager) +include(ArchitectureValidator) + +include(Libraries) include(BuildSystemInitializer) diff --git a/cmake/Platform/Libraries/Libraries.cmake b/cmake/Platform/Libraries/Libraries.cmake new file mode 100644 index 0000000..c4de6f5 --- /dev/null +++ b/cmake/Platform/Libraries/Libraries.cmake @@ -0,0 +1,3 @@ +include(LibraryArchitectureParser) +include(LibraryFlagsManager) +include(LibrariesFinder) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake new file mode 100644 index 0000000..18cc659 --- /dev/null +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -0,0 +1,70 @@ +#=============================================================================# +# Finds an Arduino library with the given library name and creates a library target from it +# with the given target name. +# The search process also resolves library's architecture to check if it even can be built +# using the current platform architecture. +# _target_name - Name of the library target to be created. Usually library's real name. +# _library_name - Name of the Arduino library to find. +# _board_id - Board ID associated with the linked Core Lib. +# [3RD_PARTY] - Whether library should be treated as a 3rd Party library. +# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files +#=============================================================================# +function(find_arduino_library _target_name _library_name _board_id) + + set(argument_options "3RD_PARTY" "HEADER_ONLY") + cmake_parse_arguments(parsed_args "${argument_options}" "" "" ${ARGN}) + + if (NOT parsed_args_3RD_PARTY) + convert_string_to_pascal_case(${_library_name} _library_name) + endif () + + find_file(library_properties_file library.properties + PATHS ${ARDUINO_SDK_LIBRARIES_PATH} ${ARDUINO_CMAKE_SKETCHBOOK_PATH}/libraries + PATH_SUFFIXES ${_library_name} + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH) + + if (${library_properties_file} MATCHES "NOTFOUND") + message(SEND_ERROR "Couldn't find library named ${_library_name}") + else () # Library is found + get_filename_component(library_path ${library_properties_file} DIRECTORY) + get_library_architecture("${library_properties_file}" lib_arch) + if (lib_arch) + if ("${lib_arch}" MATCHES "UNSUPPORTED") + string(CONCAT error_message + "${_library_name} " + "library isn't supported on the platform's architecture " + "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") + message(SEND_ERROR ${error_message}) + endif () + endif () + + find_library_header_files("${library_path}" library_headers) + if (NOT library_headers) + set(error_message "Couldn't find any header files for the ${_library_name} library") + message(SEND_ERROR "${error_message}") + else () + if (parsed_args_HEADER_ONLY) + add_arduino_header_only_library(${_target_name} ${_board_id} + ARCH ${lib_arch} + HEADERS ${library_headers}) + else () + find_library_source_files("${library_path}" library_sources) + if (NOT library_sources) + string(CONCAT error_message + "Couldn't find any source files for the ${_library_name} library - " + "Is it a header-only library?\n" + "If so, please pass the HEADER_ONLY option as an argument to the function") + message(SEND_ERROR "${error_message}") + else () + set(sources ${library_headers} ${library_sources}) + add_arduino_library(${_target_name} ${_board_id} "${sources}" + ARCH ${lib_arch}) + endif () + endif () + endif () + endif () + + unset(library_properties_file CACHE) + +endfunction() diff --git a/cmake/Platform/Libraries/LibraryArchitectureParser.cmake b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake new file mode 100644 index 0000000..8ecf308 --- /dev/null +++ b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake @@ -0,0 +1,40 @@ +#=============================================================================# +# Gets the library architecure if any, read from the given library properties file +# which includes this information. +# _library_properties_file - Full path to a library's properties file. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - If library is architecure neutral, nothing is returned. +# If library doesn't support the current platform's architecture, +# "UNSUPPORTED" string is returned. +# Otherwise, the platform's architecture is returned. +#=============================================================================# +function(get_library_architecture _library_properties_file _return_var) + + file(STRINGS ${_library_properties_file} library_properties) + + list(FILTER library_properties INCLUDE REGEX "arch") + _get_property_value("${library_properties}" arch_list) + string(REPLACE "," ";" arch_list ${arch_list}) # Turn into a valid list + + if ("${arch_list}" MATCHES "\\*") + return() # Any architecture is supported, return nothing + else () + list(LENGTH arch_list num_of_supported_archs) + if (${num_of_supported_archs} GREATER 1) # Number of architectures is supported + list(FIND arch_list ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} platform_arch_index) + if (${platform_arch_index} LESS 0) # Our arch isn't supported + set(__arch "UNSUPPORTED") + else () # Our arch is indeed supported + set(__arch ${arch_list}) + endif () + else () + list(GET arch_list 0 __arch) + if (NOT "${__arch}" STREQUAL "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") + set(__arch "UNSUPPORTED") # Our arch isn't supported + endif () + endif () + endif () + + set(${_return_var} ${__arch} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Libraries/LibraryFlagsManager.cmake b/cmake/Platform/Libraries/LibraryFlagsManager.cmake new file mode 100644 index 0000000..3d4d312 --- /dev/null +++ b/cmake/Platform/Libraries/LibraryFlagsManager.cmake @@ -0,0 +1,27 @@ +#=============================================================================# +# Sets compiler and linker flags on the given library target. +# Changes are kept even outside the scope of the function since they apply on a target. +# _library_target - Name of the library target. +# _board_id - Board ID associated with the library. Some flags require it. +#=============================================================================# +function(set_library_flags _library_target _board_id) + + set(scope_options "PRIVATE" "PUBLIC" "INTERFACE") + cmake_parse_arguments(parsed_args "${scope_options}" "" "" ${ARGN}) + + if (parsed_args_PRIVATE) + set(scope PRIVATE) + elseif (parsed_args_INTERFACE) + set(scope INTERFACE) + else () + set(scope PUBLIC) + endif () + + # Set C++ compiler flags + get_cmake_compliant_language_name(cpp flags_language) + set_compiler_target_flags(${_library_target} "${_board_id}" ${scope} LANGUAGE ${flags_language}) + + # Set linker flags + set_linker_flags(${_library_target} "${_board_id}") + +endfunction() diff --git a/cmake/Platform/Other/ArchitectureValidator.cmake b/cmake/Platform/Other/ArchitectureValidator.cmake new file mode 100644 index 0000000..b8261a0 --- /dev/null +++ b/cmake/Platform/Other/ArchitectureValidator.cmake @@ -0,0 +1,29 @@ +#=============================================================================# +# Gets a filtered list of architectures that aren't compliant with the platform's architecture. +# e.g If a list contains 'avr' and 'nrf52', while our arch is 'avr', 'nrf52' will be returned. +# _arch_list - List of all architectures probably read from a library's properties file +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Filtered list of architectures. +#=============================================================================# +function(get_unsupported_architectures _arch_list _return_var) + + cmake_parse_arguments(unsupported_archs "REGEX" "" "" ${ARGN}) + + list(FILTER _arch_list EXCLUDE REGEX + "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") + if (unsupported_archs_REGEX) # Return in regex format + list(LENGTH _arch_list num_of_unsupported_archs) + set(unsupported_arch_list "") + set(arch_index 1) + foreach (unsupported_arch ${_arch_list}) + string(APPEND unsupported_arch_list "${unsupported_arch}") + if (${arch_index} LESS ${num_of_unsupported_archs}) + string(APPEND unsupported_arch_list "|") + endif () + increment_integer(arch_index 1) + endforeach () + endif () + + set(${_return_var} ${unsupported_arch_list} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake index 132e5e4..db9fb01 100644 --- a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake @@ -1,20 +1,3 @@ -#=============================================================================# -# Sets compiler and linker flags on the given library target. -# Changes are kept even outside the scope of the function since they apply on a target. -# _library_target - Name of the library target. -# _board_id - Board ID associated with the library. Some flags require it. -#=============================================================================# -function(_set_library_flags _library_target _board_id) - - # Set C++ compiler flags - get_cmake_compliant_language_name(cpp flags_language) - set_compiler_target_flags(${_library_target} "${_board_id}" PUBLIC LANGUAGE ${flags_language}) - - # Set linker flags - set_linker_flags(${_library_target} "${_board_id}") - -endfunction() - #=============================================================================# # Creates a library target compliant with the Arduino library standard. # One can also specify an architecture for the library, which will result in a special parsing @@ -22,37 +5,45 @@ endfunction() # _target_name - Name of the library target to be created. Usually library's real name. # _board_id - Board ID associated with the linked Core Lib. # _sources - Source and header files to create library target from. -# [ARCH] - Optional library architecture (Such as 'avr', 'nrf52', etc.) +# [ARCH] - Optional library architecture (Such as 'avr', 'nrf52', etc.). +# [INTERFACE] - Whether the library should be created as an interface library (header-only). #=============================================================================# function(_add_arduino_cmake_library _target_name _board_id _sources) - cmake_parse_arguments(library "" "ARCH" "" ${ARGN}) + cmake_parse_arguments(parsed_args "INTERFACE" "ARCH" "" ${ARGN}) - if (library_ARCH) # Treat architecture-specific libraries differently + if (parsed_args_ARCH) # Treat architecture-specific libraries differently # Filter any sources that aren't supported by the platform's architecture list(LENGTH library_ARCH num_of_libs_archs) if (${num_of_libs_archs} GREATER 1) # Exclude all unsupported architectures, request filter in regex mode - _get_unsupported_architectures("${library_ARCH}" arch_filter REGEX) + get_unsupported_architectures("${parsed_args_ARCH}" arch_filter REGEX) set(filter_type EXCLUDE) else () - set(arch_filter "src\\/[^/]+\\.|${library_ARCH}") + set(arch_filter "src\\/[^/]+\\.|${parsed_args_ARCH}") set(filter_type INCLUDE) endif () list(FILTER _sources ${filter_type} REGEX ${arch_filter}) endif () - add_library(${_target_name} STATIC "${_sources}") + if (parsed_args_INTERFACE) + add_library(${_target_name} INTERFACE) + set(scope INTERFACE) + else () + add_library(${_target_name} STATIC "${_sources}") + set(scope PUBLIC) + endif () + # Treat headers' parent directories as include directories of the target get_headers_parent_directories("${_sources}" include_dirs) - target_include_directories(${_target_name} PUBLIC ${include_dirs}) + target_include_directories(${_target_name} ${scope} ${include_dirs}) - _set_library_flags(${_target_name} ${_board_id}) + set_library_flags(${_target_name} ${_board_id} ${scope}) - if (library_ARCH) - string(TOUPPER ${library_ARCH} upper_arch) + if (parsed_args_ARCH) + string(TOUPPER ${parsed_args_ARCH} upper_arch) set(arch_definition "ARDUINO_ARCH_${upper_arch}") - target_compile_definitions(${_target_name} PUBLIC ${arch_definition}) + target_compile_definitions(${_target_name} ${scope} ${arch_definition}) endif () endfunction() @@ -76,6 +67,15 @@ function(_link_arduino_cmake_library _target_name _library_name) set(scope_options "PRIVATE" "PUBLIC" "INTERFACE") cmake_parse_arguments(link_library "${scope_options}" "BOARD_CORE_TARGET" "" ${ARGN}) + # Now, link library to executable + if (link_library_PUBLIC) + set(scope PUBLIC) + elseif (link_library_INTERFACE) + set(scope INTERFACE) + else () + set(scope PRIVATE) + endif () + # First, include core lib's directories in library as well if (link_library_BOARD_CORE_TARGET) set(core_target ${link_library_BOARD_CORE_TARGET}) @@ -84,17 +84,9 @@ function(_link_arduino_cmake_library _target_name _library_name) endif () get_target_property(core_lib_includes ${core_target} INCLUDE_DIRECTORIES) - target_include_directories(${_library_name} PUBLIC "${core_lib_includes}") - target_link_libraries(${_library_name} PUBLIC ${core_target}) + target_include_directories(${_library_name} ${scope} "${core_lib_includes}") + target_link_libraries(${_library_name} ${scope} ${core_target}) - # Now, link library to executable - if (link_library_PUBLIC) - set(scope PUBLIC) - elseif (link_library_INTERFACE) - set(scope INTERFACE) - else () - set(scope PRIVATE) - endif () - target_link_libraries(${_target_name} ${scope} ${_library_name}) + target_link_libraries(${_target_name} PRIVATE ${_library_name}) endfunction() diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index ac22200..4038f80 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -1,74 +1,3 @@ -#=============================================================================# -# Gets the library architecure if any, read from the given library properties file -# which includes this information. -# _library_properties_file - Full path to a library's properties file. -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - If library is architecure neutral, nothing is returned. -# If library doesn't support the current platform's architecture, -# "UNSUPPORTED" string is returned. -# Otherwise, the platform's architecture is returned. -#=============================================================================# -function(_get_library_architecture _library_properties_file _return_var) - - file(STRINGS ${_library_properties_file} library_properties) - - list(FILTER library_properties INCLUDE REGEX "arch") - _get_property_value("${library_properties}" arch_list) - string(REPLACE "," ";" arch_list ${arch_list}) # Turn into a valid list - - if ("${arch_list}" MATCHES "\\*") - return() # Any architecture is supported, return nothing - else () - list(LENGTH arch_list num_of_supported_archs) - if (${num_of_supported_archs} GREATER 1) # Number of architectures is supported - list(FIND arch_list ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} platform_arch_index) - if (${platform_arch_index} LESS 0) # Our arch isn't supported - set(__arch "UNSUPPORTED") - else () # Our arch is indeed supported - set(__arch ${arch_list}) - endif () - else () - list(GET arch_list 0 __arch) - if (NOT "${__arch}" STREQUAL "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - set(__arch "UNSUPPORTED") # Our arch isn't supported - endif () - endif () - endif () - - set(${_return_var} ${__arch} PARENT_SCOPE) - -endfunction() - -#=============================================================================# -# Gets a filtered list of architectures that aren't compliant with the platform's architecture. -# For example: If a list contains 'avr' and 'nrf52', while our arch is 'avr', 'nrf52' will be returned. -# _arch_list - List of all architectures probably read from a library's properties file -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - Filtered list of architectures. -#=============================================================================# -function(_get_unsupported_architectures _arch_list _return_var) - - cmake_parse_arguments(unsupported_archs "REGEX" "" "" ${ARGN}) - - list(FILTER _arch_list EXCLUDE REGEX - "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - if (unsupported_archs_REGEX) # Return in regex format - list(LENGTH _arch_list num_of_unsupported_archs) - set(unsupported_arch_list "") - set(arch_index 1) - foreach (unsupported_arch ${_arch_list}) - string(APPEND unsupported_arch_list "${unsupported_arch}") - if (${arch_index} LESS ${num_of_unsupported_archs}) - string(APPEND unsupported_arch_list "|") - endif () - increment_integer(arch_index 1) - endforeach () - endif () - - set(${_return_var} ${unsupported_arch_list} PARENT_SCOPE) - -endfunction() - #=============================================================================# # Creates a library target for the given name and sources. # As it's an Arduino library, it also finds and links all dependent platform libraries (if any). @@ -86,62 +15,12 @@ function(add_arduino_library _target_name _board_id _sources) endfunction() -#=============================================================================# -# Finds an Arduino library with the given library name and creates a library target from it -# with the given target name. -# The search process also resolves library's architecture to check if it even can be built -# using the current platform architecture. -# _target_name - Name of the library target to be created. Usually library's real name. -# _library_name - Name of the Arduino library to find. -# _board_id - Board ID associated with the linked Core Lib. -# [3RD_PARTY] - Whether library should be treated as a 3rd Party library. -#=============================================================================# -function(find_arduino_library _target_name _library_name _board_id) - - cmake_parse_arguments(find_lib "3RD_PARTY" "" "" ${ARGN}) +function(add_arduino_header_only_library _target_name _board_id) - if (NOT find_lib_3RD_PARTY) - convert_string_to_pascal_case(${_library_name} _library_name) - endif () - - find_file(library_properties_file library.properties - PATHS ${ARDUINO_SDK_LIBRARIES_PATH} ${ARDUINO_CMAKE_SKETCHBOOK_PATH}/libraries - PATH_SUFFIXES ${_library_name} - NO_DEFAULT_PATH - NO_CMAKE_FIND_ROOT_PATH) - - if (${library_properties_file} MATCHES "NOTFOUND") - message(SEND_ERROR "Couldn't find library named ${_library_name}") - else () # Library is found - get_filename_component(library_path ${library_properties_file} DIRECTORY) - _get_library_architecture("${library_properties_file}" lib_arch) - if (lib_arch) - if ("${lib_arch}" MATCHES "UNSUPPORTED") - string(CONCAT error_message - "${_library_name} " - "library isn't supported on the platform's architecture " - "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - message(SEND_ERROR ${error_message}) - endif () - endif () - - find_library_header_files("${library_path}" library_headers) - if (NOT library_headers) - set(error_message "Couldn't find any header files for the ${_library_name} library") - message(SEND_ERROR "${error_message}") - else () - find_library_source_files("${library_path}" library_sources) - if (NOT library_sources) - set(error_message "Couldn't find any source files for the ${_library_name} library") - message(SEND_ERROR "${error_message}") - else () - set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} "${sources}" ARCH ${lib_arch}) - endif () - endif () - endif () + cmake_parse_arguments(parsed_args "ARCH" "" "HEADERS" ${ARGN}) - unset(library_properties_file CACHE) + _add_arduino_cmake_library(${_target_name} ${_board_id} "${parsed_args_HEADERS}" + INTERFACE ${parsed_args_ARCH}) endfunction() @@ -151,9 +30,12 @@ endfunction() # _target_name - Name of the "executable" target. # _library_target_name - Name of the library target. # _board_id - Board ID associated with the linked Core Lib. +# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files #=============================================================================# function(link_arduino_library _target_name _library_target_name _board_id) + cmake_parse_arguments(parsed_args "HEADER_ONLY" "" "" ${ARGN}) + get_core_lib_target_name(${_board_id} core_lib_target) if (NOT TARGET ${_target_name}) @@ -164,8 +46,14 @@ function(link_arduino_library _target_name _library_target_name _board_id) message(FATAL_ERROR "Core Library target doesn't exist. This is bad and should be reported") endif () - _link_arduino_cmake_library(${_target_name} ${_library_target_name} - PUBLIC - BOARD_CORE_TARGET ${core_lib_target}) + if (parsed_args_HEADER_ONLY) + _link_arduino_cmake_library(${_target_name} ${_library_target_name} + INTERFACE + BOARD_CORE_TARGET ${core_lib_target}) + else () + _link_arduino_cmake_library(${_target_name} ${_library_target_name} + PUBLIC + BOARD_CORE_TARGET ${core_lib_target}) + endif () endfunction() From b0df5ab174d73873df41f25eee7dfdbc6ded711a Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 19:59:39 +0300 Subject: [PATCH 02/17] Refactored the entire library architecture processing logic. It also came with a cost of requiring the library's root directory in the `add_library` API. --- cmake/Platform/Arduino.cmake | 2 +- cmake/Platform/Libraries/Libraries.cmake | 1 + .../Platform/Libraries/LibrariesFinder.cmake | 16 +---- .../Libraries/LibraryArchitectureParser.cmake | 46 +++++++-------- .../LibrarySourcesArchitectureResolver.cmake | 59 +++++++++++++++++++ .../Other/ArchitectureValidator.cmake | 29 --------- .../SupportedArchitecturesRetriever.cmake | 40 +++++++++++++ .../Targets/ArduinoCMakeLibraryTarget.cmake | 24 ++------ .../Targets/ArduinoLibraryTarget.cmake | 12 +++- examples/3rd-party-library/3rd_party.cpp | 14 +++++ examples/3rd-party-library/CMakeLists.txt | 17 ++++-- examples/3rd-party-library/GFXTest.cpp | 2 +- examples/3rd-party-library/NeoPixelTest.cpp | 11 +--- examples/3rd-party-library/include/GFXTest.h | 2 +- .../include/NeoPixelTest.hpp | 8 +++ 15 files changed, 177 insertions(+), 106 deletions(-) create mode 100644 cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake delete mode 100644 cmake/Platform/Other/ArchitectureValidator.cmake create mode 100644 cmake/Platform/Other/SupportedArchitecturesRetriever.cmake create mode 100644 examples/3rd-party-library/3rd_party.cpp create mode 100644 examples/3rd-party-library/include/NeoPixelTest.hpp diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index 1f04d67..190346b 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -21,7 +21,7 @@ include(TargetFlagsManager) include(SourcesManager) include(SketchManager) include(DefaultsManager) -include(ArchitectureValidator) +include(SupportedArchitecturesRetriever) include(Libraries) diff --git a/cmake/Platform/Libraries/Libraries.cmake b/cmake/Platform/Libraries/Libraries.cmake index c4de6f5..ff0a7e2 100644 --- a/cmake/Platform/Libraries/Libraries.cmake +++ b/cmake/Platform/Libraries/Libraries.cmake @@ -1,3 +1,4 @@ include(LibraryArchitectureParser) +include(LibrarySourcesArchitectureResolver) include(LibraryFlagsManager) include(LibrariesFinder) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index 18cc659..f3efae3 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -28,16 +28,6 @@ function(find_arduino_library _target_name _library_name _board_id) message(SEND_ERROR "Couldn't find library named ${_library_name}") else () # Library is found get_filename_component(library_path ${library_properties_file} DIRECTORY) - get_library_architecture("${library_properties_file}" lib_arch) - if (lib_arch) - if ("${lib_arch}" MATCHES "UNSUPPORTED") - string(CONCAT error_message - "${_library_name} " - "library isn't supported on the platform's architecture " - "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - message(SEND_ERROR ${error_message}) - endif () - endif () find_library_header_files("${library_path}" library_headers) if (NOT library_headers) @@ -46,7 +36,7 @@ function(find_arduino_library _target_name _library_name _board_id) else () if (parsed_args_HEADER_ONLY) add_arduino_header_only_library(${_target_name} ${_board_id} - ARCH ${lib_arch} + ARCH ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} HEADERS ${library_headers}) else () find_library_source_files("${library_path}" library_sources) @@ -58,8 +48,8 @@ function(find_arduino_library _target_name _library_name _board_id) message(SEND_ERROR "${error_message}") else () set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} "${sources}" - ARCH ${lib_arch}) + add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" + LIB_PROPS_FILE ${library_properties_file}) endif () endif () endif () diff --git a/cmake/Platform/Libraries/LibraryArchitectureParser.cmake b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake index 8ecf308..3f6963a 100644 --- a/cmake/Platform/Libraries/LibraryArchitectureParser.cmake +++ b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake @@ -1,40 +1,38 @@ +function(get_arduino_library_supported_architectures _library_properties_file _return_var) + + file(STRINGS ${_library_properties_file} library_properties) + + list(FILTER library_properties INCLUDE REGEX "arch") + _get_property_value("${library_properties}" _library_arch_list) + string(REPLACE "," ";" _library_arch_list ${_library_arch_list}) # Turn into a valid list + + set(${_return_var} ${_library_arch_list} PARENT_SCOPE) + +endfunction() + #=============================================================================# # Gets the library architecure if any, read from the given library properties file # which includes this information. -# _library_properties_file - Full path to a library's properties file. +# _library_arch_list - List of architectures supported by the library, +# inferred from its' 'library.properties' file. # _return_var - Name of variable in parent-scope holding the return value. -# Returns - If library is architecure neutral, nothing is returned. +# Returns - If library is architecure agnostic (Supports all), nothing is returned. # If library doesn't support the current platform's architecture, # "UNSUPPORTED" string is returned. # Otherwise, the platform's architecture is returned. #=============================================================================# -function(get_library_architecture _library_properties_file _return_var) - - file(STRINGS ${_library_properties_file} library_properties) - - list(FILTER library_properties INCLUDE REGEX "arch") - _get_property_value("${library_properties}" arch_list) - string(REPLACE "," ";" arch_list ${arch_list}) # Turn into a valid list +function(is_library_supports_platform_architecture _library_arch_list _return_var) - if ("${arch_list}" MATCHES "\\*") - return() # Any architecture is supported, return nothing + if ("${_library_arch_list}" MATCHES "\\*") # Any architecture is supported + set(result TRUE) else () - list(LENGTH arch_list num_of_supported_archs) - if (${num_of_supported_archs} GREATER 1) # Number of architectures is supported - list(FIND arch_list ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} platform_arch_index) - if (${platform_arch_index} LESS 0) # Our arch isn't supported - set(__arch "UNSUPPORTED") - else () # Our arch is indeed supported - set(__arch ${arch_list}) - endif () + if (${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} IN_LIST _library_arch_list) + set(result TRUE) # Our platform's arch is supported else () - list(GET arch_list 0 __arch) - if (NOT "${__arch}" STREQUAL "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - set(__arch "UNSUPPORTED") # Our arch isn't supported - endif () + set(result FALSE) # Our arch isn't supported endif () endif () - set(${_return_var} ${__arch} PARENT_SCOPE) + set(${_return_var} ${result} PARENT_SCOPE) endfunction() diff --git a/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake new file mode 100644 index 0000000..e7983a4 --- /dev/null +++ b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake @@ -0,0 +1,59 @@ +function(_filter_unsupported_arch_sources _unsupported_archs_regex _sources _return_var) + + #string(LENGTH _unsupported_archs_regex num_of_unsupported_archs) + + if (NOT "${_unsupported_archs_regex}" STREQUAL "") # Not all architectures are supported + # Filter sources dependant on unsupported architectures + list(FILTER _sources EXCLUDE REGEX ${_unsupported_archs_regex}) + endif () + + set(${_return_var} ${_sources} PARENT_SCOPE) + +endfunction() + +function(resolve_library_sources_by_architecture _library_root_dir _library_sources _return_var) + + cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) + + if (parsed_args_LIB_PROPS_FILE) # Library properties file is given + set(lib_props_file ${parsed_args_LIB_PROPS_FILE}) + else () # Try to automatically find file from sources + + # Get the absolute root directory (full path) + get_filename_component(absolute_lib_root_dir ${_library_root_dir} ABSOLUTE) + + if (EXISTS ${absolute_lib_root_dir}/library.properties) + set(lib_props_file ${absolute_lib_root_dir}/library.properties) + + else () # Properties file can't be found - Warn user and assume library is arch-agnostic + + get_filename_component(library_name ${absolute_lib_root_dir} NAME) + message(WARNING "\"${library_name}\" library's properties file can't be found " + "under its' root directory - Assuming the library " + "is architecture-agnostic (supports all architectures)") + set(${_return_var} ${_library_sources} PARENT_SCOPE) + return() + + endif () + + endif () + + get_arduino_library_supported_architectures("${lib_props_file}" lib_archs) + is_library_supports_platform_architecture(${lib_archs} arch_supported_by_lib) + + if (NOT ${arch_supported_by_lib}) + string(CONCAT error_message + "The ${_library_name} " + "library isn't supported on the platform's architecture " + "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") + message(SEND_ERROR ${error_message}) + endif () + + get_unsupported_architectures("${lib_archs}" unsupported_archs REGEX) + + # Filter any sources that aren't supported by the platform's architecture + _filter_unsupported_arch_sources("${unsupported_archs}" "${_library_sources}" valid_sources) + + set(${_return_var} "${valid_sources}" PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Other/ArchitectureValidator.cmake b/cmake/Platform/Other/ArchitectureValidator.cmake deleted file mode 100644 index b8261a0..0000000 --- a/cmake/Platform/Other/ArchitectureValidator.cmake +++ /dev/null @@ -1,29 +0,0 @@ -#=============================================================================# -# Gets a filtered list of architectures that aren't compliant with the platform's architecture. -# e.g If a list contains 'avr' and 'nrf52', while our arch is 'avr', 'nrf52' will be returned. -# _arch_list - List of all architectures probably read from a library's properties file -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - Filtered list of architectures. -#=============================================================================# -function(get_unsupported_architectures _arch_list _return_var) - - cmake_parse_arguments(unsupported_archs "REGEX" "" "" ${ARGN}) - - list(FILTER _arch_list EXCLUDE REGEX - "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - if (unsupported_archs_REGEX) # Return in regex format - list(LENGTH _arch_list num_of_unsupported_archs) - set(unsupported_arch_list "") - set(arch_index 1) - foreach (unsupported_arch ${_arch_list}) - string(APPEND unsupported_arch_list "${unsupported_arch}") - if (${arch_index} LESS ${num_of_unsupported_archs}) - string(APPEND unsupported_arch_list "|") - endif () - increment_integer(arch_index 1) - endforeach () - endif () - - set(${_return_var} ${unsupported_arch_list} PARENT_SCOPE) - -endfunction() diff --git a/cmake/Platform/Other/SupportedArchitecturesRetriever.cmake b/cmake/Platform/Other/SupportedArchitecturesRetriever.cmake new file mode 100644 index 0000000..78e1bed --- /dev/null +++ b/cmake/Platform/Other/SupportedArchitecturesRetriever.cmake @@ -0,0 +1,40 @@ +#=============================================================================# +# Gets a filtered list of architectures that aren't compliant with the platform's architecture. +# e.g If a list contains 'avr' and 'nrf52', while our arch is 'avr', 'nrf52' will be returned. +# _arch_list - List of all architectures probably read from a library's properties file +# [REGEX] - Returns list in a regex-compatible mode, allowing caller to +# use result in search patterns. This is currently the only supported mode. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Filtered list of architectures. +#=============================================================================# +function(get_unsupported_architectures _arch_list _return_var) + + cmake_parse_arguments(parsed_args "REGEX" "" "" ${ARGN}) + + if ("${_arch_list}" MATCHES "\\*") # All architectures are supported, return nothing + return() + endif () + + list(FILTER _arch_list EXCLUDE REGEX ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}) + set(unsupported_arch_list ${_arch_list}) # Just for better readability + + if (parsed_args_REGEX) # Return in regex format + + foreach (arch ${unsupported_arch_list}) + # Append every unsupported-architecture and "|" to represent "or" in regex-fomart + string(APPEND unsupported_archs_regex "${arch}" "|") + endforeach () + + # Remove last "|" as it's unnecessary - There's no element after it + string(LENGTH ${unsupported_archs_regex} str_len) + decrement_integer(str_len 1) # Decrement string's length by 1 to trim last char ('|') + string(SUBSTRING ${unsupported_archs_regex} 0 ${str_len} unsupported_archs_regex) + + # prepare for generalized function return + set(unsupported_arch_list ${unsupported_archs_regex}) + + endif () + + set(${_return_var} ${unsupported_arch_list} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake index db9fb01..484aad4 100644 --- a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake @@ -10,21 +10,7 @@ #=============================================================================# function(_add_arduino_cmake_library _target_name _board_id _sources) - cmake_parse_arguments(parsed_args "INTERFACE" "ARCH" "" ${ARGN}) - - if (parsed_args_ARCH) # Treat architecture-specific libraries differently - # Filter any sources that aren't supported by the platform's architecture - list(LENGTH library_ARCH num_of_libs_archs) - if (${num_of_libs_archs} GREATER 1) - # Exclude all unsupported architectures, request filter in regex mode - get_unsupported_architectures("${parsed_args_ARCH}" arch_filter REGEX) - set(filter_type EXCLUDE) - else () - set(arch_filter "src\\/[^/]+\\.|${parsed_args_ARCH}") - set(filter_type INCLUDE) - endif () - list(FILTER _sources ${filter_type} REGEX ${arch_filter}) - endif () + cmake_parse_arguments(parsed_args "INTERFACE" "" "" ${ARGN}) if (parsed_args_INTERFACE) add_library(${_target_name} INTERFACE) @@ -40,11 +26,9 @@ function(_add_arduino_cmake_library _target_name _board_id _sources) set_library_flags(${_target_name} ${_board_id} ${scope}) - if (parsed_args_ARCH) - string(TOUPPER ${parsed_args_ARCH} upper_arch) - set(arch_definition "ARDUINO_ARCH_${upper_arch}") - target_compile_definitions(${_target_name} ${scope} ${arch_definition}) - endif () + string(TOUPPER ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} upper_arch) + set(arch_definition "ARDUINO_ARCH_${upper_arch}") + target_compile_definitions(${_target_name} ${scope} ${arch_definition}) endfunction() diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 4038f80..ab95362 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -4,11 +4,17 @@ # _target_name - Name of the library target to be created. Usually library's real name. # _board_id - Board ID associated with the linked Core Lib. # _sources - Source and header files to create target from. +# [LIB_PROPS_FILE] - Full path to the library's properties file. Optional. #=============================================================================# -function(add_arduino_library _target_name _board_id _sources) +function(add_arduino_library _target_name _board_id _library_root_dir _sources) + + resolve_library_sources_by_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources + "${ARGN}") + + _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}" "${ARGN}") + + find_dependent_platform_libraries("${arch_resolved_sources}" lib_platform_libs) - _add_arduino_cmake_library(${_target_name} ${_board_id} "${_sources}" "${ARGN}") - find_dependent_platform_libraries("${_sources}" lib_platform_libs) foreach (platform_lib ${lib_platform_libs}) link_platform_library(${_target_name} ${platform_lib} ${_board_id}) endforeach () diff --git a/examples/3rd-party-library/3rd_party.cpp b/examples/3rd-party-library/3rd_party.cpp new file mode 100644 index 0000000..d7af8bd --- /dev/null +++ b/examples/3rd-party-library/3rd_party.cpp @@ -0,0 +1,14 @@ +#include +#include "NeoPixelTest.hpp" +#include "GFXTest.h" + +void setup() +{ + testNeoPixel(); + testGFX(); +} + +void loop() +{ + +} \ No newline at end of file diff --git a/examples/3rd-party-library/CMakeLists.txt b/examples/3rd-party-library/CMakeLists.txt index 0b8939d..02489fa 100644 --- a/examples/3rd-party-library/CMakeLists.txt +++ b/examples/3rd-party-library/CMakeLists.txt @@ -3,17 +3,24 @@ cmake_minimum_required(VERSION 3.8.2) project(3rd_Party_Arduino_Library) get_board_id(board_id nano atmega328) -add_arduino_executable(3rd_Party_Arduino_Library ${board_id} NeoPixelTest.cpp GFXTest.cpp) +# First, declare and create our executable - It'll use 4 sources +add_arduino_executable(3rd_Party_Arduino_Library ${board_id} 3rd_party.cpp + NeoPixelTest.cpp GFXTest.cpp) target_include_directories(3rd_Party_Arduino_Library PRIVATE include) -add_arduino_library(Adafruit_NeoPixel ${board_id} libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp) +# Add the "NeoPixel" library manually using the library addition API +add_arduino_library(Adafruit_NeoPixel ${board_id} libraries/Adafruit_NeoPixel + libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp) target_include_directories(Adafruit_NeoPixel PUBLIC libraries/Adafruit_NeoPixel) -# 'Trick' the framework to use current directory as Sketchbook, allowing us to use the 'find' API +# Find the "GFX" library by 'tricking' the framework to use current directory as the Sketchbook path, +# allowing us to use the 'find' API set(ARDUINO_CMAKE_SKETCHBOOK_PATH "${CMAKE_CURRENT_LIST_DIR}") find_arduino_library(Adafruit_GFX Adafruit-GFX-Library ${board_id} 3RD_PARTY) -target_source_directories(Adafruit_GFX - DIRS libraries/Adafruit-GFX-Library/Fonts) +# We can also explicitly add additional directories to the target, +# as only root dir and 'src' sub-dir are added by default +target_source_directories(Adafruit_GFX DIRS libraries/Adafruit-GFX-Library/Fonts) +# Link all libraries to our previously created target link_arduino_library(3rd_Party_Arduino_Library Adafruit_NeoPixel ${board_id}) link_arduino_library(3rd_Party_Arduino_Library Adafruit_GFX ${board_id}) diff --git a/examples/3rd-party-library/GFXTest.cpp b/examples/3rd-party-library/GFXTest.cpp index f415bb1..099558e 100644 --- a/examples/3rd-party-library/GFXTest.cpp +++ b/examples/3rd-party-library/GFXTest.cpp @@ -2,7 +2,7 @@ static Adafruit_GFX_Button gfxButton; -void doSomething() +void testGFX() { gfxButton.isPressed(); } diff --git a/examples/3rd-party-library/NeoPixelTest.cpp b/examples/3rd-party-library/NeoPixelTest.cpp index 0938172..ffd7731 100644 --- a/examples/3rd-party-library/NeoPixelTest.cpp +++ b/examples/3rd-party-library/NeoPixelTest.cpp @@ -1,15 +1,8 @@ -#include -#include "GFXTest.h" +#include "include/NeoPixelTest.hpp" Adafruit_NeoPixel neoPixel; -void setup() +void testNeoPixel() { neoPixel.clear(); - doSomething(); -} - -void loop() -{ - } diff --git a/examples/3rd-party-library/include/GFXTest.h b/examples/3rd-party-library/include/GFXTest.h index a9b2fec..b54612b 100644 --- a/examples/3rd-party-library/include/GFXTest.h +++ b/examples/3rd-party-library/include/GFXTest.h @@ -3,6 +3,6 @@ #include -void doSomething(); +void testGFX(); #endif //EXAMPLES_GFXTEST_HPP diff --git a/examples/3rd-party-library/include/NeoPixelTest.hpp b/examples/3rd-party-library/include/NeoPixelTest.hpp new file mode 100644 index 0000000..8a2f1b9 --- /dev/null +++ b/examples/3rd-party-library/include/NeoPixelTest.hpp @@ -0,0 +1,8 @@ +#ifndef EXAMPLES_NEOPIXELTEST_HPP +#define EXAMPLES_NEOPIXELTEST_HPP + +#include + +void testNeoPixel(); + +#endif //EXAMPLES_NEOPIXELTEST_HPP From 53280f0d0e8a693541e2939618d6adf75959040d Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 20:01:45 +0300 Subject: [PATCH 03/17] Fixed bug in 'find_dependent_platform_libraries' function. It failed when there were no included headers found, so a matching guard-check has been added. --- cmake/Platform/Targets/PlatformLibraryTarget.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmake/Platform/Targets/PlatformLibraryTarget.cmake b/cmake/Platform/Targets/PlatformLibraryTarget.cmake index 3c01144..9584288 100644 --- a/cmake/Platform/Targets/PlatformLibraryTarget.cmake +++ b/cmake/Platform/Targets/PlatformLibraryTarget.cmake @@ -11,7 +11,10 @@ function(find_dependent_platform_libraries _sources _return_var) get_source_file_included_headers(${source} source_includes WE) list(APPEND included_headers_names ${source_includes}) endforeach () - list(REMOVE_DUPLICATES included_headers_names) + + if (included_headers_names) + list(REMOVE_DUPLICATES included_headers_names) + endif () get_platform_libraries_from_names("${included_headers_names}" dependent_libs) set(${_return_var} ${dependent_libs} PARENT_SCOPE) From e7d46d34a4bff30537b6ef39356aed2fa6ded13a Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 20:09:40 +0300 Subject: [PATCH 04/17] Refactored 'StringUtils' to be more readable. --- cmake/Platform/Utilities/StringUtils.cmake | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmake/Platform/Utilities/StringUtils.cmake b/cmake/Platform/Utilities/StringUtils.cmake index 08a9de2..3efa362 100644 --- a/cmake/Platform/Utilities/StringUtils.cmake +++ b/cmake/Platform/Utilities/StringUtils.cmake @@ -9,6 +9,7 @@ function(get_cmake_compliant_language_name _language _return_var) string(TOLOWER "${_language}" language) + if ("${language}" STREQUAL "s" OR "${language}" STREQUAL "asm") set(language ASM) elseif ("${language}" STREQUAL "cpp" OR "${language}" STREQUAL "cxx" OR @@ -35,6 +36,7 @@ endfunction() function(get_arduino_compliant_language_name _language _return_var) string(TOLOWER "${_language}" language) + if ("${language}" STREQUAL "s" OR "${language}" STREQUAL "asm") set(language S) # Intentionally upper-case elseif ("${language}" STREQUAL "cpp" OR "${language}" STREQUAL "cxx" OR @@ -60,7 +62,9 @@ endfunction() function(get_core_lib_target_name _board_id _return_var) string(REPLACE "." "_" board_id "${_board_id}") + set(core_lib_target_name "${board_id}_core_lib") + string(TOLOWER "${core_lib_target_name}" core_lib_target_name) set(${_return_var} ${core_lib_target_name} PARENT_SCOPE) @@ -76,12 +80,14 @@ endfunction() function(get_name_without_file_extension _input_string _return_var) string(REGEX MATCH "${ARDUINO_CMAKE_NAME_WE_REGEX_PATTERN}" match "${_input_string}") + set(${_return_var} ${CMAKE_MATCH_1} PARENT_SCOPE) endfunction() #=============================================================================# -# Converts a given string a PascalCase string, converting 1st letter to upper and remaining to lower. +# Converts a given string to a PascalCase string, converting 1st letter to upper +# and remaining to lower. # _input_string - String to convert. # _return_var - Name of a CMake variable that will hold the extraction result. # Returns - PascalCase converted string. From 26b398156096d0eab4ec35908cb74df120b3a8e1 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 21:03:26 +0300 Subject: [PATCH 05/17] Documented many new functions and renamed a few, all related to architecture. --- cmake/Platform/Arduino.cmake | 2 +- .../Libraries/LibraryArchitectureParser.cmake | 38 +++------ .../LibrarySourcesArchitectureResolver.cmake | 85 +++++++++++++------ ...r.cmake => ArchitectureSupportQuery.cmake} | 24 ++++++ .../Targets/ArduinoLibraryTarget.cmake | 2 +- 5 files changed, 97 insertions(+), 54 deletions(-) rename cmake/Platform/Other/{SupportedArchitecturesRetriever.cmake => ArchitectureSupportQuery.cmake} (64%) diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index 190346b..fa2ab74 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -21,7 +21,7 @@ include(TargetFlagsManager) include(SourcesManager) include(SketchManager) include(DefaultsManager) -include(SupportedArchitecturesRetriever) +include(ArchitectureSupportQuery) include(Libraries) diff --git a/cmake/Platform/Libraries/LibraryArchitectureParser.cmake b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake index 3f6963a..b16a138 100644 --- a/cmake/Platform/Libraries/LibraryArchitectureParser.cmake +++ b/cmake/Platform/Libraries/LibraryArchitectureParser.cmake @@ -1,38 +1,22 @@ +#=============================================================================# +# Gets a list of architectures supported by a library. +# The list is read from the given properties file, which includes metadata about the library. +# _library_properties_file - Full path to a library's properties file +# (usually named 'library,propertie'). +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of architectures supported by the library or '*' +# if it's architecture-agnostic (Supports all arhcitectures). +#=============================================================================# function(get_arduino_library_supported_architectures _library_properties_file _return_var) file(STRINGS ${_library_properties_file} library_properties) list(FILTER library_properties INCLUDE REGEX "arch") + _get_property_value("${library_properties}" _library_arch_list) + string(REPLACE "," ";" _library_arch_list ${_library_arch_list}) # Turn into a valid list set(${_return_var} ${_library_arch_list} PARENT_SCOPE) endfunction() - -#=============================================================================# -# Gets the library architecure if any, read from the given library properties file -# which includes this information. -# _library_arch_list - List of architectures supported by the library, -# inferred from its' 'library.properties' file. -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - If library is architecure agnostic (Supports all), nothing is returned. -# If library doesn't support the current platform's architecture, -# "UNSUPPORTED" string is returned. -# Otherwise, the platform's architecture is returned. -#=============================================================================# -function(is_library_supports_platform_architecture _library_arch_list _return_var) - - if ("${_library_arch_list}" MATCHES "\\*") # Any architecture is supported - set(result TRUE) - else () - if (${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} IN_LIST _library_arch_list) - set(result TRUE) # Our platform's arch is supported - else () - set(result FALSE) # Our arch isn't supported - endif () - endif () - - set(${_return_var} ${result} PARENT_SCOPE) - -endfunction() diff --git a/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake index e7983a4..17f8539 100644 --- a/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake +++ b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake @@ -1,6 +1,41 @@ -function(_filter_unsupported_arch_sources _unsupported_archs_regex _sources _return_var) +#=============================================================================# +# Attempts to retrieve a library's properties file based on its' root directory. +# If couldn't be found, CMake generates a warning and returns an empty string. +# _library_root_directory - Path to library's root directory. Can be relative. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Full path to library's properties file. +#=============================================================================# +function(_get_library_properties_file _library_root_directory _return_var) + + # Get the absolute root directory (full path) + get_filename_component(absolute_lib_root_dir ${_library_root_directory} ABSOLUTE) + + if (EXISTS ${absolute_lib_root_dir}/library.properties) + set(lib_props_file ${absolute_lib_root_dir}/library.properties) + else () # Properties file can't be found + + # Warn user and assume library is arch-agnostic + get_filename_component(library_name ${absolute_lib_root_dir} NAME) + message(WARNING "\"${library_name}\" library's properties file can't be found " + "under its' root directory - Assuming the library " + "is architecture-agnostic (supports all architectures)") + set(lib_props_file "") + + endif () + + set(${_return_var} ${lib_props_file} PARENT_SCOPE) - #string(LENGTH _unsupported_archs_regex num_of_unsupported_archs) +endfunction() + +#=============================================================================# +# Filters sources that relate to an architecture from the given list of unsupported architectures. +# _unsupported_archs_regex - List of unsupported architectures as a regex-pattern string. +# _sources - List of sources to check and potentially filter. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Filtered list of sources containing only those that don't relate to +# any unsupported architecture. +#=============================================================================# +function(_filter_unsupported_arch_sources _unsupported_archs_regex _sources _return_var) if (NOT "${_unsupported_archs_regex}" STREQUAL "") # Not all architectures are supported # Filter sources dependant on unsupported architectures @@ -11,42 +46,42 @@ function(_filter_unsupported_arch_sources _unsupported_archs_regex _sources _ret endfunction() -function(resolve_library_sources_by_architecture _library_root_dir _library_sources _return_var) +#=============================================================================# +# Resolves library's architecture-related elements by doing several things: +# 1. Checking whether the platform's architecture is supported by the library +# 2. Filtering out any library sources that relate to unsupported architectures, i.e +# architectures other than the platform's. +# If the platform's architecture isn't supported by the library, CMake generates an error and stops. +# _library_root_dir - Path to library's root directory. Can be relative. +# _library_sources - List of library's sources to check and potentially filter. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Filtered list of sources containing only those that don't relate to +# any unsupported architecture. +#=============================================================================# +function(resolve_library_architecture _library_root_dir _library_sources _return_var) cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) if (parsed_args_LIB_PROPS_FILE) # Library properties file is given set(lib_props_file ${parsed_args_LIB_PROPS_FILE}) - else () # Try to automatically find file from sources + else () + # Try to automatically find file from sources + _get_library_properties_file(${_library_root_dir} lib_props_file) - # Get the absolute root directory (full path) - get_filename_component(absolute_lib_root_dir ${_library_root_dir} ABSOLUTE) - - if (EXISTS ${absolute_lib_root_dir}/library.properties) - set(lib_props_file ${absolute_lib_root_dir}/library.properties) - - else () # Properties file can't be found - Warn user and assume library is arch-agnostic - - get_filename_component(library_name ${absolute_lib_root_dir} NAME) - message(WARNING "\"${library_name}\" library's properties file can't be found " - "under its' root directory - Assuming the library " - "is architecture-agnostic (supports all architectures)") - set(${_return_var} ${_library_sources} PARENT_SCOPE) + if ("${lib_props_file}" STREQUAL "") # Properties file couldn't be found + set(${_return_var} "${_library_sources}" PARENT_SCOPE) return() - endif () - endif () get_arduino_library_supported_architectures("${lib_props_file}" lib_archs) - is_library_supports_platform_architecture(${lib_archs} arch_supported_by_lib) + + # Check if the platform's architecture is supported by the library + is_platform_architecture_supported(${lib_archs} arch_supported_by_lib) if (NOT ${arch_supported_by_lib}) - string(CONCAT error_message - "The ${_library_name} " - "library isn't supported on the platform's architecture " - "${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}") - message(SEND_ERROR ${error_message}) + message(SEND_ERROR "The platform's architecture, ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}, " + "isn't supported by the ${_library_name} library") endif () get_unsupported_architectures("${lib_archs}" unsupported_archs REGEX) diff --git a/cmake/Platform/Other/SupportedArchitecturesRetriever.cmake b/cmake/Platform/Other/ArchitectureSupportQuery.cmake similarity index 64% rename from cmake/Platform/Other/SupportedArchitecturesRetriever.cmake rename to cmake/Platform/Other/ArchitectureSupportQuery.cmake index 78e1bed..7897730 100644 --- a/cmake/Platform/Other/SupportedArchitecturesRetriever.cmake +++ b/cmake/Platform/Other/ArchitectureSupportQuery.cmake @@ -1,3 +1,27 @@ +#=============================================================================# +# Checks whether the platform's architecture is supported in the context of the given list +# of architectures, i.e If the list contains the platform's architecture. +# _arch_list - List of architectures supported by the library, +# inferred from its' 'library.properties' file. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - True if supported, false otherwise. +#=============================================================================# +function(is_platform_architecture_supported _arch_list _return_var) + + if ("${_arch_list}" MATCHES "\\*") # Any architecture is supported + set(result TRUE) + else () + if (${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} IN_LIST _arch_list) + set(result TRUE) # Our platform's arch is supported + else () + set(result FALSE) # Our arch isn't supported + endif () + endif () + + set(${_return_var} ${result} PARENT_SCOPE) + +endfunction() + #=============================================================================# # Gets a filtered list of architectures that aren't compliant with the platform's architecture. # e.g If a list contains 'avr' and 'nrf52', while our arch is 'avr', 'nrf52' will be returned. diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index ab95362..582f2b1 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -8,7 +8,7 @@ #=============================================================================# function(add_arduino_library _target_name _board_id _library_root_dir _sources) - resolve_library_sources_by_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources + resolve_library_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources "${ARGN}") _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}" "${ARGN}") From 3f2d66b30f5b39fb0064936dee725f66f86f67c3 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Tue, 25 Sep 2018 21:51:14 +0300 Subject: [PATCH 06/17] Removed code related to header-only-libraries, it belongs to another branch. --- .../Platform/Libraries/LibrariesFinder.cmake | 29 +++++++------------ .../Targets/ArduinoLibraryTarget.cmake | 27 +++-------------- 2 files changed, 15 insertions(+), 41 deletions(-) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index f3efae3..7c4a3b9 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -7,11 +7,10 @@ # _library_name - Name of the Arduino library to find. # _board_id - Board ID associated with the linked Core Lib. # [3RD_PARTY] - Whether library should be treated as a 3rd Party library. -# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files #=============================================================================# function(find_arduino_library _target_name _library_name _board_id) - set(argument_options "3RD_PARTY" "HEADER_ONLY") + set(argument_options "3RD_PARTY") cmake_parse_arguments(parsed_args "${argument_options}" "" "" ${ARGN}) if (NOT parsed_args_3RD_PARTY) @@ -34,23 +33,17 @@ function(find_arduino_library _target_name _library_name _board_id) set(error_message "Couldn't find any header files for the ${_library_name} library") message(SEND_ERROR "${error_message}") else () - if (parsed_args_HEADER_ONLY) - add_arduino_header_only_library(${_target_name} ${_board_id} - ARCH ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} - HEADERS ${library_headers}) + find_library_source_files("${library_path}" library_sources) + if (NOT library_sources) + string(CONCAT error_message + "Couldn't find any source files for the ${_library_name} library - " + "Is it a header-only library?\n" + "If so, please pass the HEADER_ONLY option as an argument to the function") + message(SEND_ERROR "${error_message}") else () - find_library_source_files("${library_path}" library_sources) - if (NOT library_sources) - string(CONCAT error_message - "Couldn't find any source files for the ${_library_name} library - " - "Is it a header-only library?\n" - "If so, please pass the HEADER_ONLY option as an argument to the function") - message(SEND_ERROR "${error_message}") - else () - set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" - LIB_PROPS_FILE ${library_properties_file}) - endif () + set(sources ${library_headers} ${library_sources}) + add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" + LIB_PROPS_FILE ${library_properties_file}) endif () endif () endif () diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 582f2b1..1f00259 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -8,8 +8,7 @@ #=============================================================================# function(add_arduino_library _target_name _board_id _library_root_dir _sources) - resolve_library_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources - "${ARGN}") + resolve_library_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources "${ARGN}") _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}" "${ARGN}") @@ -21,27 +20,15 @@ function(add_arduino_library _target_name _board_id _library_root_dir _sources) endfunction() -function(add_arduino_header_only_library _target_name _board_id) - - cmake_parse_arguments(parsed_args "ARCH" "" "HEADERS" ${ARGN}) - - _add_arduino_cmake_library(${_target_name} ${_board_id} "${parsed_args_HEADERS}" - INTERFACE ${parsed_args_ARCH}) - -endfunction() - #=============================================================================# # Links the given library target to the given "executable" target, but first, # it adds core lib's include directories to the libraries include directories. # _target_name - Name of the "executable" target. # _library_target_name - Name of the library target. # _board_id - Board ID associated with the linked Core Lib. -# [HEADER_ONLY] - Whether library is a header-only library, i.e has no source files #=============================================================================# function(link_arduino_library _target_name _library_target_name _board_id) - cmake_parse_arguments(parsed_args "HEADER_ONLY" "" "" ${ARGN}) - get_core_lib_target_name(${_board_id} core_lib_target) if (NOT TARGET ${_target_name}) @@ -52,14 +39,8 @@ function(link_arduino_library _target_name _library_target_name _board_id) message(FATAL_ERROR "Core Library target doesn't exist. This is bad and should be reported") endif () - if (parsed_args_HEADER_ONLY) - _link_arduino_cmake_library(${_target_name} ${_library_target_name} - INTERFACE - BOARD_CORE_TARGET ${core_lib_target}) - else () - _link_arduino_cmake_library(${_target_name} ${_library_target_name} - PUBLIC - BOARD_CORE_TARGET ${core_lib_target}) - endif () + _link_arduino_cmake_library(${_target_name} ${_library_target_name} + PUBLIC + BOARD_CORE_TARGET ${core_lib_target}) endfunction() From 641cc9712faad12ef87dc366c85d80ea0a594a33 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 00:27:04 +0300 Subject: [PATCH 07/17] Added-back header-only related code to `find_library` API. Also modified `add_arduino_header_only_library` API to accept unlimited source files (headers) as unparsed arguments, as is done with the 'Executable' API. --- .../Platform/Libraries/LibrariesFinder.cmake | 34 ++++++++++++------- .../Targets/ArduinoLibraryTarget.cmake | 10 ++++-- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index 7c4a3b9..ac077cb 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -10,7 +10,7 @@ #=============================================================================# function(find_arduino_library _target_name _library_name _board_id) - set(argument_options "3RD_PARTY") + set(argument_options "3RD_PARTY" "HEADER_ONLY") cmake_parse_arguments(parsed_args "${argument_options}" "" "" ${ARGN}) if (NOT parsed_args_3RD_PARTY) @@ -26,24 +26,32 @@ function(find_arduino_library _target_name _library_name _board_id) if (${library_properties_file} MATCHES "NOTFOUND") message(SEND_ERROR "Couldn't find library named ${_library_name}") else () # Library is found + get_filename_component(library_path ${library_properties_file} DIRECTORY) find_library_header_files("${library_path}" library_headers) + if (NOT library_headers) - set(error_message "Couldn't find any header files for the ${_library_name} library") - message(SEND_ERROR "${error_message}") + message(SEND_ERROR "Couldn't find any header files for the ${_library_name} library") else () - find_library_source_files("${library_path}" library_sources) - if (NOT library_sources) - string(CONCAT error_message - "Couldn't find any source files for the ${_library_name} library - " - "Is it a header-only library?\n" - "If so, please pass the HEADER_ONLY option as an argument to the function") - message(SEND_ERROR "${error_message}") + + if (parsed_args_HEADER_ONLY) + add_arduino_header_only_library(${_target_name} ${_board_id} ${library_headers}) else () - set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" - LIB_PROPS_FILE ${library_properties_file}) + find_library_source_files("${library_path}" library_sources) + + if (NOT library_sources) + message(SEND_ERROR "Couldn't find any source files for the " + "${_library_name} library - Is it a header-only library?" + "If so, please pass the HEADER_ONLY option " + "as an argument to the function") + else () + set(sources ${library_headers} ${library_sources}) + + add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" + LIB_PROPS_FILE ${library_properties_file}) + endif () + endif () endif () endif () diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 284b540..1720ebb 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -20,12 +20,16 @@ function(add_arduino_library _target_name _board_id _library_root_dir _sources) endfunction() +#=============================================================================# +# Creates a header-only library target for the given name and sources. +# _target_name - Name of the "executable" target. +# _board_id - Board ID associated with the linked Core Lib. +#=============================================================================# function(add_arduino_header_only_library _target_name _board_id) - cmake_parse_arguments(parsed_args "ARCH" "" "HEADERS" ${ARGN}) + set(headers ${ARGN}) - _add_arduino_cmake_library(${_target_name} ${_board_id} "${parsed_args_HEADERS}" - INTERFACE ${parsed_args_ARCH}) + _add_arduino_cmake_library(${_target_name} ${_board_id} "${headers}" INTERFACE) endfunction() From 08f8702fa07a15997a14043c2133b658174bf187 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 00:52:31 +0300 Subject: [PATCH 08/17] Refactored the 'ArduinoCMakeLibraryTarget' module. Separated target architecture definition to a function under the 'TargetFlagsManager' module. --- cmake/Platform/Other/TargetFlagsManager.cmake | 15 +++++++++++ .../Targets/ArduinoCMakeLibraryTarget.cmake | 25 ++++++++++++------- .../Targets/ArduinoLibraryTarget.cmake | 12 ++++----- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/cmake/Platform/Other/TargetFlagsManager.cmake b/cmake/Platform/Other/TargetFlagsManager.cmake index 0181d8e..946e0b1 100644 --- a/cmake/Platform/Other/TargetFlagsManager.cmake +++ b/cmake/Platform/Other/TargetFlagsManager.cmake @@ -81,3 +81,18 @@ function(set_upload_target_flags _target_name _board_id _upload_port _return_var endfunction() +#=============================================================================# +# Adds a compiler definition (#define) for the given architecture to the target. +# The affecting scope of the definition is controlled by the _scope argument. +# _target - Name of the target (Executable) to set flags on. +# _scope - PUBLIC|INTERFACE|PRIVATE. Affects outer scope - How other targets see it. +# _architecture - Architecture to define, e.g. 'avr' +#=============================================================================# +function(set_target_architecture_definition _target _scope _architecture) + + string(TOUPPER ${_architecture} upper_arch) + set(arch_definition "ARDUINO_ARCH_${upper_arch}") + + target_compile_definitions(${_target} ${_scope} ${arch_definition}) + +endfunction() diff --git a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake index 484aad4..bbed822 100644 --- a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake @@ -26,9 +26,8 @@ function(_add_arduino_cmake_library _target_name _board_id _sources) set_library_flags(${_target_name} ${_board_id} ${scope}) - string(TOUPPER ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE} upper_arch) - set(arch_definition "ARDUINO_ARCH_${upper_arch}") - target_compile_definitions(${_target_name} ${scope} ${arch_definition}) + set_target_architecture_definition(${_target_name} ${scope} + ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}) endfunction() @@ -38,7 +37,7 @@ endfunction() # then links it to the library. # _target_name - Name of the target to link against. # _library_name - Name of the library target to link. -# [PRIVATE|PUBLIC|INTERFACE] - Optional link scope. +# [PRIVATE|PUBLIC|INTERFACE] - Optional link scope for the internally linked Core-Lib. # [BOARD_CORE_TARGET] - Optional target name of the Core Lib to use. # Use when the target is a library. #=============================================================================# @@ -52,15 +51,15 @@ function(_link_arduino_cmake_library _target_name _library_name) cmake_parse_arguments(link_library "${scope_options}" "BOARD_CORE_TARGET" "" ${ARGN}) # Now, link library to executable - if (link_library_PUBLIC) - set(scope PUBLIC) + if (link_library_PRIVATE) + set(scope PRIVATE) elseif (link_library_INTERFACE) set(scope INTERFACE) else () - set(scope PRIVATE) + set(scope PUBLIC) endif () - # First, include core lib's directories in library as well + # Resolve Core-Lib's target if (link_library_BOARD_CORE_TARGET) set(core_target ${link_library_BOARD_CORE_TARGET}) else () @@ -68,9 +67,17 @@ function(_link_arduino_cmake_library _target_name _library_name) endif () get_target_property(core_lib_includes ${core_target} INCLUDE_DIRECTORIES) + + # Include core lib's include directories in library target, then link to it target_include_directories(${_library_name} ${scope} "${core_lib_includes}") target_link_libraries(${_library_name} ${scope} ${core_target}) - target_link_libraries(${_target_name} PRIVATE ${_library_name}) + # Link library target to linked-to target + if (link_library_PRIVATE) + target_link_libraries(${_target_name} PRIVATE ${_library_name}) + else () + # Link 'INTERFACE' targets publicly, otherwise code won't compile + target_link_libraries(${_target_name} PUBLIC ${_library_name}) + endif () endfunction() diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 1720ebb..9b4f20b 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -56,13 +56,13 @@ function(link_arduino_library _target_name _library_target_name _board_id) endif () if (parsed_args_HEADER_ONLY) - _link_arduino_cmake_library(${_target_name} ${_library_target_name} - INTERFACE - BOARD_CORE_TARGET ${core_lib_target}) + set(scope INTERFACE) else () - _link_arduino_cmake_library(${_target_name} ${_library_target_name} - PUBLIC - BOARD_CORE_TARGET ${core_lib_target}) + set(scope PUBLIC) endif () + _link_arduino_cmake_library(${_target_name} ${_library_target_name} + ${scope} + BOARD_CORE_TARGET ${core_lib_target}) + endfunction() From ee41ec70d2d862c55c2cc973b7410aca0247269c Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 01:28:28 +0300 Subject: [PATCH 09/17] Added utility function to parse scope arguments of a function. Updated code across all framework to use this function. --- cmake/Platform/Arduino.cmake | 1 + cmake/Platform/Other/TargetFlagsManager.cmake | 31 ++++++++-------- .../Targets/ArduinoCMakeLibraryTarget.cmake | 19 +++------- .../Targets/PlatformLibraryTarget.cmake | 14 +++++--- .../Utilities/CMakeArgumentsUtils.cmake | 35 +++++++++++++++++++ 5 files changed, 67 insertions(+), 33 deletions(-) create mode 100644 cmake/Platform/Utilities/CMakeArgumentsUtils.cmake diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index fa2ab74..699c31e 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -14,6 +14,7 @@ include(ListUtils) include(StringUtils) include(PropertyUtils) include(PlatformLibraryUtils) +include(CMakeArgumentsUtils) include(BoardManager) include(RecipeParser) diff --git a/cmake/Platform/Other/TargetFlagsManager.cmake b/cmake/Platform/Other/TargetFlagsManager.cmake index 946e0b1..06422f7 100644 --- a/cmake/Platform/Other/TargetFlagsManager.cmake +++ b/cmake/Platform/Other/TargetFlagsManager.cmake @@ -5,25 +5,24 @@ #=============================================================================# function(set_compiler_target_flags _target_name _board_id) - set(option_args PRIVATE PUBLIC INTERFACE) - set(single_args LANGUAGE) - cmake_parse_arguments(compiler "${option_args}" "${single_args}" "" ${ARGN}) - - if (compiler_LANGUAGE) - if (compiler_PRIVATE) - set(scope PRIVATE) - elseif (compiler_INTERFACE) - set(scope INTERFACE) - else () - set(scope PUBLIC) - endif () + cmake_parse_arguments(parsed_args "" "LANGUAGE" "" ${ARGN}) + parse_scope_argument("${ARGN}" scope + DEFAULT_SCOPE PUBLIC) + + if (parsed_args_LANGUAGE) + parse_compiler_recipe_flags("${_board_id}" compiler_recipe_flags - LANGUAGE "${compiler_LANGUAGE}") + LANGUAGE "${parsed_args_LANGUAGE}") + target_compile_options(${_target_name} ${scope} - $<$:${compiler_recipe_flags}>) + $<$:${compiler_recipe_flags}>) + else () + parse_compiler_recipe_flags("${_board_id}" compiler_recipe_flags) - target_compile_options(${_target_name} PUBLIC ${compiler_recipe_flags}) + + target_compile_options(${_target_name} ${scope} ${compiler_recipe_flags}) + endif () endfunction() @@ -36,7 +35,9 @@ endfunction() function(set_linker_flags _target_name _board_id) parse_linker_recpie_pattern("${_board_id}" linker_recipe_flags) + string(REPLACE ";" " " cmake_compliant_linker_flags "${linker_recipe_flags}") + set(CMAKE_EXE_LINKER_FLAGS "${cmake_compliant_linker_flags}" CACHE STRING "" FORCE) endfunction() diff --git a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake index bbed822..46b154b 100644 --- a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake @@ -47,21 +47,12 @@ function(_link_arduino_cmake_library _target_name _library_name) message(FATAL_ERROR "Target doesn't exist - It must be created first!") endif () - set(scope_options "PRIVATE" "PUBLIC" "INTERFACE") - cmake_parse_arguments(link_library "${scope_options}" "BOARD_CORE_TARGET" "" ${ARGN}) - - # Now, link library to executable - if (link_library_PRIVATE) - set(scope PRIVATE) - elseif (link_library_INTERFACE) - set(scope INTERFACE) - else () - set(scope PUBLIC) - endif () + cmake_parse_arguments(parsed_args "" "BOARD_CORE_TARGET" "" ${ARGN}) + parse_scope_argument("${ARGN}" scope) # Resolve Core-Lib's target - if (link_library_BOARD_CORE_TARGET) - set(core_target ${link_library_BOARD_CORE_TARGET}) + if (parsed_args_BOARD_CORE_TARGET) + set(core_target ${parsed_args_BOARD_CORE_TARGET}) else () set(core_target ${${_target_name}_CORE_LIB_TARGET}) endif () @@ -73,7 +64,7 @@ function(_link_arduino_cmake_library _target_name _library_name) target_link_libraries(${_library_name} ${scope} ${core_target}) # Link library target to linked-to target - if (link_library_PRIVATE) + if (parsed_args_PRIVATE) target_link_libraries(${_target_name} PRIVATE ${_library_name}) else () # Link 'INTERFACE' targets publicly, otherwise code won't compile diff --git a/cmake/Platform/Targets/PlatformLibraryTarget.cmake b/cmake/Platform/Targets/PlatformLibraryTarget.cmake index 9584288..a141201 100644 --- a/cmake/Platform/Targets/PlatformLibraryTarget.cmake +++ b/cmake/Platform/Targets/PlatformLibraryTarget.cmake @@ -17,6 +17,7 @@ function(find_dependent_platform_libraries _sources _return_var) endif () get_platform_libraries_from_names("${included_headers_names}" dependent_libs) + set(${_return_var} ${dependent_libs} PARENT_SCOPE) endfunction() @@ -32,8 +33,7 @@ function(_add_platform_library _library_name _board_id) find_source_files("${ARDUINO_CMAKE_PLATFORM_LIBRARIES_PATH}/${_library_name}/src" lib_source_files) set(lib_sources ${lib_headers} ${lib_source_files}) - _add_arduino_cmake_library(${_library_name} ${_board_id} "${lib_sources}" - ARCH ${ARDUINO_CMAKE_PLATFORM_ARCHITECTURE}) + _add_arduino_cmake_library(${_library_name} ${_board_id} "${lib_sources}") endfunction() @@ -49,14 +49,20 @@ function(link_platform_library _target_name _library_name _board_id) message(FATAL_ERROR "Target ${_target_name} doesn't exist - It must be created first!") endif () + parse_scope_argument("${ARGN}" scope + DEFAULT_SCOPE PUBLIC) + if (NOT TARGET ${_library_name}) + _add_platform_library(${_library_name} ${_board_id}) + get_core_lib_target_name(${_board_id} core_lib_target) _link_arduino_cmake_library(${_target_name} ${_library_name} - PUBLIC + ${scope} BOARD_CORE_TARGET ${core_lib_target}) + else () - target_link_libraries(${_target_name} PUBLIC ${_library_name}) + target_link_libraries(${_target_name} ${scope} ${_library_name}) endif () endfunction() diff --git a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake new file mode 100644 index 0000000..b9d45ab --- /dev/null +++ b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake @@ -0,0 +1,35 @@ +#=============================================================================# +# Parses the given arguments for scope-controlling arguments, which are PUBLIC|INTERFACE|PRIVATE. +# If none is found, CMake generates an error, unless - The DEFAULT_SCOPE argument is passed. +# _cmake_args - Arguments to parse. Usually function's unparsed arguments - ${ARGN}. +# [DEFAULT_SCOPE] - Optional default scope to return in case none is found in arguments. +# _return_var - Name of a CMake variable that will hold the extraction result. +# Returns - Parsed scope if one is found or the DEFAULT_SCOPE if set, otherwise an error. +#=============================================================================# +function(parse_scope_argument _cmake_args _return_var) + + cmake_parse_arguments(parsed_args "" "DEFAULT_SCOPE" "" ${ARGN}) + + set(scope_options "PRIVATE" "PUBLIC" "INTERFACE") + cmake_parse_arguments(scope_args "${scope_options}" "" "" ${_cmake_args}) + + # Now, link library to executable + if (scope_args_PRIVATE) + set(scope PRIVATE) + elseif (scope_args_INTERFACE) + set(scope INTERFACE) + elseif (scope_args_PUBLIC) + set(scope PUBLIC) + else () + + if (parsed_args_DEFAULT_SCOPE) # Use default scope if none are given + set(scope ${parsed_args_DEFAULT_SCOPE}) + else () + message(SEND_ERROR "Can't parse scope arguments - None are given, or invalid!") + endif () + + endif () + + set(${_return_var} ${scope} PARENT_SCOPE) + +endfunction() From 83ca4656850ead9b2aa6c8496d27821cb92caf43 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 03:16:42 +0300 Subject: [PATCH 10/17] Added utility function to parse sources from cmake's unparsed arguments. It allows functions to accept practically unlimited sources (or any other arbitrary value) as long as any reserved arguments/options aren't encountered. When at least one is encountered, processing stops and the already-parsed arguments are returned. --- .../Utilities/CMakeArgumentsUtils.cmake | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake index b9d45ab..8204e84 100644 --- a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake +++ b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake @@ -1,3 +1,47 @@ +#=============================================================================# +# Parses the given arguments for sources, stopping when all arguments have been read or +# when at least one reserved argument/option has been encountered. +# _reserved_options - Reserved option arguments. +# _reserved_single_values - Reserved single-value arguments. +# _reserved_multi_values - Reserved multi-value arguments. +# _cmake_args - Arguments to parse. Usually function's unparsed arguments - ${ARGN}. +# _return_var - Name of a CMake variable that will hold the extraction result. +# Returns - Parsed sources. +#=============================================================================# +function(parse_sources_arguments _return_var _reserved_options _reserved_single_values + _reserved_multi_values _cmake_args) + + # Prepare arguments for further inspection - Somewhat croocked logic due to CMake's limitations + # If an argument is an empty string, populate it with a theoritcally impossible value. + # just to have some value in it + if ("${_reserved_options}" STREQUAL "") + set(_reserved_options "+-*/") + endif () + if ("${_reserved_single_values}" STREQUAL "") + set(_reserved_single_values "+-*/") + endif () + if ("${_reserved_multi_values}" STREQUAL "") + set(_reserved_multi_values "+-*/") + endif () + + set(sources "") # Clear list because cmake preserves scope in nested functions + + foreach (arg ${_cmake_args}) + + if (${arg} IN_LIST _reserved_options OR + ${arg} IN_LIST _reserved_single_values OR + ${arg} IN_LIST _reserved_multi_values) + break() + else () + list(APPEND sources ${arg}) + endif () + + endforeach () + + set(${_return_var} ${sources} PARENT_SCOPE) + +endfunction() + #=============================================================================# # Parses the given arguments for scope-controlling arguments, which are PUBLIC|INTERFACE|PRIVATE. # If none is found, CMake generates an error, unless - The DEFAULT_SCOPE argument is passed. From 1ddf71f92afb0754d387de2d54cfa3b14a343f19 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 13:21:39 +0300 Subject: [PATCH 11/17] Updated `add_arduino_library` function to use new argument parsing utility. --- cmake/Platform/Targets/ArduinoLibraryTarget.cmake | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 9b4f20b..aefa2f9 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -6,11 +6,21 @@ # _sources - Source and header files to create target from. # [LIB_PROPS_FILE] - Full path to the library's properties file. Optional. #=============================================================================# -function(add_arduino_library _target_name _board_id _library_root_dir _sources) +function(add_arduino_library _target_name _board_id) + + cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) + parse_sources_arguments(parsed_sources "" "LIB_PROPS_FILE" "" "${ARGN}") resolve_library_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources "${ARGN}") - _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}" "${ARGN}") + if (parsed_args_LIB_PROPS_FILE) + resolve_library_architecture(${library_root_dir} "${parsed_sources}" arch_resolved_sources + LIB_PROPS_FILE ${parsed_args_LIB_PROPS_FILE}) + else () + resolve_library_architecture(${library_root_dir} "${parsed_sources}" arch_resolved_sources) + endif () + + _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}") find_dependent_platform_libraries("${arch_resolved_sources}" lib_platform_libs) From 0720c5d78e0a16b49a40666ba82eec5a8d90330b Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 13:32:55 +0300 Subject: [PATCH 12/17] Added utility function to get common root directory of sources. The `add_arduino_library` function has been updated to use this function in order to remove the inconvenient 'library_root_dir' argument it used to accept. As such, updated all other code using this API. --- cmake/Platform/Arduino.cmake | 7 +-- .../Platform/Libraries/LibrariesFinder.cmake | 2 +- .../Targets/ArduinoLibraryTarget.cmake | 2 +- cmake/Platform/Utilities/PathUtils.cmake | 52 +++++++++++++++++++ cmake/Platform/Utilities/Utilities.cmake | 7 +++ examples/3rd-party-library/CMakeLists.txt | 3 +- 6 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 cmake/Platform/Utilities/PathUtils.cmake create mode 100644 cmake/Platform/Utilities/Utilities.cmake diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index 699c31e..abe748b 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -9,12 +9,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Sources) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Libraries) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/Targets) -include(MathUtils) -include(ListUtils) -include(StringUtils) -include(PropertyUtils) -include(PlatformLibraryUtils) -include(CMakeArgumentsUtils) +include(Utilities) include(BoardManager) include(RecipeParser) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index ac077cb..99a984d 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -48,7 +48,7 @@ function(find_arduino_library _target_name _library_name _board_id) else () set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} ${library_path} "${sources}" + add_arduino_library(${_target_name} ${_board_id} ${sources} LIB_PROPS_FILE ${library_properties_file}) endif () diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index aefa2f9..fdb30ca 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -11,7 +11,7 @@ function(add_arduino_library _target_name _board_id) cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) parse_sources_arguments(parsed_sources "" "LIB_PROPS_FILE" "" "${ARGN}") - resolve_library_architecture(${_library_root_dir} "${_sources}" arch_resolved_sources "${ARGN}") + get_sources_root_directory("${parsed_sources}" library_root_dir) if (parsed_args_LIB_PROPS_FILE) resolve_library_architecture(${library_root_dir} "${parsed_sources}" arch_resolved_sources diff --git a/cmake/Platform/Utilities/PathUtils.cmake b/cmake/Platform/Utilities/PathUtils.cmake new file mode 100644 index 0000000..d3c370f --- /dev/null +++ b/cmake/Platform/Utilities/PathUtils.cmake @@ -0,0 +1,52 @@ +#=============================================================================# +# Finds the shallowest path among the given sources, where shallowest is the path having +# the least nesting level, i.e. The least number of '/' separators in its' path. +# _sources - List of sources paths to find shallowest path from. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Shallowest path among given sources (Lowest nesting level). +#=============================================================================# +function(get_shallowest_directory_structure_path _sources _return_var) + + set(min_nesting_level 9999) + + foreach (source ${_sources}) + + string(REGEX MATCHALL "/" nesting_regex_match ${source}) + + list(LENGTH nesting_regex_match source_nesting_level) + + if (${source_nesting_level} LESS ${min_nesting_level}) + set(min_nested_path ${source}) + set(min_nesting_level ${source_nesting_level}) + endif () + + endforeach () + + set(${_return_var} ${min_nested_path} PARENT_SCOPE) + +endfunction() + +#=============================================================================# +# Gets the path of the common root directory of all given sources. +# It is expected that indeed all sources will have the same, common root directory. +# E.g. src/foo.h and src/utility/bar.h both have 'src' in common. +# However, if src/foo.c is a relative path under the C:\ drive (in Windows), and src/bar.c is +# a relative path under the D:\ drive - This is invalid and the function will misbehave. +# _sources - List of sources that have a common root directory which needs to be found. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Path to the common root directory of the given list of sources. +#=============================================================================# +function(get_sources_root_directory _sources _return_var) + + get_shallowest_directory_structure_path("${_sources}" shallowest_path) + + get_filename_component(root_dir ${shallowest_path} DIRECTORY) + + if ("${root_dir}" MATCHES ".+src$") # 'src' directory has been retrieved as shallowest path + # The actual root directory is one level above 'src' + get_filename_component(root_dir ${root_dir} DIRECTORY) + endif () + + set(${_return_var} ${root_dir} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Utilities/Utilities.cmake b/cmake/Platform/Utilities/Utilities.cmake new file mode 100644 index 0000000..1be96cc --- /dev/null +++ b/cmake/Platform/Utilities/Utilities.cmake @@ -0,0 +1,7 @@ +include(MathUtils) +include(ListUtils) +include(StringUtils) +include(PathUtils) +include(PropertyUtils) +include(PlatformLibraryUtils) +include(CMakeArgumentsUtils) diff --git a/examples/3rd-party-library/CMakeLists.txt b/examples/3rd-party-library/CMakeLists.txt index 02489fa..d02982d 100644 --- a/examples/3rd-party-library/CMakeLists.txt +++ b/examples/3rd-party-library/CMakeLists.txt @@ -9,8 +9,7 @@ add_arduino_executable(3rd_Party_Arduino_Library ${board_id} 3rd_party.cpp target_include_directories(3rd_Party_Arduino_Library PRIVATE include) # Add the "NeoPixel" library manually using the library addition API -add_arduino_library(Adafruit_NeoPixel ${board_id} libraries/Adafruit_NeoPixel - libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp) +add_arduino_library(Adafruit_NeoPixel ${board_id} libraries/Adafruit_NeoPixel/Adafruit_NeoPixel.cpp) target_include_directories(Adafruit_NeoPixel PUBLIC libraries/Adafruit_NeoPixel) # Find the "GFX" library by 'tricking' the framework to use current directory as the Sketchbook path, From e9f7edb84455b43e0f24c73746157a629c366a91 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 14:55:16 +0300 Subject: [PATCH 13/17] Removed the need for the 'library_root_dir' argument in all functions. Everything related to the library properties file is now passed using the 'LIB_PROPS_FILE' optional argument. Also added utility function to get a library's properties file based on a given root directory - It simply appends the properties file name (which is now stored as a default cache variable) and checks for existence before returning. --- .../LibrarySourcesArchitectureResolver.cmake | 46 ++++--------------- cmake/Platform/System/DefaultsManager.cmake | 8 ++++ .../Targets/ArduinoLibraryTarget.cmake | 20 ++++++-- cmake/Platform/Utilities/LibraryUtils.cmake | 29 ++++++++++++ cmake/Platform/Utilities/Utilities.cmake | 1 + 5 files changed, 62 insertions(+), 42 deletions(-) create mode 100644 cmake/Platform/Utilities/LibraryUtils.cmake diff --git a/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake index 17f8539..335a1b9 100644 --- a/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake +++ b/cmake/Platform/Libraries/LibrarySourcesArchitectureResolver.cmake @@ -1,32 +1,3 @@ -#=============================================================================# -# Attempts to retrieve a library's properties file based on its' root directory. -# If couldn't be found, CMake generates a warning and returns an empty string. -# _library_root_directory - Path to library's root directory. Can be relative. -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - Full path to library's properties file. -#=============================================================================# -function(_get_library_properties_file _library_root_directory _return_var) - - # Get the absolute root directory (full path) - get_filename_component(absolute_lib_root_dir ${_library_root_directory} ABSOLUTE) - - if (EXISTS ${absolute_lib_root_dir}/library.properties) - set(lib_props_file ${absolute_lib_root_dir}/library.properties) - else () # Properties file can't be found - - # Warn user and assume library is arch-agnostic - get_filename_component(library_name ${absolute_lib_root_dir} NAME) - message(WARNING "\"${library_name}\" library's properties file can't be found " - "under its' root directory - Assuming the library " - "is architecture-agnostic (supports all architectures)") - set(lib_props_file "") - - endif () - - set(${_return_var} ${lib_props_file} PARENT_SCOPE) - -endfunction() - #=============================================================================# # Filters sources that relate to an architecture from the given list of unsupported architectures. # _unsupported_archs_regex - List of unsupported architectures as a regex-pattern string. @@ -52,26 +23,27 @@ endfunction() # 2. Filtering out any library sources that relate to unsupported architectures, i.e # architectures other than the platform's. # If the platform's architecture isn't supported by the library, CMake generates an error and stops. -# _library_root_dir - Path to library's root directory. Can be relative. # _library_sources - List of library's sources to check and potentially filter. +# [LIB_PROPS_FILE] - Full path to the library's properties file. Optional. # _return_var - Name of variable in parent-scope holding the return value. # Returns - Filtered list of sources containing only those that don't relate to # any unsupported architecture. #=============================================================================# -function(resolve_library_architecture _library_root_dir _library_sources _return_var) +function(resolve_library_architecture _library_sources _return_var) cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) if (parsed_args_LIB_PROPS_FILE) # Library properties file is given set(lib_props_file ${parsed_args_LIB_PROPS_FILE}) else () - # Try to automatically find file from sources - _get_library_properties_file(${_library_root_dir} lib_props_file) - if ("${lib_props_file}" STREQUAL "") # Properties file couldn't be found - set(${_return_var} "${_library_sources}" PARENT_SCOPE) - return() - endif () + # Warn user and assume library is arch-agnostic + message(STATUS "Library's properties file can't be found " + "under its' root directory - Assuming the library " + "is architecture-agnostic (supports all architectures)") + set(${_return_var} "${_library_sources}" PARENT_SCOPE) + return() + endif () get_arduino_library_supported_architectures("${lib_props_file}" lib_archs) diff --git a/cmake/Platform/System/DefaultsManager.cmake b/cmake/Platform/System/DefaultsManager.cmake index 27de7fc..8200144 100644 --- a/cmake/Platform/System/DefaultsManager.cmake +++ b/cmake/Platform/System/DefaultsManager.cmake @@ -57,6 +57,13 @@ function(set_default_arduino_cmake_options) endfunction() +function(set_default_paths) + + set(ARDUINO_CMAKE_LIBRARY_PROPERTIES_FILE_NAME "library.properties" CACHE STRING + "Name of the libraries' properties file") + +endfunction() + #=============================================================================# # Sets various defaults used throughout the platform. #=============================================================================# @@ -65,5 +72,6 @@ function(set_arduino_cmake_defaults) set_internal_search_patterns() set_source_files_patterns() set_default_arduino_cmake_options() + set_default_paths() endfunction() diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index fdb30ca..1640da2 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -3,21 +3,31 @@ # As it's an Arduino library, it also finds and links all dependent platform libraries (if any). # _target_name - Name of the library target to be created. Usually library's real name. # _board_id - Board ID associated with the linked Core Lib. -# _sources - Source and header files to create target from. # [LIB_PROPS_FILE] - Full path to the library's properties file. Optional. +# [Sources] - List of source files (Could also be headers for code-inspection in some IDEs) +# to create the executable from, similar to CMake's built-in add_executable. #=============================================================================# function(add_arduino_library _target_name _board_id) cmake_parse_arguments(parsed_args "" "LIB_PROPS_FILE" "" ${ARGN}) parse_sources_arguments(parsed_sources "" "LIB_PROPS_FILE" "" "${ARGN}") - get_sources_root_directory("${parsed_sources}" library_root_dir) - if (parsed_args_LIB_PROPS_FILE) - resolve_library_architecture(${library_root_dir} "${parsed_sources}" arch_resolved_sources + resolve_library_architecture("${parsed_sources}" arch_resolved_sources LIB_PROPS_FILE ${parsed_args_LIB_PROPS_FILE}) else () - resolve_library_architecture(${library_root_dir} "${parsed_sources}" arch_resolved_sources) + + get_sources_root_directory("${parsed_sources}" library_root_dir) + + get_library_properties_file(${library_root_dir} library_properties_file) + + if (library_properties_file) # Properties file has been found + resolve_library_architecture("${parsed_sources}" arch_resolved_sources + LIB_PROPS_FILE ${library_properties_file}) + else () + resolve_library_architecture("${parsed_sources}" arch_resolved_sources) + endif () + endif () _add_arduino_cmake_library(${_target_name} ${_board_id} "${arch_resolved_sources}") diff --git a/cmake/Platform/Utilities/LibraryUtils.cmake b/cmake/Platform/Utilities/LibraryUtils.cmake new file mode 100644 index 0000000..4b6b9c6 --- /dev/null +++ b/cmake/Platform/Utilities/LibraryUtils.cmake @@ -0,0 +1,29 @@ +#=============================================================================# +# Gets the full path to a library's properties file based on the given library root directory. +# If the root ditectory doesn't exist, CMake generates an error and stops. +# If the properties file doesn't exist under the given root directory, +# CMake generates a warning and returns an empty string. +# _library_root_directory - Path to library's root directory. Can be relative. +# _return_var - Name of a CMake variable that will hold the extraction result. +# Returns - Full path to library's properties file if found, otherwise nothing. +#=============================================================================# +function(get_library_properties_file _library_root_directory _return_var) + + get_filename_component(absolute_lib_root_dir ${_library_root_directory} ABSOLUTE) + + if (NOT EXISTS ${absolute_lib_root_dir}) + message(SEND_ERROR "Can't get library's properties file - Root directory doesn't exist.\n" + "Root directory: ${absolute_lib_root_dir}") + endif () + + set(lib_props_file ${absolute_lib_root_dir}/${ARDUINO_CMAKE_LIBRARY_PROPERTIES_FILE_NAME}) + + if (NOT EXISTS ${lib_props_file}) + message(WARNING "Library's properties file doesn't exist under the given root directory.\n" + "Root directory: ${absolute_lib_root_dir}") + return() + endif () + + set(${_return_var} ${lib_props_file} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Utilities/Utilities.cmake b/cmake/Platform/Utilities/Utilities.cmake index 1be96cc..92a56c4 100644 --- a/cmake/Platform/Utilities/Utilities.cmake +++ b/cmake/Platform/Utilities/Utilities.cmake @@ -3,5 +3,6 @@ include(ListUtils) include(StringUtils) include(PathUtils) include(PropertyUtils) +include(LibraryUtils) include(PlatformLibraryUtils) include(CMakeArgumentsUtils) From 5c2d905658e1f545fadb01e9416b900a0d6c32aa Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 14:56:32 +0300 Subject: [PATCH 14/17] Updated header-only library API to use new argument parsing function as well. --- cmake/Platform/Targets/ArduinoLibraryTarget.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake index 1640da2..8115ad8 100644 --- a/cmake/Platform/Targets/ArduinoLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoLibraryTarget.cmake @@ -47,9 +47,9 @@ endfunction() #=============================================================================# function(add_arduino_header_only_library _target_name _board_id) - set(headers ${ARGN}) + parse_sources_arguments(parsed_headers "" "" "" "${ARGN}") - _add_arduino_cmake_library(${_target_name} ${_board_id} "${headers}" INTERFACE) + _add_arduino_cmake_library(${_target_name} ${_board_id} "${parsed_headers}" INTERFACE) endfunction() From 4e719249cbf8a499f15265d0bdb81089c66854fb Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 15:01:17 +0300 Subject: [PATCH 15/17] Reordered scope argument-parsing function's arguments - Return var is now 1st. Updated usages accordingly. Also updated the `link_library_flags` function to use the scope parsing function, it was somehow missed until now. --- cmake/Platform/Libraries/LibraryFlagsManager.cmake | 12 ++---------- cmake/Platform/Other/TargetFlagsManager.cmake | 2 +- .../Platform/Targets/ArduinoCMakeLibraryTarget.cmake | 2 +- cmake/Platform/Targets/PlatformLibraryTarget.cmake | 2 +- cmake/Platform/Utilities/CMakeArgumentsUtils.cmake | 2 +- 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/cmake/Platform/Libraries/LibraryFlagsManager.cmake b/cmake/Platform/Libraries/LibraryFlagsManager.cmake index 3d4d312..a7d2964 100644 --- a/cmake/Platform/Libraries/LibraryFlagsManager.cmake +++ b/cmake/Platform/Libraries/LibraryFlagsManager.cmake @@ -6,16 +6,8 @@ #=============================================================================# function(set_library_flags _library_target _board_id) - set(scope_options "PRIVATE" "PUBLIC" "INTERFACE") - cmake_parse_arguments(parsed_args "${scope_options}" "" "" ${ARGN}) - - if (parsed_args_PRIVATE) - set(scope PRIVATE) - elseif (parsed_args_INTERFACE) - set(scope INTERFACE) - else () - set(scope PUBLIC) - endif () + parse_scope_argument(scope "${ARGN}" + DEFAULT_SCOPE PUBLIC) # Set C++ compiler flags get_cmake_compliant_language_name(cpp flags_language) diff --git a/cmake/Platform/Other/TargetFlagsManager.cmake b/cmake/Platform/Other/TargetFlagsManager.cmake index 06422f7..90577bc 100644 --- a/cmake/Platform/Other/TargetFlagsManager.cmake +++ b/cmake/Platform/Other/TargetFlagsManager.cmake @@ -6,7 +6,7 @@ function(set_compiler_target_flags _target_name _board_id) cmake_parse_arguments(parsed_args "" "LANGUAGE" "" ${ARGN}) - parse_scope_argument("${ARGN}" scope + parse_scope_argument(scope "${ARGN}" DEFAULT_SCOPE PUBLIC) if (parsed_args_LANGUAGE) diff --git a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake index 46b154b..3f9a8b6 100644 --- a/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake +++ b/cmake/Platform/Targets/ArduinoCMakeLibraryTarget.cmake @@ -48,7 +48,7 @@ function(_link_arduino_cmake_library _target_name _library_name) endif () cmake_parse_arguments(parsed_args "" "BOARD_CORE_TARGET" "" ${ARGN}) - parse_scope_argument("${ARGN}" scope) + parse_scope_argument(scope "${ARGN}") # Resolve Core-Lib's target if (parsed_args_BOARD_CORE_TARGET) diff --git a/cmake/Platform/Targets/PlatformLibraryTarget.cmake b/cmake/Platform/Targets/PlatformLibraryTarget.cmake index a141201..d14d702 100644 --- a/cmake/Platform/Targets/PlatformLibraryTarget.cmake +++ b/cmake/Platform/Targets/PlatformLibraryTarget.cmake @@ -49,7 +49,7 @@ function(link_platform_library _target_name _library_name _board_id) message(FATAL_ERROR "Target ${_target_name} doesn't exist - It must be created first!") endif () - parse_scope_argument("${ARGN}" scope + parse_scope_argument(scope "${ARGN}" DEFAULT_SCOPE PUBLIC) if (NOT TARGET ${_library_name}) diff --git a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake index 8204e84..4fd627e 100644 --- a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake +++ b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake @@ -50,7 +50,7 @@ endfunction() # _return_var - Name of a CMake variable that will hold the extraction result. # Returns - Parsed scope if one is found or the DEFAULT_SCOPE if set, otherwise an error. #=============================================================================# -function(parse_scope_argument _cmake_args _return_var) +function(parse_scope_argument _return_var _cmake_args) cmake_parse_arguments(parsed_args "" "DEFAULT_SCOPE" "" ${ARGN}) From 4e3a9e3d85d1c6d4286e58cfb04f7ee13faa4388 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 15:47:18 +0300 Subject: [PATCH 16/17] Separated crooked list-initialization technique from source argument parsing to utility macro. --- .../Utilities/CMakeArgumentsUtils.cmake | 16 ++++------------ cmake/Platform/Utilities/ListUtils.cmake | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake index 4fd627e..def8f40 100644 --- a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake +++ b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake @@ -11,18 +11,10 @@ function(parse_sources_arguments _return_var _reserved_options _reserved_single_values _reserved_multi_values _cmake_args) - # Prepare arguments for further inspection - Somewhat croocked logic due to CMake's limitations - # If an argument is an empty string, populate it with a theoritcally impossible value. - # just to have some value in it - if ("${_reserved_options}" STREQUAL "") - set(_reserved_options "+-*/") - endif () - if ("${_reserved_single_values}" STREQUAL "") - set(_reserved_single_values "+-*/") - endif () - if ("${_reserved_multi_values}" STREQUAL "") - set(_reserved_multi_values "+-*/") - endif () + # Initialize argument-lists for further inspection + initialize_list(_reserved_options) + initialize_list(_reserved_single_values) + initialize_list(_reserved_multi_values) set(sources "") # Clear list because cmake preserves scope in nested functions diff --git a/cmake/Platform/Utilities/ListUtils.cmake b/cmake/Platform/Utilities/ListUtils.cmake index 75359dc..987cb67 100644 --- a/cmake/Platform/Utilities/ListUtils.cmake +++ b/cmake/Platform/Utilities/ListUtils.cmake @@ -5,6 +5,23 @@ # Must not be negative or greater than 'list_length'-1. #=============================================================================# macro(list_replace _list _index _new_element) + list(REMOVE_AT ${_list} ${_index}) + list(INSERT ${_list} ${_index} "${_new_element}") + +endmacro() + +#=============================================================================# +# Checks whether the given list is empty. If it is - Initializes it with a theoretically +# impossible to reproduce value, so if it's used to check whether an item is in it +# the answer will be false. +# _list - List to initialize. +#=============================================================================# +macro(initialize_list _list) + + if ("${${_list}}" STREQUAL "") + set(${_list} "+-*/") + endif () + endmacro() From f4ab16ca7369a07b586a9348693f0e05ecb081c7 Mon Sep 17 00:00:00 2001 From: Timor Gruber Date: Wed, 26 Sep 2018 16:09:47 +0300 Subject: [PATCH 17/17] Added utility-helper function to consume reserved arguments before parsing "unlimited" arguments, such as sources. --- .../Platform/Libraries/LibrariesFinder.cmake | 7 ++- .../Utilities/CMakeArgumentsUtils.cmake | 51 +++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index 99a984d..5b648b2 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -37,7 +37,9 @@ function(find_arduino_library _target_name _library_name _board_id) if (parsed_args_HEADER_ONLY) add_arduino_header_only_library(${_target_name} ${_board_id} ${library_headers}) + else () + find_library_source_files("${library_path}" library_sources) if (NOT library_sources) @@ -48,8 +50,9 @@ function(find_arduino_library _target_name _library_name _board_id) else () set(sources ${library_headers} ${library_sources}) - add_arduino_library(${_target_name} ${_board_id} ${sources} - LIB_PROPS_FILE ${library_properties_file}) + add_arduino_library(${_target_name} ${_board_id} + LIB_PROPS_FILE ${library_properties_file} + ${sources}) endif () endif () diff --git a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake index def8f40..47ec858 100644 --- a/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake +++ b/cmake/Platform/Utilities/CMakeArgumentsUtils.cmake @@ -1,3 +1,44 @@ +#=============================================================================# +# Consumes given arguments for reserved options and single-value options, +# returning a new argument-list without them, no matter where they are positioned. +# It allows easy parsing of arguments which are considered "unlimited", +# limited only by multi-value options. +# _args - Arguments to consume. Usually function's unparsed arguments - ${ARGN}. +# _reserved_options - Reserved option arguments. +# _reserved_single_values - Reserved single-value arguments. +# _return_var - Name of a CMake variable that will hold the extraction result. +# Returns - Original argument-list without reserved options and single-value options. +#=============================================================================# +function(_consume_reserved_arguments _args _reserved_options _reserved_single_values _return_var) + + set(temp_arg_list ${_args}) + + list(LENGTH _args args_length) + decrement_integer(args_length 1) # We'll peform index iteration - It's always length-1 + + foreach (index RANGE ${args_length}) + + list(GET _args ${index} arg) + + if (${arg} IN_LIST _reserved_options) + list(REMOVE_ITEM temp_arg_list ${arg}) + + elseif (${arg} IN_LIST _reserved_single_values) + + # Get the next index to remove as well - It's the option's/key's value + set(next_index ${index}) + increment_integer(next_index 1) + + list(REMOVE_AT temp_arg_list ${index} ${next_index}) + + endif () + + endforeach () + + set(${_return_var} ${temp_arg_list} PARENT_SCOPE) + +endfunction() + #=============================================================================# # Parses the given arguments for sources, stopping when all arguments have been read or # when at least one reserved argument/option has been encountered. @@ -16,13 +57,15 @@ function(parse_sources_arguments _return_var _reserved_options _reserved_single_ initialize_list(_reserved_single_values) initialize_list(_reserved_multi_values) + _consume_reserved_arguments("${_cmake_args}" + "${_reserved_options}" "${_reserved_single_values}" + consumed_args) + set(sources "") # Clear list because cmake preserves scope in nested functions - foreach (arg ${_cmake_args}) + foreach (arg ${consumed_args}) - if (${arg} IN_LIST _reserved_options OR - ${arg} IN_LIST _reserved_single_values OR - ${arg} IN_LIST _reserved_multi_values) + if (${arg} IN_LIST _reserved_multi_values) break() else () list(APPEND sources ${arg})