Skip to content

Commit 2d1e75d

Browse files
committed
Imported HardwareLoader algorithm from arduino-builder (WIP)
1 parent b5f905e commit 2d1e75d

File tree

4 files changed

+356
-0
lines changed

4 files changed

+356
-0
lines changed

cores/board.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* arduino-cli is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2017-2018 ARDUINO AG (http://www.arduino.cc/)
28+
*/
29+
30+
package cores
31+
32+
import properties "github.com/arduino/go-properties-map"
33+
34+
// Board represents a board loaded from an installed platform
35+
type Board struct {
36+
BoardId string
37+
Properties properties.Map `json:"-"`
38+
PlatformRelease *PlatformRelease `json:"-"`
39+
}

cores/cores.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
"fmt"
3434
"strings"
3535

36+
properties "github.com/arduino/go-properties-map"
3637
"github.com/bcmi-labs/arduino-cli/common/releases"
3738

3839
"github.com/blang/semver"
@@ -54,6 +55,11 @@ type PlatformRelease struct {
5455
BoardNames []string
5556
Dependencies ToolDependencies // The Dependency entries to load tools.
5657
Platform *Platform `json:"-"`
58+
59+
Properties properties.Map `json:"-"`
60+
Boards map[string]*Board `json:"-"`
61+
Programmers map[string]properties.Map `json:"-"`
62+
Folder string `json:"-"`
5763
}
5864

5965
// ToolDependencies is a set of tool dependency
@@ -104,6 +110,18 @@ func (platform *Platform) latestVersion() string {
104110
return ""
105111
}
106112

113+
// GetInstalled return one of the installed PlatformRelease
114+
// TODO: This is a temporary method to help incremental transition from
115+
// arduino-builder, it will be probably removed in the future
116+
func (platform *Platform) GetInstalled() *PlatformRelease {
117+
for _, release := range platform.Releases {
118+
if release.Folder != "" {
119+
return release
120+
}
121+
}
122+
return nil
123+
}
124+
107125
func (platform *Platform) String() string {
108126
res := fmt.Sprintln("Name :", platform.Name) +
109127
fmt.Sprintln("Architecture:", platform.Architecture) +

cores/loader.go

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
/*
2+
* This file is part of arduino-cli.
3+
*
4+
* arduino-cli is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17+
*
18+
* As a special exception, you may use this file as part of a free software
19+
* library without restriction. Specifically, if other files instantiate
20+
* templates or use macros or inline functions from this file, or you compile
21+
* this file and link it with other files to produce an executable, this
22+
* file does not by itself cause the resulting executable to be covered by
23+
* the GNU General Public License. This exception does not however
24+
* invalidate any other reasons why the executable file might be covered by
25+
* the GNU General Public License.
26+
*
27+
* Copyright 2017-2018 ARDUINO AG (http://www.arduino.cc/)
28+
*/
29+
30+
package cores
31+
32+
import (
33+
"fmt"
34+
"io/ioutil"
35+
"os"
36+
"path/filepath"
37+
38+
properties "github.com/arduino/go-properties-map"
39+
)
40+
41+
// LoadHardwareFromFolders read all plaforms from a folder
42+
func (packages *Packages) LoadHardwareFromFolders(folders []string) error {
43+
for _, folder := range folders {
44+
folder, err := filepath.Abs(folder)
45+
if err != nil {
46+
return fmt.Errorf("find abs path: %s", err)
47+
}
48+
// TODO: IS THIS CHECK NEEDED? can we ignore and let it fail at next ReadDir?
49+
if stat, err := os.Stat(folder); err != nil {
50+
return fmt.Errorf("reading %s stat info: %s", folder, err)
51+
} else if !stat.IsDir() {
52+
return fmt.Errorf("%s is not a folder", folder)
53+
}
54+
55+
// TODO: IS THIS REALLY NEEDED? this is used only to get ctags properties AFAIK
56+
platformTxtPath := filepath.Join(folder, "platform.txt")
57+
if props, err := properties.SafeLoad(platformTxtPath); err == nil {
58+
packages.Properties.Merge(props)
59+
} else {
60+
return fmt.Errorf("reading %s: %s", platformTxtPath, err)
61+
}
62+
63+
// Scan subfolders.
64+
files, err := ioutil.ReadDir(folder)
65+
if err != nil {
66+
return fmt.Errorf("reading %s directory: %s", folder, err)
67+
}
68+
for _, subfolder := range files {
69+
// First exclude all "tools" folders
70+
packager := subfolder.Name()
71+
if packager == "tools" {
72+
continue
73+
}
74+
75+
if err := packages.LoadPackage(packager, filepath.Join(folder, packager)); err != nil {
76+
return err
77+
}
78+
}
79+
}
80+
81+
return nil
82+
}
83+
84+
// LoadPackage load the package identified by packager in the specified path
85+
func (packages *Packages) LoadPackage(packager string, path string) error {
86+
// Follow symlinks
87+
path, err := filepath.EvalSymlinks(path)
88+
if err != nil {
89+
return fmt.Errorf("following possible symlink %s: %s", path, err)
90+
}
91+
92+
// There are two possible package folder structures:
93+
// - PACKAGER/ARCHITECTURE/...
94+
// - PACKAGER/hardware/ARCHITECTURE/VERSION/...
95+
// if we found the latter we just move into "hardware" folder and continue
96+
var packagePath string
97+
hardwareSubdirPath := filepath.Join(path, "hardware")
98+
if info, err := os.Stat(hardwareSubdirPath); err == nil && info.IsDir() {
99+
packagePath = hardwareSubdirPath
100+
} else if info, err := os.Stat(path); err == nil && info.IsDir() {
101+
packagePath = path
102+
} else {
103+
// do nothing
104+
return nil
105+
}
106+
107+
targetPackage := packages.getOrCreatePackage(packager)
108+
if err := targetPackage.load(packagePath); err != nil {
109+
return fmt.Errorf("loading package %s: %s", packager, err)
110+
}
111+
return nil
112+
}
113+
114+
func (packages *Packages) getOrCreatePackage(packager string) *Package {
115+
if targetPackage, ok := packages.Packages[packager]; ok {
116+
return targetPackage
117+
}
118+
targetPackage := &Package{
119+
Name: packager,
120+
Platforms: map[string]*Platform{},
121+
Packages: packages,
122+
//Properties: properties.Map{},
123+
}
124+
packages.Packages[packager] = targetPackage
125+
return targetPackage
126+
}
127+
128+
func (targetPackage *Package) load(folder string) error {
129+
// packagePlatformTxt, err := properties.SafeLoad(filepath.Join(folder, constants.FILE_PLATFORM_TXT))
130+
// if err != nil {
131+
// return err
132+
// }
133+
// targetPackage.Properties.Merge(packagePlatformTxt)
134+
135+
files, err := ioutil.ReadDir(folder)
136+
if err != nil {
137+
return fmt.Errorf("reading directory %s: %s", folder, err)
138+
}
139+
140+
for _, file := range files {
141+
architecure := file.Name()
142+
platformPath := filepath.Join(folder, architecure)
143+
if architecure == "tools" ||
144+
architecure == "platform.txt" { // TODO: Check if this "platform.txt" condition should be here....
145+
continue
146+
}
147+
148+
// There are two possible platform folder structures:
149+
// - ARCHITECTURE/boards.txt
150+
// - ARCHITECTURE/VERSION/boards.txt
151+
// We identify them by checking where is the bords.txt file
152+
possibleBoardTxtPath := filepath.Join(platformPath, "boards.txt")
153+
if _, err := os.Stat(possibleBoardTxtPath); err == nil {
154+
// There is a boards.txt here, this is an unversioned Platform
155+
156+
platform := targetPackage.getOrCreatePlatform(architecure)
157+
release := platform.getOrCreateRelease("")
158+
if err := release.load(platformPath); err != nil {
159+
return fmt.Errorf("loading platform release: %s", err)
160+
}
161+
162+
} else if os.IsNotExist(err) {
163+
// There are no boards.txt here, let's fetch version folders
164+
165+
platform := targetPackage.getOrCreatePlatform(architecure)
166+
versionDirs, err := ioutil.ReadDir(platformPath)
167+
if err != nil {
168+
return fmt.Errorf("reading dir %s: %s", platformPath, err)
169+
}
170+
for _, versionDir := range versionDirs {
171+
if !versionDir.IsDir() {
172+
continue
173+
}
174+
version := versionDir.Name()
175+
release := platform.getOrCreateRelease(version)
176+
platformWithVersionPath := filepath.Join(platformPath, version)
177+
178+
if err := release.load(platformWithVersionPath); err != nil {
179+
return fmt.Errorf("loading platform release %s: %s", version, err)
180+
}
181+
}
182+
} else {
183+
return fmt.Errorf("looking for boards.txt in %s: %s", possibleBoardTxtPath, err)
184+
}
185+
}
186+
187+
return nil
188+
}
189+
190+
func (targetPackage *Package) getOrCreatePlatform(architecure string) *Platform {
191+
if platform, ok := targetPackage.Platforms[architecure]; ok {
192+
return platform
193+
}
194+
targetPlatform := &Platform{
195+
Architecture: architecure,
196+
Releases: map[string]*PlatformRelease{},
197+
Package: targetPackage,
198+
}
199+
targetPackage.Platforms[architecure] = targetPlatform
200+
return targetPlatform
201+
}
202+
203+
func (platform *Platform) getOrCreateRelease(version string) *PlatformRelease {
204+
if release, ok := platform.Releases[version]; ok {
205+
return release
206+
}
207+
release := &PlatformRelease{
208+
Boards: map[string]*Board{},
209+
Properties: properties.Map{},
210+
Programmers: map[string]properties.Map{},
211+
Platform: platform,
212+
}
213+
platform.Releases[version] = release
214+
return release
215+
}
216+
217+
func (platform *PlatformRelease) load(folder string) error {
218+
if _, err := os.Stat(filepath.Join(folder, "boards.txt")); err != nil && !os.IsNotExist(err) {
219+
return fmt.Errorf("opening boards.txt: %s", err)
220+
} else if os.IsNotExist(err) {
221+
return fmt.Errorf("invalid platform directory %s: boards.txt not found", folder)
222+
}
223+
platform.Folder = folder
224+
225+
// Some useful paths
226+
platformTxtPath := filepath.Join(folder, "platform.txt")
227+
platformTxtLocalPath := filepath.Join(folder, "platform.local.txt")
228+
programmersTxtPath := filepath.Join(folder, "programmers.txt")
229+
230+
// Create platform properties
231+
platform.Properties = platform.Properties.Clone() // TODO: why CLONE?
232+
if p, err := properties.SafeLoad(platformTxtPath); err == nil {
233+
platform.Properties.Merge(p)
234+
} else {
235+
return fmt.Errorf("loading %s: %s", platformTxtPath, err)
236+
}
237+
if p, err := properties.SafeLoad(platformTxtLocalPath); err == nil {
238+
platform.Properties.Merge(p)
239+
} else {
240+
return fmt.Errorf("loading %s: %s", platformTxtLocalPath, err)
241+
}
242+
243+
// Create programmers properties
244+
if programmersProperties, err := properties.SafeLoad(programmersTxtPath); err == nil {
245+
platform.Programmers = properties.MergeMapsOfProperties(
246+
map[string]properties.Map{},
247+
platform.Programmers, // TODO: Very weird, why not an empty one?
248+
programmersProperties.FirstLevelOf())
249+
} else {
250+
return err
251+
}
252+
253+
if err := platform.loadBoards(); err != nil {
254+
return err
255+
}
256+
257+
return nil
258+
}
259+
260+
func (platform *PlatformRelease) loadBoards() error {
261+
boardsTxtPath := filepath.Join(platform.Folder, "boards.txt")
262+
boardsLocalTxtPath := filepath.Join(platform.Folder, "boards.local.txt")
263+
264+
boardsProperties, err := properties.Load(boardsTxtPath)
265+
if err != nil {
266+
return err
267+
}
268+
if localProperties, err := properties.SafeLoad(boardsLocalTxtPath); err == nil {
269+
boardsProperties.Merge(localProperties)
270+
} else {
271+
return err
272+
}
273+
274+
propertiesByBoard := boardsProperties.FirstLevelOf()
275+
delete(propertiesByBoard, "menu") // TODO: check this one
276+
277+
for boardID, boardProperties := range propertiesByBoard {
278+
boardProperties["_id"] = boardID // TODO: What is that for??
279+
board := platform.getOrCreateBoard(boardID)
280+
board.Properties.Merge(boardProperties)
281+
platform.Boards[boardID] = board
282+
}
283+
284+
return nil
285+
}
286+
287+
func (platform *PlatformRelease) getOrCreateBoard(boardID string) *Board {
288+
if board, ok := platform.Boards[boardID]; ok {
289+
return board
290+
}
291+
return &Board{
292+
BoardId: boardID,
293+
Properties: properties.Map{},
294+
PlatformRelease: platform,
295+
}
296+
}

cores/status.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,15 @@ import (
3333
"errors"
3434
"fmt"
3535

36+
properties "github.com/arduino/go-properties-map"
3637
"github.com/pmylund/sortutil"
3738
)
3839

3940
// Packages represents a set of Packages
4041
type Packages struct {
4142
Packages map[string]*Package // Maps packager name to Package
43+
44+
Properties properties.Map `json:"-"` // TODO: used to add "ctags" properties, TO BE REMOVED
4245
}
4346

4447
// Package represents a package in the system.

0 commit comments

Comments
 (0)