Skip to content

Extended support for various OS / CPU #1077

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 63 additions & 28 deletions arduino/cores/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,55 +141,90 @@ func (tr *ToolRelease) RuntimeProperties() *properties.Map {
}

var (
regexpArmLinux = regexp.MustCompile("arm.*-linux-gnueabihf")
regexpArm64Linux = regexp.MustCompile("(aarch64|arm64)-linux-gnu")
regexpAmd64 = regexp.MustCompile("x86_64-.*linux-gnu")
regexpi386 = regexp.MustCompile("i[3456]86-.*linux-gnu")
regexpWindows = regexp.MustCompile("i[3456]86-.*(mingw32|cygwin)")
regexpMac64Bit = regexp.MustCompile("(i[3456]86|x86_64)-apple-darwin.*")
regexpmac32Bit = regexp.MustCompile("i[3456]86-apple-darwin.*")
regexpArmBSD = regexp.MustCompile("arm.*-freebsd[0-9]*")
regexpLinuxArm = regexp.MustCompile("arm.*-linux-gnueabihf")
regexpLinuxArm64 = regexp.MustCompile("(aarch64|arm64)-linux-gnu")
regexpLinux64 = regexp.MustCompile("x86_64-.*linux-gnu")
regexpLinux32 = regexp.MustCompile("i[3456]86-.*linux-gnu")
regexpWindows32 = regexp.MustCompile("i[3456]86-.*(mingw32|cygwin)")
regexpWindows64 = regexp.MustCompile("(amd64|x86_64)-.*(mingw32|cygwin)")
regexpMac64 = regexp.MustCompile("x86_64-apple-darwin.*")
regexpMac32 = regexp.MustCompile("i[3456]86-apple-darwin.*")
regexpMacArm64 = regexp.MustCompile("arm64-apple-darwin.*")
regexpFreeBSDArm = regexp.MustCompile("arm.*-freebsd[0-9]*")
regexpFreeBSD32 = regexp.MustCompile("i?[3456]86-freebsd[0-9]*")
regexpFreeBSD64 = regexp.MustCompile("amd64-freebsd[0-9]*")
)

func (f *Flavor) isCompatibleWithCurrentMachine() bool {
return f.isCompatibleWith(runtime.GOOS, runtime.GOARCH)
}

