Skip to content

Commit 1b1867f

Browse files
committed
Implementation of the Profiles parser
1 parent e82a46d commit 1b1867f

File tree

3 files changed

+265
-0
lines changed

3 files changed

+265
-0
lines changed

arduino/sketch/profiles.go

+192
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
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 sketch
17+
18+
import (
19+
"fmt"
20+
"net/url"
21+
"regexp"
22+
"strings"
23+
24+
"github.com/arduino/go-paths-helper"
25+
semver "go.bug.st/relaxed-semver"
26+
"gopkg.in/yaml.v2"
27+
)
28+
29+
// Project represents all the
30+
type Project struct {
31+
Profiles map[string]*Profile `yaml:"profiles"`
32+
DefaultProfile string `yaml:"default_profile"`
33+
}
34+
35+
// AsYaml outputs the project file as Yaml
36+
func (p *Project) AsYaml() string {
37+
res := "profiles:\n"
38+
for name, profile := range p.Profiles {
39+
res += fmt.Sprintf(" %s:\n", name)
40+
res += profile.AsYaml()
41+
res += "\n"
42+
}
43+
if p.DefaultProfile != "" {
44+
res += fmt.Sprintf("default_profile: %s\n", p.DefaultProfile)
45+
}
46+
return res
47+
}
48+
49+
// Profile is a sketch profile, it contains a reference to all the resources
50+
// needed to build and upload a sketch
51+
type Profile struct {
52+
Notes string `yaml:"notes"`
53+
FQBN string `yaml:"fqbn"`
54+
Platforms ProfileRequiredPlatforms `yaml:"platforms"`
55+
Libraries ProfileRequiredLibraries `yaml:"libraries"`
56+
}
57+
58+
// AsYaml outputs the profile as Yaml
59+
func (p *Profile) AsYaml() string {
60+
res := ""
61+
if p.Notes != "" {
62+
res += fmt.Sprintf(" notes: %s\n", p.Notes)
63+
}
64+
res += fmt.Sprintf(" fqbn: %s\n", p.FQBN)
65+
res += p.Platforms.AsYaml()
66+
res += p.Libraries.AsYaml()
67+
return res
68+
}
69+
70+
// ProfileRequiredPlatforms is a list of ProfilePlatformReference (platforms
71+
// required to build the sketch using this profile)
72+
type ProfileRequiredPlatforms []*ProfilePlatformReference
73+
74+
// AsYaml outputs the required platforms as Yaml
75+
func (p *ProfileRequiredPlatforms) AsYaml() string {
76+
res := ""
77+
for _, platform := range *p {
78+
res += platform.AsYaml()
79+
}
80+
return res
81+
}
82+
83+
// ProfileRequiredLibraries is a list of ProfileLibraryReference (libraries
84+
// required to build the sketch using this profile)
85+
type ProfileRequiredLibraries []*ProfileLibraryReference
86+
87+
// AsYaml outputs the required libraries as Yaml
88+
func (p *ProfileRequiredLibraries) AsYaml() string {
89+
return ""
90+
}
91+
92+
// ProfilePlatformReference is a reference to a platform
93+
type ProfilePlatformReference struct {
94+
Packager string
95+
Architecture string
96+
Version *semver.Version
97+
PlatformIndexURL *url.URL
98+
}
99+
100+
func (p *ProfilePlatformReference) String() string {
101+
res := fmt.Sprintf("%s:%s@%s", p.Packager, p.Architecture, p.Version)
102+
if p.PlatformIndexURL != nil {
103+
res += fmt.Sprintf(" (%s)", p.PlatformIndexURL)
104+
}
105+
return res
106+
}
107+
108+
// AsYaml outputs the platform reference as Yaml
109+
func (p *ProfilePlatformReference) AsYaml() string {
110+
res := fmt.Sprintf(" - platform: %s:%s (%s)\n", p.Packager, p.Architecture, p.Version)
111+
if p.PlatformIndexURL != nil {
112+
res += fmt.Sprintf(" platform_index_url: %s\n", p.PlatformIndexURL)
113+
}
114+
return res
115+
}
116+
117+
func parseNameAndVersion(in string) (string, string, bool) {
118+
re := regexp.MustCompile(`^([a-zA-Z0-9.\-_ :]+) \((.+)\)$`)
119+
split := re.FindAllStringSubmatch(in, -1)
120+
if len(split) != 1 || len(split[0]) != 3 {
121+
return "", "", false
122+
}
123+
return split[0][1], split[0][2], true
124+
}
125+
126+
func (p *ProfilePlatformReference) UnmarshalYAML(unmarshal func(interface{}) error) error {
127+
var data map[string]string
128+
if err := unmarshal(&data); err != nil {
129+
return err
130+
}
131+
if platformId, ok := data["platform"]; !ok {
132+
return fmt.Errorf(tr("missing 'platform' directive"))
133+
} else if platformId, platformVersion, ok := parseNameAndVersion(platformId); !ok {
134+
return fmt.Errorf(tr("invalid 'platform' directive"))
135+
} else if c, err := semver.Parse(platformVersion); err != nil {
136+
return fmt.Errorf("%s: %w", tr("error parsing version constraints"), err)
137+
} else if split := strings.SplitN(platformId, ":", 2); len(split) != 2 {
138+
return fmt.Errorf("%s: %s", tr("invalid platform identifier"), platformId)
139+
} else {
140+
p.Packager = split[0]
141+
p.Architecture = split[1]
142+
p.Version = c
143+
}
144+
145+
if rawIndexURL, ok := data["platform_index_url"]; ok {
146+
if indexURL, err := url.Parse(rawIndexURL); err != nil {
147+
return fmt.Errorf("%s: %w", tr("invlid platform index URL:"), err)
148+
} else {
149+
p.PlatformIndexURL = indexURL
150+
}
151+
}
152+
return nil
153+
}
154+
155+
// ProfileLibraryReference is a reference to a library
156+
type ProfileLibraryReference struct {
157+
Library string
158+
Version *semver.Version
159+
}
160+
161+
func (l *ProfileLibraryReference) UnmarshalYAML(unmarshal func(interface{}) error) error {
162+
var data string
163+
if err := unmarshal(&data); err != nil {
164+
return err
165+
}
166+
if libName, libVersion, ok := parseNameAndVersion(data); !ok {
167+
return fmt.Errorf("%s %s", tr("invalid library directive:"), data)
168+
} else if v, err := semver.Parse(libVersion); err != nil {
169+
return fmt.Errorf("%s %w", tr("invalid version:"), err)
170+
} else {
171+
l.Library = libName
172+
l.Version = v
173+
}
174+
return nil
175+
}
176+
177+
func (l *ProfileLibraryReference) String() string {
178+
return fmt.Sprintf("%s@%s", l.Library, l.Version)
179+
}
180+
181+
// LoadProjectFile reads a sketch project file
182+
func LoadProjectFile(file *paths.Path) (*Project, error) {
183+
data, err := file.ReadFile()
184+
if err != nil {
185+
return nil, err
186+
}
187+
res := &Project{}
188+
if err := yaml.Unmarshal(data, &res); err != nil {
189+
return nil, err
190+
}
191+
return res, nil
192+
}

