Skip to content

Commit 5dc254f

Browse files
authored
Merge pull request #78 from per1234/warnings-delta
libraries/compile-examples: add support for reporting changes in compiler warning count caused by a commit or PR
2 parents 62cc4d7 + 0deacdf commit 5dc254f

File tree

6 files changed

+1003
-116
lines changed

6 files changed

+1003
-116
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,15 @@ Path in which to save a JSON formatted file containing data from the sketch comp
114114

115115
### `github-token`
116116

117-
GitHub access token used to get information from the GitHub API. Only needed for private repositories with `enable-size-deltas-report` set to `true`. It will be convenient to use [`${{ secrets.GITHUB_TOKEN }}`](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token). Default `""`.
117+
GitHub access token used to get information from the GitHub API. Only needed for private repositories with `enable-deltas-report` set to `true`. It will be convenient to use [`${{ secrets.GITHUB_TOKEN }}`](https://help.github.com/en/actions/configuring-and-managing-workflows/authenticating-with-the-github_token). Default `""`.
118118

119-
### `enable-size-deltas-report`
119+
### `enable-deltas-report`
120120

121-
Set to `true` to cause the action to determine the change in memory usage of the compiled sketches. If the workflow is triggered by a `pull_request` event, the comparison is between the pull request branch and the tip of the pull request's base branch. If the workflow is triggered by a `push` event, the comparison is between the pushed commit and its immediate parent. This may be used with the [`arduino/actions/libraries/report-size-deltas` action](https://github.com/arduino/actions/tree/master/libraries/report-size-deltas). Default `false`.
121+
Set to `true` to cause the action to determine the change in memory usage and compiler warnings of the compiled sketches. If the workflow is triggered by a `pull_request` event, the comparison is between the pull request branch and the tip of the pull request's base branch. If the workflow is triggered by a `push` event, the comparison is between the pushed commit and its immediate parent. The deltas will be displayed in the GitHub Actions build log. This may be used with the [`arduino/actions/libraries/report-size-deltas` action](https://github.com/arduino/actions/tree/master/libraries/report-size-deltas). Default `false`.
122+
123+
### `enable-warnings-report`
124+
125+
Set to `true` to cause the action to record the compiler warning count for each sketch compilation in the sketches report. Default `false`.
122126

123127
## Example usage
124128

@@ -133,11 +137,11 @@ Only compiling examples:
133137
version: 1.1.3
134138
```
135139

136-
Storing the memory usage change report as a [workflow artifact](https://help.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts):
140+
Storing the sketches compilation report report as a [workflow artifact](https://help.github.com/en/actions/configuring-and-managing-workflows/persisting-workflow-data-using-artifacts):
137141
```yaml
138142
- uses: arduino/actions/libraries/compile-examples@master
139143
with:
140-
enable-size-deltas-report: true
144+
enable-deltas-report: true
141145
- if: github.event_name == 'pull_request'
142146
uses: actions/upload-artifact@v1
143147
with:

action.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ inputs:
2525
github-token:
2626
description: 'GitHub access token used to get information from the GitHub API. Only needed if you are using the size deltas report feature with a private repository.'
2727
default: ''
28-
enable-size-deltas-report:
29-
description: 'Set to true to cause the action to determine the change in memory usage of the compiled sketches between the head and base refs of a PR and the immediate parent commit of a push'
28+
enable-deltas-report:
29+
description: 'Set to true to cause the action to determine the change in memory usage and compiler warnings of the compiled sketches between the head and base refs of a PR and the immediate parent commit of a push'
3030
default: false
31+
enable-warnings-report:
32+
description: 'Set to true to cause the action to record the compiler warning count for each sketch compilation in the sketches report'
33+
default: false
34+
3135
runs:
3236
using: 'docker'
3337
image: 'Dockerfile'

compilesketches/compilesketches.py

Lines changed: 162 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ def main():
2828
"sketches-report-path instead.")
2929
os.environ["INPUT_SKETCHES-REPORT-PATH"] = os.environ["INPUT_SIZE-DELTAS-REPORT-FOLDER-NAME"]
3030

