Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6e8b3bd

Browse files
authoredJun 14, 2017
Merge branch 'master' into problematic_multiline
2 parents db95d14 + 4a09bad commit 6e8b3bd

27 files changed

+608
-231
lines changed
 

‎src/arduino.cc/arduino-builder/main.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import (
4949
"github.com/go-errors/errors"
5050
)
5151

52-
const VERSION = "1.3.22"
52+
const VERSION = "1.3.25"
5353

5454
const FLAG_ACTION_COMPILE = "compile"
5555
const FLAG_ACTION_PREPROCESS = "preprocess"
@@ -64,6 +64,7 @@ const FLAG_FQBN = "fqbn"
6464
const FLAG_IDE_VERSION = "ide-version"
6565
const FLAG_CORE_API_VERSION = "core-api-version"
6666
const FLAG_BUILD_PATH = "build-path"
67+
const FLAG_BUILD_CACHE = "build-cache"
6768
const FLAG_VERBOSE = "verbose"
6869
const FLAG_QUIET = "quiet"
6970
const FLAG_DEBUG_LEVEL = "debug-level"
@@ -126,6 +127,7 @@ var fqbnFlag *string
126127
var coreAPIVersionFlag *string
127128
var ideVersionFlag *string
128129
var buildPathFlag *string
130+
var buildCachePathFlag *string
129131
var verboseFlag *bool
130132
var quietFlag *bool
131133
var debugLevelFlag *int
@@ -148,6 +150,7 @@ func init() {
148150
coreAPIVersionFlag = flag.String(FLAG_CORE_API_VERSION, "10600", "version of core APIs (used to populate ARDUINO #define)")
149151
ideVersionFlag = flag.String(FLAG_IDE_VERSION, "10600", "[deprecated] use '"+FLAG_CORE_API_VERSION+"' instead")
150152
buildPathFlag = flag.String(FLAG_BUILD_PATH, "", "build path")
153+
buildCachePathFlag = flag.String(FLAG_BUILD_CACHE, "", "builds of 'core.a' are saved into this folder to be cached and reused")
151154
verboseFlag = flag.Bool(FLAG_VERBOSE, false, "if 'true' prints lots of stuff")
152155
quietFlag = flag.Bool(FLAG_QUIET, false, "if 'true' doesn't print any warnings or progress or whatever")
153156
debugLevelFlag = flag.Int(FLAG_DEBUG_LEVEL, builder.DEFAULT_DEBUG_LEVEL, "Turns on debugging messages. The higher, the chattier")
@@ -256,6 +259,25 @@ func main() {
256259
}
257260
ctx.BuildPath = buildPath
258261

262+
// FLAG_BUILD_CACHE
263+
buildCachePath, err := gohasissues.Unquote(*buildCachePathFlag)
264+
if err != nil {
265+
printCompleteError(err)
266+
}
267+
if buildCachePath != "" {
268+
_, err := os.Stat(buildCachePath)
269+
if err != nil {
270+
fmt.Fprintln(os.Stderr, err)
271+
os.Exit(1)
272+
}
273+
274+
err = utils.EnsureFolderExists(buildCachePath)
275+
if err != nil {
276+
printCompleteError(err)
277+
}
278+
}
279+
ctx.BuildCachePath = buildCachePath
280+
259281
// FLAG_VID_PID
260282
if *vidPidFlag != "" {
261283
ctx.USBVidPid = *vidPidFlag

‎src/arduino.cc/builder/add_additional_entries_to_context.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@
3030
package builder
3131

3232
import (
33+
"path/filepath"
34+
3335
"arduino.cc/builder/constants"
3436
"arduino.cc/builder/i18n"
3537
"arduino.cc/builder/types"
36-
"path/filepath"
3738
)
3839

3940
type AddAdditionalEntriesToContext struct{}
@@ -64,6 +65,15 @@ func (s *AddAdditionalEntriesToContext) Run(ctx *types.Context) error {
6465
ctx.CoreBuildPath = coreBuildPath
6566
}
6667

68+
if ctx.BuildCachePath != "" {
69+
coreBuildCachePath, err := filepath.Abs(filepath.Join(ctx.BuildCachePath, constants.FOLDER_CORE))
70+
if err != nil {
71+
return i18n.WrapError(err)
72+
}
73+
74+
ctx.CoreBuildCachePath = coreBuildCachePath
75+
}
76+
6777
if ctx.WarningsLevel == "" {
6878
ctx.WarningsLevel = DEFAULT_WARNINGS_LEVEL
6979
}

‎src/arduino.cc/builder/builder_utils/utils.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ package builder_utils
3232
import (
3333
"bytes"
3434
"fmt"
35+
"io"
3536
"os"
3637
"os/exec"
3738
"path/filepath"
@@ -116,6 +117,34 @@ func findFilesInFolder(sourcePath string, extension string, recurse bool) ([]str
116117
return sources, nil
117118
}
118119

120+
func findAllFilesInFolder(sourcePath string, recurse bool) ([]string, error) {
121+
files, err := utils.ReadDirFiltered(sourcePath, utils.FilterFiles())
122+
if err != nil {
123+
return nil, i18n.WrapError(err)
124+
}
125+
var sources []string
126+
for _, file := range files {
127+
sources = append(sources, filepath.Join(sourcePath, file.Name()))
128+
}
129+
130+
if recurse {
131+
folders, err := utils.ReadDirFiltered(sourcePath, utils.FilterDirs)
132+
if err != nil {
133+
return nil, i18n.WrapError(err)
134+
}
135+
136+
for _, folder := range folders {
137+
otherSources, err := findAllFilesInFolder(filepath.Join(sourcePath, folder.Name()), recurse)
138+
if err != nil {
139+
return nil, i18n.WrapError(err)
140+
}
141+
sources = append(sources, otherSources...)
142+
}
143+
}
144+
145+
return sources, nil
146+
}
147+
119148
func compileFilesWithRecipe(objectFiles []string, sourcePath string, sources []string, buildPath string, buildProperties properties.Map, includes []string, recipe string, verbose bool, warningsLevel string, logger i18n.Logger) ([]string, error) {
120149
for _, source := range sources {
121150
objectFile, err := compileFileWithRecipe(sourcePath, source, buildPath, buildProperties, includes, recipe, verbose, warningsLevel, logger)
@@ -258,6 +287,28 @@ func nonEmptyString(s string) bool {
258287
return s != constants.EMPTY_STRING
259288
}
260289

290+
func CoreOrReferencedCoreHasChanged(corePath, targetCorePath, targetFile string) bool {
291+
292+
targetFileStat, err := os.Stat(targetFile)
293+
if err == nil {
294+
files, err := findAllFilesInFolder(corePath, true)
295+
if err != nil {
296+
return true
297+
}
298+
for _, file := range files {
299+
fileStat, err := os.Stat(file)
300+
if err != nil || fileStat.ModTime().After(targetFileStat.ModTime()) {
301+
return true
302+
}
303+
}
304+
if targetCorePath != constants.EMPTY_STRING && !strings.EqualFold(corePath, targetCorePath) {
305+
return CoreOrReferencedCoreHasChanged(targetCorePath, constants.EMPTY_STRING, targetFile)
306+
}
307+
return false
308+
}
309+
return true
310+
}
311+
261312
func ArchiveCompiledFiles(buildPath string, archiveFile string, objectFiles []string, buildProperties properties.Map, verbose bool, logger i18n.Logger) (string, error) {
262313
archiveFilePath := filepath.Join(buildPath, archiveFile)
263314

@@ -366,3 +417,59 @@ func ExecRecipeCollectStdErr(buildProperties properties.Map, recipe string, remo
366417
func RemoveHyphenMDDFlagFromGCCCommandLine(buildProperties properties.Map) {
367418
buildProperties[constants.BUILD_PROPERTIES_COMPILER_CPP_FLAGS] = strings.Replace(buildProperties[constants.BUILD_PROPERTIES_COMPILER_CPP_FLAGS], "-MMD", "", -1)
368419
}
420+
421+
// CopyFile copies the contents of the file named src to the file named
422+
// by dst. The file will be created if it does not already exist. If the
423+
// destination file exists, all it's contents will be replaced by the contents
424+
// of the source file. The file mode will be copied from the source and
425+
// the copied data is synced/flushed to stable storage.
426+
func CopyFile(src, dst string) (err error) {
427+
in, err := os.Open(src)
428+
if err != nil {
429+
return
430+
}
431+
defer in.Close()
432+
433+
out, err := os.Create(dst)
434+
if err != nil {
435+
return
436+
}
437+
defer func() {
438+
if e := out.Close(); e != nil {
439+
err = e
440+
}
441+
}()
442+
443+
_, err = io.Copy(out, in)
444+
if err != nil {
445+
return
446+
}
447+
448+
err = out.Sync()
449+
if err != nil {
450+
return
451+
}
452+
453+
si, err := os.Stat(src)
454+
if err != nil {
455+
return
456+
}
457+
err = os.Chmod(dst, si.Mode())
458+
if err != nil {
459+
return
460+
}
461+
462+
return
463+
}
464+
465+
// GetCachedCoreArchiveFileName returns the filename to be used to store
466+
// the global cached core.a.
467+
func GetCachedCoreArchiveFileName(fqbn, coreFolder string) string {
468+
fqbnToUnderscore := strings.Replace(fqbn, ":", "_", -1)
469+
fqbnToUnderscore = strings.Replace(fqbnToUnderscore, "=", "_", -1)
470+
if absCoreFolder, err := filepath.Abs(coreFolder); err == nil {
471+
coreFolder = absCoreFolder
472+
} // silently continue if absolute path can't be detected
473+
hash := utils.MD5Sum([]byte(coreFolder))
474+
return "core_" + fqbnToUnderscore + "_" + hash + ".a"
475+
}

‎src/arduino.cc/builder/constants/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const BUILD_PROPERTIES_EXTRA_TIME_DST = "extra.time.dst"
5555
const BUILD_PROPERTIES_EXTRA_TIME_LOCAL = "extra.time.local"
5656
const BUILD_PROPERTIES_EXTRA_TIME_UTC = "extra.time.utc"
5757
const BUILD_PROPERTIES_EXTRA_TIME_ZONE = "extra.time.zone"
58+
const BUILD_PROPERTIES_FQBN = "build.fqbn"
5859
const BUILD_PROPERTIES_INCLUDES = "includes"
5960
const BUILD_PROPERTIES_OBJECT_FILE = "object_file"
6061
const BUILD_PROPERTIES_OBJECT_FILES = "object_files"

‎src/arduino.cc/builder/ctags/ctags_parser.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,16 @@ var KNOWN_TAG_KINDS = map[string]bool{
5151
}
5252

5353
type CTagsParser struct {
54-
tags []*types.CTag
54+
tags []*types.CTag
55+
mainFile string
5556
}
5657

57-
func (p *CTagsParser) Parse(ctagsOutput string) []*types.CTag {
58+
func (p *CTagsParser) Parse(ctagsOutput string, mainFile string) []*types.CTag {
5859
rows := strings.Split(ctagsOutput, "\n")
5960
rows = removeEmpty(rows)
6061

62+
p.mainFile = mainFile
63+
6164
for _, row := range rows {
6265
p.tags = append(p.tags, parseTag(row))
6366
}
@@ -199,7 +202,6 @@ func parseTag(row string) *types.CTag {
199202

200203
parts = parts[2:]
201204

202-
signature := ""
203205
returntype := ""
204206
for _, part := range parts {
205207
if strings.Contains(part, ":") {
@@ -216,7 +218,7 @@ func parseTag(row string) *types.CTag {
216218
case "typeref":
217219
tag.Typeref = value
218220
case "signature":
219-
signature = value
221+
tag.Signature = value
220222
case "returntype":
221223
returntype = value
222224
case "class":
@@ -228,7 +230,7 @@ func parseTag(row string) *types.CTag {
228230
}
229231
}
230232
}
231-
tag.Prototype = returntype + " " + tag.FunctionName + signature + ";"
233+
tag.Prototype = returntype + " " + tag.FunctionName + tag.Signature + ";"
232234

233235
if strings.Contains(row, "/^") && strings.Contains(row, "$/;") {
234236
tag.Code = row[strings.Index(row, "/^")+2 : strings.Index(row, "$/;")]

‎src/arduino.cc/builder/ctags/ctags_parser_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func produceTags(t *testing.T, filename string) []*types.CTag {
4444
require.NoError(t, err)
4545

4646
parser := CTagsParser{}
47-
return parser.Parse(string(bytes))
47+
return parser.Parse(string(bytes), "")
4848
}
4949

5050
func TestCTagsParserShouldListPrototypes(t *testing.T) {

‎src/arduino.cc/builder/ctags/ctags_to_prototypes.go

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,45 +48,51 @@ func (p *CTagsParser) findLineWhereToInsertPrototypes() int {
4848
} else {
4949
return firstFunctionPointerAsArgument
5050
}
51-
} else if firstFunctionLine == -1 {
51+
} else if firstFunctionLine != -1 {
52+
return firstFunctionLine
53+
} else if firstFunctionPointerAsArgument != -1 {
5254
return firstFunctionPointerAsArgument
5355
} else {
54-
return firstFunctionLine
56+
return 0
5557
}
5658
}
5759

5860
func (p *CTagsParser) firstFunctionPointerUsedAsArgument() int {
59-
functionNames := p.collectFunctionNames()
61+
functionTags := p.collectFunctions()
6062
for _, tag := range p.tags {
61-
if functionNameUsedAsFunctionPointerIn(tag, functionNames) {
63+
if functionNameUsedAsFunctionPointerIn(tag, functionTags) {
6264
return tag.Line
6365
}
6466
}
6567
return -1
6668
}
6769

68-
func functionNameUsedAsFunctionPointerIn(tag *types.CTag, functionNames []string) bool {
69-
for _, functionName := range functionNames {
70-
if strings.Index(tag.Code, "&"+functionName) != -1 {
70+
func functionNameUsedAsFunctionPointerIn(tag *types.CTag, functionTags []*types.CTag) bool {
71+
for _, functionTag := range functionTags {
72+
if tag.Line != functionTag.Line && strings.Index(tag.Code, "&"+functionTag.FunctionName) != -1 {
73+
return true
74+
}
75+
if tag.Line != functionTag.Line && strings.Index(tag.Code, functionTag.FunctionName) != -1 &&
76+
(functionTag.Signature == "(void)" || functionTag.Signature == "()") {
7177
return true
7278
}
7379
}
7480
return false
7581
}
7682

77-
func (p *CTagsParser) collectFunctionNames() []string {
78-
names := []string{}
83+
func (p *CTagsParser) collectFunctions() []*types.CTag {
84+
functionTags := []*types.CTag{}
7985
for _, tag := range p.tags {
80-
if tag.Kind == KIND_FUNCTION {
81-
names = append(names, tag.FunctionName)
86+
if tag.Kind == KIND_FUNCTION && !tag.SkipMe {
87+
functionTags = append(functionTags, tag)
8288
}
8389
}
84-
return names
90+
return functionTags
8591
}
8692

8793
func (p *CTagsParser) firstFunctionAtLine() int {
8894
for _, tag := range p.tags {
89-
if !tagIsUnknown(tag) && isHandled(tag) && tag.Kind == KIND_FUNCTION {
95+
if !tagIsUnknown(tag) && isHandled(tag) && tag.Kind == KIND_FUNCTION && tag.Filename == p.mainFile {
9096
return tag.Line
9197
}
9298
}

‎src/arduino.cc/builder/ctags/ctags_to_prototypes_test.go

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,17 @@ import (
3838
"github.com/stretchr/testify/require"
3939
)
4040

41-
func producePrototypes(t *testing.T, filename string) ([]*types.Prototype, int) {
41+
func producePrototypes(t *testing.T, filename string, mainFile string) ([]*types.Prototype, int) {
4242
bytes, err := ioutil.ReadFile(filepath.Join("test_data", filename))
4343
require.NoError(t, err)
4444

4545
parser := &CTagsParser{}
46-
parser.Parse(string(bytes))
46+
parser.Parse(string(bytes), mainFile)
4747
return parser.GeneratePrototypes()
4848
}
4949

5050
func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) {
51-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListPrototypes.txt")
51+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListPrototypes.txt", "/tmp/sketch7210316334309249705.cpp")
5252
require.Equal(t, 5, len(prototypes))
5353
require.Equal(t, "void setup();", prototypes[0].Prototype)
5454
require.Equal(t, "/tmp/sketch7210316334309249705.cpp", prototypes[0].File)
@@ -61,7 +61,7 @@ func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) {
6161
}
6262

6363
func TestCTagsToPrototypesShouldListTemplates(t *testing.T) {
64-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListTemplates.txt")
64+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListTemplates.txt", "/tmp/sketch8398023134925534708.cpp")
6565

6666
require.Equal(t, 3, len(prototypes))
6767
require.Equal(t, "template <typename T> T minimum (T a, T b);", prototypes[0].Prototype)
@@ -73,7 +73,7 @@ func TestCTagsToPrototypesShouldListTemplates(t *testing.T) {
7373
}
7474

7575
func TestCTagsToPrototypesShouldListTemplates2(t *testing.T) {
76-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListTemplates2.txt")
76+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldListTemplates2.txt", "/tmp/sketch463160524247569568.cpp")
7777

7878
require.Equal(t, 4, len(prototypes))
7979
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -86,15 +86,15 @@ func TestCTagsToPrototypesShouldListTemplates2(t *testing.T) {
8686
}
8787

8888
func TestCTagsToPrototypesShouldDealWithClasses(t *testing.T) {
89-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithClasses.txt")
89+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithClasses.txt", "/tmp/sketch9043227824785312266.cpp")
9090

9191
require.Equal(t, 0, len(prototypes))
9292

9393
require.Equal(t, 8, line)
9494
}
9595

9696
func TestCTagsToPrototypesShouldDealWithStructs(t *testing.T) {
97-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithStructs.txt")
97+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithStructs.txt", "/tmp/sketch8930345717354294915.cpp")
9898

9999
require.Equal(t, 3, len(prototypes))
100100
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -106,7 +106,7 @@ func TestCTagsToPrototypesShouldDealWithStructs(t *testing.T) {
106106
}
107107

108108
func TestCTagsToPrototypesShouldDealWithMacros(t *testing.T) {
109-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithMacros.txt")
109+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealWithMacros.txt", "/tmp/sketch5976699731718729500.cpp")
110110

111111
require.Equal(t, 5, len(prototypes))
112112
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -120,7 +120,7 @@ func TestCTagsToPrototypesShouldDealWithMacros(t *testing.T) {
120120
}
121121

122122
func TestCTagsToPrototypesShouldDealFunctionWithDifferentSignatures(t *testing.T) {
123-
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt")
123+
prototypes, line := producePrototypes(t, "TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt", "/tmp/test260613593/preproc/ctags_target.cpp")
124124

125125
require.Equal(t, 1, len(prototypes))
126126
require.Equal(t, "boolean getBytes( byte addr, int amount );", prototypes[0].Prototype)
@@ -130,7 +130,7 @@ func TestCTagsToPrototypesShouldDealFunctionWithDifferentSignatures(t *testing.T
130130
}
131131

132132
func TestCTagsToPrototypesClassMembersAreFilteredOut(t *testing.T) {
133-
prototypes, line := producePrototypes(t, "TestCTagsParserClassMembersAreFilteredOut.txt")
133+
prototypes, line := producePrototypes(t, "TestCTagsParserClassMembersAreFilteredOut.txt", "/tmp/test834438754/preproc/ctags_target.cpp")
134134

135135
require.Equal(t, 2, len(prototypes))
136136
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -141,7 +141,7 @@ func TestCTagsToPrototypesClassMembersAreFilteredOut(t *testing.T) {
141141
}
142142

143143
func TestCTagsToPrototypesStructWithFunctions(t *testing.T) {
144-
prototypes, line := producePrototypes(t, "TestCTagsParserStructWithFunctions.txt")
144+
prototypes, line := producePrototypes(t, "TestCTagsParserStructWithFunctions.txt", "/tmp/build7315640391316178285.tmp/preproc/ctags_target.cpp")
145145

146146
require.Equal(t, 2, len(prototypes))
147147
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -152,7 +152,7 @@ func TestCTagsToPrototypesStructWithFunctions(t *testing.T) {
152152
}
153153

154154
func TestCTagsToPrototypesDefaultArguments(t *testing.T) {
155-
prototypes, line := producePrototypes(t, "TestCTagsParserDefaultArguments.txt")
155+
prototypes, line := producePrototypes(t, "TestCTagsParserDefaultArguments.txt", "/tmp/test179252494/preproc/ctags_target.cpp")
156156

157157
require.Equal(t, 3, len(prototypes))
158158
require.Equal(t, "void test(int x = 1);", prototypes[0].Prototype)
@@ -164,7 +164,7 @@ func TestCTagsToPrototypesDefaultArguments(t *testing.T) {
164164
}
165165

166166
func TestCTagsToPrototypesNamespace(t *testing.T) {
167-
prototypes, line := producePrototypes(t, "TestCTagsParserNamespace.txt")
167+
prototypes, line := producePrototypes(t, "TestCTagsParserNamespace.txt", "/tmp/test030883150/preproc/ctags_target.cpp")
168168

169169
require.Equal(t, 2, len(prototypes))
170170
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -175,7 +175,7 @@ func TestCTagsToPrototypesNamespace(t *testing.T) {
175175
}
176176

177177
func TestCTagsToPrototypesStatic(t *testing.T) {
178-
prototypes, line := producePrototypes(t, "TestCTagsParserStatic.txt")
178+
prototypes, line := producePrototypes(t, "TestCTagsParserStatic.txt", "/tmp/test542833488/preproc/ctags_target.cpp")
179179

180180
require.Equal(t, 3, len(prototypes))
181181
require.Equal(t, "void setup();", prototypes[0].Prototype)
@@ -188,7 +188,7 @@ func TestCTagsToPrototypesStatic(t *testing.T) {
188188
}
189189

190190
func TestCTagsToPrototypesFunctionPointer(t *testing.T) {
191-
prototypes, line := producePrototypes(t, "TestCTagsParserFunctionPointer.txt")
191+
prototypes, line := producePrototypes(t, "TestCTagsParserFunctionPointer.txt", "/tmp/test547238273/preproc/ctags_target.cpp")
192192

193193
require.Equal(t, 3, len(prototypes))
194194
require.Equal(t, "void t1Callback();", prototypes[0].Prototype)
@@ -200,7 +200,7 @@ func TestCTagsToPrototypesFunctionPointer(t *testing.T) {
200200
}
201201

202202
func TestCTagsToPrototypesFunctionPointers(t *testing.T) {
203-
prototypes, line := producePrototypes(t, "TestCTagsParserFunctionPointers.txt")
203+
prototypes, line := producePrototypes(t, "TestCTagsParserFunctionPointers.txt", "/tmp/test907446433/preproc/ctags_target.cpp")
204204
require.Equal(t, 2, len(prototypes))
205205
require.Equal(t, "void setup();", prototypes[0].Prototype)
206206
require.Equal(t, "/tmp/test907446433/preproc/ctags_target.cpp", prototypes[0].File)
@@ -209,11 +209,30 @@ func TestCTagsToPrototypesFunctionPointers(t *testing.T) {
209209
require.Equal(t, 2, line)
210210
}
211211

212+
func TestCTagsToPrototypesFunctionPointersNoIndirect(t *testing.T) {
213+
prototypes, line := producePrototypes(t, "TestCTagsParserFunctionPointersNoIndirect.txt", "/tmp/test547238273/preproc/bug_callback.ino")
214+
require.Equal(t, 5, len(prototypes))
215+
require.Equal(t, "void setup();", prototypes[0].Prototype)
216+
require.Equal(t, "/tmp/test547238273/preproc/bug_callback.ino", prototypes[0].File)
217+
require.Equal(t, "void loop();", prototypes[1].Prototype)
218+
219+
require.Equal(t, 10, line)
220+
}
221+
212222
func TestCTagsRunnerSketchWithClassFunction(t *testing.T) {
213-
prototypes, _ := producePrototypes(t, "TestCTagsRunnerSketchWithClassFunction.txt")
223+
prototypes, _ := producePrototypes(t, "TestCTagsRunnerSketchWithClassFunction.txt", "/home/megabug/Workspace/arduino-builder/src/arduino.cc/builder/test/sketch_class_function/sketch_class_function.ino")
214224

215225
require.Equal(t, 3, len(prototypes))
216226
require.Equal(t, "void setup();", prototypes[0].Prototype)
217227
require.Equal(t, "void loop();", prototypes[1].Prototype)
218228
require.Equal(t, "void asdf();", prototypes[2].Prototype)
219229
}
230+
231+
func TestCTagsRunnerSketchWithMultiFile(t *testing.T) {
232+
prototypes, line := producePrototypes(t, "TestCTagsRunnerSketchWithMultifile.txt", "/tmp/apUNI8a/main.ino")
233+
234+
require.Equal(t, 0, line)
235+
require.Equal(t, "void A7105_Setup();", prototypes[0].Prototype)
236+
require.Equal(t, "void A7105_Reset();", prototypes[1].Prototype)
237+
require.Equal(t, "int A7105_calibrate_VCB(uint8_t channel);", prototypes[2].Prototype)
238+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
BUG /tmp/test547238273/preproc/bug_callback.ino /^ public: BUG(void (*fn)(void)) {}$/;" kind:function line:6 class:BUG signature:(void (*fn)(void))
2+
b /tmp/test547238273/preproc/bug_callback.ino /^BUG b(makeItBreak); \/\/this will break$/;" kind:prototype line:10 signature:(makeItBreak)returntype:BUG
3+
setup /tmp/test547238273/preproc/bug_callback.ino /^void setup() {$/;" kind:function line:12 signature:() returntype:void
4+
loop /tmp/test547238273/preproc/bug_callback.ino /^void loop() {}$/;" kind:function line:16 signature:() returntype:void
5+
makeItBreak /tmp/test547238273/preproc/bug_callback.ino /^void makeItBreak() {}$/;" kind:function line:18 signature:() returntype:void
6+
caller /tmp/test547238273/preproc/bug_callback.ino /^void caller(int (*cc)(int ),int a) {$/;" kind:function line:20 signature:(int (*cc)(int ),int a) returntype:void
7+
blub /tmp/test547238273/preproc/bug_callback.ino /^int blub(int a) {$/;" kind:function line:24 signature:(int a) returntype:int
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
allowed_ch /tmp/apUNI8a/a7105.ino /^const uint8_t allowed_ch[] = {0x14, 0x1e, 0x28, 0x32, 0x3c, 0x46, 0x50, 0x5a, 0x64, 0x6e, 0x78, 0x82};$/;" kind:variable line:21
2+
no_allowed_channels /tmp/apUNI8a/a7105.ino /^int no_allowed_channels = 12;$/;" kind:variable line:22
3+
A7105_Setup /tmp/apUNI8a/a7105.ino /^void A7105_Setup() {$/;" kind:function line:26 signature:() returntype:void
4+
A7105_Reset /tmp/apUNI8a/a7105.ino /^void A7105_Reset()$/;" kind:function line:43 signature:() returntype:void
5+
A7105_calibrate_IF /tmp/apUNI8a/a7105.ino /^int A7105_calibrate_IF() {$/;"kind:function line:52 signature:() returntype:int
6+
A7105_calibrate_VCB /tmp/apUNI8a/a7105.ino /^int A7105_calibrate_VCB(uint8_t channel) {$/;" kind:function line:87 signature:(uint8_t channel) returntype:int
7+
A7105_SetPower /tmp/apUNI8a/a7105.ino /^void A7105_SetPower(int power)$/;" kind:function line:120 signature:(int power) returntype:void
8+
A7105_Strobe /tmp/apUNI8a/a7105.ino /^void A7105_Strobe(enum A7105_State state);$/;" kind:prototype line:176 signature:(enum A7105_State state) returntype:void
9+
A7105_Strobe /tmp/apUNI8a/a7105.ino /^void A7105_Strobe(enum A7105_State state)$/;" kind:function line:179 signature:(enum A7105_State state) returntype:void
10+
A7105_WriteReg /tmp/apUNI8a/a7105.ino /^void A7105_WriteReg(uint8_t address, uint8_t data)$/;" kind:function line:190 signature:(uint8_t address, uint8_t data) returntype:void
11+
A7105_ReadReg /tmp/apUNI8a/a7105.ino /^uint8_t A7105_ReadReg(uint8_t address)$/;" kind:function line:197 signature:(uint8_t address) returntype:uint8_t
12+
A7105_WriteData /tmp/apUNI8a/a7105.ino /^void A7105_WriteData(uint8_t *dpbuffer, uint8_t len, uint8_t channel)$/;" kind:function line:211 signature:(uint8_t *dpbuffer, uint8_t len, uint8_t channel) returntype:void
13+
A7105_ReadData /tmp/apUNI8a/a7105.ino /^void A7105_ReadData(uint8_t *dpbuffer, uint8_t len)$/;" kind:function line:229 signature:(uint8_t *dpbuffer, uint8_t len) returntype:void
14+
A7105_WriteID /tmp/apUNI8a/a7105.ino /^void A7105_WriteID(unsigned long id)$/;" kind:function line:247 signature:(unsigned long id) returntype:void
15+
A7105_ReadID /tmp/apUNI8a/a7105.ino /^void A7105_ReadID()$/;" kind:function line:258 signature:() returntype:void
16+
k13 /tmp/apUNI8a/a7105.ino /^unsigned char k13;$/;" kind:variable line:272
17+
make_test_packet /tmp/apUNI8a/a7105.ino /^void make_test_packet(uint8_t testpacket[]) {$/;" kind:function line:275 signature:(uint8_t testpacket[]) returntype:void
18+
printpacket /tmp/apUNI8a/a7105.ino /^void printpacket(uint8_t packet[]) {$/;" kind:function line:295 signature:(uint8_t packet[]) returntype:void
19+
A7105_shoutchannel /tmp/apUNI8a/a7105.ino /^void A7105_shoutchannel() {$/;" kind:function line:308 signature:() returntype:void
20+
A7105_oneShout /tmp/apUNI8a/a7105.ino /^int A7105_oneShout() {$/;" kind:function line:337 signature:() returntype:int
21+
A7105_oneShoutRAM /tmp/apUNI8a/a7105.ino /^int A7105_oneShoutRAM(unsigned char xState, unsigned char xType) {$/;" kind:function line:361 signature:(unsigned char xState, unsigned char xType) returntype:int
22+
eavesdrop /tmp/apUNI8a/a7105.ino /^void eavesdrop() {$/;" kind:function line:391 signature:() returntype:void
23+
A7105_findchannel /tmp/apUNI8a/a7105.ino /^uint8_t A7105_findchannel() {$/;" kind:function line:442 signature:() returntype:uint8_t
24+
A7105_sniffchannel /tmp/apUNI8a/a7105.ino /^int A7105_sniffchannel() {$/;"kind:function line:464 signature:() returntype:int
25+
A7105_sniffchannel /tmp/apUNI8a/a7105.ino /^void A7105_sniffchannel(uint8_t _channel) {$/;" kind:function line:484 signature:(uint8_t _channel) returntype:void
26+
A7105_scanchannels /tmp/apUNI8a/a7105.ino /^void A7105_scanchannels(const uint8_t channels[]) {$/;" kind:function line:498 signature:(const uint8_t channels[]) returntype:void
27+
Channels /tmp/apUNI8a/hubsan.ino /^volatile int16_t Channels[12];$/;" kind:variable line:6
28+
initialize /tmp/apUNI8a/hubsan.ino /^static void initialize() {$/;" kind:function line:8 signature:() returntype:void
29+
hubsan_init /tmp/apUNI8a/hubsan.ino /^int hubsan_init()$/;" kind:function line:22 signature:() returntype:int
30+
update_crc /tmp/apUNI8a/hubsan.ino /^static void update_crc()$/;" kind:function line:80 signature:() returntype:void
31+
recdState /tmp/apUNI8a/main.ino /^unsigned char recdState, recdType;$/;"kind:variable line:5
32+
recdType /tmp/apUNI8a/main.ino /^unsigned char recdState, recdType;$/;"kind:variable line:5
33+
retries /tmp/apUNI8a/main.ino /^unsigned char retries;$/;" kind:variable line:6
34+
bwMap /tmp/apUNI8a/main.ino /^const unsigned char bwMap[4 \/* new cycle green yellow red blue (green) *\/] = {2, 6, 4, 1};$/;" kind:variable line:27
35+
bwButton /tmp/apUNI8a/main.ino /^unsigned char bwButton, bwCycle;$/;" kind:variable line:28
36+
bwCycle /tmp/apUNI8a/main.ino /^unsigned char bwButton, bwCycle;$/;" kind:variable line:28
37+
checkBW /tmp/apUNI8a/main.ino /^void checkBW(void); \/* watch button increment bwCycle *\/$/;" kind:prototype line:29 signature:(void) returntype:void
38+
ledState /tmp/apUNI8a/main.ino /^unsigned char ledState;$/;" kind:variable line:31
39+
mySwitches /tmp/apUNI8a/main.ino /^unsigned char mySwitches;$/;" kind:variable line:32
40+
BWMODE /tmp/apUNI8a/main.ino /^unsigned char BWMODE, BLINKMODE;$/;" kind:variable line:34
41+
BLINKMODE /tmp/apUNI8a/main.ino /^unsigned char BWMODE, BLINKMODE;$/;" kind:variable line:34
42+
lightFSM /tmp/apUNI8a/main.ino /^void lightFSM(void);$/;" kind:prototype line:36 signature:(void) returntype:void
43+
blinkLEDs /tmp/apUNI8a/main.ino /^void blinkLEDs(void);$/;" kind:prototype line:37 signature:(void) returntype:void
44+
touchTimer /tmp/apUNI8a/main.ino /^unsigned touchTimer, lastTouch;$/;" kind:variable line:38
45+
lastTouch /tmp/apUNI8a/main.ino /^unsigned touchTimer, lastTouch;$/;" kind:variable line:38
46+
lightTimer /tmp/apUNI8a/main.ino /^unsigned char lightTimer;$/;" kind:variable line:40
47+
lamp /tmp/apUNI8a/main.ino /^unsigned int lamp;$/;" kind:variable line:45
48+
loops /tmp/apUNI8a/main.ino /^unsigned long loops;$/;" kind:variable line:46
49+
recFlag /tmp/apUNI8a/main.ino /^unsigned char recFlag; \/\/ have "we" asked for a packet?$/;" kind:variable line:48
50+
needAck /tmp/apUNI8a/main.ino /^unsigned char needAck;$/;" kind:variable line:49
51+
fsmFSM /tmp/apUNI8a/main.ino /^void fsmFSM(void);$/;" kind:prototype line:51 signature:(void) returntype:void
52+
printFlag /tmp/apUNI8a/main.ino /^unsigned int printFlag;$/;" kind:variable line:52
53+
fsmTimer /tmp/apUNI8a/main.ino /^unsigned long fsmTimer;$/;" kind:variable line:53
54+
startval /tmp/apUNI8a/main.ino /^uint8_t startval, command;$/;" kind:variable line:57
55+
command /tmp/apUNI8a/main.ino /^uint8_t startval, command;$/;" kind:variable line:57
56+
iCnt /tmp/apUNI8a/main.ino /^int iCnt;$/;" kind:variable line:58
57+
myMessage /tmp/apUNI8a/main.ino /^char myMessage[8];$/;" kind:variable line:60
58+
ackMessage /tmp/apUNI8a/main.ino /^char ackMessage[8];$/;" kind:variable line:61
59+
setup /tmp/apUNI8a/main.ino /^void setup() {$/;" kind:function line:63signature:() returntype:void
60+
valid /tmp/apUNI8a/main.ino /^unsigned char valid;$/;" kind:variable line:114
61+
loop /tmp/apUNI8a/main.ino /^void loop() {$/;" kind:function line:116signature:() returntype:void
62+
listeni /tmp/apUNI8a/main.ino /^void listeni()$/;" kind:function line:165signature:() returntype:void
63+
kickIt /tmp/apUNI8a/main.ino /^void kickIt(unsigned char xType)$/;" kind:function line:172 signature:(unsigned char xType) returntype:void
64+
lightFSM /tmp/apUNI8a/main.ino /^void lightFSM()$/;" kind:function line:181 signature:() returntype:void
65+
touchFSM /tmp/apUNI8a/main.ino /^void touchFSM()$/;" kind:function line:188 signature:() returntype:void
66+
tween /tmp/apUNI8a/main.ino /^unsigned char tween[4] = {3, 5, 7, 11};$/;" kind:variable line:206
67+
fsmFSM /tmp/apUNI8a/main.ino /^void fsmFSM()$/;" kind:function line:207signature:() returntype:void
68+
doBW /tmp/apUNI8a/main.ino /^void doBW(void) {$/;" kind:function line:243signature:(void) returntype:void
69+
checkBW /tmp/apUNI8a/main.ino /^void checkBW()$/;" kind:function line:249signature:() returntype:void
70+
getSwitches /tmp/apUNI8a/main.ino /^unsigned char getSwitches()$/;" kind:function line:260 signature:() returntype:unsigned char
71+
xxLamp /tmp/apUNI8a/main.ino /^unsigned char xxLamp;$/;" kind:variable line:292
72+
setLEDs /tmp/apUNI8a/main.ino /^void setLEDs(unsigned char xx)$/;" kind:function line:294 signature:(unsigned char xx) returntype:void
73+
blinks /tmp/apUNI8a/main.ino /^const unsigned char blinks[8] = {0, 1, 0, 1, 0, 0, 1, 1};$/;" kind:variable line:311
74+
blinkLEDs /tmp/apUNI8a/main.ino /^void blinkLEDs()$/;" kind:function line:312 signature:() returntype:void
75+
proto_state /tmp/apUNI8a/protocol.ino /^static uint8_t proto_state;$/;" kind:variable line:4
76+
bind_time /tmp/apUNI8a/protocol.ino /^static uint32_t bind_time;$/;"kind:variable line:5
77+
PROTOCOL_SetBindState /tmp/apUNI8a/protocol.ino /^void PROTOCOL_SetBindState(uint32_t msec)$/;" kind:function line:11 signature:(uint32_t msec) returntype:void

‎src/arduino.cc/builder/ctags_runner.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ func (s *CTagsRunner) Run(ctx *types.Context) error {
7777
ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput)
7878

7979
parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource)
80+
ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile.Name)
81+
8082
protos, line := parser.GeneratePrototypes()
8183
if line != -1 {
8284
ctx.PrototypesLineWhereToInsert = line

‎src/arduino.cc/builder/filter_sketch_source.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func parseLineMarker(line string) string {
7979
// https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/gcc/c-family/c-ppoutput.c#L413-L415
8080

8181
split := strings.SplitN(line, " ", 3)
82-
if len(split) < 2 || split[0] != "#" {
82+
if len(split) < 3 || split[0] != "#" {
8383
return ""
8484
}
8585

‎src/arduino.cc/builder/gohasissues/go_has_issues.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ func ReadDir(dirname string) ([]os.FileInfo, error) {
109109
info, err := resolveSymlink(dirname, info)
110110
if err != nil {
111111
// unresolvable symlinks should be skipped silently
112-
return nil, nil
112+
continue
113113
}
114114
infos[idx] = info
115115
}

‎src/arduino.cc/builder/phases/core_builder.go

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
package phases
3131

3232
import (
33+
"path/filepath"
34+
3335
"arduino.cc/builder/builder_utils"
3436
"arduino.cc/builder/constants"
3537
"arduino.cc/builder/i18n"
@@ -42,6 +44,7 @@ type CoreBuilder struct{}
4244

4345
func (s *CoreBuilder) Run(ctx *types.Context) error {
4446
coreBuildPath := ctx.CoreBuildPath
47+
coreBuildCachePath := ctx.CoreBuildCachePath
4548
buildProperties := ctx.BuildProperties
4649
verbose := ctx.Verbose
4750
warningsLevel := ctx.WarningsLevel
@@ -52,7 +55,14 @@ func (s *CoreBuilder) Run(ctx *types.Context) error {
5255
return i18n.WrapError(err)
5356
}
5457

55-
archiveFile, objectFiles, err := compileCore(coreBuildPath, buildProperties, verbose, warningsLevel, logger)
58+
if coreBuildCachePath != "" {
59+
err := utils.EnsureFolderExists(coreBuildCachePath)
60+
if err != nil {
61+
return i18n.WrapError(err)
62+
}
63+
}
64+
65+
archiveFile, objectFiles, err := compileCore(coreBuildPath, coreBuildCachePath, buildProperties, verbose, warningsLevel, logger)
5666
if err != nil {
5767
return i18n.WrapError(err)
5868
}
@@ -63,10 +73,12 @@ func (s *CoreBuilder) Run(ctx *types.Context) error {
6373
return nil
6474
}
6575

66-
func compileCore(buildPath string, buildProperties properties.Map, verbose bool, warningsLevel string, logger i18n.Logger) (string, []string, error) {
76+
func compileCore(buildPath string, buildCachePath string, buildProperties properties.Map, verbose bool, warningsLevel string, logger i18n.Logger) (string, []string, error) {
6777
coreFolder := buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE_PATH]
6878
variantFolder := buildProperties[constants.BUILD_PROPERTIES_BUILD_VARIANT_PATH]
6979

80+
targetCoreFolder := buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH]
81+
7082
includes := []string{}
7183
includes = append(includes, coreFolder)
7284
if variantFolder != constants.EMPTY_STRING {
@@ -84,6 +96,24 @@ func compileCore(buildPath string, buildProperties properties.Map, verbose bool,
8496
}
8597
}
8698

99+
// Recreate the archive if ANY of the core files (including platform.txt) has changed
100+
realCoreFolder := utils.GetParentFolder(coreFolder, 2)
101+
102+
var targetArchivedCore string
103+
if buildCachePath != "" {
104+
archivedCoreName := builder_utils.GetCachedCoreArchiveFileName(buildProperties[constants.BUILD_PROPERTIES_FQBN], realCoreFolder)
105+
targetArchivedCore = filepath.Join(buildCachePath, archivedCoreName)
106+
canUseArchivedCore := !builder_utils.CoreOrReferencedCoreHasChanged(realCoreFolder, targetCoreFolder, targetArchivedCore)
107+
108+
if canUseArchivedCore {
109+
// use archived core
110+
if verbose {
111+
logger.Println(constants.LOG_LEVEL_INFO, "Using precompiled core")
112+
}
113+
return targetArchivedCore, variantObjectFiles, nil
114+
}
115+
}
116+
87117
coreObjectFiles, err := builder_utils.CompileFiles([]string{}, coreFolder, true, buildPath, buildProperties, includes, verbose, warningsLevel, logger)
88118
if err != nil {
89119
return "", nil, i18n.WrapError(err)
@@ -94,5 +124,11 @@ func compileCore(buildPath string, buildProperties properties.Map, verbose bool,
94124
return "", nil, i18n.WrapError(err)
95125
}
96126

127+
// archive core.a
128+
if targetArchivedCore != "" {
129+
logger.Println(constants.LOG_LEVEL_DEBUG, "Archiving built core (caching) in: "+targetArchivedCore)
130+
builder_utils.CopyFile(archiveFile, targetArchivedCore)
131+
}
132+
97133
return archiveFile, variantObjectFiles, nil
98134
}

‎src/arduino.cc/builder/resolve_library.go

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,18 @@ func ResolveLibrary(ctx *types.Context, header string) *types.Library {
4444
libraryResolutionResults := ctx.LibrariesResolutionResults
4545
importedLibraries := ctx.ImportedLibraries
4646

47-
markImportedLibrary := make(map[*types.Library]bool)
48-
for _, library := range importedLibraries {
49-
markImportedLibrary[library] = true
50-
}
51-
5247
libraries := append([]*types.Library{}, headerToLibraries[header]...)
5348

5449
if libraries == nil || len(libraries) == 0 {
5550
return nil
5651
}
5752

58-
if len(libraries) == 1 {
59-
markImportedLibrary[libraries[0]] = true
60-
return libraries[0]
53+
if importedLibraryContainsOneOfCandidates(importedLibraries, libraries) {
54+
return nil
6155
}
6256

63-
if markImportedLibraryContainsOneOfCandidates(markImportedLibrary, libraries) {
64-
return nil
57+
if len(libraries) == 1 {
58+
return libraries[0]
6559
}
6660

6761
reverse(libraries)
@@ -88,11 +82,13 @@ func ResolveLibrary(ctx *types.Context, header string) *types.Library {
8882
library = libraries[0]
8983
}
9084

91-
library = useAlreadyImportedLibraryWithSameNameIfExists(library, markImportedLibrary)
85+
library = useAlreadyImportedLibraryWithSameNameIfExists(library, importedLibraries)
9286

93-
libraryResolutionResults[header] = types.LibraryResolutionResult{Library: library, NotUsedLibraries: filterOutLibraryFrom(libraries, library)}
87+
libraryResolutionResults[header] = types.LibraryResolutionResult{
88+
Library: library,
89+
NotUsedLibraries: filterOutLibraryFrom(libraries, library),
90+
}
9491

95-
markImportedLibrary[library] = true
9692
return library
9793
}
9894

@@ -103,19 +99,19 @@ func reverse(data []*types.Library) {
10399
}
104100
}
105101

106-
func markImportedLibraryContainsOneOfCandidates(markImportedLibrary map[*types.Library]bool, libraries []*types.Library) bool {
107-
for markedLibrary, _ := range markImportedLibrary {
108-
for _, library := range libraries {
109-
if markedLibrary == library {
102+
func importedLibraryContainsOneOfCandidates(imported []*types.Library, candidates []*types.Library) bool {
103+
for _, i := range imported {
104+
for _, j := range candidates {
105+
if i == j {
110106
return true
111107
}
112108
}
113109
}
114110
return false
115111
}
116112

117-
func useAlreadyImportedLibraryWithSameNameIfExists(library *types.Library, markImportedLibrary map[*types.Library]bool) *types.Library {
118-
for lib, _ := range markImportedLibrary {
113+
func useAlreadyImportedLibraryWithSameNameIfExists(library *types.Library, imported []*types.Library) *types.Library {
114+
for _, lib := range imported {
119115
if lib.Name == library.Name {
120116
return lib
121117
}
@@ -222,7 +218,7 @@ func findBestLibraryWithHeader(header string, libraries []*types.Library) *types
222218

223219
func findLibWithName(name string, libraries []*types.Library) *types.Library {
224220
for _, library := range libraries {
225-
if library.Name == name {
221+
if simplifyName(library.Name) == simplifyName(name) {
226222
return library
227223
}
228224
}
@@ -231,7 +227,7 @@ func findLibWithName(name string, libraries []*types.Library) *types.Library {
231227

232228
func findLibWithNameStartingWith(name string, libraries []*types.Library) *types.Library {
233229
for _, library := range libraries {
234-
if strings.HasPrefix(library.Name, name) {
230+
if strings.HasPrefix(simplifyName(library.Name), simplifyName(name)) {
235231
return library
236232
}
237233
}
@@ -240,7 +236,7 @@ func findLibWithNameStartingWith(name string, libraries []*types.Library) *types
240236

241237
func findLibWithNameEndingWith(name string, libraries []*types.Library) *types.Library {
242238
for _, library := range libraries {
243-
if strings.HasSuffix(library.Name, name) {
239+
if strings.HasSuffix(simplifyName(library.Name), simplifyName(name)) {
244240
return library
245241
}
246242
}
@@ -249,19 +245,13 @@ func findLibWithNameEndingWith(name string, libraries []*types.Library) *types.L
249245

250246
func findLibWithNameContaining(name string, libraries []*types.Library) *types.Library {
251247
for _, library := range libraries {
252-
if strings.Contains(library.Name, name) {
248+
if strings.Contains(simplifyName(library.Name), simplifyName(name)) {
253249
return library
254250
}
255251
}
256252
return nil
257253
}
258254

259-
// thank you golang: I can not use/recycle/adapt utils.SliceContains
260-
func sliceContainsLibrary(slice []*types.Library, target *types.Library) bool {
261-
for _, value := range slice {
262-
if value.SrcFolder == target.SrcFolder {
263-
return true
264-
}
265-
}
266-
return false
255+
func simplifyName(name string) string {
256+
return strings.ToLower(strings.Replace(name, "_", " ", -1))
267257
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 2017 Arduino LLC (http://www.arduino.cc/)
28+
*/
29+
30+
package builder
31+
32+
import (
33+
"testing"
34+
35+
"arduino.cc/builder/types"
36+
37+
"github.com/stretchr/testify/require"
38+
)
39+
40+
func TestFindBestLibraryWithHeader(t *testing.T) {
41+
l1 := &types.Library{Name: "Calculus Lib"}
42+
l2 := &types.Library{Name: "Calculus Lib-master"}
43+
l3 := &types.Library{Name: "Calculus Lib Improved"}
44+
l4 := &types.Library{Name: "Another Calculus Lib"}
45+
l5 := &types.Library{Name: "Yet Another Calculus Lib Improved"}
46+
l6 := &types.Library{Name: "AnotherLib"}
47+
48+
// Test exact name matching
49+
res := findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2, l1})
50+
require.Equal(t, l1.Name, res.Name)
51+
52+
// Test exact name with "-master" postfix matching
53+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2})
54+
require.Equal(t, l2.Name, res.Name)
55+
56+
// Test prefix matching
57+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3})
58+
require.Equal(t, l3.Name, res.Name)
59+
60+
// Test postfix matching
61+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4})
62+
require.Equal(t, l4.Name, res.Name)
63+
64+
// Test "contains"" matching
65+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5})
66+
require.Equal(t, l5.Name, res.Name)
67+
68+
// Test none matching
69+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6})
70+
require.Nil(t, res)
71+
}

‎src/arduino.cc/builder/setup_build_properties.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ func (s *SetupBuildProperties) Run(ctx *types.Context) error {
7070
buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH] = targetPlatform.Folder
7171
buildProperties[constants.BUILD_PROPERTIES_RUNTIME_HARDWARE_PATH] = filepath.Join(targetPlatform.Folder, "..")
7272
buildProperties[constants.BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = ctx.ArduinoAPIVersion
73+
buildProperties[constants.BUILD_PROPERTIES_FQBN] = ctx.FQBN
7374
buildProperties[constants.IDE_VERSION] = ctx.ArduinoAPIVersion
7475
buildProperties[constants.BUILD_PROPERTIES_RUNTIME_OS] = utils.PrettyOSName()
7576

‎src/arduino.cc/builder/test/builder_test.go

Lines changed: 95 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -30,35 +30,42 @@
3030
package test
3131

3232
import (
33-
"arduino.cc/builder"
34-
"arduino.cc/builder/constants"
35-
"arduino.cc/builder/types"
36-
"github.com/stretchr/testify/require"
3733
"os"
3834
"os/exec"
3935
"path/filepath"
4036
"testing"
41-
)
37+
"time"
4238

43-
func TestBuilderEmptySketch(t *testing.T) {
44-
DownloadCoresAndToolsAndLibraries(t)
39+
"arduino.cc/builder"
40+
"arduino.cc/builder/builder_utils"
41+
"arduino.cc/builder/constants"
42+
"arduino.cc/builder/types"
43+
"github.com/stretchr/testify/require"
44+
)
4545

46-
ctx := &types.Context{
46+
func prepareBuilderTestContext(sketchPath, fqbn string) *types.Context {
47+
return &types.Context{
48+
SketchLocation: sketchPath,
49+
FQBN: fqbn,
4750
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
4851
ToolsFolders: []string{"downloaded_tools"},
4952
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
5053
OtherLibrariesFolders: []string{"libraries"},
51-
SketchLocation: filepath.Join("sketch1", "sketch.ino"),
52-
FQBN: "arduino:avr:uno",
5354
ArduinoAPIVersion: "10600",
54-
Verbose: true,
55+
Verbose: false,
5556
}
57+
}
5658

57-
buildPath := SetupBuildPath(t, ctx)
58-
defer os.RemoveAll(buildPath)
59+
func TestBuilderEmptySketch(t *testing.T) {
60+
DownloadCoresAndToolsAndLibraries(t)
5961

62+
ctx := prepareBuilderTestContext(filepath.Join("sketch1", "sketch.ino"), "arduino:avr:uno")
6063
ctx.DebugLevel = 10
6164

65+
buildPath := SetupBuildPath(t, ctx)
66+
defer os.RemoveAll(buildPath)
67+
68+
// Run builder
6269
command := builder.Builder{}
6370
err := command.Run(ctx)
6471
NoError(t, err)
@@ -78,20 +85,12 @@ func TestBuilderEmptySketch(t *testing.T) {
7885
func TestBuilderBridge(t *testing.T) {
7986
DownloadCoresAndToolsAndLibraries(t)
8087

81-
ctx := &types.Context{
82-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
83-
ToolsFolders: []string{"downloaded_tools"},
84-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
85-
OtherLibrariesFolders: []string{"libraries"},
86-
SketchLocation: filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"),
87-
FQBN: "arduino:avr:leonardo",
88-
ArduinoAPIVersion: "10600",
89-
Verbose: true,
90-
}
88+
ctx := prepareBuilderTestContext(filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
9189

9290
buildPath := SetupBuildPath(t, ctx)
9391
defer os.RemoveAll(buildPath)
9492

93+
// Run builder
9594
command := builder.Builder{}
9695
err := command.Run(ctx)
9796
NoError(t, err)
@@ -113,19 +112,12 @@ func TestBuilderBridge(t *testing.T) {
113112
func TestBuilderSketchWithConfig(t *testing.T) {
114113
DownloadCoresAndToolsAndLibraries(t)
115114

116-
ctx := &types.Context{
117-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
118-
ToolsFolders: []string{"downloaded_tools"},
119-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
120-
OtherLibrariesFolders: []string{"libraries"},
121-
SketchLocation: filepath.Join("sketch_with_config", "sketch_with_config.ino"),
122-
FQBN: "arduino:avr:leonardo",
123-
ArduinoAPIVersion: "10600",
124-
}
115+
ctx := prepareBuilderTestContext(filepath.Join("sketch_with_config", "sketch_with_config.ino"), "arduino:avr:leonardo")
125116

126117
buildPath := SetupBuildPath(t, ctx)
127118
defer os.RemoveAll(buildPath)
128119

120+
// Run builder
129121
command := builder.Builder{}
130122
err := command.Run(ctx)
131123
NoError(t, err)
@@ -147,23 +139,17 @@ func TestBuilderSketchWithConfig(t *testing.T) {
147139
func TestBuilderBridgeTwice(t *testing.T) {
148140
DownloadCoresAndToolsAndLibraries(t)
149141

150-
ctx := &types.Context{
151-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
152-
ToolsFolders: []string{"downloaded_tools"},
153-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
154-
OtherLibrariesFolders: []string{"libraries"},
155-
SketchLocation: filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"),
156-
FQBN: "arduino:avr:leonardo",
157-
ArduinoAPIVersion: "10600",
158-
}
142+
ctx := prepareBuilderTestContext(filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
159143

160144
buildPath := SetupBuildPath(t, ctx)
161145
defer os.RemoveAll(buildPath)
162146

147+
// Run builder
163148
command := builder.Builder{}
164149
err := command.Run(ctx)
165150
NoError(t, err)
166151

152+
// Run builder again
167153
command = builder.Builder{}
168154
err = command.Run(ctx)
169155
NoError(t, err)
@@ -185,21 +171,13 @@ func TestBuilderBridgeTwice(t *testing.T) {
185171
func TestBuilderBridgeSAM(t *testing.T) {
186172
DownloadCoresAndToolsAndLibraries(t)
187173

188-
ctx := &types.Context{
189-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
190-
ToolsFolders: []string{"downloaded_tools"},
191-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
192-
OtherLibrariesFolders: []string{"libraries"},
193-
SketchLocation: filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"),
194-
FQBN: "arduino:sam:arduino_due_x_dbg",
195-
ArduinoAPIVersion: "10600",
196-
}
174+
ctx := prepareBuilderTestContext(filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:sam:arduino_due_x_dbg")
175+
ctx.WarningsLevel = "all"
197176

198177
buildPath := SetupBuildPath(t, ctx)
199178
defer os.RemoveAll(buildPath)
200179

201-
ctx.WarningsLevel = "all"
202-
180+
// Run builder
203181
command := builder.Builder{}
204182
err := command.Run(ctx)
205183
NoError(t, err)
@@ -230,19 +208,14 @@ func TestBuilderBridgeSAM(t *testing.T) {
230208
func TestBuilderBridgeRedBearLab(t *testing.T) {
231209
DownloadCoresAndToolsAndLibraries(t)
232210

233-
ctx := &types.Context{
234-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware", "downloaded_board_manager_stuff"},
235-
ToolsFolders: []string{"downloaded_tools", "downloaded_board_manager_stuff"},
236-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
237-
OtherLibrariesFolders: []string{"libraries"},
238-
SketchLocation: filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"),
239-
FQBN: "RedBearLab:avr:blend",
240-
ArduinoAPIVersion: "10600",
241-
}
211+
ctx := prepareBuilderTestContext(filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "RedBearLab:avr:blend")
212+
ctx.HardwareFolders = append(ctx.HardwareFolders, "downloaded_board_manager_stuff")
213+
ctx.ToolsFolders = append(ctx.ToolsFolders, "downloaded_board_manager_stuff")
242214

243215
buildPath := SetupBuildPath(t, ctx)
244216
defer os.RemoveAll(buildPath)
245217

218+
// Run builder
246219
command := builder.Builder{}
247220
err := command.Run(ctx)
248221
NoError(t, err)
@@ -264,19 +237,14 @@ func TestBuilderBridgeRedBearLab(t *testing.T) {
264237
func TestBuilderSketchNoFunctions(t *testing.T) {
265238
DownloadCoresAndToolsAndLibraries(t)
266239

267-
ctx := &types.Context{
268-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware", "downloaded_board_manager_stuff"},
269-
ToolsFolders: []string{"downloaded_tools", "downloaded_board_manager_stuff"},
270-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
271-
OtherLibrariesFolders: []string{"libraries"},
272-
SketchLocation: filepath.Join("sketch_no_functions", "main.ino"),
273-
FQBN: "RedBearLab:avr:blend",
274-
ArduinoAPIVersion: "10600",
275-
}
240+
ctx := prepareBuilderTestContext(filepath.Join("sketch_no_functions", "main.ino"), "RedBearLab:avr:blend")
241+
ctx.HardwareFolders = append(ctx.HardwareFolders, "downloaded_board_manager_stuff")
242+
ctx.ToolsFolders = append(ctx.ToolsFolders, "downloaded_board_manager_stuff")
276243

277244
buildPath := SetupBuildPath(t, ctx)
278245
defer os.RemoveAll(buildPath)
279246

247+
// Run builder
280248
command := builder.Builder{}
281249
err := command.Run(ctx)
282250
require.Error(t, err)
@@ -285,19 +253,14 @@ func TestBuilderSketchNoFunctions(t *testing.T) {
285253
func TestBuilderSketchWithBackup(t *testing.T) {
286254
DownloadCoresAndToolsAndLibraries(t)
287255

288-
ctx := &types.Context{
289-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware", "downloaded_board_manager_stuff"},
290-
ToolsFolders: []string{"downloaded_tools", "downloaded_board_manager_stuff"},
291-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
292-
OtherLibrariesFolders: []string{"libraries"},
293-
SketchLocation: filepath.Join("sketch_with_backup_files", "sketch.ino"),
294-
FQBN: "arduino:avr:uno",
295-
ArduinoAPIVersion: "10600",
296-
}
256+
ctx := prepareBuilderTestContext(filepath.Join("sketch_with_backup_files", "sketch.ino"), "arduino:avr:uno")
257+
ctx.HardwareFolders = append(ctx.HardwareFolders, "downloaded_board_manager_stuff")
258+
ctx.ToolsFolders = append(ctx.ToolsFolders, "downloaded_board_manager_stuff")
297259

298260
buildPath := SetupBuildPath(t, ctx)
299261
defer os.RemoveAll(buildPath)
300262

263+
// Run builder
301264
command := builder.Builder{}
302265
err := command.Run(ctx)
303266
NoError(t, err)
@@ -306,19 +269,12 @@ func TestBuilderSketchWithBackup(t *testing.T) {
306269
func TestBuilderSketchWithOldLib(t *testing.T) {
307270
DownloadCoresAndToolsAndLibraries(t)
308271

309-
ctx := &types.Context{
310-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
311-
ToolsFolders: []string{"downloaded_tools"},
312-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
313-
OtherLibrariesFolders: []string{"libraries"},
314-
SketchLocation: filepath.Join("sketch_with_old_lib", "sketch.ino"),
315-
FQBN: "arduino:avr:uno",
316-
ArduinoAPIVersion: "10600",
317-
}
272+
ctx := prepareBuilderTestContext(filepath.Join("sketch_with_old_lib", "sketch.ino"), "arduino:avr:uno")
318273

319274
buildPath := SetupBuildPath(t, ctx)
320275
defer os.RemoveAll(buildPath)
321276

277+
// Run builder
322278
command := builder.Builder{}
323279
err := command.Run(ctx)
324280
NoError(t, err)
@@ -327,19 +283,12 @@ func TestBuilderSketchWithOldLib(t *testing.T) {
327283
func TestBuilderSketchWithSubfolders(t *testing.T) {
328284
DownloadCoresAndToolsAndLibraries(t)
329285

330-
ctx := &types.Context{
331-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
332-
ToolsFolders: []string{"downloaded_tools"},
333-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
334-
OtherLibrariesFolders: []string{"libraries"},
335-
SketchLocation: filepath.Join("sketch_with_subfolders", "sketch_with_subfolders.ino"),
336-
FQBN: "arduino:avr:uno",
337-
ArduinoAPIVersion: "10600",
338-
}
286+
ctx := prepareBuilderTestContext(filepath.Join("sketch_with_subfolders", "sketch_with_subfolders.ino"), "arduino:avr:uno")
339287

340288
buildPath := SetupBuildPath(t, ctx)
341289
defer os.RemoveAll(buildPath)
342290

291+
// Run builder
343292
command := builder.Builder{}
344293
err := command.Run(ctx)
345294
NoError(t, err)
@@ -348,21 +297,14 @@ func TestBuilderSketchWithSubfolders(t *testing.T) {
348297
func TestBuilderSketchBuildPathContainsUnusedPreviouslyCompiledLibrary(t *testing.T) {
349298
DownloadCoresAndToolsAndLibraries(t)
350299

351-
ctx := &types.Context{
352-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
353-
ToolsFolders: []string{"downloaded_tools"},
354-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
355-
OtherLibrariesFolders: []string{"libraries"},
356-
SketchLocation: filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"),
357-
FQBN: "arduino:avr:leonardo",
358-
ArduinoAPIVersion: "10600",
359-
}
300+
ctx := prepareBuilderTestContext(filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino"), "arduino:avr:leonardo")
360301

361302
buildPath := SetupBuildPath(t, ctx)
362303
defer os.RemoveAll(buildPath)
363304

364305
NoError(t, os.MkdirAll(filepath.Join(buildPath, constants.FOLDER_LIBRARIES, "SPI"), os.FileMode(0755)))
365306

307+
// Run builder
366308
command := builder.Builder{}
367309
err := command.Run(ctx)
368310
NoError(t, err)
@@ -377,22 +319,14 @@ func TestBuilderSketchBuildPathContainsUnusedPreviouslyCompiledLibrary(t *testin
377319
func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
378320
DownloadCoresAndToolsAndLibraries(t)
379321

380-
ctx := &types.Context{
381-
HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"},
382-
ToolsFolders: []string{"downloaded_tools"},
383-
BuiltInLibrariesFolders: []string{"downloaded_libraries"},
384-
OtherLibrariesFolders: []string{"libraries"},
385-
SketchLocation: filepath.Join("sketch1", "sketch.ino"),
386-
FQBN: "arduino:avr:uno",
387-
ArduinoAPIVersion: "10600",
388-
Verbose: true,
389-
}
322+
ctx := prepareBuilderTestContext(filepath.Join("sketch1", "sketch.ino"), "arduino:avr:uno")
390323

391324
var err error
392325
ctx.BuildPath, err = filepath.Abs(filepath.Join("sketch1", "build"))
393326
NoError(t, err)
394327
defer os.RemoveAll(ctx.BuildPath)
395328

329+
// Run build
396330
command := builder.Builder{}
397331
err = command.Run(ctx)
398332
NoError(t, err)
@@ -402,3 +336,48 @@ func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
402336
err = command.Run(ctx)
403337
NoError(t, err)
404338
}
339+
340+
func TestBuilderCacheCoreAFile(t *testing.T) {
341+
DownloadCoresAndToolsAndLibraries(t)
342+
343+
ctx := prepareBuilderTestContext(filepath.Join("sketch1", "sketch.ino"), "arduino:avr:uno")
344+
345+
SetupBuildPath(t, ctx)
346+
defer os.RemoveAll(ctx.BuildPath)
347+
SetupBuildCachePath(t, ctx)
348+
defer os.RemoveAll(ctx.BuildCachePath)
349+
350+
// Run build
351+
bldr := builder.Builder{}
352+
err := bldr.Run(ctx)
353+
NoError(t, err)
354+
355+
// Pick timestamp of cached core
356+
coreFolder := filepath.Join("downloaded_hardware", "arduino", "avr")
357+
coreFileName := builder_utils.GetCachedCoreArchiveFileName(ctx.FQBN, coreFolder)
358+
cachedCoreFile := filepath.Join(ctx.CoreBuildCachePath, coreFileName)
359+
coreStatBefore, err := os.Stat(cachedCoreFile)
360+
require.NoError(t, err)
361+
362+
// Run build again, to verify that the builder skips rebuilding core.a
363+
err = bldr.Run(ctx)
364+
NoError(t, err)
365+
366+
coreStatAfterRebuild, err := os.Stat(cachedCoreFile)
367+
require.NoError(t, err)
368+
require.Equal(t, coreStatBefore.ModTime(), coreStatAfterRebuild.ModTime())
369+
370+
// Touch a file of the core and check if the builder invalidate the cache
371+
time.Sleep(time.Second)
372+
now := time.Now().Local()
373+
err = os.Chtimes(filepath.Join(coreFolder, "cores", "arduino", "Arduino.h"), now, now)
374+
require.NoError(t, err)
375+
376+
// Run build again, to verify that the builder rebuilds core.a
377+
err = bldr.Run(ctx)
378+
NoError(t, err)
379+
380+
coreStatAfterTouch, err := os.Stat(cachedCoreFile)
381+
require.NoError(t, err)
382+
require.NotEqual(t, coreStatBefore.ModTime(), coreStatAfterTouch.ModTime())
383+
}

‎src/arduino.cc/builder/test/helper.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,18 @@
3131
package test
3232

3333
import (
34-
"arduino.cc/builder/constants"
35-
"arduino.cc/builder/types"
36-
"arduino.cc/builder/utils"
3734
"bytes"
3835
"fmt"
39-
"github.com/go-errors/errors"
40-
"github.com/stretchr/testify/assert"
4136
"io/ioutil"
4237
"path/filepath"
4338
"testing"
4439
"text/template"
40+
41+
"arduino.cc/builder/constants"
42+
"arduino.cc/builder/types"
43+
"arduino.cc/builder/utils"
44+
"github.com/go-errors/errors"
45+
"github.com/stretchr/testify/assert"
4546
)
4647

4748
func LoadAndInterpolate(t *testing.T, filename string, ctx *types.Context) string {
@@ -78,12 +79,19 @@ func NoError(t *testing.T, err error, msgAndArgs ...interface{}) {
7879
}
7980

8081
func SetupBuildPath(t *testing.T, ctx *types.Context) string {
81-
buildPath, err := ioutil.TempDir(constants.EMPTY_STRING, "test")
82+
buildPath, err := ioutil.TempDir(constants.EMPTY_STRING, "test_build_path")
8283
NoError(t, err)
8384
ctx.BuildPath = buildPath
8485
return buildPath
8586
}
8687

88+
func SetupBuildCachePath(t *testing.T, ctx *types.Context) string {
89+
buildCachePath, err := ioutil.TempDir(constants.EMPTY_STRING, "test_build_cache")
90+
NoError(t, err)
91+
ctx.BuildCachePath = buildCachePath
92+
return buildCachePath
93+
}
94+
8795
type ByLibraryName []*types.Library
8896

8997
func (s ByLibraryName) Len() int {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
template< uint16_t nBuffSize >
2+
class Foo{
3+
public:
4+
5+
template< uint16_t N >
6+
Foo &operator +=( const Foo<N> &ref ){
7+
//...
8+
return *this;
9+
}
10+
};
11+
12+
Foo<64> a;
13+
Foo<32> b;
14+
15+
void setup(){
16+
a += b;
17+
}
18+
19+
void loop(){}

‎src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ func TestTryBuild041(t *testing.T) {
213213
tryBuild(t, "sketch_with_multiline_template", "sketch_with_multiline_template.ino")
214214
}
215215

216+
func TestTryBuild042(t *testing.T) {
217+
tryBuild(t, "sketch_with_fake_function_pointer", "sketch_with_fake_function_pointer.ino")
218+
}
219+
216220
func makeDefaultContext(t *testing.T) *types.Context {
217221
DownloadCoresAndToolsAndLibraries(t)
218222

‎src/arduino.cc/builder/test/wipeout_build_path_if_build_options_changed_test.go

Lines changed: 4 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,15 @@
3030
package test
3131

3232
import (
33+
"os"
34+
"path/filepath"
35+
"testing"
36+
3337
"arduino.cc/builder"
3438
"arduino.cc/builder/gohasissues"
3539
"arduino.cc/builder/types"
3640
"arduino.cc/builder/utils"
3741
"github.com/stretchr/testify/require"
38-
"os"
39-
"path/filepath"
40-
"testing"
4142
)
4243

4344
func TestWipeoutBuildPathIfBuildOptionsChanged(t *testing.T) {
@@ -100,34 +101,3 @@ func TestWipeoutBuildPathIfBuildOptionsChangedNoPreviousBuildOptions(t *testing.
100101
_, err = os.Stat(filepath.Join(buildPath, "should_not_be_deleted.txt"))
101102
NoError(t, err)
102103
}
103-
104-
func TestWipeoutBuildPathIfBuildOptionsChangedBuildOptionsMatch(t *testing.T) {
105-
ctx := &types.Context{}
106-
107-
buildPath := SetupBuildPath(t, ctx)
108-
defer os.RemoveAll(buildPath)
109-
110-
ctx.BuildOptionsJsonPrevious = "{ \"old\":\"old\" }"
111-
ctx.BuildOptionsJson = "{ \"old\":\"old\" }"
112-
113-
utils.TouchFile(filepath.Join(buildPath, "should_not_be_deleted.txt"))
114-
115-
commands := []types.Command{
116-
&builder.WipeoutBuildPathIfBuildOptionsChanged{},
117-
}
118-
119-
for _, command := range commands {
120-
err := command.Run(ctx)
121-
NoError(t, err)
122-
}
123-
124-
_, err := os.Stat(buildPath)
125-
NoError(t, err)
126-
127-
files, err := gohasissues.ReadDir(buildPath)
128-
NoError(t, err)
129-
require.Equal(t, 1, len(files))
130-
131-
_, err = os.Stat(filepath.Join(buildPath, "should_not_be_deleted.txt"))
132-
NoError(t, err)
133-
}

‎src/arduino.cc/builder/types/context.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,10 @@ type Context struct {
3737
BuildProperties properties.Map
3838
BuildCore string
3939
BuildPath string
40+
BuildCachePath string
4041
SketchBuildPath string
4142
CoreBuildPath string
43+
CoreBuildCachePath string
4244
CoreArchiveFilePath string
4345
CoreObjectsFiles []string
4446
LibrariesBuildPath string

‎src/arduino.cc/builder/types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ type CTag struct {
252252
Filename string
253253
Typeref string
254254
SkipMe bool
255+
Signature string
255256

256257
Prototype string
257258
Function string

‎src/arduino.cc/builder/utils/utils.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,18 @@ func FilterFilesWithExtensions(extensions ...string) filterFiles {
150150
}
151151
}
152152

153+
func FilterFiles() filterFiles {
154+
return func(files []os.FileInfo) []os.FileInfo {
155+
var filtered []os.FileInfo
156+
for _, file := range files {
157+
if !file.IsDir() {
158+
filtered = append(filtered, file)
159+
}
160+
}
161+
return filtered
162+
}
163+
}
164+
153165
var SOURCE_CONTROL_FOLDERS = map[string]bool{"CVS": true, "RCS": true, ".git": true, ".github": true, ".svn": true, ".hg": true, ".bzr": true, ".vscode": true}
154166

155167
func IsSCCSOrHiddenFile(file os.FileInfo) bool {
@@ -353,6 +365,16 @@ func FindFilesInFolder(files *[]string, folder string, extensions CheckExtension
353365
return gohasissues.Walk(folder, walkFunc)
354366
}
355367

368+
func GetParentFolder(basefolder string, n int) string {
369+
tempFolder := basefolder
370+
i := 0
371+
for i < n {
372+
tempFolder = filepath.Dir(tempFolder)
373+
i++
374+
}
375+
return tempFolder
376+
}
377+
356378
func AppendIfNotPresent(target []string, elements ...string) []string {
357379
for _, element := range elements {
358380
if !SliceContains(target, element) {

‎src/arduino.cc/builder/warn_about_arch_incompatible_libraries.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,18 @@ func (s *WarnAboutArchIncompatibleLibraries) Run(ctx *types.Context) error {
5959

6060
for _, importedLibrary := range ctx.ImportedLibraries {
6161
if !importedLibrary.SupportsArchitectures(archs) {
62-
logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_LIBRARY_INCOMPATIBLE_ARCH, importedLibrary.Name, importedLibrary.Archs, archs)
62+
logger.Fprintln(os.Stdout, constants.LOG_LEVEL_WARN, constants.MSG_LIBRARY_INCOMPATIBLE_ARCH, importedLibrary.Name, sliceToCommaSeparatedString(importedLibrary.Archs), sliceToCommaSeparatedString(archs))
6363
}
6464
}
6565

6666
return nil
6767
}
68+
69+
func sliceToCommaSeparatedString(slice []string) string {
70+
str := "("
71+
str = str + slice[0]
72+
for i := 1; i < len(slice); i++ {
73+
str = str + ", " + slice[i]
74+
}
75+
return str + ")"
76+
}

‎src/arduino.cc/builder/wipeout_build_path_if_build_options_changed.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ import (
3434
"os"
3535
"path/filepath"
3636

37+
"arduino.cc/builder/builder_utils"
3738
"arduino.cc/builder/constants"
3839
"arduino.cc/builder/gohasissues"
3940
"arduino.cc/builder/i18n"
4041
"arduino.cc/builder/types"
42+
"arduino.cc/builder/utils"
4143
"arduino.cc/properties"
4244
)
4345

@@ -62,7 +64,17 @@ func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error {
6264
delete(prevOpts, "sketchLocation")
6365
}
6466

65-
if opts.Equals(prevOpts) {
67+
// check if any of the files contained in the core folders has changed
68+
// since the json was generated - like platform.txt or similar
69+
// if so, trigger a "safety" wipe
70+
buildProperties := ctx.BuildProperties
71+
targetCoreFolder := buildProperties[constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH]
72+
coreFolder := buildProperties[constants.BUILD_PROPERTIES_BUILD_CORE_PATH]
73+
realCoreFolder := utils.GetParentFolder(coreFolder, 2)
74+
jsonPath := filepath.Join(ctx.BuildPath, constants.BUILD_OPTIONS_FILE)
75+
coreHasChanged := builder_utils.CoreOrReferencedCoreHasChanged(realCoreFolder, targetCoreFolder, jsonPath)
76+
77+
if opts.Equals(prevOpts) && !coreHasChanged {
6678
return nil
6779
}
6880

0 commit comments

Comments
 (0)
Please sign in to comment.