Skip to content

Commit 0802e27

Browse files
committed
Move size calculation from Java IDE
1 parent a07ea16 commit 0802e27

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

Diff for: src/arduino.cc/builder/builder.go

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ func (s *Builder) Run(ctx *types.Context) error {
116116
&RecipeByPrefixSuffixRunner{Prefix: "recipe.objcopy.", Suffix: constants.HOOKS_PATTERN_SUFFIX},
117117
&RecipeByPrefixSuffixRunner{Prefix: constants.HOOKS_OBJCOPY_POSTOBJCOPY, Suffix: constants.HOOKS_PATTERN_SUFFIX},
118118

119+
&phases.Sizer{},
120+
119121
&MergeSketchWithBootloader{},
120122

121123
&RecipeByPrefixSuffixRunner{Prefix: constants.HOOKS_POSTBUILD, Suffix: constants.HOOKS_PATTERN_SUFFIX},

Diff for: src/arduino.cc/builder/constants/constants.go

+14
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,13 @@ const MSG_PROP_IN_LIBRARY = "Missing '{0}' from library in {1}"
170170
const MSG_RUNNING_COMMAND = "Ts: {0} - Running: {1}"
171171
const MSG_RUNNING_RECIPE = "Running recipe: {0}"
172172
const MSG_SETTING_BUILD_PATH = "Setting build path to {0}"
173+
const MSG_SIZER_TEXT_FULL = "Sketch uses {0} bytes ({2}%%) of program storage space. Maximum is {1} bytes."
174+
const MSG_SIZER_DATA_FULL = "Global variables use {0} bytes ({2}%%) of dynamic memory, leaving {3} bytes for local variables. Maximum is {1} bytes."
175+
const MSG_SIZER_DATA = "Global variables use {0} bytes of dynamic memory."
176+
const MSG_SIZER_TEXT_TOO_BIG = "Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it."
177+
const MSG_SIZER_DATA_TOO_BIG = "Not enough memory; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing your footprint."
178+
const MSG_SIZER_LOW_MEMORY = "Low memory available, stability problems may occur."
179+
const MSG_SIZER_ERROR_NO_RULE = "Couldn't determine program size"
173180
const MSG_SKETCH_CANT_BE_IN_BUILDPATH = "Sketch cannot be located in build path. Please specify a different build path"
174181
const MSG_SKIPPING_TAG_ALREADY_DEFINED = "Skipping tag {0} because prototype is already defined"
175182
const MSG_SKIPPING_TAG_BECAUSE_HAS_FIELD = "Skipping tag {0} because it has field {0}"
@@ -197,14 +204,21 @@ const PLATFORM_REWRITE_NEW = "new"
197204
const PLATFORM_REWRITE_OLD = "old"
198205
const PLATFORM_URL = "url"
199206
const PLATFORM_VERSION = "version"
207+
const PROPERTY_WARN_DATA_PERCENT = "build.warn_data_percentage"
208+
const PROPERTY_UPLOAD_MAX_SIZE = "upload.maximum_size"
209+
const PROPERTY_UPLOAD_MAX_DATA_SIZE = "upload.maximum_data_size"
200210
const PROGRAMMER_NAME = "name"
201211
const RECIPE_AR_PATTERN = "recipe.ar.pattern"
202212
const RECIPE_C_COMBINE_PATTERN = "recipe.c.combine.pattern"
203213
const RECIPE_C_PATTERN = "recipe.c.o.pattern"
204214
const RECIPE_CPP_PATTERN = "recipe.cpp.o.pattern"
215+
const RECIPE_SIZE_PATTERN = "recipe.size.pattern"
205216
const RECIPE_PREPROC_INCLUDES = "recipe.preproc.includes"
206217
const RECIPE_PREPROC_MACROS = "recipe.preproc.macros"
207218
const RECIPE_S_PATTERN = "recipe.S.o.pattern"
219+
const RECIPE_SIZE_REGEXP = "recipe.size.regex"
220+
const RECIPE_SIZE_REGEXP_DATA = "recipe.size.regex.data"
221+
const RECIPE_SIZE_REGEXP_EEPROM = "recipe.size.regex.eeprom"
208222
const REWRITING_DISABLED = "disabled"
209223
const REWRITING = "rewriting"
210224
const SPACE = " "

Diff for: src/arduino.cc/builder/phases/sizer.go

+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
/*
2+
* This file is part of Arduino Builder.
3+
*
4+
* Arduino Builder is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2016 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package phases
31+
32+
import (
33+
"errors"
34+
"regexp"
35+
"strconv"
36+
37+
"arduino.cc/builder/builder_utils"
38+
"arduino.cc/builder/constants"
39+
"arduino.cc/builder/i18n"
40+
"arduino.cc/builder/types"
41+
"arduino.cc/properties"
42+
)
43+
44+
type Sizer struct{}
45+
46+
func (s *Sizer) Run(ctx *types.Context) error {
47+
buildProperties := ctx.BuildProperties
48+
verbose := ctx.Verbose
49+
warningsLevel := ctx.WarningsLevel
50+
logger := ctx.GetLogger()
51+
52+
err := checkSize(buildProperties, verbose, warningsLevel, logger)
53+
if err != nil {
54+
return i18n.WrapError(err)
55+
}
56+
57+
return nil
58+
}
59+
60+
func checkSize(buildProperties properties.Map, verbose bool, warningsLevel string, logger i18n.Logger) error {
61+
62+
properties := buildProperties.Clone()
63+
properties[constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS] = properties[constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS+"."+warningsLevel]
64+
65+
maxTextSizeString := properties[constants.PROPERTY_UPLOAD_MAX_SIZE]
66+
maxDataSizeString := properties[constants.PROPERTY_UPLOAD_MAX_DATA_SIZE]
67+
68+
if maxTextSizeString == "" {
69+
return nil
70+
}
71+
72+
maxTextSize, err := strconv.Atoi(maxTextSizeString)
73+
if err != nil {
74+
return err
75+
}
76+
77+
maxDataSize := -1
78+
if maxDataSizeString != "" {
79+
maxDataSize, err = strconv.Atoi(maxDataSizeString)
80+
if err != nil {
81+
return err
82+
}
83+
}
84+
85+
textSize, dataSize, _, err := execSizeReceipe(properties, logger)
86+
if err != nil {
87+
logger.Println(constants.LOG_LEVEL_WARN, constants.MSG_SIZER_ERROR_NO_RULE)
88+
return nil
89+
}
90+
91+
logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_SIZER_TEXT_FULL, strconv.Itoa(textSize), strconv.Itoa(maxTextSize), strconv.Itoa(textSize*100/maxTextSize))
92+
if dataSize >= 0 {
93+
if maxDataSize > 0 {
94+
logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_SIZER_DATA_FULL, strconv.Itoa(dataSize), strconv.Itoa(maxDataSize), strconv.Itoa(dataSize*100/maxDataSize), strconv.Itoa(maxDataSize-dataSize))
95+
} else {
96+
logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_SIZER_DATA, strconv.Itoa(dataSize))
97+
}
98+
}
99+
100+
if textSize > maxTextSize {
101+
logger.Println(constants.LOG_LEVEL_ERROR, constants.MSG_SIZER_TEXT_TOO_BIG)
102+
return errors.New("")
103+
}
104+
105+
if maxDataSize > 0 && dataSize > maxDataSize {
106+
logger.Println(constants.LOG_LEVEL_ERROR, constants.MSG_SIZER_DATA_TOO_BIG)
107+
return errors.New("")
108+
}
109+
110+
if properties[constants.PROPERTY_WARN_DATA_PERCENT] != "" {
111+
warnDataPercentage, err := strconv.Atoi(properties[constants.PROPERTY_WARN_DATA_PERCENT])
112+
if err != nil {
113+
return err
114+
}
115+
if maxDataSize > 0 && dataSize > maxDataSize*warnDataPercentage/100 {
116+
logger.Println(constants.LOG_LEVEL_WARN, constants.MSG_SIZER_LOW_MEMORY)
117+
}
118+
}
119+
120+
return nil
121+
}
122+
123+
func execSizeReceipe(properties properties.Map, logger i18n.Logger) (textSize int, dataSize int, eepromSize int, resErr error) {
124+
out, err := builder_utils.ExecRecipe(properties, constants.RECIPE_SIZE_PATTERN, false, false, false, logger)
125+
if err != nil {
126+
resErr = errors.New("Error while determining sketch size: " + err.Error())
127+
return
128+
}
129+
130+
// force multiline match prepending "(?m)" to the actual regexp
131+
// return an error if RECIPE_SIZE_REGEXP doesn't exist
132+
133+
if len(properties[constants.RECIPE_SIZE_REGEXP]) > 0 {
134+
textRegexp, err := regexp.Compile("(?m)" + properties[constants.RECIPE_SIZE_REGEXP])
135+
if err != nil {
136+
resErr = errors.New("Invalid size regexp: " + err.Error())
137+
return
138+
}
139+
result := textRegexp.FindAllSubmatch(out, -1)
140+
for _, b := range result {
141+
for _, c := range b {
142+
if res, err := strconv.Atoi(string(c)); err == nil {
143+
textSize += res
144+
}
145+
}
146+
}
147+
} else {
148+
resErr = errors.New("Missing size regexp")
149+
return
150+
}
151+
152+
if len(properties[constants.RECIPE_SIZE_REGEXP_DATA]) > 0 {
153+
dataRegexp, err := regexp.Compile("(?m)" + properties[constants.RECIPE_SIZE_REGEXP_DATA])
154+
if err != nil {
155+
resErr = errors.New("Invalid data size regexp: " + err.Error())
156+
return
157+
}
158+
result := dataRegexp.FindAllSubmatch(out, -1)
159+
for _, b := range result {
160+
for _, c := range b {
161+
if res, err := strconv.Atoi(string(c)); err == nil {
162+
dataSize += res
163+
}
164+
}
165+
}
166+
} else {
167+
dataSize = -1
168+
}
169+
170+
if len(properties[constants.RECIPE_SIZE_REGEXP_EEPROM]) > 0 {
171+
eepromRegexp, err := regexp.Compile("(?m)" + properties[constants.RECIPE_SIZE_REGEXP_EEPROM])
172+
if err != nil {
173+
resErr = errors.New("Invalid eeprom size regexp: " + err.Error())
174+
return
175+
}
176+
result := eepromRegexp.FindAllSubmatch(out, -1)
177+
for _, b := range result {
178+
for _, c := range b {
179+
if res, err := strconv.Atoi(string(c)); err == nil {
180+
eepromSize += res
181+
}
182+
}
183+
}
184+
} else {
185+
eepromSize = -1
186+
}
187+
return
188+
}

0 commit comments

Comments
 (0)