Skip to content

Commit 3db4ad8

Browse files
authored
Allow platforms without fixed version in profiles. (#2940)
* Allow platforms without fixed version in profiles. * Correctly mark package manager instance as "under profile" even in case an unversioned platform is requsted. * Improved error checks in profile loading * Updated docs
1 parent cff56cd commit 3db4ad8

File tree

7 files changed

+121
-15
lines changed

7 files changed

+121
-15
lines changed

commands/instances.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
248248
s := &cmderrors.PlatformLoadingError{Cause: err}
249249
responseError(s.GRPCStatus())
250250
}
251+
} else if profile.RequireSystemInstalledPlatform() {
252+
for _, err := range pmb.LoadGlobalHardwareForProfile(profile) {
253+
s := &cmderrors.PlatformLoadingError{Cause: err}
254+
responseError(s.GRPCStatus())
255+
}
251256
} else {
252257
// Load platforms from profile
253258
errs := pmb.LoadHardwareForProfile(ctx, profile, true, downloadCallback, taskCallback, s.settings)

docs/sketch-project-file.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ profiles:
2828
fqbn: <FQBN>
2929
programmer: <PROGRAMMER>
3030
platforms:
31-
- platform: <PLATFORM> (<PLATFORM_VERSION>)
31+
- platform: <PLATFORM> [(<PLATFORM_VERSION>)]
3232
platform_index_url: <3RD_PARTY_PLATFORM_URL>
33-
- platform: <PLATFORM_DEPENDENCY> (<PLATFORM_DEPENDENCY_VERSION>)
33+
- platform: <PLATFORM_DEPENDENCY> [(<PLATFORM_DEPENDENCY_VERSION>)]
3434
platform_index_url: <3RD_PARTY_PLATFORM_DEPENDENCY_URL>
3535
libraries:
3636
- <INDEX_LIB_NAME> (<INDEX_LIB_VERSION>)
@@ -73,6 +73,14 @@ The following fields are available since Arduino CLI 1.1.0:
7373
`baudrate: 115200`) but any setting/value can be specified. Multiple settings can be set. These fields are optional.
7474
- `<PORT_PROTOCOL>` is the protocol for the port used to upload and monitor the board. This field is optional.
7575

76+
#### Using a system-installed platform.
77+
78+
The fields `<PLATFORM_VERSION>` and `<PLATFORM_DEPENDENCY_VERSION>` are optional, if they are omitted, the sketch
79+
compilation will use the platforms installed system-wide. This could be helpful during the development of a platform
80+
(where a specific release is not yet available), or if a specific version of a platform is not a strict requirement.
81+
82+
#### An example of a complete project file.
83+
7684
A complete example of a sketch project file may be the following:
7785

