Skip to content

Add --library flag to compile command #1258

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions arduino/libraries/libraries_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const (
ReferencedPlatformBuiltIn
// User are user installed libraries
User
// Unmanaged is for libraries set manually by the user in the CLI command or from the gRPC function.
// Ideally it's used for `libraries` outside folders managed by the CLI.
Unmanaged
)

func (d *LibraryLocation) String() string {
Expand All @@ -47,6 +50,8 @@ func (d *LibraryLocation) String() string {
return "ref-platform"
case User:
return "user"
case Unmanaged:
return "unmanaged"
}
panic(fmt.Sprintf("invalid LibraryLocation value %d", *d))
}
Expand All @@ -62,6 +67,8 @@ func (d *LibraryLocation) MarshalJSON() ([]byte, error) {
return json.Marshal("ref-platform")
case User:
return json.Marshal("user")
case Unmanaged:
return json.Marshal("unmanaged")
}
return nil, fmt.Errorf("invalid library location value: %d", *d)
}
Expand All @@ -81,6 +88,8 @@ func (d *LibraryLocation) UnmarshalJSON(b []byte) error {
*d = ReferencedPlatformBuiltIn
case "user":
*d = User
case "unmanaged":
*d = Unmanaged
}
return fmt.Errorf("invalid library location: %s", s)
}
Expand All @@ -96,6 +105,8 @@ func (d *LibraryLocation) ToRPCLibraryLocation() rpc.LibraryLocation {
return rpc.LibraryLocation_LIBRARY_LOCATION_REFERENCED_PLATFORM_BUILTIN
case User:
return rpc.LibraryLocation_LIBRARY_LOCATION_USER
case Unmanaged:
return rpc.LibraryLocation_LIBRARY_LOCATION_UNMANAGED
}
panic(fmt.Sprintf("invalid LibraryLocation value %d", *d))
}
Expand All @@ -111,6 +122,8 @@ func FromRPCLibraryLocation(l rpc.LibraryLocation) LibraryLocation {
return ReferencedPlatformBuiltIn
case rpc.LibraryLocation_LIBRARY_LOCATION_USER:
return User
case rpc.LibraryLocation_LIBRARY_LOCATION_UNMANAGED:
return Unmanaged
}
panic(fmt.Sprintf("invalid rpc.LibraryLocation value %d", l))
}
69 changes: 46 additions & 23 deletions arduino/libraries/librariesmanager/librariesmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ func (alts *LibraryAlternatives) FindVersion(version *semver.Version) *libraries
}

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

