Unverified Commit f328ecd1 authored by Cristian Maglie's avatar Cristian Maglie Committed by GitHub

[skip-changelog] legacy: Arduino preprocess subroutine refactorization (part 1) (#2186)

* legacy: Removed ReadFileAndStoreInContext command

* ctags parser.Parse signature change

* removing ctags-related data from builder ctx (part 1)

* removing ctags-related data from builder ctx (part 2)

* removing ctags-related data from builder ctx (part 3)

* Clearly separate Source code processing phases

* Removed Command wrapper from ContainerMergeCopySketchFiles

* Moved builder.CopySketchFilesToBuildPath into proper location

* Converted FilterSketchSource into a function

* Moved a couple of functions in the proper builder package

* Fixed lint error
parent 493fa838
// This file is part of arduino-cli.
//
// Copyright 2023 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package builder
import (
"strings"
"unicode/utf8"
)
// QuoteCppString returns the given string as a quoted string for use with the C
// preprocessor. This adds double quotes around it and escapes any
// double quotes and backslashes in the string.
func QuoteCppString(str string) string {
str = strings.Replace(str, "\\", "\\\\", -1)
str = strings.Replace(str, "\"", "\\\"", -1)
return "\"" + str + "\""
}
// ParseCppString parse a C-preprocessor string as emitted by the preprocessor. This
// is a string contained in double quotes, with any backslashes or
// quotes escaped with a backslash. If a valid string was present at the
// start of the given line, returns the unquoted string contents, the
// remainder of the line (everything after the closing "), and true.
// Otherwise, returns the empty string, the entire line and false.
func ParseCppString(line string) (string, string, bool) {
// For details about how these strings are output by gcc, see:
// https://github.com/gcc-mirror/gcc/blob/a588355ab948cf551bc9d2b89f18e5ae5140f52c/libcpp/macro.c#L491-L511
// Note that the documentation suggests all non-printable
// characters are also escaped, but the implementation does not
// actually do this. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51259
if len(line) < 1 || line[0] != '"' {
return "", line, false
}
i := 1
res := ""
for {
if i >= len(line) {
return "", line, false
}
c, width := utf8.DecodeRuneInString(line[i:])
switch c {
case '\\':
// Backslash, next character is used unmodified
i += width
if i >= len(line) {
return "", line, false
}
res += string(line[i])
case '"':
// Quote, end of string
return res, line[i+width:], true
default:
res += string(c)
}
i += width
}
}
package builder_test
import (
"testing"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/stretchr/testify/require"
)
func TestParseCppString(t *testing.T) {
_, _, ok := builder.ParseCppString(`foo`)
require.Equal(t, false, ok)
_, _, ok = builder.ParseCppString(`"foo`)
require.Equal(t, false, ok)
str, rest, ok := builder.ParseCppString(`"foo"`)
require.Equal(t, true, ok)
require.Equal(t, `foo`, str)
require.Equal(t, ``, rest)
str, rest, ok = builder.ParseCppString(`"foo\\bar"`)
require.Equal(t, true, ok)
require.Equal(t, `foo\bar`, str)
require.Equal(t, ``, rest)
str, rest, ok = builder.ParseCppString(`"foo \"is\" quoted and \\\\bar\"\" escaped\\" and "then" some`)
require.Equal(t, true, ok)
require.Equal(t, `foo "is" quoted and \\bar"" escaped\`, str)
require.Equal(t, ` and "then" some`, rest)
str, rest, ok = builder.ParseCppString(`" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`)
require.Equal(t, true, ok)
require.Equal(t, ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`, str)
require.Equal(t, ``, rest)
str, rest, ok = builder.ParseCppString(`"/home/ççç/"`)
require.Equal(t, true, ok)
require.Equal(t, `/home/ççç/`, str)
require.Equal(t, ``, rest)
str, rest, ok = builder.ParseCppString(`"/home/ççç/ /$sdsdd\\"`)
require.Equal(t, true, ok)
require.Equal(t, `/home/ççç/ /$sdsdd\`, str)
require.Equal(t, ``, rest)
}
...@@ -19,7 +19,6 @@ import ( ...@@ -19,7 +19,6 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"regexp" "regexp"
"strings"
"github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/i18n"
...@@ -34,13 +33,20 @@ var ( ...@@ -34,13 +33,20 @@ var (
tr = i18n.Tr tr = i18n.Tr
) )
// QuoteCppString returns the given string as a quoted string for use with the C // PrepareSketchBuildPath copies the sketch source files in the build path.
// preprocessor. This adds double quotes around it and escapes any // The .ino files are merged together to create a .cpp file (by the way, the
// double quotes and backslashes in the string. // .cpp file still needs to be Arduino-preprocessed to compile).
func QuoteCppString(str string) string { func PrepareSketchBuildPath(sketch *sketch.Sketch, sourceOverrides map[string]string, buildPath *paths.Path) (offset int, mergedSource string, err error) {
str = strings.Replace(str, "\\", "\\\\", -1) if offset, mergedSource, err = sketchMergeSources(sketch, sourceOverrides); err != nil {
str = strings.Replace(str, "\"", "\\\"", -1) return
return "\"" + str + "\"" }
if err = SketchSaveItemCpp(sketch.MainFile, []byte(mergedSource), buildPath); err != nil {
return
}
if err = sketchCopyAdditionalFiles(sketch, buildPath, sourceOverrides); err != nil {
return
}
return
} }
// SketchSaveItemCpp saves a preprocessed .cpp sketch file on disk // SketchSaveItemCpp saves a preprocessed .cpp sketch file on disk
...@@ -59,8 +65,9 @@ func SketchSaveItemCpp(path *paths.Path, contents []byte, destPath *paths.Path) ...@@ -59,8 +65,9 @@ func SketchSaveItemCpp(path *paths.Path, contents []byte, destPath *paths.Path)
return nil return nil
} }
// SketchMergeSources merges all the source files included in a sketch // sketchMergeSources merges all the .ino source files included in a sketch to produce
func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, string, error) { // a single .cpp file.
func sketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, string, error) {
lineOffset := 0 lineOffset := 0
mergedSource := "" mergedSource := ""
...@@ -105,9 +112,9 @@ func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, st ...@@ -105,9 +112,9 @@ func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, st
return lineOffset, mergedSource, nil return lineOffset, mergedSource, nil
} }
// SketchCopyAdditionalFiles copies the additional files for a sketch to the // sketchCopyAdditionalFiles copies the additional files for a sketch to the
// specified destination directory. // specified destination directory.
func SketchCopyAdditionalFiles(sketch *sketch.Sketch, destPath *paths.Path, overrides map[string]string) error { func sketchCopyAdditionalFiles(sketch *sketch.Sketch, destPath *paths.Path, overrides map[string]string) error {
if err := destPath.MkdirAll(); err != nil { if err := destPath.MkdirAll(); err != nil {
return errors.Wrap(err, tr("unable to create a folder to save the sketch files")) return errors.Wrap(err, tr("unable to create a folder to save the sketch files"))
} }
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
// Arduino software without disclosing the source code of your own applications. // Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc. // To purchase a commercial license, send an email to license@arduino.cc.
package builder_test package builder
import ( import (
"fmt" "fmt"
...@@ -23,7 +23,6 @@ import ( ...@@ -23,7 +23,6 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/go-paths-helper" "github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -48,7 +47,7 @@ func TestSaveSketch(t *testing.T) { ...@@ -48,7 +47,7 @@ func TestSaveSketch(t *testing.T) {
t.Fatalf("unable to read golden file %s: %v", sketchFile, err) t.Fatalf("unable to read golden file %s: %v", sketchFile, err)
} }
builder.SketchSaveItemCpp(paths.New(sketchName), source, tmp) SketchSaveItemCpp(paths.New(sketchName), source, tmp)
out, err := tmp.Join(outName).ReadFile() out, err := tmp.Join(outName).ReadFile()
if err != nil { if err != nil {
...@@ -82,7 +81,7 @@ func TestMergeSketchSources(t *testing.T) { ...@@ -82,7 +81,7 @@ func TestMergeSketchSources(t *testing.T) {
} }
mergedSources := strings.ReplaceAll(string(mergedBytes), "%s", pathToGoldenSource) mergedSources := strings.ReplaceAll(string(mergedBytes), "%s", pathToGoldenSource)
offset, source, err := builder.SketchMergeSources(s, nil) offset, source, err := sketchMergeSources(s, nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 2, offset) require.Equal(t, 2, offset)
require.Equal(t, mergedSources, source) require.Equal(t, mergedSources, source)
...@@ -94,7 +93,7 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) { ...@@ -94,7 +93,7 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) {
require.NotNil(t, s) require.NotNil(t, s)
// ensure not to include Arduino.h when it's already there // ensure not to include Arduino.h when it's already there
_, source, err := builder.SketchMergeSources(s, nil) _, source, err := sketchMergeSources(s, nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 1, strings.Count(source, "<Arduino.h>")) require.Equal(t, 1, strings.Count(source, "<Arduino.h>"))
} }
...@@ -110,7 +109,7 @@ func TestCopyAdditionalFiles(t *testing.T) { ...@@ -110,7 +109,7 @@ func TestCopyAdditionalFiles(t *testing.T) {
// copy the sketch over, create a fake main file we don't care about it // copy the sketch over, create a fake main file we don't care about it
// but we need it for `SketchLoad` to succeed later // but we need it for `SketchLoad` to succeed later
err = builder.SketchCopyAdditionalFiles(s1, tmp, nil) err = sketchCopyAdditionalFiles(s1, tmp, nil)
require.Nil(t, err) require.Nil(t, err)
fakeIno := tmp.Join(fmt.Sprintf("%s.ino", tmp.Base())) fakeIno := tmp.Join(fmt.Sprintf("%s.ino", tmp.Base()))
require.Nil(t, fakeIno.WriteFile([]byte{})) require.Nil(t, fakeIno.WriteFile([]byte{}))
...@@ -125,7 +124,7 @@ func TestCopyAdditionalFiles(t *testing.T) { ...@@ -125,7 +124,7 @@ func TestCopyAdditionalFiles(t *testing.T) {
require.Nil(t, err) require.Nil(t, err)
// copy again // copy again
err = builder.SketchCopyAdditionalFiles(s1, tmp, nil) err = sketchCopyAdditionalFiles(s1, tmp, nil)
require.Nil(t, err) require.Nil(t, err)
// verify file hasn't changed // verify file hasn't changed
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"reflect" "reflect"
"time" "time"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/i18n"
"github.com/arduino/arduino-cli/legacy/builder/phases" "github.com/arduino/arduino-cli/legacy/builder/phases"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
...@@ -39,6 +40,7 @@ func (s *Builder) Run(ctx *types.Context) error { ...@@ -39,6 +40,7 @@ func (s *Builder) Run(ctx *types.Context) error {
return err return err
} }
var _err error
commands := []types.Command{ commands := []types.Command{
&ContainerSetupHardwareToolsLibsSketchAndProps{}, &ContainerSetupHardwareToolsLibsSketchAndProps{},
...@@ -46,7 +48,10 @@ func (s *Builder) Run(ctx *types.Context) error { ...@@ -46,7 +48,10 @@ func (s *Builder) Run(ctx *types.Context) error {
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"},
&ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = builder.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
utils.LogIfVerbose(false, tr("Detecting libraries used...")), utils.LogIfVerbose(false, tr("Detecting libraries used...")),
&ContainerFindIncludes{}, &ContainerFindIncludes{},
...@@ -127,6 +132,7 @@ func (s *Preprocess) Run(ctx *types.Context) error { ...@@ -127,6 +132,7 @@ func (s *Preprocess) Run(ctx *types.Context) error {
return err return err
} }
var _err error
commands := []types.Command{ commands := []types.Command{
&ContainerSetupHardwareToolsLibsSketchAndProps{}, &ContainerSetupHardwareToolsLibsSketchAndProps{},
...@@ -134,7 +140,10 @@ func (s *Preprocess) Run(ctx *types.Context) error { ...@@ -134,7 +140,10 @@ func (s *Preprocess) Run(ctx *types.Context) error {
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"},
&ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = builder.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&ContainerFindIncludes{}, &ContainerFindIncludes{},
...@@ -148,7 +157,7 @@ func (s *Preprocess) Run(ctx *types.Context) error { ...@@ -148,7 +157,7 @@ func (s *Preprocess) Run(ctx *types.Context) error {
} }
// Output arduino-preprocessed source // Output arduino-preprocessed source
ctx.WriteStdout([]byte(ctx.Source)) ctx.WriteStdout([]byte(ctx.SketchSourceAfterArduinoPreprocessing))
return nil return nil
} }
......
...@@ -16,10 +16,17 @@ ...@@ -16,10 +16,17 @@
package builder package builder
import ( import (
"bufio"
"bytes"
"fmt" "fmt"
"io"
"strconv"
"strings"
bldr "github.com/arduino/arduino-cli/arduino/builder" bldr "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/go-paths-helper"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
...@@ -28,7 +35,7 @@ func PreprocessSketchWithCtags(ctx *types.Context) error { ...@@ -28,7 +35,7 @@ func PreprocessSketchWithCtags(ctx *types.Context) error {
if err := ctx.PreprocPath.MkdirAll(); err != nil { if err := ctx.PreprocPath.MkdirAll(); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
targetFilePath := ctx.PreprocPath.Join("ctags_target_for_gcc_minus_e.cpp") targetFilePath := ctx.PreprocPath.Join("sketch_merged.cpp")
// Run preprocessor // Run preprocessor
sourceFile := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp") sourceFile := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp")
...@@ -47,11 +54,14 @@ func PreprocessSketchWithCtags(ctx *types.Context) error { ...@@ -47,11 +54,14 @@ func PreprocessSketchWithCtags(ctx *types.Context) error {
} }
} }
if src, err := targetFilePath.ReadFile(); err != nil {
return err
} else {
ctx.SketchSourceAfterCppPreprocessing = filterSketchSource(ctx.Sketch, bytes.NewReader(src), false)
}
commands := []types.Command{ commands := []types.Command{
&ReadFileAndStoreInContext{FileToRead: targetFilePath, Target: &ctx.SourceGccMinusE}, &CTagsRunner{Source: &ctx.SketchSourceAfterCppPreprocessing, TargetFileName: "sketch_merged.cpp"},
&FilterSketchSource{Source: &ctx.SourceGccMinusE},
&CTagsTargetFileSaver{Source: &ctx.SourceGccMinusE, TargetFileName: "ctags_target_for_gcc_minus_e.cpp"},
&CTagsRunner{},
&PrototypesAdder{}, &PrototypesAdder{},
} }
...@@ -63,9 +73,66 @@ func PreprocessSketchWithCtags(ctx *types.Context) error { ...@@ -63,9 +73,66 @@ func PreprocessSketchWithCtags(ctx *types.Context) error {
} }
} }
if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.Source), ctx.SketchBuildPath); err != nil { if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.SketchSourceAfterArduinoPreprocessing), ctx.SketchBuildPath); err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
return nil return nil
} }
func filterSketchSource(sketch *sketch.Sketch, source io.Reader, removeLineMarkers bool) string {
fileNames := paths.NewPathList()
fileNames.Add(sketch.MainFile)
fileNames.AddAll(sketch.OtherSketchFiles)
inSketch := false
filtered := ""
scanner := bufio.NewScanner(source)
for scanner.Scan() {
line := scanner.Text()
if filename := parseLineMarker(line); filename != nil {
inSketch = fileNames.Contains(filename)
if inSketch && removeLineMarkers {
continue
}
}
if inSketch {
filtered += line + "\n"
}
}
return filtered
}
// Parses the given line as a gcc line marker and returns the contained
// filename.
func parseLineMarker(line string) *paths.Path {
// A line marker contains the line number and filename and looks like:
// # 123 /path/to/file.cpp
// It can be followed by zero or more flag number that indicate the
// preprocessor state and can be ignored.
// For exact details on this format, see:
// https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/gcc/c-family/c-ppoutput.c#L413-L415
split := strings.SplitN(line, " ", 3)
if len(split) < 3 || len(split[0]) == 0 || split[0][0] != '#' {
return nil
}
_, err := strconv.Atoi(split[1])
if err != nil {
return nil
}
// If we get here, we found a # followed by a line number, so
// assume this is a line marker and see if the rest of the line
// starts with a string containing the filename
str, rest, ok := bldr.ParseCppString(split[2])
if ok && (rest == "" || rest[0] == ' ') {
return paths.New(str)
}
return nil
}
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package builder
import (
bldr "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/pkg/errors"
)
type ContainerMergeCopySketchFiles struct{}
func (s *ContainerMergeCopySketchFiles) Run(ctx *types.Context) error {
offset, source, err := bldr.SketchMergeSources(ctx.Sketch, ctx.SourceOverride)
if err != nil {
return err
}
ctx.LineOffset = offset
ctx.Source = source
if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.Source), ctx.SketchBuildPath); err != nil {
return errors.WithStack(err)
}
if err := bldr.SketchCopyAdditionalFiles(ctx.Sketch, ctx.SketchBuildPath, ctx.SourceOverride); err != nil {
return errors.WithStack(err)
}
return nil
}
...@@ -120,12 +120,7 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error { ...@@ -120,12 +120,7 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error {
} }
// Use old ctags method to generate export file // Use old ctags method to generate export file
commands := []types.Command{ ctx.SketchSourceMerged = filterSketchSource(ctx.Sketch, strings.NewReader(ctx.SketchSourceMerged), true)
&FilterSketchSource{Source: &ctx.Source, RemoveLineMarkers: true},
}
for _, command := range commands {
command.Run(ctx)
}
err = utils.CopyDir(ctx.SketchBuildPath.String(), cmakeFolder.Join("sketch").String(), validExportExtensions) err = utils.CopyDir(ctx.SketchBuildPath.String(), cmakeFolder.Join("sketch").String(), validExportExtensions)
if err != nil { if err != nil {
......
...@@ -23,14 +23,12 @@ import ( ...@@ -23,14 +23,12 @@ import (
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
) )
func (p *CTagsParser) FixCLinkageTagsDeclarations(tags []*types.CTag) { func (p *CTagsParser) FixCLinkageTagsDeclarations() {
linesMap := p.FindCLinkageLines(p.tags)
linesMap := p.FindCLinkageLines(tags) for i := range p.tags {
for i := range tags { if sliceContainsInt(linesMap[p.tags[i].Filename], p.tags[i].Line) &&
!strings.Contains(p.tags[i].PrototypeModifiers, EXTERN) {
if sliceContainsInt(linesMap[tags[i].Filename], tags[i].Line) && p.tags[i].PrototypeModifiers = p.tags[i].PrototypeModifiers + " " + EXTERN
!strings.Contains(tags[i].PrototypeModifiers, EXTERN) {
tags[i].PrototypeModifiers = tags[i].PrototypeModifiers + " " + EXTERN
} }
} }
} }
......
...@@ -43,8 +43,8 @@ type CTagsParser struct { ...@@ -43,8 +43,8 @@ type CTagsParser struct {
mainFile *paths.Path mainFile *paths.Path
} }
func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) []*types.CTag { func (p *CTagsParser) Parse(ctagsOutput []byte, mainFile *paths.Path) []*types.CTag {
rows := strings.Split(ctagsOutput, "\n") rows := strings.Split(string(ctagsOutput), "\n")
rows = removeEmpty(rows) rows = removeEmpty(rows)
p.mainFile = mainFile p.mainFile = mainFile
......
...@@ -30,7 +30,7 @@ func produceTags(t *testing.T, filename string) []*types.CTag { ...@@ -30,7 +30,7 @@ func produceTags(t *testing.T, filename string) []*types.CTag {
require.NoError(t, err) require.NoError(t, err)
parser := CTagsParser{} parser := CTagsParser{}
return parser.Parse(string(bytes), nil) return parser.Parse(bytes, nil)
} }
func TestCTagsParserShouldListPrototypes(t *testing.T) { func TestCTagsParserShouldListPrototypes(t *testing.T) {
......
...@@ -30,7 +30,7 @@ func producePrototypes(t *testing.T, filename string, mainFile string) ([]*types ...@@ -30,7 +30,7 @@ func producePrototypes(t *testing.T, filename string, mainFile string) ([]*types
require.NoError(t, err) require.NoError(t, err)
parser := &CTagsParser{} parser := &CTagsParser{}
parser.Parse(string(bytes), paths.New(mainFile)) parser.Parse(bytes, paths.New(mainFile))
return parser.GeneratePrototypes() return parser.GeneratePrototypes()
} }
......
...@@ -26,10 +26,26 @@ import ( ...@@ -26,10 +26,26 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type CTagsRunner struct{} type CTagsRunner struct {
Source *string
TargetFileName string
func (s *CTagsRunner) Run(ctx *types.Context) error { // Needed for unit-testing
ctagsTargetFilePath := ctx.CTagsTargetFile CtagsOutput []byte
}
func (r *CTagsRunner) Run(ctx *types.Context) error {
source := *r.Source
preprocPath := ctx.PreprocPath
if err := preprocPath.MkdirAll(); err != nil {
return errors.WithStack(err)
}
ctagsTargetFilePath := preprocPath.Join(r.TargetFileName)
if err := ctagsTargetFilePath.WriteFile([]byte(source)); err != nil {
return errors.WithStack(err)
}
buildProperties := properties.NewMap() buildProperties := properties.NewMap()
buildProperties.Set("tools.ctags.path", "{runtime.tools.ctags.path}") buildProperties.Set("tools.ctags.path", "{runtime.tools.ctags.path}")
...@@ -52,17 +68,14 @@ func (s *CTagsRunner) Run(ctx *types.Context) error { ...@@ -52,17 +68,14 @@ func (s *CTagsRunner) Run(ctx *types.Context) error {
command := exec.Command(parts[0], parts[1:]...) command := exec.Command(parts[0], parts[1:]...)
command.Env = append(os.Environ(), ctx.PackageManager.GetEnvVarsForSpawnedProcess()...) command.Env = append(os.Environ(), ctx.PackageManager.GetEnvVarsForSpawnedProcess()...)
sourceBytes, _, err := utils.ExecCommand(ctx, command, utils.Capture /* stdout */, utils.ShowIfVerbose /* stderr */) ctagsOutput, _, err := utils.ExecCommand(ctx, command, utils.Capture /* stdout */, utils.ShowIfVerbose /* stderr */)
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
ctx.CTagsOutput = string(sourceBytes)
parser := &ctags.CTagsParser{} parser := &ctags.CTagsParser{}
parser.Parse(ctagsOutput, ctx.Sketch.MainFile)
ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile) parser.FixCLinkageTagsDeclarations()
parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource)
protos, line := parser.GeneratePrototypes() protos, line := parser.GeneratePrototypes()
if line != -1 { if line != -1 {
...@@ -70,5 +83,7 @@ func (s *CTagsRunner) Run(ctx *types.Context) error { ...@@ -70,5 +83,7 @@ func (s *CTagsRunner) Run(ctx *types.Context) error {
} }
ctx.Prototypes = protos ctx.Prototypes = protos
// Needed for unit-testing
r.CtagsOutput = ctagsOutput
return nil return nil
} }
...@@ -14,30 +14,3 @@ ...@@ -14,30 +14,3 @@
// To purchase a commercial license, send an email to license@arduino.cc. // To purchase a commercial license, send an email to license@arduino.cc.
package builder package builder
import (
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/pkg/errors"
)
type CTagsTargetFileSaver struct {
Source *string
TargetFileName string
}
func (s *CTagsTargetFileSaver) Run(ctx *types.Context) error {
source := *s.Source
preprocPath := ctx.PreprocPath
if err := preprocPath.MkdirAll(); err != nil {
return errors.WithStack(err)
}
ctagsTargetFilePath := preprocPath.Join(s.TargetFileName)
if err := ctagsTargetFilePath.WriteFile([]byte(source)); err != nil {
return errors.WithStack(err)
}
ctx.CTagsTargetFile = ctagsTargetFilePath
return nil
}
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package builder
import (
"bufio"
"strconv"
"strings"
"github.com/arduino/go-paths-helper"
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/arduino-cli/legacy/builder/utils"
)
type FilterSketchSource struct {
Source *string
RemoveLineMarkers bool
}
func (s *FilterSketchSource) Run(ctx *types.Context) error {
fileNames := paths.NewPathList()
fileNames.Add(ctx.Sketch.MainFile)
fileNames.AddAll(ctx.Sketch.OtherSketchFiles)
inSketch := false
filtered := ""
scanner := bufio.NewScanner(strings.NewReader(*s.Source))
for scanner.Scan() {
line := scanner.Text()
if filename := parseLineMarker(line); filename != nil {
inSketch = fileNames.Contains(filename)
if inSketch && s.RemoveLineMarkers {
continue
}
}
if inSketch {
filtered += line + "\n"
}
}
*s.Source = filtered
return nil
}
// Parses the given line as a gcc line marker and returns the contained
// filename.
func parseLineMarker(line string) *paths.Path {
// A line marker contains the line number and filename and looks like:
// # 123 /path/to/file.cpp
// It can be followed by zero or more flag number that indicate the
// preprocessor state and can be ignored.
// For exact details on this format, see:
// https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/gcc/c-family/c-ppoutput.c#L413-L415
split := strings.SplitN(line, " ", 3)
if len(split) < 3 || len(split[0]) == 0 || split[0][0] != '#' {
return nil
}
_, err := strconv.Atoi(split[1])
if err != nil {
return nil
}
// If we get here, we found a # followed by a line number, so
// assume this is a line marker and see if the rest of the line
// starts with a string containing the filename
str, rest, ok := utils.ParseCppString(split[2])
if ok && (rest == "" || rest[0] == ' ') {
return paths.New(str)
}
return nil
}
...@@ -35,9 +35,9 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { ...@@ -35,9 +35,9 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error {
} }
sourceFile := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp") sourceFile := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp")
GCCPreprocRunner(ctx, sourceFile, ctx.PreprocPath.Join("ctags_target_for_gcc_minus_e.cpp"), ctx.IncludeFolders) targetFile := ctx.PreprocPath.Join("sketch_merged.cpp")
GCCPreprocRunner(ctx, sourceFile, targetFile, ctx.IncludeFolders)
targetFilePath := ctx.PreprocPath.Join("ctags_target_for_gcc_minus_e.cpp")
buildProperties := properties.NewMap() buildProperties := properties.NewMap()
buildProperties.Set("tools.arduino-preprocessor.path", "{runtime.tools.arduino-preprocessor.path}") buildProperties.Set("tools.arduino-preprocessor.path", "{runtime.tools.arduino-preprocessor.path}")
buildProperties.Set("tools.arduino-preprocessor.cmd.path", "{path}/arduino-preprocessor") buildProperties.Set("tools.arduino-preprocessor.cmd.path", "{path}/arduino-preprocessor")
...@@ -45,7 +45,7 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { ...@@ -45,7 +45,7 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error {
buildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC") buildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC")
buildProperties.Merge(ctx.BuildProperties) buildProperties.Merge(ctx.BuildProperties)
buildProperties.Merge(buildProperties.SubTree("tools").SubTree("arduino-preprocessor")) buildProperties.Merge(buildProperties.SubTree("tools").SubTree("arduino-preprocessor"))
buildProperties.SetPath("source_file", targetFilePath) buildProperties.SetPath("source_file", targetFile)
pattern := buildProperties.Get("pattern") pattern := buildProperties.Get("pattern")
if pattern == "" { if pattern == "" {
...@@ -79,7 +79,6 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { ...@@ -79,7 +79,6 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error {
result := utils.NormalizeUTF8(buf) result := utils.NormalizeUTF8(buf)
//fmt.Printf("PREPROCESSOR OUTPUT:\n%s\n", output) //fmt.Printf("PREPROCESSOR OUTPUT:\n%s\n", output)
ctx.Source = string(result) ctx.SketchSourceAfterArduinoPreprocessing = string(result)
return bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.SketchSourceAfterArduinoPreprocessing), ctx.SketchBuildPath)
return bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.Source), ctx.SketchBuildPath)
} }
...@@ -17,19 +17,20 @@ package builder ...@@ -17,19 +17,20 @@ package builder
import ( import (
"fmt" "fmt"
"strconv"
"strings"
"github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/constants"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/arduino-cli/legacy/builder/utils" "github.com/arduino/arduino-cli/legacy/builder/utils"
"strconv"
"strings"
) )
type PrototypesAdder struct{} type PrototypesAdder struct{}
func (s *PrototypesAdder) Run(ctx *types.Context) error { func (s *PrototypesAdder) Run(ctx *types.Context) error {
debugOutput := ctx.DebugPreprocessor debugOutput := ctx.DebugPreprocessor
source := ctx.Source
source := ctx.SketchSourceMerged
source = strings.Replace(source, "\r\n", "\n", -1) source = strings.Replace(source, "\r\n", "\n", -1)
source = strings.Replace(source, "\r", "\n", -1) source = strings.Replace(source, "\r", "\n", -1)
...@@ -61,7 +62,7 @@ func (s *PrototypesAdder) Run(ctx *types.Context) error { ...@@ -61,7 +62,7 @@ func (s *PrototypesAdder) Run(ctx *types.Context) error {
} }
fmt.Println("#END OF PREPROCESSED SOURCE") fmt.Println("#END OF PREPROCESSED SOURCE")
} }
ctx.Source = source ctx.SketchSourceAfterArduinoPreprocessing = source
return nil return nil
} }
......
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package builder
import (
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/go-paths-helper"
"github.com/pkg/errors"
)
type ReadFileAndStoreInContext struct {
FileToRead *paths.Path
Target *string
}
func (s *ReadFileAndStoreInContext) Run(ctx *types.Context) error {
bytes, err := s.FileToRead.ReadFile()
if err != nil {
return errors.WithStack(err)
}
*s.Target = string(bytes)
return nil
}
...@@ -116,7 +116,7 @@ func TestBuilderEmptySketch(t *testing.T) { ...@@ -116,7 +116,7 @@ func TestBuilderEmptySketch(t *testing.T) {
exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch1.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch1.ino.cpp.o").ExistCheck()
...@@ -143,7 +143,7 @@ func TestBuilderBridge(t *testing.T) { ...@@ -143,7 +143,7 @@ func TestBuilderBridge(t *testing.T) {
exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck()
...@@ -173,7 +173,7 @@ func TestBuilderSketchWithConfig(t *testing.T) { ...@@ -173,7 +173,7 @@ func TestBuilderSketchWithConfig(t *testing.T) {
exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch_with_config.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch_with_config.ino.cpp.o").ExistCheck()
...@@ -208,7 +208,7 @@ func TestBuilderBridgeTwice(t *testing.T) { ...@@ -208,7 +208,7 @@ func TestBuilderBridgeTwice(t *testing.T) {
exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck()
...@@ -245,7 +245,7 @@ func TestBuilderBridgeSAM(t *testing.T) { ...@@ -245,7 +245,7 @@ func TestBuilderBridgeSAM(t *testing.T) {
exist, err = buildPath.Join(constants.FOLDER_CORE, "avr", "dtostrf.c.d").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_CORE, "avr", "dtostrf.c.d").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck()
...@@ -286,7 +286,7 @@ func TestBuilderBridgeRedBearLab(t *testing.T) { ...@@ -286,7 +286,7 @@ func TestBuilderBridgeRedBearLab(t *testing.T) {
exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_PREPROC, "sketch_merged.cpp").ExistCheck()
NoError(t, err) NoError(t, err)
require.True(t, exist) require.True(t, exist)
exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck()
......
...@@ -19,6 +19,7 @@ import ( ...@@ -19,6 +19,7 @@ import (
"strings" "strings"
"testing" "testing"
bldr "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
paths "github.com/arduino/go-paths-helper" paths "github.com/arduino/go-paths-helper"
...@@ -31,14 +32,18 @@ func TestCTagsRunner(t *testing.T) { ...@@ -31,14 +32,18 @@ func TestCTagsRunner(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
ctagsRunner := &builder.CTagsRunner{Source: &ctx.SketchSourceMerged, TargetFileName: "ctags_target.cpp"}
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
&builder.PrintUsedLibrariesIfVerbose{}, &builder.PrintUsedLibrariesIfVerbose{},
&builder.WarnAboutArchIncompatibleLibraries{}, &builder.WarnAboutArchIncompatibleLibraries{},
&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, ctagsRunner,
&builder.CTagsRunner{},
} }
for _, command := range commands { for _, command := range commands {
err := command.Run(ctx) err := command.Run(ctx)
...@@ -53,8 +58,7 @@ func TestCTagsRunner(t *testing.T) { ...@@ -53,8 +58,7 @@ func TestCTagsRunner(t *testing.T) {
"digitalCommand " + quotedSketchLocation + " /^void digitalCommand(BridgeClient client) {$/;\" kind:function line:82 signature:(BridgeClient client) returntype:void\n" + "digitalCommand " + quotedSketchLocation + " /^void digitalCommand(BridgeClient client) {$/;\" kind:function line:82 signature:(BridgeClient client) returntype:void\n" +
"analogCommand " + quotedSketchLocation + " /^void analogCommand(BridgeClient client) {$/;\" kind:function line:109 signature:(BridgeClient client) returntype:void\n" + "analogCommand " + quotedSketchLocation + " /^void analogCommand(BridgeClient client) {$/;\" kind:function line:109 signature:(BridgeClient client) returntype:void\n" +
"modeCommand " + quotedSketchLocation + " /^void modeCommand(BridgeClient client) {$/;\" kind:function line:149 signature:(BridgeClient client) returntype:void\n" "modeCommand " + quotedSketchLocation + " /^void modeCommand(BridgeClient client) {$/;\" kind:function line:149 signature:(BridgeClient client) returntype:void\n"
require.Equal(t, expectedOutput, strings.Replace(string(ctagsRunner.CtagsOutput), "\r\n", "\n", -1))
require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1))
} }
func TestCTagsRunnerSketchWithClass(t *testing.T) { func TestCTagsRunnerSketchWithClass(t *testing.T) {
...@@ -63,14 +67,18 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) { ...@@ -63,14 +67,18 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
ctagsRunner := &builder.CTagsRunner{Source: &ctx.SketchSourceMerged, TargetFileName: "ctags_target.cpp"}
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
&builder.PrintUsedLibrariesIfVerbose{}, &builder.PrintUsedLibrariesIfVerbose{},
&builder.WarnAboutArchIncompatibleLibraries{}, &builder.WarnAboutArchIncompatibleLibraries{},
&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, ctagsRunner,
&builder.CTagsRunner{},
} }
for _, command := range commands { for _, command := range commands {
err := command.Run(ctx) err := command.Run(ctx)
...@@ -83,7 +91,7 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) { ...@@ -83,7 +91,7 @@ func TestCTagsRunnerSketchWithClass(t *testing.T) {
"set_values\t" + quotedSketchLocation + "\t/^void Rectangle::set_values (int x, int y) {$/;\"\tkind:function\tline:8\tclass:Rectangle\tsignature:(int x, int y)\treturntype:void\n" + "set_values\t" + quotedSketchLocation + "\t/^void Rectangle::set_values (int x, int y) {$/;\"\tkind:function\tline:8\tclass:Rectangle\tsignature:(int x, int y)\treturntype:void\n" +
"setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:13\tsignature:()\treturntype:void\n" + "setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:13\tsignature:()\treturntype:void\n" +
"loop\t" + quotedSketchLocation + "\t/^void loop() {$/;\"\tkind:function\tline:17\tsignature:()\treturntype:void\n" "loop\t" + quotedSketchLocation + "\t/^void loop() {$/;\"\tkind:function\tline:17\tsignature:()\treturntype:void\n"
require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) require.Equal(t, expectedOutput, strings.Replace(string(ctagsRunner.CtagsOutput), "\r\n", "\n", -1))
} }
func TestCTagsRunnerSketchWithTypename(t *testing.T) { func TestCTagsRunnerSketchWithTypename(t *testing.T) {
...@@ -92,14 +100,18 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) { ...@@ -92,14 +100,18 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
ctagsRunner := &builder.CTagsRunner{Source: &ctx.SketchSourceMerged, TargetFileName: "ctags_target.cpp"}
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
&builder.PrintUsedLibrariesIfVerbose{}, &builder.PrintUsedLibrariesIfVerbose{},
&builder.WarnAboutArchIncompatibleLibraries{}, &builder.WarnAboutArchIncompatibleLibraries{},
&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, ctagsRunner,
&builder.CTagsRunner{},
} }
for _, command := range commands { for _, command := range commands {
err := command.Run(ctx) err := command.Run(ctx)
...@@ -111,7 +123,7 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) { ...@@ -111,7 +123,7 @@ func TestCTagsRunnerSketchWithTypename(t *testing.T) {
"setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:6\tsignature:()\treturntype:void\n" + "setup\t" + quotedSketchLocation + "\t/^void setup() {$/;\"\tkind:function\tline:6\tsignature:()\treturntype:void\n" +
"loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:10\tsignature:()\treturntype:void\n" + "loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:10\tsignature:()\treturntype:void\n" +
"func\t" + quotedSketchLocation + "\t/^typename Foo<char>::Bar func(){$/;\"\tkind:function\tline:12\tsignature:()\treturntype:Foo::Bar\n" "func\t" + quotedSketchLocation + "\t/^typename Foo<char>::Bar func(){$/;\"\tkind:function\tline:12\tsignature:()\treturntype:Foo::Bar\n"
require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) require.Equal(t, expectedOutput, strings.Replace(string(ctagsRunner.CtagsOutput), "\r\n", "\n", -1))
} }
func TestCTagsRunnerSketchWithNamespace(t *testing.T) { func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
...@@ -120,14 +132,18 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) { ...@@ -120,14 +132,18 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
ctagsRunner := &builder.CTagsRunner{Source: &ctx.SketchSourceMerged, TargetFileName: "ctags_target.cpp"}
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
&builder.PrintUsedLibrariesIfVerbose{}, &builder.PrintUsedLibrariesIfVerbose{},
&builder.WarnAboutArchIncompatibleLibraries{}, &builder.WarnAboutArchIncompatibleLibraries{},
&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, ctagsRunner,
&builder.CTagsRunner{},
} }
for _, command := range commands { for _, command := range commands {
err := command.Run(ctx) err := command.Run(ctx)
...@@ -138,7 +154,7 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) { ...@@ -138,7 +154,7 @@ func TestCTagsRunnerSketchWithNamespace(t *testing.T) {
expectedOutput := "value\t" + quotedSketchLocation + "\t/^\tint value() {$/;\"\tkind:function\tline:2\tnamespace:Test\tsignature:()\treturntype:int\n" + expectedOutput := "value\t" + quotedSketchLocation + "\t/^\tint value() {$/;\"\tkind:function\tline:2\tnamespace:Test\tsignature:()\treturntype:int\n" +
"setup\t" + quotedSketchLocation + "\t/^void setup() {}$/;\"\tkind:function\tline:7\tsignature:()\treturntype:void\n" + "setup\t" + quotedSketchLocation + "\t/^void setup() {}$/;\"\tkind:function\tline:7\tsignature:()\treturntype:void\n" +
"loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:8\tsignature:()\treturntype:void\n" "loop\t" + quotedSketchLocation + "\t/^void loop() {}$/;\"\tkind:function\tline:8\tsignature:()\treturntype:void\n"
require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) require.Equal(t, expectedOutput, strings.Replace(string(ctagsRunner.CtagsOutput), "\r\n", "\n", -1))
} }
func TestCTagsRunnerSketchWithTemplates(t *testing.T) { func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
...@@ -147,14 +163,18 @@ func TestCTagsRunnerSketchWithTemplates(t *testing.T) { ...@@ -147,14 +163,18 @@ func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
ctagsRunner := &builder.CTagsRunner{Source: &ctx.SketchSourceMerged, TargetFileName: "ctags_target.cpp"}
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
&builder.PrintUsedLibrariesIfVerbose{}, &builder.PrintUsedLibrariesIfVerbose{},
&builder.WarnAboutArchIncompatibleLibraries{}, &builder.WarnAboutArchIncompatibleLibraries{},
&builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, ctagsRunner,
&builder.CTagsRunner{},
} }
for _, command := range commands { for _, command := range commands {
err := command.Run(ctx) err := command.Run(ctx)
...@@ -166,5 +186,5 @@ func TestCTagsRunnerSketchWithTemplates(t *testing.T) { ...@@ -166,5 +186,5 @@ func TestCTagsRunnerSketchWithTemplates(t *testing.T) {
"bVar\t" + quotedSketchLocation + "\t/^c< 8 > bVar;$/;\"\tkind:variable\tline:15\n" + "bVar\t" + quotedSketchLocation + "\t/^c< 8 > bVar;$/;\"\tkind:variable\tline:15\n" +
"aVar\t" + quotedSketchLocation + "\t/^c< 1<<8 > aVar;$/;\"\tkind:variable\tline:16\n" + "aVar\t" + quotedSketchLocation + "\t/^c< 1<<8 > aVar;$/;\"\tkind:variable\tline:16\n" +
"func\t" + quotedSketchLocation + "\t/^template<int X> func( c< 1<<X> & aParam) {$/;\"\tkind:function\tline:18\tsignature:( c< 1<<X> & aParam)\treturntype:template\n" "func\t" + quotedSketchLocation + "\t/^template<int X> func( c< 1<<X> & aParam) {$/;\"\tkind:function\tline:18\tsignature:( c< 1<<X> & aParam)\treturntype:template\n"
require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) require.Equal(t, expectedOutput, strings.Replace(string(ctagsRunner.CtagsOutput), "\r\n", "\n", -1))
} }
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"sort" "sort"
"testing" "testing"
bldr "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
paths "github.com/arduino/go-paths-helper" paths "github.com/arduino/go-paths-helper"
...@@ -31,9 +32,13 @@ func TestIncludesToIncludeFolders(t *testing.T) { ...@@ -31,9 +32,13 @@ func TestIncludesToIncludeFolders(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -51,9 +56,13 @@ func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) { ...@@ -51,9 +56,13 @@ func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -70,9 +79,13 @@ func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) { ...@@ -70,9 +79,13 @@ func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -92,9 +105,13 @@ func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) { ...@@ -92,9 +105,13 @@ func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -119,9 +136,13 @@ func TestIncludesToIncludeFoldersDuplicateLibs(t *testing.T) { ...@@ -119,9 +136,13 @@ func TestIncludesToIncludeFoldersDuplicateLibs(t *testing.T) {
ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun") ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun")
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -147,9 +168,13 @@ func TestIncludesToIncludeFoldersDuplicateLibsWithConflictingLibsOutsideOfPlatfo ...@@ -147,9 +168,13 @@ func TestIncludesToIncludeFoldersDuplicateLibsWithConflictingLibsOutsideOfPlatfo
ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun") ctx = prepareBuilderTestContext(t, ctx, paths.New("user_hardware", "my_avr_platform", "avr", "libraries", "SPI", "examples", "BarometricPressureSensor", "BarometricPressureSensor.ino"), "my_avr_platform:avr:custom_yun")
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -175,9 +200,13 @@ func TestIncludesToIncludeFoldersDuplicateLibs2(t *testing.T) { ...@@ -175,9 +200,13 @@ func TestIncludesToIncludeFoldersDuplicateLibs2(t *testing.T) {
ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch_usbhost", "sketch_usbhost.ino"), "arduino:samd:arduino_zero_native") ctx = prepareBuilderTestContext(t, ctx, paths.New("sketch_usbhost", "sketch_usbhost.ino"), "arduino:samd:arduino_zero_native")
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
...@@ -197,9 +226,13 @@ func TestIncludesToIncludeFoldersSubfolders(t *testing.T) { ...@@ -197,9 +226,13 @@ func TestIncludesToIncludeFoldersSubfolders(t *testing.T) {
defer cleanUpBuilderTestContext(t, ctx) defer cleanUpBuilderTestContext(t, ctx)
ctx.Verbose = true ctx.Verbose = true
var _err error
commands := []types.Command{ commands := []types.Command{
&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, &builder.ContainerSetupHardwareToolsLibsSketchAndProps{},
&builder.ContainerMergeCopySketchFiles{}, types.BareCommand(func(ctx *types.Context) error {
ctx.LineOffset, ctx.SketchSourceMerged, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath)
return _err
}),
&builder.ContainerFindIncludes{}, &builder.ContainerFindIncludes{},
} }
for _, command := range commands { for _, command := range commands {
......
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package test
import (
"os"
"testing"
"github.com/arduino/arduino-cli/legacy/builder"
"github.com/arduino/arduino-cli/legacy/builder/types"
paths "github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require"
)
func TestReadFileAndStoreInContext(t *testing.T) {
filePath, err := os.CreateTemp("", "test")
NoError(t, err)
file := paths.New(filePath.Name())
defer file.RemoveAll()
file.WriteFile([]byte("test test\nciao"))
ctx := &types.Context{}
command := &builder.ReadFileAndStoreInContext{FileToRead: file, Target: &ctx.SourceGccMinusE}
err = command.Run(ctx)
NoError(t, err)
require.Equal(t, "test test\nciao", ctx.SourceGccMinusE)
}
...@@ -65,41 +65,3 @@ func TestQuoteCppString(t *testing.T) { ...@@ -65,41 +65,3 @@ func TestQuoteCppString(t *testing.T) {
require.Equal(t, expected, utils.QuoteCppString(input)) require.Equal(t, expected, utils.QuoteCppString(input))
} }
} }
func TestParseCppString(t *testing.T) {
_, _, ok := utils.ParseCppString(`foo`)
require.Equal(t, false, ok)
_, _, ok = utils.ParseCppString(`"foo`)
require.Equal(t, false, ok)
str, rest, ok := utils.ParseCppString(`"foo"`)
require.Equal(t, true, ok)
require.Equal(t, `foo`, str)
require.Equal(t, ``, rest)
str, rest, ok = utils.ParseCppString(`"foo\\bar"`)
require.Equal(t, true, ok)
require.Equal(t, `foo\bar`, str)
require.Equal(t, ``, rest)
str, rest, ok = utils.ParseCppString(`"foo \"is\" quoted and \\\\bar\"\" escaped\\" and "then" some`)
require.Equal(t, true, ok)
require.Equal(t, `foo "is" quoted and \\bar"" escaped\`, str)
require.Equal(t, ` and "then" some`, rest)
str, rest, ok = utils.ParseCppString(`" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`)
require.Equal(t, true, ok)
require.Equal(t, ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`, str)
require.Equal(t, ``, rest)
str, rest, ok = utils.ParseCppString(`"/home/ççç/"`)
require.Equal(t, true, ok)
require.Equal(t, `/home/ççç/`, str)
require.Equal(t, ``, rest)
str, rest, ok = utils.ParseCppString(`"/home/ççç/ /$sdsdd\\"`)
require.Equal(t, true, ok)
require.Equal(t, `/home/ççç/ /$sdsdd\`, str)
require.Equal(t, ``, rest)
}
...@@ -101,11 +101,16 @@ type Context struct { ...@@ -101,11 +101,16 @@ type Context struct {
CollectedSourceFiles *UniqueSourceFileQueue CollectedSourceFiles *UniqueSourceFileQueue
Sketch *sketch.Sketch Sketch *sketch.Sketch
Source string
SourceGccMinusE string
WarningsLevel string WarningsLevel string
// Arduino sketch (.ino) to C++ (.cpp) conversion steps:
// 1. Concatenate *.ino files into a single merged source file -> SketchSourceMerged
SketchSourceMerged string
// 2. Run a pass of C++ preprocessor to remove macro definitions and ifdef-ed code -> SketchSourceAfterCppPreprocessing
SketchSourceAfterCppPreprocessing string
// 3. Do the Arduino preprocessing of the sketch (add missing prototypes) -> SketchSourceAfterArduinoPreprocessing
SketchSourceAfterArduinoPreprocessing string
// Libraries handling // Libraries handling
LibrariesManager *librariesmanager.LibrariesManager LibrariesManager *librariesmanager.LibrariesManager
LibrariesResolver *librariesresolver.Cpp LibrariesResolver *librariesresolver.Cpp
...@@ -115,9 +120,6 @@ type Context struct { ...@@ -115,9 +120,6 @@ type Context struct {
UseCachedLibrariesResolution bool UseCachedLibrariesResolution bool
// C++ Parsing // C++ Parsing
CTagsOutput string
CTagsTargetFile *paths.Path
CTagsOfPreprocessedSource []*CTag
LineOffset int LineOffset int
PrototypesSection string PrototypesSection string
PrototypesLineWhereToInsert int PrototypesLineWhereToInsert int
......
...@@ -122,3 +122,9 @@ type CTag struct { ...@@ -122,3 +122,9 @@ type CTag struct {
type Command interface { type Command interface {
Run(ctx *Context) error Run(ctx *Context) error
} }
type BareCommand func(ctx *Context) error
func (cmd BareCommand) Run(ctx *Context) error {
return cmd(ctx)
}
...@@ -26,7 +26,6 @@ import ( ...@@ -26,7 +26,6 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
"github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/i18n"
"github.com/arduino/arduino-cli/legacy/builder/gohasissues" "github.com/arduino/arduino-cli/legacy/builder/gohasissues"
...@@ -301,50 +300,6 @@ func QuoteCppPath(path *paths.Path) string { ...@@ -301,50 +300,6 @@ func QuoteCppPath(path *paths.Path) string {
return QuoteCppString(path.String()) return QuoteCppString(path.String())
} }
// Parse a C-preprocessor string as emitted by the preprocessor. This
// is a string contained in double quotes, with any backslashes or
// quotes escaped with a backslash. If a valid string was present at the
// start of the given line, returns the unquoted string contents, the
// remainder of the line (everything after the closing "), and true.
// Otherwise, returns the empty string, the entire line and false.
func ParseCppString(line string) (string, string, bool) {
// For details about how these strings are output by gcc, see:
// https://github.com/gcc-mirror/gcc/blob/a588355ab948cf551bc9d2b89f18e5ae5140f52c/libcpp/macro.c#L491-L511
// Note that the documentation suggests all non-printable
// characters are also escaped, but the implementation does not
// actually do this. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51259
if len(line) < 1 || line[0] != '"' {
return "", line, false
}
i := 1
res := ""
for {
if i >= len(line) {
return "", line, false
}
c, width := utf8.DecodeRuneInString(line[i:])
switch c {
case '\\':
// Backslash, next character is used unmodified
i += width
if i >= len(line) {
return "", line, false
}
res += string(line[i])
case '"':
// Quote, end of string
return res, line[i+width:], true
default:
res += string(c)
}
i += width
}
}
// Normalizes an UTF8 byte slice // Normalizes an UTF8 byte slice
// TODO: use it more often troughout all the project (maybe on logger interface?) // TODO: use it more often troughout all the project (maybe on logger interface?)
func NormalizeUTF8(buf []byte) []byte { func NormalizeUTF8(buf []byte) []byte {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment