Skip to content

Commit 088d427

Browse files
authored
Add --library flag to compile command (#1258)
* Add --library flag to compile command * Fix library prioritization * [skip changelog] Fix receiver name in LibraryManager functions * [skip changelog] Fix variables names and some docstrings * Fix libraries not being recompiled when path to source file changes
1 parent 6ac85fc commit 088d427

File tree

22 files changed

+279
-82
lines changed

22 files changed

+279
-82
lines changed

Diff for: arduino/libraries/libraries_location.go

+13
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ const (
3535
ReferencedPlatformBuiltIn
3636
// User are user installed libraries
3737
User
38+
// Unmanaged is for libraries set manually by the user in the CLI command or from the gRPC function.
39+
// Ideally it's used for `libraries` outside folders managed by the CLI.
40+
Unmanaged
3841
)
3942

4043
func (d *LibraryLocation) String() string {
@@ -47,6 +50,8 @@ func (d *LibraryLocation) String() string {
4750
return "ref-platform"
4851
case User:
4952
return "user"
53+
case Unmanaged:
54+
return "unmanaged"
5055
}
5156
panic(fmt.Sprintf("invalid LibraryLocation value %d", *d))
5257
}
@@ -62,6 +67,8 @@ func (d *LibraryLocation) MarshalJSON() ([]byte, error) {
6267
return json.Marshal("ref-platform")
6368
case User:
6469
return json.Marshal("user")
70+
case Unmanaged:
71+
return json.Marshal("unmanaged")
6572
}
6673
return nil, fmt.Errorf("invalid library location value: %d", *d)
6774
}
@@ -81,6 +88,8 @@ func (d *LibraryLocation) UnmarshalJSON(b []byte) error {
8188
*d = ReferencedPlatformBuiltIn
8289
case "user":
8390
*d = User
91+
case "unmanaged":
92+
*d = Unmanaged
8493
}
8594
return fmt.Errorf("invalid library location: %s", s)
8695
}
@@ -96,6 +105,8 @@ func (d *LibraryLocation) ToRPCLibraryLocation() rpc.LibraryLocation {
96105
return rpc.LibraryLocation_LIBRARY_LOCATION_REFERENCED_PLATFORM_BUILTIN
97106
case User:
98107
return rpc.LibraryLocation_LIBRARY_LOCATION_USER
108+
case Unmanaged:
109+
return rpc.LibraryLocation_LIBRARY_LOCATION_UNMANAGED
99110
}
100111
panic(fmt.Sprintf("invalid LibraryLocation value %d", *d))
101112
}
@@ -111,6 +122,8 @@ func FromRPCLibraryLocation(l rpc.LibraryLocation) LibraryLocation {
111122
return ReferencedPlatformBuiltIn
112123
case rpc.LibraryLocation_LIBRARY_LOCATION_USER:
113124
return User
125+
case rpc.LibraryLocation_LIBRARY_LOCATION_UNMANAGED:
126+
return Unmanaged
114127
}
115128
panic(fmt.Sprintf("invalid rpc.LibraryLocation value %d", l))
116129
}

Diff for: arduino/libraries/librariesmanager/librariesmanager.go

+46-23
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,10 @@ func (alts *LibraryAlternatives) FindVersion(version *semver.Version) *libraries
8282
}
8383

