Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

legacy: Builder refactorization (part 4...) #2310

Merged
merged 21 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b735f49
move CoreBuilder in arduino/builder and move CompilationDatabase in c…
alessio-perugini Sep 11, 2023
76bb3f7
rename CompilationDatabse in Database
alessio-perugini Sep 11, 2023
9a05ce5
move libraries phases in arduino/builder
alessio-perugini Sep 11, 2023
98ca4ee
move linker phases in arduino/builder
alessio-perugini Sep 11, 2023
8f8fe39
move sketch_builder phases in arduino/builder
alessio-perugini Sep 11, 2023
b13222c
move sizer phases in arduino/builder/sizer
alessio-perugini Sep 11, 2023
9f489d6
create bulder logger
alessio-perugini Sep 11, 2023
7e80526
use builder logger in the sketLibrariesDetector
alessio-perugini Sep 11, 2023
35ffce9
replace context Info logger with BuilderLogger
alessio-perugini Sep 11, 2023
b0add32
replace context Warn logger with BuilderLogger
alessio-perugini Sep 11, 2023
73ec654
replace context WriteStdout logger with BuilderLogger
alessio-perugini Sep 11, 2023
46aaac6
replace context WriteStderr logger with BuilderLogger
alessio-perugini Sep 11, 2023
1a7dc47
directly pass the BuilderLogger to RecipeByPrefixSuffixRunner func
alessio-perugini Sep 11, 2023
9f5bd52
directly pass the BuilderLogger to part2
alessio-perugini Sep 11, 2023
93d74de
directly pass the BuilderLogger to Linker func
alessio-perugini Sep 11, 2023
455ce61
directly pass the BuilderLogger to sizer.Size func
alessio-perugini Sep 11, 2023
ce5276c
remove Stdout, Stderr and stdlock from Context
alessio-perugini Sep 11, 2023
1dc6fcc
appease golint
alessio-perugini Sep 11, 2023
396faa8
add missinig license headers
alessio-perugini Sep 11, 2023
91419cf
remove WarningsLevel from context
alessio-perugini Sep 11, 2023
2ad132c
remove Verbose from context
alessio-perugini Sep 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,55 @@
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package builder
package compilation

import (
"encoding/json"
"fmt"
"os"

"github.com/arduino/arduino-cli/executils"
"github.com/arduino/arduino-cli/i18n"
"github.com/arduino/go-paths-helper"
)

// CompilationDatabase keeps track of all the compile commands run by the builder
type CompilationDatabase struct {
Contents []CompilationCommand
var tr = i18n.Tr

// Database keeps track of all the compile commands run by the builder
type Database struct {
Contents []Command
File *paths.Path
}

// CompilationCommand keeps track of a single run of a compile command
type CompilationCommand struct {
// Command keeps track of a single run of a compile command
type Command struct {
Directory string `json:"directory"`
Command string `json:"command,omitempty"`
Arguments []string `json:"arguments,omitempty"`
File string `json:"file"`
}

// NewCompilationDatabase creates an empty CompilationDatabase
func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase {
return &CompilationDatabase{
// NewDatabase creates an empty CompilationDatabase
func NewDatabase(filename *paths.Path) *Database {
return &Database{
File: filename,
Contents: []CompilationCommand{},
Contents: []Command{},
}
}

// LoadCompilationDatabase reads a compilation database from a file
func LoadCompilationDatabase(file *paths.Path) (*CompilationDatabase, error) {
// LoadDatabase reads a compilation database from a file
func LoadDatabase(file *paths.Path) (*Database, error) {
f, err := file.ReadFile()
if err != nil {
return nil, err
}
res := NewCompilationDatabase(file)
res := NewDatabase(file)
return res, json.Unmarshal(f, &res.Contents)
}

// SaveToFile save the CompilationDatabase to file as a clangd-compatible compile_commands.json,
// see https://clang.llvm.org/docs/JSONCompilationDatabase.html
func (db *CompilationDatabase) SaveToFile() {
func (db *Database) SaveToFile() {
if jsonContents, err := json.MarshalIndent(db.Contents, "", " "); err != nil {
fmt.Println(tr("Error serializing compilation database: %s", err))
return
Expand All @@ -68,7 +71,7 @@ func (db *CompilationDatabase) SaveToFile() {
}

// Add adds a new CompilationDatabase entry
func (db *CompilationDatabase) Add(target *paths.Path, command *executils.Process) {
func (db *Database) Add(target *paths.Path, command *executils.Process) {
commandDir := command.GetDir()
if commandDir == "" {
// This mimics what Cmd.Run also does: Use Dir if specified,
Expand All @@ -80,7 +83,7 @@ func (db *CompilationDatabase) Add(target *paths.Path, command *executils.Proces
commandDir = dir
}

entry := CompilationCommand{
entry := Command{
Directory: commandDir,
Arguments: command.GetArgs(),
File: target.String(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to [email protected].

package builder
package compilation

import (
"testing"
Expand All @@ -30,11 +30,11 @@ func TestCompilationDatabase(t *testing.T) {

cmd, err := executils.NewProcess(nil, "gcc", "arg1", "arg2")
require.NoError(t, err)
db := NewCompilationDatabase(tmpfile)
db := NewDatabase(tmpfile)
db.Add(paths.New("test"), cmd)
db.SaveToFile()

db2, err := LoadCompilationDatabase(tmpfile)
db2, err := LoadDatabase(tmpfile)
require.NoError(t, err)
require.Equal(t, db, db2)
require.Len(t, db2.Contents, 1)
Expand Down
216 changes: 215 additions & 1 deletion arduino/builder/core.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,222 @@
// 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 [email protected].

package builder

import "github.com/arduino/go-paths-helper"
import (
"crypto/md5"
"encoding/hex"
"fmt"
"os"
"strings"

"github.com/arduino/arduino-cli/arduino/builder/compilation"
"github.com/arduino/arduino-cli/arduino/builder/cpp"
"github.com/arduino/arduino-cli/arduino/builder/logger"
"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"
f "github.com/arduino/arduino-cli/internal/algorithms"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/go-paths-helper"
"github.com/arduino/go-properties-orderedmap"
"github.com/pkg/errors"
)

// CoreBuildCachePath fixdoc
func (b *Builder) CoreBuildCachePath() *paths.Path {
return b.coreBuildCachePath
}

// CoreBuilder fixdoc
func CoreBuilder(
buildPath, coreBuildPath, coreBuildCachePath *paths.Path,
buildProperties *properties.Map,
actualPlatform *cores.PlatformRelease,
onlyUpdateCompilationDatabase, clean bool,
compilationDatabase *compilation.Database,
jobs int,
builderLogger *logger.BuilderLogger,
progress *progress.Struct, progressCB rpc.TaskProgressCB,
) (paths.PathList, *paths.Path, error) {
if err := coreBuildPath.MkdirAll(); err != nil {
return nil, nil, errors.WithStack(err)
}

if coreBuildCachePath != nil {
if _, err := coreBuildCachePath.RelTo(buildPath); err != nil {
builderLogger.Info(tr("Couldn't deeply cache core build: %[1]s", err))
builderLogger.Info(tr("Running normal build of the core..."))
coreBuildCachePath = nil
} else if err := coreBuildCachePath.MkdirAll(); err != nil {
return nil, nil, errors.WithStack(err)
}
}

archiveFile, objectFiles, err := compileCore(
onlyUpdateCompilationDatabase, clean,
actualPlatform,
coreBuildPath, coreBuildCachePath,
buildProperties,
compilationDatabase,
jobs,
builderLogger,
progress, progressCB,
)
if err != nil {
return nil, nil, errors.WithStack(err)
}

return objectFiles, archiveFile, nil
}

func compileCore(
onlyUpdateCompilationDatabase, clean bool,
actualPlatform *cores.PlatformRelease,
buildPath, buildCachePath *paths.Path,
buildProperties *properties.Map,
compilationDatabase *compilation.Database,
jobs int,
builderLogger *logger.BuilderLogger,
progress *progress.Struct, progressCB rpc.TaskProgressCB,
) (*paths.Path, paths.PathList, error) {
coreFolder := buildProperties.GetPath("build.core.path")
variantFolder := buildProperties.GetPath("build.variant.path")
targetCoreFolder := buildProperties.GetPath("runtime.platform.path")

includes := []string{coreFolder.String()}
if variantFolder != nil && variantFolder.IsDir() {
includes = append(includes, variantFolder.String())
}
includes = f.Map(includes, cpp.WrapWithHyphenI)

var err error
variantObjectFiles := paths.NewPathList()
if variantFolder != nil && variantFolder.IsDir() {
variantObjectFiles, err = utils.CompileFilesRecursive(
variantFolder, buildPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
builderLogger,
progress, progressCB,
)
if err != nil {
return nil, nil, errors.WithStack(err)
}
}

var targetArchivedCore *paths.Path
if buildCachePath != nil {
realCoreFolder := coreFolder.Parent().Parent()
archivedCoreName := GetCachedCoreArchiveDirName(
buildProperties.Get("build.fqbn"),
buildProperties.Get("compiler.optimization_flags"),
realCoreFolder,
)
targetArchivedCore = buildCachePath.Join(archivedCoreName, "core.a")

if _, err := buildcache.New(buildCachePath).GetOrCreate(archivedCoreName); errors.Is(err, buildcache.CreateDirErr) {
return nil, nil, fmt.Errorf(tr("creating core cache folder: %s", err))
}

var canUseArchivedCore bool
if onlyUpdateCompilationDatabase || clean {
canUseArchivedCore = false
} 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
canUseArchivedCore = false
} else if targetCoreFolder == nil || realCoreFolder.EquivalentTo(targetCoreFolder) {
canUseArchivedCore = true
} 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
canUseArchivedCore = false
} else {
canUseArchivedCore = true
}

if canUseArchivedCore {
// use archived core
if builderLogger.Verbose() {
builderLogger.Info(tr("Using precompiled core: %[1]s", targetArchivedCore))
}
return targetArchivedCore, variantObjectFiles, nil
}
}

coreObjectFiles, err := utils.CompileFilesRecursive(
coreFolder, buildPath, buildProperties, includes,
onlyUpdateCompilationDatabase,
compilationDatabase,
jobs,
builderLogger,
progress, progressCB,
)
if err != nil {
return nil, nil, errors.WithStack(err)
}

archiveFile, verboseInfo, err := utils.ArchiveCompiledFiles(
buildPath, paths.New("core.a"), coreObjectFiles, buildProperties,
onlyUpdateCompilationDatabase, builderLogger.Verbose(), builderLogger.Stdout(), builderLogger.Stderr(),
)
if builderLogger.Verbose() {
builderLogger.Info(string(verboseInfo))
}
if err != nil {
return nil, nil, errors.WithStack(err)
}

// archive core.a
if targetArchivedCore != nil && !onlyUpdateCompilationDatabase {
err := archiveFile.CopyTo(targetArchivedCore)
if builderLogger.Verbose() {
if err == nil {
builderLogger.Info(tr("Archiving built core (caching) in: %[1]s", targetArchivedCore))
} else if os.IsNotExist(err) {
builderLogger.Info(tr("Unable to cache built core, please tell %[1]s maintainers to follow %[2]s",
actualPlatform,
"https://arduino.github.io/arduino-cli/latest/platform-specification/#recipes-to-build-the-corea-archive-file"))
} else {
builderLogger.Info(tr("Error archiving built core (caching) in %[1]s: %[2]s", targetArchivedCore, err))
}
}
}

return archiveFile, variantObjectFiles, nil
}

// GetCachedCoreArchiveDirName returns the directory name to be used to store
// the global cached core.a.
func GetCachedCoreArchiveDirName(fqbn string, optimizationFlags string, coreFolder *paths.Path) string {
fqbnToUnderscore := strings.ReplaceAll(fqbn, ":", "_")
fqbnToUnderscore = strings.ReplaceAll(fqbnToUnderscore, "=", "_")
if absCoreFolder, err := coreFolder.Abs(); err == nil {
coreFolder = absCoreFolder
} // silently continue if absolute path can't be detected

md5Sum := func(data []byte) string {
md5sumBytes := md5.Sum(data)
return hex.EncodeToString(md5sumBytes[:])
}
hash := md5Sum([]byte(coreFolder.String() + optimizationFlags))
realName := fqbnToUnderscore + "_" + hash
if len(realName) > 100 {
// avoid really long names, simply hash the name again
realName = md5Sum([]byte(realName))
}
return realName
}
Loading