Skip to content

Commit 5c391e4

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 5f36fff + d3bf497 commit 5c391e4

11 files changed

+171
-76
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ go get google.golang.org/grpc
6969
go get github.com/golang/protobuf/proto
7070
go get golang.org/x/net/context
7171
go get github.com/fsnotify/fsnotify
72+
go get github.com/schollz/closestmatch
7273
go get github.com/arduino/arduino-builder
7374
go build github.com/arduino/arduino-builder/arduino-builder
7475
```

arduino-builder/main.go

+5-15
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"io/ioutil"
3838
"os"
3939
"os/exec"
40+
"path/filepath"
4041
"runtime"
4142
"strings"
4243
"syscall"
@@ -54,7 +55,7 @@ import (
5455
"github.com/go-errors/errors"
5556
)
5657

57-
const VERSION = "1.4.1"
58+
const VERSION = "1.4.4"
5859

5960
const FLAG_ACTION_COMPILE = "compile"
6061
const FLAG_ACTION_PREPROCESS = "preprocess"
@@ -96,19 +97,8 @@ func (h *foldersFlag) String() string {
9697
return fmt.Sprint(*h)
9798
}
9899

99-
func (h *foldersFlag) Set(csv string) error {
100-
var values []string
101-
if strings.Contains(csv, string(os.PathListSeparator)) {
102-
values = strings.Split(csv, string(os.PathListSeparator))
103-
} else {
104-
values = strings.Split(csv, ",")
105-
}
106-
107-
for _, value := range values {
108-
value = strings.TrimSpace(value)
109-
*h = append(*h, value)
110-
}
111-
100+
func (h *foldersFlag) Set(folder string) error {
101+
*h = append(*h, folder)
112102
return nil
113103
}
114104

@@ -321,8 +311,8 @@ func main() {
321311
if err != nil {
322312
printCompleteError(err)
323313
}
314+
ctx.BuildPath, _ = filepath.Abs(buildPath)
324315
}
325-
ctx.BuildPath = buildPath
326316

327317
// FLAG_BUILD_CACHE
328318
buildCachePath, err := gohasissues.Unquote(*buildCachePathFlag)

create_cmake_rule.go

+54-38
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,16 @@ import (
3535
"path/filepath"
3636
"strings"
3737

38+
"github.com/arduino/go-properties-map"
39+
3840
"github.com/arduino/arduino-builder/builder_utils"
3941
"github.com/arduino/arduino-builder/constants"
4042
"github.com/arduino/arduino-builder/i18n"
4143
"github.com/arduino/arduino-builder/types"
4244
"github.com/arduino/arduino-builder/utils"
4345
)
4446

45-
var VALID_EXPORT_EXTENSIONS = map[string]bool{".h": true, ".c": true, ".hpp": true, ".hh": true, ".cpp": true, ".s": true, ".a": true}
47+
var VALID_EXPORT_EXTENSIONS = map[string]bool{".h": true, ".c": true, ".hpp": true, ".hh": true, ".cpp": true, ".s": true, ".a": true, ".properties": true}
4648
var DOTHEXTENSION = map[string]bool{".h": true, ".hh": true, ".hpp": true}
4749
var DOTAEXTENSION = map[string]bool{".a": true}
4850

@@ -76,23 +78,39 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
7678
coreFolder := filepath.Join(cmakeFolder, "core")
7779
cmakeFile := filepath.Join(cmakeFolder, "CMakeLists.txt")
7880

79-
// Copy used libraries in the correct folder
81+
dynamicLibsFromPkgConfig := map[string]bool{}
8082
extensions := func(ext string) bool { return VALID_EXPORT_EXTENSIONS[ext] }
83+
staticLibsExtensions := func(ext string) bool { return DOTAEXTENSION[ext] }
8184
for _, library := range ctx.ImportedLibraries {
85+
// Copy used libraries in the correct folder
8286
libFolder := filepath.Join(libBaseFolder, library.Name)
87+
mcu := ctx.BuildProperties[constants.BUILD_PROPERTIES_BUILD_MCU]
8388
utils.CopyDir(library.Folder, libFolder, extensions)
89+
90+
// Read cmake options if available
91+
isStaticLib := true
92+
if cmakeOptions, err := properties.Load(filepath.Join(libFolder, "src", mcu, "arduino_builder.properties")); err == nil {
93+
// If the library can be linked dynamically do not copy the library folder
94+
if pkgs, ok := cmakeOptions["cmake.pkg_config"]; ok {
95+
isStaticLib = false
96+
for _, pkg := range strings.Split(pkgs, " ") {
97+
dynamicLibsFromPkgConfig[pkg] = true
98+
}
99+
}
100+
}
101+
84102
// Remove examples folder
85103
if _, err := os.Stat(filepath.Join(libFolder, "examples")); err == nil {
86104
os.RemoveAll(filepath.Join(libFolder, "examples"))
87105
}
88-
// Remove stray folders contining incompatible libraries
89-
staticLibsExtensions := func(ext string) bool { return DOTAEXTENSION[ext] }
90-
mcu := ctx.BuildProperties[constants.BUILD_PROPERTIES_BUILD_MCU]
106+
107+
// Remove stray folders contining incompatible or not needed libraries archives
91108
var files []string
92109
utils.FindFilesInFolder(&files, filepath.Join(libFolder, "src"), staticLibsExtensions, true)
93110
for _, file := range files {
94-
if !strings.Contains(filepath.Dir(file), mcu) {
95-
os.RemoveAll(filepath.Dir(file))
111+
staticLibDir := filepath.Dir(file)
112+
if !isStaticLib || !strings.Contains(staticLibDir, mcu) {
113+
os.RemoveAll(staticLibDir)
96114
}
97115
}
98116
}
@@ -109,10 +127,10 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
109127

110128
// Use old ctags method to generate export file
111129
commands := []types.Command{
112-
&ContainerMergeCopySketchFiles{},
130+
//&ContainerMergeCopySketchFiles{},
113131
&ContainerAddPrototypes{},
114-
&FilterSketchSource{Source: &ctx.Source, RemoveLineMarkers: true},
115-
&SketchSaver{},
132+
//&FilterSketchSource{Source: &ctx.Source, RemoveLineMarkers: true},
133+
//&SketchSaver{},
116134
}
117135

118136
for _, command := range commands {
@@ -127,12 +145,12 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
127145
// Extract CFLAGS, CPPFLAGS and LDFLAGS
128146
var defines []string
129147
var linkerflags []string
130-
var libs []string
148+
var dynamicLibsFromGccMinusL []string
131149
var linkDirectories []string
132150

133-
extractCompileFlags(ctx, constants.RECIPE_C_COMBINE_PATTERN, &defines, &libs, &linkerflags, &linkDirectories, logger)
134-
extractCompileFlags(ctx, constants.RECIPE_C_PATTERN, &defines, &libs, &linkerflags, &linkDirectories, logger)
135-
extractCompileFlags(ctx, constants.RECIPE_CPP_PATTERN, &defines, &libs, &linkerflags, &linkDirectories, logger)
151+
extractCompileFlags(ctx, constants.RECIPE_C_COMBINE_PATTERN, &defines, &dynamicLibsFromGccMinusL, &linkerflags, &linkDirectories, logger)
152+
extractCompileFlags(ctx, constants.RECIPE_C_PATTERN, &defines, &dynamicLibsFromGccMinusL, &linkerflags, &linkDirectories, logger)
153+
extractCompileFlags(ctx, constants.RECIPE_CPP_PATTERN, &defines, &dynamicLibsFromGccMinusL, &linkerflags, &linkDirectories, logger)
136154

137155
// Extract folders with .h in them for adding in include list
138156
var headerFiles []string
@@ -141,9 +159,8 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
141159
foldersContainingDotH := findUniqueFoldersRelative(headerFiles, cmakeFolder)
142160

143161
// Extract folders with .a in them for adding in static libs paths list
144-
var staticLibsFiles []string
145-
isStaticLib := func(ext string) bool { return DOTAEXTENSION[ext] }
146-
utils.FindFilesInFolder(&staticLibsFiles, cmakeFolder, isStaticLib, true)
162+
var staticLibs []string
163+
utils.FindFilesInFolder(&staticLibs, cmakeFolder, staticLibsExtensions, true)
147164

148165
// Generate the CMakeLists global file
149166

@@ -168,33 +185,39 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
168185
// Add SO_PATHS option for libraries not getting found by pkg_config
169186
cmakelist += "set(EXTRA_LIBS_DIRS \"\" CACHE STRING \"Additional paths for dynamic libraries\")\n"
170187

171-
for i, lib := range libs {
188+
linkGroup := ""
189+
for _, lib := range dynamicLibsFromGccMinusL {
172190
// Dynamic libraries should be discovered by pkg_config
173-
lib = strings.TrimPrefix(lib, "-l")
174-
libs[i] = lib
175191
cmakelist += "pkg_search_module (" + strings.ToUpper(lib) + " " + lib + ")\n"
176192
relLinkDirectories = append(relLinkDirectories, "${"+strings.ToUpper(lib)+"_LIBRARY_DIRS}")
193+
linkGroup += " " + lib
194+
}
195+
for lib := range dynamicLibsFromPkgConfig {
196+
cmakelist += "pkg_search_module (" + strings.ToUpper(lib) + " " + lib + ")\n"
197+
relLinkDirectories = append(relLinkDirectories, "${"+strings.ToUpper(lib)+"_LIBRARY_DIRS}")
198+
linkGroup += " ${" + strings.ToUpper(lib) + "_LIBRARIES}"
177199
}
178200
cmakelist += "link_directories (" + strings.Join(relLinkDirectories, " ") + " ${EXTRA_LIBS_DIRS})\n"
179-
for _, staticLibsFile := range staticLibsFiles {
201+
for _, staticLib := range staticLibs {
180202
// Static libraries are fully configured
181-
lib := filepath.Base(staticLibsFile)
203+
lib := filepath.Base(staticLib)
182204
lib = strings.TrimPrefix(lib, "lib")
183205
lib = strings.TrimSuffix(lib, ".a")
184-
if !utils.SliceContains(libs, lib) {
185-
libs = append(libs, lib)
206+
if !utils.SliceContains(dynamicLibsFromGccMinusL, lib) {
207+
linkGroup += " " + lib
186208
cmakelist += "add_library (" + lib + " STATIC IMPORTED)\n"
187-
location := strings.TrimPrefix(staticLibsFile, cmakeFolder)
209+
location := strings.TrimPrefix(staticLib, cmakeFolder)
188210
cmakelist += "set_property(TARGET " + lib + " PROPERTY IMPORTED_LOCATION " + "${PROJECT_SOURCE_DIR}" + location + " )\n"
189211
}
190212
}
213+
191214
// Include source files
192215
// TODO: remove .cpp and .h from libraries example folders
193216
cmakelist += "file (GLOB_RECURSE SOURCES core/*.c* lib/*.c* sketch/*.c*)\n"
194217

195218
// Compile and link project
196219
cmakelist += "add_executable (" + projectName + " ${SOURCES} ${SOURCES_LIBS})\n"
197-
cmakelist += "target_link_libraries( " + projectName + " -Wl,--as-needed -Wl,--start-group " + strings.Join(libs, " ") + " -Wl,--end-group)\n"
220+
cmakelist += "target_link_libraries( " + projectName + " -Wl,--as-needed -Wl,--start-group " + linkGroup + " -Wl,--end-group)\n"
198221

199222
utils.WriteFile(cmakeFile, cmakelist)
200223

@@ -205,26 +228,26 @@ func canExportCmakeProject(ctx *types.Context) bool {
205228
return ctx.BuildProperties[constants.BUILD_PROPERTIES_COMPILER_EXPORT_CMAKE_FLAGS] != ""
206229
}
207230

208-
func extractCompileFlags(ctx *types.Context, receipe string, defines, libs, linkerflags, linkDirectories *[]string, logger i18n.Logger) {
231+
func extractCompileFlags(ctx *types.Context, receipe string, defines, dynamicLibs, linkerflags, linkDirectories *[]string, logger i18n.Logger) {
209232
command, _ := builder_utils.PrepareCommandForRecipe(ctx, ctx.BuildProperties, receipe, true)
210233

211234
for _, arg := range command.Args {
212235
if strings.HasPrefix(arg, "-D") {
213-
*defines = appendIfUnique(*defines, arg)
236+
*defines = utils.AppendIfNotPresent(*defines, arg)
214237
continue
215238
}
216239
if strings.HasPrefix(arg, "-l") {
217-
*libs = appendIfUnique(*libs, arg)
240+
*dynamicLibs = utils.AppendIfNotPresent(*dynamicLibs, arg[2:])
218241
continue
219242
}
220243
if strings.HasPrefix(arg, "-L") {
221-
*linkDirectories = appendIfUnique(*linkDirectories, strings.TrimPrefix(arg, "-L"))
244+
*linkDirectories = utils.AppendIfNotPresent(*linkDirectories, arg[2:])
222245
continue
223246
}
224247
if strings.HasPrefix(arg, "-") && !strings.HasPrefix(arg, "-I") && !strings.HasPrefix(arg, "-o") {
225248
// HACK : from linkerflags remove MMD (no cache is produced)
226249
if !strings.HasPrefix(arg, "-MMD") {
227-
*linkerflags = appendIfUnique(*linkerflags, arg)
250+
*linkerflags = utils.AppendIfNotPresent(*linkerflags, arg)
228251
}
229252
}
230253
}
@@ -241,10 +264,3 @@ func findUniqueFoldersRelative(slice []string, base string) string {
241264
}
242265
return strings.Join(out, " ")
243266
}
244-
245-
func appendIfUnique(slice []string, element string) []string {
246-
if !utils.SliceContains(slice, element) {
247-
slice = append(slice, element)
248-
}
249-
return slice
250-
}

ctags/ctags_to_prototypes.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,7 @@ func functionNameUsedAsFunctionPointerIn(tag *types.CTag, functionTags []*types.
7272
if tag.Line != functionTag.Line && strings.Index(tag.Code, "&"+functionTag.FunctionName) != -1 {
7373
return true
7474
}
75-
if tag.Line != functionTag.Line && strings.Index(tag.Code, functionTag.FunctionName) != -1 &&
76-
(functionTag.Signature == "(void)" || functionTag.Signature == "()") {
75+
if tag.Line != functionTag.Line && strings.Index(strings.TrimSpace(tag.Code), "("+functionTag.FunctionName+")") != -1 {
7776
return true
7877
}
7978
}

resolve_library.go

+27
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/arduino/arduino-builder/constants"
3737
"github.com/arduino/arduino-builder/types"
3838
"github.com/arduino/arduino-builder/utils"
39+
"github.com/schollz/closestmatch"
3940
)
4041

4142
func ResolveLibrary(ctx *types.Context, header string) *types.Library {
@@ -211,6 +212,10 @@ func findBestLibraryWithHeader(header string, libraries []*types.Library) *types
211212
if library != nil {
212213
return library
213214
}
215+
library = findLibWithNameBestDistance(headerName, libraries)
216+
if library != nil {
217+
return library
218+
}
214219
}
215220

216221
return nil
@@ -252,6 +257,28 @@ func findLibWithNameContaining(name string, libraries []*types.Library) *types.L
252257
return nil
253258
}
254259

260+
func findLibWithNameBestDistance(name string, libraries []*types.Library) *types.Library {
261+
// create closestmatch DB
262+
var wordsToTest []string
263+
264+
for _, library := range libraries {
265+
wordsToTest = append(wordsToTest, simplifyName(library.Name))
266+
}
267+
// Choose a set of bag sizes, more is more accurate but slower
268+
bagSizes := []int{2}
269+
// Create a closestmatch object
270+
cm := closestmatch.New(wordsToTest, bagSizes)
271+
closest_name := cm.Closest(name)
272+
273+
for _, library := range libraries {
274+
if (closest_name == simplifyName(library.Name)) {
275+
return library
276+
}
277+
}
278+
279+
return nil
280+
}
281+
255282
func simplifyName(name string) string {
256283
return strings.ToLower(strings.Replace(name, "_", " ", -1))
257284
}

resolve_library_test.go

+17-8
Original file line numberDiff line numberDiff line change
@@ -43,29 +43,38 @@ func TestFindBestLibraryWithHeader(t *testing.T) {
4343
l3 := &types.Library{Name: "Calculus Lib Improved"}
4444
l4 := &types.Library{Name: "Another Calculus Lib"}
4545
l5 := &types.Library{Name: "Yet Another Calculus Lib Improved"}
46-
l6 := &types.Library{Name: "AnotherLib"}
46+
l6 := &types.Library{Name: "Calculus Unified Lib"}
47+
l7 := &types.Library{Name: "AnotherLib"}
4748

4849
// Test exact name matching
49-
res := findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2, l1})
50+
res := findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3, l2, l1})
5051
require.Equal(t, l1.Name, res.Name)
5152

5253
// Test exact name with "-master" postfix matching
53-
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3, l2})
54+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3, l2})
5455
require.Equal(t, l2.Name, res.Name)
5556

5657
// Test prefix matching
57-
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4, l3})
58+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4, l3})
5859
require.Equal(t, l3.Name, res.Name)
5960

6061
// Test postfix matching
61-
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5, l4})
62+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5, l4})
6263
require.Equal(t, l4.Name, res.Name)
6364

6465
// Test "contains"" matching
65-
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l5})
66+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6, l5})
6667
require.Equal(t, l5.Name, res.Name)
6768

69+
// Test lexicographic similarity matching
70+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7, l6})
71+
require.Equal(t, l6.Name, res.Name)
72+
73+
// Test lexicographic similarity matching (2)
74+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6, l7})
75+
require.Equal(t, l6.Name, res.Name)
76+
6877
// Test none matching
69-
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l6})
70-
require.Nil(t, res)
78+
res = findBestLibraryWithHeader("calculus_lib.h", []*types.Library{l7})
79+
require.Equal(t, l7.Name, res.Name)
7180
}

sketch_loader.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func (s *SketchLoader) Run(ctx *types.Context) error {
7676
return i18n.ErrorfWithLogger(logger, constants.MSG_CANT_FIND_SKETCH_IN_PATH, sketchLocation, filepath.Dir(sketchLocation))
7777
}
7878

79-
sketch, err := makeSketch(sketchLocation, allSketchFilePaths, logger)
79+
sketch, err := makeSketch(sketchLocation, allSketchFilePaths, ctx.BuildPath, logger)
8080
if err != nil {
8181
return i18n.WrapError(err)
8282
}
@@ -100,7 +100,7 @@ func collectAllSketchFiles(from string) ([]string, error) {
100100
return filePaths, i18n.WrapError(err)
101101
}
102102

103-
func makeSketch(sketchLocation string, allSketchFilePaths []string, logger i18n.Logger) (*types.Sketch, error) {
103+
func makeSketch(sketchLocation string, allSketchFilePaths []string, buildLocation string, logger i18n.Logger) (*types.Sketch, error) {
104104
sketchFilesMap := make(map[string]types.SketchFile)
105105
for _, sketchFilePath := range allSketchFilePaths {
106106
source, err := ioutil.ReadFile(sketchFilePath)
@@ -123,7 +123,9 @@ func makeSketch(sketchLocation string, allSketchFilePaths []string, logger i18n.
123123
otherSketchFiles = append(otherSketchFiles, sketchFile)
124124
}
125125
} else if ADDITIONAL_FILE_VALID_EXTENSIONS[ext] {
126-
additionalFiles = append(additionalFiles, sketchFile)
126+
if !strings.Contains(filepath.Dir(sketchFile.Name), buildLocation) || buildLocation == "" {
127+
additionalFiles = append(additionalFiles, sketchFile)
128+
}
127129
} else {
128130
return nil, i18n.ErrorfWithLogger(logger, constants.MSG_UNKNOWN_SKETCH_EXT, sketchFile.Name)
129131
}

0 commit comments

Comments
 (0)