Skip to content

Commit ee36877

Browse files
author
Federico Fissore
committed
Added recursive library discovery, discovering libraries included by other
libraries Signed-off-by: Federico Fissore <[email protected]>
1 parent f6689b9 commit ee36877

17 files changed

+145
-22
lines changed

src/arduino.cc/builder/add_additional_entries_to_context.go

+4
Original file line numberDiff line numberDiff line change
@@ -75,5 +75,9 @@ func (s *AddAdditionalEntriesToContext) Run(context map[string]interface{}) erro
7575
context[constants.CTX_DEBUG_LEVEL] = DEFAULT_DEBUG_LEVEL
7676
}
7777

78+
if !utils.MapHas(context, constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH) {
79+
context[constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH] = DEFAULT_LIBRARY_DISCOVERY_RECURSION_DEPTH
80+
}
81+
7882
return nil
7983
}

src/arduino.cc/builder/builder.go

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ const DEFAULT_DEBUG_LEVEL = 5
6161
const DEFAULT_WARNINGS_LEVEL = "none"
6262
const DEFAULT_SOFTWARE = "ARDUINO"
6363
const DEFAULT_BUILD_CORE = "arduino"
64+
const DEFAULT_LIBRARY_DISCOVERY_RECURSION_DEPTH = 3
6465

6566
type Builder struct{}
6667

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

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ const BUILD_PROPERTIES_TOOLS_KEY = "tools"
6565
const BUILD_PROPERTIES_VID = "vid"
6666
const COAN = "coan"
6767
const CTAGS = "ctags"
68+
const CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH = "libraryDiscoveryRecursionDepth"
6869
const CTX_ACTUAL_PLATFORM = "actualPlatform"
6970
const CTX_BUILD_CORE = "buildCore"
7071
const CTX_BUILD_OPTIONS = "buildOptions"

src/arduino.cc/builder/container_find_includes.go

+14-10
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,28 @@
3030
package builder
3131

3232
import (
33+
"arduino.cc/builder/constants"
3334
"arduino.cc/builder/types"
3435
"arduino.cc/builder/utils"
3536
)
3637

3738
type ContainerFindIncludes struct{}
3839

3940
func (s *ContainerFindIncludes) Run(context map[string]interface{}) error {
40-
commands := []types.Command{
41-
&IncludesFinderWithGCC{},
42-
&GCCMinusMOutputParser{},
43-
&IncludesToIncludeFolders{},
44-
}
41+
wheelSpins := context[constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH].(int)
42+
for i := 0; i < wheelSpins+1; i++ {
43+
commands := []types.Command{
44+
&IncludesFinderWithGCC{},
45+
&GCCMinusMOutputParser{},
46+
&IncludesToIncludeFolders{},
47+
}
4548

46-
for _, command := range commands {
47-
PrintRingNameIfDebug(context, command)
48-
err := command.Run(context)
49-
if err != nil {
50-
return utils.WrapError(err)
49+
for _, command := range commands {
50+
PrintRingNameIfDebug(context, command)
51+
err := command.Run(context)
52+
if err != nil {
53+
return utils.WrapError(err)
54+
}
5155
}
5256
}
5357

src/arduino.cc/builder/gcc_minus_m_output_parser.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ package builder
3131

3232
import (
3333
"arduino.cc/builder/constants"
34+
"arduino.cc/builder/utils"
3435
"strings"
3536
)
3637

@@ -54,7 +55,17 @@ func (s *GCCMinusMOutputParser) Run(context map[string]interface{}) error {
5455
}
5556
}
5657

57-
context[constants.CTX_INCLUDES] = includes
58+
if !utils.MapHas(context, constants.CTX_INCLUDES) {
59+
context[constants.CTX_INCLUDES] = includes
60+
return nil
61+
}
62+
63+
previousIncludes := utils.SliceToMapStringBool(context[constants.CTX_INCLUDES].([]string), true)
64+
currentIncludes := utils.SliceToMapStringBool(includes, true)
65+
66+
mergedIncludes := utils.MergeMapsOfStringBool(previousIncludes, currentIncludes)
67+
68+
context[constants.CTX_INCLUDES] = utils.KeysOfMapOfStringBool(mergedIncludes)
5869

5970
return nil
6071
}

src/arduino.cc/builder/hardware/platform.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ tools.ctags.pattern="{cmd.path}" -u --language-force=c++ -f - --c++-kinds=svpf -
88
tools.avrdude.path={runtime.tools.avrdude.path}
99

1010
preproc.includes.flags=-w -x c++ -M -MG -MP
11-
recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {preproc.includes.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} "{source_file}"
11+
recipe.preproc.includes="{compiler.path}{compiler.cpp.cmd}" {preproc.includes.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} "{source_file}"
1212

1313
preproc.macros.flags=-w -x c++ -E -CC
1414
preproc.macros.compatibility_flags={build.mbed_api_include} {build.nRF51822_api_include} {build.ble_api_include} {compiler.libsam.c.flags} {compiler.arm.cmsis.path} {build.variant_system_include}

src/arduino.cc/builder/includes_finder_with_gcc.go

+9
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"arduino.cc/builder/types"
3737
"arduino.cc/builder/utils"
3838
"path/filepath"
39+
"strings"
3940
)
4041