31+
if "INPUT_ENABLE-SIZE-DELTAS-REPORT" in os.environ:
32+
print("::warning::The enable-size-deltas-report input is deprecated. Use the equivalent input: "
33+
"enable-deltas-report instead.")
34+
os.environ["INPUT_ENABLE-DELTAS-REPORT"] = os.environ["INPUT_ENABLE-SIZE-DELTAS-REPORT"]
35+
3136
if "INPUT_ENABLE-SIZE-TRENDS-REPORT" in os.environ:
3237
print("::warning::The size trends report feature has been moved to a dedicated action. See the documentation "
3338
"at https://github.com/arduino/actions/tree/report-size-trends-action/libraries/report-size-trends")
@@ -40,7 +45,8 @@ def main():
4045
sketch_paths=os.environ["INPUT_SKETCH-PATHS"],
4146
verbose=os.environ["INPUT_VERBOSE"],
4247
github_token=os.environ["INPUT_GITHUB-TOKEN"],
43-
enable_size_deltas_report=os.environ["INPUT_ENABLE-SIZE-DELTAS-REPORT"],
48+
enable_deltas_report=os.environ["INPUT_ENABLE-DELTAS-REPORT"],
49+
enable_warnings_report=os.environ["INPUT_ENABLE-WARNINGS-REPORT"],
4450
sketches_report_path=os.environ["INPUT_SKETCHES-REPORT-PATH"]
4551
)
4652

@@ -60,7 +66,9 @@ class CompileSketches:
6066
recursively for sketches.
6167
verbose -- set to "true" for verbose output ("true", "false")
6268
github_token -- GitHub access token
63-
enable_size_deltas_report -- set to "true" to cause the action to determine the change in memory usage
69+
enable_deltas_report -- set to "true" to cause the action to determine the change in memory usage
70+
("true", "false")
71+
enable_warnings_report -- set to "true" to cause the action to add compiler warning count to the sketches report
6472
("true", "false")
6573
sketches_report_path -- folder to save the sketches report to
6674
"""
@@ -87,6 +95,7 @@ class ReportKeys:
8795
commit_url = "commit_url"
8896
compilation_success = "compilation_success"
8997
sizes = "sizes"
98+
warnings = "warnings"
9099
name = "name"
91100
absolute = "absolute"
92101
relative = "relative"
@@ -106,7 +115,7 @@ class ReportKeys:
106115
latest_release_indicator = "latest"
107116

108117
def __init__(self, cli_version, fqbn_arg, platforms, libraries, sketch_paths, verbose, github_token,
109-
enable_size_deltas_report, sketches_report_path):
118+
enable_deltas_report, enable_warnings_report, sketches_report_path):
110119
"""Process, store, and validate the action's inputs."""
111120
self.cli_version = cli_version
112121

