Skip to content

Commit 79eb7f4

Browse files
committed
Added function to load packages for profiles
1 parent 670c9f1 commit 79eb7f4

File tree

4 files changed

+220
-0
lines changed

4 files changed

+220
-0
lines changed

arduino/cores/cores.go

+15
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616
package cores
1717

1818
import (
19+
"crypto/sha256"
20+
"encoding/hex"
1921
"encoding/json"
2022
"fmt"
23+
"net/url"
2124
"sort"
2225
"strings"
2326

2427
"github.com/arduino/arduino-cli/arduino/resources"
28+
"github.com/arduino/arduino-cli/arduino/utils"
2529
"github.com/arduino/arduino-cli/i18n"
2630
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
2731
paths "github.com/arduino/go-paths-helper"
@@ -122,6 +126,17 @@ func (dep *ToolDependency) String() string {
122126
return dep.ToolPackager + ":" + dep.ToolName + "@" + dep.ToolVersion.String()
123127
}
124128

129+
// InternalUniqueIdentifier returns the unique identifier for this object
130+
func (dep *ToolDependency) InternalUniqueIdentifier(platformIndexURL *url.URL) string {
131+
h := sha256.New()
132+
h.Write([]byte(dep.String()))
133+
if platformIndexURL != nil {
134+
h.Write([]byte(platformIndexURL.String()))
135+
}
136+
res := dep.String() + "_" + hex.EncodeToString(h.Sum([]byte{}))[:16]
137+
return utils.SanitizeName(res)
138+
}
139+
125140
// DiscoveryDependencies is a list of DiscoveryDependency
126141
type DiscoveryDependencies []*DiscoveryDependency
127142

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// This file is part of arduino-cli.
2+
//
3+
// Copyright 2020-2022 ARDUINO SA (http://www.arduino.cc/)
4+
//
5+
// This software is released under the GNU General Public License version 3,
6+
// which covers the main part of arduino-cli.
7+
// The terms of this license can be found at:
8+
// https://www.gnu.org/licenses/gpl-3.0.en.html
9+
//
10+
// You can be released from the requirements of the above licenses by purchasing
11+
// a commercial license. Buying such a license is mandatory if you want to
12+
// modify or otherwise use the software for commercial activities involving the
13+
// Arduino software without disclosing the source code of your own applications.
14+
// To purchase a commercial license, send an email to [email protected].
15+
16+
package packagemanager
17+
18+
import (
19+
"fmt"
20+
"net/url"
21+
22+
"github.com/arduino/arduino-cli/arduino"
23+
"github.com/arduino/arduino-cli/arduino/cores"
24+
"github.com/arduino/arduino-cli/arduino/resources"
25+
"github.com/arduino/arduino-cli/arduino/sketch"
26+
"github.com/arduino/arduino-cli/cli/globals"
27+
"github.com/arduino/arduino-cli/configuration"
28+
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
29+
"github.com/arduino/go-paths-helper"
30+
"github.com/sirupsen/logrus"
31+
)
32+
33+
// LoadHardwareForProfile load the hardware platforms for the given profile.
34+
func (pm *PackageManager) LoadHardwareForProfile(p *sketch.Profile) []error {
35+
// Load required platforms
36+
var merr []error
37+
var platformReleases []*cores.PlatformRelease
38+
indexURLs := map[string]*url.URL{}
39+
for _, platformRef := range p.Platforms {
40+
if platformRelease, err := pm.loadProfilePlatform(platformRef); err != nil {
41+
merr = append(merr, fmt.Errorf("%s: %w", tr("loading required platform %s", platformRef), err))
42+
logrus.WithField("platform", platformRef).WithError(err).Debugf("Error loading platform for profile")
43+
} else {
44+
platformReleases = append(platformReleases, platformRelease)
45+
indexURLs[platformRelease.Platform.Name] = platformRef.PlatformIndexURL
46+
logrus.WithField("platform", platformRef).Debugf("Loaded platform for profile")
47+
}
48+
}
49+
50+
// Load tools dependencies for the platforms
51+
for _, platformRelease := range platformReleases {
52+
for _, toolDep := range platformRelease.ToolDependencies {
53+
indexURL := indexURLs[toolDep.ToolPackager]
54+
if err := pm.loadProfileTool(toolDep, indexURL); err != nil {
55+
merr = append(merr, fmt.Errorf("%s: %w", tr("loading required tool %s", toolDep), err))
56+
logrus.WithField("tool", toolDep).WithField("index_url", indexURL).WithError(err).Debugf("Error loading tool for profile")
57+
} else {
58+
logrus.WithField("tool", toolDep).WithField("index_url", indexURL).Debugf("Loaded tool for profile")
59+
}
60+
}
61+
}
62+
63+
return merr
64+
}
65+
66+
func (pm *PackageManager) loadProfilePlatform(platformRef *sketch.ProfilePlatformReference) (*cores.PlatformRelease, error) {
67+
targetPackage := pm.Packages.GetOrCreatePackage(platformRef.Packager)
68+
platform := targetPackage.GetOrCreatePlatform(platformRef.Architecture)
69+
release := platform.GetOrCreateRelease(platformRef.Version)
70+
71+
uid := platformRef.InternalUniqueIdentifier()
72+
destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid)
73+
return release, pm.loadPlatformRelease(release, destDir)
74+
}
75+
76+
func (pm *PackageManager) installMissingProfilePlatform(platformRef *sketch.ProfilePlatformReference, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
77+
// TODO: TaskProgressCB
78+
79+
// Instantiate a temporary package manager only for platform installation
80+
tmp, err := paths.MkTempDir("", "")
81+
if err != nil {
82+
return fmt.Errorf("installing missing platform: could not create temp dir %s", err)
83+
}
84+
tmpPm := NewPackageManager(tmp, tmp, pm.DownloadDir, tmp, pm.userAgent)
85+
defer tmp.RemoveAll()
86+
87+
// Download the main index and parse it
88+
taskCB(&rpc.TaskProgress{Name: tr("Downloading platform %s", platformRef)})
89+
defaultIndexURL, _ := url.Parse(globals.DefaultIndexURL)
90+
indexesToDownload := []*url.URL{defaultIndexURL}
91+
if platformRef.PlatformIndexURL != nil {
92+
indexesToDownload = append(indexesToDownload, platformRef.PlatformIndexURL)
93+
}
94+
for _, indexURL := range indexesToDownload {
95+
if err != nil {
96+
taskCB(&rpc.TaskProgress{Name: tr("Error downloading %s", indexURL)})
97+
return &arduino.FailedDownloadError{Message: tr("Error downloading %s", indexURL), Cause: err}
98+
}
99+
indexResource := resources.IndexResource{URL: indexURL}
100+
if err := indexResource.Download(tmpPm.IndexDir, downloadCB); err != nil {
101+
taskCB(&rpc.TaskProgress{Name: tr("Error downloading %s", indexURL)})
102+
return &arduino.FailedDownloadError{Message: tr("Error downloading %s", indexURL), Cause: err}
103+
}
104+
if err := tmpPm.LoadPackageIndex(indexURL); err != nil {
105+
taskCB(&rpc.TaskProgress{Name: tr("Error loading index %s", indexURL)})
106+
return &arduino.FailedInstallError{Message: tr("Error loading index %s", indexURL), Cause: err}
107+
}
108+
}
109+
110+
// Download the platform
111+
tmpTargetPackage := tmpPm.Packages.GetOrCreatePackage(platformRef.Packager)
112+
tmpPlatform := tmpTargetPackage.GetOrCreatePlatform(platformRef.Architecture)
113+
tmpPlatformRelease := tmpPlatform.GetOrCreateRelease(platformRef.Version)
114+
if err := tmpPm.DownloadPlatformRelease(tmpPlatformRelease, nil, downloadCB); err != nil {
115+
taskCB(&rpc.TaskProgress{Name: tr("Error downloading platform %s", tmpPlatformRelease)})
116+
return &arduino.FailedInstallError{Message: tr("Error downloading platform %s", tmpPlatformRelease), Cause: err}
117+
}
118+
taskCB(&rpc.TaskProgress{Completed: true})
119+
120+
// Perform install
121+
taskCB(&rpc.TaskProgress{Name: tr("Installing platform %s", tmpPlatformRelease)})
122+
if err := tmpPm.InstallPlatformInDirectory(tmpPlatformRelease, destDir); err != nil {
123+
taskCB(&rpc.TaskProgress{Name: tr("Error installing platform %s", tmpPlatformRelease)})
124+
return &arduino.FailedInstallError{Message: tr("Error installing platform %s", tmpPlatformRelease), Cause: err}
125+
}
126+
taskCB(&rpc.TaskProgress{Completed: true})
127+
return nil
128+
}
129+
130+
func (pm *PackageManager) loadProfileTool(toolRef *cores.ToolDependency, indexURL *url.URL) error {
131+
targetPackage := pm.Packages.GetOrCreatePackage(toolRef.ToolPackager)
132+
tool := targetPackage.GetOrCreateTool(toolRef.ToolName)
133+
134+
uid := toolRef.InternalUniqueIdentifier(indexURL)
135+
destDir := configuration.ProfilesCacheDir(configuration.Settings).Join(uid)
136+
137+
if !destDir.IsDir() && installMissing {
138+
// Try installing the missing tool
139+
toolRelease := tool.GetOrCreateRelease(toolRef.ToolVersion)
140+
if toolRelease == nil {
141+
return &arduino.InvalidVersionError{Cause: fmt.Errorf(tr("version %s not found", toolRef.ToolVersion))}
142+
}
143+
if err := pm.installMissingProfileTool(toolRelease, destDir, downloadCB, taskCB); err != nil {
144+
return err
145+
}
146+
}
147+
148+
return pm.loadToolReleaseFromDirectory(tool, toolRef.ToolVersion, destDir)
149+
}
150+
151+
func (pm *PackageManager) installMissingProfileTool(toolRelease *cores.ToolRelease, destDir *paths.Path, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
152+
// Instantiate a temporary package manager only for platform installation
153+
tmp, err := paths.MkTempDir(destDir.Parent().String(), "")
154+
if err != nil {
155+
return fmt.Errorf("installing missing platform: could not create temp dir %s", err)
156+
}
157+
defer tmp.RemoveAll()
158+
159+
// Download the tool
160+
toolResource := toolRelease.GetCompatibleFlavour()
161+
if toolResource == nil {
162+
return &arduino.InvalidVersionError{Cause: fmt.Errorf(tr("version %s not available for this operating system", toolRelease))}
163+
}
164+
taskCB(&rpc.TaskProgress{Name: tr("Downloading tool %s", toolRelease)})
165+
if err := toolResource.Download(pm.DownloadDir, nil, toolRelease.String(), downloadCB); err != nil {
166+
taskCB(&rpc.TaskProgress{Name: tr("Error downloading tool %s", toolRelease)})
167+
return &arduino.FailedInstallError{Message: tr("Error installing tool %s", toolRelease), Cause: err}
168+
}
169+
taskCB(&rpc.TaskProgress{Completed: true})
170+
171+
// Install tool
172+
taskCB(&rpc.TaskProgress{Name: tr("Installing tool %s", toolRelease)})
173+
if err := toolResource.Install(pm.DownloadDir, tmp, destDir); err != nil {
174+
taskCB(&rpc.TaskProgress{Name: tr("Error installing tool %s", toolRelease)})
175+
return &arduino.FailedInstallError{Message: tr("Error installing tool %s", toolRelease), Cause: err}
176+
}
177+
taskCB(&rpc.TaskProgress{Completed: true})
178+
return nil
179+
}

