diff --git a/internal/arduino/builder/sketch.go b/internal/arduino/builder/sketch.go index 76e77f4c8e8..f5196630a9b 100644 --- a/internal/arduino/builder/sketch.go +++ b/internal/arduino/builder/sketch.go @@ -37,6 +37,14 @@ var ( includesArduinoH = regexp.MustCompile(`(?m)^\s*#\s*include\s*[<\"]Arduino\.h[>\"]`) ) +// stripUTF8BOM removes a UTF-8 BOM if present at the start of the file. +func stripUTF8BOM(b []byte) []byte { + if len(b) >= 3 && b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF { + return b[3:] + } + return b +} + // prepareSketchBuildPath copies the sketch source files in the build path. // The .ino files are merged together to create a .cpp file (by the way, the // .cpp file still needs to be Arduino-preprocessed to compile). @@ -82,6 +90,7 @@ func (b *Builder) sketchMergeSources(overrides map[string]string) (int, string, if err != nil { return "", errors.New(i18n.Tr("reading file %[1]s: %[2]s", f, err)) } + data = stripUTF8BOM(data) return string(data), nil } @@ -136,7 +145,9 @@ func (b *Builder) sketchCopyAdditionalFiles(buildPath *paths.Path, overrides map if err != nil { return fmt.Errorf("%s: %w", i18n.Tr("unable to read contents of the source item"), err) } + s = stripUTF8BOM(s) sourceBytes = s + } // tag each additional file with the filename of the source it was copied from diff --git a/internal/arduino/builder/sketch_test.go b/internal/arduino/builder/sketch_test.go index 208efc468b0..d46ffd0eb3e 100644 --- a/internal/arduino/builder/sketch_test.go +++ b/internal/arduino/builder/sketch_test.go @@ -106,3 +106,22 @@ func TestCopyAdditionalFiles(t *testing.T) { require.NoError(t, err) require.Equal(t, info1.ModTime(), info2.ModTime()) } + +func TestStripUTF8BOM(t *testing.T) { + // Case 1: Input with BOM + inputWithBOM := []byte{0xEF, 0xBB, 0xBF, 'H', 'e', 'l', 'l', 'o'} + expected := []byte("Hello") + + output := stripUTF8BOM(inputWithBOM) + require.Equal(t, expected, output, "BOM should be stripped") + + // Case 2: Input without BOM + inputNoBOM := []byte("Hello") + outputNoBOM := stripUTF8BOM(inputNoBOM) + require.Equal(t, inputNoBOM, outputNoBOM, "Input without BOM should remain unchanged") + + // Case 3: Input shorter than 3 bytes (edge case) + shortInput := []byte{0xEF} + outputShort := stripUTF8BOM(shortInput) + require.Equal(t, shortInput, outputShort, "Short input should remain unchanged") +}