Unverified Commit b8024c31 authored by Alessio Perugini's avatar Alessio Perugini Committed by GitHub

[skip-changelog] legacy: Builder refactorization (part 2) (#2298)

* remove unused LibraryDir from legacy context

* remove unused WatchedLocation from legacy context

* remove unused IgnoreSketchFolderNameErrors from legacy context

* remove CanUseCachedTools from legacy context

* remove UseArduinoPreprocessor from legacy context

* make the CoreBuilder command a function

* remove the use of context from builder_utils

* mvoe types.ProgressStruct in a dedicated pkg

* move ExecCommand under arduino/utils

* move LogIfVerbose from utils to legacy builder

* move some legacy constans in builder package

* move builder_utils under arduino/builder/utils pkg

* appease golint

* move coreBuildCachePath in the arduino Builder

* refactor Linker command in a function

* refactor SketchBuilder in a function

* refactor LibrariesBuilder in a function

* refactor Sizer in a function

* remove empty file

* remove unused struct FailIfBuildPathEqualsSketchPath
parent 1c110e9c
...@@ -15,16 +15,34 @@ ...@@ -15,16 +15,34 @@
package builder package builder
import "github.com/arduino/arduino-cli/arduino/sketch" import (
"github.com/arduino/arduino-cli/arduino/sketch"
"github.com/arduino/go-paths-helper"
)
// nolint
const (
BuildPropertiesArchiveFile = "archive_file"
BuildPropertiesArchiveFilePath = "archive_file_path"
BuildPropertiesObjectFile = "object_file"
RecipeARPattern = "recipe.ar.pattern"
BuildPropertiesIncludes = "includes"
BuildPropertiesCompilerWarningFlags = "compiler.warning_flags"
Space = " "
)
// Builder is a Sketch builder. // Builder is a Sketch builder.
type Builder struct { type Builder struct {
sketch *sketch.Sketch sketch *sketch.Sketch
// core related
coreBuildCachePath *paths.Path
} }
// NewBuilder creates a sketch Builder. // NewBuilder creates a sketch Builder.
func NewBuilder(sk *sketch.Sketch) *Builder { func NewBuilder(sk *sketch.Sketch, coreBuildCachePath *paths.Path) *Builder {
return &Builder{ return &Builder{
sketch: sk, sketch: sk,
coreBuildCachePath: coreBuildCachePath,
} }
} }
package builder
import "github.com/arduino/go-paths-helper"
// CoreBuildCachePath fixdoc
func (b *Builder) CoreBuildCachePath() *paths.Path {
return b.coreBuildCachePath
}
package progress
// Struct fixdoc
type Struct struct {
Progress float32
StepAmount float32
Parent *Struct
}
// AddSubSteps fixdoc
func (p *Struct) AddSubSteps(steps int) {
p.Parent = &Struct{
Progress: p.Progress,
StepAmount: p.StepAmount,
Parent: p.Parent,
}
if p.StepAmount == 0.0 {
p.StepAmount = 100.0
}
p.StepAmount /= float32(steps)
}
// RemoveSubSteps fixdoc
func (p *Struct) RemoveSubSteps() {
p.Progress = p.Parent.Progress
p.StepAmount = p.Parent.StepAmount
p.Parent = p.Parent.Parent
}
// CompleteStep fixdoc
func (p *Struct) CompleteStep() {
p.Progress += p.StepAmount
}
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
// Arduino software without disclosing the source code of your own applications. // Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc. // To purchase a commercial license, send an email to license@arduino.cc.
package types package progress
import ( import (
"fmt" "fmt"
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
) )
func TestProgress(t *testing.T) { func TestProgress(t *testing.T) {
p := &ProgressStruct{} p := &Struct{}
p.AddSubSteps(3) p.AddSubSteps(3)
require.Equal(t, float32(0.0), p.Progress) require.Equal(t, float32(0.0), p.Progress)
require.InEpsilon(t, 33.33333, p.StepAmount, 0.00001) require.InEpsilon(t, 33.33333, p.StepAmount, 0.00001)
......
package builder
import rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
// ExecutableSectionSize represents a section of the executable output file
type ExecutableSectionSize struct {
Name string `json:"name"`
Size int `json:"size"`
MaxSize int `json:"max_size"`
}
// ExecutablesFileSections is an array of ExecutablesFileSection
type ExecutablesFileSections []ExecutableSectionSize
// ToRPCExecutableSectionSizeArray transforms this array into a []*rpc.ExecutableSectionSize
func (s ExecutablesFileSections) ToRPCExecutableSectionSizeArray() []*rpc.ExecutableSectionSize {
res := []*rpc.ExecutableSectionSize{}
for _, section := range s {
res = append(res, &rpc.ExecutableSectionSize{
Name: section.Name,
Size: int64(section.Size),
MaxSize: int64(section.MaxSize),
})
}
return res
}
...@@ -48,7 +48,7 @@ func TestMergeSketchSources(t *testing.T) { ...@@ -48,7 +48,7 @@ func TestMergeSketchSources(t *testing.T) {
} }
mergedSources := strings.ReplaceAll(string(mergedBytes), "%s", pathToGoldenSource) mergedSources := strings.ReplaceAll(string(mergedBytes), "%s", pathToGoldenSource)
b := NewBuilder(sk) b := NewBuilder(sk, nil)
offset, source, err := b.sketchMergeSources(nil) offset, source, err := b.sketchMergeSources(nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 2, offset) require.Equal(t, 2, offset)
...@@ -61,7 +61,7 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) { ...@@ -61,7 +61,7 @@ func TestMergeSketchSourcesArduinoIncluded(t *testing.T) {
require.NotNil(t, sk) require.NotNil(t, sk)
// ensure not to include Arduino.h when it's already there // ensure not to include Arduino.h when it's already there
b := NewBuilder(sk) b := NewBuilder(sk, nil)
_, source, err := b.sketchMergeSources(nil) _, source, err := b.sketchMergeSources(nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 1, strings.Count(source, "<Arduino.h>")) require.Equal(t, 1, strings.Count(source, "<Arduino.h>"))
...@@ -76,7 +76,7 @@ func TestCopyAdditionalFiles(t *testing.T) { ...@@ -76,7 +76,7 @@ func TestCopyAdditionalFiles(t *testing.T) {
sk1, err := sketch.New(paths.New("testdata", t.Name())) sk1, err := sketch.New(paths.New("testdata", t.Name()))
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, sk1.AdditionalFiles.Len(), 1) require.Equal(t, sk1.AdditionalFiles.Len(), 1)
b1 := NewBuilder(sk1) b1 := NewBuilder(sk1, nil)
// copy the sketch over, create a fake main file we don't care about it // copy the sketch over, create a fake main file we don't care about it
// but we need it for `SketchLoad` to succeed later // but we need it for `SketchLoad` to succeed later
......
This diff is collapsed.
// This file is part of arduino-cli. package utils
//
// 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 ( import (
"testing" "testing"
"github.com/arduino/arduino-cli/legacy/builder/utils"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -37,6 +21,6 @@ func TestPrintableCommand(t *testing.T) { ...@@ -37,6 +21,6 @@ func TestPrintableCommand(t *testing.T) {
" \"specialchar-`~!@#$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?-argument\"" + " \"specialchar-`~!@#$%^&*()-_=+[{]}\\\\|;:'\\\",<.>/?-argument\"" +
" \"arg with spaces\" \"arg\twith\t\ttabs\"" + " \"arg with spaces\" \"arg\twith\t\ttabs\"" +
" lastarg" " lastarg"
result := utils.PrintableCommand(parts) result := printableCommand(parts)
require.Equal(t, correct, result) require.Equal(t, correct, result)
} }
...@@ -155,7 +155,21 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream ...@@ -155,7 +155,21 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
// cache is purged after compilation to not remove entries that might be required // cache is purged after compilation to not remove entries that might be required
defer maybePurgeBuildCache() defer maybePurgeBuildCache()
sketchBuilder := bldr.NewBuilder(sk) var coreBuildCachePath *paths.Path
if req.GetBuildCachePath() == "" {
coreBuildCachePath = paths.TempDir().Join("arduino", "cores")
} else {
buildCachePath, err := paths.New(req.GetBuildCachePath()).Abs()
if err != nil {
return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err}
}
if err := buildCachePath.MkdirAll(); err != nil {
return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err}
}
coreBuildCachePath = buildCachePath.Join("core")
}
sketchBuilder := bldr.NewBuilder(sk, coreBuildCachePath)
// Add build properites related to sketch data // Add build properites related to sketch data
buildProperties = sketchBuilder.SetupBuildProperties(buildProperties, buildPath, req.GetOptimizeForDebug()) buildProperties = sketchBuilder.SetupBuildProperties(buildProperties, buildPath, req.GetOptimizeForDebug())
...@@ -193,7 +207,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream ...@@ -193,7 +207,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
builderCtx.BuiltInToolsDirs = configuration.BuiltinToolsDirectories(configuration.Settings) builderCtx.BuiltInToolsDirs = configuration.BuiltinToolsDirectories(configuration.Settings)
builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...) builderCtx.OtherLibrariesDirs = paths.NewPathList(req.GetLibraries()...)
builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings)) builderCtx.OtherLibrariesDirs.Add(configuration.LibrariesDir(configuration.Settings))
builderCtx.LibraryDirs = paths.NewPathList(req.Library...)
builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( builderCtx.CompilationDatabase = bldr.NewCompilationDatabase(
builderCtx.BuildPath.Join("compile_commands.json"), builderCtx.BuildPath.Join("compile_commands.json"),
...@@ -207,19 +220,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream ...@@ -207,19 +220,6 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
builderCtx.WarningsLevel = builder.DEFAULT_WARNINGS_LEVEL builderCtx.WarningsLevel = builder.DEFAULT_WARNINGS_LEVEL
} }
if req.GetBuildCachePath() == "" {
builderCtx.CoreBuildCachePath = paths.TempDir().Join("arduino", "cores")
} else {
buildCachePath, err := paths.New(req.GetBuildCachePath()).Abs()
if err != nil {
return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err}
}
if err := buildCachePath.MkdirAll(); err != nil {
return nil, &arduino.PermissionDeniedError{Message: tr("Cannot create build cache directory"), Cause: err}
}
builderCtx.CoreBuildCachePath = buildCachePath.Join("core")
}
builderCtx.BuiltInLibrariesDirs = configuration.IDEBuiltinLibrariesDir(configuration.Settings) builderCtx.BuiltInLibrariesDirs = configuration.IDEBuiltinLibrariesDir(configuration.Settings)
builderCtx.Stdout = outStream builderCtx.Stdout = outStream
...@@ -255,9 +255,10 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream ...@@ -255,9 +255,10 @@ func Compile(ctx context.Context, req *rpc.CompileRequest, outStream, errStream
libsManager = lm libsManager = lm
} }
useCachedLibrariesResolution := req.GetSkipLibrariesDiscovery() useCachedLibrariesResolution := req.GetSkipLibrariesDiscovery()
libraryDir := paths.NewPathList(req.Library...)
libsManager, libsResolver, verboseOut, err := detector.LibrariesLoader( libsManager, libsResolver, verboseOut, err := detector.LibrariesLoader(
useCachedLibrariesResolution, libsManager, useCachedLibrariesResolution, libsManager,
builderCtx.BuiltInLibrariesDirs, builderCtx.LibraryDirs, builderCtx.OtherLibrariesDirs, builderCtx.BuiltInLibrariesDirs, libraryDir, builderCtx.OtherLibrariesDirs,
builderCtx.ActualPlatform, builderCtx.TargetPlatform, builderCtx.ActualPlatform, builderCtx.TargetPlatform,
) )
if err != nil { if err != nil {
......
...@@ -23,7 +23,6 @@ import ( ...@@ -23,7 +23,6 @@ import (
"github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/i18n"
"github.com/arduino/arduino-cli/legacy/builder/phases" "github.com/arduino/arduino-cli/legacy/builder/phases"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/arduino-cli/legacy/builder/utils"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
...@@ -51,33 +50,120 @@ func (s *Builder) Run(ctx *types.Context) error { ...@@ -51,33 +50,120 @@ func (s *Builder) Run(ctx *types.Context) error {
return _err return _err
}), }),
utils.LogIfVerbose(false, tr("Detecting libraries used...")), logIfVerbose(false, tr("Detecting libraries used...")),
findIncludes(ctx), findIncludes(ctx),
&WarnAboutArchIncompatibleLibraries{}, &WarnAboutArchIncompatibleLibraries{},
utils.LogIfVerbose(false, tr("Generating function prototypes...")), logIfVerbose(false, tr("Generating function prototypes...")),
types.BareCommand(PreprocessSketch), types.BareCommand(PreprocessSketch),
utils.LogIfVerbose(false, tr("Compiling sketch...")), logIfVerbose(false, tr("Compiling sketch...")),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.sketch.prebuild", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.sketch.prebuild", Suffix: ".pattern"},
&phases.SketchBuilder{}, types.BareCommand(func(ctx *types.Context) error {
sketchObjectFiles, err := phases.SketchBuilder(
ctx.SketchBuildPath,
ctx.BuildProperties,
ctx.SketchLibrariesDetector.IncludeFolders(),
ctx.OnlyUpdateCompilationDatabase,
ctx.Verbose,
ctx.CompilationDatabase,
ctx.Jobs,
ctx.WarningsLevel,
ctx.Stdout, ctx.Stderr,
func(msg string) { ctx.Info(msg) },
func(data []byte) { ctx.WriteStdout(data) },
func(data []byte) { ctx.WriteStderr(data) },
&ctx.Progress, ctx.ProgressCB,
)
if err != nil {
return err
}
ctx.SketchObjectFiles = sketchObjectFiles
return nil
}),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.sketch.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.sketch.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true},
utils.LogIfVerbose(false, tr("Compiling libraries...")), logIfVerbose(false, tr("Compiling libraries...")),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.libraries.prebuild", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.libraries.prebuild", Suffix: ".pattern"},
&UnusedCompiledLibrariesRemover{}, &UnusedCompiledLibrariesRemover{},
&phases.LibrariesBuilder{}, types.BareCommand(func(ctx *types.Context) error {
librariesObjectFiles, err := phases.LibrariesBuilder(
ctx.LibrariesBuildPath,
ctx.BuildProperties,
ctx.SketchLibrariesDetector.IncludeFolders(),
ctx.SketchLibrariesDetector.ImportedLibraries(),
ctx.Verbose,
ctx.OnlyUpdateCompilationDatabase,
ctx.CompilationDatabase,
ctx.Jobs,
ctx.WarningsLevel,
ctx.Stdout,
ctx.Stderr,
func(msg string) { ctx.Info(msg) },
func(data []byte) { ctx.WriteStdout(data) },
func(data []byte) { ctx.WriteStderr(data) },
&ctx.Progress, ctx.ProgressCB,
)
if err != nil {
return err
}
ctx.LibrariesObjectFiles = librariesObjectFiles
return nil
}),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.libraries.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.libraries.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true},
utils.LogIfVerbose(false, tr("Compiling core...")), logIfVerbose(false, tr("Compiling core...")),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.core.prebuild", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.core.prebuild", Suffix: ".pattern"},
&phases.CoreBuilder{},
types.BareCommand(func(ctx *types.Context) error {
objectFiles, archiveFile, err := phases.CoreBuilder(
ctx.BuildPath, ctx.CoreBuildPath, ctx.Builder.CoreBuildCachePath(),
ctx.BuildProperties,
ctx.ActualPlatform,
ctx.Verbose, ctx.OnlyUpdateCompilationDatabase, ctx.Clean,
ctx.CompilationDatabase,
ctx.Jobs,
ctx.WarningsLevel,
ctx.Stdout, ctx.Stderr,
func(msg string) { ctx.Info(msg) },
func(data []byte) { ctx.WriteStdout(data) },
func(data []byte) { ctx.WriteStderr(data) },
&ctx.Progress, ctx.ProgressCB,
)
ctx.CoreObjectsFiles = objectFiles
ctx.CoreArchiveFilePath = archiveFile
return err
}),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.core.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.core.postbuild", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true},
utils.LogIfVerbose(false, tr("Linking everything together...")), logIfVerbose(false, tr("Linking everything together...")),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.linking.prelink", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.linking.prelink", Suffix: ".pattern"},
&phases.Linker{},
types.BareCommand(func(ctx *types.Context) error {
verboseInfoOut, err := phases.Linker(
ctx.OnlyUpdateCompilationDatabase,
ctx.Verbose,
ctx.SketchObjectFiles,
ctx.LibrariesObjectFiles,
ctx.CoreObjectsFiles,
ctx.CoreArchiveFilePath,
ctx.BuildPath,
ctx.BuildProperties,
ctx.Stdout,
ctx.Stderr,
ctx.WarningsLevel,
)
if ctx.Verbose {
ctx.Info(string(verboseInfoOut))
}
return err
}),
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.linking.postlink", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.linking.postlink", Suffix: ".pattern", SkipIfOnlyUpdatingCompilationDatabase: true},
&RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.objcopy.preobjcopy", Suffix: ".pattern"}, &RecipeByPrefixSuffixRunner{Prefix: "recipe.hooks.objcopy.preobjcopy", Suffix: ".pattern"},
...@@ -118,7 +204,18 @@ func (s *Builder) Run(ctx *types.Context) error { ...@@ -118,7 +204,18 @@ func (s *Builder) Run(ctx *types.Context) error {
&ExportProjectCMake{SketchError: mainErr != nil}, &ExportProjectCMake{SketchError: mainErr != nil},
&phases.Sizer{SketchError: mainErr != nil}, types.BareCommand(func(ctx *types.Context) error {
executableSectionsSize, err := phases.Sizer(
ctx.OnlyUpdateCompilationDatabase, mainErr != nil, ctx.Verbose,
ctx.BuildProperties,
ctx.Stdout, ctx.Stderr,
func(msg string) { ctx.Info(msg) },
func(msg string) { ctx.Warn(msg) },
ctx.WarningsLevel,
)
ctx.ExecutableSectionsSize = executableSectionsSize
return err
}),
} }
for _, command := range commands { for _, command := range commands {
PrintRingNameIfDebug(ctx, command) PrintRingNameIfDebug(ctx, command)
...@@ -140,9 +237,6 @@ func (s *Builder) Run(ctx *types.Context) error { ...@@ -140,9 +237,6 @@ func (s *Builder) Run(ctx *types.Context) error {
func PreprocessSketch(ctx *types.Context) error { func PreprocessSketch(ctx *types.Context) error {
preprocessorImpl := preprocessor.PreprocessSketchWithCtags preprocessorImpl := preprocessor.PreprocessSketchWithCtags
if ctx.UseArduinoPreprocessor {
preprocessorImpl = preprocessor.PreprocessSketchWithArduinoPreprocessor
}
normalOutput, verboseOutput, err := preprocessorImpl( normalOutput, verboseOutput, err := preprocessorImpl(
ctx.Sketch, ctx.BuildPath, ctx.SketchLibrariesDetector.IncludeFolders(), ctx.LineOffset, ctx.Sketch, ctx.BuildPath, ctx.SketchLibrariesDetector.IncludeFolders(), ctx.LineOffset,
ctx.BuildProperties, ctx.OnlyUpdateCompilationDatabase) ctx.BuildProperties, ctx.OnlyUpdateCompilationDatabase)
...@@ -235,3 +329,17 @@ func findIncludes(ctx *types.Context) types.BareCommand { ...@@ -235,3 +329,17 @@ func findIncludes(ctx *types.Context) types.BareCommand {
) )
}) })
} }
func logIfVerbose(warn bool, msg string) types.BareCommand {
return types.BareCommand(func(ctx *types.Context) error {
if !ctx.Verbose {
return nil
}
if warn {
ctx.Warn(msg)
} else {
ctx.Info(msg)
}
return nil
})
}
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.
package builder_utils
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"sync"
bUtils "github.com/arduino/arduino-cli/arduino/builder/utils"
"github.com/arduino/arduino-cli/arduino/globals"
"github.com/arduino/arduino-cli/executils"
"github.com/arduino/arduino-cli/i18n"
"github.com/arduino/arduino-cli/legacy/builder/constants"
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/arduino-cli/legacy/builder/utils"
"github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
)
var tr = i18n.Tr
// DirContentIsOlderThan returns true if the content of the given directory is
// older than target file. If extensions are given, only the files with these
// extensions are tested.
func DirContentIsOlderThan(dir *paths.Path, target *paths.Path, extensions ...string) (bool, error) {
targetStat, err := target.Stat()
if err != nil {
return false, err
}
targetModTime := targetStat.ModTime()
files, err := bUtils.FindFilesInFolder(dir, true, extensions...)
if err != nil {
return false, err
}
for _, file := range files {
file, err := file.Stat()
if err != nil {
return false, err
}
if file.ModTime().After(targetModTime) {
return false, nil
}
}
return true, nil
}
func CompileFiles(ctx *types.Context, sourceDir *paths.Path, buildPath *paths.Path, buildProperties *properties.Map, includes []string) (paths.PathList, error) {
return compileFiles(ctx, sourceDir, false, buildPath, buildProperties, includes)
}
func CompileFilesRecursive(ctx *types.Context, sourceDir *paths.Path, buildPath *paths.Path, buildProperties *properties.Map, includes []string) (paths.PathList, error) {
return compileFiles(ctx, sourceDir, true, buildPath, buildProperties, includes)
}
func compileFiles(ctx *types.Context, sourceDir *paths.Path, recurse bool, buildPath *paths.Path, buildProperties *properties.Map, includes []string) (paths.PathList, error) {
validExtensions := []string{}
for ext := range globals.SourceFilesValidExtensions {
validExtensions = append(validExtensions, ext)
}
sources, err := bUtils.FindFilesInFolder(sourceDir, recurse, validExtensions...)
if err != nil {
return nil, err
}
ctx.Progress.AddSubSteps(len(sources))
defer ctx.Progress.RemoveSubSteps()
objectFiles := paths.NewPathList()
var objectFilesMux sync.Mutex
if len(sources) == 0 {
return objectFiles, nil
}
var errorsList []error
var errorsMux sync.Mutex
queue := make(chan *paths.Path)
job := func(source *paths.Path) {
recipe := fmt.Sprintf("recipe%s.o.pattern", source.Ext())
if !buildProperties.ContainsKey(recipe) {
recipe = fmt.Sprintf("recipe%s.o.pattern", globals.SourceFilesValidExtensions[source.Ext()])
}
objectFile, err := compileFileWithRecipe(ctx, sourceDir, source, buildPath, buildProperties, includes, recipe)
if err != nil {
errorsMux.Lock()
errorsList = append(errorsList, err)
errorsMux.Unlock()
} else {
objectFilesMux.Lock()
objectFiles.Add(objectFile)
objectFilesMux.Unlock()
}
}
// Spawn jobs runners
var wg sync.WaitGroup
jobs := ctx.Jobs
if jobs == 0 {
jobs = runtime.NumCPU()
}
for i := 0; i < jobs; i++ {
wg.Add(1)
go func() {
for source := range queue {
job(source)
}
wg.Done()
}()
}
// Feed jobs until error or done
for _, source := range sources {
errorsMux.Lock()
gotError := len(errorsList) > 0
errorsMux.Unlock()
if gotError {
break
}
queue <- source
ctx.Progress.CompleteStep()
ctx.PushProgress()
}
close(queue)
wg.Wait()
if len(errorsList) > 0 {
// output the first error
return nil, errors.WithStack(errorsList[0])
}
objectFiles.Sort()
return objectFiles, nil
}
func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *paths.Path, buildPath *paths.Path, buildProperties *properties.Map, includes []string, recipe string) (*paths.Path, error) {
properties := buildProperties.Clone()
properties.Set(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS, properties.Get(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS+"."+ctx.WarningsLevel))
properties.Set(constants.BUILD_PROPERTIES_INCLUDES, strings.Join(includes, constants.SPACE))
properties.SetPath("source_file", source)
relativeSource, err := sourcePath.RelTo(source)
if err != nil {
return nil, errors.WithStack(err)
}
depsFile := buildPath.Join(relativeSource.String() + ".d")
objectFile := buildPath.Join(relativeSource.String() + ".o")
properties.SetPath(constants.BUILD_PROPERTIES_OBJECT_FILE, objectFile)
err = objectFile.Parent().MkdirAll()
if err != nil {
return nil, errors.WithStack(err)
}
objIsUpToDate, err := bUtils.ObjFileIsUpToDate(source, objectFile, depsFile)
if err != nil {
return nil, errors.WithStack(err)
}
command, err := PrepareCommandForRecipe(properties, recipe, false)
if err != nil {
return nil, errors.WithStack(err)
}
if ctx.CompilationDatabase != nil {
ctx.CompilationDatabase.Add(source, command)
}
if !objIsUpToDate && !ctx.OnlyUpdateCompilationDatabase {
// Since this compile could be multithreaded, we first capture the command output
stdout, stderr, err := utils.ExecCommand(ctx, command, utils.Capture, utils.Capture)
// and transfer all at once at the end...
if ctx.Verbose {
ctx.WriteStdout(stdout)
}
ctx.WriteStderr(stderr)
// ...and then return the error
if err != nil {
return nil, errors.WithStack(err)
}
} else if ctx.Verbose {
if objIsUpToDate {
ctx.Info(tr("Using previously compiled file: %[1]s", objectFile))
} else {
ctx.Info(tr("Skipping compile of: %[1]s", objectFile))
}
}
return objectFile, nil
}
func ArchiveCompiledFiles(ctx *types.Context, buildPath *paths.Path, archiveFile *paths.Path, objectFilesToArchive paths.PathList, buildProperties *properties.Map) (*paths.Path, error) {
archiveFilePath := buildPath.JoinPath(archiveFile)
if ctx.OnlyUpdateCompilationDatabase {
if ctx.Verbose {
ctx.Info(tr("Skipping archive creation of: %[1]s", archiveFilePath))
}
return archiveFilePath, nil
}
if archiveFileStat, err := archiveFilePath.Stat(); err == nil {
rebuildArchive := false
for _, objectFile := range objectFilesToArchive {
objectFileStat, err := objectFile.Stat()
if err != nil || objectFileStat.ModTime().After(archiveFileStat.ModTime()) {
// need to rebuild the archive
rebuildArchive = true
break
}
}
// something changed, rebuild the core archive
if rebuildArchive {
if err := archiveFilePath.Remove(); err != nil {
return nil, errors.WithStack(err)
}
} else {
if ctx.Verbose {
ctx.Info(tr("Using previously compiled file: %[1]s", archiveFilePath))
}
return archiveFilePath, nil
}
}
for _, objectFile := range objectFilesToArchive {
properties := buildProperties.Clone()
properties.Set(constants.BUILD_PROPERTIES_ARCHIVE_FILE, archiveFilePath.Base())
properties.SetPath(constants.BUILD_PROPERTIES_ARCHIVE_FILE_PATH, archiveFilePath)
properties.SetPath(constants.BUILD_PROPERTIES_OBJECT_FILE, objectFile)
command, err := PrepareCommandForRecipe(properties, constants.RECIPE_AR_PATTERN, false)
if err != nil {
return nil, errors.WithStack(err)
}
_, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */)
if err != nil {
return nil, errors.WithStack(err)
}
}
return archiveFilePath, nil
}
const COMMANDLINE_LIMIT = 30000
func PrepareCommandForRecipe(buildProperties *properties.Map, recipe string, removeUnsetProperties bool) (*executils.Process, error) {
pattern := buildProperties.Get(recipe)
if pattern == "" {
return nil, errors.Errorf(tr("%[1]s pattern is missing"), recipe)
}
commandLine := buildProperties.ExpandPropsInString(pattern)
if removeUnsetProperties {
commandLine = properties.DeleteUnexpandedPropsFromString(commandLine)
}
parts, err := properties.SplitQuotedString(commandLine, `"'`, false)
if err != nil {
return nil, errors.WithStack(err)
}
// if the overall commandline is too long for the platform
// try reducing the length by making the filenames relative
// and changing working directory to build.path
var relativePath string
if len(commandLine) > COMMANDLINE_LIMIT {
relativePath = buildProperties.Get("build.path")
for i, arg := range parts {
if _, err := os.Stat(arg); os.IsNotExist(err) {
continue
}
rel, err := filepath.Rel(relativePath, arg)
if err == nil && !strings.Contains(rel, "..") && len(rel) < len(arg) {
parts[i] = rel
}
}
}
command, err := executils.NewProcess(nil, parts...)
if err != nil {
return nil, errors.WithStack(err)
}
if relativePath != "" {
command.SetDir(relativePath)
}
return command, nil
}
...@@ -17,19 +17,13 @@ ...@@ -17,19 +17,13 @@
package constants package constants
const BUILD_OPTIONS_FILE = "build.options.json" const BUILD_OPTIONS_FILE = "build.options.json"
const BUILD_PROPERTIES_ARCHIVE_FILE = "archive_file"
const BUILD_PROPERTIES_ARCHIVE_FILE_PATH = "archive_file_path"
const BUILD_PROPERTIES_ARCH_OVERRIDE_CHECK = "architecture.override_check" const BUILD_PROPERTIES_ARCH_OVERRIDE_CHECK = "architecture.override_check"
const BUILD_PROPERTIES_BOOTLOADER_FILE = "bootloader.file" const BUILD_PROPERTIES_BOOTLOADER_FILE = "bootloader.file"
const BUILD_PROPERTIES_BOOTLOADER_NOBLINK = "bootloader.noblink" const BUILD_PROPERTIES_BOOTLOADER_NOBLINK = "bootloader.noblink"
const BUILD_PROPERTIES_BUILD_BOARD = "build.board" const BUILD_PROPERTIES_BUILD_BOARD = "build.board"
const BUILD_PROPERTIES_BUILD_MCU = "build.mcu" const BUILD_PROPERTIES_BUILD_MCU = "build.mcu"
const BUILD_PROPERTIES_COMPILER_C_ELF_FLAGS = "compiler.c.elf.flags"
const BUILD_PROPERTIES_COMPILER_LDFLAGS = "compiler.ldflags" const BUILD_PROPERTIES_COMPILER_LDFLAGS = "compiler.ldflags"
const BUILD_PROPERTIES_COMPILER_CPP_FLAGS = "compiler.cpp.flags" const BUILD_PROPERTIES_COMPILER_CPP_FLAGS = "compiler.cpp.flags"
const BUILD_PROPERTIES_COMPILER_WARNING_FLAGS = "compiler.warning_flags"
const BUILD_PROPERTIES_INCLUDES = "includes"
const BUILD_PROPERTIES_OBJECT_FILE = "object_file"
const BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH = "runtime.platform.path" const BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH = "runtime.platform.path"
const EMPTY_STRING = "" const EMPTY_STRING = ""
const FOLDER_BOOTLOADERS = "bootloaders" const FOLDER_BOOTLOADERS = "bootloaders"
...@@ -50,9 +44,7 @@ const PACKAGE_TOOLS = "tools" ...@@ -50,9 +44,7 @@ const PACKAGE_TOOLS = "tools"
const PLATFORM_ARCHITECTURE = "architecture" const PLATFORM_ARCHITECTURE = "architecture"
const PLATFORM_URL = "url" const PLATFORM_URL = "url"
const PLATFORM_VERSION = "version" const PLATFORM_VERSION = "version"
const RECIPE_AR_PATTERN = "recipe.ar.pattern"
const RECIPE_C_COMBINE_PATTERN = "recipe.c.combine.pattern" const RECIPE_C_COMBINE_PATTERN = "recipe.c.combine.pattern"
const SPACE = " "
const TOOL_NAME = "name" const TOOL_NAME = "name"
const TOOL_URL = "url" const TOOL_URL = "url"
const TOOL_VERSION = "version" const TOOL_VERSION = "version"
...@@ -28,7 +28,6 @@ import ( ...@@ -28,7 +28,6 @@ import (
"github.com/arduino/arduino-cli/arduino/builder/utils" "github.com/arduino/arduino-cli/arduino/builder/utils"
"github.com/arduino/arduino-cli/arduino/globals" "github.com/arduino/arduino-cli/arduino/globals"
"github.com/arduino/arduino-cli/legacy/builder/builder_utils"
"github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/constants"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
) )
...@@ -367,7 +366,7 @@ func extractCompileFlags(ctx *types.Context, recipe string, defines, dynamicLibs ...@@ -367,7 +366,7 @@ func extractCompileFlags(ctx *types.Context, recipe string, defines, dynamicLibs
return target return target
} }
command, _ := builder_utils.PrepareCommandForRecipe(ctx.BuildProperties, recipe, true) command, _ := utils.PrepareCommandForRecipe(ctx.BuildProperties, recipe, true)
for _, arg := range command.GetArgs() { for _, arg := range command.GetArgs() {
if strings.HasPrefix(arg, "-D") { if strings.HasPrefix(arg, "-D") {
......
// 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
// 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/pkg/errors"
)
type FailIfBuildPathEqualsSketchPath struct{}
func (s *FailIfBuildPathEqualsSketchPath) Run(ctx *types.Context) error {
buildPath := ctx.BuildPath.Canonical()
sketchPath := ctx.Sketch.FullPath.Canonical()
if buildPath.EqualsTo(sketchPath) {
return errors.New(tr("Sketch cannot be located in build path. Please specify a different build path"))
}
return nil
}
...@@ -19,74 +19,111 @@ import ( ...@@ -19,74 +19,111 @@ import (
"crypto/md5" "crypto/md5"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io"
"os" "os"
"strings" "strings"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/arduino/builder/cpp"
"github.com/arduino/arduino-cli/arduino/builder/progress"
"github.com/arduino/arduino-cli/arduino/builder/utils"
"github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/buildcache" "github.com/arduino/arduino-cli/buildcache"
"github.com/arduino/arduino-cli/i18n" "github.com/arduino/arduino-cli/i18n"
f "github.com/arduino/arduino-cli/internal/algorithms" 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/constants"
"github.com/arduino/arduino-cli/legacy/builder/types" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/go-paths-helper" "github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type CoreBuilder struct{}
var tr = i18n.Tr var tr = i18n.Tr
func (s *CoreBuilder) Run(ctx *types.Context) error { func CoreBuilder(
coreBuildPath := ctx.CoreBuildPath buildPath, coreBuildPath, coreBuildCachePath *paths.Path,
coreBuildCachePath := ctx.CoreBuildCachePath buildProperties *properties.Map,
buildProperties := ctx.BuildProperties actualPlatform *cores.PlatformRelease,
verbose, onlyUpdateCompilationDatabase, clean bool,
compilationDatabase *builder.CompilationDatabase,
jobs int,
warningsLevel string,
stdoutWriter, stderrWriter io.Writer,
verboseInfoFn func(msg string),
verboseStdoutFn, verboseStderrFn func(data []byte),
progress *progress.Struct, progressCB rpc.TaskProgressCB,
) (paths.PathList, *paths.Path, error) {
if err := coreBuildPath.MkdirAll(); err != nil { if err := coreBuildPath.MkdirAll(); err != nil {
return errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
if coreBuildCachePath != nil { if coreBuildCachePath != nil {
if _, err := coreBuildCachePath.RelTo(ctx.BuildPath); err != nil { if _, err := coreBuildCachePath.RelTo(buildPath); err != nil {
ctx.Info(tr("Couldn't deeply cache core build: %[1]s", err)) verboseInfoFn(tr("Couldn't deeply cache core build: %[1]s", err))
ctx.Info(tr("Running normal build of the core...")) verboseInfoFn(tr("Running normal build of the core..."))
coreBuildCachePath = nil coreBuildCachePath = nil
ctx.CoreBuildCachePath = nil
} else if err := coreBuildCachePath.MkdirAll(); err != nil { } else if err := coreBuildCachePath.MkdirAll(); err != nil {
return errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
} }
archiveFile, objectFiles, err := compileCore(ctx, coreBuildPath, coreBuildCachePath, buildProperties) archiveFile, objectFiles, err := compileCore(
verbose, onlyUpdateCompilationDatabase, clean,
actualPlatform,
coreBuildPath, coreBuildCachePath,
buildProperties,
compilationDatabase,
jobs,
warningsLevel,
stdoutWriter, stderrWriter,
verboseInfoFn,
verboseStdoutFn, verboseStderrFn,
progress, progressCB,
)
if err != nil { if err != nil {
return errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
ctx.CoreArchiveFilePath = archiveFile return objectFiles, archiveFile, nil
ctx.CoreObjectsFiles = objectFiles
return nil
} }
func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *paths.Path, buildProperties *properties.Map) (*paths.Path, paths.PathList, error) { func compileCore(
verbose, onlyUpdateCompilationDatabase, clean bool,
actualPlatform *cores.PlatformRelease,
buildPath, buildCachePath *paths.Path,
buildProperties *properties.Map,
compilationDatabase *builder.CompilationDatabase,
jobs int,
warningsLevel string,
stdoutWriter, stderrWriter io.Writer,
verboseInfoFn func(msg string),
verboseStdoutFn, verboseStderrFn func(data []byte),
progress *progress.Struct, progressCB rpc.TaskProgressCB,
) (*paths.Path, paths.PathList, error) {
coreFolder := buildProperties.GetPath("build.core.path") coreFolder := buildProperties.GetPath("build.core.path")
variantFolder := buildProperties.GetPath("build.variant.path") variantFolder := buildProperties.GetPath("build.variant.path")
targetCoreFolder := buildProperties.GetPath(constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH) targetCoreFolder := buildProperties.GetPath(constants.BUILD_PROPERTIES_RUNTIME_PLATFORM_PATH)
includes := []string{} includes := []string{coreFolder.String()}
includes = append(includes, coreFolder.String())
if variantFolder != nil && variantFolder.IsDir() { if variantFolder != nil && variantFolder.IsDir() {
includes = append(includes, variantFolder.String()) includes = append(includes, variantFolder.String())
} }
includes = f.Map(includes, cpp.WrapWithHyphenI) includes = f.Map(includes, cpp.WrapWithHyphenI)
var err error var err error
variantObjectFiles := paths.NewPathList() variantObjectFiles := paths.NewPathList()
if variantFolder != nil && variantFolder.IsDir() { if variantFolder != nil && variantFolder.IsDir() {
variantObjectFiles, err = builder_utils.CompileFilesRecursive(ctx, variantFolder, buildPath, buildProperties, includes) variantObjectFiles, err = utils.CompileFilesRecursive(
variantFolder, buildPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
verbose,
warningsLevel,
stdoutWriter, stderrWriter,
verboseInfoFn, verboseStdoutFn, verboseStderrFn,
progress, progressCB,
)
if err != nil { if err != nil {
return nil, nil, errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
...@@ -98,7 +135,8 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path ...@@ -98,7 +135,8 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
archivedCoreName := GetCachedCoreArchiveDirName( archivedCoreName := GetCachedCoreArchiveDirName(
buildProperties.Get("build.fqbn"), buildProperties.Get("build.fqbn"),
buildProperties.Get("compiler.optimization_flags"), buildProperties.Get("compiler.optimization_flags"),
realCoreFolder) realCoreFolder,
)
targetArchivedCore = buildCachePath.Join(archivedCoreName, "core.a") targetArchivedCore = buildCachePath.Join(archivedCoreName, "core.a")
if _, err := buildcache.New(buildCachePath).GetOrCreate(archivedCoreName); errors.Is(err, buildcache.CreateDirErr) { if _, err := buildcache.New(buildCachePath).GetOrCreate(archivedCoreName); errors.Is(err, buildcache.CreateDirErr) {
...@@ -106,14 +144,14 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path ...@@ -106,14 +144,14 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
} }
var canUseArchivedCore bool var canUseArchivedCore bool
if ctx.OnlyUpdateCompilationDatabase || ctx.Clean { if onlyUpdateCompilationDatabase || clean {
canUseArchivedCore = false canUseArchivedCore = false
} else if isOlder, err := builder_utils.DirContentIsOlderThan(realCoreFolder, targetArchivedCore); err != nil || !isOlder { } else if isOlder, err := utils.DirContentIsOlderThan(realCoreFolder, targetArchivedCore); err != nil || !isOlder {
// Recreate the archive if ANY of the core files (including platform.txt) has changed // Recreate the archive if ANY of the core files (including platform.txt) has changed
canUseArchivedCore = false canUseArchivedCore = false
} else if targetCoreFolder == nil || realCoreFolder.EquivalentTo(targetCoreFolder) { } else if targetCoreFolder == nil || realCoreFolder.EquivalentTo(targetCoreFolder) {
canUseArchivedCore = true canUseArchivedCore = true
} else if isOlder, err := builder_utils.DirContentIsOlderThan(targetCoreFolder, targetArchivedCore); err != nil || !isOlder { } else if isOlder, err := utils.DirContentIsOlderThan(targetCoreFolder, targetArchivedCore); err != nil || !isOlder {
// Recreate the archive if ANY of the build core files (including platform.txt) has changed // Recreate the archive if ANY of the build core files (including platform.txt) has changed
canUseArchivedCore = false canUseArchivedCore = false
} else { } else {
...@@ -122,35 +160,51 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path ...@@ -122,35 +160,51 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
if canUseArchivedCore { if canUseArchivedCore {
// use archived core // use archived core
if ctx.Verbose { if verbose {
ctx.Info(tr("Using precompiled core: %[1]s", targetArchivedCore)) verboseInfoFn(tr("Using precompiled core: %[1]s", targetArchivedCore))
} }
return targetArchivedCore, variantObjectFiles, nil return targetArchivedCore, variantObjectFiles, nil
} }
} }
coreObjectFiles, err := builder_utils.CompileFilesRecursive(ctx, coreFolder, buildPath, buildProperties, includes) coreObjectFiles, err := utils.CompileFilesRecursive(
coreFolder, buildPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
verbose,
warningsLevel,
stdoutWriter, stderrWriter,
verboseInfoFn, verboseStdoutFn, verboseStderrFn,
progress, progressCB,
)
if err != nil { if err != nil {
return nil, nil, errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
archiveFile, err := builder_utils.ArchiveCompiledFiles(ctx, buildPath, paths.New("core.a"), coreObjectFiles, buildProperties) archiveFile, verboseInfo, err := utils.ArchiveCompiledFiles(
buildPath, paths.New("core.a"), coreObjectFiles, buildProperties,
onlyUpdateCompilationDatabase, verbose, stdoutWriter, stderrWriter,
)
if verbose {
verboseInfoFn(string(verboseInfo))
}
if err != nil { if err != nil {
return nil, nil, errors.WithStack(err) return nil, nil, errors.WithStack(err)
} }
// archive core.a // archive core.a
if targetArchivedCore != nil && !ctx.OnlyUpdateCompilationDatabase { if targetArchivedCore != nil && !onlyUpdateCompilationDatabase {
err := archiveFile.CopyTo(targetArchivedCore) err := archiveFile.CopyTo(targetArchivedCore)
if ctx.Verbose { if verbose {
if err == nil { if err == nil {
ctx.Info(tr("Archiving built core (caching) in: %[1]s", targetArchivedCore)) verboseInfoFn(tr("Archiving built core (caching) in: %[1]s", targetArchivedCore))
} else if os.IsNotExist(err) { } else if os.IsNotExist(err) {
ctx.Info(tr("Unable to cache built core, please tell %[1]s maintainers to follow %[2]s", verboseInfoFn(tr("Unable to cache built core, please tell %[1]s maintainers to follow %[2]s",
ctx.ActualPlatform, actualPlatform,
"https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-build-the-corea-archive-file")) "https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-build-the-corea-archive-file"))
} else { } else {
ctx.Info(tr("Error archiving built core (caching) in %[1]s: %[2]s", targetArchivedCore, err)) verboseInfoFn(tr("Error archiving built core (caching) in %[1]s: %[2]s", targetArchivedCore, err))
} }
} }
} }
...@@ -161,8 +215,8 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path ...@@ -161,8 +215,8 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path
// GetCachedCoreArchiveDirName returns the directory name to be used to store // GetCachedCoreArchiveDirName returns the directory name to be used to store
// the global cached core.a. // the global cached core.a.
func GetCachedCoreArchiveDirName(fqbn string, optimizationFlags string, coreFolder *paths.Path) string { func GetCachedCoreArchiveDirName(fqbn string, optimizationFlags string, coreFolder *paths.Path) string {
fqbnToUnderscore := strings.Replace(fqbn, ":", "_", -1) fqbnToUnderscore := strings.ReplaceAll(fqbn, ":", "_")
fqbnToUnderscore = strings.Replace(fqbnToUnderscore, "=", "_", -1) fqbnToUnderscore = strings.ReplaceAll(fqbnToUnderscore, "=", "_")
if absCoreFolder, err := coreFolder.Abs(); err == nil { if absCoreFolder, err := coreFolder.Abs(); err == nil {
coreFolder = absCoreFolder coreFolder = absCoreFolder
} // silently continue if absolute path can't be detected } // silently continue if absolute path can't be detected
......
This diff is collapsed.
...@@ -16,55 +16,68 @@ ...@@ -16,55 +16,68 @@
package phases package phases
import ( import (
"bytes"
"io"
"strings" "strings"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/builder/utils"
f "github.com/arduino/arduino-cli/internal/algorithms" 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"
"github.com/arduino/arduino-cli/legacy/builder/utils"
"github.com/arduino/go-paths-helper" "github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type Linker struct{} func Linker(
onlyUpdateCompilationDatabase, verbose bool,
func (s *Linker) Run(ctx *types.Context) error { sketchObjectFiles, librariesObjectFiles, coreObjectsFiles paths.PathList,
if ctx.OnlyUpdateCompilationDatabase { coreArchiveFilePath, buildPath *paths.Path,
if ctx.Verbose { buildProperties *properties.Map,
ctx.Info(tr("Skip linking of final executable.")) stdoutWriter, stderrWriter io.Writer,
warningsLevel string,
) ([]byte, error) {
verboseInfo := &bytes.Buffer{}
if onlyUpdateCompilationDatabase {
if verbose {
verboseInfo.WriteString(tr("Skip linking of final executable."))
} }
return nil return verboseInfo.Bytes(), nil
} }
objectFilesSketch := ctx.SketchObjectFiles objectFilesSketch := sketchObjectFiles
objectFilesLibraries := ctx.LibrariesObjectFiles objectFilesLibraries := librariesObjectFiles
objectFilesCore := ctx.CoreObjectsFiles objectFilesCore := coreObjectsFiles
objectFiles := paths.NewPathList() objectFiles := paths.NewPathList()
objectFiles.AddAll(objectFilesSketch) objectFiles.AddAll(objectFilesSketch)
objectFiles.AddAll(objectFilesLibraries) objectFiles.AddAll(objectFilesLibraries)
objectFiles.AddAll(objectFilesCore) objectFiles.AddAll(objectFilesCore)
coreArchiveFilePath := ctx.CoreArchiveFilePath
buildPath := ctx.BuildPath
coreDotARelPath, err := buildPath.RelTo(coreArchiveFilePath) coreDotARelPath, err := buildPath.RelTo(coreArchiveFilePath)
if err != nil { if err != nil {
return errors.WithStack(err) return nil, errors.WithStack(err)
} }
buildProperties := ctx.BuildProperties verboseInfoOut, err := link(
objectFiles, coreDotARelPath, coreArchiveFilePath, buildProperties,
err = link(ctx, objectFiles, coreDotARelPath, coreArchiveFilePath, buildProperties) verbose, stdoutWriter, stderrWriter, warningsLevel,
)
verboseInfo.Write(verboseInfoOut)
if err != nil { if err != nil {
return errors.WithStack(err) return verboseInfo.Bytes(), errors.WithStack(err)
} }
return nil return verboseInfo.Bytes(), nil
} }
func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths.Path, coreArchiveFilePath *paths.Path, buildProperties *properties.Map) error { func link(
objectFiles paths.PathList, coreDotARelPath *paths.Path, coreArchiveFilePath *paths.Path, buildProperties *properties.Map,
verbose bool,
stdoutWriter, stderrWriter io.Writer,
warningsLevel string,
) ([]byte, error) {
verboseBuffer := &bytes.Buffer{}
wrapWithDoubleQuotes := func(value string) string { return "\"" + value + "\"" }
objectFileList := strings.Join(f.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 // If command line length is too big (> 30000 chars), try to collect the object files into archives
...@@ -93,13 +106,16 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths ...@@ -93,13 +106,16 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths
properties.SetPath("archive_file_path", archive) properties.SetPath("archive_file_path", archive)
properties.SetPath("object_file", object) properties.SetPath("object_file", object)
command, err := builder_utils.PrepareCommandForRecipe(properties, constants.RECIPE_AR_PATTERN, false) command, err := utils.PrepareCommandForRecipe(properties, builder.RecipeARPattern, false)
if err != nil { if err != nil {
return errors.WithStack(err) return nil, errors.WithStack(err)
} }
if _, _, err := utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */); err != nil { if verboseInfo, _, _, err := utils.ExecCommand(verbose, stdoutWriter, stderrWriter, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */); err != nil {
return errors.WithStack(err) if verbose {
verboseBuffer.WriteString(string(verboseInfo))
}
return verboseBuffer.Bytes(), errors.WithStack(err)
} }
} }
...@@ -108,21 +124,20 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths ...@@ -108,21 +124,20 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths
} }
properties := buildProperties.Clone() properties := buildProperties.Clone()
properties.Set(constants.BUILD_PROPERTIES_COMPILER_C_ELF_FLAGS, properties.Get(constants.BUILD_PROPERTIES_COMPILER_C_ELF_FLAGS)) properties.Set("compiler.c.elf.flags", properties.Get("compiler.c.elf.flags"))
properties.Set(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS, properties.Get(constants.BUILD_PROPERTIES_COMPILER_WARNING_FLAGS+"."+ctx.WarningsLevel)) properties.Set(builder.BuildPropertiesCompilerWarningFlags, properties.Get(builder.BuildPropertiesCompilerWarningFlags+"."+warningsLevel))
properties.Set(constants.BUILD_PROPERTIES_ARCHIVE_FILE, coreDotARelPath.String()) properties.Set(builder.BuildPropertiesArchiveFile, coreDotARelPath.String())
properties.Set(constants.BUILD_PROPERTIES_ARCHIVE_FILE_PATH, coreArchiveFilePath.String()) properties.Set(builder.BuildPropertiesArchiveFilePath, coreArchiveFilePath.String())
properties.Set("object_files", objectFileList) properties.Set("object_files", objectFileList)
command, err := builder_utils.PrepareCommandForRecipe(properties, constants.RECIPE_C_COMBINE_PATTERN, false) command, err := utils.PrepareCommandForRecipe(properties, "recipe.c.combine.pattern", false)
if err != nil { if err != nil {
return err return verboseBuffer.Bytes(), err
} }
_, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */)
return err
}
func wrapWithDoubleQuotes(value string) string { verboseInfo, _, _, err := utils.ExecCommand(verbose, stdoutWriter, stderrWriter, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */)
return "\"" + value + "\"" if verbose {
verboseBuffer.WriteString(string(verboseInfo))
}
return verboseBuffer.Bytes(), err
} }
...@@ -18,46 +18,50 @@ package phases ...@@ -18,46 +18,50 @@ package phases
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"regexp" "regexp"
"strconv" "strconv"
"github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/arduino/builder/utils"
"github.com/arduino/arduino-cli/legacy/builder/utils"
"github.com/arduino/go-properties-orderedmap" "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type Sizer struct { func Sizer(
SketchError bool onlyUpdateCompilationDatabase, sketchError, verbose bool,
} buildProperties *properties.Map,
stdoutWriter, stderrWriter io.Writer,
func (s *Sizer) Run(ctx *types.Context) error { printInfoFn, printWarnFn func(msg string),
if ctx.OnlyUpdateCompilationDatabase { warningsLevel string,
return nil ) (builder.ExecutablesFileSections, error) {
} if onlyUpdateCompilationDatabase || sketchError {
if s.SketchError { return nil, nil
return nil
} }
buildProperties := ctx.BuildProperties
if buildProperties.ContainsKey("recipe.advanced_size.pattern") { if buildProperties.ContainsKey("recipe.advanced_size.pattern") {
return checkSizeAdvanced(ctx, buildProperties) return checkSizeAdvanced(buildProperties, verbose, stdoutWriter, stderrWriter, printInfoFn, printWarnFn)
} }
return checkSize(ctx, buildProperties) return checkSize(buildProperties, verbose, stdoutWriter, stderrWriter, printInfoFn, printWarnFn, warningsLevel)
} }
func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error { func checkSizeAdvanced(buildProperties *properties.Map,
command, err := builder_utils.PrepareCommandForRecipe(properties, "recipe.advanced_size.pattern", false) verbose bool,
stdoutWriter, stderrWriter io.Writer,
printInfoFn, printWarnFn func(msg string),
) (builder.ExecutablesFileSections, error) {
command, err := utils.PrepareCommandForRecipe(buildProperties, "recipe.advanced_size.pattern", false)
if err != nil { if err != nil {
return errors.New(tr("Error while determining sketch size: %s", err)) return nil, errors.New(tr("Error while determining sketch size: %s", err))
} }
out, _, err := utils.ExecCommand(ctx, command, utils.Capture /* stdout */, utils.Show /* stderr */) verboseInfo, out, _, err := utils.ExecCommand(verbose, stdoutWriter, stderrWriter, command, utils.Capture /* stdout */, utils.Show /* stderr */)
if verbose {
printInfoFn(string(verboseInfo))
}
if err != nil { if err != nil {
return errors.New(tr("Error while determining sketch size: %s", err)) return nil, errors.New(tr("Error while determining sketch size: %s", err))
} }
type AdvancedSizerResponse struct { type AdvancedSizerResponse struct {
...@@ -67,7 +71,7 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error { ...@@ -67,7 +71,7 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error {
// likely be printed in red. Errors will stop build/upload. // likely be printed in red. Errors will stop build/upload.
Severity string `json:"severity"` Severity string `json:"severity"`
// Sections are the sections sizes for machine readable use // Sections are the sections sizes for machine readable use
Sections []types.ExecutableSectionSize `json:"sections"` Sections []builder.ExecutableSectionSize `json:"sections"`
// ErrorMessage is a one line error message like: // ErrorMessage is a one line error message like:
// "text section exceeds available space in board" // "text section exceeds available space in board"
// it must be set when Severity is "error" // it must be set when Severity is "error"
...@@ -76,71 +80,76 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error { ...@@ -76,71 +80,76 @@ func checkSizeAdvanced(ctx *types.Context, properties *properties.Map) error {
var resp AdvancedSizerResponse var resp AdvancedSizerResponse
if err := json.Unmarshal(out, &resp); err != nil { if err := json.Unmarshal(out, &resp); err != nil {
return errors.New(tr("Error while determining sketch size: %s", err)) return nil, errors.New(tr("Error while determining sketch size: %s", err))
} }
ctx.ExecutableSectionsSize = resp.Sections executableSectionsSize := resp.Sections
switch resp.Severity { switch resp.Severity {
case "error": case "error":
ctx.Warn(resp.Output) printWarnFn(resp.Output)
return errors.New(resp.ErrorMessage) return executableSectionsSize, errors.New(resp.ErrorMessage)
case "warning": case "warning":
ctx.Warn(resp.Output) printWarnFn(resp.Output)
case "info": case "info":
ctx.Info(resp.Output) printInfoFn(resp.Output)
default: default:
return fmt.Errorf("invalid '%s' severity from sketch sizer: it must be 'error', 'warning' or 'info'", resp.Severity) return executableSectionsSize, fmt.Errorf("invalid '%s' severity from sketch sizer: it must be 'error', 'warning' or 'info'", resp.Severity)
} }
return nil return executableSectionsSize, nil
} }
func checkSize(ctx *types.Context, buildProperties *properties.Map) error { func checkSize(buildProperties *properties.Map,
verbose bool,
stdoutWriter, stderrWriter io.Writer,
printInfoFn, printWarnFn func(msg string),
warningsLevel string,
) (builder.ExecutablesFileSections, error) {
properties := buildProperties.Clone() properties := buildProperties.Clone()
properties.Set("compiler.warning_flags", properties.Get("compiler.warning_flags."+ctx.WarningsLevel)) properties.Set("compiler.warning_flags", properties.Get("compiler.warning_flags."+warningsLevel))
maxTextSizeString := properties.Get("upload.maximum_size") maxTextSizeString := properties.Get("upload.maximum_size")
maxDataSizeString := properties.Get("upload.maximum_data_size") maxDataSizeString := properties.Get("upload.maximum_data_size")
if maxTextSizeString == "" { if maxTextSizeString == "" {
return nil return nil, nil
} }
maxTextSize, err := strconv.Atoi(maxTextSizeString) maxTextSize, err := strconv.Atoi(maxTextSizeString)
if err != nil { if err != nil {
return err return nil, err
} }
maxDataSize := -1 maxDataSize := -1
if maxDataSizeString != "" { if maxDataSizeString != "" {
maxDataSize, err = strconv.Atoi(maxDataSizeString) maxDataSize, err = strconv.Atoi(maxDataSizeString)
if err != nil { if err != nil {
return err return nil, err
} }
} }
textSize, dataSize, _, err := execSizeRecipe(ctx, properties) textSize, dataSize, _, err := execSizeRecipe(properties, verbose, stdoutWriter, stderrWriter, printInfoFn)
if err != nil { if err != nil {
ctx.Warn(tr("Couldn't determine program size")) printWarnFn(tr("Couldn't determine program size"))
return nil return nil, nil
} }
ctx.Info(tr("Sketch uses %[1]s bytes (%[3]s%%) of program storage space. Maximum is %[2]s bytes.", printInfoFn(tr("Sketch uses %[1]s bytes (%[3]s%%) of program storage space. Maximum is %[2]s bytes.",
strconv.Itoa(textSize), strconv.Itoa(textSize),
strconv.Itoa(maxTextSize), strconv.Itoa(maxTextSize),
strconv.Itoa(textSize*100/maxTextSize))) strconv.Itoa(textSize*100/maxTextSize)))
if dataSize >= 0 { if dataSize >= 0 {
if maxDataSize > 0 { if maxDataSize > 0 {
ctx.Info(tr("Global variables use %[1]s bytes (%[3]s%%) of dynamic memory, leaving %[4]s bytes for local variables. Maximum is %[2]s bytes.", printInfoFn(tr("Global variables use %[1]s bytes (%[3]s%%) of dynamic memory, leaving %[4]s bytes for local variables. Maximum is %[2]s bytes.",
strconv.Itoa(dataSize), strconv.Itoa(dataSize),
strconv.Itoa(maxDataSize), strconv.Itoa(maxDataSize),
strconv.Itoa(dataSize*100/maxDataSize), strconv.Itoa(dataSize*100/maxDataSize),
strconv.Itoa(maxDataSize-dataSize))) strconv.Itoa(maxDataSize-dataSize)))
} else { } else {
ctx.Info(tr("Global variables use %[1]s bytes of dynamic memory.", strconv.Itoa(dataSize))) printInfoFn(tr("Global variables use %[1]s bytes of dynamic memory.", strconv.Itoa(dataSize)))
} }
} }
ctx.ExecutableSectionsSize = []types.ExecutableSectionSize{ executableSectionsSize := []builder.ExecutableSectionSize{
{ {
Name: "text", Name: "text",
Size: textSize, Size: textSize,
...@@ -148,7 +157,7 @@ func checkSize(ctx *types.Context, buildProperties *properties.Map) error { ...@@ -148,7 +157,7 @@ func checkSize(ctx *types.Context, buildProperties *properties.Map) error {
}, },
} }
if maxDataSize > 0 { if maxDataSize > 0 {
ctx.ExecutableSectionsSize = append(ctx.ExecutableSectionsSize, types.ExecutableSectionSize{ executableSectionsSize = append(executableSectionsSize, builder.ExecutableSectionSize{
Name: "data", Name: "data",
Size: dataSize, Size: dataSize,
MaxSize: maxDataSize, MaxSize: maxDataSize,
...@@ -156,36 +165,43 @@ func checkSize(ctx *types.Context, buildProperties *properties.Map) error { ...@@ -156,36 +165,43 @@ func checkSize(ctx *types.Context, buildProperties *properties.Map) error {
} }
if textSize > maxTextSize { if textSize > maxTextSize {
ctx.Warn(tr("Sketch too big; see %[1]s for tips on reducing it.", "https://support.arduino.cc/hc/en-us/articles/360013825179")) printWarnFn(tr("Sketch too big; see %[1]s for tips on reducing it.", "https://support.arduino.cc/hc/en-us/articles/360013825179"))
return errors.New(tr("text section exceeds available space in board")) return executableSectionsSize, errors.New(tr("text section exceeds available space in board"))
} }
if maxDataSize > 0 && dataSize > maxDataSize { if maxDataSize > 0 && dataSize > maxDataSize {
ctx.Warn(tr("Not enough memory; see %[1]s for tips on reducing your footprint.", "https://support.arduino.cc/hc/en-us/articles/360013825179")) printWarnFn(tr("Not enough memory; see %[1]s for tips on reducing your footprint.", "https://support.arduino.cc/hc/en-us/articles/360013825179"))
return errors.New(tr("data section exceeds available space in board")) return executableSectionsSize, errors.New(tr("data section exceeds available space in board"))
} }
if w := properties.Get("build.warn_data_percentage"); w != "" { if w := properties.Get("build.warn_data_percentage"); w != "" {
warnDataPercentage, err := strconv.Atoi(w) warnDataPercentage, err := strconv.Atoi(w)
if err != nil { if err != nil {
return err return executableSectionsSize, err
} }
if maxDataSize > 0 && dataSize > maxDataSize*warnDataPercentage/100 { if maxDataSize > 0 && dataSize > maxDataSize*warnDataPercentage/100 {
ctx.Warn(tr("Low memory available, stability problems may occur.")) printWarnFn(tr("Low memory available, stability problems may occur."))
} }
} }
return nil return executableSectionsSize, nil
} }
func execSizeRecipe(ctx *types.Context, properties *properties.Map) (textSize int, dataSize int, eepromSize int, resErr error) { func execSizeRecipe(properties *properties.Map,
command, err := builder_utils.PrepareCommandForRecipe(properties, "recipe.size.pattern", false) verbose bool,
stdoutWriter, stderrWriter io.Writer,
printInfoFn func(msg string),
) (textSize int, dataSize int, eepromSize int, resErr error) {
command, err := utils.PrepareCommandForRecipe(properties, "recipe.size.pattern", false)
if err != nil { if err != nil {
resErr = fmt.Errorf(tr("Error while determining sketch size: %s"), err) resErr = fmt.Errorf(tr("Error while determining sketch size: %s"), err)
return return
} }
out, _, err := utils.ExecCommand(ctx, command, utils.Capture /* stdout */, utils.Show /* stderr */) verboseInfo, out, _, err := utils.ExecCommand(verbose, stdoutWriter, stderrWriter, command, utils.Capture /* stdout */, utils.Show /* stderr */)
if verbose {
printInfoFn(string(verboseInfo))
}
if err != nil { if err != nil {
resErr = fmt.Errorf(tr("Error while determining sketch size: %s"), err) resErr = fmt.Errorf(tr("Error while determining sketch size: %s"), err)
return return
......
...@@ -16,41 +16,76 @@ ...@@ -16,41 +16,76 @@
package phases package phases
import ( import (
"io"
"github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/builder/cpp" "github.com/arduino/arduino-cli/arduino/builder/cpp"
"github.com/arduino/arduino-cli/arduino/builder/progress"
"github.com/arduino/arduino-cli/arduino/builder/utils"
f "github.com/arduino/arduino-cli/internal/algorithms" f "github.com/arduino/arduino-cli/internal/algorithms"
"github.com/arduino/arduino-cli/legacy/builder/builder_utils" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type SketchBuilder struct{} func SketchBuilder(
sketchBuildPath *paths.Path,
func (s *SketchBuilder) Run(ctx *types.Context) error { buildProperties *properties.Map,
sketchBuildPath := ctx.SketchBuildPath includesFolders paths.PathList,
buildProperties := ctx.BuildProperties onlyUpdateCompilationDatabase, verbose bool,
includesFolders := ctx.SketchLibrariesDetector.IncludeFolders() compilationDatabase *builder.CompilationDatabase,
jobs int,
warningsLevel string,
stdoutWriter, stderrWriter io.Writer,
verboseInfoFn func(msg string),
verboseStdoutFn, verboseStderrFn func(data []byte),
progress *progress.Struct, progressCB rpc.TaskProgressCB,
) (paths.PathList, error) {
includes := f.Map(includesFolders.AsStrings(), cpp.WrapWithHyphenI) includes := f.Map(includesFolders.AsStrings(), cpp.WrapWithHyphenI)
if err := sketchBuildPath.MkdirAll(); err != nil { if err := sketchBuildPath.MkdirAll(); err != nil {
return errors.WithStack(err) return nil, errors.WithStack(err)
} }
objectFiles, err := builder_utils.CompileFiles(ctx, sketchBuildPath, sketchBuildPath, buildProperties, includes) sketchObjectFiles, err := utils.CompileFiles(
sketchBuildPath, sketchBuildPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
verbose,
warningsLevel,
stdoutWriter, stderrWriter,
verboseInfoFn,
verboseStdoutFn,
verboseStderrFn,
progress, progressCB,
)
if err != nil { if err != nil {
return errors.WithStack(err) return nil, errors.WithStack(err)
} }
// The "src/" subdirectory of a sketch is compiled recursively // The "src/" subdirectory of a sketch is compiled recursively
sketchSrcPath := sketchBuildPath.Join("src") sketchSrcPath := sketchBuildPath.Join("src")
if sketchSrcPath.IsDir() { if sketchSrcPath.IsDir() {
srcObjectFiles, err := builder_utils.CompileFilesRecursive(ctx, sketchSrcPath, sketchSrcPath, buildProperties, includes) srcObjectFiles, err := utils.CompileFilesRecursive(
sketchSrcPath, sketchSrcPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
verbose,
warningsLevel,
stdoutWriter, stderrWriter,
verboseInfoFn,
verboseStdoutFn,
verboseStderrFn,
progress, progressCB,
)
if err != nil { if err != nil {
return errors.WithStack(err) return nil, errors.WithStack(err)
} }
objectFiles.AddAll(srcObjectFiles) sketchObjectFiles.AddAll(srcObjectFiles)
} }
ctx.SketchObjectFiles = objectFiles return sketchObjectFiles, nil
return nil
} }
...@@ -20,9 +20,8 @@ import ( ...@@ -20,9 +20,8 @@ import (
"sort" "sort"
"strings" "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/types"
"github.com/arduino/arduino-cli/legacy/builder/utils" "github.com/arduino/arduino-cli/arduino/builder/utils"
properties "github.com/arduino/go-properties-orderedmap" properties "github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
...@@ -44,7 +43,7 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error { ...@@ -44,7 +43,7 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error {
for _, recipe := range recipes { for _, recipe := range recipes {
logrus.Debugf(fmt.Sprintf("Running recipe: %s", recipe)) logrus.Debugf(fmt.Sprintf("Running recipe: %s", recipe))
command, err := builder_utils.PrepareCommandForRecipe(properties, recipe, false) command, err := utils.PrepareCommandForRecipe(properties, recipe, false)
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
...@@ -56,7 +55,10 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error { ...@@ -56,7 +55,10 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error {
return nil return nil
} }
_, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */) verboseInfo, _, _, err := utils.ExecCommand(ctx.Verbose, ctx.Stdout, ctx.Stderr, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */)
if ctx.Verbose {
ctx.Info(string(verboseInfo))
}
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
......
...@@ -84,11 +84,8 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat ...@@ -84,11 +84,8 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat
// NoError(t, err) // NoError(t, err)
fmt.Println(err) fmt.Println(err)
} }
if !ctx.CanUseCachedTools { if ctx.BuiltInToolsDirs != nil {
if ctx.BuiltInToolsDirs != nil { pmb.LoadToolsFromBundleDirectories(ctx.BuiltInToolsDirs)
pmb.LoadToolsFromBundleDirectories(ctx.BuiltInToolsDirs)
}
ctx.CanUseCachedTools = true
} }
pm := pmb.Build() pm := pmb.Build()
pme, _ /* never release... */ := pm.NewExplorer() pme, _ /* never release... */ := pm.NewExplorer()
...@@ -100,7 +97,7 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat ...@@ -100,7 +97,7 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat
ctx.Sketch = sk ctx.Sketch = sk
} }
ctx.Builder = bldr.NewBuilder(ctx.Sketch) ctx.Builder = bldr.NewBuilder(ctx.Sketch, nil)
if fqbn != "" { if fqbn != "" {
ctx.FQBN = parseFQBN(t, fqbn) ctx.FQBN = parseFQBN(t, fqbn)
targetPackage, targetPlatform, targetBoard, buildProperties, buildPlatform, err := pme.ResolveFQBN(ctx.FQBN) targetPackage, targetPlatform, targetBoard, buildProperties, buildPlatform, err := pme.ResolveFQBN(ctx.FQBN)
...@@ -125,7 +122,7 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat ...@@ -125,7 +122,7 @@ func prepareBuilderTestContext(t *testing.T, ctx *types.Context, sketchPath *pat
if !stepToSkip[skipLibraries] { if !stepToSkip[skipLibraries] {
lm, libsResolver, _, err := detector.LibrariesLoader( lm, libsResolver, _, err := detector.LibrariesLoader(
false, nil, false, nil,
ctx.BuiltInLibrariesDirs, ctx.LibraryDirs, ctx.OtherLibrariesDirs, ctx.BuiltInLibrariesDirs, nil, ctx.OtherLibrariesDirs,
ctx.ActualPlatform, ctx.TargetPlatform, ctx.ActualPlatform, ctx.TargetPlatform,
) )
require.NoError(t, err) require.NoError(t, err)
......
...@@ -21,7 +21,6 @@ import ( ...@@ -21,7 +21,6 @@ import (
"github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/libraries" "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/types"
paths "github.com/arduino/go-paths-helper" paths "github.com/arduino/go-paths-helper"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -40,13 +39,6 @@ func SetupBuildPath(t *testing.T, ctx *types.Context) *paths.Path { ...@@ -40,13 +39,6 @@ func SetupBuildPath(t *testing.T, ctx *types.Context) *paths.Path {
return buildPath return buildPath
} }
func SetupBuildCachePath(t *testing.T, ctx *types.Context) *paths.Path {
buildCachePath, err := paths.MkTempDir(constants.EMPTY_STRING, "test_build_cache")
require.NoError(t, err)
ctx.CoreBuildCachePath = buildCachePath
return buildCachePath
}
func parseFQBN(t *testing.T, fqbnIn string) *cores.FQBN { func parseFQBN(t *testing.T, fqbnIn string) *cores.FQBN {
fqbn, err := cores.ParseFQBN(fqbnIn) fqbn, err := cores.ParseFQBN(fqbnIn)
require.NoError(t, err) require.NoError(t, err)
......
...@@ -49,7 +49,7 @@ func TestLoadLibrariesAVR(t *testing.T) { ...@@ -49,7 +49,7 @@ func TestLoadLibrariesAVR(t *testing.T) {
lm, libsResolver, _, err := detector.LibrariesLoader( lm, libsResolver, _, err := detector.LibrariesLoader(
false, nil, false, nil,
ctx.BuiltInLibrariesDirs, ctx.LibraryDirs, ctx.OtherLibrariesDirs, ctx.BuiltInLibrariesDirs, nil, ctx.OtherLibrariesDirs,
ctx.ActualPlatform, ctx.TargetPlatform, ctx.ActualPlatform, ctx.TargetPlatform,
) )
require.NoError(t, err) require.NoError(t, err)
...@@ -153,7 +153,7 @@ func TestLoadLibrariesSAM(t *testing.T) { ...@@ -153,7 +153,7 @@ func TestLoadLibrariesSAM(t *testing.T) {
lm, libsResolver, _, err := detector.LibrariesLoader( lm, libsResolver, _, err := detector.LibrariesLoader(
false, nil, false, nil,
ctx.BuiltInLibrariesDirs, ctx.LibraryDirs, ctx.OtherLibrariesDirs, ctx.BuiltInLibrariesDirs, nil, ctx.OtherLibrariesDirs,
ctx.ActualPlatform, ctx.TargetPlatform, ctx.ActualPlatform, ctx.TargetPlatform,
) )
require.NoError(t, err) require.NoError(t, err)
...@@ -230,7 +230,7 @@ func TestLoadLibrariesAVRNoDuplicateLibrariesFolders(t *testing.T) { ...@@ -230,7 +230,7 @@ func TestLoadLibrariesAVRNoDuplicateLibrariesFolders(t *testing.T) {
lm, _, _, err := detector.LibrariesLoader( lm, _, _, err := detector.LibrariesLoader(
false, nil, false, nil,
ctx.BuiltInLibrariesDirs, ctx.LibraryDirs, ctx.OtherLibrariesDirs, ctx.BuiltInLibrariesDirs, nil, ctx.OtherLibrariesDirs,
ctx.ActualPlatform, ctx.TargetPlatform, ctx.ActualPlatform, ctx.TargetPlatform,
) )
require.NoError(t, err) require.NoError(t, err)
...@@ -253,7 +253,7 @@ func TestLoadLibrariesMyAVRPlatform(t *testing.T) { ...@@ -253,7 +253,7 @@ func TestLoadLibrariesMyAVRPlatform(t *testing.T) {
lm, _, _, err := detector.LibrariesLoader( lm, _, _, err := detector.LibrariesLoader(
false, nil, false, nil,
ctx.BuiltInLibrariesDirs, ctx.LibraryDirs, ctx.OtherLibrariesDirs, ctx.BuiltInLibrariesDirs, nil, ctx.OtherLibrariesDirs,
ctx.ActualPlatform, ctx.TargetPlatform, ctx.ActualPlatform, ctx.TargetPlatform,
) )
require.NoError(t, err) require.NoError(t, err)
......
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
"github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/builder"
"github.com/arduino/arduino-cli/arduino/builder/detector" "github.com/arduino/arduino-cli/arduino/builder/detector"
"github.com/arduino/arduino-cli/arduino/builder/progress"
"github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores"
"github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/arduino/sketch" "github.com/arduino/arduino-cli/arduino/sketch"
...@@ -32,34 +33,6 @@ import ( ...@@ -32,34 +33,6 @@ import (
properties "github.com/arduino/go-properties-orderedmap" properties "github.com/arduino/go-properties-orderedmap"
) )
type ProgressStruct struct {
Progress float32
StepAmount float32
Parent *ProgressStruct
}
func (p *ProgressStruct) AddSubSteps(steps int) {
p.Parent = &ProgressStruct{
Progress: p.Progress,
StepAmount: p.StepAmount,
Parent: p.Parent,
}
if p.StepAmount == 0.0 {
p.StepAmount = 100.0
}
p.StepAmount /= float32(steps)
}
func (p *ProgressStruct) RemoveSubSteps() {
p.Progress = p.Parent.Progress
p.StepAmount = p.Parent.StepAmount
p.Parent = p.Parent.Parent
}
func (p *ProgressStruct) CompleteStep() {
p.Progress += p.StepAmount
}
// Context structure // Context structure
type Context struct { type Context struct {
Builder *builder.Builder Builder *builder.Builder
...@@ -70,8 +43,6 @@ type Context struct { ...@@ -70,8 +43,6 @@ type Context struct {
BuiltInToolsDirs paths.PathList BuiltInToolsDirs paths.PathList
BuiltInLibrariesDirs *paths.Path BuiltInLibrariesDirs *paths.Path
OtherLibrariesDirs paths.PathList OtherLibrariesDirs paths.PathList
LibraryDirs paths.PathList // List of paths pointing to individual library root folders
WatchedLocations paths.PathList
FQBN *cores.FQBN FQBN *cores.FQBN
Clean bool Clean bool
...@@ -86,17 +57,15 @@ type Context struct { ...@@ -86,17 +57,15 @@ type Context struct {
TargetPlatform *cores.PlatformRelease TargetPlatform *cores.PlatformRelease
ActualPlatform *cores.PlatformRelease ActualPlatform *cores.PlatformRelease
BuildProperties *properties.Map BuildProperties *properties.Map
BuildPath *paths.Path BuildPath *paths.Path
SketchBuildPath *paths.Path SketchBuildPath *paths.Path
CoreBuildPath *paths.Path CoreBuildPath *paths.Path
CoreBuildCachePath *paths.Path CoreArchiveFilePath *paths.Path
CoreArchiveFilePath *paths.Path CoreObjectsFiles paths.PathList
CoreObjectsFiles paths.PathList LibrariesBuildPath *paths.Path
LibrariesBuildPath *paths.Path LibrariesObjectFiles paths.PathList
LibrariesObjectFiles paths.PathList SketchObjectFiles paths.PathList
SketchObjectFiles paths.PathList
IgnoreSketchFolderNameErrors bool
Sketch *sketch.Sketch Sketch *sketch.Sketch
WarningsLevel string WarningsLevel string
...@@ -108,19 +77,13 @@ type Context struct { ...@@ -108,19 +77,13 @@ type Context struct {
Verbose bool Verbose bool
// Dry run, only create progress map // Dry run, only create progress map
Progress ProgressStruct Progress progress.Struct
// Send progress events to this callback // Send progress events to this callback
ProgressCB rpc.TaskProgressCB ProgressCB rpc.TaskProgressCB
// Custom build properties defined by user (line by line as "key=value" pairs) // Custom build properties defined by user (line by line as "key=value" pairs)
CustomBuildProperties []string CustomBuildProperties []string
// Reuse old tools since the backing storage didn't change
CanUseCachedTools bool
// Experimental: use arduino-preprocessor to create prototypes
UseArduinoPreprocessor bool
// Parallel processes // Parallel processes
Jobs int Jobs int
...@@ -130,7 +93,7 @@ type Context struct { ...@@ -130,7 +93,7 @@ type Context struct {
stdLock sync.Mutex stdLock sync.Mutex
// Sizer results // Sizer results
ExecutableSectionsSize ExecutablesFileSections ExecutableSectionsSize builder.ExecutablesFileSections
// Compilation Database to build/update // Compilation Database to build/update
CompilationDatabase *builder.CompilationDatabase CompilationDatabase *builder.CompilationDatabase
...@@ -143,29 +106,6 @@ type Context struct { ...@@ -143,29 +106,6 @@ type Context struct {
SourceOverride map[string]string SourceOverride map[string]string
} }
// ExecutableSectionSize represents a section of the executable output file
type ExecutableSectionSize struct {
Name string `json:"name"`
Size int `json:"size"`
MaxSize int `json:"max_size"`
}
// ExecutablesFileSections is an array of ExecutablesFileSection
type ExecutablesFileSections []ExecutableSectionSize
// ToRPCExecutableSectionSizeArray transforms this array into a []*rpc.ExecutableSectionSize
func (s ExecutablesFileSections) ToRPCExecutableSectionSizeArray() []*rpc.ExecutableSectionSize {
res := []*rpc.ExecutableSectionSize{}
for _, section := range s {
res = append(res, &rpc.ExecutableSectionSize{
Name: section.Name,
Size: int64(section.Size),
MaxSize: int64(section.MaxSize),
})
}
return res
}
func (ctx *Context) ExtractBuildOptions() *properties.Map { func (ctx *Context) ExtractBuildOptions() *properties.Map {
opts := properties.NewMap() opts := properties.NewMap()
opts.Set("hardwareFolders", strings.Join(ctx.HardwareDirs.AsStrings(), ",")) opts.Set("hardwareFolders", strings.Join(ctx.HardwareDirs.AsStrings(), ","))
......
// 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 utils
import (
"bytes"
"os"
"strings"
"github.com/arduino/arduino-cli/executils"
f "github.com/arduino/arduino-cli/internal/algorithms"
"github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/pkg/errors"
)
func printableArgument(arg string) string {
if strings.ContainsAny(arg, "\"\\ \t") {
arg = strings.Replace(arg, "\\", "\\\\", -1)
arg = strings.Replace(arg, "\"", "\\\"", -1)
return "\"" + arg + "\""
} else {
return arg
}
}
// Convert a command and argument slice back to a printable string.
// This adds basic escaping which is sufficient for debug output, but
// probably not for shell interpretation. This essentially reverses
// ParseCommandLine.
func PrintableCommand(parts []string) string {
return strings.Join(f.Map(parts, printableArgument), " ")
}
const (
Ignore = 0 // Redirect to null
Show = 1 // Show on stdout/stderr as normal
ShowIfVerbose = 2 // Show if verbose is set, Ignore otherwise
Capture = 3 // Capture into buffer
)
func ExecCommand(ctx *types.Context, command *executils.Process, stdout int, stderr int) ([]byte, []byte, error) {
if ctx.Verbose {
ctx.Info(PrintableCommand(command.GetArgs()))
}
stdoutBuffer := &bytes.Buffer{}
if stdout == Capture {
command.RedirectStdoutTo(stdoutBuffer)
} else if stdout == Show || (stdout == ShowIfVerbose && ctx.Verbose) {
if ctx.Stdout != nil {
command.RedirectStdoutTo(ctx.Stdout)
} else {
command.RedirectStdoutTo(os.Stdout)
}
}
stderrBuffer := &bytes.Buffer{}
if stderr == Capture {
command.RedirectStderrTo(stderrBuffer)
} else if stderr == Show || (stderr == ShowIfVerbose && ctx.Verbose) {
if ctx.Stderr != nil {
command.RedirectStderrTo(ctx.Stderr)
} else {
command.RedirectStderrTo(os.Stderr)
}
}
err := command.Start()
if err != nil {
return nil, nil, errors.WithStack(err)
}
err = command.Wait()
return stdoutBuffer.Bytes(), stderrBuffer.Bytes(), errors.WithStack(err)
}
type loggerAction struct {
onlyIfVerbose bool
warn bool
msg string
}
func (l *loggerAction) Run(ctx *types.Context) error {
if !l.onlyIfVerbose || ctx.Verbose {
if l.warn {
ctx.Warn(l.msg)
} else {
ctx.Info(l.msg)
}
}
return nil
}
func LogIfVerbose(warn bool, msg string) types.Command {
return &loggerAction{onlyIfVerbose: true, warn: warn, msg: msg}
}
...@@ -19,7 +19,7 @@ import ( ...@@ -19,7 +19,7 @@ import (
"encoding/json" "encoding/json"
"path/filepath" "path/filepath"
"github.com/arduino/arduino-cli/legacy/builder/builder_utils" "github.com/arduino/arduino-cli/arduino/builder/utils"
"github.com/arduino/arduino-cli/legacy/builder/constants" "github.com/arduino/arduino-cli/legacy/builder/constants"
"github.com/arduino/arduino-cli/legacy/builder/types" "github.com/arduino/arduino-cli/legacy/builder/types"
"github.com/arduino/go-paths-helper" "github.com/arduino/go-paths-helper"
...@@ -66,9 +66,9 @@ func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error { ...@@ -66,9 +66,9 @@ func (s *WipeoutBuildPathIfBuildOptionsChanged) Run(ctx *types.Context) error {
coreFolder := buildProperties.GetPath("build.core.path") coreFolder := buildProperties.GetPath("build.core.path")
realCoreFolder := coreFolder.Parent().Parent() realCoreFolder := coreFolder.Parent().Parent()
jsonPath := ctx.BuildPath.Join(constants.BUILD_OPTIONS_FILE) jsonPath := ctx.BuildPath.Join(constants.BUILD_OPTIONS_FILE)
coreUnchanged, _ := builder_utils.DirContentIsOlderThan(realCoreFolder, jsonPath, ".txt") coreUnchanged, _ := utils.DirContentIsOlderThan(realCoreFolder, jsonPath, ".txt")
if coreUnchanged && targetCoreFolder != nil && !realCoreFolder.EqualsTo(targetCoreFolder) { if coreUnchanged && targetCoreFolder != nil && !realCoreFolder.EqualsTo(targetCoreFolder) {
coreUnchanged, _ = builder_utils.DirContentIsOlderThan(targetCoreFolder, jsonPath, ".txt") coreUnchanged, _ = utils.DirContentIsOlderThan(targetCoreFolder, jsonPath, ".txt")
} }
if coreUnchanged { if coreUnchanged {
return nil return nil
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment