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 4b2a32b

Browse files
authoredOct 19, 2023
[skip-changelog] refactor: Made command functions to access PackageManager unavailable from public API (#2335)
* Let instance commands require an *rpc.Instance Removed the shorthand of the InstanceCommand interface. This makes the API more coherent among the `commands` instance handling. * Moved 'command' package's instances functions inside internal package This change highlights the incorrect PackageManager access in 'cli' package. Now those are errors: package github.com/arduino/arduino-cli imports github.com/arduino/arduino-cli/internal/cli imports github.com/arduino/arduino-cli/internal/cli/board imports github.com/arduino/arduino-cli/internal/cli/arguments internal/cli/arguments/completion.go:23:2: use of internal package github.com/arduino/arduino-cli/commands/internal/instances not allowed package github.com/arduino/arduino-cli imports github.com/arduino/arduino-cli/internal/cli imports github.com/arduino/arduino-cli/internal/cli/board imports github.com/arduino/arduino-cli/internal/cli/arguments internal/cli/arguments/port.go:24:2: use of internal package github.com/arduino/arduino-cli/commands/internal/instances not allowed * Made CoreInstance private * Inlined coreInstancesContainer singleton methods * Removed all wrong accesses to package Explorer * Replaced DiscoveryManager.Watch with equivalent gRPC BoardListWatch call * Replaced direct access to PackageManager to get discovery protocols Previously the function GetConnectedBoards() counter-intuitively returned a list of port address. Now it has been reneamed GetAvailablePorts() and returns the full Port object that is mapped into an array of Addresses or into an array of Prorocols based on the auto-completion request.
1 parent 1e51cdc commit 4b2a32b

File tree

31 files changed

+237
-271
lines changed

31 files changed

+237
-271
lines changed
 

‎commands/board/details.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ import (
2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/arduino/cores"
2323
"github.com/arduino/arduino-cli/arduino/utils"
24-
"github.com/arduino/arduino-cli/commands"
24+
"github.com/arduino/arduino-cli/commands/internal/instances"
2525
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2626
)
2727

2828
// Details returns all details for a board including tools and HW identifiers.
2929
// This command basically gather al the information and translates it into the required grpc struct properties
3030
func Details(ctx context.Context, req *rpc.BoardDetailsRequest) (*rpc.BoardDetailsResponse, error) {
31-
pme, release := commands.GetPackageManagerExplorer(req)
31+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3232
if pme == nil {
3333
return nil, &arduino.InvalidInstanceError{}
3434
}

‎commands/board/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
3232
"github.com/arduino/arduino-cli/arduino/discovery"
3333
"github.com/arduino/arduino-cli/arduino/httpclient"
34-
"github.com/arduino/arduino-cli/commands"
34+
"github.com/arduino/arduino-cli/commands/internal/instances"
3535
"github.com/arduino/arduino-cli/internal/inventory"
3636
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3737
"github.com/arduino/go-properties-orderedmap"
@@ -203,7 +203,7 @@ func identify(pme *packagemanager.Explorer, port *discovery.Port) ([]*rpc.BoardL
203203
// In case of errors partial results from discoveries that didn't fail
204204
// are returned.
205205
func List(req *rpc.BoardListRequest) (r []*rpc.DetectedPort, discoveryStartErrors []error, e error) {
206-
pme, release := commands.GetPackageManagerExplorer(req)
206+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
207207
if pme == nil {
208208
return nil, nil, &arduino.InvalidInstanceError{}
209209
}
@@ -258,7 +258,7 @@ func hasMatchingBoard(b *rpc.DetectedPort, fqbnFilter *cores.FQBN) bool {
258258

259259
// Watch returns a channel that receives boards connection and disconnection events.
260260
func Watch(ctx context.Context, req *rpc.BoardListWatchRequest) (<-chan *rpc.BoardListWatchResponse, error) {
261-
pme, release := commands.GetPackageManagerExplorer(req)
261+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
262262
if pme == nil {
263263
return nil, &arduino.InvalidInstanceError{}
264264
}

‎commands/board/listall.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ import (
2323
"github.com/arduino/arduino-cli/arduino"
2424
"github.com/arduino/arduino-cli/arduino/cores"
2525
"github.com/arduino/arduino-cli/arduino/utils"
26-
"github.com/arduino/arduino-cli/commands"
26+
"github.com/arduino/arduino-cli/commands/internal/instances"
2727
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2828
)
2929

3030
// ListAll FIXMEDOC
3131
func ListAll(ctx context.Context, req *rpc.BoardListAllRequest) (*rpc.BoardListAllResponse, error) {
32-
pme, release := commands.GetPackageManagerExplorer(req)
32+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3333
if pme == nil {
3434
return nil, &arduino.InvalidInstanceError{}
3535
}

‎commands/board/search.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222

2323
"github.com/arduino/arduino-cli/arduino"
2424
"github.com/arduino/arduino-cli/arduino/utils"
25-
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2626
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2727
)
2828

@@ -31,7 +31,7 @@ import (
3131
// installed. Note that platforms that are not installed don't include boards' FQBNs.
3232
// If no search argument is used all boards are returned.
3333
func Search(ctx context.Context, req *rpc.BoardSearchRequest) (*rpc.BoardSearchResponse, error) {
34-
pme, release := commands.GetPackageManagerExplorer(req)
34+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3535
if pme == nil {
3636
return nil, &arduino.InvalidInstanceError{}
3737
}

‎commands/compile/compile.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
"github.com/arduino/arduino-cli/arduino/sketch"
3131
"github.com/arduino/arduino-cli/arduino/utils"
3232
"github.com/arduino/arduino-cli/buildcache"
33-
"github.com/arduino/arduino-cli/commands"
33+
"github.com/arduino/arduino-cli/commands/internal/instances"
3434
"github.com/arduino/arduino-cli/configuration"
3535
"github.com/arduino/arduino-cli/i18n"
3636
"github.com/arduino/arduino-cli/internal/inventory"
@@ -57,13 +57,13 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
5757
exportBinaries = reqExportBinaries.Value
5858
}
5959

60-
pme, release := commands.GetPackageManagerExplorer(req)
60+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
6161
if pme == nil {
6262
return nil, &arduino.InvalidInstanceError{}
6363
}
6464
defer release()
6565

66-
lm := commands.GetLibraryManager(req)
66+
lm := instances.GetLibraryManager(req.GetInstance())
6767
if lm == nil {
6868
return nil, &arduino.InvalidInstanceError{}
6969
}

‎commands/core/download.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2323
"github.com/arduino/arduino-cli/commands"
24+
"github.com/arduino/arduino-cli/commands/internal/instances"
2425
"github.com/arduino/arduino-cli/i18n"
2526
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2627
)
@@ -29,7 +30,7 @@ var tr = i18n.Tr
2930

3031
// PlatformDownload FIXMEDOC
3132
func PlatformDownload(ctx context.Context, req *rpc.PlatformDownloadRequest, downloadCB rpc.DownloadProgressCB) (*rpc.PlatformDownloadResponse, error) {
32-
pme, release := commands.GetPackageManagerExplorer(req)
33+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3334
if pme == nil {
3435
return nil, &arduino.InvalidInstanceError{}
3536
}

‎commands/core/install.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import (
2222
"github.com/arduino/arduino-cli/arduino"
2323
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2424
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2526
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2627
)
2728

2829
// PlatformInstall FIXMEDOC
2930
func PlatformInstall(ctx context.Context, req *rpc.PlatformInstallRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*rpc.PlatformInstallResponse, error) {
3031
install := func() error {
31-
pme, release := commands.GetPackageManagerExplorer(req)
32+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3233
if pme == nil {
3334
return &arduino.InvalidInstanceError{}
3435
}

‎commands/core/list.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,14 @@ import (
2222

2323
"github.com/arduino/arduino-cli/arduino"
2424
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2526
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2627
)
2728

2829
// PlatformList returns a list of installed platforms, optionally filtered by
2930
// those requiring an update.
3031
func PlatformList(req *rpc.PlatformListRequest) (*rpc.PlatformListResponse, error) {
31-
pme, release := commands.GetPackageManagerExplorer(req)
32+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3233
if pme == nil {
3334
return nil, &arduino.InvalidInstanceError{}
3435
}

‎commands/core/search.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ import (
2424
"github.com/arduino/arduino-cli/arduino/cores"
2525
"github.com/arduino/arduino-cli/arduino/utils"
2626
"github.com/arduino/arduino-cli/commands"
27+
"github.com/arduino/arduino-cli/commands/internal/instances"
2728
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2829
)
2930

3031
// PlatformSearch FIXMEDOC
3132
func PlatformSearch(req *rpc.PlatformSearchRequest) (*rpc.PlatformSearchResponse, error) {
32-
pme, release := commands.GetPackageManagerExplorer(req)
33+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3334
if pme == nil {
3435
return nil, &arduino.InvalidInstanceError{}
3536
}

‎commands/core/uninstall.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2323
"github.com/arduino/arduino-cli/commands"
24+
"github.com/arduino/arduino-cli/commands/internal/instances"
2425
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2526
)
2627

@@ -37,7 +38,7 @@ func PlatformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, t
3738

3839
// platformUninstall is the implementation of platform unistaller
3940
func platformUninstall(ctx context.Context, req *rpc.PlatformUninstallRequest, taskCB rpc.TaskProgressCB) error {
40-
pme, release := commands.GetPackageManagerExplorer(req)
41+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
4142
if pme == nil {
4243
return &arduino.InvalidInstanceError{}
4344
}

‎commands/core/upgrade.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,18 @@ package core
1818
import (
1919
"context"
2020

21-
"github.com/arduino/arduino-cli/arduino/cores"
22-
2321
"github.com/arduino/arduino-cli/arduino"
22+
"github.com/arduino/arduino-cli/arduino/cores"
2423
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2524
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2626
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2727
)
2828

2929
// PlatformUpgrade FIXMEDOC
3030
func PlatformUpgrade(ctx context.Context, req *rpc.PlatformUpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) (*rpc.PlatformUpgradeResponse, error) {
3131
upgrade := func() (*cores.PlatformRelease, error) {
32-
pme, release := commands.GetPackageManagerExplorer(req)
32+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3333
if pme == nil {
3434
return nil, &arduino.InvalidInstanceError{}
3535
}

‎commands/debug/debug.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626

2727
"github.com/arduino/arduino-cli/arduino"
2828
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
29-
"github.com/arduino/arduino-cli/commands"
29+
"github.com/arduino/arduino-cli/commands/internal/instances"
3030
"github.com/arduino/arduino-cli/executils"
3131
"github.com/arduino/arduino-cli/i18n"
3232
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
@@ -45,7 +45,7 @@ var tr = i18n.Tr
4545
func Debug(ctx context.Context, req *rpc.GetDebugConfigRequest, inStream io.Reader, out io.Writer, interrupt <-chan os.Signal) (*rpc.DebugResponse, error) {
4646

4747
// Get debugging command line to run debugger
48-
pme, release := commands.GetPackageManagerExplorer(req)
48+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
4949
if pme == nil {
5050
return nil, &arduino.InvalidInstanceError{}
5151
}

‎commands/debug/debug_info.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"github.com/arduino/arduino-cli/arduino/cores"
2626
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2727
"github.com/arduino/arduino-cli/arduino/sketch"
28-
"github.com/arduino/arduino-cli/commands"
28+
"github.com/arduino/arduino-cli/commands/internal/instances"
2929
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3030
"github.com/arduino/go-paths-helper"
3131
"github.com/arduino/go-properties-orderedmap"
@@ -35,7 +35,7 @@ import (
3535

3636
// GetDebugConfig returns metadata to start debugging with the specified board
3737
func GetDebugConfig(ctx context.Context, req *rpc.GetDebugConfigRequest) (*rpc.GetDebugConfigResponse, error) {
38-
pme, release := commands.GetPackageManagerExplorer(req)
38+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3939
if pme == nil {
4040
return nil, &arduino.InvalidInstanceError{}
4141
}

‎commands/instances.go

Lines changed: 13 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"net/url"
2222
"path/filepath"
2323
"strings"
24-
"sync"
2524

2625
"github.com/arduino/arduino-cli/arduino"
2726
"github.com/arduino/arduino-cli/arduino/cores"
@@ -34,10 +33,10 @@ import (
3433
"github.com/arduino/arduino-cli/arduino/resources"
3534
"github.com/arduino/arduino-cli/arduino/sketch"
3635
"github.com/arduino/arduino-cli/arduino/utils"
36+
"github.com/arduino/arduino-cli/commands/internal/instances"
3737
"github.com/arduino/arduino-cli/configuration"
3838
"github.com/arduino/arduino-cli/i18n"
3939
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
40-
"github.com/arduino/arduino-cli/version"
4140
paths "github.com/arduino/go-paths-helper"
4241
"github.com/sirupsen/logrus"
4342
"google.golang.org/grpc/codes"
@@ -46,90 +45,6 @@ import (
4645

4746
var tr = i18n.Tr
4847

49-
// CoreInstance is an instance of the Arduino Core Services. The user can
50-
// instantiate as many as needed by providing a different configuration
51-
// for each one.
52-
type CoreInstance struct {
53-
pm *packagemanager.PackageManager
54-
lm *librariesmanager.LibrariesManager
55-
}
56-
57-
// coreInstancesContainer has methods to add an remove instances atomically.
58-
type coreInstancesContainer struct {
59-
instances map[int32]*CoreInstance
60-
instancesCount int32
61-
instancesMux sync.Mutex
62-
}
63-
64-
// instances contains all the running Arduino Core Services instances
65-
var instances = &coreInstancesContainer{
66-
instances: map[int32]*CoreInstance{},
67-
instancesCount: 1,
68-
}
69-
70-
// GetInstance returns a CoreInstance for the given ID, or nil if ID
71-
// doesn't exist
72-
func (c *coreInstancesContainer) GetInstance(id int32) *CoreInstance {
73-
c.instancesMux.Lock()
74-
defer c.instancesMux.Unlock()
75-
return c.instances[id]
76-
}
77-
78-
// AddAndAssignID saves the CoreInstance and assigns a unique ID to
79-
// retrieve it later
80-
func (c *coreInstancesContainer) AddAndAssignID(i *CoreInstance) int32 {
81-
c.instancesMux.Lock()
82-
defer c.instancesMux.Unlock()
83-
id := c.instancesCount
84-
c.instances[id] = i
85-
c.instancesCount++
86-
return id
87-
}
88-
89-
// RemoveID removes the CoreInstance referenced by id. Returns true
90-
// if the operation is successful, or false if the CoreInstance does
91-
// not exist
92-
func (c *coreInstancesContainer) RemoveID(id int32) bool {
93-
c.instancesMux.Lock()
94-
defer c.instancesMux.Unlock()
95-
if _, ok := c.instances[id]; !ok {
96-
return false
97-
}
98-
delete(c.instances, id)
99-
return true
100-
}
101-
102-
// GetPackageManager returns a PackageManager. If the package manager is not found
103-
// (because the instance is invalid or has been destroyed), nil is returned.
104-
// Deprecated: use GetPackageManagerExplorer instead.
105-
func GetPackageManager(instance rpc.InstanceCommand) *packagemanager.PackageManager {
106-
i := instances.GetInstance(instance.GetInstance().GetId())
107-
if i == nil {
108-
return nil
109-
}
110-
return i.pm
111-
}
112-
113-
// GetPackageManagerExplorer returns a new package manager Explorer. The
114-
// explorer holds a read lock on the underlying PackageManager and it should
115-
// be released by calling the returned "release" function.
116-
func GetPackageManagerExplorer(req rpc.InstanceCommand) (explorer *packagemanager.Explorer, release func()) {
117-
pm := GetPackageManager(req)
118-
if pm == nil {
119-
return nil, nil
120-
}
121-
return pm.NewExplorer()
122-
}
123-
124-
// GetLibraryManager returns the library manager for the given instance.
125-
func GetLibraryManager(req rpc.InstanceCommand) *librariesmanager.LibrariesManager {
126-
i := instances.GetInstance(req.GetInstance().GetId())
127-
if i == nil {
128-
return nil
129-
}
130-
return i.lm
131-
}
132-
13348
func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
13449
pme, release := pm.NewExplorer()
13550
defer release()
@@ -146,50 +61,11 @@ func installTool(pm *packagemanager.PackageManager, tool *cores.ToolRelease, dow
14661

14762
// Create a new CoreInstance ready to be initialized, supporting directories are also created.
14863
func Create(req *rpc.CreateRequest, extraUserAgent ...string) (*rpc.CreateResponse, error) {
149-
instance := &CoreInstance{}
150-
151-
// Setup downloads directory
152-
downloadsDir := configuration.DownloadsDir(configuration.Settings)
153-
if downloadsDir.NotExist() {
154-
err := downloadsDir.MkdirAll()
155-
if err != nil {
156-
return nil, &arduino.PermissionDeniedError{Message: tr("Failed to create downloads directory"), Cause: err}
157-
}
158-
}
159-
160-
// Setup data directory
161-
dataDir := configuration.DataDir(configuration.Settings)
162-
packagesDir := configuration.PackagesDir(configuration.Settings)
163-
if packagesDir.NotExist() {
164-
err := packagesDir.MkdirAll()
165-
if err != nil {
166-
return nil, &arduino.PermissionDeniedError{Message: tr("Failed to create data directory"), Cause: err}
167-
}
168-
}
169-
170-
// Create package manager
171-
userAgent := "arduino-cli/" + version.VersionInfo.VersionString
172-
for _, ua := range extraUserAgent {
173-
userAgent += " " + ua
64+
inst, err := instances.Create(extraUserAgent...)
65+
if err != nil {
66+
return nil, err
17467
}
175-
instance.pm = packagemanager.NewBuilder(
176-
dataDir,
177-
configuration.PackagesDir(configuration.Settings),
178-
downloadsDir,
179-
dataDir.Join("tmp"),
180-
userAgent,
181-
).Build()
182-
instance.lm = librariesmanager.NewLibraryManager(
183-
dataDir,
184-
downloadsDir,
185-
)
186-
187-
// Save instance
188-
instanceID := instances.AddAndAssignID(instance)
189-
190-
return &rpc.CreateResponse{
191-
Instance: &rpc.Instance{Id: instanceID},
192-
}, nil
68+
return &rpc.CreateResponse{Instance: inst}, nil
19369
}
19470

19571
// Init loads installed libraries and Platforms in CoreInstance with specified ID,
@@ -205,8 +81,8 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
20581
if reqInst == nil {
20682
return &arduino.InvalidInstanceError{}
20783
}
208-
instance := instances.GetInstance(reqInst.GetId())
209-
if instance == nil {
84+
instance := req.GetInstance()
85+
if !instances.IsValid(instance) {
21086
return &arduino.InvalidInstanceError{}
21187
}
21288

@@ -295,7 +171,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
295171
// after reinitializing an instance after installing or uninstalling a core.
296172
// If this is not done the information of the uninstall core is kept in memory,
297173
// even if it should not.
298-
pmb, commitPackageManager := instance.pm.NewBuilder()
174+
pmb, commitPackageManager := instances.GetPackageManager(instance).NewBuilder()
299175

300176
// Load packages index
301177
for _, URL := range allPackageIndexUrls {
@@ -390,7 +266,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
390266
commitPackageManager()
391267
}
392268

393-
pme, release := instance.pm.NewExplorer()
269+
pme, release := instances.GetPackageManagerExplorer(instance)
394270
defer release()
395271

396272
for _, err := range pme.LoadDiscoveries() {
@@ -403,7 +279,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
403279
pme.IndexDir,
404280
pme.DownloadDir,
405281
)
406-
instance.lm = lm
282+
_ = instances.SetLibraryManager(instance, lm) // should never fail
407283

408284
// Load libraries
409285
for _, pack := range pme.GetPackages() {
@@ -485,7 +361,7 @@ func Init(req *rpc.InitRequest, responseCallback func(r *rpc.InitResponse)) erro
485361

486362
// Destroy FIXMEDOC
487363
func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse, error) {
488-
if ok := instances.RemoveID(req.GetInstance().GetId()); !ok {
364+
if ok := instances.Delete(req.GetInstance()); !ok {
489365
return nil, &arduino.InvalidInstanceError{}
490366
}
491367
return &rpc.DestroyResponse{}, nil
@@ -494,7 +370,7 @@ func Destroy(ctx context.Context, req *rpc.DestroyRequest) (*rpc.DestroyResponse
494370
// UpdateLibrariesIndex updates the library_index.json
495371
func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequest, downloadCB rpc.DownloadProgressCB) error {
496372
logrus.Info("Updating libraries index")
497-
lm := GetLibraryManager(req)
373+
lm := instances.GetLibraryManager(req.GetInstance())
498374
if lm == nil {
499375
return &arduino.InvalidInstanceError{}
500376
}
@@ -523,7 +399,7 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequ
523399

524400
// UpdateIndex FIXMEDOC
525401
func UpdateIndex(ctx context.Context, req *rpc.UpdateIndexRequest, downloadCB rpc.DownloadProgressCB) error {
526-
if instances.GetInstance(req.GetInstance().GetId()) == nil {
402+
if !instances.IsValid(req.GetInstance()) {
527403
return &arduino.InvalidInstanceError{}
528404
}
529405

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package instances
2+
3+
import (
4+
"sync"
5+
6+
"github.com/arduino/arduino-cli/arduino"
7+
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
8+
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
9+
"github.com/arduino/arduino-cli/configuration"
10+
"github.com/arduino/arduino-cli/i18n"
11+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
12+
"github.com/arduino/arduino-cli/version"
13+
)
14+
15+
var tr = i18n.Tr
16+
17+
// coreInstance is an instance of the Arduino Core Services. The user can
18+
// instantiate as many as needed by providing a different configuration
19+
// for each one.
20+
type coreInstance struct {
21+
pm *packagemanager.PackageManager
22+
lm *librariesmanager.LibrariesManager
23+
}
24+
25+
// instances contains all the running Arduino Core Services instances
26+
var instances = map[int32]*coreInstance{}
27+
var instancesCount int32 = 1
28+
var instancesMux sync.Mutex
29+
30+
// GetPackageManager returns a PackageManager. If the package manager is not found
31+
// (because the instance is invalid or has been destroyed), nil is returned.
32+
// Deprecated: use GetPackageManagerExplorer instead.
33+
func GetPackageManager(inst *rpc.Instance) *packagemanager.PackageManager {
34+
instancesMux.Lock()
35+
i := instances[inst.GetId()]
36+
instancesMux.Unlock()
37+
if i == nil {
38+
return nil
39+
}
40+
return i.pm
41+
}
42+
43+
// GetPackageManagerExplorer returns a new package manager Explorer. The
44+
// explorer holds a read lock on the underlying PackageManager and it should
45+
// be released by calling the returned "release" function.
46+
func GetPackageManagerExplorer(req *rpc.Instance) (explorer *packagemanager.Explorer, release func()) {
47+
pm := GetPackageManager(req)
48+
if pm == nil {
49+
return nil, nil
50+
}
51+
return pm.NewExplorer()
52+
}
53+
54+
// GetLibraryManager returns the library manager for the given instance.
55+
func GetLibraryManager(inst *rpc.Instance) *librariesmanager.LibrariesManager {
56+
instancesMux.Lock()
57+
i := instances[inst.GetId()]
58+
instancesMux.Unlock()
59+
if i == nil {
60+
return nil
61+
}
62+
return i.lm
63+
}
64+
65+
// SetLibraryManager sets the library manager for the given instance.
66+
func SetLibraryManager(inst *rpc.Instance, lm *librariesmanager.LibrariesManager) bool {
67+
instancesMux.Lock()
68+
i := instances[inst.GetId()]
69+
instancesMux.Unlock()
70+
if i == nil {
71+
return false
72+
}
73+
i.lm = lm
74+
return true
75+
}
76+
77+
// Create a new *rpc.Instance ready to be initialized, supporting directories are also created.
78+
func Create(extraUserAgent ...string) (*rpc.Instance, error) {
79+
instance := &coreInstance{}
80+
81+
// Setup downloads directory
82+
downloadsDir := configuration.DownloadsDir(configuration.Settings)
83+
if downloadsDir.NotExist() {
84+
err := downloadsDir.MkdirAll()
85+
if err != nil {
86+
return nil, &arduino.PermissionDeniedError{Message: tr("Failed to create downloads directory"), Cause: err}
87+
}
88+
}
89+
90+
// Setup data directory
91+
dataDir := configuration.DataDir(configuration.Settings)
92+
packagesDir := configuration.PackagesDir(configuration.Settings)
93+
if packagesDir.NotExist() {
94+
err := packagesDir.MkdirAll()
95+
if err != nil {
96+
return nil, &arduino.PermissionDeniedError{Message: tr("Failed to create data directory"), Cause: err}
97+
}
98+
}
99+
100+
// Create package manager
101+
userAgent := "arduino-cli/" + version.VersionInfo.VersionString
102+
for _, ua := range extraUserAgent {
103+
userAgent += " " + ua
104+
}
105+
instance.pm = packagemanager.NewBuilder(
106+
dataDir,
107+
configuration.PackagesDir(configuration.Settings),
108+
downloadsDir,
109+
dataDir.Join("tmp"),
110+
userAgent,
111+
).Build()
112+
instance.lm = librariesmanager.NewLibraryManager(
113+
dataDir,
114+
downloadsDir,
115+
)
116+
117+
// Save instance
118+
instancesMux.Lock()
119+
id := instancesCount
120+
instances[id] = instance
121+
instancesCount++
122+
instancesMux.Unlock()
123+
124+
return &rpc.Instance{Id: id}, nil
125+
}
126+
127+
// IsValid returns true if the given instance is valid.
128+
func IsValid(inst *rpc.Instance) bool {
129+
instancesMux.Lock()
130+
i := instances[inst.GetId()]
131+
instancesMux.Unlock()
132+
return i != nil
133+
}
134+
135+
// Delete removes an instance.
136+
func Delete(inst *rpc.Instance) bool {
137+
instancesMux.Lock()
138+
defer instancesMux.Unlock()
139+
if _, ok := instances[inst.GetId()]; !ok {
140+
return false
141+
}
142+
delete(instances, inst.GetId())
143+
return true
144+
}

‎commands/lib/download.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"github.com/arduino/arduino-cli/arduino/httpclient"
2323
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2424
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
25-
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2626
"github.com/arduino/arduino-cli/i18n"
2727
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2828
"github.com/sirupsen/logrus"
@@ -35,7 +35,7 @@ var tr = i18n.Tr
3535
func LibraryDownload(ctx context.Context, req *rpc.LibraryDownloadRequest, downloadCB rpc.DownloadProgressCB) (*rpc.LibraryDownloadResponse, error) {
3636
logrus.Info("Executing `arduino-cli lib download`")
3737

38-
lm := commands.GetLibraryManager(req)
38+
lm := instances.GetLibraryManager(req.GetInstance())
3939
if lm == nil {
4040
return nil, &arduino.InvalidInstanceError{}
4141
}

‎commands/lib/install.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ import (
2525
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2626
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
2727
"github.com/arduino/arduino-cli/commands"
28+
"github.com/arduino/arduino-cli/commands/internal/instances"
2829
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2930
"github.com/arduino/go-paths-helper"
3031
"github.com/sirupsen/logrus"
3132
)
3233

3334
// LibraryInstall resolves the library dependencies, then downloads and installs the libraries into the install location.
3435
func LibraryInstall(ctx context.Context, req *rpc.LibraryInstallRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
35-
lm := commands.GetLibraryManager(req)
36+
lm := instances.GetLibraryManager(req.GetInstance())
3637
if lm == nil {
3738
return &arduino.InvalidInstanceError{}
3839
}
@@ -143,7 +144,7 @@ func installLibrary(lm *librariesmanager.LibrariesManager, libRelease *libraries
143144

144145
// ZipLibraryInstall FIXMEDOC
145146
func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallRequest, taskCB rpc.TaskProgressCB) error {
146-
lm := commands.GetLibraryManager(req)
147+
lm := instances.GetLibraryManager(req.GetInstance())
147148
if err := lm.InstallZipLib(ctx, paths.New(req.Path), req.Overwrite); err != nil {
148149
return &arduino.FailedLibraryInstallError{Cause: err}
149150
}
@@ -153,7 +154,7 @@ func ZipLibraryInstall(ctx context.Context, req *rpc.ZipLibraryInstallRequest, t
153154

154155
// GitLibraryInstall FIXMEDOC
155156
func GitLibraryInstall(ctx context.Context, req *rpc.GitLibraryInstallRequest, taskCB rpc.TaskProgressCB) error {
156-
lm := commands.GetLibraryManager(req)
157+
lm := instances.GetLibraryManager(req.GetInstance())
157158
if err := lm.InstallGitLib(req.Url, req.Overwrite); err != nil {
158159
return &arduino.FailedLibraryInstallError{Cause: err}
159160
}

‎commands/lib/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2626
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
2727
"github.com/arduino/arduino-cli/arduino/libraries/librariesresolver"
28-
"github.com/arduino/arduino-cli/commands"
28+
"github.com/arduino/arduino-cli/commands/internal/instances"
2929
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3030
)
3131

@@ -36,13 +36,13 @@ type installedLib struct {
3636

3737
// LibraryList FIXMEDOC
3838
func LibraryList(ctx context.Context, req *rpc.LibraryListRequest) (*rpc.LibraryListResponse, error) {
39-
pme, release := commands.GetPackageManagerExplorer(req)
39+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
4040
if pme == nil {
4141
return nil, &arduino.InvalidInstanceError{}
4242
}
4343
defer release()
4444

45-
lm := commands.GetLibraryManager(req)
45+
lm := instances.GetLibraryManager(req.GetInstance())
4646
if lm == nil {
4747
return nil, &arduino.InvalidInstanceError{}
4848
}

‎commands/lib/resolve_deps.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@ import (
2222

2323
"github.com/arduino/arduino-cli/arduino"
2424
"github.com/arduino/arduino-cli/arduino/libraries"
25-
"github.com/arduino/arduino-cli/commands"
25+
"github.com/arduino/arduino-cli/commands/internal/instances"
2626
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2727
)
2828

2929
// LibraryResolveDependencies FIXMEDOC
3030
func LibraryResolveDependencies(ctx context.Context, req *rpc.LibraryResolveDependenciesRequest) (*rpc.LibraryResolveDependenciesResponse, error) {
31-
lm := commands.GetLibraryManager(req)
31+
lm := instances.GetLibraryManager(req.GetInstance())
3232
if lm == nil {
3333
return nil, &arduino.InvalidInstanceError{}
3434
}

‎commands/lib/search.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ import (
2424
"github.com/arduino/arduino-cli/arduino/libraries/librariesindex"
2525
"github.com/arduino/arduino-cli/arduino/libraries/librariesmanager"
2626
"github.com/arduino/arduino-cli/arduino/utils"
27-
"github.com/arduino/arduino-cli/commands"
27+
"github.com/arduino/arduino-cli/commands/internal/instances"
2828
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2929
semver "go.bug.st/relaxed-semver"
3030
)
3131

3232
// LibrarySearch FIXMEDOC
3333
func LibrarySearch(ctx context.Context, req *rpc.LibrarySearchRequest) (*rpc.LibrarySearchResponse, error) {
34-
lm := commands.GetLibraryManager(req)
34+
lm := instances.GetLibraryManager(req.GetInstance())
3535
if lm == nil {
3636
return nil, &arduino.InvalidInstanceError{}
3737
}

‎commands/lib/uninstall.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ import (
2020

2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/arduino/libraries"
23-
"github.com/arduino/arduino-cli/commands"
23+
"github.com/arduino/arduino-cli/commands/internal/instances"
2424
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2525
"github.com/arduino/go-paths-helper"
2626
)
2727

2828
// LibraryUninstall FIXMEDOC
2929
func LibraryUninstall(ctx context.Context, req *rpc.LibraryUninstallRequest, taskCB rpc.TaskProgressCB) error {
30-
lm := commands.GetLibraryManager(req)
30+
lm := instances.GetLibraryManager(req.GetInstance())
3131
ref, err := createLibIndexReference(lm, req)
3232
if err != nil {
3333
return &arduino.InvalidLibraryError{Cause: err}

‎commands/lib/upgrade.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@ import (
2020

2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/commands"
23+
"github.com/arduino/arduino-cli/commands/internal/instances"
2324
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2425
)
2526

2627
// LibraryUpgradeAll upgrades all the available libraries
2728
func LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
28-
lm := commands.GetLibraryManager(req)
29+
lm := instances.GetLibraryManager(req.GetInstance())
2930
if lm == nil {
3031
return &arduino.InvalidInstanceError{}
3132
}
@@ -43,7 +44,7 @@ func LibraryUpgradeAll(req *rpc.LibraryUpgradeAllRequest, downloadCB rpc.Downloa
4344

4445
// LibraryUpgrade upgrades a library
4546
func LibraryUpgrade(ctx context.Context, req *rpc.LibraryUpgradeRequest, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
46-
lm := commands.GetLibraryManager(req)
47+
lm := instances.GetLibraryManager(req.GetInstance())
4748
if lm == nil {
4849
return &arduino.InvalidInstanceError{}
4950
}

‎commands/monitor/monitor.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
"github.com/arduino/arduino-cli/arduino/cores"
2525
"github.com/arduino/arduino-cli/arduino/cores/packagemanager"
2626
pluggableMonitor "github.com/arduino/arduino-cli/arduino/monitor"
27-
"github.com/arduino/arduino-cli/commands"
27+
"github.com/arduino/arduino-cli/commands/internal/instances"
2828
"github.com/arduino/arduino-cli/i18n"
2929
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
3030
"github.com/arduino/go-properties-orderedmap"
@@ -61,7 +61,7 @@ func (p *PortProxy) Close() error {
6161
// Monitor opens a communication port. It returns a PortProxy to communicate with the port and a PortDescriptor
6262
// that describes the available configuration settings.
6363
func Monitor(ctx context.Context, req *rpc.MonitorRequest) (*PortProxy, *pluggableMonitor.PortDescriptor, error) {
64-
pme, release := commands.GetPackageManagerExplorer(req)
64+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
6565
if pme == nil {
6666
return nil, nil, &arduino.InvalidInstanceError{}
6767
}

‎commands/monitor/settings.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ import (
2020

2121
"github.com/arduino/arduino-cli/arduino"
2222
pluggableMonitor "github.com/arduino/arduino-cli/arduino/monitor"
23-
"github.com/arduino/arduino-cli/commands"
23+
"github.com/arduino/arduino-cli/commands/internal/instances"
2424
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2525
)
2626

2727
// EnumerateMonitorPortSettings returns a description of the configuration settings of a monitor port
2828
func EnumerateMonitorPortSettings(ctx context.Context, req *rpc.EnumerateMonitorPortSettingsRequest) (*rpc.EnumerateMonitorPortSettingsResponse, error) {
29-
pme, release := commands.GetPackageManagerExplorer(req)
29+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3030
if pme == nil {
3131
return nil, &arduino.InvalidInstanceError{}
3232
}

‎commands/upload/burnbootloader.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"io"
2121

2222
"github.com/arduino/arduino-cli/arduino"
23-
"github.com/arduino/arduino-cli/commands"
23+
"github.com/arduino/arduino-cli/commands/internal/instances"
2424
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2525
"github.com/sirupsen/logrus"
2626
)
@@ -33,7 +33,7 @@ func BurnBootloader(ctx context.Context, req *rpc.BurnBootloaderRequest, outStre
3333
WithField("programmer", req.GetProgrammer()).
3434
Trace("BurnBootloader started", req.GetFqbn())
3535

36-
pme, release := commands.GetPackageManagerExplorer(req)
36+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3737
if pme == nil {
3838
return nil, &arduino.InvalidInstanceError{}
3939
}

‎commands/upload/programmers_list.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ import (
2020

2121
"github.com/arduino/arduino-cli/arduino"
2222
"github.com/arduino/arduino-cli/arduino/cores"
23-
"github.com/arduino/arduino-cli/commands"
23+
"github.com/arduino/arduino-cli/commands/internal/instances"
2424
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2525
)
2626

2727
// ListProgrammersAvailableForUpload FIXMEDOC
2828
func ListProgrammersAvailableForUpload(ctx context.Context, req *rpc.ListProgrammersAvailableForUploadRequest) (*rpc.ListProgrammersAvailableForUploadResponse, error) {
29-
pme, release := commands.GetPackageManagerExplorer(req)
29+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
3030
if pme == nil {
3131
return nil, &arduino.InvalidInstanceError{}
3232
}

‎commands/upload/upload.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import (
3030
"github.com/arduino/arduino-cli/arduino/globals"
3131
"github.com/arduino/arduino-cli/arduino/serialutils"
3232
"github.com/arduino/arduino-cli/arduino/sketch"
33-
"github.com/arduino/arduino-cli/commands"
33+
"github.com/arduino/arduino-cli/commands/internal/instances"
3434
"github.com/arduino/arduino-cli/executils"
3535
"github.com/arduino/arduino-cli/i18n"
3636
f "github.com/arduino/arduino-cli/internal/algorithms"
@@ -50,7 +50,7 @@ func SupportedUserFields(ctx context.Context, req *rpc.SupportedUserFieldsReques
5050
return nil, &arduino.MissingPortProtocolError{}
5151
}
5252

53-
pme, release := commands.GetPackageManagerExplorer(req)
53+
pme, release := instances.GetPackageManagerExplorer(req.GetInstance())
5454
defer release()
5555

5656
if pme == nil {
@@ -137,7 +137,7 @@ func Upload(ctx context.Context, req *rpc.UploadRequest, outStream io.Writer, er
137137
return nil, &arduino.CantOpenSketchError{Cause: err}
138138
}
139139

140-
pme, pmeRelease := commands.GetPackageManagerExplorer(req)
140+
pme, pmeRelease := instances.GetPackageManagerExplorer(req.GetInstance())
141141
if pme == nil {
142142
return nil, &arduino.InvalidInstanceError{}
143143
}

‎internal/cli/arguments/completion.go

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ package arguments
1818
import (
1919
"context"
2020

21-
"github.com/arduino/arduino-cli/commands"
2221
"github.com/arduino/arduino-cli/commands/board"
2322
"github.com/arduino/arduino-cli/commands/core"
2423
"github.com/arduino/arduino-cli/commands/lib"
@@ -46,41 +45,6 @@ func GetInstalledBoards() []string {
4645
return res
4746
}
4847

49-
// GetInstalledProtocols is an helper function useful to autocomplete.
50-
// It returns a list of protocols available based on the installed boards
51-
func GetInstalledProtocols() []string {
52-
inst := instance.CreateAndInit()
53-
54-
// FIXME: We must not access PackageManager directly here but use one of the commands.* functions
55-
pme, release := commands.GetPackageManagerExplorer(&rpc.BoardListAllRequest{Instance: inst})
56-
if pme == nil {
57-
return nil // should never happen...
58-
}
59-
defer release()
60-
61-
boards := pme.InstalledBoards()
62-
63-
installedProtocols := make(map[string]struct{})
64-
for _, board := range boards {
65-
for _, protocol := range board.Properties.SubTree("upload.tool").FirstLevelKeys() {
66-
if protocol == "default" {
67-
// default is used as fallback when trying to upload to a board
68-
// using a protocol not defined for it, it's useless showing it
69-
// in autocompletion
70-
continue
71-
}
72-
installedProtocols[protocol] = struct{}{}
73-
}
74-
}
75-
res := make([]string, len(installedProtocols))
76-
i := 0
77-
for k := range installedProtocols {
78-
res[i] = k
79-
i++
80-
}
81-
return res
82-
}
83-
8448
// GetInstalledProgrammers is an helper function useful to autocomplete.
8549
// It returns a list of programmers available based on the installed boards
8650
func GetInstalledProgrammers() []string {
@@ -94,13 +58,6 @@ func GetInstalledProgrammers() []string {
9458
}
9559
list, _ := board.ListAll(context.Background(), listAllReq)
9660

97-
// FIXME: We must not access PackageManager directly here but use one of the commands.* functions
98-
pme, release := commands.GetPackageManagerExplorer(listAllReq)
99-
if pme == nil {
100-
return nil // should never happen...
101-
}
102-
defer release()
103-
10461
installedProgrammers := make(map[string]string)
10562
for _, board := range list.Boards {
10663
programmers, _ := upload.ListProgrammersAvailableForUpload(context.Background(), &rpc.ListProgrammersAvailableForUploadRequest{
@@ -203,19 +160,19 @@ func GetInstallableLibs() []string {
203160
return res
204161
}
205162

206-
// GetConnectedBoards is an helper function useful to autocomplete.
207-
// It returns a list of boards which are currently connected
208-
// Obviously it does not suggests network ports because of the timeout
209-
func GetConnectedBoards() []string {
163+
// GetAvailablePorts is an helper function useful to autocomplete.
164+
// It returns a list of upload port of the boards which are currently connected.
165+
// It will not suggests network ports because the timeout is not set.
166+
func GetAvailablePorts() []*rpc.Port {
210167
inst := instance.CreateAndInit()
211168

212169
list, _, _ := board.List(&rpc.BoardListRequest{
213170
Instance: inst,
214171
})
215-
var res []string
172+
var res []*rpc.Port
216173
// transform the data structure for the completion
217174
for _, i := range list {
218-
res = append(res, i.Port.Address)
175+
res = append(res, i.Port)
219176
}
220177
return res
221178
}

‎internal/cli/arguments/port.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
package arguments
1717

1818
import (
19+
"context"
1920
"fmt"
2021
"time"
2122

2223
"github.com/arduino/arduino-cli/arduino"
23-
"github.com/arduino/arduino-cli/commands"
2424
"github.com/arduino/arduino-cli/commands/board"
25+
f "github.com/arduino/arduino-cli/internal/algorithms"
2526
"github.com/arduino/arduino-cli/internal/cli/feedback"
2627
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2728
"github.com/sirupsen/logrus"
@@ -41,11 +42,11 @@ type Port struct {
4142
func (p *Port) AddToCommand(cmd *cobra.Command) {
4243
cmd.Flags().StringVarP(&p.address, "port", "p", "", tr("Upload port address, e.g.: COM3 or /dev/ttyACM2"))
4344
cmd.RegisterFlagCompletionFunc("port", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
44-
return GetConnectedBoards(), cobra.ShellCompDirectiveDefault
45+
return f.Map(GetAvailablePorts(), (*rpc.Port).GetAddress), cobra.ShellCompDirectiveDefault
4546
})
4647
cmd.Flags().StringVarP(&p.protocol, "protocol", "l", "", tr("Upload port protocol, e.g: serial"))
4748
cmd.RegisterFlagCompletionFunc("protocol", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
48-
return GetInstalledProtocols(), cobra.ShellCompDirectiveDefault
49+
return f.Map(GetAvailablePorts(), (*rpc.Port).GetProtocol), cobra.ShellCompDirectiveDefault
4950
})
5051
p.timeout.AddToCommand(cmd)
5152
}
@@ -88,30 +89,23 @@ func (p *Port) GetPort(instance *rpc.Instance, defaultAddress, defaultProtocol s
8889
}
8990
logrus.WithField("port", address).Tracef("Upload port")
9091

91-
// FIXME: We must not access PackageManager directly here but use one of the commands.* functions
92-
pme, release := commands.GetPackageManagerExplorer(&rpc.BoardListAllRequest{Instance: instance})
93-
if pme == nil {
94-
return nil, &arduino.InvalidInstanceError{}
95-
}
96-
defer release()
97-
98-
dm := pme.DiscoveryManager()
99-
watcher, err := dm.Watch()
92+
ctx, cancel := context.WithCancel(context.Background())
93+
defer cancel()
94+
watcher, err := board.Watch(ctx, &rpc.BoardListWatchRequest{Instance: instance})
10095
if err != nil {
10196
return nil, err
10297
}
103-
defer watcher.Close()
10498

10599
deadline := time.After(p.timeout.Get())
106100
for {
107101
select {
108-
case portEvent := <-watcher.Feed():
109-
if portEvent.Type != "add" {
102+
case portEvent := <-watcher:
103+
if portEvent.GetEventType() != "add" {
110104
continue
111105
}
112-
port := portEvent.Port
113-
if (protocol == "" || protocol == port.Protocol) && address == port.Address {
114-
return port.ToRPC(), nil
106+
port := portEvent.GetPort().GetPort()
107+
if (protocol == "" || protocol == port.GetProtocol()) && address == port.GetAddress() {
108+
return port, nil
115109
}
116110

117111
case <-deadline:

‎internal/integrationtest/completion/completion_test.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,16 +216,10 @@ func TestCoreCompletion(t *testing.T) {
216216
require.Contains(t, string(stdout), "arduino:avr:uno")
217217
stdout, _, _ = cli.Run("__complete", "monitor", "-b", "")
218218
require.Contains(t, string(stdout), "arduino:avr:uno")
219-
stdout, _, _ = cli.Run("__complete", "burn-bootloader", "-l", "")
220-
require.Contains(t, string(stdout), "network")
221-
stdout, _, _ = cli.Run("__complete", "compile", "-l", "")
222-
require.Contains(t, string(stdout), "network")
223-
stdout, _, _ = cli.Run("__complete", "debug", "-l", "")
224-
require.Contains(t, string(stdout), "network")
225-
stdout, _, _ = cli.Run("__complete", "upload", "-l", "")
226-
require.Contains(t, string(stdout), "network")
227-
stdout, _, _ = cli.Run("__complete", "monitor", "-l", "")
228-
require.Contains(t, string(stdout), "network")
219+
220+
// -l/--protocol and -p/--port cannot be tested because there are
221+
// no board connected.
222+
229223
stdout, _, _ = cli.Run("__complete", "burn-bootloader", "-P", "")
230224
require.Contains(t, string(stdout), "atmel_ice")
231225
stdout, _, _ = cli.Run("__complete", "compile", "-P", "")

‎rpc/cc/arduino/cli/commands/v1/common.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,3 @@ func (d DownloadProgressCB) End(success bool, message string) {
5656

5757
// TaskProgressCB is a callback to receive progress messages
5858
type TaskProgressCB func(msg *TaskProgress)
59-
60-
// InstanceCommand is an interface that represents a gRPC command with
61-
// a gRPC Instance.
62-
type InstanceCommand interface {
63-
GetInstance() *Instance
64-
}

0 commit comments

Comments
 (0)
Please sign in to comment.