7886
```

internal/arduino/cores/packagemanager/profiles.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ import (
3333
"github.com/sirupsen/logrus"
3434
)
3535

36+
// LoadGlobalHardwareForProfile loads the hardware platforms for the given profile.
37+
// It uses the global package manager and does not download or install any missing tools or platforms.
38+
func (pmb *Builder) LoadGlobalHardwareForProfile(p *sketch.Profile) []error {
39+
pmb.profile = p
40+
return pmb.LoadHardware()
41+
}
42+
3643
// LoadHardwareForProfile load the hardware platforms for the given profile.
3744
// If installMissing is true then possibly missing tools and platforms will be downloaded and installed.
3845
func (pmb *Builder) LoadHardwareForProfile(ctx context.Context, p *sketch.Profile, installMissing bool, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, settings *configuration.Settings) []error {

internal/arduino/sketch/profiles.go

Lines changed: 65 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ type Profile struct {
120120
Libraries ProfileRequiredLibraries `yaml:"libraries"`
121121
}
122122

123+
// UsesSystemPlatform checks if this profile requires a system installed platform.
124+
func (p *Profile) RequireSystemInstalledPlatform() bool {
125+
return p.Platforms[0].RequireSystemInstalledPlatform()
126+
}
127+
123128
// ToRpc converts this Profile to an rpc.SketchProfile
124129
func (p *Profile) ToRpc() *rpc.SketchProfile {
125130
var portConfig *rpc.MonitorPortConfiguration
@@ -182,6 +187,20 @@ func (p *ProfileRequiredPlatforms) AsYaml() string {
182187
return res
183188
}
184189

190+
func (p *ProfileRequiredPlatforms) UnmarshalYAML(unmarshal func(interface{}) error) error {
191+
_p := (*[]*ProfilePlatformReference)(p)
192+
if err := unmarshal(_p); err != nil {
193+
return err
194+
}
195+
requireSystemPlatform := (*_p)[0].RequireSystemInstalledPlatform()
196+
for _, platform := range *_p {
197+
if platform.RequireSystemInstalledPlatform() != requireSystemPlatform {
198+
return errors.New(i18n.Tr("all platforms in a profile must either require a specific version or not"))
199+
}
200+
}
201+
return nil
202+
}
203+
185204
// ProfileRequiredLibraries is a list of ProfileLibraryReference (libraries
186205
// required to build the sketch using this profile)
187206
type ProfileRequiredLibraries []*ProfileLibraryReference
@@ -206,6 +225,12 @@ type ProfilePlatformReference struct {
206225
PlatformIndexURL *url.URL
207226
}
208227

228+
// RequireSystemInstalledPlatform returns true if the platform reference
229+
// does not specify a version, meaning it requires the system installed platform.
230+
func (p *ProfilePlatformReference) RequireSystemInstalledPlatform() bool {
231+
return p.Version == nil
232+
}
233+
209234
// InternalUniqueIdentifier returns the unique identifier for this object
210235
func (p *ProfilePlatformReference) InternalUniqueIdentifier() string {
211236
id := p.String()
@@ -224,20 +249,38 @@ func (p *ProfilePlatformReference) String() string {
224249

225250
// AsYaml outputs the platform reference as Yaml
226251
func (p *ProfilePlatformReference) AsYaml() string {
227-
res := fmt.Sprintf(" - platform: %s:%s (%s)\n", p.Packager, p.Architecture, p.Version)
252+
res := ""
253+
if p.Version != nil {
254+
res += fmt.Sprintf(" - platform: %s:%s (%s)\n", p.Packager, p.Architecture, p.Version)
255+
} else {
256+
res += fmt.Sprintf(" - platform: %s:%s\n", p.Packager, p.Architecture)
257+
}
228258
if p.PlatformIndexURL != nil {
229259
res += fmt.Sprintf(" platform_index_url: %s\n", p.PlatformIndexURL)
230260
}
231261
return res
232262
}
233263

234264
func parseNameAndVersion(in string) (string, string, bool) {
235-
re := regexp.MustCompile(`^([a-zA-Z0-9.\-_ :]+) \((.+)\)$`)
236-
split := re.FindAllStringSubmatch(in, -1)
237-
if len(split) != 1 || len(split[0]) != 3 {
238-
return "", "", false
265+
{
266+
// Try to parse the input string in the format "VENDOR:ARCH (VERSION)"
267+
re := regexp.MustCompile(`^([a-zA-Z0-9.\-_ :]+) \((.+)\)$`)
268+
split := re.FindAllStringSubmatch(in, -1)
269+
if len(split) == 1 && len(split[0]) == 3 {
270+
return split[0][1], split[0][2], true
271+
}
272+
}
273+
274+
{
275+
// Try to parse the input string in the format "VENDOR:ARCH"
276+
re := regexp.MustCompile(`^([a-zA-Z0-9.\-_ :]+)$`)
277+
split := re.FindAllStringSubmatch(in, -1)
278+
if len(split) == 1 && len(split[0]) == 2 {
279+
return split[0][1], "", true
280+
}
239281
}
240-
return split[0][1], split[0][2], true
282+
283+
return "", "", false
241284
}
242285

243286
// UnmarshalYAML decodes a ProfilePlatformReference from YAML source.
@@ -250,14 +293,23 @@ func (p *ProfilePlatformReference) UnmarshalYAML(unmarshal func(interface{}) err
250293
return errors.New(i18n.Tr("missing '%s' directive", "platform"))
251294
} else if platformID, platformVersion, ok := parseNameAndVersion(platformID); !ok {
252295
return errors.New(i18n.Tr("invalid '%s' directive", "platform"))
253-
} else if c, err := semver.Parse(platformVersion); err != nil {
254-
return fmt.Errorf("%s: %w", i18n.Tr("error parsing version constraints"), err)
255-
} else if split := strings.SplitN(platformID, ":", 2); len(split) != 2 {
256-
return fmt.Errorf("%s: %s", i18n.Tr("invalid platform identifier"), platformID)
257296
} else {
258-
p.Packager = split[0]
259-
p.Architecture = split[1]
260-
p.Version = c
297+
var version *semver.Version
298+
if platformVersion != "" {
299+
if v, err := semver.Parse(platformVersion); err != nil {
300+
return fmt.Errorf("%s: %w", i18n.Tr("error parsing version constraints"), err)
301+
} else {
302+
version = v
303+
}
304+
}
305+
306+
if split := strings.SplitN(platformID, ":", 2); len(split) != 2 {
307+
return fmt.Errorf("%s: %s", i18n.Tr("invalid platform identifier"), platformID)
308+
} else {
309+
p.Packager = split[0]
310+
p.Architecture = split[1]
311+
p.Version = version
312+
}
261313
}
262314

263315
if rawIndexURL, ok := data["platform_index_url"]; ok {

internal/arduino/sketch/profiles_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,17 @@ func TestProjectFileLoading(t *testing.T) {
3939
require.NoError(t, err)
4040
require.Equal(t, proj.AsYaml(), string(golden))
4141
}
42+
{
43+
sketchProj := paths.New("testdata", "profiles", "profile_1.yml")
44+
proj, err := LoadProjectFile(sketchProj)
45+
require.NoError(t, err)
46+
golden, err := sketchProj.ReadFile()
47+
require.NoError(t, err)
48+
require.Equal(t, string(golden), proj.AsYaml())
49+
}
50+
{
51+
sketchProj := paths.New("testdata", "profiles", "bad_profile_1.yml")
52+
_, err := LoadProjectFile(sketchProj)
53+
require.Error(t, err)
54+
}
4255
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
profiles:
2+
tiny:
3+
notes: Invalid profile mixing versioned and non-versioned platforms.
4+
fqbn: attiny:avr:ATtinyX5:cpu=attiny85,clock=internal16
5+
platforms:
6+
- platform: attiny:avr
7+
platform_index_url: http://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json
8+
- platform: arduino:avr (1.8.3)
9+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
profiles:
2+
giga:
3+
fqbn: arduino:mbed_giga:giga
4+
platforms:
5+
- platform: arduino:mbed_giga (4.3.1)
6+
7+
giga_any:
8+
fqbn: arduino:mbed_giga:giga
9+
platforms:
10+
- platform: arduino:mbed_giga
11+
12+
default_profile: giga_any

0 commit comments

Comments
 (0)