Skip to content

Commit 71b39f9

Browse files
committed
Compile command now works with sketches containing .pde files
1 parent 283036f commit 71b39f9

File tree

9 files changed

+195
-12
lines changed

9 files changed

+195
-12
lines changed

arduino/globals/globals.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ package globals
1818
var (
1919
empty struct{}
2020

21+
// MainFileValidExtension is the extension that must be used for files in new sketches
22+
MainFileValidExtension string = ".ino"
23+
2124
// MainFileValidExtensions lists valid extensions for a sketch file
2225
MainFileValidExtensions = map[string]struct{}{
23-
".ino": empty,
26+
MainFileValidExtension: empty,
27+
// .pde extension is deprecated and must not be used for new sketches
2428
".pde": empty,
2529
}
2630

arduino/sketches/sketches.go

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ import (
2020
"fmt"
2121

2222
"github.com/arduino/arduino-cli/arduino/builder"
23+
"github.com/arduino/arduino-cli/arduino/globals"
2324
"github.com/arduino/go-paths-helper"
2425
"github.com/pkg/errors"
2526
)
2627

2728
// Sketch is a sketch for Arduino
2829
type Sketch struct {
29-
Name string
30-
FullPath *paths.Path
31-
Metadata *Metadata
30+
Name string
31+
MainFileExtension string
32+
FullPath *paths.Path
33+
Metadata *Metadata
3234
}
3335

3436
// Metadata is the kind of data associated to a project such as the connected board
@@ -52,14 +54,32 @@ func NewSketchFromPath(path *paths.Path) (*Sketch, error) {
5254
if !path.IsDir() {
5355
path = path.Parent()
5456
}
55-
sketchFile := path.Join(path.Base() + ".ino")
56-
if !sketchFile.Exist() {
57-
return nil, errors.Errorf("no valid sketch found in %s: missing %s", path, sketchFile.Base())
57+
58+
var mainSketchFile *paths.Path
59+
for ext := range globals.MainFileValidExtensions {
60+
candidateSketchMainFile := path.Join(path.Base() + ext)
61+
if candidateSketchMainFile.Exist() {
62+
if mainSketchFile == nil {
63+
mainSketchFile = candidateSketchMainFile
64+
} else {
65+
return nil, errors.Errorf("multiple main sketch files found (%v, %v)",
66+
mainSketchFile,
67+
candidateSketchMainFile,
68+
)
69+
}
70+
}
71+
}
72+
73+
if mainSketchFile == nil {
74+
sketchFile := path.Join(path.Base() + globals.MainFileValidExtension)
75+
return nil, errors.Errorf("no valid sketch found in %s: missing %s", path, sketchFile)
5876
}
77+
5978
sketch := &Sketch{
60-
FullPath: path,
61-
Name: path.Base(),
62-
Metadata: &Metadata{},
79+
FullPath: path,
80+
MainFileExtension: mainSketchFile.Ext(),
81+
Name: path.Base(),
82+
Metadata: &Metadata{},
6383
}
6484
sketch.ImportMetadata()
6585
return sketch, nil
@@ -108,3 +128,24 @@ func (s *Sketch) BuildPath() (*paths.Path, error) {
108128
}
109129
return builder.GenBuildPath(s.FullPath), nil
110130
}
131+
132+
// CheckForPdeFiles returns all files ending with .pde extension
133+
// in dir, this is mainly used to warn the user that these files
134+
// must be changed to .ino extension.
135+
// When .pde files won't be supported anymore this function must be removed.
136+
func CheckForPdeFiles(sketch *paths.Path) []*paths.Path {
137+
if sketch.IsNotDir() {
138+
sketch = sketch.Parent()
139+
}
140+
141+
// It's not a problem if we can't read files right now
142+
files, _ := sketch.ReadDirRecursive()
143+
files.FilterOutDirs()
144+
res := []*paths.Path{}
145+
for _, f := range files {
146+
if f.Ext() == ".pde" {
147+
res = append(res, f)
148+
}
149+
}
150+
return res
151+
}

arduino/sketches/sketches_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,59 @@ func TestSketchBuildPath(t *testing.T) {
5353
require.NoError(t, err)
5454
require.Contains(t, buildPath.String(), "arduino-sketch-")
5555

56+
// Verifies sketch path is returned if sketch has .pde extension
57+
sketchPath = paths.New("testdata", "SketchPde")
58+
sketch, err = NewSketchFromPath(sketchPath)
59+
require.NoError(t, err)
60+
require.NotNil(t, sketch)
61+
buildPath, err = sketch.BuildPath()
62+
require.NoError(t, err)
63+
require.Contains(t, buildPath.String(), "arduino-sketch-")
64+
65+
// Verifies error is returned if there are multiple main files
66+
sketchPath = paths.New("testdata", "SketchMultipleMainFiles")
67+
sketch, err = NewSketchFromPath(sketchPath)
68+
require.Nil(t, sketch)
69+
require.Error(t, err, "multiple main sketch files found")
70+
5671
// Verifies error is returned if sketch path is not set
5772
sketch = &Sketch{}
5873
buildPath, err = sketch.BuildPath()
5974
require.Nil(t, buildPath)
6075
require.Error(t, err, "sketch path is empty")
6176
}
77+
78+
func TestCheckForPdeFiles(t *testing.T) {
79+
sketchPath := paths.New("testdata", "Sketch1")
80+
files := CheckForPdeFiles(sketchPath)
81+
require.Empty(t, files)
82+
83+
sketchPath = paths.New("testdata", "SketchPde")
84+
files = CheckForPdeFiles(sketchPath)
85+
require.Len(t, files, 1)
86+
require.Equal(t, sketchPath.Join("SketchPde.pde"), files[0])
87+
88+
sketchPath = paths.New("testdata", "SketchMultipleMainFiles")
89+
files = CheckForPdeFiles(sketchPath)
90+
require.Len(t, files, 1)
91+
require.Equal(t, sketchPath.Join("SketchMultipleMainFiles.pde"), files[0])
92+
93+
sketchPath = paths.New("testdata", "Sketch1", "Sketch1.ino")
94+
files = CheckForPdeFiles(sketchPath)
95+
require.Empty(t, files)
96+
97+
sketchPath = paths.New("testdata", "SketchPde", "SketchPde.pde")
98+
files = CheckForPdeFiles(sketchPath)
99+
require.Len(t, files, 1)
100+
require.Equal(t, sketchPath.Parent().Join("SketchPde.pde"), files[0])
101+
102+
sketchPath = paths.New("testdata", "SketchMultipleMainFiles", "SketchMultipleMainFiles.ino")
103+
files = CheckForPdeFiles(sketchPath)
104+
require.Len(t, files, 1)
105+
require.Equal(t, sketchPath.Parent().Join("SketchMultipleMainFiles.pde"), files[0])
106+
107+
sketchPath = paths.New("testdata", "SketchMultipleMainFiles", "SketchMultipleMainFiles.pde")
108+
files = CheckForPdeFiles(sketchPath)
109+
require.Len(t, files, 1)
110+
require.Equal(t, sketchPath.Parent().Join("SketchMultipleMainFiles.pde"), files[0])
111+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
void setup() {}
3+
void loop() {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
void setup() {}
3+
void loop() {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
void setup() {}
3+
void loop() {}

cli/compile/compile.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"encoding/json"
2222
"os"
2323

24+
"github.com/arduino/arduino-cli/arduino/sketches"
2425
"github.com/arduino/arduino-cli/cli/feedback"
2526
"github.com/arduino/arduino-cli/cli/output"
2627
"github.com/arduino/arduino-cli/configuration"
@@ -127,6 +128,15 @@ func run(cmd *cobra.Command, args []string) {
127128
}
128129

129130
sketchPath := initSketchPath(path)
131+
132+
// .pde files are still supported but deprecated, this warning urges the user to rename them
133+
if files := sketches.CheckForPdeFiles(sketchPath); len(files) > 0 {
134+
feedback.Error("Sketches with .pde extension are deprecated, please rename the following files to .ino:")
135+
for _, f := range files {
136+
feedback.Error(f)
137+
}
138+
}
139+
130140
// We must read this from settings since the value is set when the binding is accessed from viper,
131141
// accessing it from cobra would only read it if the flag is explicitly set by the user and ignore
132142
// the config file and the env vars.

docs/sketch-specification.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ Support for sketch folder names starting with a number was added in Arduino IDE
1818

1919
### Primary sketch file
2020

21-
Every sketch must contain a .ino or .pde file with a file name matching the sketch root folder name.
21+
Every sketch must contain a `.ino` file with a file name matching the sketch root folder name.
22+
23+
`.pde` is also supported but **deprecated** and will be removed in the future, using the `.ino` extension is strongly
24+
recommended.
2225

2326
### Additional code files
2427

@@ -28,7 +31,7 @@ The following extensions are supported:
2831

2932
- .ino - [Arduino language](https://www.arduino.cc/reference/en/) files.
3033
- .pde - Alternate extension for Arduino language files. This file extension is also used by Processing sketches. .ino
31-
is recommended to avoid confusion.
34+
is recommended to avoid confusion. **`.pde` extension is deprecated and will be removed in the future.**
3235
- .cpp - C++ files.
3336
- .c - C Files.
3437
- .S - Assembly language files.

test/test_compile.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import platform
1717
import tempfile
1818
import hashlib
19+
import shutil
1920
from pathlib import Path
2021
import simplejson as json
2122

@@ -629,3 +630,68 @@ def test_compile_with_fully_precompiled_library(run_command, data_dir):
629630
result = run_command(f"compile -b {fqbn} {sketch_folder} -v")
630631
assert result.ok
631632
assert "Skipping dependencies detection for precompiled library Arduino_TensorFlowLite" in result.stdout
633+
634+
635+
def test_compile_sketch_with_pde_extension(run_command, data_dir):
636+
# Init the environment explicitly
637+
assert run_command("update")
638+
639+
# Install core to compile
640+
assert run_command("core install arduino:[email protected]")
641+
642+
sketch_name = "CompilePdeSketch"
643+
sketch_path = Path(data_dir, sketch_name)
644+
fqbn = "arduino:avr:uno"
645+
646+
# Create a test sketch
647+
assert run_command(f"sketch new {sketch_path}")
648+
649+
# Renames sketch file to pde
650+
sketch_file = Path(sketch_path, f"{sketch_name}.ino").rename(sketch_path / f"{sketch_name}.pde")
651+
652+
# Build sketch from folder
653+
res = run_command(f"compile --clean -b {fqbn} {sketch_path}")
654+
assert res.ok
655+
assert "Sketches with .pde extension are deprecated, please rename the following files to .ino:" in res.stderr
656+
assert str(sketch_file) in res.stderr
657+
658+
# Build sketch from file
659+
res = run_command(f"compile --clean -b {fqbn} {sketch_file}")
660+
assert res.ok
661+
assert "Sketches with .pde extension are deprecated, please rename the following files to .ino" in res.stderr
662+
assert str(sketch_file) in res.stderr
663+
664+
665+
def test_compile_sketch_with_multiple_main_files(run_command, data_dir):
666+
# Init the environment explicitly
667+
assert run_command("update")
668+
669+
# Install core to compile
670+
assert run_command("core install arduino:[email protected]")
671+
672+
sketch_name = "CompileSketchMultipleMainFiles"
673+
sketch_path = Path(data_dir, sketch_name)
674+
fqbn = "arduino:avr:uno"
675+
676+
# Create a test sketch
677+
assert run_command(f"sketch new {sketch_path}")
678+
679+
# Copy .ino sketch file to .pde
680+
sketch_ino_file = Path(sketch_path, f"{sketch_name}.ino")
681+
sketch_pde_file = Path(sketch_path / f"{sketch_name}.pde")
682+
shutil.copyfile(sketch_ino_file, sketch_pde_file)
683+
684+
# Build sketch from folder
685+
res = run_command(f"compile --clean -b {fqbn} {sketch_path}")
686+
assert res.failed
687+
assert "Error during build: opening sketch: multiple main sketch files found" in res.stderr
688+
689+
# Build sketch from .ino file
690+
res = run_command(f"compile --clean -b {fqbn} {sketch_ino_file}")
691+
assert res.failed
692+
assert "Error during build: opening sketch: multiple main sketch files found" in res.stderr
693+
694+
# Build sketch from .pde file
695+
res = run_command(f"compile --clean -b {fqbn} {sketch_pde_file}")
696+
assert res.failed
697+
assert "Error during build: opening sketch: multiple main sketch files found" in res.stderr

0 commit comments

Comments
 (0)