4142
type IncludesFinderWithGCC struct{}
@@ -47,8 +48,16 @@ func (s *IncludesFinderWithGCC) Run(context map[string]interface{}) error {
4748
verbose := context[constants.CTX_VERBOSE].(bool)
4849
logger := context[constants.CTX_LOGGER].(i18n.Logger)
4950

51+
includesParams := ""
52+
if utils.MapHas(context, constants.CTX_INCLUDE_FOLDERS) {
53+
includes := context[constants.CTX_INCLUDE_FOLDERS].([]string)
54+
includes = utils.Map(includes, utils.WrapWithHyphenI)
55+
includesParams = strings.Join(includes, " ")
56+
}
57+
5058
properties := utils.MergeMapsOfStrings(make(map[string]string), buildProperties)
5159
properties[constants.BUILD_PROPERTIES_SOURCE_FILE] = filepath.Join(sketchBuildPath, filepath.Base(sketch.MainFile.Name)+".cpp")
60+
properties[constants.BUILD_PROPERTIES_INCLUDES] = includesParams
5261
builder_utils.RemoveHyphenMDDFlagFromGCCCommandLine(properties)
5362

5463
output, err := builder_utils.ExecRecipe(properties, constants.RECIPE_PREPROC_INCLUDES, true, verbose, false, logger)

src/arduino.cc/builder/includes_to_include_folders.go

+22-6
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,21 @@ import (
4242
type IncludesToIncludeFolders struct{}
4343

4444
func (s *IncludesToIncludeFolders) Run(context map[string]interface{}) error {
45+
if !utils.MapHas(context, constants.CTX_LIBRARIES) {
46+
return nil
47+
}
4548
includes := context[constants.CTX_INCLUDES].([]string)
4649
headerToLibraries := context[constants.CTX_HEADER_TO_LIBRARIES].(map[string][]*types.Library)
4750
debugLevel := utils.DebugLevel(context)
4851
logger := context[constants.CTX_LOGGER].(i18n.Logger)
4952
platform := context[constants.CTX_TARGET_PLATFORM].(*types.Platform)
5053
actualPlatform := context[constants.CTX_ACTUAL_PLATFORM].(*types.Platform)
5154

52-
importedLibraries, err := resolveLibraries(includes, headerToLibraries, []*types.Platform{actualPlatform, platform}, debugLevel, logger)
55+
var previousImportedLibraries []*types.Library
56+
if utils.MapHas(context, constants.CTX_IMPORTED_LIBRARIES) {
57+
previousImportedLibraries = context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library)
58+
}
59+
importedLibraries, err := resolveLibraries(includes, headerToLibraries, previousImportedLibraries, []*types.Platform{actualPlatform, platform}, debugLevel, logger)
5360
if err != nil {
5461
return utils.WrapError(err)
5562
}
@@ -77,17 +84,15 @@ func resolveIncludeFolders(importedLibraries []*types.Library, buildProperties m
7784
return includeFolders
7885
}
7986

80-
func resolveLibraries(includes []string, headerToLibraries map[string][]*types.Library, platforms []*types.Platform, debugLevel int, logger i18n.Logger) ([]*types.Library, error) {
87+
//FIXME it's also resolving previously resolved libraries
88+
func resolveLibraries(includes []string, headerToLibraries map[string][]*types.Library, previousImportedLibraries []*types.Library, platforms []*types.Platform, debugLevel int, logger i18n.Logger) ([]*types.Library, error) {
8189
markImportedLibrary := make(map[*types.Library]bool)
8290
for _, header := range includes {
8391
libraries := headerToLibraries[header]
8492
if libraries != nil {
8593
if len(libraries) == 1 {
8694
markImportedLibrary[libraries[0]] = true
8795
} else {
88-
if debugLevel > 0 {
89-
logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_MULTIPLE_LIBS_FOUND_FOR, header)
90-
}
9196
var library *types.Library
9297
for _, platform := range platforms {
9398
if platform != nil && library == nil {
@@ -109,7 +114,8 @@ func resolveLibraries(includes []string, headerToLibraries map[string][]*types.L
109114
if library == nil {
110115
library = libraries[0]
111116
}
112-
if debugLevel > 0 {
117+
if debugLevel > 0 && !sliceContainsLibrary(previousImportedLibraries, library) {
118+
logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_MULTIPLE_LIBS_FOUND_FOR, header)
113119
logger.Fprintln(os.Stderr, constants.MSG_LIBRARIES_USED, library.Folder)
114120
for _, notUsedLibrary := range libraries {
115121
if library != notUsedLibrary {
@@ -232,3 +238,13 @@ func findLibWithNameContaining(name string, libraries []*types.Library) *types.L
232238
}
233239
return nil
234240
}
241+
242+
// thank you golang for s***ing: I can't use/recycle/adapt utils.SliceContains
243+
func sliceContainsLibrary(slice []*types.Library, target *types.Library) bool {
244+
for _, value := range slice {
245+
if value == target {
246+
return true
247+
}
248+
}
249+
return false
250+
}

src/arduino.cc/builder/test/add_additional_entries_to_context_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ func TestAddAdditionalEntriesToContextNoBuildPath(t *testing.T) {
5151
require.NotNil(t, context[constants.CTX_WARNINGS_LEVEL])
5252
require.NotNil(t, context[constants.CTX_VERBOSE])
5353
require.NotNil(t, context[constants.CTX_DEBUG_LEVEL])
54+
require.NotNil(t, context[constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH])
5455
}
5556

5657
func TestAddAdditionalEntriesToContextWithBuildPath(t *testing.T) {
@@ -69,4 +70,5 @@ func TestAddAdditionalEntriesToContextWithBuildPath(t *testing.T) {
6970
require.NotNil(t, context[constants.CTX_WARNINGS_LEVEL])
7071
require.NotNil(t, context[constants.CTX_VERBOSE])
7172
require.NotNil(t, context[constants.CTX_DEBUG_LEVEL])
73+
require.NotNil(t, context[constants.CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH])
7274
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <library2.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <library3.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#warning included!

src/arduino.cc/builder/test/hardware_loader_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func TestLoadHardwareMixingUserHardwareFolder(t *testing.T) {
121121
require.Equal(t, "-w -x c++ -M -MG -MP", avrPlatform.Properties["preproc.includes.flags"])
122122
require.Equal(t, "-w -x c++ -E -CC", avrPlatform.Properties["preproc.macros.flags"])
123123
require.Equal(t, "{build.mbed_api_include} {build.nRF51822_api_include} {build.ble_api_include} {compiler.libsam.c.flags} {compiler.arm.cmsis.path} {build.variant_system_include}", avrPlatform.Properties["preproc.macros.compatibility_flags"])
124-
require.Equal(t, "\"{compiler.path}{compiler.cpp.cmd}\" {preproc.includes.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} \"{source_file}\"", avrPlatform.Properties[constants.RECIPE_PREPROC_INCLUDES])
124+
require.Equal(t, "\"{compiler.path}{compiler.cpp.cmd}\" {preproc.includes.flags} -DF_CPU={build.f_cpu} -DARDUINO={runtime.ide.version} -DARDUINO_{build.board} -DARDUINO_ARCH_{build.arch} {compiler.cpp.extra_flags} {build.extra_flags} {includes} \"{source_file}\"", avrPlatform.Properties[constants.RECIPE_PREPROC_INCLUDES])
125125

126126
require.NotNil(t, packages["my_avr_platform"])
127127
myAVRPlatform := packages["my_avr_platform"].Platforms["avr"]

src/arduino.cc/builder/test/includes_finder_with_gcc_test.go

+56-2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import (
3636
"github.com/stretchr/testify/require"
3737
"os"
3838
"path/filepath"
39+
"sort"
3940
"testing"
4041
)
4142

@@ -61,8 +62,7 @@ func TestIncludesFinderWithGCC(t *testing.T) {
6162

6263
&builder.ContainerMergeCopySketchFiles{},
6364

64-
&builder.IncludesFinderWithGCC{},
65-
&builder.GCCMinusMOutputParser{},
65+
&builder.ContainerFindIncludes{},
6666
}
6767

6868
for _, command := range commands {
@@ -73,6 +73,7 @@ func TestIncludesFinderWithGCC(t *testing.T) {
7373
require.NotNil(t, context[constants.CTX_INCLUDES])
7474
includes := context[constants.CTX_INCLUDES].([]string)
7575
require.Equal(t, 2, len(includes))
76+
sort.Strings(includes)
7677
require.Equal(t, filepath.Join(buildPath, constants.FOLDER_SKETCH, "empty_1.h"), includes[0])
7778
require.Equal(t, filepath.Join(buildPath, constants.FOLDER_SKETCH, "empty_2.h"), includes[1])
7879
}
@@ -115,3 +116,56 @@ func TestIncludesFinderWithGCCSketchWithConfig(t *testing.T) {
115116
require.Equal(t, filepath.Join(buildPath, constants.FOLDER_SKETCH, "includes")+"/de bug.h", includes[1])
116117
require.Equal(t, "Bridge.h", includes[2])
117118
}
119+
120+
func TestIncludesFinderWithGCCSketchWithDependendLibraries(t *testing.T) {
121+
DownloadCoresAndToolsAndLibraries(t)
122+
123+
context := make(map[string]interface{})
124+
125+
buildPath := SetupBuildPath(t, context)
126+
defer os.RemoveAll(buildPath)
127+
128+
context[constants.CTX_HARDWARE_FOLDERS] = []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}
129+
context[constants.CTX_TOOLS_FOLDERS] = []string{"downloaded_tools"}
130+
context[constants.CTX_LIBRARIES_FOLDERS] = []string{"dependent_libraries"}
131+
context[constants.CTX_FQBN] = "arduino:avr:leonardo"
132+
context[constants.CTX_SKETCH_LOCATION] = filepath.Join("sketch_with_dependend_libraries", "sketch.ino")
133+
context[constants.CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION] = "10600"
134+
context[constants.CTX_VERBOSE] = false
135+
136+
commands := []types.Command{
137+
&builder.SetupHumanLoggerIfMissing{},
138+
139+
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
140+
141+
&builder.ContainerMergeCopySketchFiles{},
142+
143+
&builder.ContainerFindIncludes{},
144+
}
145+
146+
for _, command := range commands {
147+
err := command.Run(context)
148+
NoError(t, err)
149+
}
150+
151+
require.NotNil(t, context[constants.CTX_INCLUDES])
152+
includes := context[constants.CTX_INCLUDES].([]string)
153+
require.Equal(t, 6, len(includes))
154+
155+
sort.Strings(includes)
156+
require.Equal(t, Abs(t, filepath.Join("dependent_libraries", "library1", "library1.h")), includes[0])
157+
require.Equal(t, Abs(t, filepath.Join("dependent_libraries", "library2", "library2.h")), includes[1])
158+
require.Equal(t, Abs(t, filepath.Join("dependent_libraries", "library3", "library3.h")), includes[2])
159+
require.Equal(t, "library1.h", includes[3])
160+
require.Equal(t, "library2.h", includes[4])
161+
require.Equal(t, "library3.h", includes[5])
162+
163+
require.NotNil(t, context[constants.CTX_IMPORTED_LIBRARIES])
164+
importedLibraries := context[constants.CTX_IMPORTED_LIBRARIES].([]*types.Library)
165+
require.Equal(t, 3, len(importedLibraries))
166+
167+
sort.Sort(ByLibraryName(importedLibraries))
168+
require.Equal(t, "library1", importedLibraries[0].Name)
169+
require.Equal(t, "library2", importedLibraries[1].Name)
170+
require.Equal(t, "library3", importedLibraries[2].Name)
171+
}
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
#warn IRRemote included!
1+
#warning IRRemote included!
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include <library1.h>

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

+17
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ func KeysOfMapOfString(input map[string]string) []string {
5151
return keys
5252
}
5353

54+
func KeysOfMapOfStringBool(input map[string]bool) []string {
55+
var keys []string
56+
for key, _ := range input {
57+
keys = append(keys, key)
58+
}
59+
return keys
60+
}
61+
5462
func MergeMapsOfStrings(target map[string]string, sources ...map[string]string) map[string]string {
5563
for _, source := range sources {
5664
for key, value := range source {
@@ -60,6 +68,15 @@ func MergeMapsOfStrings(target map[string]string, sources ...map[string]string)
6068
return target
6169
}
6270

71+
func MergeMapsOfStringBool(target map[string]bool, sources ...map[string]bool) map[string]bool {
72+
for _, source := range sources {
73+
for key, value := range source {
74+
target[key] = value
75+
}
76+
}
77+
return target
78+
}
79+
6380
func MergeMapsOfMapsOfStrings(target map[string]map[string]string, sources ...map[string]map[string]string) map[string]map[string]string {
6481
for _, source := range sources {
6582
for key, value := range source {

0 commit comments

Comments
 (0)