arduino/sketch/profiles_test.go

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 sketch
17+
18+
import (
19+
"fmt"
20+
"testing"
21+
22+
"github.com/arduino/go-paths-helper"
23+
"github.com/stretchr/testify/require"
24+
)
25+
26+
func TestProjectFileLoading(t *testing.T) {
27+
proj, err := LoadProjectFile(paths.New("testdata", "SketchWithProfiles", "sketch.yml"))
28+
require.NoError(t, err)
29+
fmt.Println(proj)
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
profiles:
2+
nanorp:
3+
fqbn: arduino:mbed_nano:nanorp2040connect
4+
platforms:
5+
- platform: arduino:mbed_nano (2.1.0)
6+
libraries:
7+
- ArduinoIoTCloud (1.0.2)
8+
- Arduino_ConnectionHandler (0.6.4)
9+
- TinyDHT sensor library (1.1.0)
10+
11+
another_profile_name:
12+
notes: testing the limit of the AVR platform, may be unstable
13+
fqbn: arduino:avr:uno
14+
platforms:
15+
- platform: arduino:avr (1.8.4)
16+
libraries:
17+
- VitconMQTT (1.0.1)
18+
- Arduino_ConnectionHandler (0.6.4)
19+
- TinyDHT sensor library (1.1.0)
20+
21+
tiny:
22+
notes: testing the very limit of the AVR platform, it will be very unstable
23+
fqbn: attiny:avr:ATtinyX5:cpu=attiny85,clock=internal16
24+
platforms:
25+
- platform: attiny:avr (1.0.2)
26+
platform_index_url: http://raw.githubusercontent.com/damellis/attiny/ide-1.6.x-boards-manager/package_damellis_attiny_index.json
27+
- platform: arduino:avr (1.8.3)
28+
libraries:
29+
- ArduinoIoTCloud (1.0.2)
30+
- Arduino_ConnectionHandler (0.6.4)
31+
- TinyDHT sensor library (1.1.0)
32+
33+
feather:
34+
fqbn: adafruit:samd:adafruit_feather_m0
35+
platforms:
36+
- platform: adafruit:samd (1.6.0)
37+
platform_index_url: https://adafruit.github.io/arduino-board-index/package_adafruit_index.json
38+
libraries:
39+
- ArduinoIoTCloud (1.0.2)
40+
- Arduino_ConnectionHandler (0.6.4)
41+
- TinyDHT sensor library (1.1.0)
42+
43+
default_profile: nanorp

0 commit comments

Comments
 (0)