diff --git a/.gitignore b/.gitignore index d4efd4a..d7246b7 100644 --- a/.gitignore +++ b/.gitignore @@ -116,6 +116,93 @@ crashlytics.properties crashlytics-build.properties fabric.properties +# Created by https://www.gitignore.io/api/clion +# Edit at https://www.gitignore.io/?templates=clion + +### CLion ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# JetBrains templates +**___jb_tmp___ + +### CLion Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +# End of https://www.gitignore.io/api/clion + ### VisualStudioCode ### .vscode/* !.vscode/tasks.json @@ -129,8 +216,9 @@ fabric.properties ### User-Defined [Aa]ssets/* -/examples/blink-example/Blink.cpp -/examples/servo-knob-example/Knob.cpp -/examples/sketch/sketch1.cpp -/examples/sketch/sketch2.cpp -/docs/wiki +[Bb]uild/ +examples/blink-example/Blink.cpp +examples/servo-knob-example/Knob.cpp +examples/sketch/sketch1.cpp +examples/sketch/sketch2.cpp +docs/wiki diff --git a/cmake/Platform/Arduino.cmake b/cmake/Platform/Arduino.cmake index 5980fc1..63ac4af 100644 --- a/cmake/Platform/Arduino.cmake +++ b/cmake/Platform/Arduino.cmake @@ -17,8 +17,10 @@ include(Boards) include(RecipeParser) include(TargetFlagsManager) -include(SourcesManager) -include(SketchManager) + +include(Sources) +include(Sketches) + include(DefaultsManager) include(ArchitectureSupportQuery) include(CMakeProperties) diff --git a/cmake/Platform/Libraries/LibrariesFinder.cmake b/cmake/Platform/Libraries/LibrariesFinder.cmake index b9bf4a1..e6bb515 100644 --- a/cmake/Platform/Libraries/LibrariesFinder.cmake +++ b/cmake/Platform/Libraries/LibrariesFinder.cmake @@ -1,4 +1,4 @@ -macro(_cleanup_find_arduino_library) +macro(_clear_find_library_state) unset(library_path CACHE) @@ -18,6 +18,8 @@ endmacro() #=============================================================================# function(find_arduino_library _target_name _library_name) + _clear_find_library_state() + set(argument_options "3RD_PARTY" "HEADER_ONLY" "QUIET") cmake_parse_arguments(parsed_args "${argument_options}" "" "" ${ARGN}) @@ -28,7 +30,7 @@ function(find_arduino_library _target_name _library_name) endif () find_file(library_path - NAMES ${_library_name} + NAMES "${_library_name}" PATHS ${ARDUINO_CMAKE_PLATFORM_LIBRARIES_PATH} ${ARDUINO_SDK_LIBRARIES_PATH} ${ARDUINO_CMAKE_SKETCHBOOK_PATH} ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR} PATH_SUFFIXES libraries dependencies @@ -43,7 +45,7 @@ function(find_arduino_library _target_name _library_name) if (NOT library_headers) if (parsed_args_QUIET) - _cleanup_find_arduino_library() + _clear_find_library_state() return() else () message(SEND_ERROR "Couldn't find any header files for the " @@ -57,7 +59,7 @@ function(find_arduino_library _target_name _library_name) if (NOT library_sources) if (parsed_args_QUIET) - _cleanup_find_arduino_library() + _clear_find_library_state() return() else () message(SEND_ERROR "Couldn't find any source files for the " @@ -78,6 +80,6 @@ function(find_arduino_library _target_name _library_name) endif () endif () - _cleanup_find_arduino_library() + _clear_find_library_state() endfunction() diff --git a/cmake/Platform/Other/ArduinoSDKSeeker.cmake b/cmake/Platform/Other/ArduinoSDKSeeker.cmake index be65f7a..7ca6cd5 100644 --- a/cmake/Platform/Other/ArduinoSDKSeeker.cmake +++ b/cmake/Platform/Other/ArduinoSDKSeeker.cmake @@ -57,15 +57,16 @@ function(find_arduino_sdk_bin _return_var) set(${_return_var} "${ARDUINO_SDK_PATH}/hardware/tools/avr/bin" PARENT_SCOPE) else () # Some systems like the Arch Linux arduino package install binaries to /usr/bin - find_program(avr_gcc_location avr-gcc) - if ("${avr_gcc_location}" MATCHES "NOTFOUND") + # But gcc can be found first in ccache folder so let's search ar instead + find_program(avr_gcc_ar_location avr-gcc-ar) + if ("${avr_gcc_ar_location}" MATCHES "NOTFOUND") string(CONCAT error_message "Couldn't find Arduino bin path - Is it in a non-standard location?" "\n" "If so, please set the ARDUINO_SDK_BIN_PATH CMake-Variable") message(FATAL_ERROR ${error_message}) else () - get_filename_component(avr_gcc_parent ${avr_gcc_location} DIRECTORY) - set(${_return_var} "${avr_gcc_parent}" PARENT_SCOPE) + get_filename_component(avr_gcc_ar_parent ${avr_gcc_ar_location} DIRECTORY) + set(${_return_var} "${avr_gcc_ar_parent}" PARENT_SCOPE) endif () endif () diff --git a/cmake/Platform/Sketches/SketchHeadersManager.cmake b/cmake/Platform/Sketches/SketchHeadersManager.cmake deleted file mode 100644 index e98025a..0000000 --- a/cmake/Platform/Sketches/SketchHeadersManager.cmake +++ /dev/null @@ -1,47 +0,0 @@ -#=============================================================================# -# Resolves the header files included in a sketch by linking their appropriate library if necessary -# or by validating they're included by the sketch target. -# _target_name - Name of the target to add the sketch file to. -# _sketch_file - Path to a sketch file to add to the target. -#=============================================================================# -function(resolve_sketch_headers _target_name _sketch_file) - - get_source_file_included_headers("${_sketch_file}" sketch_headers) - - foreach (header ${sketch_headers}) - - # Header name without extension (such as '.h') can represent an Arduino/Platform library - # So first we should check whether it's a library - get_name_without_file_extension("${header}" header_we) - - is_platform_library(${header_we} is_header_platform_lib) - - if (is_header_platform_lib) - - string(TOLOWER ${header_we} header_we_lower) - - link_platform_library(${_target_name} ${header_we_lower}) - - else () - - # Pass the '3RD_PARTY' option to avoid name-conversion - find_arduino_library(${header_we}_sketch_lib ${header_we} 3RD_PARTY QUIET) - - # If library isn't found, display a status since it might be a user library - if (NOT TARGET ${header_we}_sketch_lib OR - "${${header_we}_sketch_lib}" MATCHES "NOTFOUND") - - message(STATUS "The header '${header_we}' is used by the '${_sketch_file}' sketch, " - "but it isn't part of an Arduino nor a Platform library.\n\t" - "However, it may be part of a user library but " - "you'd have to check this manually!") - - else () - link_arduino_library(${_target_name} ${header_we}_sketch_lib) - endif () - - endif () - - endforeach () - -endfunction() diff --git a/cmake/Platform/Sketches/SketchHeadersResolver.cmake b/cmake/Platform/Sketches/SketchHeadersResolver.cmake new file mode 100644 index 0000000..4edddf4 --- /dev/null +++ b/cmake/Platform/Sketches/SketchHeadersResolver.cmake @@ -0,0 +1,23 @@ +#=============================================================================# +# Resolves all headers used by a given sketch file by searching its 'include lines', recursively. +# _target_name - Name of the sketch's target created earlier. +# _sketch_file - Path to the sketch file which its' headers should be resolved. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of all unique header files used by the sketch file, recursively. +#=============================================================================# +function(resolve_sketch_headers _target_name _sketch_file _return_var) + + get_target_include_directories(${_target_name} target_include_dirs) + + get_source_headers("${_sketch_file}" "${target_include_dirs}" sketch_headers RECURSIVE) + get_source_headers("${ARDUINO_CMAKE_PLATFORM_HEADER_PATH}" "${target_include_dirs}" platform_headers RECURSIVE) + + list(APPEND sketch_headers ${platform_headers}) + + if (sketch_headers) + list(REMOVE_DUPLICATES sketch_headers) + endif () + + set(${_return_var} ${sketch_headers} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Sketches/SketchLibrariesResolver.cmake b/cmake/Platform/Sketches/SketchLibrariesResolver.cmake new file mode 100644 index 0000000..e8f7131 --- /dev/null +++ b/cmake/Platform/Sketches/SketchLibrariesResolver.cmake @@ -0,0 +1,24 @@ +#=============================================================================# +# Resolves the libraries used by a sketch file. It's possible that not all libraries will be resolved, +# as the current algorithm relies on the name of the included headers to match a library name. +# _target_name - Name of the sketch's target created earlier. +# _sketch_file - Path to the sketch file which its' libraries should be resolved. +# _sketch_headers - List of headers files used by the sketch, directly or indirectly. +#=============================================================================# +function(resolve_sketch_libraries _target_name _sketch_file _sketch_headers) + + foreach (header ${_sketch_headers}) + + # Header name without extension (such as '.h') can represent an Arduino/Platform library + get_name_without_file_extension("${header}" header_we) + + # Pass the '3RD_PARTY' option to avoid name-conversion + find_arduino_library(${header_we}_sketch_lib ${header_we} 3RD_PARTY QUIET) + + if (TARGET ${header_we}_sketch_lib) + link_arduino_library(${_target_name} ${header_we}_sketch_lib) + endif () + + endforeach () + +endfunction() diff --git a/cmake/Platform/Sketches/SketchManager.cmake b/cmake/Platform/Sketches/SketchManager.cmake index c92e7bf..67c63df 100644 --- a/cmake/Platform/Sketches/SketchManager.cmake +++ b/cmake/Platform/Sketches/SketchManager.cmake @@ -1,6 +1,3 @@ -include(SketchSourceConverter) -include(SketchHeadersManager) - #=============================================================================# # Returns a desired path for sources converted from sketches. # It can't be resolved just by a cache variable since sketches may belong each to a different project, @@ -31,12 +28,13 @@ function(add_sketch_to_target _target_name _sketch_file) _get_converted_source_desired_path(${_sketch_file} sketch_converted_source_path) # Only perform conversion if policy is set or if sketch hasn't been converted yet - if (CONVERT_SKETCHES_IF_CONVERTED_SOURCES_EXISTS OR - NOT EXISTS ${sketch_converted_source_path}) + if (CONVERT_SKETCHES_IF_CONVERTED_SOURCES_EXISTS OR NOT EXISTS ${sketch_converted_source_path}) - resolve_sketch_headers(${_target_name} ${_sketch_file}) + resolve_sketch_headers(${_target_name} ${_sketch_file} sketch_headers) + resolve_sketch_libraries(${_target_name} ${_sketch_file} "${sketch_headers}") + resolve_sketch_prototypes(${_sketch_file} "${sketch_headers}" sketch_prototypes) - convert_sketch_to_source(${_sketch_file} ${sketch_converted_source_path}) + convert_sketch_to_source(${_sketch_file} ${sketch_converted_source_path} "${sketch_prototypes}") endif () diff --git a/cmake/Platform/Sketches/SketchPrototypesResolver.cmake b/cmake/Platform/Sketches/SketchPrototypesResolver.cmake new file mode 100644 index 0000000..62784de --- /dev/null +++ b/cmake/Platform/Sketches/SketchPrototypesResolver.cmake @@ -0,0 +1,32 @@ +#=============================================================================# +# Resolves the given sketch file's prototypes, which are just function declarations, +# by matching all function definitions with their declaration. If a declaration can't be found, +# the definition is added to a list of prototypes to generate, which is then returned. +# _sketch_file - Path to the sketch file which its' libraries should be resolved. +# _sketch_headers - List of headers files used by the sketch, directly or indirectly. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of prototypes to generate, i.e. function definitions without a matching declaration. +#=============================================================================# +function(resolve_sketch_prototypes _sketch_file _sketch_headers _return_var) + + get_source_function_definitions(${_sketch_file} sketch_func_defines) + if (NOT sketch_func_defines) # Source has no function definitions at all + return() + endif () + + # Add the current file to the list of headers to search in as well - It's the functions' containing file + list(APPEND _sketch_headers "${_sketch_file}") + + foreach (func_def ${sketch_func_defines}) + + match_function_declaration("${func_def}" "${_sketch_headers}" match) + + if (${match} MATCHES "NOTFOUND") + list(APPEND prototypes "${func_def}") + endif () + + endforeach () + + set(${_return_var} ${prototypes} PARENT_SCOPE) + +endfunction() \ No newline at end of file diff --git a/cmake/Platform/Sketches/SketchSourceConverter.cmake b/cmake/Platform/Sketches/SketchSourceConverter.cmake index a6da3f1..e63067b 100644 --- a/cmake/Platform/Sketches/SketchSourceConverter.cmake +++ b/cmake/Platform/Sketches/SketchSourceConverter.cmake @@ -1,144 +1,162 @@ #=============================================================================# # Writes the given lines of code belonging to the sketch to the given file path. -# _sketch_loc - List of lines-of-code belonging to the sketch. +# _sketch_lines - List of lines-of-code belonging to the sketch. # _file_path - Full path to the written source file. #=============================================================================# -function(_write_source_file _sketch_loc _file_path) +function(_write_source_file _sketch_lines _file_path) file(WRITE "${_file_path}" "") # Clear previous file's contents - foreach (loc ${_sketch_loc}) - - string(REGEX REPLACE "^(.+)${ARDUINO_CMAKE_SEMICOLON_REPLACEMENT}(.*)$" "\\1;\\2" - original_loc "${loc}") - - file(APPEND "${_file_path}" "${original_loc}") - + foreach (line ${_sketch_lines}) + escape_semicolon_in_string("${line}" original_line REVERSE) + file(APPEND "${_file_path}" "${original_line}") endforeach () endfunction() -#=============================================================================# -# Finds the best line to insert an '#include' of the platform's main header to. -# The function assumes that the initial state of the given 'active index' is set to the line that -# best fitted the insertion, however, it might need a bit more optimization. Why? -# Because above those lines there might be a comment, or a comment block, -# all of which should be taken into account in order to minimize the effect on code's readability. -# _sketch_loc - List of lines-of-code belonging to the sketch. -# _active_index - Index that indicates the best-not-optimized loc to insert header to. -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - Best fitted index to insert platform's main header '#include' to. -#=============================================================================# -function(_get_matching_header_insertion_index _sketch_loc _active_index _return_var) +macro(_setup_regex_patterns) - if (${_active_index} EQUAL 0) # First line in a file will always result in the 1st index - set(${_return_var} 0 PARENT_SCOPE) - return() - else () - decrement_integer(_active_index 1) - endif () + get_property(function_declaration_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DECLARATION_REGEX_PATTERN) + get_property(function_definition_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DEFINITION_REGEX_PATTERN) + get_property(preprocessor_regex GLOBAL PROPERTY ARDUINO_CMAKE_PREPROCESSOR_REGEX_PATTERN) - list(GET _sketch_loc ${_active_index} previous_loc) + string(CONCAT function_prototype_regex + "${function_declaration_regex}" + "|${function_definition_regex}") + string(CONCAT code_pattern + "${preprocessor_regex}" + "|${function_prototype_regex}") - if ("${previous_loc}" MATCHES "^//") # Simple one-line comment - set(matching_index ${_active_index}) + set(comment_line_pattern "\\/\\/") + set(comment_block_start_pattern "\\/\\*") + set(comment_block_end_pattern "\\*\\/") - elseif ("${previous_loc}" MATCHES "\\*/$") # End of multi-line comment +endmacro() - foreach (index RANGE ${_active_index} 0) +macro(_insert_platform_header _current_line _line_index) - list(GET _sketch_loc ${index} multi_comment_line) - - if ("${multi_comment_line}" MATCHES "^\\/\\*") # Start of multi-line comment - set(matching_index ${index}) - break() - endif () + get_property(header_include_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN) - endforeach () - - else () # Previous line isn't a comment - Return original index + if ("${_current_line}" MATCHES "${header_include_regex}") + set(include_line "${ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE}\n") + else () + set(include_line "${ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE}\n\n") + set(header_inclusion_block FALSE) + endif () - increment_integer(_active_index 1) - set(matching_index ${_active_index}) + list(INSERT converted_source ${_line_index} "${include_line}") - endif () +endmacro() - set(${_return_var} ${matching_index} PARENT_SCOPE) +macro(_handle_platform_header) -endfunction() + if ("${line}" MATCHES "${comment_line_pattern}") + set(last_comment_start_index ${line_index}) + set(last_comment_end_index ${line_index}) + elseif ("${line}" MATCHES "${comment_block_start_pattern}") + set(last_comment_start_index ${line_index}) + elseif ("${line}" MATCHES "${comment_block_end_pattern}") + set(last_comment_end_index ${line_index}) + elseif ("${line}" MATCHES "${code_pattern}") -#=============================================================================# -# Converts the given sketch file into a valid 'cpp' source file under the project's working dir. -# During the conversion process the platform's main header file is inserted to the source file -# since it's critical for it to include it - Something that doesn't happen in "Standard" sketches. -# _sketch_file - Full path to the original sketch file (Read from). -# _converted_source_path - Full path to the converted target source file (Written to). -#=============================================================================# -function(convert_sketch_to_source _sketch_file _converted_source_path) + set(header_inclusion_block TRUE) - file(STRINGS "${_sketch_file}" sketch_loc) + # Calculate difference between current line index and last comment's end index + math(EXPR line_index_diff "${line_index} - ${last_comment_end_index}") - list(LENGTH sketch_loc num_of_loc) - decrement_integer(num_of_loc 1) + # Comment ends above current line, any lines should be inserted above + if (${line_index_diff} EQUAL 1) + _insert_platform_header("${line}" ${last_comment_start_index}) + else () + _insert_platform_header("${line}" ${line_index}) + endif () - set(refined_sketch) - set(header_inserted FALSE) + set(header_inserted TRUE) - set(header_insert_pattern - "${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}|${ARDUINO_CMAKE_FUNCTION_REGEX_PATTERN}") - set(include_line "#include <${ARDUINO_CMAKE_PLATFORM_HEADER_NAME}>") + endif () - foreach (loc_index RANGE 0 ${num_of_loc}) +endmacro() - list(GET sketch_loc ${loc_index} loc) +macro(_handle_prototype_generation) - if (NOT ${header_inserted}) + get_property(header_include_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN) - if ("${loc}" MATCHES "${header_insert_pattern}") + if (NOT "${line}" MATCHES "${header_include_regex}") + if (NOT "${line}" STREQUAL "") # Not a newline - _get_matching_header_insertion_index("${sketch_loc}" ${loc_index} header_index) + if (NOT header_inclusion_block) + # Insert a newline to separate prototypes from the rest of the code + list(INSERT converted_source ${line_index} "\n") + endif () - if (${header_index} LESS ${loc_index}) - set(formatted_include_line ${include_line} "\n\n") + foreach (prototype ${_sketch_prototypes}) + # Add missing semicolon to make a definition a declaration and escape it + escape_semicolon_in_string("${prototype};" escaped_prototype) + list(INSERT converted_source ${line_index} "${escaped_prototype}\n") + endforeach () - elseif (${header_index} EQUAL 0) - set(formatted_include_line ${include_line} "\n") + if (header_inclusion_block) + list(INSERT converted_source ${line_index} "\n// Prototypes generated by Arduino-CMake\n") + else () + list(INSERT converted_source ${line_index} "// Prototypes generated by Arduino-CMake\n") + endif () - else () + set(prototypes_inserted TRUE) + set(header_inclusion_block FALSE) - set(formatted_include_line "\n" ${include_line}) + endif () + endif () - if (${header_index} GREATER_EQUAL ${loc_index}) +endmacro() - decrement_integer(header_index 1) +macro(_handle_simple_lines) - string(APPEND formatted_include_line "\n") + if ("${line}" STREQUAL "") + list(APPEND converted_source "\n") + else () + escape_semicolon_in_string("${line}" formatted_line) + list(APPEND converted_source "${formatted_line}\n") + endif () - endif () +endmacro() - endif () +#=============================================================================# +# Converts the given sketch file into a valid '.cpp' source file under the project's working dir. +# During the conversion process the platform's main header file is inserted to the source file, +# as well as any given prototypes, found earlier through a function def-dec matching process. +# _sketch_file - Full path to the original sketch file (Read from). +# _converted_source_path - Full path to the converted target source file (Written to). +# _sketch_prototypes - List of prototypes to genereate, i.e. function definitions without a declaration. +#=============================================================================# +function(convert_sketch_to_source _sketch_file _converted_source_path _sketch_prototypes) - list(INSERT refined_sketch ${header_index} ${formatted_include_line}) + file(STRINGS "${_sketch_file}" sketch_lines) - set(header_inserted TRUE) + _setup_regex_patterns() - endif () + set(header_inserted FALSE) + set(prototypes_inserted FALSE) + set(header_inclusion_block FALSE) - endif () + set(last_comment_start_index 0) + set(last_comment_end_index 0) - if ("${loc}" STREQUAL "") - list(APPEND refined_sketch "\n") - else () + list_max_index("${sketch_lines}" lines_count) - string(REGEX REPLACE "^(.+);(.*)$" "\\1${ARDUINO_CMAKE_SEMICOLON_REPLACEMENT}\\2" - refined_loc "${loc}") + foreach (line_index RANGE ${lines_count}) - list(APPEND refined_sketch "${refined_loc}\n") + list(GET sketch_lines ${line_index} line) + if (NOT header_inserted) + _handle_platform_header() + elseif (NOT prototypes_inserted) + _handle_prototype_generation() endif () + _handle_simple_lines() + endforeach () - _write_source_file("${refined_sketch}" "${_converted_source_path}") + _write_source_file("${converted_source}" "${_converted_source_path}") endfunction() diff --git a/cmake/Platform/Sketches/Sketches.cmake b/cmake/Platform/Sketches/Sketches.cmake new file mode 100644 index 0000000..1fabae4 --- /dev/null +++ b/cmake/Platform/Sketches/Sketches.cmake @@ -0,0 +1,5 @@ +include(SketchHeadersResolver) +include(SketchLibrariesResolver) +include(SketchPrototypesResolver) +include(SketchSourceConverter) +include(SketchManager) diff --git a/cmake/Platform/Sources/FunctionDeclarationMatcher.cmake b/cmake/Platform/Sources/FunctionDeclarationMatcher.cmake new file mode 100644 index 0000000..0256c3b --- /dev/null +++ b/cmake/Platform/Sources/FunctionDeclarationMatcher.cmake @@ -0,0 +1,69 @@ +#=============================================================================# +# Attempts to match a given function definition signature to its' declaration, searching in all given headers. +# These headers are usually a list of all headers included by the function's file, recursively. +# Given a match, the declaration is returned, otherwise - "NOTFOUND" string. +# _definition_signature - String representing a full function signature, e.g. 'int main(int argc, char **argv)' +# _included_headers - List of headers to search the declaration in. +# Should include the function's containing file itself. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Function's declaration signature if exists, otherwise "NOTFOUND". +#=============================================================================# +function(match_function_declaration _definition_signature _included_headers _return_var) + + get_property(function_declaration_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DECLARATION_REGEX_PATTERN) + + # Get function name and list of argument-types + strip_function_signature("${_definition_signature}" original_stripped_function) + list(GET original_stripped_function 0 original_function_name) + list_max_index("${original_stripped_function}" original_function_args_length) + + foreach (included_header ${_included_headers}) + + file(STRINGS "${included_header}" header_lines) + + foreach (line ${header_lines}) + + # Search for function declarations + if ("${line}" MATCHES "${function_declaration_regex}") + + # Get function name and list of argument-types + strip_function_signature("${line}" iterated_stripped_function) + list(GET iterated_stripped_function 0 iterated_function_name) + list_max_index("${iterated_stripped_function}" iterated_function_args_length) + + if ("${original_function_name}" STREQUAL "${iterated_function_name}") + if (${orig_func_list_len} EQUAL ${iter_func_list_len}) + + set(arg_types_match TRUE) + + if (${iterated_function_args_length} GREATER 0) + foreach (arg_type_index RANGE 1 ${iterated_function_args_length}) + + list(GET original_stripped_function ${arg_type_index} orig_arg_type) + list(GET iterated_stripped_function ${arg_type_index} iter_arg_type) + + if (NOT ${orig_arg_type} EQUAL ${iter_arg_type}) + set(arg_types_match FALSE) + break() + endif () + + endforeach () + endif () + + if (${arg_types_match}) # Signature has been matched + set(${_return_var} ${line} PARENT_SCOPE) + return() + endif () + + endif () + endif () + + endif () + + endforeach () + + endforeach () + + set(${_return_var} "NOTFOUND" PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Sources/FunctionSignatureStripper.cmake b/cmake/Platform/Sources/FunctionSignatureStripper.cmake new file mode 100644 index 0000000..4da64cf --- /dev/null +++ b/cmake/Platform/Sources/FunctionSignatureStripper.cmake @@ -0,0 +1,53 @@ +#=============================================================================# +# Gets the types of a function's arguments as a list. +# _signature - String representing a full function signature, e.g. 'int main(int argc, char **argv)' +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of types, matching the given function-argument string. +#=============================================================================# +function(_get_function_arguments_types _signature _return_var) + + get_property(function_args_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_ARGS_REGEX_PATTERN) + get_property(function_single_arg_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_SINGLE_ARG_REGEX_PATTERN) + get_property(function_arg_type_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_ARG_TYPE_REGEX_PATTERN) + + string(REGEX MATCH ${function_args_regex} function_args_string "${_signature}") + string(REGEX MATCHALL ${function_single_arg_regex} + function_arg_list "${function_args_string}") + # Iterate through all arguments to extract only their type + foreach (arg ${function_arg_list}) + + string(REGEX MATCH ${function_arg_type_regex} arg_type "${arg}") + string(STRIP "${arg_type}" arg_type) # Strip remaining whitespaces + + if (NOT "${arg_type}" STREQUAL "void") # Do NOT append 'void' arguments - they're meaningless + list(APPEND function_args ${arg_type}) + endif () + + endforeach () + + set(${_return_var} ${function_args} PARENT_SCOPE) + +endfunction() + +#=============================================================================# +# Strips a given function signature to its name and its arguments' types. +# _signature - String representing a full function signature, e.g. 'int main(int argc, char **argv)' +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List consisting of the function's name and arguments types. +#=============================================================================# +function(strip_function_signature _signature _return_var) + + get_property(function_name_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_NAME_REGEX_PATTERN) + + # Strip function's name + string(REGEX MATCH ${function_name_regex} function_name_match "${_signature}") + set(function_name ${CMAKE_MATCH_1}) + list(APPEND stripped_signature ${function_name}) + + # Strip arguments types , i.e. without names + _get_function_arguments_types("${_signature}" function_args_types) + list(APPEND stripped_signature ${function_args_types}) + + set(${_return_var} ${stripped_signature} PARENT_SCOPE) + +endfunction() \ No newline at end of file diff --git a/cmake/Platform/Sources/HeaderExistanceChecker.cmake b/cmake/Platform/Sources/HeaderExistanceChecker.cmake new file mode 100644 index 0000000..f593259 --- /dev/null +++ b/cmake/Platform/Sources/HeaderExistanceChecker.cmake @@ -0,0 +1,21 @@ +#=============================================================================# +# Checks whether the given header name is discoverable by the given target, +# i.e. whether it's part of the target's 'INCLUDE_DIRECTORIES' property. +# _header_we - Name of a header to check its' discoverability. +# _target_name - Name of a target to check discoverability against. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - True if discoverable, false otherwise. +#=============================================================================# +function(is_header_discoverable_by_target _header_we _target_name _return_var) + + get_target_include_directories(${_target_name} target_include_dirs) + + get_header_file(${_header_we} ${target_include_dirs} header_found) + + if (NOT header_found OR "${header_found}" MATCHES "NOTFOUND") + set(_return_var FALSE PARENT_SCOPE) + else () + set(_return_var TRUE PARENT_SCOPE) + endif () + +endfunction() diff --git a/cmake/Platform/Sources/HeaderRetriever.cmake b/cmake/Platform/Sources/HeaderRetriever.cmake new file mode 100644 index 0000000..ee99974 --- /dev/null +++ b/cmake/Platform/Sources/HeaderRetriever.cmake @@ -0,0 +1,29 @@ +#=============================================================================# +# Retrieves full path to the file associated with the given header name, +# which should be located under one of the directories in the given list. +# The search is performed recursively (i.e. including sub-dirs) by default. +# If the header can't be found, "NOTFOUND" string is returned. +# _header_we - Name of a header file which should be retrieved. +# _dir_list - List of directories which could contain the searched header file. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Full path to the header file associated with the given header name, "NOTFOUND" if can't be found. +#=============================================================================# +function(get_header_file _header_we _dir_list _return_var) + + foreach (include_dir ${_dir_list}) + + find_header_files("${include_dir}" include_dir_headers RECURSE) + + foreach (included_header ${include_dir_headers}) + get_name_without_file_extension("${included_header}" included_header_we) + if ("${included_header_we}" STREQUAL "${_header_we}") + set(${_return_var} ${included_header} PARENT_SCOPE) + return() + endif () + endforeach () + + endforeach () + + set(${_return_var} "NOTFOUND" PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Sources/SourceFunctionsRetriever.cmake b/cmake/Platform/Sources/SourceFunctionsRetriever.cmake new file mode 100644 index 0000000..bc1b8d7 --- /dev/null +++ b/cmake/Platform/Sources/SourceFunctionsRetriever.cmake @@ -0,0 +1,15 @@ +function(get_source_function_definitions _source_file _return_var) + + get_property(function_definition_regex GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DEFINITION_REGEX_PATTERN) + + file(STRINGS "${_source_file}" source_lines) + + foreach (line ${source_lines}) + if ("${line}" MATCHES "${function_definition_regex}") + list(APPEND definitions "${line}") + endif () + endforeach () + + set(${_return_var} ${definitions} PARENT_SCOPE) + +endfunction() \ No newline at end of file diff --git a/cmake/Platform/Sources/SourceHeadersRetriever.cmake b/cmake/Platform/Sources/SourceHeadersRetriever.cmake new file mode 100644 index 0000000..de777ff --- /dev/null +++ b/cmake/Platform/Sources/SourceHeadersRetriever.cmake @@ -0,0 +1,75 @@ +#=============================================================================# +# Retrieves all headers included by a source file. +# Headers are returned by their name, with extension (such as '.h'). +# _source_file - Path to a source file to get its' included headers. +# [WE] - Return headers without extension, just their names. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of headers names with extension that are included by the given source file. +#=============================================================================# +function(_get_source_included_headers _source_file _return_var) + + cmake_parse_arguments(parsed_args "WE" "" "" ${ARGN}) + + get_property(header_include_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN) + get_property(header_name_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_NAME_REGEX_PATTERN) + + file(STRINGS "${_source_file}" source_lines) # Loc = Lines of code + + list(FILTER source_lines INCLUDE REGEX ${header_include_regex}) + + # Extract header names from inclusion + foreach (loc ${source_lines}) + + string(REGEX MATCH ${header_name_regex} match ${loc}) + + if (parsed_args_WE) + get_name_without_file_extension("${CMAKE_MATCH_1}" header_name) + else () + set(header_name ${CMAKE_MATCH_1}) + endif () + + list(APPEND headers ${header_name}) + + endforeach () + + set(${_return_var} ${headers} PARENT_SCOPE) + +endfunction() + +#=============================================================================# +# Retrieves all headers used by a source file, possibly recursively (Headers used by headers). +# _source_file - Path to a source file to get its' used headers. +# _search_dirs - List of directories where headers should be searched at. +# [RECURSIVE] - Whether to search for headers recursively. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - List of full paths to the headers that are used by the given source file. +#=============================================================================# +function(get_source_headers _source_file _search_dirs _return_var) + + cmake_parse_arguments(parsed_args "RECURSIVE" "" "" ${ARGN}) + + _get_source_included_headers(${_source_file} included_headers WE) + + foreach (header ${included_headers}) + + get_header_file(${header} ${_search_dirs} header_path) + if (NOT header_path OR "${header_path}" MATCHES "NOTFOUND") + continue() + endif () + + list(APPEND final_included_headers ${header_path}) + + if (parsed_args_RECURSIVE) + get_source_headers(${header_path} ${_search_dirs} recursive_included_headers RECURSIVE) + list(APPEND final_included_headers ${recursive_included_headers}) + endif () + + endforeach () + + if (final_included_headers) + list(REMOVE_DUPLICATES final_included_headers) + endif () + + set(${_return_var} ${final_included_headers} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Sources/Sources.cmake b/cmake/Platform/Sources/Sources.cmake new file mode 100644 index 0000000..6c71b2d --- /dev/null +++ b/cmake/Platform/Sources/Sources.cmake @@ -0,0 +1,10 @@ +include(FunctionSignatureStripper) +include(FunctionDeclarationMatcher) +include(SourceFunctionsRetriever) +include(SourceSeeker) +include(ExampleSourcesSeeker) +include(ArduinoLibrarySourcesSeeker) +include(HeaderRetriever) +include(HeaderExistanceChecker) +include(SourceHeadersRetriever) +include(SourcesManager) diff --git a/cmake/Platform/Sources/SourcesManager.cmake b/cmake/Platform/Sources/SourcesManager.cmake index 6dda993..bb55c6b 100644 --- a/cmake/Platform/Sources/SourcesManager.cmake +++ b/cmake/Platform/Sources/SourcesManager.cmake @@ -1,7 +1,3 @@ -include(SourceSeeker) -include(ExampleSourcesSeeker) -include(ArduinoLibrarySourcesSeeker) - #=============================================================================# # Appends all sources and headers under the given directory to the givne target. # This could also be done recursively if the RECURSE option is provided. @@ -82,49 +78,16 @@ function(get_source_file_includes _source_file _return_var) endif () file(STRINGS "${_source_file}" source_lines) - - list(FILTER source_lines INCLUDE REGEX "${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}") - - set(${_return_var} ${source_lines} PARENT_SCOPE) - -endfunction() - -#=============================================================================# -# Retrieves all headers includedby a source file. -# Headers are returned by their name, with extension (such as '.h'). -# _source_file - Path to a source file to get its' included headers. -# _return_var - Name of variable in parent-scope holding the return value. -# Returns - List of headers names with extension that are included by the given source file. -#=============================================================================# -function(get_source_file_included_headers _source_file _return_var) - cmake_parse_arguments(headers "WE" "" "" ${ARGN}) + get_property(header_include_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN) + list(FILTER source_lines INCLUDE REGEX "${header_include_regex}") - file(STRINGS "${_source_file}" source_lines) # Loc = Lines of code - - list(FILTER source_lines INCLUDE REGEX ${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}) - - # Extract header names from inclusion - foreach (loc ${source_lines}) - - string(REGEX MATCH ${ARDUINO_CMAKE_HEADER_NAME_REGEX_PATTERN} match ${loc}) - - if (headers_WE) - get_name_without_file_extension("${CMAKE_MATCH_1}" header_name) - else () - set(header_name ${CMAKE_MATCH_1}) - endif () - - list(APPEND headers ${header_name}) - - endforeach () - - set(${_return_var} ${headers} PARENT_SCOPE) + set(${_return_var} ${source_lines} PARENT_SCOPE) endfunction() #=============================================================================# -# Gets paths of parent directories from all header files amongst the given sources. +# Gets paths of parent directories of all header files amongst the given sources. # The list of paths is unique (without duplicates). # _sources - List of sources to get include directories from. # _return_var - Name of variable in parent-scope holding the return value. @@ -132,15 +95,14 @@ endfunction() #=============================================================================# function(get_headers_parent_directories _sources _return_var) + get_property(header_file_extension_regex GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_FILE_EXTENSION_REGEX_PATTERN) + # Extract header files - list(FILTER _sources INCLUDE REGEX "${ARDUINO_CMAKE_HEADER_FILE_EXTENSION_REGEX_PATTERN}") + list(FILTER _sources INCLUDE REGEX "${header_file_extension_regex}") foreach (header_source ${_sources}) - get_filename_component(header_parent_dir ${header_source} DIRECTORY) - list(APPEND parent_dirs ${header_parent_dir}) - endforeach () if (parent_dirs) # Check parent dirs, could be none if there aren't any headers amongst sources diff --git a/cmake/Platform/System/DefaultsManager.cmake b/cmake/Platform/System/DefaultsManager.cmake index 1e22147..702b3ad 100644 --- a/cmake/Platform/System/DefaultsManager.cmake +++ b/cmake/Platform/System/DefaultsManager.cmake @@ -3,19 +3,25 @@ #=============================================================================# function(set_internal_search_patterns) - set(ARDUINO_CMAKE_SEMICOLON_REPLACEMENT "!@&#%" CACHE STRING - "String replacement for the semicolon char, required when treating lists as code") - set(ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN "^#include.*[<\"]" CACHE STRING - "Regex pattern matching header inclusion in a source file") - set(ARDUINO_CMAKE_HEADER_NAME_REGEX_PATTERN - "${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}(.+)[>\"]$" CACHE STRING - "Regex pattern matching a header's name when wrapped in inclusion line") - set(ARDUINO_CMAKE_HEADER_FILE_EXTENSION_REGEX_PATTERN ".+\\.h.*$" CACHE STRING - "Regex pattern matching all header file extensions") - set(ARDUINO_CMAKE_NAME_WE_REGEX_PATTERN "(.+)\\." CACHE STRING - "Regex pattern matching name without file extension") - set(ARDUINO_CMAKE_FUNCTION_REGEX_PATTERN "^([a-z]|[A-Z])+.*\(([a-z]|[A-Z])*\)" CACHE STRING - "Regex pattern matching a function signature in a source file") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_SEMICOLON_REPLACEMENT "!@&#%") + + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_PREPROCESSOR_REGEX_PATTERN "^#([A-Za-z0-9_])+") + set(header_include_regex "^#include.*[<\"]") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN "${header_include_regex}") + + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_NAME_REGEX_PATTERN "${header_include_regex}(.+)[>\"]$") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_HEADER_FILE_EXTENSION_REGEX_PATTERN ".+\\.h.*$") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_NAME_WE_REGEX_PATTERN "([^\\/]+)\\.") + + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DEFINITION_REGEX_PATTERN + "^([A-Za-z0-9_][ \t\r\n]*)+\\(.*\\)[ \t\r\n]*[{]*$") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_DECLARATION_REGEX_PATTERN + "^([A-Za-z0-9_])+.+([A-Za-z0-9_])+[ \t\r\n]*\\((.*)\\);$") + + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_NAME_REGEX_PATTERN "(([A-Za-z0-9_])+)[ \t\r\n]*\\(.*\\)") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_ARGS_REGEX_PATTERN "\\((.*)\\)") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_SINGLE_ARG_REGEX_PATTERN "([A-Za-z0-9_]+)[^,]*") + set_property(GLOBAL PROPERTY ARDUINO_CMAKE_FUNCTION_ARG_TYPE_REGEX_PATTERN "[A-Za-z0-9_]+.*[ \t\r\n]+") endfunction() diff --git a/cmake/Platform/System/PlatformElementsManager.cmake b/cmake/Platform/System/PlatformElementsManager.cmake index ef3f173..7dcf735 100644 --- a/cmake/Platform/System/PlatformElementsManager.cmake +++ b/cmake/Platform/System/PlatformElementsManager.cmake @@ -165,8 +165,11 @@ function(find_platform_main_header) message(STATUS "Determined Platform Header: ${biggest_header}") # Store both header's name (with extension) and path get_filename_component(platform_header_name "${biggest_header}" NAME) + set(ARDUINO_CMAKE_PLATFORM_HEADER_NAME "${platform_header_name}" CACHE STRING "Platform's main header name (With extension)") + set(ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE "#include <${platform_header_name}>" CACHE STRING + "Include line of the platform's main header file to be embedded in source files") set(ARDUINO_CMAKE_PLATFORM_HEADER_PATH "${biggest_header}" CACHE PATH "Path to platform's main header file") diff --git a/cmake/Platform/Targets/PlatformLibraryTarget.cmake b/cmake/Platform/Targets/PlatformLibraryTarget.cmake index 4a49842..68f59f1 100644 --- a/cmake/Platform/Targets/PlatformLibraryTarget.cmake +++ b/cmake/Platform/Targets/PlatformLibraryTarget.cmake @@ -8,7 +8,7 @@ function(find_dependent_platform_libraries _sources _return_var) foreach (source ${_sources}) - get_source_file_included_headers(${source} source_includes WE) + _get_source_included_headers(${source} source_includes WE) list(APPEND included_headers_names ${source_includes}) endforeach () diff --git a/cmake/Platform/Utilities/ListUtils.cmake b/cmake/Platform/Utilities/ListUtils.cmake index 987cb67..fe33da8 100644 --- a/cmake/Platform/Utilities/ListUtils.cmake +++ b/cmake/Platform/Utilities/ListUtils.cmake @@ -7,7 +7,6 @@ macro(list_replace _list _index _new_element) list(REMOVE_AT ${_list} ${_index}) - list(INSERT ${_list} ${_index} "${_new_element}") endmacro() @@ -25,3 +24,23 @@ macro(initialize_list _list) endif () endmacro() + +#=============================================================================# +# Gets the maximal index a given list can relate to, i.e. the largest index, zero-counted. +# Usually it's just `length - 1`, but there are edge cases where special treatment must be applied. +# _list - List to get its maximal index. +# _return_var - Name of variable in parent-scope holding the return value. +# Returns - Maximal index the list can relate to (usually `length - 1`). +#=============================================================================# +function(list_max_index _list _return_var) + + list(LENGTH _list list_length) + + set(index ${list_length}) + if (${list_length} GREATER 0) + decrement_integer(index 1) + endif () + + set(${_return_var} ${index} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Utilities/PlatformLibraryUtils.cmake b/cmake/Platform/Utilities/PlatformLibraryUtils.cmake index 1056353..0885555 100644 --- a/cmake/Platform/Utilities/PlatformLibraryUtils.cmake +++ b/cmake/Platform/Utilities/PlatformLibraryUtils.cmake @@ -7,6 +7,7 @@ function(is_platform_library _name _return_var) string(TOLOWER "${_name}" name_lower) + if ("${name_lower}" IN_LIST ARDUINO_CMAKE_PLATFORM_LIBRARIES) set(lib_found TRUE) else () diff --git a/cmake/Platform/Utilities/StringUtils.cmake b/cmake/Platform/Utilities/StringUtils.cmake index c4b599f..e4a3072 100644 --- a/cmake/Platform/Utilities/StringUtils.cmake +++ b/cmake/Platform/Utilities/StringUtils.cmake @@ -74,12 +74,14 @@ endfunction() #=============================================================================# # Extracts a name symbol without possible file extension (marked usually by a dot ('.'). # _input_string - String containing name symbol and possibly file extension. -# _return_var - Name of a CMake variable that will hold the extraction result. +# _return_var - Name of a CMake variable that will hold the return value. # Returns - String containing input name without possible file extension. #=============================================================================# function(get_name_without_file_extension _input_string _return_var) - string(REGEX MATCH "${ARDUINO_CMAKE_NAME_WE_REGEX_PATTERN}" match "${_input_string}") + get_property(name_we_regex GLOBAL PROPERTY ARDUINO_CMAKE_NAME_WE_REGEX_PATTERN) + + string(REGEX MATCH "${name_we_regex}" match "${_input_string}") set(${_return_var} ${CMAKE_MATCH_1} PARENT_SCOPE) @@ -89,7 +91,7 @@ endfunction() # 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. +# _return_var - Name of a CMake variable that will hold the return value. # Returns - PascalCase converted string. #=============================================================================# function(convert_string_to_pascal_case _input_string _return_var) @@ -108,3 +110,28 @@ function(convert_string_to_pascal_case _input_string _return_var) set(${_return_var} ${combined_string} PARENT_SCOPE) endfunction() + +#=============================================================================# +# Escapes a semicolon in a given string by replacing it with a "magic" string instead, +# which isn't parsed as a list separator by CMake. +# This function can also reverse an escaped semicolon by replacing it back with a semicolon. +# _string - String to escape/re-escape. +# [REVERSE] - Optional flag indicating whether to reverse an already-escaped string back to its original form. +# _return_var - Name of a CMake variable that will hold the return value. +# Returns - Original string if [REVERSE] provided, Semicolon-escaped string otherwise. +#=============================================================================# +function(escape_semicolon_in_string _string _return_var) + + cmake_parse_arguments(parsed_args "REVERSE" "" "" ${ARGN}) + + get_property(semicolon_replacement GLOBAL PROPERTY ARDUINO_CMAKE_SEMICOLON_REPLACEMENT) + + if (parsed_args_REVERSE) + string(REGEX REPLACE "^(.+)${semicolon_replacement}(.*)$" "\\1;\\2" escaped_line "${_string}") + else () + string(REGEX REPLACE "^(.+);(.*)$" "\\1${semicolon_replacement}\\2" escaped_line "${_string}") + endif () + + set(${_return_var} ${escaped_line} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Utilities/TargetUtils.cmake b/cmake/Platform/Utilities/TargetUtils.cmake new file mode 100644 index 0000000..1019175 --- /dev/null +++ b/cmake/Platform/Utilities/TargetUtils.cmake @@ -0,0 +1,29 @@ +function(get_target_include_directories _target_name _return_var) + + # Get target's direct include dirs + get_target_property(target_include_dirs ${_target_name} INCLUDE_DIRECTORIES) + + # Get include dirs of targets linked to the given target + get_target_property(target_linked_libs ${_target_name} LINK_LIBRARIES) + + # Explictly add include dirs of all linked libraries (given they're valid cmake targets) + foreach (linked_lib ${target_linked_libs}) + + if (NOT TARGET ${linked_lib}) # Might be a command-line linked library (such as 'm'/math) + continue() + endif () + + get_target_include_directories(${linked_lib} lib_include_dirs) + set(include_dirs ${lib_include_dirs}) # Update list with recursive call results + + endforeach () + + if (NOT "${target_include_dirs}" MATCHES "NOTFOUND") # Target has direct include dirs + list(APPEND include_dirs ${target_include_dirs}) + endif () + + list(REMOVE_DUPLICATES include_dirs) + + set(${_return_var} ${include_dirs} PARENT_SCOPE) + +endfunction() diff --git a/cmake/Platform/Utilities/Utilities.cmake b/cmake/Platform/Utilities/Utilities.cmake index 92a56c4..da1488d 100644 --- a/cmake/Platform/Utilities/Utilities.cmake +++ b/cmake/Platform/Utilities/Utilities.cmake @@ -4,5 +4,6 @@ include(StringUtils) include(PathUtils) include(PropertyUtils) include(LibraryUtils) +include(TargetUtils) include(PlatformLibraryUtils) include(CMakeArgumentsUtils) diff --git a/examples/sketch/sketch2.pde b/examples/sketch/sketch2.pde index e7c965e..7d33ef7 100644 --- a/examples/sketch/sketch2.pde +++ b/examples/sketch/sketch2.pde @@ -2,7 +2,10 @@ int x = 5; +int bar(short, int b); + void foo() { Serial.print(x); + bar(5, 6); }