Skip to content

Fix loading of sketches with folder and main file mismatched casing #1179

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 2 commits into from
Feb 9, 2021
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
14 changes: 14 additions & 0 deletions arduino/builder/sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,17 @@ func TestCopyAdditionalFiles(t *testing.T) {
info2, err := os.Stat(s2.AdditionalFiles[0].Path)
require.Equal(t, info1.ModTime(), info2.ModTime())
}

func TestLoadSketchCaseMismatch(t *testing.T) {
// pass the path to the sketch folder
sketchPath := filepath.Join("testdata", t.Name())
mainFilePath := filepath.Join(sketchPath, t.Name()+".ino")
s, err := builder.SketchLoad(sketchPath, "")
require.Nil(t, s)
require.Error(t, err)

// pass the path to the main file
s, err = builder.SketchLoad(mainFilePath, "")
require.Nil(t, s)
require.Error(t, err)
}
34 changes: 34 additions & 0 deletions arduino/sketch/sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"

"github.com/arduino/arduino-cli/arduino/globals"
"github.com/arduino/go-paths-helper"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -116,10 +117,43 @@ func New(sketchFolderPath, mainFilePath, buildPath string, allFilesPaths []strin
sort.Sort(ItemByPath(additionalFiles))
sort.Sort(ItemByPath(otherSketchFiles))

if err := CheckSketchCasing(sketchFolderPath); err != nil {
return nil, err
}

return &Sketch{
MainFile: mainFile,
LocationPath: sketchFolderPath,
OtherSketchFiles: otherSketchFiles,
AdditionalFiles: additionalFiles,
}, nil
}

// CheckSketchCasing returns an error if the casing of the sketch folder and the main file are different.
// Correct:
// MySketch/MySketch.ino
// Wrong:
// MySketch/mysketch.ino
// mysketch/MySketch.ino
//
// This is mostly necessary to avoid errors on Mac OS X.
// For more info see: https://github.com/arduino/arduino-cli/issues/1174
func CheckSketchCasing(sketchFolder string) error {
sketchPath := paths.New(sketchFolder)
files, err := sketchPath.ReadDir()
if err != nil {
return errors.Errorf("reading files: %v", err)
}
files.FilterOutDirs()

sketchName := sketchPath.Base()
files.FilterPrefix(sketchName)

if files.Len() == 0 {
sketchFolderPath := paths.New(sketchFolder)
sketchFile := sketchFolderPath.Join(sketchFolderPath.Base() + globals.MainFileValidExtension)
return errors.Errorf("no valid sketch found in %s: missing %s", sketchFolderPath, sketchFile)
}

return nil
}
43 changes: 40 additions & 3 deletions arduino/sketch/sketch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
package sketch_test