func (f *Flavor) isCompatibleWith(osName, osArch string) bool {
func (f *Flavor) isExactMatchWith(osName, osArch string) bool {
if f.OS == "all" {
return true
}

switch osName + "," + osArch {
case "linux,arm", "linux,armbe":
return regexpArmLinux.MatchString(f.OS)
return regexpLinuxArm.MatchString(f.OS)
case "linux,arm64":
return regexpArm64Linux.MatchString(f.OS)
return regexpLinuxArm64.MatchString(f.OS)
case "linux,amd64":
return regexpAmd64.MatchString(f.OS)
return regexpLinux64.MatchString(f.OS)
case "linux,386":
return regexpi386.MatchString(f.OS)
case "windows,386", "windows,amd64":
return regexpWindows.MatchString(f.OS)
return regexpLinux32.MatchString(f.OS)
case "windows,386":
return regexpWindows32.MatchString(f.OS)
case "windows,amd64":
return regexpWindows64.MatchString(f.OS)
case "darwin,arm64":
return regexpMacArm64.MatchString(f.OS)
case "darwin,amd64":
return regexpmac32Bit.MatchString(f.OS) || regexpMac64Bit.MatchString(f.OS)
return regexpMac64.MatchString(f.OS)
case "darwin,386":
return regexpmac32Bit.MatchString(f.OS)
return regexpMac32.MatchString(f.OS)
case "freebsd,arm":
return regexpArmBSD.MatchString(f.OS)
case "freebsd,386", "freebsd,amd64":
genericFreeBSDexp := regexp.MustCompile(osArch + "%s-freebsd[0-9]*")
return genericFreeBSDexp.MatchString(f.OS)
return regexpFreeBSDArm.MatchString(f.OS)
case "freebsd,386":
return regexpFreeBSD32.MatchString(f.OS)
case "freebsd,amd64":
return regexpFreeBSD64.MatchString(f.OS)
}
return false
}

func (f *Flavor) isCompatibleWith(osName, osArch string) (bool, int) {
if f.isExactMatchWith(osName, osArch) {
return true, 1000
}

switch osName + "," + osArch {
case "windows,amd64":
return regexpWindows32.MatchString(f.OS), 10
case "darwin,amd64":
return regexpMac32.MatchString(f.OS), 10
case "darwin,arm64":
// Compatibility guaranteed through Rosetta emulation
if regexpMac64.MatchString(f.OS) {
// Prefer amd64 version if available
return true, 20
}
return regexpMac32.MatchString(f.OS), 10
}

return false, 0
}

// GetCompatibleFlavour returns the downloadable resource compatible with the running O.S.
func (tr *ToolRelease) GetCompatibleFlavour() *resources.DownloadResource {
return tr.GetFlavourCompatibleWith(runtime.GOOS, runtime.GOARCH)
}

// GetFlavourCompatibleWith returns the downloadable resource compatible with the specified O.S.
func (tr *ToolRelease) GetFlavourCompatibleWith(osName, osArch string) *resources.DownloadResource {
var resource *resources.DownloadResource
priority := -1
for _, flavour := range tr.Flavors {
if flavour.isCompatibleWithCurrentMachine() {
return flavour.Resource
if comp, p := flavour.isCompatibleWith(osName, osArch); comp && p > priority {
resource = flavour.Resource
priority = p
}
}
return nil
return resource
}
157 changes: 117 additions & 40 deletions arduino/cores/tools_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package cores
import (
"testing"

"github.com/arduino/arduino-cli/arduino/resources"
"github.com/stretchr/testify/require"
)

Expand All @@ -26,71 +27,147 @@ func TestFlavorCompatibility(t *testing.T) {
Os string
Arch string
}
windowsi386 := &os{"windows", "386"}
windowsx8664 := &os{"windows", "amd64"}
linuxi386 := &os{"linux", "386"}
linuxamd64 := &os{"linux", "amd64"}
linuxarm := &os{"linux", "arm"}
linuxarmbe := &os{"linux", "armbe"}
linuxarm64 := &os{"linux", "arm64"}
darwini386 := &os{"darwin", "386"}
darwinamd64 := &os{"darwin", "amd64"}
freebsdi386 := &os{"freebsd", "386"}
freebsdamd64 := &os{"freebsd", "amd64"}
windows32 := &os{"windows", "386"}
windows64 := &os{"windows", "amd64"}
linux32 := &os{"linux", "386"}
linux64 := &os{"linux", "amd64"}
linuxArm := &os{"linux", "arm"}
linuxArmbe := &os{"linux", "armbe"}
linuxArm64 := &os{"linux", "arm64"}
darwin32 := &os{"darwin", "386"}
darwin64 := &os{"darwin", "amd64"}
darwinArm64 := &os{"darwin", "arm64"}
freebsd32 := &os{"freebsd", "386"}
freebsd64 := &os{"freebsd", "amd64"}
oses := []*os{
windowsi386,
windowsx8664,
linuxi386,
linuxamd64,
linuxarm,
linuxarmbe,
linuxarm64,
darwini386,
darwinamd64,
freebsdi386,
freebsdamd64,
windows32,
windows64,
linux32,
linux64,
linuxArm,
linuxArmbe,
linuxArm64,
darwin32,
darwin64,
darwinArm64,
freebsd32,
freebsd64,
}

type test struct {
Flavour *Flavor
Positives []*os
Flavour *Flavor
Compatibles []*os
ExactMatch []*os
}
tests := []*test{
{&Flavor{OS: "i686-mingw32"}, []*os{windowsi386, windowsx8664}},
{&Flavor{OS: "i386-apple-darwin11"}, []*os{darwini386, darwinamd64}},
{&Flavor{OS: "x86_64-apple-darwin"}, []*os{darwinamd64}},
{&Flavor{OS: "i686-mingw32"}, []*os{windows32, windows64}, []*os{windows32}},
{&Flavor{OS: "x86_64-mingw32"}, []*os{windows64}, []*os{windows64}},
{&Flavor{OS: "i386-apple-darwin11"}, []*os{darwin32, darwin64, darwinArm64}, []*os{darwin32}},
{&Flavor{OS: "x86_64-apple-darwin"}, []*os{darwin64, darwinArm64}, []*os{darwin64}},
{&Flavor{OS: "arm64-apple-darwin"}, []*os{darwinArm64}, []*os{darwinArm64}},

// Raspberry PI, BBB or other ARM based host
// PI: "arm-linux-gnueabihf"
// Raspbian on PI2: "arm-linux-gnueabihf"
// Ubuntu Mate on PI2: "arm-linux-gnueabihf"
// Debian 7.9 on BBB: "arm-linux-gnueabihf"
// Raspbian on PI Zero: "arm-linux-gnueabihf"
{&Flavor{OS: "arm-linux-gnueabihf"}, []*os{linuxarm, linuxarmbe}},
{&Flavor{OS: "arm-linux-gnueabihf"}, []*os{linuxArm, linuxArmbe}, []*os{linuxArm, linuxArmbe}},
// Arch-linux on PI2: "armv7l-unknown-linux-gnueabihf"
{&Flavor{OS: "armv7l-unknown-linux-gnueabihf"}, []*os{linuxarm, linuxarmbe}},
{&Flavor{OS: "armv7l-unknown-linux-gnueabihf"}, []*os{linuxArm, linuxArmbe}, []*os{linuxArm, linuxArmbe}},

{&Flavor{OS: "i686-linux-gnu"}, []*os{linuxi386}},
{&Flavor{OS: "i686-pc-linux-gnu"}, []*os{linuxi386}},
{&Flavor{OS: "x86_64-linux-gnu"}, []*os{linuxamd64}},
{&Flavor{OS: "x86_64-pc-linux-gnu"}, []*os{linuxamd64}},
{&Flavor{OS: "aarch64-linux-gnu"}, []*os{linuxarm64}},
{&Flavor{OS: "arm64-linux-gnu"}, []*os{linuxarm64}},
{&Flavor{OS: "i686-linux-gnu"}, []*os{linux32}, []*os{linux32}},
{&Flavor{OS: "i686-pc-linux-gnu"}, []*os{linux32}, []*os{linux32}},
{&Flavor{OS: "x86_64-linux-gnu"}, []*os{linux64}, []*os{linux64}},
{&Flavor{OS: "x86_64-pc-linux-gnu"}, []*os{linux64}, []*os{linux64}},
{&Flavor{OS: "aarch64-linux-gnu"}, []*os{linuxArm64}, []*os{linuxArm64}},
{&Flavor{OS: "arm64-linux-gnu"}, []*os{linuxArm64}, []*os{linuxArm64}},
}

check := func(test *test, os *os) {
for _, positiveOs := range test.Positives {
checkCompatible := func(test *test, os *os) {
// if the os is in the "positive" set iCompatibleWith must return true...
res, _ := test.Flavour.isCompatibleWith(os.Os, os.Arch)
for _, compatibleOs := range test.Compatibles {
if compatibleOs == os {
require.True(t, res, "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
return
}
}
// ...otherwise false
require.False(t, res, "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
}
checkExactMatch := func(test *test, os *os) {
// if the os is in the "positive" set iExactMatchWith must return true...
for _, positiveOs := range test.ExactMatch {
if positiveOs == os {
require.True(t, test.Flavour.isCompatibleWith(os.Os, os.Arch), "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
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)
return
}
}
require.False(t, test.Flavour.isCompatibleWith(os.Os, os.Arch), "'%s' tag compatible with '%s,%s' pair", test.Flavour.OS, os.Os, os.Arch)
// ...otherwise false
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)
}

for _, test := range tests {
for _, os := range oses {
check(test, os)
checkCompatible(test, os)
checkExactMatch(test, os)
}
}
}

func TestFlavorPrioritySelection(t *testing.T) {
res := (&ToolRelease{
Flavors: []*Flavor{
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
{OS: "arm64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "3"}},
},
}).GetFlavourCompatibleWith("darwin", "arm64")
require.NotNil(t, res)
require.Equal(t, "3", res.ArchiveFileName)

res = (&ToolRelease{
Flavors: []*Flavor{
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
},
}).GetFlavourCompatibleWith("darwin", "arm64")
require.NotNil(t, res)
require.Equal(t, "2", res.ArchiveFileName)

res = (&ToolRelease{
Flavors: []*Flavor{
{OS: "x86_64-apple-darwin", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
},
}).GetFlavourCompatibleWith("darwin", "arm64")
require.NotNil(t, res)
require.Equal(t, "2", res.ArchiveFileName)

res = (&ToolRelease{
Flavors: []*Flavor{
{OS: "i386-apple-darwin11", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
},
}).GetFlavourCompatibleWith("darwin", "arm64")
require.NotNil(t, res)
require.Equal(t, "1", res.ArchiveFileName)

res = (&ToolRelease{
Flavors: []*Flavor{
{OS: "i686-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
{OS: "x86_64-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
},
}).GetFlavourCompatibleWith("windows", "amd64")
require.NotNil(t, res)
require.Equal(t, "2", res.ArchiveFileName)

res = (&ToolRelease{
Flavors: []*Flavor{
{OS: "x86_64-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "2"}},
{OS: "i686-mingw32", Resource: &resources.DownloadResource{ArchiveFileName: "1"}},
},
}).GetFlavourCompatibleWith("windows", "amd64")
require.NotNil(t, res)
require.Equal(t, "2", res.ArchiveFileName)
}