16
16
package phases
17
17
18
18
import (
19
+ "bytes"
19
20
"encoding/json"
20
21
"fmt"
22
+ "io"
21
23
"regexp"
22
24
"strconv"
23
25
26
+ "github.com/arduino/arduino-cli/arduino/builder"
24
27
"github.com/arduino/arduino-cli/arduino/builder/utils"
25
- "github.com/arduino/arduino-cli/legacy/builder/types"
26
28
"github.com/arduino/go-properties-orderedmap"
27
29
"github.com/pkg/errors"
28
30
)
29
31
30
- type Sizer struct {
31
- SketchError bool
32
- }
33
-
34
- func (s * Sizer ) Run (ctx * types.Context ) error {
35
- if ctx .OnlyUpdateCompilationDatabase {
36
- return nil
37
- }
38
- if s .SketchError {
39
- return nil
32
+ func Sizer (
33
+ sketchError , onlyUpdateCompilationDatabase bool ,
34
+ buildProperties * properties.Map ,
35
+ verbose bool ,
36
+ stdoutWriter , stderrWriter io.Writer ,
37
+ warningsLevel string ,
38
+ ) (builder.ExecutablesFileSections , []byte , []byte , []byte , error ) {
39
+ if onlyUpdateCompilationDatabase || sketchError {
40
+ return nil , nil , nil , nil , nil
40
41
}
41
42
42
- buildProperties := ctx .BuildProperties
43
-
44
43
if buildProperties .ContainsKey ("recipe.advanced_size.pattern" ) {
45
- return checkSizeAdvanced (ctx , buildProperties )
44
+ return checkSizeAdvanced (buildProperties , verbose , stdoutWriter , stderrWriter )
46
45
}
47
46
48
- return checkSize (ctx , buildProperties )
47
+ return checkSize (buildProperties , verbose , stdoutWriter , stderrWriter , warningsLevel )
49
48
}
50
49
51
- func checkSizeAdvanced (ctx * types.Context , properties * properties.Map ) error {
52
- command , err := utils .PrepareCommandForRecipe (properties , "recipe.advanced_size.pattern" , false )
50
+ func checkSizeAdvanced (
51
+ buildProperties * properties.Map ,
52
+ verbose bool ,
53
+ stdoutWriter , stderrWriter io.Writer ,
54
+ ) (builder.ExecutablesFileSections , []byte , []byte , []byte , error ) {
55
+ verboseInfoBuf , infoBuf , warnBuf := & bytes.Buffer {}, & bytes.Buffer {}, & bytes.Buffer {}
56
+ command , err := utils .PrepareCommandForRecipe (buildProperties , "recipe.advanced_size.pattern" , false )
53
57
if err != nil {
54
- return errors .New (tr ("Error while determining sketch size: %s" , err ))
58
+ return nil , nil , nil , nil , errors .New (tr ("Error while determining sketch size: %s" , err ))
55
59
}
56
60
57
- verboseInfo , out , _ , err := utils .ExecCommand (ctx . Verbose , ctx . Stdout , ctx . Stderr , command , utils .Capture /* stdout */ , utils .Show /* stderr */ )
58
- if ctx . Verbose {
59
- ctx . Info (string (verboseInfo ))
61
+ verboseInfo , out , _ , err := utils .ExecCommand (verbose , stdoutWriter , stderrWriter , command , utils .Capture /* stdout */ , utils .Show /* stderr */ )
62
+ if verbose {
63
+ verboseInfoBuf . WriteString (string (verboseInfo ))
60
64
}
61
65
if err != nil {
62
- return errors .New (tr ("Error while determining sketch size: %s" , err ))
66
+ return nil , verboseInfoBuf . Bytes (), nil , nil , errors .New (tr ("Error while determining sketch size: %s" , err ))
63
67
}
64
68
65
69
type AdvancedSizerResponse struct {
@@ -69,7 +73,7 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error {
69
73
// likely be printed in red. Errors will stop build/upload.
70
74
Severity string `json:"severity"`
71
75
// Sections are the sections sizes for machine readable use
72
- Sections []types .ExecutableSectionSize `json:"sections"`
76
+ Sections []builder .ExecutableSectionSize `json:"sections"`
73
77
// ErrorMessage is a one line error message like:
74
78
// "text section exceeds available space in board"
75
79
// it must be set when Severity is "error"
@@ -78,118 +82,126 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error {
78
82
79
83
var resp AdvancedSizerResponse
80
84
if err := json .Unmarshal (out , & resp ); err != nil {
81
- return errors .New (tr ("Error while determining sketch size: %s" , err ))
85
+ return nil , verboseInfoBuf . Bytes (), nil , nil , errors .New (tr ("Error while determining sketch size: %s" , err ))
82
86
}
83
-
84
- ctx .ExecutableSectionsSize = resp .Sections
87
+ executableSectionsSize := resp .Sections
85
88
switch resp .Severity {
86
89
case "error" :
87
- ctx . Warn (resp .Output )
88
- return errors .New (resp .ErrorMessage )
90
+ warnBuf . WriteString (resp .Output )
91
+ return executableSectionsSize , verboseInfoBuf . Bytes (), nil , warnBuf . Bytes (), errors .New (resp .ErrorMessage )
89
92
case "warning" :
90
- ctx . Warn (resp .Output )
93
+ warnBuf . WriteString (resp .Output )
91
94
case "info" :
92
- ctx . Info (resp .Output )
95
+ infoBuf . WriteString (resp .Output )
93
96
default :
94
- return fmt .Errorf ("invalid '%s' severity from sketch sizer: it must be 'error', 'warning' or 'info'" , resp .Severity )
97
+ return executableSectionsSize , verboseInfoBuf . Bytes (), nil , nil , fmt .Errorf ("invalid '%s' severity from sketch sizer: it must be 'error', 'warning' or 'info'" , resp .Severity )
95
98
}
96
- return nil
99
+ return executableSectionsSize , verboseInfoBuf . Bytes (), infoBuf . Bytes (), warnBuf . Bytes (), nil
97
100
}
98
101
99
- func checkSize (ctx * types.Context , buildProperties * properties.Map ) error {
102
+ func checkSize (
103
+ buildProperties * properties.Map ,
104
+ verbose bool ,
105
+ stdoutWriter , stderrWriter io.Writer ,
106
+ warningsLevel string ,
107
+ ) (builder.ExecutablesFileSections , []byte , []byte , []byte , error ) {
108
+ infoBuf , warnBuf := & bytes.Buffer {}, & bytes.Buffer {}
100
109
properties := buildProperties .Clone ()
101
- properties .Set ("compiler.warning_flags" , properties .Get ("compiler.warning_flags." + ctx . WarningsLevel ))
110
+ properties .Set ("compiler.warning_flags" , properties .Get ("compiler.warning_flags." + warningsLevel ))
102
111
103
112
maxTextSizeString := properties .Get ("upload.maximum_size" )
104
113
maxDataSizeString := properties .Get ("upload.maximum_data_size" )
105
114
106
115
if maxTextSizeString == "" {
107
- return nil
116
+ return nil , nil , nil , nil , nil
108
117
}
109
118
110
119
maxTextSize , err := strconv .Atoi (maxTextSizeString )
111
120
if err != nil {
112
- return err
121
+ return nil , nil , nil , nil , err
113
122
}
114
123
115
124
maxDataSize := - 1
116
125
if maxDataSizeString != "" {
117
126
maxDataSize , err = strconv .Atoi (maxDataSizeString )
118
127
if err != nil {
119
- return err
128
+ return nil , nil , nil , nil , err
120
129
}
121
130
}
122
131
123
- textSize , dataSize , _ , err := execSizeRecipe (ctx , properties )
132
+ textSize , dataSize , _ , verboseInfoOut , err := execSizeRecipe (properties , verbose , stdoutWriter , stderrWriter )
124
133
if err != nil {
125
- ctx . Warn (tr ("Couldn't determine program size" ))
126
- return nil
134
+ infoBuf . WriteString (tr ("Couldn't determine program size" ))
135
+ return nil , verboseInfoOut , infoBuf . Bytes (), nil , nil
127
136
}
128
137
129
- ctx . Info (tr ("Sketch uses %[1]s bytes (%[3]s%%) of program storage space. Maximum is %[2]s bytes." ,
138
+ infoBuf . WriteString (tr ("Sketch uses %[1]s bytes (%[3]s%%) of program storage space. Maximum is %[2]s bytes." ,
130
139
strconv .Itoa (textSize ),
131
140
strconv .Itoa (maxTextSize ),
132
141
strconv .Itoa (textSize * 100 / maxTextSize )))
133
142
if dataSize >= 0 {
134
143
if maxDataSize > 0 {
135
- ctx . Info (tr ("Global variables use %[1]s bytes (%[3]s%%) of dynamic memory, leaving %[4]s bytes for local variables. Maximum is %[2]s bytes." ,
144
+ infoBuf . WriteString (tr ("Global variables use %[1]s bytes (%[3]s%%) of dynamic memory, leaving %[4]s bytes for local variables. Maximum is %[2]s bytes." ,
136
145
strconv .Itoa (dataSize ),
137
146
strconv .Itoa (maxDataSize ),
138
147
strconv .Itoa (dataSize * 100 / maxDataSize ),
139
148
strconv .Itoa (maxDataSize - dataSize )))
140
149
} else {
141
- ctx . Info (tr ("Global variables use %[1]s bytes of dynamic memory." , strconv .Itoa (dataSize )))
150
+ infoBuf . WriteString (tr ("Global variables use %[1]s bytes of dynamic memory." , strconv .Itoa (dataSize )))
142
151
}
143
152
}
144
-
145
- ctx .ExecutableSectionsSize = []types.ExecutableSectionSize {
153
+ executableSectionsSize := []builder.ExecutableSectionSize {
146
154
{
147
155
Name : "text" ,
148
156
Size : textSize ,
149
157
MaxSize : maxTextSize ,
150
158
},
151
159
}
152
160
if maxDataSize > 0 {
153
- ctx . ExecutableSectionsSize = append (ctx . ExecutableSectionsSize , types .ExecutableSectionSize {
161
+ executableSectionsSize = append (executableSectionsSize , builder .ExecutableSectionSize {
154
162
Name : "data" ,
155
163
Size : dataSize ,
156
164
MaxSize : maxDataSize ,
157
165
})
158
166
}
159
167
160
168
if textSize > maxTextSize {
161
- ctx . Warn (tr ("Sketch too big; see %[1]s for tips on reducing it." , "https://support.arduino.cc/hc/en-us/articles/360013825179" ))
162
- return errors .New (tr ("text section exceeds available space in board" ))
169
+ warnBuf . WriteString (tr ("Sketch too big; see %[1]s for tips on reducing it." , "https://support.arduino.cc/hc/en-us/articles/360013825179" ))
170
+ return executableSectionsSize , verboseInfoOut , infoBuf . Bytes (), warnBuf . Bytes (), errors .New (tr ("text section exceeds available space in board" ))
163
171
}
164
172
165
173
if maxDataSize > 0 && dataSize > maxDataSize {
166
- ctx . Warn (tr ("Not enough memory; see %[1]s for tips on reducing your footprint." , "https://support.arduino.cc/hc/en-us/articles/360013825179" ))
167
- return errors .New (tr ("data section exceeds available space in board" ))
174
+ warnBuf . WriteString (tr ("Not enough memory; see %[1]s for tips on reducing your footprint." , "https://support.arduino.cc/hc/en-us/articles/360013825179" ))
175
+ return executableSectionsSize , verboseInfoOut , infoBuf . Bytes (), warnBuf . Bytes (), errors .New (tr ("data section exceeds available space in board" ))
168
176
}
169
177
170
178
if w := properties .Get ("build.warn_data_percentage" ); w != "" {
171
179
warnDataPercentage , err := strconv .Atoi (w )
172
180
if err != nil {
173
- return err
181
+ return executableSectionsSize , verboseInfoOut , infoBuf . Bytes (), warnBuf . Bytes (), err
174
182
}
175
183
if maxDataSize > 0 && dataSize > maxDataSize * warnDataPercentage / 100 {
176
- ctx . Warn (tr ("Low memory available, stability problems may occur." ))
184
+ warnBuf . WriteString (tr ("Low memory available, stability problems may occur." ))
177
185
}
178
186
}
179
187
180
- return nil
188
+ return executableSectionsSize , verboseInfoOut , infoBuf . Bytes (), warnBuf . Bytes (), nil
181
189
}
182
190
183
- func execSizeRecipe (ctx * types.Context , properties * properties.Map ) (textSize int , dataSize int , eepromSize int , resErr error ) {
184
- command , err := utils .PrepareCommandForRecipe (properties , "recipe.size.pattern" , false )
191
+ func execSizeRecipe (
192
+ buildProperties * properties.Map ,
193
+ verbose bool ,
194
+ stdoutWriter , stderrWriter io.Writer ,
195
+ ) (textSize int , dataSize int , eepromSize int , verboseInfoOut []byte , resErr error ) {
196
+ command , err := utils .PrepareCommandForRecipe (buildProperties , "recipe.size.pattern" , false )
185
197
if err != nil {
186
198
resErr = fmt .Errorf (tr ("Error while determining sketch size: %s" ), err )
187
199
return
188
200
}
189
201
190
- verboseInfo , out , _ , err := utils .ExecCommand (ctx . Verbose , ctx . Stdout , ctx . Stderr , command , utils .Capture /* stdout */ , utils .Show /* stderr */ )
191
- if ctx . Verbose {
192
- ctx . Info ( string ( verboseInfo ))
202
+ verboseInfo , out , _ , err := utils .ExecCommand (verbose , stdoutWriter , stderrWriter , command , utils .Capture /* stdout */ , utils .Show /* stderr */ )
203
+ if verbose {
204
+ verboseInfoOut = verboseInfo
193
205
}
194
206
if err != nil {
195
207
resErr = fmt .Errorf (tr ("Error while determining sketch size: %s" ), err )
@@ -199,7 +211,7 @@ func execSizeRecipe(ctx *types.Context, properties *properties.Map) (textSize in
199
211
// force multiline match prepending "(?m)" to the actual regexp
200
212
// return an error if RECIPE_SIZE_REGEXP doesn't exist
201
213
202
- textSize , err = computeSize (properties .Get ("recipe.size.regex" ), out )
214
+ textSize , err = computeSize (buildProperties .Get ("recipe.size.regex" ), out )
203
215
if err != nil {
204
216
resErr = fmt .Errorf (tr ("Invalid size regexp: %s" ), err )
205
217
return
@@ -209,13 +221,13 @@ func execSizeRecipe(ctx *types.Context, properties *properties.Map) (textSize in
209
221
return
210
222
}
211
223
212
- dataSize , err = computeSize (properties .Get ("recipe.size.regex.data" ), out )
224
+ dataSize , err = computeSize (buildProperties .Get ("recipe.size.regex.data" ), out )
213
225
if err != nil {
214
226
resErr = fmt .Errorf (tr ("Invalid data size regexp: %s" ), err )
215
227
return
216
228
}
217
229
218
- eepromSize , err = computeSize (properties .Get ("recipe.size.regex.eeprom" ), out )
230
+ eepromSize , err = computeSize (buildProperties .Get ("recipe.size.regex.eeprom" ), out )
219
231
if err != nil {
220
232
resErr = fmt .Errorf (tr ("Invalid eeprom size regexp: %s" ), err )
221
233
return
0 commit comments