@@ -129,13 +138,19 @@ def __init__(self, cli_version, fqbn_arg, platforms, libraries, sketch_paths, ve
129138
else:
130139
self.github_api = github.Github(login_or_token=github_token)
131140

132-
self.enable_size_deltas_report = parse_boolean_input(boolean_input=enable_size_deltas_report)
133-
# The enable-size-deltas-report input has a default value so it should always be either True or False
134-
if self.enable_size_deltas_report is None:
135-
print("::error::Invalid value for enable-size-deltas-report input")
141+
self.enable_deltas_report = parse_boolean_input(boolean_input=enable_deltas_report)
142+
# The enable-deltas-report input has a default value so it should always be either True or False
143+
if self.enable_deltas_report is None:
144+
print("::error::Invalid value for enable-deltas-report input")
145+
sys.exit(1)
146+
147+
self.enable_warnings_report = parse_boolean_input(boolean_input=enable_warnings_report)
148+
# The enable-deltas-report input has a default value so it should always be either True or False
149+
if self.enable_warnings_report is None:
150+
print("::error::Invalid value for enable-warnings-report input")
136151
sys.exit(1)
137152

138-
if self.enable_size_deltas_report:
153+
if self.enable_deltas_report:
139154
self.deltas_base_ref = self.get_deltas_base_ref()
140155
else:
141156
# If deltas reports are not enabled, there is no use for the base ref and it could result in an GitHub API
@@ -185,7 +200,9 @@ def compile_sketches(self):
185200

186201
sketch_list = self.find_sketches()
187202
for sketch in sketch_list:
188-
compilation_result = self.compile_sketch(sketch_path=sketch)
203+
# It's necessary to clear the cache between each compilation to get a true compiler warning count, otherwise
204+
# only the first sketch compilation's warning count would reflect warnings from cached code
205+
compilation_result = self.compile_sketch(sketch_path=sketch, clean_build_cache=self.enable_warnings_report)
189206
if not compilation_result.success:
190207
all_compilations_successful = False
191208

@@ -845,17 +862,22 @@ def find_sketches(self):
845862

846863
return sketch_list
847864

848-
def compile_sketch(self, sketch_path):
865+
def compile_sketch(self, sketch_path, clean_build_cache):
849866
"""Compile the specified sketch and returns an object containing the result:
850867
sketch -- the sketch path relative to the workspace
851868
success -- the success of the compilation (True, False)
852869
output -- stdout from Arduino CLI
853870
854871
Keyword arguments:
855872
sketch_path -- path of the sketch to compile
873+
clean_build_cache -- whether to delete cached compiled from previous compilations before compiling
856874
"""
857875
compilation_command = ["compile", "--warnings", "all", "--fqbn", self.fqbn, sketch_path]
858876

877+
if clean_build_cache:
878+
for cache_path in pathlib.Path("/tmp").glob(pattern="arduino*"):
879+
shutil.rmtree(path=cache_path)
880+
859881
compilation_data = self.run_arduino_cli_command(
860882
command=compilation_command, enable_output=self.RunCommandOutput.NONE, exit_on_failure=False)
861883
# Group compilation output to make the log easy to read
@@ -881,8 +903,15 @@ def get_sketch_report(self, compilation_result):
881903
compilation_result -- object returned by compile_sketch()
882904
"""
883905
current_sizes = self.get_sizes_from_output(compilation_result=compilation_result)
906+
if self.enable_warnings_report:
907+
current_warning_count = self.get_warning_count_from_output(compilation_result=compilation_result)
908+
else:
909+
current_warning_count = None
884910
previous_sizes = None
885-
if self.do_size_deltas_report(compilation_result=compilation_result, current_sizes=current_sizes):
911+
previous_warning_count = None
912+
if self.do_deltas_report(compilation_result=compilation_result,
913+
current_sizes=current_sizes,
914+
current_warnings=current_warning_count):
886915
# Get data for the sketch at the base ref
887916
# Get the head ref
888917
repository = git.Repo(path=os.environ["GITHUB_WORKSPACE"])
@@ -893,20 +922,30 @@ def get_sketch_report(self, compilation_result):
893922

894923
# Compile the sketch again
895924
print("Compiling previous version of sketch to determine memory usage change")
896-
previous_compilation_result = self.compile_sketch(sketch_path=compilation_result.sketch)
925+
previous_compilation_result = self.compile_sketch(sketch_path=compilation_result.sketch,
926+
clean_build_cache=self.enable_warnings_report)
897927

898928
# git checkout the head ref to return the repository to its previous state
899929
repository.git.checkout(original_git_ref, recurse_submodules=True)
900930

901931
previous_sizes = self.get_sizes_from_output(compilation_result=previous_compilation_result)
932+
if self.enable_warnings_report:
933+
previous_warning_count = (
934+
self.get_warning_count_from_output(compilation_result=previous_compilation_result)
935+
)
902936

903937
# Add global data for sketch to report
904938
sketch_report = {
905939
self.ReportKeys.name: str(path_relative_to_workspace(path=compilation_result.sketch)),
906940
self.ReportKeys.compilation_success: compilation_result.success,
907941
self.ReportKeys.sizes: self.get_sizes_report(current_sizes=current_sizes,
908-
previous_sizes=previous_sizes)
942+
previous_sizes=previous_sizes),
909943
}
944+
if self.enable_warnings_report:
945+
sketch_report[self.ReportKeys.warnings] = (
946+
self.get_warnings_report(current_warnings=current_warning_count,
947+
previous_warnings=previous_warning_count)
948+
)
910949

911950
return sketch_report
912951

@@ -1003,18 +1042,40 @@ def get_size_data_from_output(self, compilation_output, memory_type, size_data_t
10031042

10041043
return size_data
10051044

1006-
def do_size_deltas_report(self, compilation_result, current_sizes):
1045+
def get_warning_count_from_output(self, compilation_result):
1046+
"""Parse the stdout from the compilation process and return the number of compiler warnings. Since the
1047+
information is likely not relevant in that case, "N/A" is returned if compilation failed.
1048+
1049+
Keyword arguments:
1050+
compilation_result -- object returned by compile_sketch()
1051+
"""
1052+
if compilation_result.success is True:
1053+
compiler_warning_regex = ":[0-9]+:[0-9]+: warning:"
1054+
warning_count = len(re.findall(pattern=compiler_warning_regex, string=compilation_result.output))
1055+
else:
1056+
warning_count = self.not_applicable_indicator
1057+
1058+
return warning_count
1059+
1060+
def do_deltas_report(self, compilation_result, current_sizes, current_warnings):
10071061
"""Return whether size deltas reporting is enabled.
10081062
10091063
Keyword arguments:
10101064
compilation_result -- object returned by compile_sketch()
10111065
current_sizes -- memory usage data from the compilation
1066+
current_warnings -- compiler warning count
10121067
"""
10131068
return (
1014-
self.enable_size_deltas_report
1069+
self.enable_deltas_report
10151070
and compilation_result.success
1016-
and any(size.get(self.ReportKeys.absolute) != self.not_applicable_indicator for
1071+
and (
1072+
any(size.get(self.ReportKeys.absolute) != self.not_applicable_indicator for
10171073
size in current_sizes)
1074+
or (
1075+
current_warnings is not None
1076+
and current_warnings != self.not_applicable_indicator
1077+
)
1078+
)
10181079
)
10191080

10201081
def checkout_deltas_base_ref(self):
@@ -1107,6 +1168,42 @@ def get_size_report(self, current_size, previous_size):
11071168

11081169
return size_report
11091170

1171+
def get_warnings_report(self, current_warnings, previous_warnings):
1172+
"""Return a dictionary containing the compiler warning counts.
1173+
1174+
Keyword arguments:
1175+
current_warnings -- compiler warning count at the head ref
1176+
previous_warnings -- compiler warning count at the base ref, or None if the size deltas feature is not enabled
1177+
"""
1178+
warnings_report = {
1179+
self.ReportKeys.current: {
1180+
self.ReportKeys.absolute: current_warnings,
1181+
}
1182+
}
1183+
1184+
if previous_warnings is not None:
1185+
# Deltas reports are enabled
1186+
# Calculate the change in the warnings count
1187+
if (
1188+
current_warnings == self.not_applicable_indicator
1189+
or previous_warnings == self.not_applicable_indicator
1190+
):
1191+
warnings_delta = self.not_applicable_indicator
1192+
else:
1193+
warnings_delta = current_warnings - previous_warnings
1194+
1195+
# Print the warning count change to the log
1196+
print("Change in compiler warning count:", warnings_delta)
1197+
1198+
warnings_report[self.ReportKeys.previous] = {
1199+
self.ReportKeys.absolute: previous_warnings
1200+
}
1201+
warnings_report[self.ReportKeys.delta] = {
1202+
self.ReportKeys.absolute: warnings_delta
1203+
}
1204+
1205+
return warnings_report
1206+
11101207
def get_sketches_report(self, sketch_report_list):
11111208
"""Return the dictionary containing data on all sketch compilations for each board
11121209
@@ -1136,6 +1233,10 @@ def get_sketches_report(self, sketch_report_list):
11361233
if sizes_summary_report:
11371234
sketches_report[self.ReportKeys.boards][0][self.ReportKeys.sizes] = sizes_summary_report
11381235

1236+
warnings_summary_report = self.get_warnings_summary_report(sketch_report_list=sketch_report_list)
1237+
if warnings_summary_report:
1238+
sketches_report[self.ReportKeys.boards][0][self.ReportKeys.warnings] = warnings_summary_report
1239+
11391240
return sketches_report
11401241

11411242
def get_sizes_summary_report(self, sketch_report_list):
@@ -1236,6 +1337,51 @@ def get_sizes_summary_report(self, sketch_report_list):
12361337

12371338
return sizes_summary_report
12381339

1340+
def get_warnings_summary_report(self, sketch_report_list):
1341+
"""Return a dictionary containing a summary of the compilation warnings count for all sketch compilations.
1342+
1343+
Keyword arguments:
1344+
sketch_report_list -- list of reports from each sketch compilation
1345+
"""
1346+
summary_report_minimum = None
1347+
summary_report_maximum = None
1348+
for sketch_report in sketch_report_list:
1349+
if (
1350+
self.ReportKeys.warnings in sketch_report
1351+
and self.ReportKeys.delta in sketch_report[self.ReportKeys.warnings]
1352+
):
1353+
sketch_report_delta = (
1354+
sketch_report[self.ReportKeys.warnings][self.ReportKeys.delta][self.ReportKeys.absolute]
1355+
)
1356+
1357+
if (
1358+
summary_report_minimum is None
1359+
or summary_report_minimum == self.not_applicable_indicator
1360+
or summary_report_minimum > sketch_report_delta
1361+
):
1362+
summary_report_minimum = sketch_report_delta
1363+
1364+
if (
1365+
summary_report_maximum is None
1366+
or summary_report_maximum == self.not_applicable_indicator
1367+
or summary_report_maximum < sketch_report_delta
1368+
):
1369+
summary_report_maximum = sketch_report_delta
1370+
1371+
if summary_report_minimum is not None:
1372+
warnings_summary_report = {
1373+
self.ReportKeys.delta: {
1374+
self.ReportKeys.absolute: {
1375+
self.ReportKeys.minimum: summary_report_minimum,
1376+
self.ReportKeys.maximum: summary_report_maximum
1377+
}
1378+
}
1379+
}
1380+
else:
1381+
warnings_summary_report = {}
1382+
1383+
return warnings_summary_report
1384+
12391385
def create_sketches_report_file(self, sketches_report):
12401386
"""Write the report for the report sketch to a file.
12411387

0 commit comments

Comments
 (0)