// LoadIndex reads a library_index.json from a file and returns
// the corresponding Index structure.
func (sc *LibrariesManager) LoadIndex() error {
index, err := librariesindex.LoadIndex(sc.IndexFile)
func (lm *LibrariesManager) LoadIndex() error {
index, err := librariesindex.LoadIndex(lm.IndexFile)
if err != nil {
sc.Index = librariesindex.EmptyIndex
lm.Index = librariesindex.EmptyIndex
return err
}
sc.Index = index
lm.Index = index
return nil
}

// AddLibrariesDir adds path to the list of directories
// to scan when searching for libraries. If a path is already
// in the list it is ignored.
func (sc *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries.LibraryLocation) {
for _, dir := range sc.LibrariesDir {
func (lm *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries.LibraryLocation) {
for _, dir := range lm.LibrariesDir {
if dir.Path.EquivalentTo(path) {
return
}
}
logrus.WithField("dir", path).WithField("location", location.String()).Info("Adding libraries dir")
sc.LibrariesDir = append(sc.LibrariesDir, &LibrariesDir{
lm.LibrariesDir = append(lm.LibrariesDir, &LibrariesDir{
Path: path,
Location: location,
})
Expand All @@ -138,36 +138,36 @@ func (sc *LibrariesManager) AddLibrariesDir(path *paths.Path, location libraries
// AddPlatformReleaseLibrariesDir add the libraries directory in the
// specified PlatformRelease to the list of directories to scan when
// searching for libraries.
func (sc *LibrariesManager) AddPlatformReleaseLibrariesDir(plaftormRelease *cores.PlatformRelease, location libraries.LibraryLocation) {
func (lm *LibrariesManager) AddPlatformReleaseLibrariesDir(plaftormRelease *cores.PlatformRelease, location libraries.LibraryLocation) {
path := plaftormRelease.GetLibrariesDir()
if path == nil {
return
}
for _, dir := range sc.LibrariesDir {
for _, dir := range lm.LibrariesDir {
if dir.Path.EquivalentTo(path) {
return
}
}
logrus.WithField("dir", path).WithField("location", location.String()).Info("Adding libraries dir")
sc.LibrariesDir = append(sc.LibrariesDir, &LibrariesDir{
lm.LibrariesDir = append(lm.LibrariesDir, &LibrariesDir{
Path: path,
Location: location,
PlatformRelease: plaftormRelease,
})
}

// RescanLibraries reload all installed libraries in the system.
func (sc *LibrariesManager) RescanLibraries() error {
for _, dir := range sc.LibrariesDir {
if err := sc.LoadLibrariesFromDir(dir); err != nil {
func (lm *LibrariesManager) RescanLibraries() error {
for _, dir := range lm.LibrariesDir {
if err := lm.LoadLibrariesFromDir(dir); err != nil {
return fmt.Errorf("loading libs from %s: %s", dir.Path, err)
}
}
return nil
}

func (sc *LibrariesManager) getUserLibrariesDir() *paths.Path {
for _, dir := range sc.LibrariesDir {
func (lm *LibrariesManager) getUserLibrariesDir() *paths.Path {
for _, dir := range lm.LibrariesDir {
if dir.Location == libraries.User {
return dir.Path
}
Expand All @@ -177,7 +177,7 @@ func (sc *LibrariesManager) getUserLibrariesDir() *paths.Path {

// LoadLibrariesFromDir loads all libraries in the given directory. Returns
// nil if the directory doesn't exists.
func (sc *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) error {
func (lm *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) error {
subDirs, err := librariesDir.Path.ReadDir()
if os.IsNotExist(err) {
return nil
Expand All @@ -194,22 +194,45 @@ func (sc *LibrariesManager) LoadLibrariesFromDir(librariesDir *LibrariesDir) err
return fmt.Errorf("loading library from %s: %s", subDir, err)
}
library.ContainerPlatform = librariesDir.PlatformRelease
alternatives, ok := sc.Libraries[library.Name]
alternatives, ok := lm.Libraries[library.Name]
if !ok {
alternatives = &LibraryAlternatives{}
sc.Libraries[library.Name] = alternatives
lm.Libraries[library.Name] = alternatives
}
alternatives.Add(library)
}
return nil
}

// LoadLibraryFromDir loads one single library from the libRootDir.
// libRootDir must point to the root of a valid library.
// An error is returned if the path doesn't exist or loading of the library fails.
func (lm *LibrariesManager) LoadLibraryFromDir(libRootDir *paths.Path, location libraries.LibraryLocation) error {
if libRootDir.NotExist() {
return fmt.Errorf("library path does not exist: %s", libRootDir)
}

library, err := libraries.Load(libRootDir, location)
if err != nil {
return fmt.Errorf("loading library from %s: %s", libRootDir, err)
}

alternatives, ok := lm.Libraries[library.Name]
if !ok {
alternatives = &LibraryAlternatives{}
lm.Libraries[library.Name] = alternatives
}
alternatives.Add(library)

return nil
}

// FindByReference return the installed library matching the Reference
// name and version or, if the version is nil, the library installed
// in the User folder.
func (sc *LibrariesManager) FindByReference(libRef *librariesindex.Reference) *libraries.Library {
func (lm *LibrariesManager) FindByReference(libRef *librariesindex.Reference) *libraries.Library {
saneName := utils.SanitizeName(libRef.Name)
alternatives, have := sc.Libraries[saneName]
alternatives, have := lm.Libraries[saneName]
if !have {
return nil
}
Expand Down
2 changes: 2 additions & 0 deletions arduino/libraries/librariesresolver/cpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ func computePriority(lib *libraries.Library, header, arch string) int {
priority += 2
case libraries.User:
priority += 3
case libraries.Unmanaged:
priority += 4
default:
panic(fmt.Sprintf("Invalid library location: %d", lib.Location))
}
Expand Down
11 changes: 9 additions & 2 deletions cli/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,16 @@ var (
port string // Upload port, e.g.: COM10 or /dev/ttyACM0.
verify bool // Upload, verify uploaded binary after the upload.
exportDir string // The compiled binary is written to this file
libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.
optimizeForDebug bool // Optimize compile output for debug, not for release
programmer string // Use the specified programmer to upload
clean bool // Cleanup the build folder and do not use any cached build
compilationDatabaseOnly bool // Only create compilation database without actually compiling
sourceOverrides string // Path to a .json file that contains a set of replacements of the sketch source code.
// library and libraries sound similar but they're actually different.
// library expects a path to the root folder of one single library.
// libraries expects a path to a directory containing multiple libraries, similarly to the <directories.user>/libraries path.
library []string // List of paths to libraries root folders. Can be used multiple times for different libraries
libraries []string // List of custom libraries dir paths separated by commas. Or can be used multiple times for multiple libraries paths.
)

// NewCommand created a new `compile` command
Expand Down Expand Up @@ -93,8 +97,10 @@ func NewCommand() *cobra.Command {
command.Flags().StringVarP(&port, "port", "p", "", "Upload port, e.g.: COM10 or /dev/ttyACM0")
command.Flags().BoolVarP(&verify, "verify", "t", false, "Verify uploaded binary after the upload.")
command.Flags().StringVar(&vidPid, "vid-pid", "", "When specified, VID/PID specific build properties are used, if board supports them.")
command.Flags().StringSliceVar(&library, "library", []string{},
"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.")
command.Flags().StringSliceVar(&libraries, "libraries", []string{},
"List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.")
"List of custom libraries dir paths separated by commas. Or can be used multiple times for multiple libraries dir paths.")
Comment on lines +100 to +103
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering about the mixture of "folder" and "dir" ("directory") terms in the descriptions of these two closely related flags. Is that intentional?

Even though I like the term "directory" better (probably due to it being the term used when I started with computers), I have made a decision to prefer "folder" in my own communications because all directories are folders, but not all folders are directories and it's too confusing to me to try to figure out whether "directory" will always be applicable for the specific situation. But "directory" is used more often in the Arduino CLI documentation, and is part of the configuration keys.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use them interchangeably, with this change I wanted to make it clear that --libraries must point to a single directory containing multiple libraries folders but am not sure I made it clearer.

I'm open to suggestions.

command.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, "Optional, optimize compile output for debugging, rather than for release.")
command.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.")
command.Flags().BoolVar(&compilationDatabaseOnly, "only-compilation-database", false, "Just produce the compilation database, without actually compiling.")
Expand Down Expand Up @@ -171,6 +177,7 @@ func run(cmd *cobra.Command, args []string) {
Clean: clean,
CreateCompilationDatabaseOnly: compilationDatabaseOnly,
SourceOverride: overrides,
Library: library,
}
compileOut := new(bytes.Buffer)
compileErr := new(bytes.Buffer)
Expand Down
2 changes: 2 additions & 0 deletions commands/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...)
builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings))

builderCtx.LibraryDirs = paths.NewPathList(req.Library...)

if req.GetBuildPath() == "" {
builderCtx.BuildPath = bldr.GenBuildPath(sketch.FullPath)
} else {
Expand Down
2 changes: 2 additions & 0 deletions docs/sketch-build-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ The "folder name priority" is determined as follows (in order of highest to lowe

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

1. The library is specified using the [`--library` option](commands/arduino-cli_compile.md#options) of
`arduino-cli compile`
1. The library is under a custom libraries path specified via the
[`--libraries` option](commands/arduino-cli_compile.md#options) of `arduino-cli compile` (in decreasing order of
priority when multiple custom paths are defined)
Expand Down
10 changes: 10 additions & 0 deletions legacy/builder/builder_utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,16 @@ func ObjFileIsUpToDate(ctx *types.Context, sourceFile, objectFile, dependencyFil
return false, nil
}

// The first line of the depfile contains the path to the object file to generate.
// The second line of the depfile contains the path to the source file.
// All subsequent lines contain the header files necessary to compile the object file.

// If we don't do this check it might happen that trying to compile a source file
// that has the same name but a different path wouldn't recreate the object file.
if sourceFile.String() != strings.Trim(rows[1], " ") {
return false, nil
}

rows = rows[1:]
for _, row := range rows {
depStat, err := os.Stat(row)
Expand Down
7 changes: 7 additions & 0 deletions legacy/builder/libraries_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@ func (s *LibrariesLoader) Run(ctx *types.Context) error {
return errors.WithStack(err)
}

for _, dir := range ctx.LibraryDirs {
// Libraries specified this way have top priority
if err := lm.LoadLibraryFromDir(dir, libraries.Unmanaged); err != nil {
return err
}
}

if debugLevel > 0 {
for _, lib := range lm.Libraries {
for _, libAlt := range lib.Alternatives {
Expand Down
1 change: 1 addition & 0 deletions legacy/builder/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ type Context struct {
BuiltInToolsDirs paths.PathList
BuiltInLibrariesDirs paths.PathList
OtherLibrariesDirs paths.PathList
LibraryDirs paths.PathList // List of paths pointing to individual library root folders
SketchLocation *paths.Path
WatchedLocations paths.PathList
ArduinoAPIVersion string
Expand Down
2 changes: 1 addition & 1 deletion rpc/cc/arduino/cli/commands/v1/board.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rpc/cc/arduino/cli/commands/v1/commands.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion rpc/cc/arduino/cli/commands/v1/common.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading