diff --git a/.licenses/go/golang.org/x/exp/constraints.dep.yml b/.licenses/go/golang.org/x/exp/constraints.dep.yml new file mode 100644 index 00000000000..14cd0ef4868 --- /dev/null +++ b/.licenses/go/golang.org/x/exp/constraints.dep.yml @@ -0,0 +1,63 @@ +--- +name: golang.org/x/exp/constraints +version: v0.0.0-20230321023759-10a507213a29 +type: go +summary: Package constraints defines a set of useful constraints to be used with type + parameters. +homepage: https://pkg.go.dev/golang.org/x/exp/constraints +license: bsd-3-clause +licenses: +- sources: exp@v0.0.0-20230321023759-10a507213a29/LICENSE + text: | + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- sources: exp@v0.0.0-20230321023759-10a507213a29/PATENTS + text: | + Additional IP Rights Grant (Patents) + + "This implementation" means the copyrightable works distributed by + Google as part of the Go project. + + Google hereby grants to You a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this section) + patent license to make, have made, use, offer to sell, sell, import, + transfer and otherwise run, modify and propagate the contents of this + implementation of Go, where such license applies only to those patent + claims, both currently owned or controlled by Google and acquired in + the future, licensable by Google that are necessarily infringed by this + implementation of Go. This grant does not include claims that would be + infringed only as a consequence of further modification of this + implementation. If you or your agent or exclusive licensee institute or + order or agree to the institution of patent litigation against any + entity (including a cross-claim or counterclaim in a lawsuit) alleging + that this implementation of Go or any code incorporated within this + implementation of Go constitutes direct or contributory patent + infringement, or inducement of patent infringement, then any patent + rights granted to you under this License for this implementation of Go + shall terminate as of the date such litigation is filed. +notices: [] diff --git a/.licenses/go/golang.org/x/exp/slices.dep.yml b/.licenses/go/golang.org/x/exp/slices.dep.yml new file mode 100644 index 00000000000..8fca8863335 --- /dev/null +++ b/.licenses/go/golang.org/x/exp/slices.dep.yml @@ -0,0 +1,62 @@ +--- +name: golang.org/x/exp/slices +version: v0.0.0-20230321023759-10a507213a29 +type: go +summary: Package slices defines various functions useful with slices of any type. +homepage: https://pkg.go.dev/golang.org/x/exp/slices +license: bsd-3-clause +licenses: +- sources: exp@v0.0.0-20230321023759-10a507213a29/LICENSE + text: | + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- sources: exp@v0.0.0-20230321023759-10a507213a29/PATENTS + text: | + Additional IP Rights Grant (Patents) + + "This implementation" means the copyrightable works distributed by + Google as part of the Go project. + + Google hereby grants to You a perpetual, worldwide, non-exclusive, + no-charge, royalty-free, irrevocable (except as stated in this section) + patent license to make, have made, use, offer to sell, sell, import, + transfer and otherwise run, modify and propagate the contents of this + implementation of Go, where such license applies only to those patent + claims, both currently owned or controlled by Google and acquired in + the future, licensable by Google that are necessarily infringed by this + implementation of Go. This grant does not include claims that would be + infringed only as a consequence of further modification of this + implementation. If you or your agent or exclusive licensee institute or + order or agree to the institution of patent litigation against any + entity (including a cross-claim or counterclaim in a lawsuit) alleging + that this implementation of Go or any code incorporated within this + implementation of Go constitutes direct or contributory patent + infringement, or inducement of patent infringement, then any patent + rights granted to you under this License for this implementation of Go + shall terminate as of the date such litigation is filed. +notices: [] diff --git a/arduino/builder/cpp/cpp.go b/arduino/builder/cpp/cpp.go new file mode 100644 index 00000000000..71c2b696702 --- /dev/null +++ b/arduino/builder/cpp/cpp.go @@ -0,0 +1,108 @@ +// 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 cpp + +import ( + "strconv" + "strings" + "unicode/utf8" + + "github.com/arduino/go-paths-helper" +) + +// QuoteString 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 QuoteString(str string) string { + str = strings.Replace(str, "\\", "\\\\", -1) + str = strings.Replace(str, "\"", "\\\"", -1) + return "\"" + str + "\"" +} + +// ParseLineMarker 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 := ParseString(split[2]) + + if ok && (rest == "" || rest[0] == ' ') { + return paths.New(str) + } + return nil +} + +// ParseString parse a 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 ParseString(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 + } +} diff --git a/arduino/builder/cpp/cpp_test.go b/arduino/builder/cpp/cpp_test.go new file mode 100644 index 00000000000..b6ae543f558 --- /dev/null +++ b/arduino/builder/cpp/cpp_test.go @@ -0,0 +1,74 @@ +// 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 cpp_test + +import ( + "testing" + + "github.com/arduino/arduino-cli/arduino/builder/cpp" + "github.com/stretchr/testify/require" +) + +func TestParseString(t *testing.T) { + _, _, ok := cpp.ParseString(`foo`) + require.Equal(t, false, ok) + + _, _, ok = cpp.ParseString(`"foo`) + require.Equal(t, false, ok) + + str, rest, ok := cpp.ParseString(`"foo"`) + require.Equal(t, true, ok) + require.Equal(t, `foo`, str) + require.Equal(t, ``, rest) + + str, rest, ok = cpp.ParseString(`"foo\\bar"`) + require.Equal(t, true, ok) + require.Equal(t, `foo\bar`, str) + require.Equal(t, ``, rest) + + str, rest, ok = cpp.ParseString(`"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 = cpp.ParseString(`" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`) + require.Equal(t, true, ok) + require.Equal(t, ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`, str) + require.Equal(t, ``, rest) + + str, rest, ok = cpp.ParseString(`"/home/ççç/"`) + require.Equal(t, true, ok) + require.Equal(t, `/home/ççç/`, str) + require.Equal(t, ``, rest) + + str, rest, ok = cpp.ParseString(`"/home/ççç/ /$sdsdd\\"`) + require.Equal(t, true, ok) + require.Equal(t, `/home/ççç/ /$sdsdd\`, str) + require.Equal(t, ``, rest) +} + +func TestQuoteString(t *testing.T) { + cases := map[string]string{ + `foo`: `"foo"`, + `foo\bar`: `"foo\\bar"`, + `foo "is" quoted and \\bar"" escaped\`: `"foo \"is\" quoted and \\\\bar\"\" escaped\\"`, + // ASCII 0x20 - 0x7e, excluding ` + ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`: `" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`, + } + for input, expected := range cases { + require.Equal(t, expected, cpp.QuoteString(input)) + } +} diff --git a/arduino/builder/preprocessor/ctags.go b/arduino/builder/preprocessor/ctags.go new file mode 100644 index 00000000000..bf5f25b4ca2 --- /dev/null +++ b/arduino/builder/preprocessor/ctags.go @@ -0,0 +1,234 @@ +// 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 preprocessor + +import ( + "bufio" + "bytes" + "context" + "fmt" + "io" + "strconv" + "strings" + + "github.com/arduino/arduino-cli/arduino/builder/cpp" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor/internal/ctags" + "github.com/arduino/arduino-cli/arduino/sketch" + "github.com/arduino/arduino-cli/executils" + "github.com/arduino/arduino-cli/i18n" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/pkg/errors" +) + +var tr = i18n.Tr + +// DebugPreprocessor when set to true the CTags preprocessor will output debugging info to stdout +// this is useful for unit-testing to provide more infos +var DebugPreprocessor bool + +// PreprocessSketchWithCtags performs preprocessing of the arduino sketch using CTags. +func PreprocessSketchWithCtags(sketch *sketch.Sketch, buildPath *paths.Path, includes paths.PathList, lineOffset int, buildProperties *properties.Map, onlyUpdateCompilationDatabase bool) ([]byte, []byte, error) { + // Create a temporary working directory + tmpDir, err := paths.MkTempDir("", "") + if err != nil { + return nil, nil, err + } + defer tmpDir.RemoveAll() + ctagsTarget := tmpDir.Join("sketch_merged.cpp") + + normalOutput := &bytes.Buffer{} + verboseOutput := &bytes.Buffer{} + + // Run GCC preprocessor + sourceFile := buildPath.Join("sketch", sketch.MainFile.Base()+".cpp") + gccStdout, gccStderr, err := GCC(sourceFile, ctagsTarget, includes, buildProperties) + verboseOutput.Write(gccStdout) + verboseOutput.Write(gccStderr) + normalOutput.Write(gccStderr) + if err != nil { + if !onlyUpdateCompilationDatabase { + return normalOutput.Bytes(), verboseOutput.Bytes(), errors.WithStack(err) + } + + // Do not bail out if we are generating the compile commands database + normalOutput.WriteString(fmt.Sprintf("%s: %s", + tr("An error occurred adding prototypes"), + tr("the compilation database may be incomplete or inaccurate"))) + if err := sourceFile.CopyTo(ctagsTarget); err != nil { + return normalOutput.Bytes(), verboseOutput.Bytes(), errors.WithStack(err) + } + } + + if src, err := ctagsTarget.ReadFile(); err == nil { + filteredSource := filterSketchSource(sketch, bytes.NewReader(src), false) + if err := ctagsTarget.WriteFile([]byte(filteredSource)); err != nil { + return normalOutput.Bytes(), verboseOutput.Bytes(), err + } + } else { + return normalOutput.Bytes(), verboseOutput.Bytes(), err + } + + // Run CTags on gcc-preprocessed source + ctagsOutput, ctagsStdErr, err := RunCTags(ctagsTarget, buildProperties) + verboseOutput.Write(ctagsStdErr) + if err != nil { + return normalOutput.Bytes(), verboseOutput.Bytes(), err + } + + // Parse CTags output + parser := &ctags.Parser{} + prototypes, firstFunctionLine := parser.Parse(ctagsOutput, sketch.MainFile) + if firstFunctionLine == -1 { + firstFunctionLine = 0 + } + + // Add prototypes to the original sketch source + var source string + if sourceData, err := sourceFile.ReadFile(); err == nil { + source = string(sourceData) + } else { + return normalOutput.Bytes(), verboseOutput.Bytes(), err + } + source = strings.Replace(source, "\r\n", "\n", -1) + source = strings.Replace(source, "\r", "\n", -1) + sourceRows := strings.Split(source, "\n") + if isFirstFunctionOutsideOfSource(firstFunctionLine, sourceRows) { + return normalOutput.Bytes(), verboseOutput.Bytes(), nil + } + + insertionLine := firstFunctionLine + lineOffset - 1 + firstFunctionChar := len(strings.Join(sourceRows[:insertionLine], "\n")) + 1 + prototypeSection := composePrototypeSection(firstFunctionLine, prototypes) + preprocessedSource := source[:firstFunctionChar] + prototypeSection + source[firstFunctionChar:] + + if DebugPreprocessor { + fmt.Println("#PREPROCESSED SOURCE") + prototypesRows := strings.Split(prototypeSection, "\n") + prototypesRows = prototypesRows[:len(prototypesRows)-1] + for i := 0; i < len(sourceRows)+len(prototypesRows); i++ { + if i < insertionLine { + fmt.Printf(" |%s\n", sourceRows[i]) + } else if i < insertionLine+len(prototypesRows) { + fmt.Printf("PRO|%s\n", prototypesRows[i-insertionLine]) + } else { + fmt.Printf(" |%s\n", sourceRows[i-len(prototypesRows)]) + } + } + fmt.Println("#END OF PREPROCESSED SOURCE") + } + + // Write back arduino-preprocess output to the sourceFile + err = sourceFile.WriteFile([]byte(preprocessedSource)) + return normalOutput.Bytes(), verboseOutput.Bytes(), err +} + +func composePrototypeSection(line int, prototypes []*ctags.Prototype) string { + if len(prototypes) == 0 { + return "" + } + + str := joinPrototypes(prototypes) + str += "\n#line " + str += strconv.Itoa(line) + str += " " + cpp.QuoteString(prototypes[0].File) + str += "\n" + + return str +} + +func joinPrototypes(prototypes []*ctags.Prototype) string { + prototypesSlice := []string{} + for _, proto := range prototypes { + if signatureContainsaDefaultArg(proto) { + continue + } + prototypesSlice = append(prototypesSlice, "#line "+strconv.Itoa(proto.Line)+" "+cpp.QuoteString(proto.File)) + prototypeParts := []string{} + if proto.Modifiers != "" { + prototypeParts = append(prototypeParts, proto.Modifiers) + } + prototypeParts = append(prototypeParts, proto.Prototype) + prototypesSlice = append(prototypesSlice, strings.Join(prototypeParts, " ")) + } + return strings.Join(prototypesSlice, "\n") +} + +func signatureContainsaDefaultArg(proto *ctags.Prototype) bool { + return strings.Contains(proto.Prototype, "=") +} + +func isFirstFunctionOutsideOfSource(firstFunctionLine int, sourceRows []string) bool { + return firstFunctionLine > len(sourceRows)-1 +} + +// RunCTags performs a run of ctags on the given source file. Returns the ctags output and the stderr contents. +func RunCTags(sourceFile *paths.Path, buildProperties *properties.Map) ([]byte, []byte, error) { + ctagsBuildProperties := properties.NewMap() + ctagsBuildProperties.Set("tools.ctags.path", "{runtime.tools.ctags.path}") + ctagsBuildProperties.Set("tools.ctags.cmd.path", "{path}/ctags") + ctagsBuildProperties.Set("tools.ctags.pattern", `"{cmd.path}" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "{source_file}"`) + ctagsBuildProperties.Merge(buildProperties) + ctagsBuildProperties.Merge(ctagsBuildProperties.SubTree("tools").SubTree("ctags")) + ctagsBuildProperties.SetPath("source_file", sourceFile) + + pattern := ctagsBuildProperties.Get("pattern") + if pattern == "" { + return nil, nil, errors.Errorf(tr("%s pattern is missing"), "ctags") + } + + commandLine := ctagsBuildProperties.ExpandPropsInString(pattern) + parts, err := properties.SplitQuotedString(commandLine, `"'`, false) + if err != nil { + return nil, nil, err + } + proc, err := executils.NewProcess(nil, parts...) + if err != nil { + return nil, nil, err + } + stdout, stderr, err := proc.RunAndCaptureOutput(context.Background()) + + // Append ctags arguments to stderr + args := fmt.Sprintln(strings.Join(parts, " ")) + stderr = append([]byte(args), stderr...) + return stdout, stderr, err +} + +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 := cpp.ParseLineMarker(line); filename != nil { + inSketch = fileNames.Contains(filename) + if inSketch && removeLineMarkers { + continue + } + } + + if inSketch { + filtered += line + "\n" + } + } + + return filtered +} diff --git a/arduino/builder/preprocessor/gcc.go b/arduino/builder/preprocessor/gcc.go new file mode 100644 index 00000000000..02fb62f47e1 --- /dev/null +++ b/arduino/builder/preprocessor/gcc.go @@ -0,0 +1,81 @@ +// 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 preprocessor + +import ( + "context" + "fmt" + "strings" + + "github.com/arduino/arduino-cli/executils" + f "github.com/arduino/arduino-cli/internal/algorithms" + "github.com/arduino/arduino-cli/legacy/builder/utils" + "github.com/arduino/go-paths-helper" + "github.com/arduino/go-properties-orderedmap" + "github.com/pkg/errors" +) + +// GCC performs a run of the gcc preprocess (macro/includes expansion). The function output the result +// to targetFilePath. Returns the stdout/stderr of gcc if any. +func GCC(sourceFilePath *paths.Path, targetFilePath *paths.Path, includes paths.PathList, buildProperties *properties.Map) ([]byte, []byte, error) { + gccBuildProperties := properties.NewMap() + gccBuildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC") + gccBuildProperties.Merge(buildProperties) + gccBuildProperties.Set("build.library_discovery_phase", "1") + gccBuildProperties.SetPath("source_file", sourceFilePath) + gccBuildProperties.SetPath("preprocessed_file_path", targetFilePath) + + includesStrings := f.Map(includes.AsStrings(), utils.WrapWithHyphenI) + gccBuildProperties.Set("includes", strings.Join(includesStrings, " ")) + + const gccPreprocRecipeProperty = "recipe.preproc.macros" + if gccBuildProperties.Get(gccPreprocRecipeProperty) == "" { + // autogenerate preprocess macros recipe from compile recipe + preprocPattern := gccBuildProperties.Get("recipe.cpp.o.pattern") + // add {preproc.macros.flags} to {compiler.cpp.flags} + preprocPattern = strings.Replace(preprocPattern, "{compiler.cpp.flags}", "{compiler.cpp.flags} {preproc.macros.flags}", 1) + // replace "{object_file}" with "{preprocessed_file_path}" + preprocPattern = strings.Replace(preprocPattern, "{object_file}", "{preprocessed_file_path}", 1) + + gccBuildProperties.Set(gccPreprocRecipeProperty, preprocPattern) + } + + pattern := gccBuildProperties.Get(gccPreprocRecipeProperty) + if pattern == "" { + return nil, nil, errors.Errorf(tr("%s pattern is missing"), gccPreprocRecipeProperty) + } + + commandLine := gccBuildProperties.ExpandPropsInString(pattern) + args, err := properties.SplitQuotedString(commandLine, `"'`, false) + if err != nil { + return nil, nil, err + } + + // Remove -MMD argument if present. Leaving it will make gcc try + // to create a /dev/null.d dependency file, which won't work. + args = f.Filter(args, f.NotEquals("-MMD")) + + proc, err := executils.NewProcess(nil, args...) + if err != nil { + return nil, nil, err + } + stdout, stderr, err := proc.RunAndCaptureOutput(context.Background()) + + // Append gcc arguments to stdout + stdout = append([]byte(fmt.Sprintln(strings.Join(args, " "))), stdout...) + + return stdout, stderr, err +} diff --git a/legacy/builder/ctags/ctags_has_issues.go b/arduino/builder/preprocessor/internal/ctags/ctags_has_issues.go similarity index 84% rename from legacy/builder/ctags/ctags_has_issues.go rename to arduino/builder/preprocessor/internal/ctags/ctags_has_issues.go index 7cc693f2af6..4d4d7d48f65 100644 --- a/legacy/builder/ctags/ctags_has_issues.go +++ b/arduino/builder/preprocessor/internal/ctags/ctags_has_issues.go @@ -20,31 +20,20 @@ import ( "os" "strings" - "github.com/arduino/arduino-cli/legacy/builder/types" + "golang.org/x/exp/slices" ) -func (p *CTagsParser) FixCLinkageTagsDeclarations(tags []*types.CTag) { - - linesMap := p.FindCLinkageLines(tags) - for i := range tags { - - if sliceContainsInt(linesMap[tags[i].Filename], tags[i].Line) && - !strings.Contains(tags[i].PrototypeModifiers, EXTERN) { - tags[i].PrototypeModifiers = tags[i].PrototypeModifiers + " " + EXTERN - } - } -} - -func sliceContainsInt(s []int, e int) bool { - for _, a := range s { - if a == e { - return true +func (p *Parser) fixCLinkageTagsDeclarations() { + linesMap := p.FindCLinkageLines(p.tags) + for i := range p.tags { + if slices.Contains(linesMap[p.tags[i].Filename], p.tags[i].Line) && + !strings.Contains(p.tags[i].PrototypeModifiers, keywordExternC) { + p.tags[i].PrototypeModifiers = p.tags[i].PrototypeModifiers + " " + keywordExternC } } - return false } -func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { +func (p *Parser) prototypeAndCodeDontMatch(tag *Tag) bool { if tag.SkipMe { return true } @@ -109,7 +98,7 @@ func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { return ret == -1 } -func findTemplateMultiline(tag *types.CTag) string { +func findTemplateMultiline(tag *Tag) string { code, _ := getFunctionProtoUntilTemplateToken(tag, tag.Code) return removeEverythingAfterClosingRoundBracket(code) } @@ -119,7 +108,7 @@ func removeEverythingAfterClosingRoundBracket(s string) string { return s[0 : n+1] } -func getFunctionProtoUntilTemplateToken(tag *types.CTag, code string) (string, int) { +func getFunctionProtoUntilTemplateToken(tag *Tag, code string) (string, int) { /* FIXME I'm ugly */ line := 0 @@ -139,7 +128,7 @@ func getFunctionProtoUntilTemplateToken(tag *types.CTag, code string) (string, i textBuffer = append(textBuffer, text) } - for line > 0 && !strings.Contains(code, TEMPLATE) { + for line > 0 && !strings.Contains(code, keywordTemplate) { line = line - 1 text := textBuffer[line] @@ -152,7 +141,7 @@ func getFunctionProtoUntilTemplateToken(tag *types.CTag, code string) (string, i return code, line } -func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int) (string, int) { +func getFunctionProtoWithNPreviousCharacters(tag *Tag, code string, n int) (string, int) { /* FIXME I'm ugly */ expectedPrototypeLen := len(code) + n @@ -215,10 +204,9 @@ func removeComments(text string, multilinecomment bool) (string, bool) { return text, multilinecomment } -/* This function scans the source files searching for "extern C" context - * It save the line numbers in a map filename -> {lines...} - */ -func (p *CTagsParser) FindCLinkageLines(tags []*types.CTag) map[string][]int { +// FindCLinkageLines scans the source files searching for "extern C" context +// It save the line numbers in a map filename -> {lines...} +func (p *Parser) FindCLinkageLines(tags []*Tag) map[string][]int { lines := make(map[string][]int) @@ -256,7 +244,7 @@ func (p *CTagsParser) FindCLinkageLines(tags []*types.CTag) map[string][]int { indentLevels := 0 line := 0 - externCDecl := removeSpacesAndTabs(EXTERN) + externCDecl := removeSpacesAndTabs(keywordExternC) for scanner.Scan() { line++ diff --git a/legacy/builder/ctags/ctags_parser.go b/arduino/builder/preprocessor/internal/ctags/ctags_parser.go similarity index 75% rename from legacy/builder/ctags/ctags_parser.go rename to arduino/builder/preprocessor/internal/ctags/ctags_parser.go index 4ced464ae1e..c2e6e99585f 100644 --- a/legacy/builder/ctags/ctags_parser.go +++ b/arduino/builder/preprocessor/internal/ctags/ctags_parser.go @@ -20,31 +20,49 @@ import ( "strings" "github.com/arduino/go-paths-helper" - - "github.com/arduino/arduino-cli/legacy/builder/types" ) -const KIND_PROTOTYPE = "prototype" -const KIND_FUNCTION = "function" +const kindPrototype = "prototype" +const kindFunction = "function" //const KIND_PROTOTYPE_MODIFIERS = "prototype_modifiers" -const TEMPLATE = "template" -const STATIC = "static" -const EXTERN = "extern \"C\"" +const keywordTemplate = "template" +const keywordStatic = "static" +const keywordExternC = "extern \"C\"" -var KNOWN_TAG_KINDS = map[string]bool{ +var knownTagKinds = map[string]bool{ "prototype": true, "function": true, } -type CTagsParser struct { - tags []*types.CTag +// Parser is a parser for ctags output +type Parser struct { + tags []*Tag mainFile *paths.Path } -func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) []*types.CTag { - rows := strings.Split(ctagsOutput, "\n") +// Tag is a tag generated by ctags +type Tag struct { + FunctionName string + Kind string + Line int + Code string + Class string + Struct string + Namespace string + Filename string + Typeref string + SkipMe bool + Signature string + + Prototype string + PrototypeModifiers string +} + +// Parse a ctags output and generates Prototypes +func (p *Parser) Parse(ctagsOutput []byte, mainFile *paths.Path) ([]*Prototype, int) { + rows := strings.Split(string(ctagsOutput), "\n") rows = removeEmpty(rows) p.mainFile = mainFile @@ -59,11 +77,12 @@ func (p *CTagsParser) Parse(ctagsOutput string, mainFile *paths.Path) []*types.C p.removeDefinedProtypes() p.skipDuplicates() p.skipTagsWhere(p.prototypeAndCodeDontMatch) + p.fixCLinkageTagsDeclarations() - return p.tags + return p.toPrototypes(), p.findLineWhereToInsertPrototypes() } -func (p *CTagsParser) addPrototypes() { +func (p *Parser) addPrototypes() { for _, tag := range p.tags { if !tag.SkipMe { addPrototype(tag) @@ -71,9 +90,9 @@ func (p *CTagsParser) addPrototypes() { } } -func addPrototype(tag *types.CTag) { - if strings.Index(tag.Prototype, TEMPLATE) == 0 { - if strings.Index(tag.Code, TEMPLATE) == 0 { +func addPrototype(tag *Tag) { + if strings.Index(tag.Prototype, keywordTemplate) == 0 { + if strings.Index(tag.Code, keywordTemplate) == 0 { code := tag.Code if strings.Contains(code, "{") { code = code[:strings.Index(code, "{")] @@ -90,8 +109,8 @@ func addPrototype(tag *types.CTag) { } tag.PrototypeModifiers = "" - if strings.Contains(tag.Code, STATIC+" ") { - tag.PrototypeModifiers = tag.PrototypeModifiers + " " + STATIC + if strings.Contains(tag.Code, keywordStatic+" ") { + tag.PrototypeModifiers = tag.PrototypeModifiers + " " + keywordStatic } // Extern "C" modifier is now added in FixCLinkageTagsDeclarations @@ -99,10 +118,10 @@ func addPrototype(tag *types.CTag) { tag.PrototypeModifiers = strings.TrimSpace(tag.PrototypeModifiers) } -func (p *CTagsParser) removeDefinedProtypes() { +func (p *Parser) removeDefinedProtypes() { definedPrototypes := make(map[string]bool) for _, tag := range p.tags { - if tag.Kind == KIND_PROTOTYPE { + if tag.Kind == kindPrototype { definedPrototypes[tag.Prototype] = true } } @@ -117,7 +136,7 @@ func (p *CTagsParser) removeDefinedProtypes() { } } -func (p *CTagsParser) skipDuplicates() { +func (p *Parser) skipDuplicates() { definedPrototypes := make(map[string]bool) for _, tag := range p.tags { @@ -129,9 +148,9 @@ func (p *CTagsParser) skipDuplicates() { } } -type skipFuncType func(tag *types.CTag) bool +type skipFuncType func(tag *Tag) bool -func (p *CTagsParser) skipTagsWhere(skipFunc skipFuncType) { +func (p *Parser) skipTagsWhere(skipFunc skipFuncType) { for _, tag := range p.tags { if !tag.SkipMe { skip := skipFunc(tag) @@ -153,11 +172,11 @@ func removeSpacesAndTabs(s string) string { return s } -func tagIsUnhandled(tag *types.CTag) bool { +func tagIsUnhandled(tag *Tag) bool { return !isHandled(tag) } -func isHandled(tag *types.CTag) bool { +func isHandled(tag *Tag) bool { if tag.Class != "" { return false } @@ -170,12 +189,12 @@ func isHandled(tag *types.CTag) bool { return true } -func tagIsUnknown(tag *types.CTag) bool { - return !KNOWN_TAG_KINDS[tag.Kind] +func tagIsUnknown(tag *Tag) bool { + return !knownTagKinds[tag.Kind] } -func parseTag(row string) *types.CTag { - tag := &types.CTag{} +func parseTag(row string) *Tag { + tag := &Tag{} parts := strings.Split(row, "\t") tag.FunctionName = parts[0] diff --git a/legacy/builder/ctags/ctags_parser_test.go b/arduino/builder/preprocessor/internal/ctags/ctags_parser_test.go similarity index 97% rename from legacy/builder/ctags/ctags_parser_test.go rename to arduino/builder/preprocessor/internal/ctags/ctags_parser_test.go index d3bb03b23a0..7fc390cec67 100644 --- a/legacy/builder/ctags/ctags_parser_test.go +++ b/arduino/builder/preprocessor/internal/ctags/ctags_parser_test.go @@ -20,17 +20,17 @@ import ( "path/filepath" "testing" - "github.com/arduino/arduino-cli/legacy/builder/types" - + "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" ) -func produceTags(t *testing.T, filename string) []*types.CTag { - bytes, err := os.ReadFile(filepath.Join("test_data", filename)) +func produceTags(t *testing.T, filename string) []*Tag { + bytes, err := os.ReadFile(filepath.Join("testdata", filename)) require.NoError(t, err) - parser := CTagsParser{} - return parser.Parse(string(bytes), nil) + parser := Parser{} + parser.Parse(bytes, paths.New("sketch.ino")) + return parser.tags } func TestCTagsParserShouldListPrototypes(t *testing.T) { diff --git a/legacy/builder/ctags/ctags_to_prototypes.go b/arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes.go similarity index 70% rename from legacy/builder/ctags/ctags_to_prototypes.go rename to arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes.go index dea077ff0aa..95fc384f116 100644 --- a/legacy/builder/ctags/ctags_to_prototypes.go +++ b/arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes.go @@ -16,24 +16,31 @@ package ctags import ( + "strconv" "strings" - - "github.com/arduino/arduino-cli/legacy/builder/types" ) -func (p *CTagsParser) GeneratePrototypes() ([]*types.Prototype, int) { - return p.toPrototypes(), p.findLineWhereToInsertPrototypes() +// Prototype is a C++ prototype generated from ctags output +type Prototype struct { + FunctionName string + File string + Prototype string + Modifiers string + Line int +} + +func (proto *Prototype) String() string { + return proto.Modifiers + " " + proto.Prototype + " @ " + strconv.Itoa(proto.Line) } -func (p *CTagsParser) findLineWhereToInsertPrototypes() int { +func (p *Parser) findLineWhereToInsertPrototypes() int { firstFunctionLine := p.firstFunctionAtLine() firstFunctionPointerAsArgument := p.firstFunctionPointerUsedAsArgument() if firstFunctionLine != -1 && firstFunctionPointerAsArgument != -1 { if firstFunctionLine < firstFunctionPointerAsArgument { return firstFunctionLine - } else { - return firstFunctionPointerAsArgument } + return firstFunctionPointerAsArgument } else if firstFunctionLine != -1 { return firstFunctionLine } else if firstFunctionPointerAsArgument != -1 { @@ -43,7 +50,7 @@ func (p *CTagsParser) findLineWhereToInsertPrototypes() int { } } -func (p *CTagsParser) firstFunctionPointerUsedAsArgument() int { +func (p *Parser) firstFunctionPointerUsedAsArgument() int { functionTags := p.collectFunctions() for _, tag := range p.tags { if functionNameUsedAsFunctionPointerIn(tag, functionTags) { @@ -53,7 +60,7 @@ func (p *CTagsParser) firstFunctionPointerUsedAsArgument() int { return -1 } -func functionNameUsedAsFunctionPointerIn(tag *types.CTag, functionTags []*types.CTag) bool { +func functionNameUsedAsFunctionPointerIn(tag *Tag, functionTags []*Tag) bool { for _, functionTag := range functionTags { if tag.Line != functionTag.Line && strings.Contains(tag.Code, "&"+functionTag.FunctionName) { return true @@ -65,33 +72,33 @@ func functionNameUsedAsFunctionPointerIn(tag *types.CTag, functionTags []*types. return false } -func (p *CTagsParser) collectFunctions() []*types.CTag { - functionTags := []*types.CTag{} +func (p *Parser) collectFunctions() []*Tag { + functionTags := []*Tag{} for _, tag := range p.tags { - if tag.Kind == KIND_FUNCTION && !tag.SkipMe { + if tag.Kind == kindFunction && !tag.SkipMe { functionTags = append(functionTags, tag) } } return functionTags } -func (p *CTagsParser) firstFunctionAtLine() int { +func (p *Parser) firstFunctionAtLine() int { for _, tag := range p.tags { - if !tagIsUnknown(tag) && isHandled(tag) && tag.Kind == KIND_FUNCTION && tag.Filename == p.mainFile.String() { + if !tagIsUnknown(tag) && isHandled(tag) && tag.Kind == kindFunction && tag.Filename == p.mainFile.String() { return tag.Line } } return -1 } -func (p *CTagsParser) toPrototypes() []*types.Prototype { - prototypes := []*types.Prototype{} +func (p *Parser) toPrototypes() []*Prototype { + prototypes := []*Prototype{} for _, tag := range p.tags { if strings.TrimSpace(tag.Prototype) == "" { continue } if !tag.SkipMe { - prototype := &types.Prototype{ + prototype := &Prototype{ FunctionName: tag.FunctionName, File: tag.Filename, Prototype: tag.Prototype, diff --git a/legacy/builder/ctags/ctags_to_prototypes_test.go b/arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes_test.go similarity index 97% rename from legacy/builder/ctags/ctags_to_prototypes_test.go rename to arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes_test.go index e73a1312069..bfec902d8d9 100644 --- a/legacy/builder/ctags/ctags_to_prototypes_test.go +++ b/arduino/builder/preprocessor/internal/ctags/ctags_to_prototypes_test.go @@ -20,18 +20,16 @@ import ( "path/filepath" "testing" - "github.com/arduino/arduino-cli/legacy/builder/types" paths "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" ) -func producePrototypes(t *testing.T, filename string, mainFile string) ([]*types.Prototype, int) { - bytes, err := os.ReadFile(filepath.Join("test_data", filename)) +func producePrototypes(t *testing.T, filename string, mainFile string) ([]*Prototype, int) { + bytes, err := os.ReadFile(filepath.Join("testdata", filename)) require.NoError(t, err) - parser := &CTagsParser{} - parser.Parse(string(bytes), paths.New(mainFile)) - return parser.GeneratePrototypes() + parser := &Parser{} + return parser.Parse(bytes, paths.New(mainFile)) } func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) { diff --git a/legacy/builder/ctags/test_data/TestCTagsParserClassMembersAreFilteredOut.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserClassMembersAreFilteredOut.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserClassMembersAreFilteredOut.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserClassMembersAreFilteredOut.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserDefaultArguments.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserDefaultArguments.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserDefaultArguments.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserDefaultArguments.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserFunctionPointer.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointer.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserFunctionPointer.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointer.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserFunctionPointers.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointers.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserFunctionPointers.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointers.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserFunctionPointersNoIndirect.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointersNoIndirect.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserFunctionPointersNoIndirect.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserFunctionPointersNoIndirect.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserNamespace.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserNamespace.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserNamespace.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserNamespace.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealFunctionWithDifferentSignatures.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithClasses.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithClasses.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithClasses.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithClasses.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithMacros.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithMacros.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithMacros.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithMacros.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithStructs.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithStructs.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldDealWithStructs.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldDealWithStructs.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldListPrototypes.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListPrototypes.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldListPrototypes.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListPrototypes.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldListTemplates.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListTemplates.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldListTemplates.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListTemplates.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserShouldListTemplates2.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListTemplates2.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserShouldListTemplates2.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserShouldListTemplates2.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserStatic.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserStatic.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserStatic.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserStatic.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsParserStructWithFunctions.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserStructWithFunctions.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsParserStructWithFunctions.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsParserStructWithFunctions.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsRunnerSketchWithClassFunction.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsRunnerSketchWithClassFunction.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsRunnerSketchWithClassFunction.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsRunnerSketchWithClassFunction.txt diff --git a/legacy/builder/ctags/test_data/TestCTagsRunnerSketchWithMultifile.txt b/arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsRunnerSketchWithMultifile.txt similarity index 100% rename from legacy/builder/ctags/test_data/TestCTagsRunnerSketchWithMultifile.txt rename to arduino/builder/preprocessor/internal/ctags/testdata/TestCTagsRunnerSketchWithMultifile.txt diff --git a/arduino/builder/sketch.go b/arduino/builder/sketch.go index b5a6a97abb9..a79bbac4bef 100644 --- a/arduino/builder/sketch.go +++ b/arduino/builder/sketch.go @@ -19,8 +19,8 @@ import ( "bytes" "fmt" "regexp" - "strings" + "github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/i18n" "github.com/arduino/go-paths-helper" @@ -34,13 +34,19 @@ var ( tr = i18n.Tr ) -// 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 + "\"" +// 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). +func PrepareSketchBuildPath(sketch *sketch.Sketch, sourceOverrides map[string]string, buildPath *paths.Path) (int, error) { + if offset, mergedSource, err := sketchMergeSources(sketch, sourceOverrides); err != nil { + return 0, err + } else if err := SketchSaveItemCpp(sketch.MainFile, []byte(mergedSource), buildPath); err != nil { + return 0, err + } else if err := sketchCopyAdditionalFiles(sketch, buildPath, sourceOverrides); err != nil { + return 0, err + } else { + return offset, nil + } } // SketchSaveItemCpp saves a preprocessed .cpp sketch file on disk @@ -59,8 +65,9 @@ func SketchSaveItemCpp(path *paths.Path, contents []byte, destPath *paths.Path) return nil } -// SketchMergeSources merges all the source files included in a sketch -func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, string, error) { +// sketchMergeSources merges all the .ino source files included in a sketch to produce +// a single .cpp file. +func sketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, string, error) { lineOffset := 0 mergedSource := "" @@ -89,7 +96,7 @@ func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, st lineOffset++ } - mergedSource += "#line 1 " + QuoteCppString(sk.MainFile.String()) + "\n" + mergedSource += "#line 1 " + cpp.QuoteString(sk.MainFile.String()) + "\n" mergedSource += mainSrc + "\n" lineOffset++ @@ -98,16 +105,16 @@ func SketchMergeSources(sk *sketch.Sketch, overrides map[string]string) (int, st if err != nil { return 0, "", err } - mergedSource += "#line 1 " + QuoteCppString(file.String()) + "\n" + mergedSource += "#line 1 " + cpp.QuoteString(file.String()) + "\n" mergedSource += src + "\n" } 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. -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 { return errors.Wrap(err, tr("unable to create a folder to save the sketch files")) } @@ -138,7 +145,7 @@ func SketchCopyAdditionalFiles(sketch *sketch.Sketch, destPath *paths.Path, over } // tag each addtional file with the filename of the source it was copied from - sourceBytes = append([]byte("#line 1 "+QuoteCppString(file.String())+"\n"), sourceBytes...) + sourceBytes = append([]byte("#line 1 "+cpp.QuoteString(file.String())+"\n"), sourceBytes...) err = writeIfDifferent(sourceBytes, targetPath) if err != nil { diff --git a/arduino/builder/sketch_test.go b/arduino/builder/sketch_test.go index a8f1cb6b1b0..5466cdff819 100644 --- a/arduino/builder/sketch_test.go +++ b/arduino/builder/sketch_test.go @@ -13,7 +13,7 @@ // 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_test +package builder import ( "fmt" @@ -23,7 +23,6 @@ import ( "strings" "testing" - "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" @@ -48,7 +47,7 @@ func TestSaveSketch(t *testing.T) { 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() if err != nil { @@ -82,7 +81,7 @@ func TestMergeSketchSources(t *testing.T) { } 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.Equal(t, 2, offset) require.Equal(t, mergedSources, source) @@ -94,7 +93,7 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) { require.NotNil(t, s) // 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.Equal(t, 1, strings.Count(source, "")) } @@ -110,7 +109,7 @@ func TestCopyAdditionalFiles(t *testing.T) { // copy the sketch over, create a fake main file we don't care about it // but we need it for `SketchLoad` to succeed later - err = builder.SketchCopyAdditionalFiles(s1, tmp, nil) + err = sketchCopyAdditionalFiles(s1, tmp, nil) require.Nil(t, err) fakeIno := tmp.Join(fmt.Sprintf("%s.ino", tmp.Base())) require.Nil(t, fakeIno.WriteFile([]byte{})) @@ -125,7 +124,7 @@ func TestCopyAdditionalFiles(t *testing.T) { require.Nil(t, err) // copy again - err = builder.SketchCopyAdditionalFiles(s1, tmp, nil) + err = sketchCopyAdditionalFiles(s1, tmp, nil) require.Nil(t, err) // verify file hasn't changed diff --git a/executils/process.go b/executils/process.go index 6044275af85..7e0ac135089 100644 --- a/executils/process.go +++ b/executils/process.go @@ -16,6 +16,7 @@ package executils import ( + "bytes" "context" "io" "os" @@ -174,3 +175,15 @@ func (p *Process) RunWithinContext(ctx context.Context) error { }() return p.Wait() } + +// RunAndCaptureOutput starts the specified command and waits for it to complete. If the given context +// is canceled before the normal process termination, the process is killed. The standard output and +// standard error of the process are captured and returned at process termination. +func (p *Process) RunAndCaptureOutput(ctx context.Context) ([]byte, []byte, error) { + stdout := &bytes.Buffer{} + stderr := &bytes.Buffer{} + p.RedirectStdoutTo(stdout) + p.RedirectStderrTo(stderr) + err := p.RunWithinContext(ctx) + return stdout.Bytes(), stderr.Bytes(), err +} diff --git a/internal/algorithms/slices.go b/internal/algorithms/slices.go new file mode 100644 index 00000000000..4cb2b9f8cea --- /dev/null +++ b/internal/algorithms/slices.go @@ -0,0 +1,53 @@ +// 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 f + +// Filter takes a slice of type []T and a function of type func(T) bool. It returns +// a newly allocated slice containing only those elements of the input slice that +// satisfy the test function. +func Filter[T any](values []T, test func(x T) bool) []T { + res := []T{} + for _, x := range values { + if test(x) { + res = append(res, x) + } + } + return res +} + +// Map applies the mapping function to each element of the slice and returns a new +// slice with the results in the same order. +func Map[T any](values []T, mapping func(x T) T) []T { + res := []T{} + for _, x := range values { + res = append(res, mapping(x)) + } + return res +} + +// Equals return a functor that matches the given value +func Equals[T comparable](value T) func(x T) bool { + return func(x T) bool { + return x == value + } +} + +// NotEquals return a functor that does not match the given value +func NotEquals[T comparable](value T) func(x T) bool { + return func(x T) bool { + return x != value + } +} diff --git a/internal/algorithms/slices_test.go b/internal/algorithms/slices_test.go new file mode 100644 index 00000000000..8ef58c660a9 --- /dev/null +++ b/internal/algorithms/slices_test.go @@ -0,0 +1,53 @@ +// 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 f_test + +import ( + "strings" + "testing" + + f "github.com/arduino/arduino-cli/internal/algorithms" + "github.com/stretchr/testify/require" +) + +func TestFilter(t *testing.T) { + a := []string{"aaa", "bbb", "ccc"} + require.Equal(t, []string{"bbb", "ccc"}, f.Filter(a, func(x string) bool { return x > "b" })) + b := []int{5, 9, 15, 2, 4, -2} + require.Equal(t, []int{5, 9, 15}, f.Filter(b, func(x int) bool { return x > 4 })) +} + +func TestIsEmpty(t *testing.T) { + require.True(t, f.Equals(int(0))(0)) + require.False(t, f.Equals(int(1))(0)) + require.True(t, f.Equals("")("")) + require.False(t, f.Equals("abc")("")) + require.False(t, f.NotEquals(int(0))(0)) + require.True(t, f.NotEquals(int(1))(0)) + require.False(t, f.NotEquals("")("")) + require.True(t, f.NotEquals("abc")("")) +} + +func TestMap(t *testing.T) { + value := "hello, world , how are,you? " + parts := f.Map(strings.Split(value, ","), strings.TrimSpace) + + require.Equal(t, 4, len(parts)) + require.Equal(t, "hello", parts[0]) + require.Equal(t, "world", parts[1]) + require.Equal(t, "how are", parts[2]) + require.Equal(t, "you?", parts[3]) +} diff --git a/internal/integrationtest/compile_1/compile_test.go b/internal/integrationtest/compile_1/compile_test.go index dcfb8736f19..60c6552a678 100644 --- a/internal/integrationtest/compile_1/compile_test.go +++ b/internal/integrationtest/compile_1/compile_test.go @@ -25,7 +25,7 @@ import ( "testing" "time" - "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/internal/integrationtest" "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" @@ -1060,7 +1060,6 @@ func compileWithRelativeBuildPath(t *testing.T, env *integrationtest.Environment "core", "includes.cache", "libraries", - "preproc", "sketch", } @@ -1191,7 +1190,7 @@ void loop() { } ` - expected = strings.ReplaceAll(expected, "%SKETCH_PATH%", builder.QuoteCppString(sketchPath.Join("SketchSimple.ino").String())) + expected = strings.ReplaceAll(expected, "%SKETCH_PATH%", cpp.QuoteString(sketchPath.Join("SketchSimple.ino").String())) jsonOut, _, err := cli.Run("compile", "-b", fqbn, "--preprocess", sketchPath.String(), "--format", "json") require.NoError(t, err) diff --git a/legacy/builder/add_additional_entries_to_context.go b/legacy/builder/add_additional_entries_to_context.go index e6ee138855f..9e9fdd80669 100644 --- a/legacy/builder/add_additional_entries_to_context.go +++ b/legacy/builder/add_additional_entries_to_context.go @@ -26,10 +26,6 @@ type AddAdditionalEntriesToContext struct{} func (*AddAdditionalEntriesToContext) Run(ctx *types.Context) error { if ctx.BuildPath != nil { buildPath := ctx.BuildPath - preprocPath, err := buildPath.Join(constants.FOLDER_PREPROC).Abs() - if err != nil { - return errors.WithStack(err) - } sketchBuildPath, err := buildPath.Join(constants.FOLDER_SKETCH).Abs() if err != nil { return errors.WithStack(err) @@ -43,7 +39,6 @@ func (*AddAdditionalEntriesToContext) Run(ctx *types.Context) error { return errors.WithStack(err) } - ctx.PreprocPath = preprocPath ctx.SketchBuildPath = sketchBuildPath ctx.LibrariesBuildPath = librariesBuildPath ctx.CoreBuildPath = coreBuildPath @@ -53,8 +48,6 @@ func (*AddAdditionalEntriesToContext) Run(ctx *types.Context) error { ctx.WarningsLevel = DEFAULT_WARNINGS_LEVEL } - ctx.CollectedSourceFiles = &types.UniqueSourceFileQueue{} - ctx.LibrariesResolutionResults = map[string]types.LibraryResolutionResult{} return nil diff --git a/legacy/builder/builder.go b/legacy/builder/builder.go index d73ec778ca7..fe11d684faa 100644 --- a/legacy/builder/builder.go +++ b/legacy/builder/builder.go @@ -19,6 +19,8 @@ import ( "reflect" "time" + "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor" "github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/legacy/builder/phases" "github.com/arduino/arduino-cli/legacy/builder/types" @@ -39,6 +41,7 @@ func (s *Builder) Run(ctx *types.Context) error { return err } + var _err error commands := []types.Command{ &ContainerSetupHardwareToolsLibsSketchAndProps{}, @@ -46,7 +49,10 @@ func (s *Builder) Run(ctx *types.Context) error { &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"}, - &ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = builder.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), utils.LogIfVerbose(false, tr("Detecting libraries used...")), &ContainerFindIncludes{}, @@ -54,7 +60,7 @@ func (s *Builder) Run(ctx *types.Context) error { &WarnAboutArchIncompatibleLibraries{}, utils.LogIfVerbose(false, tr("Generating function prototypes...")), - &PreprocessSketch{}, + types.BareCommand(PreprocessSketch), utils.LogIfVerbose(false, tr("Compiling sketch...")), &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.sketch.prebuild", Suffix: ".pattern"}, @@ -110,13 +116,19 @@ func (s *Builder) Run(ctx *types.Context) error { return otherErr } -type PreprocessSketch struct{} - -func (s *PreprocessSketch) Run(ctx *types.Context) error { +func PreprocessSketch(ctx *types.Context) error { if ctx.UseArduinoPreprocessor { return PreprocessSketchWithArduinoPreprocessor(ctx) } else { - return PreprocessSketchWithCtags(ctx) + normalOutput, verboseOutput, err := preprocessor.PreprocessSketchWithCtags( + ctx.Sketch, ctx.BuildPath, ctx.IncludeFolders, ctx.LineOffset, + ctx.BuildProperties, ctx.OnlyUpdateCompilationDatabase) + if ctx.Verbose { + ctx.WriteStdout(verboseOutput) + } else { + ctx.WriteStdout(normalOutput) + } + return err } } @@ -127,6 +139,7 @@ func (s *Preprocess) Run(ctx *types.Context) error { return err } + var _err error commands := []types.Command{ &ContainerSetupHardwareToolsLibsSketchAndProps{}, @@ -134,13 +147,16 @@ func (s *Preprocess) Run(ctx *types.Context) error { &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.prebuild", Suffix: ".pattern"}, - &ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = builder.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &ContainerFindIncludes{}, &WarnAboutArchIncompatibleLibraries{}, - &PreprocessSketch{}, + types.BareCommand(PreprocessSketch), } if err := runCommands(ctx, commands); err != nil { @@ -148,7 +164,11 @@ func (s *Preprocess) Run(ctx *types.Context) error { } // Output arduino-preprocessed source - ctx.WriteStdout([]byte(ctx.Source)) + preprocessedSketch, err := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp").ReadFile() + if err != nil { + return err + } + ctx.WriteStdout(preprocessedSketch) return nil } diff --git a/legacy/builder/builder_utils/utils.go b/legacy/builder/builder_utils/utils.go index 44cc55c90af..5c25d2c86c0 100644 --- a/legacy/builder/builder_utils/utils.go +++ b/legacy/builder/builder_utils/utils.go @@ -26,6 +26,7 @@ import ( "github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/i18n" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/utils" @@ -262,10 +263,10 @@ func ObjFileIsUpToDate(sourceFile, objectFile, dependencyFile *paths.Path) (bool return false, errors.WithStack(err) } - rows = utils.Map(rows, removeEndingBackSlash) - rows = utils.Map(rows, strings.TrimSpace) - rows = utils.Map(rows, unescapeDep) - rows = utils.Filter(rows, nonEmptyString) + rows = f.Map(rows, removeEndingBackSlash) + rows = f.Map(rows, strings.TrimSpace) + rows = f.Map(rows, unescapeDep) + rows = f.Filter(rows, f.NotEquals("")) if len(rows) == 0 { return true, nil @@ -327,10 +328,6 @@ func removeEndingBackSlash(s string) string { return strings.TrimSuffix(s, "\\") } -func nonEmptyString(s string) bool { - return s != constants.EMPTY_STRING -} - func CoreOrReferencedCoreHasChanged(corePath, targetCorePath, targetFile *paths.Path) bool { targetFileStat, err := targetFile.Stat() diff --git a/legacy/builder/constants/constants.go b/legacy/builder/constants/constants.go index 36d11764ebc..a91f454ffcf 100644 --- a/legacy/builder/constants/constants.go +++ b/legacy/builder/constants/constants.go @@ -34,7 +34,6 @@ const BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH = "runtime.platform.path" const EMPTY_STRING = "" const FOLDER_BOOTLOADERS = "bootloaders" const FOLDER_CORE = "core" -const FOLDER_PREPROC = "preproc" const FOLDER_SKETCH = "sketch" const FOLDER_TOOLS = "tools" const FOLDER_LIBRARIES = "libraries" diff --git a/legacy/builder/container_add_prototypes.go b/legacy/builder/container_add_prototypes.go deleted file mode 100644 index d710b82c979..00000000000 --- a/legacy/builder/container_add_prototypes.go +++ /dev/null @@ -1,71 +0,0 @@ -// 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 ( - "fmt" - - bldr "github.com/arduino/arduino-cli/arduino/builder" - "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/pkg/errors" -) - -func PreprocessSketchWithCtags(ctx *types.Context) error { - // Generate the full pathname for the preproc output file - if err := ctx.PreprocPath.MkdirAll(); err != nil { - return errors.WithStack(err) - } - targetFilePath := ctx.PreprocPath.Join("ctags_target_for_gcc_minus_e.cpp") - - // Run preprocessor - sourceFile := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp") - if err := GCCPreprocRunner(ctx, sourceFile, targetFilePath, ctx.IncludeFolders); err != nil { - if !ctx.OnlyUpdateCompilationDatabase { - return errors.WithStack(err) - } - - // Do not bail out if we are generating the compile commands database - ctx.Info( - fmt.Sprintf("%s: %s", - tr("An error occurred adding prototypes"), - tr("the compilation database may be incomplete or inaccurate"))) - if err := sourceFile.CopyTo(targetFilePath); err != nil { - return errors.WithStack(err) - } - } - - commands := []types.Command{ - &ReadFileAndStoreInContext{FileToRead: targetFilePath, Target: &ctx.SourceGccMinusE}, - &FilterSketchSource{Source: &ctx.SourceGccMinusE}, - &CTagsTargetFileSaver{Source: &ctx.SourceGccMinusE, TargetFileName: "ctags_target_for_gcc_minus_e.cpp"}, - &CTagsRunner{}, - &PrototypesAdder{}, - } - - for _, command := range commands { - PrintRingNameIfDebug(ctx, command) - err := command.Run(ctx) - if err != nil { - return errors.WithStack(err) - } - } - - if err := bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.Source), ctx.SketchBuildPath); err != nil { - return errors.WithStack(err) - } - - return nil -} diff --git a/legacy/builder/container_find_includes.go b/legacy/builder/container_find_includes.go index 189c3ff7b2c..e7b8cbe0994 100644 --- a/legacy/builder/container_find_includes.go +++ b/legacy/builder/container_find_includes.go @@ -98,6 +98,7 @@ import ( "os/exec" "time" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor" "github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" @@ -143,23 +144,24 @@ func (s *ContainerFindIncludes) findIncludes(ctx *types.Context) error { appendIncludeFolder(ctx, cache, nil, "", ctx.BuildProperties.GetPath("build.variant.path")) } + sourceFileQueue := &types.UniqueSourceFileQueue{} + if !ctx.UseCachedLibrariesResolution { sketch := ctx.Sketch mergedfile, err := types.MakeSourceFile(ctx, sketch, paths.New(sketch.MainFile.Base()+".cpp")) if err != nil { return errors.WithStack(err) } - ctx.CollectedSourceFiles.Push(mergedfile) + sourceFileQueue.Push(mergedfile) - sourceFilePaths := ctx.CollectedSourceFiles - queueSourceFilesFromFolder(ctx, sourceFilePaths, sketch, ctx.SketchBuildPath, false /* recurse */) + queueSourceFilesFromFolder(ctx, sourceFileQueue, sketch, ctx.SketchBuildPath, false /* recurse */) srcSubfolderPath := ctx.SketchBuildPath.Join("src") if srcSubfolderPath.IsDir() { - queueSourceFilesFromFolder(ctx, sourceFilePaths, sketch, srcSubfolderPath, true /* recurse */) + queueSourceFilesFromFolder(ctx, sourceFileQueue, sketch, srcSubfolderPath, true /* recurse */) } - for !sourceFilePaths.Empty() { - err := findIncludesUntilDone(ctx, cache, sourceFilePaths.Pop()) + for !sourceFileQueue.Empty() { + err := findIncludesUntilDone(ctx, cache, sourceFileQueue) if err != nil { cachePath.Remove() return errors.WithStack(err) @@ -314,7 +316,8 @@ func writeCache(cache *includeCache, path *paths.Path) error { return nil } -func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile types.SourceFile) error { +func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFileQueue *types.UniqueSourceFileQueue) error { + sourceFile := sourceFileQueue.Pop() sourcePath := sourceFile.SourcePath(ctx) targetFilePath := paths.NullPath() depPath := sourceFile.DepfilePath(ctx) @@ -367,7 +370,12 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t ctx.Info(tr("Using cached library dependencies for file: %[1]s", sourcePath)) } } else { - preproc_stderr, preproc_err = GCCPreprocRunnerForDiscoveringIncludes(ctx, sourcePath, targetFilePath, includes) + var preproc_stdout []byte + preproc_stdout, preproc_stderr, preproc_err = preprocessor.GCC(sourcePath, targetFilePath, includes, ctx.BuildProperties) + if ctx.Verbose { + ctx.WriteStdout(preproc_stdout) + ctx.WriteStdout(preproc_stderr) + } // Unwrap error and see if it is an ExitError. _, is_exit_error := errors.Cause(preproc_err).(*exec.ExitError) if preproc_err == nil { @@ -395,11 +403,13 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t library := ResolveLibrary(ctx, include) if library == nil { // Library could not be resolved, show error - // err := runCommand(ctx, &GCCPreprocRunner{SourceFilePath: sourcePath, TargetFileName: paths.New(constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E), Includes: includes}) - // return errors.WithStack(err) if preproc_err == nil || preproc_stderr == nil { // Filename came from cache, so run preprocessor to obtain error to show - preproc_stderr, preproc_err = GCCPreprocRunnerForDiscoveringIncludes(ctx, sourcePath, targetFilePath, includes) + var preproc_stdout []byte + preproc_stdout, preproc_stderr, preproc_err = preprocessor.GCC(sourcePath, targetFilePath, includes, ctx.BuildProperties) + if ctx.Verbose { + ctx.WriteStdout(preproc_stdout) + } if preproc_err == nil { // If there is a missing #include in the cache, but running // gcc does not reproduce that, there is something wrong. @@ -419,13 +429,13 @@ func findIncludesUntilDone(ctx *types.Context, cache *includeCache, sourceFile t appendIncludeFolder(ctx, cache, sourcePath, include, library.SourceDir) sourceDirs := library.SourceDirs() for _, sourceDir := range sourceDirs { - queueSourceFilesFromFolder(ctx, ctx.CollectedSourceFiles, library, sourceDir.Dir, sourceDir.Recurse) + queueSourceFilesFromFolder(ctx, sourceFileQueue, library, sourceDir.Dir, sourceDir.Recurse) } first = false } } -func queueSourceFilesFromFolder(ctx *types.Context, queue *types.UniqueSourceFileQueue, origin interface{}, folder *paths.Path, recurse bool) error { +func queueSourceFilesFromFolder(ctx *types.Context, sourceFileQueue *types.UniqueSourceFileQueue, origin interface{}, folder *paths.Path, recurse bool) error { sourceFileExtensions := []string{} for k := range globals.SourceFilesValidExtensions { sourceFileExtensions = append(sourceFileExtensions, k) @@ -440,7 +450,7 @@ func queueSourceFilesFromFolder(ctx *types.Context, queue *types.UniqueSourceFil if err != nil { return errors.WithStack(err) } - queue.Push(sourceFile) + sourceFileQueue.Push(sourceFile) } return nil diff --git a/legacy/builder/container_merge_copy_sketch_files.go b/legacy/builder/container_merge_copy_sketch_files.go deleted file mode 100644 index b98eaebb721..00000000000 --- a/legacy/builder/container_merge_copy_sketch_files.go +++ /dev/null @@ -1,43 +0,0 @@ -// 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 -} diff --git a/legacy/builder/create_cmake_rule.go b/legacy/builder/create_cmake_rule.go index b7640ca58be..9cc38434286 100644 --- a/legacy/builder/create_cmake_rule.go +++ b/legacy/builder/create_cmake_rule.go @@ -22,6 +22,7 @@ import ( "strings" properties "github.com/arduino/go-properties-orderedmap" + "golang.org/x/exp/slices" "github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" @@ -114,18 +115,10 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error { fmt.Println(err) } - if err := PreprocessSketchWithCtags(ctx); err != nil { + if err := PreprocessSketch(ctx); err != nil { return err } - // Use old ctags method to generate export file - commands := []types.Command{ - &FilterSketchSource{Source: &ctx.Source, RemoveLineMarkers: true}, - } - for _, command := range commands { - command.Run(ctx) - } - err = utils.CopyDir(ctx.SketchBuildPath.String(), cmakeFolder.Join("sketch").String(), validExportExtensions) if err != nil { fmt.Println(err) @@ -213,7 +206,7 @@ func (s *ExportProjectCMake) Run(ctx *types.Context) error { lib := staticLib.Base() lib = strings.TrimPrefix(lib, "lib") lib = strings.TrimSuffix(lib, ".a") - if !utils.SliceContains(dynamicLibsFromGccMinusL, lib) { + if !slices.Contains(dynamicLibsFromGccMinusL, lib) { linkGroup += " " + lib cmakelist += "add_library (" + lib + " STATIC IMPORTED)\n" location := strings.TrimPrefix(staticLib.String(), cmakeFolder.String()) @@ -268,7 +261,7 @@ func findUniqueFoldersRelative(slice []string, base string) string { for _, element := range slice { path := filepath.Dir(element) path = strings.TrimPrefix(path, base+"/") - if !utils.SliceContains(out, path) { + if !slices.Contains(out, path) { out = append(out, path) } } diff --git a/legacy/builder/ctags_runner.go b/legacy/builder/ctags_runner.go deleted file mode 100644 index 2e42bdca234..00000000000 --- a/legacy/builder/ctags_runner.go +++ /dev/null @@ -1,74 +0,0 @@ -// 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 ( - "os" - "os/exec" - - "github.com/arduino/arduino-cli/legacy/builder/ctags" - "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" - properties "github.com/arduino/go-properties-orderedmap" - "github.com/pkg/errors" -) - -type CTagsRunner struct{} - -func (s *CTagsRunner) Run(ctx *types.Context) error { - ctagsTargetFilePath := ctx.CTagsTargetFile - - buildProperties := properties.NewMap() - buildProperties.Set("tools.ctags.path", "{runtime.tools.ctags.path}") - buildProperties.Set("tools.ctags.cmd.path", "{path}/ctags") - buildProperties.Set("tools.ctags.pattern", `"{cmd.path}" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "{source_file}"`) - buildProperties.Merge(ctx.BuildProperties) - buildProperties.Merge(buildProperties.SubTree("tools").SubTree("ctags")) - buildProperties.SetPath("source_file", ctagsTargetFilePath) - - pattern := buildProperties.Get("pattern") - if pattern == "" { - return errors.Errorf(tr("%s pattern is missing"), "ctags") - } - - commandLine := buildProperties.ExpandPropsInString(pattern) - parts, err := properties.SplitQuotedString(commandLine, `"'`, false) - if err != nil { - return errors.WithStack(err) - } - command := exec.Command(parts[0], parts[1:]...) - command.Env = append(os.Environ(), ctx.PackageManager.GetEnvVarsForSpawnedProcess()...) - - sourceBytes, _, err := utils.ExecCommand(ctx, command, utils.Capture /* stdout */, utils.ShowIfVerbose /* stderr */) - if err != nil { - return errors.WithStack(err) - } - - ctx.CTagsOutput = string(sourceBytes) - - parser := &ctags.CTagsParser{} - - ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile) - parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource) - - protos, line := parser.GeneratePrototypes() - if line != -1 { - ctx.PrototypesLineWhereToInsert = line - } - ctx.Prototypes = protos - - return nil -} diff --git a/legacy/builder/ctags_target_file_saver.go b/legacy/builder/ctags_target_file_saver.go index a4014662c41..261bc7c759a 100644 --- a/legacy/builder/ctags_target_file_saver.go +++ b/legacy/builder/ctags_target_file_saver.go @@ -14,30 +14,3 @@ // 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/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 -} diff --git a/legacy/builder/filter_sketch_source.go b/legacy/builder/filter_sketch_source.go deleted file mode 100644 index 507615a55b2..00000000000 --- a/legacy/builder/filter_sketch_source.go +++ /dev/null @@ -1,90 +0,0 @@ -// 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 -} diff --git a/legacy/builder/gcc_preproc_runner.go b/legacy/builder/gcc_preproc_runner.go deleted file mode 100644 index c1118d5889c..00000000000 --- a/legacy/builder/gcc_preproc_runner.go +++ /dev/null @@ -1,90 +0,0 @@ -// 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 ( - "os/exec" - "strings" - - "github.com/arduino/arduino-cli/legacy/builder/builder_utils" - "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" - "github.com/arduino/go-paths-helper" - properties "github.com/arduino/go-properties-orderedmap" - "github.com/pkg/errors" -) - -func GCCPreprocRunner(ctx *types.Context, sourceFilePath *paths.Path, targetFilePath *paths.Path, includes paths.PathList) error { - cmd, err := prepareGCCPreprocRecipeProperties(ctx, sourceFilePath, targetFilePath, includes) - if err != nil { - return errors.WithStack(err) - } - - _, _, err = utils.ExecCommand(ctx, cmd /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show) - if err != nil { - return errors.WithStack(err) - } - - return nil -} - -func GCCPreprocRunnerForDiscoveringIncludes(ctx *types.Context, sourceFilePath *paths.Path, targetFilePath *paths.Path, includes paths.PathList) ([]byte, error) { - cmd, err := prepareGCCPreprocRecipeProperties(ctx, sourceFilePath, targetFilePath, includes) - if err != nil { - return nil, errors.WithStack(err) - } - - _, stderr, err := utils.ExecCommand(ctx, cmd /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Capture) - if err != nil { - return stderr, errors.WithStack(err) - } - - return stderr, nil -} - -func prepareGCCPreprocRecipeProperties(ctx *types.Context, sourceFilePath *paths.Path, targetFilePath *paths.Path, includes paths.PathList) (*exec.Cmd, error) { - buildProperties := properties.NewMap() - buildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC") - buildProperties.Merge(ctx.BuildProperties) - buildProperties.Set("build.library_discovery_phase", "1") - buildProperties.SetPath("source_file", sourceFilePath) - buildProperties.SetPath("preprocessed_file_path", targetFilePath) - - includesStrings := utils.Map(includes.AsStrings(), utils.WrapWithHyphenI) - buildProperties.Set("includes", strings.Join(includesStrings, " ")) - - if buildProperties.Get("recipe.preproc.macros") == "" { - // autogenerate preprocess macros recipe from compile recipe - preprocPattern := buildProperties.Get("recipe.cpp.o.pattern") - // add {preproc.macros.flags} to {compiler.cpp.flags} - preprocPattern = strings.Replace(preprocPattern, "{compiler.cpp.flags}", "{compiler.cpp.flags} {preproc.macros.flags}", 1) - // replace "{object_file}" with "{preprocessed_file_path}" - preprocPattern = strings.Replace(preprocPattern, "{object_file}", "{preprocessed_file_path}", 1) - - buildProperties.Set("recipe.preproc.macros", preprocPattern) - } - - cmd, err := builder_utils.PrepareCommandForRecipe(buildProperties, "recipe.preproc.macros", true, ctx.PackageManager.GetEnvVarsForSpawnedProcess()) - if err != nil { - return nil, errors.WithStack(err) - } - - // Remove -MMD argument if present. Leaving it will make gcc try - // to create a /dev/null.d dependency file, which won't work. - cmd.Args = utils.Filter(cmd.Args, func(a string) bool { return a != "-MMD" }) - - return cmd, nil -} diff --git a/legacy/builder/phases/core_builder.go b/legacy/builder/phases/core_builder.go index a484c335f7b..e60af767b42 100644 --- a/legacy/builder/phases/core_builder.go +++ b/legacy/builder/phases/core_builder.go @@ -22,6 +22,7 @@ import ( "github.com/arduino/arduino-cli/buildcache" "github.com/arduino/arduino-cli/i18n" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/types" @@ -77,7 +78,7 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path if variantFolder != nil && variantFolder.IsDir() { includes = append(includes, variantFolder.String()) } - includes = utils.Map(includes, utils.WrapWithHyphenI) + includes = f.Map(includes, utils.WrapWithHyphenI) var err error diff --git a/legacy/builder/phases/libraries_builder.go b/legacy/builder/phases/libraries_builder.go index 32bb0b2f01c..5ee81bb4385 100644 --- a/legacy/builder/phases/libraries_builder.go +++ b/legacy/builder/phases/libraries_builder.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/arduino/arduino-cli/arduino/libraries" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/types" @@ -36,7 +37,7 @@ type LibrariesBuilder struct{} func (s *LibrariesBuilder) Run(ctx *types.Context) error { librariesBuildPath := ctx.LibrariesBuildPath buildProperties := ctx.BuildProperties - includes := utils.Map(ctx.IncludeFolders.AsStrings(), utils.WrapWithHyphenI) + includes := f.Map(ctx.IncludeFolders.AsStrings(), utils.WrapWithHyphenI) libs := ctx.ImportedLibraries if err := librariesBuildPath.MkdirAll(); err != nil { diff --git a/legacy/builder/phases/linker.go b/legacy/builder/phases/linker.go index 0f2f76d336e..cc7c293199d 100644 --- a/legacy/builder/phases/linker.go +++ b/legacy/builder/phases/linker.go @@ -18,6 +18,7 @@ package phases import ( "strings" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/types" @@ -64,7 +65,7 @@ func (s *Linker) Run(ctx *types.Context) error { } func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths.Path, coreArchiveFilePath *paths.Path, buildProperties *properties.Map) error { - objectFileList := strings.Join(utils.Map(objectFiles.AsStrings(), wrapWithDoubleQuotes), " ") + objectFileList := strings.Join(f.Map(objectFiles.AsStrings(), wrapWithDoubleQuotes), " ") // If command line length is too big (> 30000 chars), try to collect the object files into archives // and use that archives to complete the build. @@ -102,7 +103,7 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths } } - objectFileList = strings.Join(utils.Map(archives.AsStrings(), wrapWithDoubleQuotes), " ") + objectFileList = strings.Join(f.Map(archives.AsStrings(), wrapWithDoubleQuotes), " ") objectFileList = "-Wl,--whole-archive " + objectFileList + " -Wl,--no-whole-archive" } diff --git a/legacy/builder/phases/sketch_builder.go b/legacy/builder/phases/sketch_builder.go index d60f8ab9a0c..300cb6836af 100644 --- a/legacy/builder/phases/sketch_builder.go +++ b/legacy/builder/phases/sketch_builder.go @@ -16,6 +16,7 @@ package phases import ( + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/utils" @@ -27,7 +28,7 @@ type SketchBuilder struct{} func (s *SketchBuilder) Run(ctx *types.Context) error { sketchBuildPath := ctx.SketchBuildPath buildProperties := ctx.BuildProperties - includes := utils.Map(ctx.IncludeFolders.AsStrings(), utils.WrapWithHyphenI) + includes := f.Map(ctx.IncludeFolders.AsStrings(), utils.WrapWithHyphenI) if err := sketchBuildPath.MkdirAll(); err != nil { return errors.WithStack(err) diff --git a/legacy/builder/preprocess_sketch.go b/legacy/builder/preprocess_sketch.go index 31c19ca3bc1..7dff77c7dde 100644 --- a/legacy/builder/preprocess_sketch.go +++ b/legacy/builder/preprocess_sketch.go @@ -23,6 +23,7 @@ import ( "runtime" bldr "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor" "github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/utils" properties "github.com/arduino/go-properties-orderedmap" @@ -30,14 +31,21 @@ import ( ) func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { - if err := ctx.PreprocPath.MkdirAll(); err != nil { + if err := ctx.BuildPath.Join("preproc").MkdirAll(); err != nil { return errors.WithStack(err) } 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.BuildPath.Join("preproc", "sketch_merged.cpp") + gccStdout, gccStderr, err := preprocessor.GCC(sourceFile, targetFile, ctx.IncludeFolders, ctx.BuildProperties) + if ctx.Verbose { + ctx.WriteStdout(gccStdout) + ctx.WriteStderr(gccStderr) + } + if err != nil { + return err + } - targetFilePath := ctx.PreprocPath.Join("ctags_target_for_gcc_minus_e.cpp") buildProperties := properties.NewMap() buildProperties.Set("tools.arduino-preprocessor.path", "{runtime.tools.arduino-preprocessor.path}") buildProperties.Set("tools.arduino-preprocessor.cmd.path", "{path}/arduino-preprocessor") @@ -45,7 +53,7 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { buildProperties.Set("preproc.macros.flags", "-w -x c++ -E -CC") buildProperties.Merge(ctx.BuildProperties) buildProperties.Merge(buildProperties.SubTree("tools").SubTree("arduino-preprocessor")) - buildProperties.SetPath("source_file", targetFilePath) + buildProperties.SetPath("source_file", targetFile) pattern := buildProperties.Get("pattern") if pattern == "" { @@ -77,9 +85,5 @@ func PreprocessSketchWithArduinoPreprocessor(ctx *types.Context) error { } result := utils.NormalizeUTF8(buf) - - //fmt.Printf("PREPROCESSOR OUTPUT:\n%s\n", output) - ctx.Source = string(result) - - return bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, []byte(ctx.Source), ctx.SketchBuildPath) + return bldr.SketchSaveItemCpp(ctx.Sketch.MainFile, result, ctx.SketchBuildPath) } diff --git a/legacy/builder/prototypes_adder.go b/legacy/builder/prototypes_adder.go index abc816b2763..261bc7c759a 100644 --- a/legacy/builder/prototypes_adder.go +++ b/legacy/builder/prototypes_adder.go @@ -14,93 +14,3 @@ // To purchase a commercial license, send an email to license@arduino.cc. package builder - -import ( - "fmt" - "github.com/arduino/arduino-cli/legacy/builder/constants" - "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" - "strconv" - "strings" -) - -type PrototypesAdder struct{} - -func (s *PrototypesAdder) Run(ctx *types.Context) error { - debugOutput := ctx.DebugPreprocessor - source := ctx.Source - - source = strings.Replace(source, "\r\n", "\n", -1) - source = strings.Replace(source, "\r", "\n", -1) - - sourceRows := strings.Split(source, "\n") - - firstFunctionLine := ctx.PrototypesLineWhereToInsert - if isFirstFunctionOutsideOfSource(firstFunctionLine, sourceRows) { - return nil - } - - insertionLine := firstFunctionLine + ctx.LineOffset - 1 - firstFunctionChar := len(strings.Join(sourceRows[:insertionLine], "\n")) + 1 - prototypeSection := composePrototypeSection(firstFunctionLine, ctx.Prototypes) - ctx.PrototypesSection = prototypeSection - source = source[:firstFunctionChar] + prototypeSection + source[firstFunctionChar:] - - if debugOutput { - fmt.Println("#PREPROCESSED SOURCE") - prototypesRows := strings.Split(prototypeSection, "\n") - prototypesRows = prototypesRows[:len(prototypesRows)-1] - for i := 0; i < len(sourceRows)+len(prototypesRows); i++ { - if i < insertionLine { - fmt.Printf(" |%s\n", sourceRows[i]) - } else if i < insertionLine+len(prototypesRows) { - fmt.Printf("PRO|%s\n", prototypesRows[i-insertionLine]) - } else { - fmt.Printf(" |%s\n", sourceRows[i-len(prototypesRows)]) - } - } - fmt.Println("#END OF PREPROCESSED SOURCE") - } - ctx.Source = source - - return nil -} - -func composePrototypeSection(line int, prototypes []*types.Prototype) string { - if len(prototypes) == 0 { - return constants.EMPTY_STRING - } - - str := joinPrototypes(prototypes) - str += "\n#line " - str += strconv.Itoa(line) - str += " " + utils.QuoteCppString(prototypes[0].File) - str += "\n" - - return str -} - -func joinPrototypes(prototypes []*types.Prototype) string { - prototypesSlice := []string{} - for _, proto := range prototypes { - if signatureContainsaDefaultArg(proto) { - continue - } - prototypesSlice = append(prototypesSlice, "#line "+strconv.Itoa(proto.Line)+" "+utils.QuoteCppString(proto.File)) - prototypeParts := []string{} - if proto.Modifiers != "" { - prototypeParts = append(prototypeParts, proto.Modifiers) - } - prototypeParts = append(prototypeParts, proto.Prototype) - prototypesSlice = append(prototypesSlice, strings.Join(prototypeParts, " ")) - } - return strings.Join(prototypesSlice, "\n") -} - -func signatureContainsaDefaultArg(proto *types.Prototype) bool { - return strings.Contains(proto.Prototype, "=") -} - -func isFirstFunctionOutsideOfSource(firstFunctionLine int, sourceRows []string) bool { - return firstFunctionLine > len(sourceRows)-1 -} diff --git a/legacy/builder/read_file_and_store_in_context.go b/legacy/builder/read_file_and_store_in_context.go deleted file mode 100644 index cdd75896387..00000000000 --- a/legacy/builder/read_file_and_store_in_context.go +++ /dev/null @@ -1,38 +0,0 @@ -// 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 -} diff --git a/legacy/builder/test/add_additional_entries_to_context_test.go b/legacy/builder/test/add_additional_entries_to_context_test.go index ab64c26ec4c..fc1edb5acb2 100644 --- a/legacy/builder/test/add_additional_entries_to_context_test.go +++ b/legacy/builder/test/add_additional_entries_to_context_test.go @@ -31,15 +31,12 @@ func TestAddAdditionalEntriesToContextNoBuildPath(t *testing.T) { command := builder.AddAdditionalEntriesToContext{} NoError(t, command.Run(ctx)) - require.Empty(t, ctx.PreprocPath) require.Empty(t, ctx.SketchBuildPath) require.Empty(t, ctx.LibrariesBuildPath) require.Empty(t, ctx.CoreBuildPath) require.NotNil(t, ctx.WarningsLevel) - require.True(t, ctx.CollectedSourceFiles.Empty()) - require.Equal(t, 0, len(ctx.LibrariesResolutionResults)) } @@ -50,14 +47,11 @@ func TestAddAdditionalEntriesToContextWithBuildPath(t *testing.T) { command := builder.AddAdditionalEntriesToContext{} NoError(t, command.Run(ctx)) - require.Equal(t, Abs(t, paths.New("folder", constants.FOLDER_PREPROC)), ctx.PreprocPath) require.Equal(t, Abs(t, paths.New("folder", constants.FOLDER_SKETCH)), ctx.SketchBuildPath) require.Equal(t, Abs(t, paths.New("folder", "libraries")), ctx.LibrariesBuildPath) require.Equal(t, Abs(t, paths.New("folder", constants.FOLDER_CORE)), ctx.CoreBuildPath) require.NotNil(t, ctx.WarningsLevel) - require.True(t, ctx.CollectedSourceFiles.Empty()) - require.Equal(t, 0, len(ctx.LibrariesResolutionResults)) } diff --git a/legacy/builder/test/builder_test.go b/legacy/builder/test/builder_test.go index 8e088eb50c1..3d5740d659d 100644 --- a/legacy/builder/test/builder_test.go +++ b/legacy/builder/test/builder_test.go @@ -116,9 +116,6 @@ func TestBuilderEmptySketch(t *testing.T) { exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch1.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) @@ -143,9 +140,6 @@ func TestBuilderBridge(t *testing.T) { exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) @@ -173,9 +167,6 @@ func TestBuilderSketchWithConfig(t *testing.T) { exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "sketch_with_config.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) @@ -208,9 +199,6 @@ func TestBuilderBridgeTwice(t *testing.T) { exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) @@ -245,9 +233,6 @@ func TestBuilderBridgeSAM(t *testing.T) { exist, err = buildPath.Join(constants.FOLDER_CORE, "avr", "dtostrf.c.d").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) @@ -286,9 +271,6 @@ func TestBuilderBridgeRedBearLab(t *testing.T) { exist, err := buildPath.Join(constants.FOLDER_CORE, "HardwareSerial.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) - exist, err = buildPath.Join(constants.FOLDER_PREPROC, "ctags_target_for_gcc_minus_e.cpp").ExistCheck() - NoError(t, err) - require.True(t, exist) exist, err = buildPath.Join(constants.FOLDER_SKETCH, "Bridge.ino.cpp.o").ExistCheck() NoError(t, err) require.True(t, exist) diff --git a/legacy/builder/test/ctags_runner_test.go b/legacy/builder/test/ctags_runner_test.go index 013499a461c..557aa34f57d 100644 --- a/legacy/builder/test/ctags_runner_test.go +++ b/legacy/builder/test/ctags_runner_test.go @@ -19,31 +19,37 @@ import ( "strings" "testing" + bldr "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor" "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 TestCTagsRunner(t *testing.T) { - sketchLocation := Abs(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino")) +func ctagsRunnerTestTemplate(t *testing.T, sketchLocation *paths.Path) []byte { ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true - commands := []types.Command{ - &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, - &builder.ContainerFindIncludes{}, - &builder.PrintUsedLibrariesIfVerbose{}, - &builder.WarnAboutArchIncompatibleLibraries{}, - &builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, - &builder.CTagsRunner{}, - } - for _, command := range commands { - err := command.Run(ctx) - NoError(t, err) - } + err := (&builder.ContainerSetupHardwareToolsLibsSketchAndProps{}).Run(ctx) + NoError(t, err) + + _, err = bldr.PrepareSketchBuildPath(ctx.Sketch, nil, ctx.SketchBuildPath) + NoError(t, err) + + source := loadPreprocessedSketch(t, ctx) + target := ctx.BuildPath.Join("ctags_target.cpp") + NoError(t, target.WriteFile([]byte(source))) + + ctagsOutput, _, err := preprocessor.RunCTags(target, ctx.BuildProperties) + NoError(t, err) + + return ctagsOutput +} + +func TestCTagsRunner(t *testing.T) { + sketchLocation := Abs(t, paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino")) + ctagsOutput := ctagsRunnerTestTemplate(t, sketchLocation) quotedSketchLocation := strings.Replace(sketchLocation.String(), "\\", "\\\\", -1) expectedOutput := "server " + quotedSketchLocation + " /^BridgeServer server;$/;\" kind:variable line:31\n" + @@ -53,29 +59,12 @@ func TestCTagsRunner(t *testing.T) { "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" + "modeCommand " + quotedSketchLocation + " /^void modeCommand(BridgeClient client) {$/;\" kind:function line:149 signature:(BridgeClient client) returntype:void\n" - - require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) + require.Equal(t, expectedOutput, strings.Replace(string(ctagsOutput), "\r\n", "\n", -1)) } func TestCTagsRunnerSketchWithClass(t *testing.T) { sketchLocation := Abs(t, paths.New("sketch_with_class", "sketch_with_class.ino")) - ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") - defer cleanUpBuilderTestContext(t, ctx) - ctx.Verbose = true - - commands := []types.Command{ - &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, - &builder.ContainerFindIncludes{}, - &builder.PrintUsedLibrariesIfVerbose{}, - &builder.WarnAboutArchIncompatibleLibraries{}, - &builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, - &builder.CTagsRunner{}, - } - for _, command := range commands { - err := command.Run(ctx) - NoError(t, err) - } + ctagsOutput := ctagsRunnerTestTemplate(t, sketchLocation) quotedSketchLocation := strings.Replace(sketchLocation.String(), "\\", "\\\\", -1) expectedOutput := "set_values\t" + quotedSketchLocation + "\t/^ void set_values (int,int);$/;\"\tkind:prototype\tline:4\tclass:Rectangle\tsignature:(int,int)\treturntype:void\n" + @@ -83,88 +72,40 @@ 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" + "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" - require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) + require.Equal(t, expectedOutput, strings.Replace(string(ctagsOutput), "\r\n", "\n", -1)) } func TestCTagsRunnerSketchWithTypename(t *testing.T) { sketchLocation := Abs(t, paths.New("sketch_with_typename", "sketch_with_typename.ino")) - ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") - defer cleanUpBuilderTestContext(t, ctx) - ctx.Verbose = true - - commands := []types.Command{ - &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, - &builder.ContainerFindIncludes{}, - &builder.PrintUsedLibrariesIfVerbose{}, - &builder.WarnAboutArchIncompatibleLibraries{}, - &builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, - &builder.CTagsRunner{}, - } - for _, command := range commands { - err := command.Run(ctx) - NoError(t, err) - } + ctagsOutput := ctagsRunnerTestTemplate(t, sketchLocation) quotedSketchLocation := strings.Replace(sketchLocation.String(), "\\", "\\\\", -1) expectedOutput := "Foo\t" + quotedSketchLocation + "\t/^ struct Foo{$/;\"\tkind:struct\tline:2\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" + "func\t" + quotedSketchLocation + "\t/^typename Foo::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(ctagsOutput), "\r\n", "\n", -1)) } func TestCTagsRunnerSketchWithNamespace(t *testing.T) { sketchLocation := Abs(t, paths.New("sketch_with_namespace", "sketch_with_namespace.ino")) - ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") - defer cleanUpBuilderTestContext(t, ctx) - ctx.Verbose = true - - commands := []types.Command{ - &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, - &builder.ContainerFindIncludes{}, - &builder.PrintUsedLibrariesIfVerbose{}, - &builder.WarnAboutArchIncompatibleLibraries{}, - &builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, - &builder.CTagsRunner{}, - } - for _, command := range commands { - err := command.Run(ctx) - NoError(t, err) - } + ctagsOutput := ctagsRunnerTestTemplate(t, sketchLocation) quotedSketchLocation := strings.Replace(sketchLocation.String(), "\\", "\\\\", -1) 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" + "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(ctagsOutput), "\r\n", "\n", -1)) } func TestCTagsRunnerSketchWithTemplates(t *testing.T) { sketchLocation := Abs(t, paths.New("sketch_with_templates_and_shift", "sketch_with_templates_and_shift.ino")) - ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") - defer cleanUpBuilderTestContext(t, ctx) - ctx.Verbose = true - - commands := []types.Command{ - &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, - &builder.ContainerFindIncludes{}, - &builder.PrintUsedLibrariesIfVerbose{}, - &builder.WarnAboutArchIncompatibleLibraries{}, - &builder.CTagsTargetFileSaver{Source: &ctx.Source, TargetFileName: "ctags_target.cpp"}, - &builder.CTagsRunner{}, - } - for _, command := range commands { - err := command.Run(ctx) - NoError(t, err) - } + ctagsOutput := ctagsRunnerTestTemplate(t, sketchLocation) quotedSketchLocation := strings.Replace(sketchLocation.String(), "\\", "\\\\", -1) expectedOutput := "printGyro\t" + quotedSketchLocation + "\t/^void printGyro()$/;\"\tkind:function\tline:10\tsignature:()\treturntype:void\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" + "func\t" + quotedSketchLocation + "\t/^template func( c< 1< & aParam) {$/;\"\tkind:function\tline:18\tsignature:( c< 1< & aParam)\treturntype:template\n" - require.Equal(t, expectedOutput, strings.Replace(ctx.CTagsOutput, "\r\n", "\n", -1)) + require.Equal(t, expectedOutput, strings.Replace(string(ctagsOutput), "\r\n", "\n", -1)) } diff --git a/legacy/builder/test/helper.go b/legacy/builder/test/helper.go index 4581dd7c921..88432e5fc76 100644 --- a/legacy/builder/test/helper.go +++ b/legacy/builder/test/helper.go @@ -23,11 +23,11 @@ import ( "testing" "text/template" + "github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" paths "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,7 +35,7 @@ import ( func LoadAndInterpolate(t *testing.T, filename string, ctx *types.Context) string { funcsMap := template.FuncMap{ - "QuoteCppString": utils.QuoteCppPath, + "QuoteCppString": func(p *paths.Path) string { return cpp.QuoteString(p.String()) }, } tpl, err := template.New(filepath.Base(filename)).Funcs(funcsMap).ParseFiles(filename) diff --git a/legacy/builder/test/helper_tools_downloader.go b/legacy/builder/test/helper_tools_downloader.go index 6e79e4d3aac..133122c4ab6 100644 --- a/legacy/builder/test/helper_tools_downloader.go +++ b/legacy/builder/test/helper_tools_downloader.go @@ -29,10 +29,10 @@ import ( "github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/gohasissues" - "github.com/arduino/arduino-cli/legacy/builder/utils" "github.com/arduino/go-paths-helper" "github.com/arduino/go-properties-orderedmap" "github.com/pkg/errors" + "golang.org/x/exp/slices" ) var hardwareFolder = paths.New("downloaded_hardware") @@ -692,7 +692,7 @@ func translateGOOSGOARCHToPackageIndexValue() []string { func findToolUrl(index map[string]interface{}, tool Tool, host []string) (string, error) { if len(tool.OsUrls) > 0 { for _, osUrl := range tool.OsUrls { - if utils.SliceContains(host, osUrl.Os) { + if slices.Contains(host, osUrl.Os) { return osUrl.Url, nil } } @@ -709,7 +709,7 @@ func findToolUrl(index map[string]interface{}, tool Tool, host []string) (string systems := packageTool["systems"].([]interface{}) for _, s := range systems { system := s.(map[string]interface{}) - if utils.SliceContains(host, system["host"].(string)) { + if slices.Contains(host, system["host"].(string)) { return system[constants.TOOL_URL].(string), nil } } diff --git a/legacy/builder/test/includes_to_include_folders_test.go b/legacy/builder/test/includes_to_include_folders_test.go index 537b58d327e..f457e518185 100644 --- a/legacy/builder/test/includes_to_include_folders_test.go +++ b/legacy/builder/test/includes_to_include_folders_test.go @@ -20,6 +20,7 @@ import ( "sort" "testing" + bldr "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder/types" paths "github.com/arduino/go-paths-helper" @@ -31,9 +32,13 @@ func TestIncludesToIncludeFolders(t *testing.T) { defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -51,9 +56,13 @@ func TestIncludesToIncludeFoldersSketchWithIfDef(t *testing.T) { defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -70,9 +79,13 @@ func TestIncludesToIncludeFoldersIRremoteLibrary(t *testing.T) { defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -92,9 +105,13 @@ func TestIncludesToIncludeFoldersANewLibrary(t *testing.T) { defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -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") defer cleanUpBuilderTestContext(t, ctx) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -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") defer cleanUpBuilderTestContext(t, ctx) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -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") defer cleanUpBuilderTestContext(t, ctx) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { @@ -197,9 +226,13 @@ func TestIncludesToIncludeFoldersSubfolders(t *testing.T) { defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { diff --git a/legacy/builder/test/prototypes_adder_test.go b/legacy/builder/test/prototypes_adder_test.go index 67989677ea0..024d86cf265 100644 --- a/legacy/builder/test/prototypes_adder_test.go +++ b/legacy/builder/test/prototypes_adder_test.go @@ -21,35 +21,47 @@ import ( "strings" "testing" + bldr "github.com/arduino/arduino-cli/arduino/builder" + "github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" paths "github.com/arduino/go-paths-helper" "github.com/stretchr/testify/require" ) +func loadPreprocessedSketch(t *testing.T, ctx *types.Context) string { + res, err := ctx.SketchBuildPath.Join(ctx.Sketch.MainFile.Base() + ".cpp").ReadFile() + NoError(t, err) + return string(res) +} + func TestPrototypesAdderBridgeExample(t *testing.T) { sketchLocation := paths.New("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 33 "+quotedSketchLocation+"\nvoid setup();\n#line 46 "+quotedSketchLocation+"\nvoid loop();\n#line 62 "+quotedSketchLocation+"\nvoid process(BridgeClient client);\n#line 82 "+quotedSketchLocation+"\nvoid digitalCommand(BridgeClient client);\n#line 109 "+quotedSketchLocation+"\nvoid analogCommand(BridgeClient client);\n#line 149 "+quotedSketchLocation+"\nvoid modeCommand(BridgeClient client);\n#line 33 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 33 "+quotedSketchLocation+"\nvoid setup();\n#line 46 "+quotedSketchLocation+"\nvoid loop();\n#line 62 "+quotedSketchLocation+"\nvoid process(BridgeClient client);\n#line 82 "+quotedSketchLocation+"\nvoid digitalCommand(BridgeClient client);\n#line 109 "+quotedSketchLocation+"\nvoid analogCommand(BridgeClient client);\n#line 149 "+quotedSketchLocation+"\nvoid modeCommand(BridgeClient client);\n#line 33 "+quotedSketchLocation+"\n") } func TestPrototypesAdderSketchWithIfDef(t *testing.T) { @@ -58,19 +70,24 @@ func TestPrototypesAdderSketchWithIfDef(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("SketchWithIfDef", "SketchWithIfDef.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderBaladuino(t *testing.T) { @@ -79,19 +96,24 @@ func TestPrototypesAdderBaladuino(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("Baladuino", "Baladuino.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderCharWithEscapedDoubleQuote(t *testing.T) { @@ -100,19 +122,24 @@ func TestPrototypesAdderCharWithEscapedDoubleQuote(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("CharWithEscapedDoubleQuote", "CharWithEscapedDoubleQuote.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderIncludeBetweenMultilineComment(t *testing.T) { @@ -121,19 +148,24 @@ func TestPrototypesAdderIncludeBetweenMultilineComment(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("IncludeBetweenMultilineComment", "IncludeBetweenMultilineComment.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderLineContinuations(t *testing.T) { @@ -142,19 +174,24 @@ func TestPrototypesAdderLineContinuations(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("LineContinuations", "LineContinuations.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderStringWithComment(t *testing.T) { @@ -163,19 +200,24 @@ func TestPrototypesAdderStringWithComment(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("StringWithComment", "StringWithComment.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderSketchWithStruct(t *testing.T) { @@ -184,19 +226,24 @@ func TestPrototypesAdderSketchWithStruct(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) preprocessed := LoadAndInterpolate(t, filepath.Join("SketchWithStruct", "SketchWithStruct.preprocessed.txt"), ctx) - obtained := strings.Replace(ctx.Source, "\r\n", "\n", -1) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + obtained := strings.Replace(preprocessedSketch, "\r\n", "\n", -1) // ctags based preprocessing removes the space after "dostuff", but this is still OK // TODO: remove this exception when moving to a more powerful parser preprocessed = strings.Replace(preprocessed, "void dostuff (A_NEW_TYPE * bar);", "void dostuff(A_NEW_TYPE * bar);", 1) @@ -206,53 +253,64 @@ func TestPrototypesAdderSketchWithStruct(t *testing.T) { func TestPrototypesAdderSketchWithConfig(t *testing.T) { sketchLocation := paths.New("sketch_with_config", "sketch_with_config.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 13 "+quotedSketchLocation+"\nvoid setup();\n#line 17 "+quotedSketchLocation+"\nvoid loop();\n#line 13 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 13 "+quotedSketchLocation+"\nvoid setup();\n#line 17 "+quotedSketchLocation+"\nvoid loop();\n#line 13 "+quotedSketchLocation+"\n") preprocessed := LoadAndInterpolate(t, filepath.Join("sketch_with_config", "sketch_with_config.preprocessed.txt"), ctx) - require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + require.Equal(t, preprocessed, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderSketchNoFunctionsTwoFiles(t *testing.T) { sketchLocation := paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, paths.New("sketch_no_functions_two_files", "sketch_no_functions_two_files.ino"), "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + mergedSketch := loadPreprocessedSketch(t, ctx) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Equal(t, mergedSketch, preprocessedSketch) // No prototypes added } func TestPrototypesAdderSketchNoFunctions(t *testing.T) { @@ -262,71 +320,86 @@ func TestPrototypesAdderSketchNoFunctions(t *testing.T) { ctx.Verbose = true sketchLocation := paths.New("sketch_no_functions", "sketch_no_functions.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) - + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + mergedSketch := loadPreprocessedSketch(t, ctx) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Equal(t, mergedSketch, preprocessedSketch) // No prototypes added } func TestPrototypesAdderSketchWithDefaultArgs(t *testing.T) { sketchLocation := paths.New("sketch_with_default_args", "sketch_with_default_args.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 4 "+quotedSketchLocation+"\nvoid setup();\n#line 7 "+quotedSketchLocation+"\nvoid loop();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 4 "+quotedSketchLocation+"\nvoid setup();\n#line 7 "+quotedSketchLocation+"\nvoid loop();\n#line 1 "+quotedSketchLocation+"\n") } func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) { sketchLocation := paths.New("sketch_with_inline_function", "sketch_with_inline_function.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") expected := "#line 1 " + quotedSketchLocation + "\nvoid setup();\n#line 2 " + quotedSketchLocation + "\nvoid loop();\n#line 4 " + quotedSketchLocation + "\nshort unsigned int testInt();\n#line 8 " + quotedSketchLocation + "\nstatic int8_t testInline();\n#line 12 " + quotedSketchLocation + "\n__attribute__((always_inline)) uint8_t testAttribute();\n#line 1 " + quotedSketchLocation + "\n" - obtained := ctx.PrototypesSection + obtained := preprocessedSketch // ctags based preprocessing removes "inline" but this is still OK // TODO: remove this exception when moving to a more powerful parser expected = strings.Replace(expected, "static inline int8_t testInline();", "static int8_t testInline();", -1) @@ -335,36 +408,41 @@ func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) { // TODO: remove this exception when moving to a more powerful parser expected = strings.Replace(expected, "__attribute__((always_inline)) uint8_t testAttribute();", "uint8_t testAttribute();", -1) obtained = strings.Replace(obtained, "__attribute__((always_inline)) uint8_t testAttribute();", "uint8_t testAttribute();", -1) - require.Equal(t, expected, obtained) + require.Contains(t, obtained, expected) } func TestPrototypesAdderSketchWithFunctionSignatureInsideIFDEF(t *testing.T) { sketchLocation := paths.New("sketch_with_function_signature_inside_ifdef", "sketch_with_function_signature_inside_ifdef.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 3 "+quotedSketchLocation+"\nvoid loop();\n#line 15 "+quotedSketchLocation+"\nint8_t adalight();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 3 "+quotedSketchLocation+"\nvoid loop();\n#line 15 "+quotedSketchLocation+"\nint8_t adalight();\n#line 1 "+quotedSketchLocation+"\n") } func TestPrototypesAdderSketchWithUSBCON(t *testing.T) { sketchLocation := paths.New("sketch_with_usbcon", "sketch_with_usbcon.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := &types.Context{ HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"), @@ -376,24 +454,29 @@ func TestPrototypesAdderSketchWithUSBCON(t *testing.T) { ctx = prepareBuilderTestContext(t, ctx, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 5 "+quotedSketchLocation+"\nvoid ciao();\n#line 10 "+quotedSketchLocation+"\nvoid setup();\n#line 15 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 5 "+quotedSketchLocation+"\nvoid ciao();\n#line 10 "+quotedSketchLocation+"\nvoid setup();\n#line 15 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n") } func TestPrototypesAdderSketchWithTypename(t *testing.T) { sketchLocation := paths.New("sketch_with_typename", "sketch_with_typename.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := &types.Context{ HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware"), @@ -404,103 +487,123 @@ func TestPrototypesAdderSketchWithTypename(t *testing.T) { ctx = prepareBuilderTestContext(t, ctx, sketchLocation, "arduino:avr:leonardo") defer cleanUpBuilderTestContext(t, ctx) + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") expected := "#line 6 " + quotedSketchLocation + "\nvoid setup();\n#line 10 " + quotedSketchLocation + "\nvoid loop();\n#line 12 " + quotedSketchLocation + "\ntypename Foo::Bar func();\n#line 6 " + quotedSketchLocation + "\n" - obtained := ctx.PrototypesSection + obtained := preprocessedSketch // ctags based preprocessing ignores line with typename // TODO: remove this exception when moving to a more powerful parser expected = strings.Replace(expected, "#line 12 "+quotedSketchLocation+"\ntypename Foo::Bar func();\n", "", -1) obtained = strings.Replace(obtained, "#line 12 "+quotedSketchLocation+"\ntypename Foo::Bar func();\n", "", -1) - require.Equal(t, expected, obtained) + require.Contains(t, obtained, expected) } func TestPrototypesAdderSketchWithIfDef2(t *testing.T) { sketchLocation := paths.New("sketch_with_ifdef", "sketch_with_ifdef.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:yun") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 5 "+quotedSketchLocation+"\nvoid elseBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 5 "+quotedSketchLocation+"\nvoid elseBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n") expectedSource := LoadAndInterpolate(t, filepath.Join("sketch_with_ifdef", "sketch.preprocessed.txt"), ctx) - require.Equal(t, expectedSource, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + require.Equal(t, expectedSource, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderSketchWithIfDef2SAM(t *testing.T) { sketchLocation := paths.New("sketch_with_ifdef", "sketch_with_ifdef.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:sam:arduino_due_x_dbg") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 2 "+quotedSketchLocation+"\nvoid ifBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 2 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 2 "+quotedSketchLocation+"\nvoid ifBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 2 "+quotedSketchLocation+"\n") expectedSource := LoadAndInterpolate(t, filepath.Join("sketch_with_ifdef", "sketch.preprocessed.SAM.txt"), ctx) - require.Equal(t, expectedSource, strings.Replace(ctx.Source, "\r\n", "\n", -1)) + require.Equal(t, expectedSource, strings.Replace(preprocessedSketch, "\r\n", "\n", -1)) } func TestPrototypesAdderSketchWithConst(t *testing.T) { sketchLocation := paths.New("sketch_with_const", "sketch_with_const.ino") - quotedSketchLocation := utils.QuoteCppPath(Abs(t, sketchLocation)) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:uno") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "#include \n#line 1 "+quotedSketchLocation+"\n") - require.Equal(t, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 2 "+quotedSketchLocation+"\nvoid loop();\n#line 4 "+quotedSketchLocation+"\nconst __FlashStringHelper* test();\n#line 6 "+quotedSketchLocation+"\nconst int test3();\n#line 8 "+quotedSketchLocation+"\nvolatile __FlashStringHelper* test2();\n#line 10 "+quotedSketchLocation+"\nvolatile int test4();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "#include \n#line 1 "+quotedSketchLocation+"\n") + require.Contains(t, preprocessedSketch, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 2 "+quotedSketchLocation+"\nvoid loop();\n#line 4 "+quotedSketchLocation+"\nconst __FlashStringHelper* test();\n#line 6 "+quotedSketchLocation+"\nconst int test3();\n#line 8 "+quotedSketchLocation+"\nvolatile __FlashStringHelper* test2();\n#line 10 "+quotedSketchLocation+"\nvolatile int test4();\n#line 1 "+quotedSketchLocation+"\n") } func TestPrototypesAdderSketchWithDosEol(t *testing.T) { @@ -509,38 +612,47 @@ func TestPrototypesAdderSketchWithDosEol(t *testing.T) { ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) // only requires no error as result } func TestPrototypesAdderSketchWithSubstringFunctionMember(t *testing.T) { sketchLocation := paths.New("sketch_with_class_and_method_substring", "sketch_with_class_and_method_substring.ino") - quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation).String()) + quotedSketchLocation := cpp.QuoteString(Abs(t, sketchLocation).String()) ctx := prepareBuilderTestContext(t, nil, sketchLocation, "arduino:avr:uno") defer cleanUpBuilderTestContext(t, ctx) ctx.Verbose = true + var _err error commands := []types.Command{ &builder.ContainerSetupHardwareToolsLibsSketchAndProps{}, - &builder.ContainerMergeCopySketchFiles{}, + types.BareCommand(func(ctx *types.Context) error { + ctx.LineOffset, _err = bldr.PrepareSketchBuildPath(ctx.Sketch, ctx.SourceOverride, ctx.SketchBuildPath) + return _err + }), &builder.ContainerFindIncludes{}, } for _, command := range commands { err := command.Run(ctx) NoError(t, err) } - NoError(t, builder.PreprocessSketchWithCtags(ctx)) + NoError(t, builder.PreprocessSketch(ctx)) - require.Contains(t, ctx.Source, "class Foo {\nint blooper(int x) { return x+1; }\n};\n\nFoo foo;\n\n#line 7 "+quotedSketchLocation+"\nvoid setup();") + preprocessedSketch := loadPreprocessedSketch(t, ctx) + require.Contains(t, preprocessedSketch, "class Foo {\nint blooper(int x) { return x+1; }\n};\n\nFoo foo;\n\n#line 7 "+quotedSketchLocation+"\nvoid setup();") } diff --git a/legacy/builder/test/read_file_and_store_in_context_test.go b/legacy/builder/test/read_file_and_store_in_context_test.go deleted file mode 100644 index c4e299e2c60..00000000000 --- a/legacy/builder/test/read_file_and_store_in_context_test.go +++ /dev/null @@ -1,44 +0,0 @@ -// 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) -} diff --git a/legacy/builder/test/try_build_of_problematic_sketch_test.go b/legacy/builder/test/try_build_of_problematic_sketch_test.go index c82c6e0b290..90f3633f2b6 100644 --- a/legacy/builder/test/try_build_of_problematic_sketch_test.go +++ b/legacy/builder/test/try_build_of_problematic_sketch_test.go @@ -20,6 +20,7 @@ import ( "path/filepath" "testing" + "github.com/arduino/arduino-cli/arduino/builder/preprocessor" "github.com/arduino/arduino-cli/legacy/builder" "github.com/arduino/arduino-cli/legacy/builder/types" paths "github.com/arduino/go-paths-helper" @@ -202,13 +203,13 @@ func TestTryBuild042(t *testing.T) { } func makeDefaultContext() *types.Context { + preprocessor.DebugPreprocessor = true return &types.Context{ HardwareDirs: paths.NewPathList(filepath.Join("..", "hardware"), "downloaded_hardware", "downloaded_board_manager_stuff"), BuiltInToolsDirs: paths.NewPathList("downloaded_tools"), BuiltInLibrariesDirs: paths.New("downloaded_libraries"), OtherLibrariesDirs: paths.NewPathList("libraries"), Verbose: true, - DebugPreprocessor: true, } } diff --git a/legacy/builder/test/unique_string_queue_test.go b/legacy/builder/test/unique_string_queue_test.go deleted file mode 100644 index 20158792a53..00000000000 --- a/legacy/builder/test/unique_string_queue_test.go +++ /dev/null @@ -1,34 +0,0 @@ -// 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 ( - "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/stretchr/testify/require" - "testing" -) - -func TestUniqueStringQueue(t *testing.T) { - queue := types.UniqueStringQueue{} - queue.Push("hello") - queue.Push("world") - queue.Push("hello") - queue.Push("world") - - require.Equal(t, "hello", queue.Pop()) - require.Equal(t, "world", queue.Pop()) - require.True(t, queue.Empty()) -} diff --git a/legacy/builder/test/utils_test.go b/legacy/builder/test/utils_test.go index b6f88555283..a6a4dea8fb4 100644 --- a/legacy/builder/test/utils_test.go +++ b/legacy/builder/test/utils_test.go @@ -16,7 +16,6 @@ package test import ( - "strings" "testing" "github.com/arduino/arduino-cli/legacy/builder/utils" @@ -41,65 +40,3 @@ func TestPrintableCommand(t *testing.T) { result := utils.PrintableCommand(parts) require.Equal(t, correct, result) } - -func TestMapTrimSpace(t *testing.T) { - value := "hello, world , how are,you? " - parts := utils.Map(strings.Split(value, ","), utils.TrimSpace) - - require.Equal(t, 4, len(parts)) - require.Equal(t, "hello", parts[0]) - require.Equal(t, "world", parts[1]) - require.Equal(t, "how are", parts[2]) - require.Equal(t, "you?", parts[3]) -} - -func TestQuoteCppString(t *testing.T) { - cases := map[string]string{ - `foo`: `"foo"`, - `foo\bar`: `"foo\\bar"`, - `foo "is" quoted and \\bar"" escaped\`: `"foo \"is\" quoted and \\\\bar\"\" escaped\\"`, - // ASCII 0x20 - 0x7e, excluding ` - ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`: `" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`, - } - for input, expected := range cases { - 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) -} diff --git a/legacy/builder/types/accessories.go b/legacy/builder/types/accessories.go index b0c85d37e20..8decf206162 100644 --- a/legacy/builder/types/accessories.go +++ b/legacy/builder/types/accessories.go @@ -15,33 +15,7 @@ package types -import ( - "bytes" - "sync" -) - -type UniqueStringQueue []string - -func (queue UniqueStringQueue) Len() int { return len(queue) } -func (queue UniqueStringQueue) Less(i, j int) bool { return false } -func (queue UniqueStringQueue) Swap(i, j int) { panic("Who called me?!?") } - -func (queue *UniqueStringQueue) Push(value string) { - if !sliceContains(*queue, value) { - *queue = append(*queue, value) - } -} - -func (queue *UniqueStringQueue) Pop() interface{} { - old := *queue - x := old[0] - *queue = old[1:] - return x -} - -func (queue *UniqueStringQueue) Empty() bool { - return queue.Len() == 0 -} +import "golang.org/x/exp/slices" type UniqueSourceFileQueue []SourceFile @@ -50,7 +24,10 @@ func (queue UniqueSourceFileQueue) Less(i, j int) bool { return false } func (queue UniqueSourceFileQueue) Swap(i, j int) { panic("Who called me?!?") } func (queue *UniqueSourceFileQueue) Push(value SourceFile) { - if !sliceContainsSourceFile(*queue, value) { + equals := func(elem SourceFile) bool { + return elem.Origin == value.Origin && elem.RelativePath.EqualsTo(value.RelativePath) + } + if !slices.ContainsFunc(*queue, equals) { *queue = append(*queue, value) } } @@ -65,32 +42,3 @@ func (queue *UniqueSourceFileQueue) Pop() SourceFile { func (queue *UniqueSourceFileQueue) Empty() bool { return queue.Len() == 0 } - -type BufferedUntilNewLineWriter struct { - PrintFunc PrintFunc - Buffer bytes.Buffer - lock sync.Mutex -} - -type PrintFunc func([]byte) - -func (w *BufferedUntilNewLineWriter) Write(p []byte) (n int, err error) { - w.lock.Lock() - defer w.lock.Unlock() - - writtenToBuffer, err := w.Buffer.Write(p) - return writtenToBuffer, err -} - -func (w *BufferedUntilNewLineWriter) Flush() { - w.lock.Lock() - defer w.lock.Unlock() - - remainingBytes := w.Buffer.Bytes() - if len(remainingBytes) > 0 { - if remainingBytes[len(remainingBytes)-1] != '\n' { - remainingBytes = append(remainingBytes, '\n') - } - w.PrintFunc(remainingBytes) - } -} diff --git a/legacy/builder/types/context.go b/legacy/builder/types/context.go index 0048d6d0c02..8bbd05f7308 100644 --- a/legacy/builder/types/context.go +++ b/legacy/builder/types/context.go @@ -94,16 +94,10 @@ type Context struct { CoreObjectsFiles paths.PathList LibrariesBuildPath *paths.Path LibrariesObjectFiles paths.PathList - PreprocPath *paths.Path SketchObjectFiles paths.PathList IgnoreSketchFolderNameErrors bool - CollectedSourceFiles *UniqueSourceFileQueue - - Sketch *sketch.Sketch - Source string - SourceGccMinusE string - + Sketch *sketch.Sketch WarningsLevel string // Libraries handling @@ -115,17 +109,10 @@ type Context struct { UseCachedLibrariesResolution bool // C++ Parsing - CTagsOutput string - CTagsTargetFile *paths.Path - CTagsOfPreprocessedSource []*CTag - LineOffset int - PrototypesSection string - PrototypesLineWhereToInsert int - Prototypes []*Prototype + LineOffset int // Verbosity settings - Verbose bool - DebugPreprocessor bool + Verbose bool // Dry run, only create progress map Progress ProgressStruct diff --git a/legacy/builder/types/types.go b/legacy/builder/types/types.go index 855c0295ffb..8d34056fff5 100644 --- a/legacy/builder/types/types.go +++ b/legacy/builder/types/types.go @@ -17,7 +17,6 @@ package types import ( "fmt" - "strconv" "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/arduino/sketch" @@ -85,72 +84,17 @@ func (f *SourceFile) DepfilePath(ctx *Context) *paths.Path { return buildRoot(ctx, f.Origin).Join(f.RelativePath.String() + ".d") } -type SketchFile struct { - Name *paths.Path -} - -type SketchFileSortByName []SketchFile - -func (s SketchFileSortByName) Len() int { - return len(s) -} - -func (s SketchFileSortByName) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -func (s SketchFileSortByName) Less(i, j int) bool { - return s[i].Name.String() < s[j].Name.String() -} - -type PlatforKeysRewrite struct { - Rewrites []PlatforKeyRewrite -} - -func (p *PlatforKeysRewrite) Empty() bool { - return len(p.Rewrites) == 0 -} - -type PlatforKeyRewrite struct { - Key string - OldValue string - NewValue string -} - -type Prototype struct { - FunctionName string - File string - Prototype string - Modifiers string - Line int -} - -func (proto *Prototype) String() string { - return proto.Modifiers + " " + proto.Prototype + " @ " + strconv.Itoa(proto.Line) -} - type LibraryResolutionResult struct { Library *libraries.Library NotUsedLibraries []*libraries.Library } -type CTag struct { - FunctionName string - Kind string - Line int - Code string - Class string - Struct string - Namespace string - Filename string - Typeref string - SkipMe bool - Signature string - - Prototype string - PrototypeModifiers string -} - type Command interface { Run(ctx *Context) error } + +type BareCommand func(ctx *Context) error + +func (cmd BareCommand) Run(ctx *Context) error { + return cmd(ctx) +} diff --git a/legacy/builder/types/utils.go b/legacy/builder/types/utils.go deleted file mode 100644 index 7102cd555ca..00000000000 --- a/legacy/builder/types/utils.go +++ /dev/null @@ -1,35 +0,0 @@ -// 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 types - -// duplication of utils.SliceContains! Thanks golang! Why? Because with golang you can't have import cycles -func sliceContains(slice []string, target string) bool { - for _, value := range slice { - if value == target { - return true - } - } - return false -} - -func sliceContainsSourceFile(slice []SourceFile, target SourceFile) bool { - for _, elem := range slice { - if elem.Origin == target.Origin && elem.RelativePath.EqualsTo(target.RelativePath) { - return true - } - } - return false -} diff --git a/legacy/builder/unused_compiled_libraries_remover.go b/legacy/builder/unused_compiled_libraries_remover.go index 595ce91302e..d1b36b147ac 100644 --- a/legacy/builder/unused_compiled_libraries_remover.go +++ b/legacy/builder/unused_compiled_libraries_remover.go @@ -18,8 +18,8 @@ package builder import ( "github.com/arduino/arduino-cli/arduino/libraries" "github.com/arduino/arduino-cli/legacy/builder/types" - "github.com/arduino/arduino-cli/legacy/builder/utils" "github.com/pkg/errors" + "golang.org/x/exp/slices" ) type UnusedCompiledLibrariesRemover struct{} @@ -39,7 +39,7 @@ func (s *UnusedCompiledLibrariesRemover) Run(ctx *types.Context) error { } for _, file := range files { if file.IsDir() { - if !utils.SliceContains(libraryNames, file.Base()) { + if !slices.Contains(libraryNames, file.Base()) { if err := file.RemoveAll(); err != nil { return errors.WithStack(err) } diff --git a/legacy/builder/utils/utils.go b/legacy/builder/utils/utils.go index 87113cce633..3efc8b73d6d 100644 --- a/legacy/builder/utils/utils.go +++ b/legacy/builder/utils/utils.go @@ -26,13 +26,14 @@ import ( "path/filepath" "strings" "unicode" - "unicode/utf8" "github.com/arduino/arduino-cli/i18n" + f "github.com/arduino/arduino-cli/internal/algorithms" "github.com/arduino/arduino-cli/legacy/builder/gohasissues" "github.com/arduino/arduino-cli/legacy/builder/types" paths "github.com/arduino/go-paths-helper" "github.com/pkg/errors" + "golang.org/x/exp/slices" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" @@ -64,7 +65,7 @@ func FilterFilesWithExtensions(extensions ...string) filterFiles { return func(files []os.FileInfo) []os.FileInfo { var filtered []os.FileInfo for _, file := range files { - if !file.IsDir() && SliceContains(extensions, filepath.Ext(file.Name())) { + if !file.IsDir() && slices.Contains(extensions, filepath.Ext(file.Name())) { filtered = append(filtered, file) } } @@ -119,45 +120,10 @@ func IsSCCSFile(file os.FileInfo) bool { return SOURCE_CONTROL_FOLDERS[name] } -func SliceContains(slice []string, target string) bool { - for _, value := range slice { - if value == target { - return true - } - } - return false -} - -type mapFunc func(string) string - -func Map(slice []string, fn mapFunc) []string { - newSlice := []string{} - for _, elem := range slice { - newSlice = append(newSlice, fn(elem)) - } - return newSlice -} - -type filterFunc func(string) bool - -func Filter(slice []string, fn filterFunc) []string { - newSlice := []string{} - for _, elem := range slice { - if fn(elem) { - newSlice = append(newSlice, elem) - } - } - return newSlice -} - func WrapWithHyphenI(value string) string { return "\"-I" + value + "\"" } -func TrimSpace(value string) string { - return strings.TrimSpace(value) -} - func printableArgument(arg string) string { if strings.ContainsAny(arg, "\"\\ \t") { arg = strings.Replace(arg, "\\", "\\\\", -1) @@ -173,7 +139,7 @@ func printableArgument(arg string) string { // probably not for shell interpretation. This essentially reverses // ParseCommandLine. func PrintableCommand(parts []string) string { - return strings.Join(Map(parts, printableArgument), " ") + return strings.Join(f.Map(parts, printableArgument), " ") } const ( @@ -263,7 +229,7 @@ func FindFilesInFolder(dir *paths.Path, recurse bool, extensions []string) (path func AppendIfNotPresent(target []string, elements ...string) []string { for _, element := range elements { - if !SliceContains(target, element) { + if !slices.Contains(target, element) { target = append(target, element) } } @@ -296,63 +262,6 @@ func LogIfVerbose(warn bool, msg string) types.Command { return &loggerAction{onlyIfVerbose: true, warn: warn, msg: msg} } -// 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 + "\"" -} - -func QuoteCppPath(path *paths.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 // TODO: use it more often troughout all the project (maybe on logger interface?) func NormalizeUTF8(buf []byte) []byte {