Skip to content
This repository was archived by the owner on Apr 17, 2023. It is now read-only.

Commit 646a22f

Browse files
committed
Completely redesigned sketch-to-source conversion process
It now fully supports both platform header & prototypes insertion
1 parent 505283e commit 646a22f

File tree

2 files changed

+90
-81
lines changed

2 files changed

+90
-81
lines changed

cmake/Platform/Sketches/SketchSourceConverter.cmake

Lines changed: 87 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -14,92 +14,108 @@ function(_write_source_file _sketch_lines _file_path)
1414

1515
endfunction()
1616

17-
#=============================================================================#
18-
# Finds the best line to insert an '#include' of the platform's main header to.
19-
# The function assumes that the initial state of the given 'active index' is set to the line that
20-
# best fitted the insertion, however, it might need a bit more optimization. Why?
21-
# Because above those lines there might be a comment, or a comment block,
22-
# all of which should be taken into account in order to minimize the effect on code's readability.
23-
# _sketch_lines - List of lines-of-code belonging to the sketch.
24-
# _active_index - Index that indicates the best-not-optimized line to insert header to.
25-
# _return_var - Name of variable in parent-scope holding the return value.
26-
# Returns - Best fitted index to insert platform's main header '#include' to.
27-
#=============================================================================#
28-
function(_get_matching_header_insertion_index _sketch_lines _active_index _return_var)
17+
macro(_setup_regex_patterns)
18+
19+
string(CONCAT function_prototype_pattern
20+
"${ARDUINO_CMAKE_FUNCTION_DECLARATION_REGEX_PATTERN}"
21+
"|${ARDUINO_CMAKE_FUNCTION_DEFINITION_REGEX_PATTERN}")
22+
string(CONCAT code_pattern
23+
"${ARDUINO_CMAKE_PREPROCESSOR_REGEX_PATTERN}"
24+
"|${function_prototype_pattern}")
25+
26+
set(comment_line_pattern "\\/\\/")
27+
set(comment_block_start_pattern "\\/\\*")
28+
set(comment_block_end_pattern "\\*\\/")
2929

30-
if (${_active_index} EQUAL 0) # First line in a file will always result in the 1st index
31-
set(${_return_var} 0 PARENT_SCOPE)
32-
return()
30+
endmacro()
31+
32+
macro(_insert_platform_header _current_line _line_index)
33+
34+
if ("${_current_line}" MATCHES "${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}")
35+
set(include_line "${ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE}\n")
3336
else ()
34-
decrement_integer(_active_index 1)
37+
set(include_line "${ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE}\n\n")
38+
set(header_inclusion_block FALSE)
3539
endif ()
3640

37-
list(GET _sketch_lines ${_active_index} previous_loc)
41+
list(INSERT converted_source ${_line_index} "${include_line}")
42+
43+
endmacro()
3844

39-
if ("${previous_loc}" MATCHES "^//") # Simple one-line comment
40-
set(matching_index ${_active_index})
41-
elseif ("${previous_loc}" MATCHES "\\*/$") # End of multi-line comment
45+
macro(_handle_platform_header)
4246

43-
foreach (index RANGE ${_active_index} 0)
44-
list(GET _sketch_lines ${index} multi_comment_line)
47+
if ("${line}" MATCHES "${comment_line_pattern}")
48+
set(last_comment_start_index ${line_index})
49+
set(last_comment_end_index ${line_index})
50+
elseif ("${line}" MATCHES "${comment_block_start_pattern}")
51+
set(last_comment_start_index ${line_index})
52+
elseif ("${line}" MATCHES "${comment_block_end_pattern}")
53+
set(last_comment_end_index ${line_index})
54+
elseif ("${line}" MATCHES "${code_pattern}")
4555

46-
if ("${multi_comment_line}" MATCHES "^\\/\\*") # Start of multi-line comment
47-
set(matching_index ${index})
48-
break()
49-
endif ()
50-
endforeach ()
56+
set(header_inclusion_block TRUE)
57+
58+
# Calculate difference between current line index and last comment's end index
59+
math(EXPR line_index_diff "${line_index} - ${last_comment_end_index}")
60+
61+
# Comment ends above current line, any lines should be inserted above
62+
if (${line_index_diff} EQUAL 1)
63+
_insert_platform_header("${line}" ${last_comment_start_index})
64+
else ()
65+
_insert_platform_header("${line}" ${line_index})
66+
endif ()
67+
68+
set(header_inserted TRUE)
5169

52-
else () # Previous line isn't a comment - Return original index
53-
increment_integer(_active_index 1)
54-
set(matching_index ${_active_index})
5570
endif ()
5671

57-
set(${_return_var} ${matching_index} PARENT_SCOPE)
72+
endmacro()
5873

59-
endfunction()
74+
macro(_handle_prototype_generation)
6075

61-
#=============================================================================#
62-
# Inline macro which handles the process of inserting a line including the platform header.
63-
# _sketch_lines - List of code lines read from the converted sketch file.
64-
# _insertion_line_index - Index of a code line at which the header should be inserted
65-
#=============================================================================#
66-
macro(_insert_line _inserted_line _sketch_lines _insertion_line_index)
76+
if (NOT "${line}" MATCHES "${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}")
77+
if (NOT "${line}" STREQUAL "") # Not a newline
6778

68-
_get_matching_header_insertion_index("${_sketch_lines}" ${_insertion_line_index} header_index)
79+
if (NOT header_inclusion_block)
80+
# Insert a newline to separate prototypes from the rest of the code
81+
list(INSERT converted_source ${line_index} "\n")
82+
endif ()
6983

