Skip to content

Commit 24503d5

Browse files
matthijskooijmanRoberto Sora
authored and
Roberto Sora
committed
Load contents of source files only when needed (#559)
* [skip changelog] Pass path and contents separately to SketchSaveItemCpp Previously, these two arguments were wrapped together in a sketch.Item, but since all callers build such an item during the call, there is no compelling reason to do it like this. This commit splits the Item parameter into a separate path and contents, which prepares for removing the file contents from sketch.Item later. * [skip changelog] Let SketchMergeSources return error This adds an error return value, which is currently always nil. This prepares for making changes that require returning errors. * [skip changelog] Let sketch.Item.GetSourceStr return error This adds an error return value to this method, which is currently always nil. This prepares for actually returning errors later. * Load sketch file contents only when needed Previously, the full contents of *all* sketch files would be loaded into memory. This includes all source and header files inside the sketch directory, even when they will not even be compiled (e.g. subdirectories other than src). In practice, only the .ino file contents will actually be used, so these are now read on demand. Note that when copying the sketch into the build directory, the contents of all these sketch files *is* used, but that code (`writeIfDifferent()` in `arduino/builder/sketch.go`) already did not use the preloaded data but read the file contents when copying. For small sketches, this does not make much of a difference, but bigger sketches, especially when they include libraries, core definitions, tools, examples, documentation, etc. the memory usage can quite explode, for no good reason.
1 parent 50961cd commit 24503d5

9 files changed

+65
-59
lines changed

Diff for: arduino/builder/sketch.go

+16-8
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ func QuoteCppString(str string) string {
4747
}
4848

4949
// SketchSaveItemCpp saves a preprocessed .cpp sketch file on disk
50-
func SketchSaveItemCpp(item *sketch.Item, destPath string) error {
50+
func SketchSaveItemCpp(path string, contents []byte, destPath string) error {
5151

52-
sketchName := filepath.Base(item.Path)
52+
sketchName := filepath.Base(path)
5353

5454
if err := os.MkdirAll(destPath, os.FileMode(0755)); err != nil {
5555
return errors.Wrap(err, "unable to create a folder to save the sketch")
5656
}
5757

5858
destFile := filepath.Join(destPath, sketchName+".cpp")
5959

60-
if err := ioutil.WriteFile(destFile, item.Source, os.FileMode(0644)); err != nil {
60+
if err := ioutil.WriteFile(destFile, contents, os.FileMode(0644)); err != nil {
6161
return errors.Wrap(err, "unable to save the sketch on disk")
6262
}
6363

@@ -216,26 +216,34 @@ func SketchLoad(sketchPath, buildPath string) (*sketch.Sketch, error) {
216216
}
217217

218218
// SketchMergeSources merges all the source files included in a sketch
219-
func SketchMergeSources(sketch *sketch.Sketch) (int, string) {
219+
func SketchMergeSources(sketch *sketch.Sketch) (int, string, error) {
220220
lineOffset := 0
221221
mergedSource := ""
222222

223223
// add Arduino.h inclusion directive if missing
224-
if !includesArduinoH.MatchString(sketch.MainFile.GetSourceStr()) {
224+
mainSrc, err := sketch.MainFile.GetSourceStr()
225+
if err != nil {
226+
return 0, "", err
227+
}
228+
if !includesArduinoH.MatchString(mainSrc) {
225229
mergedSource += "#include <Arduino.h>\n"
226230
lineOffset++
227231
}
228232

229233
mergedSource += "#line 1 " + QuoteCppString(sketch.MainFile.Path) + "\n"
230-
mergedSource += sketch.MainFile.GetSourceStr() + "\n"
234+
mergedSource += mainSrc + "\n"
231235
lineOffset++
232236

233237
for _, item := range sketch.OtherSketchFiles {
238+
src, err := item.GetSourceStr()
239+
if err != nil {
240+
return 0, "", err
241+
}
234242
mergedSource += "#line 1 " + QuoteCppString(item.Path) + "\n"
235-
mergedSource += item.GetSourceStr() + "\n"
243+
mergedSource += src + "\n"
236244
}
237245

238-
return lineOffset, mergedSource
246+
return lineOffset, mergedSource, nil
239247
}
240248

241249
// SketchCopyAdditionalFiles copies the additional files for a sketch to the

Diff for: arduino/builder/sketch_test.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import (
2525
"testing"
2626

2727
"github.com/arduino/arduino-cli/arduino/builder"
28-
"github.com/arduino/arduino-cli/arduino/sketch"
2928
"github.com/stretchr/testify/require"
3029
)
3130

@@ -40,7 +39,7 @@ func TestSaveSketch(t *testing.T) {
4039
t.Fatalf("unable to read golden file %s: %v", sketchFile, err)
4140
}
4241

43-
builder.SketchSaveItemCpp(&sketch.Item{Path: sketchName, Source: source}, tmp)
42+
builder.SketchSaveItemCpp(sketchName, source, tmp)
4443

4544
out, err := ioutil.ReadFile(filepath.Join(tmp, outName))
4645
if err != nil {
@@ -181,7 +180,8 @@ func TestMergeSketchSources(t *testing.T) {
181180
t.Fatalf("unable to read golden file %s: %v", mergedPath, err)
182181
}
183182

184-
offset, source := builder.SketchMergeSources(s)
183+
offset, source, err := builder.SketchMergeSources(s)
184+
require.Nil(t, err)
185185
require.Equal(t, 2, offset)
186186
require.Equal(t, string(mergedBytes), source)
187187
}
@@ -192,7 +192,8 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) {
192192
require.NotNil(t, s)
193193

194194
// ensure not to include Arduino.h when it's already there
195-
_, source := builder.SketchMergeSources(s)
195+
_, source, err := builder.SketchMergeSources(s)
196+
require.Nil(t, err)
196197
require.Equal(t, 1, strings.Count(source, "<Arduino.h>"))
197198
}
198199

Diff for: arduino/sketch/sketch.go

+17-13
Original file line numberDiff line numberDiff line change
@@ -27,25 +27,32 @@ import (
2727

2828
// Item holds the source and the path for a single sketch file
2929
type Item struct {
30-
Path string
31-
Source []byte
30+
Path string
3231
}
3332

3433
// NewItem reads the source code for a sketch item and returns an
3534
// Item instance
36-
func NewItem(itemPath string) (*Item, error) {
35+
func NewItem(itemPath string) *Item {
36+
return &Item{itemPath}
37+
}
38+
39+
// GetSourceBytes reads the item file contents and returns it as bytes
40+
func (i *Item) GetSourceBytes() ([]byte, error) {
3741
// read the file
38-
source, err := ioutil.ReadFile(itemPath)
42+
source, err := ioutil.ReadFile(i.Path)
3943
if err != nil {
4044
return nil, errors.Wrap(err, "error reading source file")
4145
}
42-
43-
return &Item{itemPath, source}, nil
46+
return source, nil
4447
}
4548

46-
// GetSourceStr returns the Source contents in string format
47-
func (i *Item) GetSourceStr() string {
48-
return string(i.Source)
49+
// GetSourceStr reads the item file contents and returns it as a string
50+
func (i *Item) GetSourceStr() (string, error) {
51+
source, err := i.GetSourceBytes()
52+
if err != nil {
53+
return "", err
54+
}
55+
return string(source), nil
4956
}
5057

5158
// ItemByPath implements sort.Interface for []Item based on
@@ -73,10 +80,7 @@ func New(sketchFolderPath, mainFilePath, buildPath string, allFilesPaths []strin
7380
pathToItem := make(map[string]*Item)
7481
for _, p := range allFilesPaths {
7582
// create an Item
76-
item, err := NewItem(p)
77-
if err != nil {
78-
return nil, errors.Wrap(err, "error creating the sketch")
79-
}
83+
item := NewItem(p)
8084

8185
if p == mainFilePath {
8286
// store the main sketch file

Diff for: arduino/sketch/sketch_test.go

+13-9
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,26 @@ import (
2626

2727
func TestNewItem(t *testing.T) {
2828
sketchItem := filepath.Join("testdata", t.Name()+".ino")
29-
item, err := sketch.NewItem(sketchItem)
30-
assert.Nil(t, err)
29+
item := sketch.NewItem(sketchItem)
3130
assert.Equal(t, sketchItem, item.Path)
32-
assert.Equal(t, []byte(`#include <testlib.h>`), item.Source)
33-
assert.Equal(t, "#include <testlib.h>", item.GetSourceStr())
31+
sourceBytes, err := item.GetSourceBytes()
32+
assert.Nil(t, err)
33+
assert.Equal(t, []byte(`#include <testlib.h>`), sourceBytes)
34+
sourceStr, err := item.GetSourceStr()
35+
assert.Nil(t, err)
36+
assert.Equal(t, "#include <testlib.h>", sourceStr)
3437

35-
item, err = sketch.NewItem("doesnt/exist")
36-
assert.Nil(t, item)
38+
item = sketch.NewItem("doesnt/exist")
39+
sourceBytes, err = item.GetSourceBytes()
40+
assert.Nil(t, sourceBytes)
3741
assert.NotNil(t, err)
3842
}
3943

4044
func TestSort(t *testing.T) {
4145
items := []*sketch.Item{
42-
&sketch.Item{"foo", nil},
43-
&sketch.Item{"baz", nil},
44-
&sketch.Item{"bar", nil},
46+
&sketch.Item{"foo"},
47+
&sketch.Item{"baz"},
48+
&sketch.Item{"bar"},
4549
}
4650

4751
sort.Sort(sketch.ItemByPath(items))

Diff for: legacy/builder/container_add_prototypes.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package builder
1717

1818
import (
1919
bldr "github.com/arduino/arduino-cli/arduino/builder"
20-
"github.com/arduino/arduino-cli/arduino/sketch"
2120
"github.com/arduino/arduino-cli/legacy/builder/constants"
2221
"github.com/arduino/arduino-cli/legacy/builder/i18n"
2322
"github.com/arduino/arduino-cli/legacy/builder/types"
@@ -54,7 +53,7 @@ func (s *ContainerAddPrototypes) Run(ctx *types.Context) error {
5453
}
5554
}
5655

57-
if err := bldr.SketchSaveItemCpp(&sketch.Item{ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source)}, ctx.SketchBuildPath.String()); err != nil {
56+
if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source), ctx.SketchBuildPath.String()); err != nil {
5857
return i18n.WrapError(err)
5958
}
6059

Diff for: legacy/builder/container_merge_copy_sketch_files.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ package builder
1717

1818
import (
1919
bldr "github.com/arduino/arduino-cli/arduino/builder"
20-
"github.com/arduino/arduino-cli/arduino/sketch"
2120
"github.com/arduino/arduino-cli/legacy/builder/i18n"
2221
"github.com/arduino/arduino-cli/legacy/builder/types"
2322
"github.com/go-errors/errors"
@@ -30,11 +29,14 @@ func (s *ContainerMergeCopySketchFiles) Run(ctx *types.Context) error {
3029
if sk == nil {
3130
return i18n.WrapError(errors.New("unable to convert legacy sketch to the new type"))
3231
}
33-
offset, source := bldr.SketchMergeSources(sk)
32+
offset, source, err := bldr.SketchMergeSources(sk)
33+
if err != nil {
34+
return err
35+
}
3436
ctx.LineOffset = offset
3537
ctx.Source = source
3638

37-
if err := bldr.SketchSaveItemCpp(&sketch.Item{ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source)}, ctx.SketchBuildPath.String()); err != nil {
39+
if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source), ctx.SketchBuildPath.String()); err != nil {
3840
return i18n.WrapError(err)
3941
}
4042

Diff for: legacy/builder/preprocess_sketch.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
"strings"
2525

2626
bldr "github.com/arduino/arduino-cli/arduino/builder"
27-
"github.com/arduino/arduino-cli/arduino/sketch"
2827
"github.com/arduino/arduino-cli/legacy/builder/constants"
2928
"github.com/arduino/arduino-cli/legacy/builder/i18n"
3029
"github.com/arduino/arduino-cli/legacy/builder/types"
@@ -68,7 +67,7 @@ func (s *PreprocessSketchArduino) Run(ctx *types.Context) error {
6867
if ctx.CodeCompleteAt != "" {
6968
err = new(OutputCodeCompletions).Run(ctx)
7069
} else {
71-
err = bldr.SketchSaveItemCpp(&sketch.Item{ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source)}, ctx.SketchBuildPath.String())
70+
err = bldr.SketchSaveItemCpp(ctx.Sketch.MainFile.Name.String(), []byte(ctx.Source), ctx.SketchBuildPath.String())
7271
}
7372

7473
return err

Diff for: legacy/builder/sketch_loader.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,7 @@ func collectAllSketchFiles(from *paths.Path) (paths.PathList, error) {
8888
func makeSketch(sketchLocation *paths.Path, allSketchFilePaths paths.PathList, buildLocation *paths.Path, logger i18n.Logger) (*types.Sketch, error) {
8989
sketchFilesMap := make(map[string]types.SketchFile)
9090
for _, sketchFilePath := range allSketchFilePaths {
91-
source, err := sketchFilePath.ReadFile()
92-
if err != nil {
93-
return nil, i18n.WrapError(err)
94-
}
95-
sketchFilesMap[sketchFilePath.String()] = types.SketchFile{Name: sketchFilePath, Source: string(source)}
91+
sketchFilesMap[sketchFilePath.String()] = types.SketchFile{Name: sketchFilePath}
9692
}
9793

9894
mainFile := sketchFilesMap[sketchLocation.String()]

Diff for: legacy/builder/types/types.go

+6-13
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,7 @@ func (f *SourceFile) DepfilePath(ctx *Context) *paths.Path {
8686
}
8787

8888
type SketchFile struct {
89-
Name *paths.Path
90-
Source string
89+
Name *paths.Path
9190
}
9291

9392
type SketchFileSortByName []SketchFile
@@ -114,20 +113,17 @@ func SketchToLegacy(sketch *sketch.Sketch) *Sketch {
114113
s := &Sketch{}
115114
s.MainFile = SketchFile{
116115
paths.New(sketch.MainFile.Path),
117-
string(sketch.MainFile.Source),
118116
}
119117

120118
for _, item := range sketch.OtherSketchFiles {
121119
s.OtherSketchFiles = append(s.OtherSketchFiles, SketchFile{
122120
paths.New(item.Path),
123-
string(item.Source),
124121
})
125122
}
126123

127124
for _, item := range sketch.AdditionalFiles {
128125
s.AdditionalFiles = append(s.AdditionalFiles, SketchFile{
129126
paths.New(item.Path),
130-
string(item.Source),
131127
})
132128
}
133129

@@ -137,22 +133,19 @@ func SketchToLegacy(sketch *sketch.Sketch) *Sketch {
137133
func SketchFromLegacy(s *Sketch) *sketch.Sketch {
138134
others := []*sketch.Item{}
139135
for _, f := range s.OtherSketchFiles {
140-
if i, err := sketch.NewItem(f.Name.String()); err == nil {
141-
others = append(others, i)
142-
}
136+
i := sketch.NewItem(f.Name.String())
137+
others = append(others, i)
143138
}
144139

145140
additional := []*sketch.Item{}
146141
for _, f := range s.AdditionalFiles {
147-
if i, err := sketch.NewItem(f.Name.String()); err == nil {
148-
additional = append(additional, i)
149-
}
142+
i := sketch.NewItem(f.Name.String())
143+
additional = append(additional, i)
150144
}
151145

152146
return &sketch.Sketch{
153147
MainFile: &sketch.Item{
154-
Path: s.MainFile.Name.String(),
155-
Source: []byte(s.MainFile.Source),
148+
Path: s.MainFile.Name.String(),
156149
},
157150
LocationPath: s.MainFile.Name.Parent().String(),
158151
OtherSketchFiles: others,

0 commit comments

Comments
 (0)