@@ -71,6 +71,7 @@ class RunCommandOutput(enum.Enum):
71
71
ALWAYS = enum .auto ()
72
72
73
73
not_applicable_indicator = "N/A"
74
+ relative_size_report_decimal_places = 2
74
75
75
76
arduino_cli_installation_path = pathlib .Path .home ().joinpath ("bin" )
76
77
arduino_cli_user_directory_path = pathlib .Path .home ().joinpath ("Arduino" )
@@ -88,6 +89,7 @@ class ReportKeys:
88
89
sizes = "sizes"
89
90
name = "name"
90
91
absolute = "absolute"
92
+ relative = "relative"
91
93
current = "current"
92
94
previous = "previous"
93
95
delta = "delta"
@@ -837,11 +839,24 @@ def get_sizes_from_output(self, compilation_result):
837
839
memory_types = [
838
840
{
839
841
"name" : "flash" ,
840
- "regex" : r"Sketch uses [0-9]+ bytes .*of program storage space\."
842
+ # Use capturing parentheses to identify the location of the data in the regular expression
843
+ "regex" : {
844
+ # The regular expression for the absolute memory usage
845
+ self .ReportKeys .absolute : r"Sketch uses ([0-9]+) bytes .*of program storage space\." ,
846
+ # The regular expression for the total memory
847
+ self .ReportKeys .maximum : (
848
+ r"Sketch uses [0-9]+ bytes .*of program storage space\. Maximum is ([0-9]+) bytes."
849
+ )
850
+ }
841
851
},
842
852
{
843
853
"name" : "RAM for global variables" ,
844
- "regex" : r"Global variables use [0-9]+ bytes .*of dynamic memory"
854
+ "regex" : {
855
+ self .ReportKeys .absolute : r"Global variables use ([0-9]+) bytes .*of dynamic memory" ,
856
+ self .ReportKeys .maximum : (
857
+ r"Global variables use [0-9]+ bytes .*of dynamic memory.*\. Maximum is ([0-9]+) bytes."
858
+ )
859
+ }
845
860
}
846
861
]
847
862
@@ -850,35 +865,64 @@ def get_sizes_from_output(self, compilation_result):
850
865
size = {
851
866
self .ReportKeys .name : memory_type ["name" ],
852
867
# Set default memory usage value, to be used if memory usage can't be determined
853
- self .ReportKeys .absolute : self .not_applicable_indicator
868
+ self .ReportKeys .absolute : self .not_applicable_indicator ,
869
+ self .ReportKeys .maximum : self .not_applicable_indicator ,
870
+ self .ReportKeys .relative : self .not_applicable_indicator
854
871
}
855
872
856
873
if compilation_result .success is True :
857
874
# Determine memory usage of the sketch by parsing Arduino CLI's output
858
- regex_match = re .search (pattern = memory_type ["regex" ], string = compilation_result .output )
859
- if regex_match :
860
- size [self .ReportKeys .absolute ] = int (
861
- re .search (pattern = "[0-9]+" , string = regex_match .group (0 )).group (0 ))
862
- else :
863
- # If any of the following:
864
- # - recipe.size.regex is not defined in platform.txt
865
- # - upload.maximum_size is not defined in boards.txt
866
- # flash usage will not be reported in the Arduino CLI output
867
- # If any of the following:
868
- # - recipe.size.regex.data is not defined in platform.txt (e.g., Arduino SAM Boards)
869
- # - recipe.size.regex is not defined in platform.txt
870
- # - upload.maximum_size is not defined in boards.txt
871
- # RAM usage will not be reported in the Arduino CLI output
872
- self .verbose_print (
873
- "::warning::Unable to determine" ,
874
- memory_type ["name" ],
875
- "memory usage. The board's platform may not have been configured to provide this information."
876
- )
875
+ size_data = self .get_size_data_from_output (compilation_output = compilation_result .output ,
876
+ memory_type = memory_type ,
877
+ size_data_type = self .ReportKeys .absolute )
878
+ if size_data :
879
+ size [self .ReportKeys .absolute ] = size_data
880
+
881
+ size_data = self .get_size_data_from_output (compilation_output = compilation_result .output ,
882
+ memory_type = memory_type ,
883
+ size_data_type = self .ReportKeys .maximum )
884
+ if size_data :
885
+ size [self .ReportKeys .maximum ] = size_data
886
+
887
+ size [self .ReportKeys .relative ] = round (
888
+ (100 * size [self .ReportKeys .absolute ] / size [self .ReportKeys .maximum ]),
889
+ self .relative_size_report_decimal_places
890
+ )
877
891
878
892
sizes .append (size )
879
893
880
894
return sizes
881
895
896
+ def get_size_data_from_output (self , compilation_output , memory_type , size_data_type ):
897
+ """Parse the stdout from the compilation process for a specific datum and return it, or None if not found.
898
+
899
+ Keyword arguments:
900
+ compilation_output -- stdout from the compilation process
901
+ memory_type -- dictionary defining a memory type
902
+ size_data_type -- the type of size data to get
903
+ """
904
+ size_data = None
905
+ regex_match = re .search (pattern = memory_type ["regex" ][size_data_type ], string = compilation_output )
906
+ if regex_match :
907
+ size_data = int (regex_match .group (1 ))
908
+ else :
909
+ # If any of the following:
910
+ # - recipe.size.regex is not defined in platform.txt
911
+ # - upload.maximum_size is not defined in boards.txt
912
+ # flash usage will not be reported in the Arduino CLI output
913
+ # If any of the following:
914
+ # - recipe.size.regex.data is not defined in platform.txt (e.g., Arduino SAM Boards)
915
+ # - recipe.size.regex is not defined in platform.txt
916
+ # - upload.maximum_size is not defined in boards.txt
917
+ # RAM usage will not be reported in the Arduino CLI output
918
+ self .verbose_print (
919
+ "::warning::Unable to determine the: \" " + size_data_type + "\" value for memory type: \" "
920
+ + memory_type ["name" ]
921
+ + "\" . The board's platform may not have been configured to provide this information."
922
+ )
923
+
924
+ return size_data
925
+
882
926
def do_size_deltas_report (self , compilation_result , current_sizes ):
883
927
"""Return whether size deltas reporting is enabled.
884
928
@@ -934,8 +978,10 @@ def get_size_report(self, current_size, previous_size):
934
978
"""
935
979
size_report = {
936
980
self .ReportKeys .name : current_size [self .ReportKeys .name ],
981
+ self .ReportKeys .maximum : current_size [self .ReportKeys .maximum ],
937
982
self .ReportKeys .current : {
938
- self .ReportKeys .absolute : current_size [self .ReportKeys .absolute ]
983
+ self .ReportKeys .absolute : current_size [self .ReportKeys .absolute ],
984
+ self .ReportKeys .relative : current_size [self .ReportKeys .relative ]
939
985
}
940
986
}
941
987
@@ -949,15 +995,30 @@ def get_size_report(self, current_size, previous_size):
949
995
else :
950
996
absolute_delta = (current_size [self .ReportKeys .absolute ] - previous_size [self .ReportKeys .absolute ])
951
997
998
+ if (
999
+ absolute_delta == self .not_applicable_indicator
1000
+ or size_report [self .ReportKeys .maximum ] == self .not_applicable_indicator
1001
+ ):
1002
+ relative_delta = self .not_applicable_indicator
1003
+ else :
1004
+ # Calculate from absolute values to avoid rounding errors
1005
+ relative_delta = round ((100 * absolute_delta / size_report [self .ReportKeys .maximum ]),
1006
+ self .relative_size_report_decimal_places )
1007
+
952
1008
# Size deltas reports are enabled
953
1009
# Print the memory usage change data to the log
954
- print ("Change in" , current_size [self .ReportKeys .name ] + ":" , absolute_delta )
1010
+ delta_message = "Change in " + str (current_size [self .ReportKeys .name ]) + ": " + str (absolute_delta )
1011
+ if relative_delta != self .not_applicable_indicator :
1012
+ delta_message += " (" + str (relative_delta ) + "%)"
1013
+ print (delta_message )
955
1014
956
1015
size_report [self .ReportKeys .previous ] = {
957
- self .ReportKeys .absolute : previous_size [self .ReportKeys .absolute ]
1016
+ self .ReportKeys .absolute : previous_size [self .ReportKeys .absolute ],
1017
+ self .ReportKeys .relative : previous_size [self .ReportKeys .relative ]
958
1018
}
959
1019
size_report [self .ReportKeys .delta ] = {
960
- self .ReportKeys .absolute : absolute_delta
1020
+ self .ReportKeys .absolute : absolute_delta ,
1021
+ self .ReportKeys .relative : relative_delta
961
1022
}
962
1023
963
1024
return size_report
@@ -1013,19 +1074,33 @@ def get_sizes_summary_report(self, sketch_report_list):
1013
1074
sizes_summary_report .append (
1014
1075
{
1015
1076
self .ReportKeys .name : size_report [self .ReportKeys .name ],
1077
+ self .ReportKeys .maximum : size_report [self .ReportKeys .maximum ],
1016
1078
self .ReportKeys .delta : {
1017
1079
self .ReportKeys .absolute : {
1018
1080
self .ReportKeys .minimum : size_report [self .ReportKeys .delta ][
1019
1081
self .ReportKeys .absolute ],
1020
1082
self .ReportKeys .maximum : size_report [self .ReportKeys .delta ][
1021
1083
self .ReportKeys .absolute ]
1022
- }
1084
+ },
1085
+ self .ReportKeys .relative : {
1086
+ self .ReportKeys .minimum : size_report [self .ReportKeys .delta ][
1087
+ self .ReportKeys .relative ],
1088
+ self .ReportKeys .maximum : size_report [self .ReportKeys .delta ][
1089
+ self .ReportKeys .relative ]
1090
+ },
1023
1091
}
1024
1092
}
1025
1093
)
1026
1094
else :
1027
1095
size_summary_report_index = size_summary_report_index_list [0 ]
1028
1096
1097
+ if (
1098
+ sizes_summary_report [size_summary_report_index ][
1099
+ self .ReportKeys .maximum ] == self .not_applicable_indicator
1100
+ ):
1101
+ sizes_summary_report [size_summary_report_index ][
1102
+ self .ReportKeys .maximum ] = size_report [self .ReportKeys .maximum ]
1103
+
1029
1104
if (
1030
1105
sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1031
1106
self .ReportKeys .absolute ][self .ReportKeys .minimum ] == self .not_applicable_indicator
@@ -1034,10 +1109,18 @@ def get_sizes_summary_report(self, sketch_report_list):
1034
1109
self .ReportKeys .absolute ][self .ReportKeys .minimum ] = size_report [self .ReportKeys .delta ][
1035
1110
self .ReportKeys .absolute ]
1036
1111
1112
+ sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1113
+ self .ReportKeys .relative ][self .ReportKeys .minimum ] = size_report [self .ReportKeys .delta ][
1114
+ self .ReportKeys .relative ]
1115
+
1037
1116
sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1038
1117
self .ReportKeys .absolute ][self .ReportKeys .maximum ] = size_report [self .ReportKeys .delta ][
1039
1118
self .ReportKeys .absolute ]
1040
1119
1120
+ sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1121
+ self .ReportKeys .relative ][self .ReportKeys .maximum ] = size_report [self .ReportKeys .delta ][
1122
+ self .ReportKeys .relative ]
1123
+
1041
1124
elif size_report [self .ReportKeys .delta ][self .ReportKeys .absolute ] != (
1042
1125
self .not_applicable_indicator
1043
1126
):
@@ -1049,6 +1132,11 @@ def get_sizes_summary_report(self, sketch_report_list):
1049
1132
size_report [self .ReportKeys .delta ][self .ReportKeys .absolute ]
1050
1133
)
1051
1134
1135
+ sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1136
+ self .ReportKeys .relative ][self .ReportKeys .minimum ] = (
1137
+ size_report [self .ReportKeys .delta ][self .ReportKeys .relative ]
1138
+ )
1139
+
1052
1140
if (size_report [self .ReportKeys .delta ][self .ReportKeys .absolute ]
1053
1141
> sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1054
1142
self .ReportKeys .absolute ][self .ReportKeys .maximum ]):
@@ -1057,6 +1145,11 @@ def get_sizes_summary_report(self, sketch_report_list):
1057
1145
size_report [self .ReportKeys .delta ][self .ReportKeys .absolute ]
1058
1146
)
1059
1147
1148
+ sizes_summary_report [size_summary_report_index ][self .ReportKeys .delta ][
1149
+ self .ReportKeys .relative ][self .ReportKeys .maximum ] = (
1150
+ size_report [self .ReportKeys .delta ][self .ReportKeys .relative ]
1151
+ )
1152
+
1060
1153
return sizes_summary_report
1061
1154
1062
1155
def create_sketches_report_file (self , sketches_report ):
0 commit comments