arduino/sketch/profiles.go

+19
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,14 @@
1616
package sketch
1717

1818
import (
19+
"crypto/sha256"
20+
"encoding/hex"
1921
"fmt"
2022
"net/url"
2123
"regexp"
2224
"strings"
2325

26+
"github.com/arduino/arduino-cli/arduino/utils"
2427
"github.com/arduino/go-paths-helper"
2528
semver "go.bug.st/relaxed-semver"
2629
"gopkg.in/yaml.v2"
@@ -97,6 +100,14 @@ type ProfilePlatformReference struct {
97100
PlatformIndexURL *url.URL
98101
}
99102

103+
// InternalUniqueIdentifier returns the unique identifier for this object
104+
func (p *ProfilePlatformReference) InternalUniqueIdentifier() string {
105+
id := p.String()
106+
h := sha256.Sum256([]byte(id))
107+
res := fmt.Sprintf("%s:%s@%s_%s", p.Packager, p.Architecture, p.Version, hex.EncodeToString(h[:])[:16])
108+
return utils.SanitizeName(res)
109+
}
110+
100111
func (p *ProfilePlatformReference) String() string {
101112
res := fmt.Sprintf("%s:%s@%s", p.Packager, p.Architecture, p.Version)
102113
if p.PlatformIndexURL != nil {
@@ -178,6 +189,14 @@ func (l *ProfileLibraryReference) String() string {
178189
return fmt.Sprintf("%s@%s", l.Library, l.Version)
179190
}
180191

192+
// InternalUniqueIdentifier returns the unique identifier for this object
193+
func (l *ProfileLibraryReference) InternalUniqueIdentifier() string {
194+
id := l.String()
195+
h := sha256.Sum256([]byte(id))
196+
res := fmt.Sprintf("%s_%s", id, hex.EncodeToString(h[:])[:16])
197+
return utils.SanitizeName(res)
198+
}
199+
181200
// LoadProjectFile reads a sketch project file
182201
func LoadProjectFile(file *paths.Path) (*Project, error) {
183202
data, err := file.ReadFile()

configuration/directories.go

+7
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,10 @@ func LibrariesDir(settings *viper.Viper) *paths.Path {
9090
func PackagesDir(settings *viper.Viper) *paths.Path {
9191
return paths.New(settings.GetString("directories.Data")).Join("packages")
9292
}
93+
94+
// ProfilesCacheDir returns the full path to the profiles cache directory
95+
// (it contains all the platforms and libraries used to compile a sketch
96+
// using profiles)
97+
func ProfilesCacheDir(settings *viper.Viper) *paths.Path {
98+
return paths.New(settings.GetString("directories.Data")).Join("internal")
99+
}

0 commit comments

Comments
 (0)