import (
"fmt"
"path/filepath"
"sort"
"testing"

"github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestNewItem(t *testing.T) {
Expand All @@ -43,9 +46,9 @@ func TestNewItem(t *testing.T) {

func TestSort(t *testing.T) {
items := []*sketch.Item{
&sketch.Item{"foo"},
&sketch.Item{"baz"},
&sketch.Item{"bar"},
{"foo"},
{"baz"},
{"bar"},
}

sort.Sort(sketch.ItemByPath(items))
Expand All @@ -71,3 +74,37 @@ func TestNew(t *testing.T) {
assert.Len(t, sketch.OtherSketchFiles, 0)
assert.Len(t, sketch.AdditionalFiles, 1)
}

func TestNewSketchCasingWrong(t *testing.T) {
sketchPath := paths.New("testdata", "SketchCasingWrong")
mainFilePath := paths.New("testadata", "sketchcasingwrong.ino").String()
sketch, err := sketch.New(sketchPath.String(), mainFilePath, "", []string{mainFilePath})
assert.Nil(t, sketch)
expectedError := fmt.Sprintf("no valid sketch found in %s: missing %s", sketchPath.String(), sketchPath.Join(sketchPath.Base()+".ino"))
assert.EqualError(t, err, expectedError)
}

func TestNewSketchCasingCorrect(t *testing.T) {
sketchPath := paths.New("testdata", "SketchCasingCorrect").String()
mainFilePath := paths.New("testadata", "SketchCasingCorrect.ino").String()
sketch, err := sketch.New(sketchPath, mainFilePath, "", []string{mainFilePath})
assert.NotNil(t, sketch)
assert.NoError(t, err)
assert.Equal(t, sketchPath, sketch.LocationPath)
assert.Equal(t, mainFilePath, sketch.MainFile.Path)
assert.Len(t, sketch.OtherSketchFiles, 0)
assert.Len(t, sketch.AdditionalFiles, 0)
}

func TestCheckSketchCasingWrong(t *testing.T) {
sketchFolder := paths.New("testdata", "SketchCasingWrong")
err := sketch.CheckSketchCasing(sketchFolder.String())
expectedError := fmt.Sprintf("no valid sketch found in %s: missing %s", sketchFolder, sketchFolder.Join(sketchFolder.Base()+".ino"))
assert.EqualError(t, err, expectedError)
}

func TestCheckSketchCasingCorrect(t *testing.T) {
sketchFolder := paths.New("testdata", "SketchCasingCorrect").String()
err := sketch.CheckSketchCasing(sketchFolder)
require.NoError(t, err)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
void setup() {

}

void loop() {

}
9 changes: 5 additions & 4 deletions arduino/sketches/sketches.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/globals"
"github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/go-paths-helper"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -70,19 +71,19 @@ func NewSketchFromPath(path *paths.Path) (*Sketch, error) {
}
}

if mainSketchFile == nil {
if mainSketchFile == nil || sketch.CheckSketchCasing(path.String()) != nil {
sketchFile := path.Join(path.Base() + globals.MainFileValidExtension)
return nil, errors.Errorf("no valid sketch found in %s: missing %s", path, sketchFile)
}

sketch := &Sketch{
s := &Sketch{
FullPath: path,
MainFileExtension: mainSketchFile.Ext(),
Name: path.Base(),
Metadata: &Metadata{},
}
sketch.ImportMetadata()
return sketch, nil
s.ImportMetadata()
return s, nil
}

// ImportMetadata imports metadata into the sketch from a sketch.json file in the root
Expand Down
21 changes: 21 additions & 0 deletions arduino/sketches/sketches_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,24 @@ func TestCheckForPdeFiles(t *testing.T) {
require.Len(t, files, 1)
require.Equal(t, sketchPath.Parent().Join("SketchMultipleMainFiles.pde"), files[0])
}

func TestSketchLoadWithCasing(t *testing.T) {
sketchFolder := paths.New("testdata", "SketchCasingWrong")

sketch, err := NewSketchFromPath(sketchFolder)
require.Nil(t, sketch)

sketchFolderAbs, _ := sketchFolder.Abs()
sketchMainFileAbs := sketchFolderAbs.Join("SketchCasingWrong.ino")
expectedError := fmt.Sprintf("no valid sketch found in %s: missing %s", sketchFolderAbs, sketchMainFileAbs)
require.EqualError(t, err, expectedError)
}

func TestSketchLoadingCorrectCasing(t *testing.T) {
sketchFolder := paths.New("testdata", "SketchCasingCorrect")
sketch, err := NewSketchFromPath(sketchFolder)
require.NotNil(t, sketch)
require.NoError(t, err)
require.Equal(t, sketch.Name, "SketchCasingCorrect")
require.True(t, sketch.FullPath.EquivalentTo(sketchFolder))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
void setup() {

}

void loop() {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
void setup() {

}

void loop() {

}
16 changes: 8 additions & 8 deletions legacy/builder/test/builder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func prepareBuilderTestContext(t *testing.T, sketchPath *paths.Path, fqbn string
func TestBuilderEmptySketch(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch.ino"), "arduino:avr:uno")
ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")
ctx.DebugLevel = 10

buildPath := SetupBuildPath(t, ctx)
Expand All @@ -63,13 +63,13 @@ func TestBuilderEmptySketch(t *testing.T) {
exist, err = buildPath.Join(constants.FOLDER_PREPROC, constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E).ExistCheck()
NoError(t, err)
require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch.ino.cpp.o").ExistCheck()
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch1.ino.cpp.o").ExistCheck()
NoError(t, err)
require.True(t, exist)
exist, err = buildPath.Join("sketch.ino.elf").ExistCheck()
exist, err = buildPath.Join("sketch1.ino.elf").ExistCheck()
NoError(t, err)
require.True(t, exist)
exist, err = buildPath.Join("sketch.ino.hex").ExistCheck()
exist, err = buildPath.Join("sketch1.ino.hex").ExistCheck()
NoError(t, err)
require.True(t, exist)
}
Expand Down Expand Up @@ -277,7 +277,7 @@ func TestBuilderSketchNoFunctions(t *testing.T) {
func TestBuilderSketchWithBackup(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

ctx := prepareBuilderTestContext(t, paths.New("sketch_with_backup_files", "sketch.ino"), "arduino:avr:uno")
ctx := prepareBuilderTestContext(t, paths.New("sketch_with_backup_files", "sketch_with_backup_files.ino"), "arduino:avr:uno")
ctx.HardwareDirs = append(ctx.HardwareDirs, paths.New("downloaded_board_manager_stuff"))
ctx.BuiltInToolsDirs = append(ctx.BuiltInToolsDirs, paths.New("downloaded_board_manager_stuff"))

Expand All @@ -293,7 +293,7 @@ func TestBuilderSketchWithBackup(t *testing.T) {
func TestBuilderSketchWithOldLib(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

ctx := prepareBuilderTestContext(t, paths.New("sketch_with_old_lib", "sketch.ino"), "arduino:avr:uno")
ctx := prepareBuilderTestContext(t, paths.New("sketch_with_old_lib", "sketch_with_old_lib.ino"), "arduino:avr:uno")

buildPath := SetupBuildPath(t, ctx)
defer buildPath.RemoveAll()
Expand Down Expand Up @@ -344,7 +344,7 @@ func TestBuilderSketchBuildPathContainsUnusedPreviouslyCompiledLibrary(t *testin
func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch.ino"), "arduino:avr:uno")
ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")

var err error
ctx.BuildPath, err = paths.New("sketch1", "build").Abs()
Expand All @@ -365,7 +365,7 @@ func TestBuilderWithBuildPathInSketchDir(t *testing.T) {
func TestBuilderCacheCoreAFile(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch.ino"), "arduino:avr:uno")
ctx := prepareBuilderTestContext(t, paths.New("sketch1", "sketch1.ino"), "arduino:avr:uno")

SetupBuildPath(t, ctx)
defer ctx.BuildPath.RemoveAll()
Expand Down
8 changes: 4 additions & 4 deletions legacy/builder/test/ctags_runner_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func TestCTagsRunner(t *testing.T) {
func TestCTagsRunnerSketchWithClass(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

sketchLocation := Abs(t, paths.New("sketch_with_class", "sketch.ino"))
sketchLocation := Abs(t, paths.New("sketch_with_class", "sketch_with_class.ino"))

ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
Expand Down Expand Up @@ -127,7 +127,7 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) {
func TestCTagsRunnerSketchWithTypename(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

sketchLocation := Abs(t, paths.New("sketch_with_typename", "sketch.ino"))
sketchLocation := Abs(t, paths.New("sketch_with_typename", "sketch_with_typename.ino"))

ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
Expand Down Expand Up @@ -174,7 +174,7 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) {
func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

sketchLocation := Abs(t, paths.New("sketch_with_namespace", "sketch.ino"))
sketchLocation := Abs(t, paths.New("sketch_with_namespace", "sketch_with_namespace.ino"))

ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
Expand Down Expand Up @@ -220,7 +220,7 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
DownloadCoresAndToolsAndLibraries(t)

sketchLocation := Abs(t, paths.New("sketch_with_templates_and_shift", "template_and_shift.cpp"))
sketchLocation := Abs(t, paths.New("sketch_with_templates_and_shift", "sketch_with_templates_and_shift.cpp"))

ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
Expand Down
6 changes: 3 additions & 3 deletions legacy/builder/test/includes_to_include_folders_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) {
BuiltInToolsDirs: paths.NewPathList("downloaded_tools"),
BuiltInLibrariesDirs: paths.NewPathList("downloaded_libraries"),
OtherLibrariesDirs: paths.NewPathList("libraries"),
SketchLocation: paths.New("sketch2", "SketchWithIfDef.ino"),
SketchLocation: paths.New("SketchWithIfDef", "SketchWithIfDef.ino"),
FQBN: parseFQBN(t, "arduino:avr:leonardo"),
ArduinoAPIVersion: "10600",
Verbose: true,
Expand Down Expand Up @@ -105,7 +105,7 @@ func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) {
BuiltInToolsDirs: paths.NewPathList("downloaded_tools"),
BuiltInLibrariesDirs: paths.NewPathList("downloaded_libraries"),
OtherLibrariesDirs: paths.NewPathList("libraries"),
SketchLocation: paths.New("sketch9", "sketch.ino"),
SketchLocation: paths.New("sketch9", "sketch9.ino"),
FQBN: parseFQBN(t, "arduino:avr:leonardo"),
ArduinoAPIVersion: "10600",
Verbose: true,
Expand Down Expand Up @@ -143,7 +143,7 @@ func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) {
BuiltInToolsDirs: paths.NewPathList("downloaded_tools"),
BuiltInLibrariesDirs: paths.NewPathList("downloaded_libraries"),
OtherLibrariesDirs: paths.NewPathList("libraries"),
SketchLocation: paths.New("sketch10", "sketch.ino"),
SketchLocation: paths.New("sketch10", "sketch10.ino"),
FQBN: parseFQBN(t, "arduino:avr:leonardo"),
ArduinoAPIVersion: "10600",
Verbose: true,
Expand Down
4 changes: 2 additions & 2 deletions legacy/builder/test/load_vid_pid_specific_properties_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func TestLoadVIDPIDSpecificPropertiesWhenNoVIDPIDAreProvided(t *testing.T) {
ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "./tools_builtin"),
SketchLocation: paths.New("sketch1", "sketch.ino"),
SketchLocation: paths.New("sketch1", "sketch1.ino"),
FQBN: parseFQBN(t, "arduino:avr:micro"),
ArduinoAPIVersion: "10600",
}
Expand Down Expand Up @@ -61,7 +61,7 @@ func TestLoadVIDPIDSpecificProperties(t *testing.T) {
ctx := &types.Context{
HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"),
BuiltInToolsDirs: paths.NewPathList("downloaded_tools", "./tools_builtin"),
SketchLocation: paths.New("sketch1", "sketch.ino"),
SketchLocation: paths.New("sketch1", "sketch1.ino"),
FQBN: parseFQBN(t, "arduino:avr:micro"),
ArduinoAPIVersion: "10600",
}
Expand Down
Loading