Skip to content

Commit a86b21d

Browse files
cmagliesilvanocerza
authored andcommitted
Extended tool support for various OS / CPU (#1077)
* tools-flavor-tests: Some renames and clean up * tools-flavors: Prioritize OS exatch-match over compatible-match * tools-flavors: unrolled freebsd regexps * tools-flavors: Added Apple silicon support * tools-flavors: Added support for Windows 64 bit * tools-flavors: give priority to better matching OS
1 parent 7081b38 commit a86b21d

File tree

2 files changed

+180
-68
lines changed

2 files changed

+180
-68
lines changed

arduino/cores/tools.go

+63-28
Original file line numberDiff line numberDiff line change
@@ -141,55 +141,90 @@ func (tr *ToolRelease) RuntimeProperties() *properties.Map {
141141
}
142142

143143
var (
144-
regexpArmLinux = regexp.MustCompile("arm.*-linux-gnueabihf")
145-
regexpArm64Linux = regexp.MustCompile("(aarch64|arm64)-linux-gnu")
146-
regexpAmd64 = regexp.MustCompile("x86_64-.*linux-gnu")
147-
regexpi386 = regexp.MustCompile("i[3456]86-.*linux-gnu")
148-
regexpWindows = regexp.MustCompile("i[3456]86-.*(mingw32|cygwin)")
149-
regexpMac64Bit = regexp.MustCompile("(i[3456]86|x86_64)-apple-darwin.*")
150-
regexpmac32Bit = regexp.MustCompile("i[3456]86-apple-darwin.*")
151-
regexpArmBSD = regexp.MustCompile("arm.*-freebsd[0-9]*")
144+
regexpLinuxArm = regexp.MustCompile("arm.*-linux-gnueabihf")
145+
regexpLinuxArm64 = regexp.MustCompile("(aarch64|arm64)-linux-gnu")
146+
regexpLinux64 = regexp.MustCompile("x86_64-.*linux-gnu")
147+
regexpLinux32 = regexp.MustCompile("i[3456]86-.*linux-gnu")
148+
regexpWindows32 = regexp.MustCompile("i[3456]86-.*(mingw32|cygwin)")
149+
regexpWindows64 = regexp.MustCompile("(amd64|x86_64)-.*(mingw32|cygwin)")
150+
regexpMac64 = regexp.MustCompile("x86_64-apple-darwin.*")
151+
regexpMac32 = regexp.MustCompile("i[3456]86-apple-darwin.*")
152+
regexpMacArm64 = regexp.MustCompile("arm64-apple-darwin.*")
153+
regexpFreeBSDArm = regexp.MustCompile("arm.*-freebsd[0-9]*")
154+
regexpFreeBSD32 = regexp.MustCompile("i?[3456]86-freebsd[0-9]*")
155+
regexpFreeBSD64 = regexp.MustCompile("amd64-freebsd[0-9]*")
152156
)
153157

154-
func (f *Flavor) isCompatibleWithCurrentMachine() bool {
155-
return f.isCompatibleWith(runtime.GOOS, runtime.GOARCH)
156-
}
157-
158-
func (f *Flavor) isCompatibleWith(osName, osArch string) bool {
158+
func (f *Flavor) isExactMatchWith(osName, osArch string) bool {
159159
if f.OS == "all" {
160160
return true
161161
}
162162

163163
switch osName + "," + osArch {
164164
case "linux,arm", "linux,armbe":
165-
return regexpArmLinux.MatchString(f.OS)
165+
return regexpLinuxArm.MatchString(f.OS)
166166
case "linux,arm64":
167-
return regexpArm64Linux.MatchString(f.OS)
167+
return regexpLinuxArm64.MatchString(f.OS)
168168
case "linux,amd64":
169-
return regexpAmd64.MatchString(f.OS)
169+
return regexpLinux64.MatchString(f.OS)
170170
case "linux,386":
171-
return regexpi386.MatchString(f.OS)
172-
case "windows,386", "windows,amd64":
173-
return regexpWindows.MatchString(f.OS)
171+
return regexpLinux32.MatchString(f.OS)
172+
case "windows,386":
173+
return regexpWindows32.MatchString(f.OS)
174+
case "windows,amd64":
175+
return regexpWindows64.MatchString(f.OS)
176+
case "darwin,arm64":
177+
return regexpMacArm64.MatchString(f.OS)
174178
case "darwin,amd64":
175-
return regexpmac32Bit.MatchString(f.OS) || regexpMac64Bit.MatchString(f.OS)
179+
return regexpMac64.MatchString(f.OS)
176180
case "darwin,386":
177-
return regexpmac32Bit.MatchString(f.OS)
181+
return regexpMac32.MatchString(f.OS)
178182
case "freebsd,arm":
179-
return regexpArmBSD.MatchString(f.OS)
180-
case "freebsd,386", "freebsd,amd64":
181-
genericFreeBSDexp := regexp.MustCompile(osArch + "%s-freebsd[0-9]*")
182-
return genericFreeBSDexp.MatchString(f.OS)
183+
return regexpFreeBSDArm.MatchString(f.OS)
184+
case "freebsd,386":
185+
return regexpFreeBSD32.MatchString(f.OS)
186+
case "freebsd,amd64":
187+
return regexpFreeBSD64.MatchString(f.OS)
183188
}
184189
return false
185190
}
186191

192+
func (f *Flavor) isCompatibleWith(osName, osArch string) (bool, int) {
193+
if f.isExactMatchWith(osName, osArch) {
194+
return true, 1000
195+
}
196+
197+
switch osName + "," + osArch {
198+
case "windows,amd64":
199+
return regexpWindows32.MatchString(f.OS), 10
200+
case "darwin,amd64":
201+
return regexpMac32.MatchString(f.OS), 10
202+
case "darwin,arm64":
203+
// Compatibility guaranteed through Rosetta emulation
204+
if regexpMac64.MatchString(f.OS) {
205+
// Prefer amd64 version if available
206+
return true, 20
207+
}
208+
return regexpMac32.MatchString(f.OS), 10
209+
}
210+
211+
return false, 0
212+
}
213+
187214
// GetCompatibleFlavour returns the downloadable resource compatible with the running O.S.
188215
func (tr *ToolRelease) GetCompatibleFlavour() *resources.DownloadResource {
216+
return tr.GetFlavourCompatibleWith(runtime.GOOS, runtime.GOARCH)
217+
}
218+
219+
// GetFlavourCompatibleWith returns the downloadable resource compatible with the specified O.S.
220+
func (tr *ToolRelease) GetFlavourCompatibleWith(osName, osArch string) *resources.DownloadResource {
221+
var resource *resources.DownloadResource
222+
priority := -1
189223
for _, flavour := range tr.Flavors {
190-
if flavour.isCompatibleWithCurrentMachine() {
191-
return flavour.Resource
224+
if comp, p := flavour.isCompatibleWith(osName, osArch); comp && p > priority {
225+
resource = flavour.Resource
226+
priority = p
192227
}
193228
}
194-
return nil
229+
return resource
195230
}

arduino/cores/tools_test.go

+117-40
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package cores
1818
import (
1919
"testing"
2020

21+
"github.com/arduino/arduino-cli/arduino/resources"
2122
"github.com/stretchr/testify/require"
2223
)
2324

@@ -26,71 +27,147 @@ func TestFlavorCompatibility(t *testing.T) {
2627
Os string
2728
Arch string
2829
}
29-
windowsi386 := &os{"windows", "386"}
30-
windowsx8664 := &os{"windows", "amd64"}
31-
linuxi386 := &os{"linux", "386"}
32-
linuxamd64 := &os{"linux", "amd64"}
33-
linuxarm := &os{"linux", "arm"}
34-
linuxarmbe := &os{"linux", "armbe"}
35-
linuxarm64 := &os{"linux", "arm64"}
36-
darwini386 := &os{"darwin", "386"}
37-
darwinamd64 := &os{"darwin", "amd64"}
38-
freebsdi386 := &os{"freebsd", "386"}
39-
freebsdamd64 := &os{"freebsd", "amd64"}
30+
windows32 := &os{"windows", "386"}
31+
windows64 := &os{"windows", "amd64"}
32+
linux32 := &os{"linux", "386"}
33+
linux64 := &os{"linux", "amd64"}
34+
linuxArm := &os{"linux", "arm"}
35+
linuxArmbe := &os{"linux", "armbe"}
36+
linuxArm64 := &os{"linux", "arm64"}
37+
darwin32 := &os{"darwin", "386"}
38+
darwin64 := &os{"darwin", "amd64"}
39+
darwinArm64 := &os{"darwin", "arm64"}
40+
freebsd32 := &os{"freebsd", "386"}
41+
freebsd64 := &os{"freebsd", "amd64"}
4042
oses := []*os{
41-
windowsi386,
42-
windowsx8664,
43-
linuxi386,
44-
linuxamd64,
45-
linuxarm,
46-
linuxarmbe,
47-
linuxarm64,
48-
darwini386,
49-
darwinamd64,
50-
freebsdi386,
51-
freebsdamd64,
43+
windows32,
44+
windows64,
45+
linux32,
46+
linux64,
47+
linuxArm,
48+
linuxArmbe,
49+
linuxArm64,
50+
darwin32,
51+
darwin64,
52+
darwinArm64,
53+
freebsd32,
54+
freebsd64,
5255
}
5356

5457
type test struct {
55-
Flavour *Flavor
56-
Positives []*os
58+
Flavour *Flavor
59+
Compatibles []*os
60+
ExactMatch []*os
5761
}
5862
tests := []*test{
59-
{&Flavor{OS: "i686-mingw32"}, []*os{windowsi386, windowsx8664}},
60-
{&Flavor{OS: "i386-apple-darwin11"}, []*os{darwini386, darwinamd64}},
61-
{&Flavor{OS: "x86_64-apple-darwin"}, []*os{darwinamd64}},
63+
{&Flavor{OS: "i686-mingw32"}, []*os{windows32, windows64}, []*os{windows32}},
64+
{&Flavor{OS: "x86_64-mingw32"}, []*os{windows64}, []*os{windows64}},
65+
{&Flavor{OS: "i386-apple-darwin11"}, []*os{darwin32, darwin64, darwinArm64}, []*os{darwin32}},
66+
{&Flavor{OS: "x86_64-apple-darwin"}, []*os{darwin64, darwinArm64}, []*os{darwin64}},
67+
{&Flavor{OS: "arm64-apple-darwin"}, []*os{darwinArm64}, []*os{darwinArm64}},
6268

6369
// Raspberry PI, BBB or other ARM based host
6470
// PI: "arm-linux-gnueabihf"
6571
// Raspbian on PI2: "arm-linux-gnueabihf"
6672
// Ubuntu Mate on PI2: "arm-linux-gnueabihf"
6773
// Debian 7.9 on BBB: "arm-linux-gnueabihf"
6874
// Raspbian on PI Zero: "arm-linux-gnueabihf"
69-
{&Flavor{OS: "arm-linux-gnueabihf"}, []*os{linuxarm, linuxarmbe}},
75+
{&Flavor{OS: "arm-linux-gnueabihf"}, []*os{linuxArm, linuxArmbe}, []*os{linuxArm, linuxArmbe}},
7076
// Arch-linux on PI2: "armv7l-unknown-linux-gnueabihf"
71-
{&Flavor{OS: "armv7l-unknown-linux-gnueabihf"}, []*os{linuxarm, linuxarmbe}},
77+
{&Flavor{OS: "armv7l-unknown-linux-gnueabihf"}, []*os{linuxArm, linuxArmbe}, []*os{linuxArm, linuxArmbe}},
7278

73-
{&Flavor{OS: "i686-linux-gnu"}, []*os{linuxi386}},
74-
{&Flavor{OS: "i686-pc-linux-gnu"}, []*os{linuxi386}},
75-
{&Flavor{OS: "x86_64-linux-gnu"}, []*os{linuxamd64}},
76-
{&Flavor{OS: "x86_64-pc-linux-gnu"}, []*os{linuxamd64}},
77-
{&Flavor{OS: "aarch64-linux-gnu"}, []*os{linuxarm64}},
78-
{&Flavor{OS: "arm64-linux-gnu"}, []*os{linuxarm64}},
79+
{&Flavor{OS: "i686-linux-gnu"}, []*os{linux32}, []*os{linux32}},
80+
{&Flavor{OS: "i686-pc-linux-gnu"}, []*os{linux32}, []*os{linux32}},
81+
{&Flavor{OS: "x86_64-linux-gnu"}, []*os{linux64}, []*os{linux64}},
82+
{&Flavor{OS: "x86_64-pc-linux-gnu"}, []*os{linux64}, []*os{linux64}},
83+
{&Flavor{OS: "aarch64-linux-gnu"}, []*os{linuxArm64}, []*os{linuxArm64}},
84+
{&Flavor{OS: "arm64-linux-gnu"}, []*os{linuxArm64}, []*os{linuxArm64}},
7985
}
8086

81-
check := func(test *test, os *os) {
82-
for _, positiveOs := range test.Positives {
87+
checkCompatible := func(test *test, os *os) {
88+
// if the os is in the "positive" set iCompatibleWith must return true...
89+
res, _ := test.Flavour.isCompatibleWith(os.Os, os.Arch)
90+
for _, compatibleOs := range test.Compatibles {
91+
if compatibleOs == os {
92+
require.True(t, res, "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
93+
return
94+
}
95+
}
96+
// ...otherwise false
97+
require.False(t, res, "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
98+
}
99+
checkExactMatch := func(test *test, os *os) {
100+
// if the os is in the "positive" set iExactMatchWith must return true...
101+
for _, positiveOs := range test.ExactMatch {
83102
if positiveOs == os {
84-
require.True(t, test.Flavour.isCompatibleWith(os.Os, os.Arch), "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
103+
require.True(t, test.Flavour.isExactMatchWith(os.Os, os.Arch), "'%s' tag exact match with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
85104
return
86105
}
87106
}
88-
require.False(t, test.Flavour.isCompatibleWith(os.Os, os.Arch), "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
107+
// ...otherwise false
108+
require.False(t, test.Flavour.isExactMatchWith(os.Os, os.Arch), "'%s' tag exact match with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
89109
}
90110

91111
for _, test := range tests {
92112
for _, os := range oses {
93-
check(test, os)
113+
checkCompatible(test, os)
114+
checkExactMatch(test, os)
94115
}
95116
}
96117
}
118+
119+
func TestFlavorPrioritySelection(t *testing.T) {
120+
res := (&ToolRelease{
121+
Flavors: []*Flavor{
122+
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
123+
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
124+
{OS: "arm64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "3"}},
125+
},
126+
}).GetFlavourCompatibleWith("darwin", "arm64")
127+
require.NotNil(t, res)
128+
require.Equal(t, "3", res.ArchiveFileName)
129+
130+
res = (&ToolRelease{
131+
Flavors: []*Flavor{
132+
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
133+
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
134+
},
135+
}).GetFlavourCompatibleWith("darwin", "arm64")
136+
require.NotNil(t, res)
137+
require.Equal(t, "2", res.ArchiveFileName)
138+
139+
res = (&ToolRelease{
140+
Flavors: []*Flavor{
141+
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
142+
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
143+
},
144+
}).GetFlavourCompatibleWith("darwin", "arm64")
145+
require.NotNil(t, res)
146+
require.Equal(t, "2", res.ArchiveFileName)
147+
148+
res = (&ToolRelease{
149+
Flavors: []*Flavor{
150+
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
151+
},
152+
}).GetFlavourCompatibleWith("darwin", "arm64")
153+
require.NotNil(t, res)
154+
require.Equal(t, "1", res.ArchiveFileName)
155+
156+
res = (&ToolRelease{
157+
Flavors: []*Flavor{
158+
{OS: "i686-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
159+
{OS: "x86_64-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
160+
},
161+
}).GetFlavourCompatibleWith("windows", "amd64")
162+
require.NotNil(t, res)
163+
require.Equal(t, "2", res.ArchiveFileName)
164+
165+
res = (&ToolRelease{
166+
Flavors: []*Flavor{
167+
{OS: "x86_64-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
168+
{OS: "i686-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
169+
},
170+
}).GetFlavourCompatibleWith("windows", "amd64")
171+
require.NotNil(t, res)
172+
require.Equal(t, "2", res.ArchiveFileName)
173+
}

0 commit comments

Comments
 (0)