70-
if (${header_index} LESS ${_insertion_line_index})
71-
set(formatted_include_line ${_inserted_line} "\n\n")
72-
elseif (${header_index} EQUAL 0)
73-
set(formatted_include_line ${_inserted_line} "\n")
74-
else ()
75-
set(formatted_include_line "\n" ${_inserted_line})
84+
foreach (prototype ${_sketch_prototypes})
85+
# Add missing semicolon to make a definition a declaration and escape it
86+
escape_semicolon_in_string("${prototype};" escaped_prototype)
87+
list(INSERT converted_source ${line_index} "${escaped_prototype}\n")
88+
endforeach ()
89+
90+
if (header_inclusion_block)
91+
list(INSERT converted_source ${line_index} "\n// Prototypes generated by Arduino-CMake\n")
92+
else ()
93+
list(INSERT converted_source ${line_index} "// Prototypes generated by Arduino-CMake\n")
94+
endif ()
95+
96+
set(prototypes_inserted TRUE)
97+
set(header_inclusion_block FALSE)
7698

77-
if (${header_index} GREATER_EQUAL ${_insertion_line_index})
78-
decrement_integer(header_index 1)
79-
string(APPEND formatted_include_line "\n")
8099
endif ()
81100
endif ()
82101

83-
list(INSERT converted_source ${header_index} ${formatted_include_line})
84-
85102
endmacro()
86103

87-
macro(_insert_prototypes _prototypes _sketch_lines _insertion_line_index)
104+
macro(_handle_simple_lines)
88105

89-
foreach (prototype ${_prototypes})
90-
# Add semicolon ';' to make it a declaration
91-
escape_semicolon_in_string("${prototype};" formatted_prototype)
92-
93-
_insert_line("${formatted_prototype}" "${sketch_lines}" ${line_index})
94-
increment_integer(_insertion_line_index 1)
95-
endforeach ()
106+
if ("${line}" STREQUAL "")
107+
list(APPEND converted_source "\n")
108+
else ()
109+
escape_semicolon_in_string("${line}" formatted_line)
110+
list(APPEND converted_source "${formatted_line}\n")
111+
endif ()
96112

97113
endmacro()
98114

99115
#=============================================================================#
100-
# Converts the given sketch file into a valid 'cpp' source file under the project's working dir.
101-
# During the conversion process the platform's main header file is inserted to the source file
102-
# since it's critical for it to include it - Something that doesn't happen in "Standard" sketches.
116+
# Converts the given sketch file into a valid '.cpp' source file under the project's working dir.
117+
# During the conversion process the platform's main header file is inserted to the source file,
118+
# as well as any given prototypes, found earlier through a function def-dec matching process.
103119
# _sketch_file - Full path to the original sketch file (Read from).
104120
# _converted_source_path - Full path to the converted target source file (Written to).
105121
# _sketch_prototypes - List of prototypes to genereate, i.e. function definitions without a declaration.
@@ -108,38 +124,28 @@ function(convert_sketch_to_source _sketch_file _converted_source_path _sketch_pr
108124

109125
file(STRINGS "${_sketch_file}" sketch_lines)
110126

111-
set(function_prototype_pattern
112-
"${ARDUINO_CMAKE_FUNCTION_DECLARATION_REGEX_PATTERN}|${ARDUINO_CMAKE_FUNCTION_DEFINITION_REGEX_PATTERN}")
113-
set(header_insert_pattern
114-
"${ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN}|${function_prototype_pattern}")
127+
_setup_regex_patterns()
115128

116129
set(header_inserted FALSE)
117130
set(prototypes_inserted FALSE)
131+
set(header_inclusion_block FALSE)
132+
133+
set(last_comment_start_index 0)
134+
set(last_comment_end_index 0)
118135

119136
list_max_index("${sketch_lines}" lines_count)
120-
#[[list(LENGTH sketch_lines lines_count)
121-
decrement_integer(lines_count 1)]]
122137

123138
foreach (line_index RANGE ${lines_count})
124139

125140
list(GET sketch_lines ${line_index} line)
126141

127-
if (NOT ${header_inserted})
128-
if ("${line}" MATCHES "${header_insert_pattern}")
129-
_insert_line("${ARDUINO_CMAKE_PLATFORM_HEADER_INCLUDE_LINE}" "${sketch_lines}" ${line_index})
130-
set(header_inserted TRUE)
131-
endif ()
132-
elseif (NOT ${prototypes_inserted} AND "${line}" MATCHES "${function_prototype_pattern}")
133-
_insert_prototypes("${_sketch_prototypes}" "${sketch_lines}" ${line_index})
134-
set(prototypes_inserted TRUE)
142+
if (NOT header_inserted)
143+
_handle_platform_header()
144+
elseif (NOT prototypes_inserted)
145+
_handle_prototype_generation()
135146
endif ()
136147

137-
if ("${line}" STREQUAL "")
138-
list(APPEND converted_source "\n")
139-
else ()
140-
escape_semicolon_in_string("${line}" formatted_line)
141-
list(APPEND converted_source "${formatted_line}\n")
142-
endif ()
148+
_handle_simple_lines()
143149

144150
endforeach ()
145151

cmake/Platform/System/DefaultsManager.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ function(set_internal_search_patterns)
66
set(ARDUINO_CMAKE_SEMICOLON_REPLACEMENT "!@&#%" CACHE STRING
77
"String replacement for the semicolon char, required when treating lists as code")
88

9+
set(ARDUINO_CMAKE_PREPROCESSOR_REGEX_PATTERN "^#([A-Za-z0-9_])+" CACHE STRING
10+
"Regex pattern matching preprocessor directives in source files")
11+
912
set(ARDUINO_CMAKE_HEADER_INCLUDE_REGEX_PATTERN "^#include.*[<\"]" CACHE STRING
1013
"Regex pattern matching header inclusion in a source file")
1114

0 commit comments

Comments
 (0)