8484
// Names returns an array with all the names of the installed libraries.
85-
func (sc LibrariesManager) Names() []string {
86-
res := make([]string, len(sc.Libraries))
85+
func (lm LibrariesManager) Names() []string {
86+
res := make([]string, len(lm.Libraries))
8787
i := 0
88-
for n := range sc.Libraries {
88+
for n := range lm.Libraries {
8989
res[i] = n
9090
i++
9191
}
@@ -109,27 +109,27 @@ func NewLibraryManager(indexDir *paths.Path, downloadsDir *paths.Path) *Librarie
109109

110110
// LoadIndex reads a library_index.json from a file and returns
111111
// the corresponding Index structure.
112-
func (sc *LibrariesManager) LoadIndex() error {
113-
index, err := librariesindex.LoadIndex(sc.IndexFile)
112+
func (lm *LibrariesManager) LoadIndex() error {
113+
index, err := librariesindex.LoadIndex(lm.IndexFile)
114114
if err != nil {
115-
sc.Index = librariesindex.EmptyIndex
115+
lm.Index = librariesindex.EmptyIndex
116116
return err
117117
}
118-
sc.Index = index
118+
lm.Index = index
119119
return nil
120120
}
121121

122122
// AddLibrariesDir adds path to the list of directories
123123
// to scan when searching for libraries. If a path is already
124124
// in the list it is ignored.
125-
func (sc *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries.LibraryLocation) {
126-
for _, dir := range sc.LibrariesDir {
125+
func (lm *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries.LibraryLocation) {
126+
for _, dir := range lm.LibrariesDir {
127127
if dir.Path.EquivalentTo(path) {
128128
return
129129
}
130130
}
131131
logrus.WithField("dir", path).WithField("location", location.String()).Info("Adding libraries dir")
132-
sc.LibrariesDir = append(sc.LibrariesDir, &LibrariesDir{
132+
lm.LibrariesDir = append(lm.LibrariesDir, &LibrariesDir{
133133
Path: path,
134134
Location: location,
135135
})
@@ -138,36 +138,36 @@ func (sc *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries
138138
// AddPlatformReleaseLibrariesDir add the libraries directory in the
139139
// specified PlatformRelease to the list of directories to scan when
140140
// searching for libraries.
141-
func (sc *LibrariesManager) AddPlatformReleaseLibrariesDir(plaftormRelease *cores.PlatformRelease, location libraries.LibraryLocation) {
141+
func (lm *LibrariesManager) AddPlatformReleaseLibrariesDir(plaftormRelease *cores.PlatformRelease, location libraries.LibraryLocation) {
142142
path := plaftormRelease.GetLibrariesDir()
143143
if path == nil {
144144
return
145145
}
146-
for _, dir := range sc.LibrariesDir {
146+
for _, dir := range lm.LibrariesDir {
147147
if dir.Path.EquivalentTo(path) {
148148
return
149149
}
150150
}
151151
logrus.WithField("dir", path).WithField("location", location.String()).Info("Adding libraries dir")
152-
sc.LibrariesDir = append(sc.LibrariesDir, &LibrariesDir{
152+
lm.LibrariesDir = append(lm.LibrariesDir, &LibrariesDir{
153153
Path: path,
154154
Location: location,
155155
PlatformRelease: plaftormRelease,
156156
})
157157
}
158158

159159
// RescanLibraries reload all installed libraries in the system.
160-
func (sc *LibrariesManager) RescanLibraries() error {
161-
for _, dir := range sc.LibrariesDir {
162-
if err := sc.LoadLibrariesFromDir(dir); err != nil {
160+
func (lm *LibrariesManager) RescanLibraries() error {
161+
for _, dir := range lm.LibrariesDir {
162+
if err := lm.LoadLibrariesFromDir(dir); err != nil {
163163
return fmt.Errorf("loading libs from %s: %s", dir.Path, err)
164164
}
165165
}
166166
return nil
167167
}
168168

169-
func (sc *LibrariesManager) getUserLibrariesDir() *paths.Path {
170-
for _, dir := range sc.LibrariesDir {
169+
func (lm *LibrariesManager) getUserLibrariesDir() *paths.Path {
170+
for _, dir := range lm.LibrariesDir {
171171
if dir.Location == libraries.User {
172172
return dir.Path
173173
}
@@ -177,7 +177,7 @@ func (sc *LibrariesManager) getUserLibrariesDir() *paths.Path {
177177

178178
// LoadLibrariesFromDir loads all libraries in the given directory. Returns
179179
// nil if the directory doesn't exists.
180-
func (sc *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) error {
180+
func (lm *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) error {
181181
subDirs, err := librariesDir.Path.ReadDir()
182182
if os.IsNotExist(err) {
183183
return nil
@@ -194,22 +194,45 @@ func (sc *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) err
194194
return fmt.Errorf("loading library from %s: %s", subDir, err)
195195
}
196196
library.ContainerPlatform = librariesDir.PlatformRelease
197-
alternatives, ok := sc.Libraries[library.Name]
197+
alternatives, ok := lm.Libraries[library.Name]
198198
if !ok {
199199
alternatives = &LibraryAlternatives{}
200-
sc.Libraries[library.Name] = alternatives
200+
lm.Libraries[library.Name] = alternatives
201201
}
202202
alternatives.Add(library)
203203
}
204204
return nil
205205
}
206206

207+
// LoadLibraryFromDir loads one single library from the libRootDir.
208+
// libRootDir must point to the root of a valid library.
209+
// An error is returned if the path doesn't exist or loading of the library fails.
210+
func (lm *LibrariesManager) LoadLibraryFromDir(libRootDir *paths.Path, location libraries.LibraryLocation) error {
211+
if libRootDir.NotExist() {
212+
return fmt.Errorf("library path does not exist: %s", libRootDir)
213+
}
214+
215+
library, err := libraries.Load(libRootDir, location)
216+
if err != nil {
217+
return fmt.Errorf("loading library from %s: %s", libRootDir, err)
218+
}
219+
220+
alternatives, ok := lm.Libraries[library.Name]
221+
if !ok {
222+
alternatives = &LibraryAlternatives{}
223+
lm.Libraries[library.Name] = alternatives
224+
}
225+
alternatives.Add(library)
226+
227+
return nil
228+
}
229+
207230
// FindByReference return the installed library matching the Reference
208231
// name and version or, if the version is nil, the library installed
209232
// in the User folder.
210-
func (sc *LibrariesManager) FindByReference(libRef *librariesindex.Reference) *libraries.Library {
233+
func (lm *LibrariesManager) FindByReference(libRef *librariesindex.Reference) *libraries.Library {
211234
saneName := utils.SanitizeName(libRef.Name)
212-
alternatives, have := sc.Libraries[saneName]
235+
alternatives, have := lm.Libraries[saneName]
213236
if !have {
214237
return nil
215238
}

Diff for: arduino/libraries/librariesresolver/cpp.go

+2
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,8 @@ func computePriority(lib *libraries.Library, header, arch string) int {
158158
priority += 2
159159
case libraries.User:
160160
priority += 3
161+
case libraries.Unmanaged:
162+
priority += 4
161163
default:
162164
panic(fmt.Sprintf("Invalid library location: %d", lib.Location))
163165
}

Diff for: cli/compile/compile.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,16 @@ var (
5151
port string // Upload port, e.g.: COM10 or /dev/ttyACM0.
5252
verify bool // Upload, verify uploaded binary after the upload.
5353
exportDir string // The compiled binary is written to this file
54-
libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.
5554
optimizeForDebug bool // Optimize compile output for debug, not for release
5655
programmer string // Use the specified programmer to upload
5756
clean bool // Cleanup the build folder and do not use any cached build
5857
compilationDatabaseOnly bool // Only create compilation database without actually compiling
5958
sourceOverrides string // Path to a .json file that contains a set of replacements of the sketch source code.
59+
// library and libraries sound similar but they're actually different.
60+
// library expects a path to the root folder of one single library.
61+
// libraries expects a path to a directory containing multiple libraries, similarly to the <directories.user>/libraries path.
62+
library []string // List of paths to libraries root folders. Can be used multiple times for different libraries
63+
libraries []string // List of custom libraries dir paths separated by commas. Or can be used multiple times for multiple libraries paths.
6064
)
6165

6266
// NewCommand created a new `compile` command
@@ -93,8 +97,10 @@ func NewCommand() *cobra.Command {
9397
command.Flags().StringVarP(&port, "port", "p", "", "Upload port, e.g.: COM10 or /dev/ttyACM0")
9498
command.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
9599
command.Flags().StringVar(&vidPid, "vid-pid", "", "When specified, VID/PID specific build properties are used, if board supports them.")
100+
command.Flags().StringSliceVar(&library, "library", []string{},
101+
"List of paths to libraries root folders. Libraries set this way have top priority in case of conflicts. Can be used multiple times for different libraries.")
96102
command.Flags().StringSliceVar(&libraries, "libraries", []string{},
97-
"List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.")
103+
"List of custom libraries dir paths separated by commas. Or can be used multiple times for multiple libraries dir paths.")
98104
command.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, "Optional, optimize compile output for debugging, rather than for release.")
99105
command.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.")
100106
command.Flags().BoolVar(&compilationDatabaseOnly, "only-compilation-database", false, "Just produce the compilation database, without actually compiling.")
@@ -171,6 +177,7 @@ func run(cmd *cobra.Command, args []string) {
171177
Clean: clean,
172178
CreateCompilationDatabaseOnly: compilationDatabaseOnly,
173179
SourceOverride: overrides,
180+
Library: library,
174181
}
175182
compileOut := new(bytes.Buffer)
176183
compileErr := new(bytes.Buffer)

Diff for: commands/compile/compile.go

+2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
137137
builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...)
138138
builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings))
139139

140+
builderCtx.LibraryDirs = paths.NewPathList(req.Library...)
141+
140142
if req.GetBuildPath() == "" {
141143
builderCtx.BuildPath = bldr.GenBuildPath(sketch.FullPath)
142144
} else {

Diff for: docs/sketch-build-process.md

+2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ The "folder name priority" is determined as follows (in order of highest to lowe
102102

103103
The "location priority" is determined as follows (in order of highest to lowest priority):
104104

105+
1. The library is specified using the [`--library` option](commands/arduino-cli_compile.md#options) of
106+
`arduino-cli compile`
105107
1. The library is under a custom libraries path specified via the
106108
[`--libraries` option](commands/arduino-cli_compile.md#options) of `arduino-cli compile` (in decreasing order of
107109
priority when multiple custom paths are defined)

Diff for: legacy/builder/builder_utils/utils.go

+10
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,16 @@ func ObjFileIsUpToDate(ctx *types.Context, sourceFile, objectFile, dependencyFil
357357
return false, nil
358358
}
359359

360+
// The first line of the depfile contains the path to the object file to generate.
361+
// The second line of the depfile contains the path to the source file.
362+
// All subsequent lines contain the header files necessary to compile the object file.
363+
364+
// If we don't do this check it might happen that trying to compile a source file
365+
// that has the same name but a different path wouldn't recreate the object file.
366+
if sourceFile.String() != strings.Trim(rows[1], " ") {
367+
return false, nil
368+
}
369+
360370
rows = rows[1:]
361371
for _, row := range rows {
362372
depStat, err := os.Stat(row)

Diff for: legacy/builder/libraries_loader.go

+7
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ func (s *LibrariesLoader) Run(ctx *types.Context) error {
6161
return errors.WithStack(err)
6262
}
6363

64+
for _, dir := range ctx.LibraryDirs {
65+
// Libraries specified this way have top priority
66+
if err := lm.LoadLibraryFromDir(dir, libraries.Unmanaged); err != nil {
67+
return err
68+
}
69+
}
70+
6471
if debugLevel > 0 {
6572
for _, lib := range lm.Libraries {
6673
for _, libAlt := range lib.Alternatives {

Diff for: legacy/builder/types/context.go

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ type Context struct {
6767
BuiltInToolsDirs paths.PathList
6868
BuiltInLibrariesDirs paths.PathList
6969
OtherLibrariesDirs paths.PathList
70+
LibraryDirs paths.PathList // List of paths pointing to individual library root folders
7071
SketchLocation *paths.Path
7172
WatchedLocations paths.PathList
7273
ArduinoAPIVersion string

Diff for: rpc/cc/arduino/cli/commands/v1/board.pb.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: rpc/cc/arduino/cli/commands/v1/commands.pb.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: rpc/cc/arduino/cli/commands/v1/common.pb.go

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)