From 2d78752ebef0307179a7b1130098095d04d18bb9 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Fri, 11 Sep 2020 18:43:13 +0200 Subject: [PATCH 1/9] Draft: Support generating a compile_commands.json file This is still very rough and unfinished. --- arduino/builder/compilation_database.go | 73 +++++++++++++++++++++++++ commands/compile/compile.go | 3 + legacy/builder/builder.go | 5 ++ legacy/builder/builder_utils/utils.go | 3 + legacy/builder/types/context.go | 4 ++ 5 files changed, 88 insertions(+) create mode 100644 arduino/builder/compilation_database.go diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go new file mode 100644 index 00000000000..50828c3add4 --- /dev/null +++ b/arduino/builder/compilation_database.go @@ -0,0 +1,73 @@ +package builder + +import ( + "encoding/json" + "fmt" + "github.com/arduino/go-paths-helper" + "io/ioutil" + "os" + "os/exec" +) + +type compilationCommand struct { + Directory string `json:"directory"` + Arguments []string `json:"arguments"` + File string `json:"file"` +} + +type CompilationDatabase struct { + contents []compilationCommand + filename *paths.Path +} + +func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase { + return &CompilationDatabase{ + filename: filename, + } +} + +func (db *CompilationDatabase) UpdateFile(complete bool) { + // TODO: Read any existing file and use its contents for any + // kept files, or any files not in db.contents if !complete. + fmt.Printf("Writing compilation database to \"%s\"...\n", db.filename.String()) + + contents := db.contents + jsonContents, err := json.MarshalIndent(contents, "", " ") + if err != nil { + fmt.Printf("Error serializing compilation database: %s", err) + return + } + err = ioutil.WriteFile(db.filename.String(), jsonContents, 0644) + if err != nil { + fmt.Printf("Error writing compilation database: %s", err) + } +} + +func (db *CompilationDatabase) dirForCommand(command *exec.Cmd) string { + // This mimics what Cmd.Run also does: Use Dir if specified, + // current directory otherwise + if command.Dir != "" { + return command.Dir + } else { + dir, err := os.Getwd() + if err != nil { + fmt.Printf("Error getting current directory for compilation database: %s", err) + return "" + } + return dir + } +} + +func (db *CompilationDatabase) ReplaceEntry(filename *paths.Path, command *exec.Cmd) { + entry := compilationCommand{ + Directory: db.dirForCommand(command), + Arguments: command.Args, + File: filename.String(), + } + + db.contents = append(db.contents, entry) +} + +func (db *CompilationDatabase) KeepEntry(filename *paths.Path) { + // TODO +} diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 360db3638c6..4d2cc89e083 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -116,6 +116,9 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.PackageManager = pm builderCtx.FQBN = fqbn builderCtx.SketchLocation = sketch.FullPath + builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( + sketch.FullPath.Join("compile_commands.json"), + ) // FIXME: This will be redundant when arduino-builder will be part of the cli builderCtx.HardwareDirs = configuration.HardwareDirectories(configuration.Settings) diff --git a/legacy/builder/builder.go b/legacy/builder/builder.go index d4e4ed962b5..3d4a1c9e37c 100644 --- a/legacy/builder/builder.go +++ b/legacy/builder/builder.go @@ -96,6 +96,11 @@ func (s *Builder) Run(ctx *types.Context) error { mainErr := runCommands(ctx, commands) + // TODO: Make proper step? + if ctx.CompilationDatabase != nil { + ctx.CompilationDatabase.UpdateFile(mainErr != nil) + } + commands = []types.Command{ &PrintUsedAndNotUsedLibraries{SketchError: mainErr != nil}, diff --git a/legacy/builder/builder_utils/utils.go b/legacy/builder/builder_utils/utils.go index 7f1d098dea8..5e2e4bf98d4 100644 --- a/legacy/builder/builder_utils/utils.go +++ b/legacy/builder/builder_utils/utils.go @@ -258,6 +258,9 @@ func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *p return nil, errors.WithStack(err) } } else if ctx.Verbose { + if ctx.CompilationDatabase != nil { + ctx.CompilationDatabase.KeepEntry(source) + } logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_USING_PREVIOUS_COMPILED_FILE, objectFile) } diff --git a/legacy/builder/types/context.go b/legacy/builder/types/context.go index 50ca24dce93..55fd3e4a705 100644 --- a/legacy/builder/types/context.go +++ b/legacy/builder/types/context.go @@ -19,6 +19,7 @@ import ( "io" "strings" + "github.com/arduino/arduino-cli/arduino/builder" "github.com/arduino/arduino-cli/arduino/cores" "github.com/arduino/arduino-cli/arduino/cores/packagemanager" "github.com/arduino/arduino-cli/arduino/libraries" @@ -163,6 +164,9 @@ type Context struct { // Sizer results ExecutableSectionsSize ExecutablesFileSections + + // Compilation Database to build/update + CompilationDatabase *builder.CompilationDatabase } // ExecutableSectionSize represents a section of the executable output file From b52d4fbfe5d71073a9e846abc9343935f9bb7cc0 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 11 Nov 2020 21:01:56 +0100 Subject: [PATCH 2/9] Added compile flag to produce only the compilation database --- cli/compile/compile.go | 75 ++++++------ commands/compile/compile.go | 1 + legacy/builder/builder_utils/utils.go | 30 +++-- .../builder/merge_sketch_with_bootloader.go | 4 + legacy/builder/phases/core_builder.go | 6 +- legacy/builder/phases/linker.go | 7 ++ legacy/builder/phases/sizer.go | 4 +- legacy/builder/recipe_runner.go | 7 ++ legacy/builder/types/context.go | 2 + rpc/commands/compile.pb.go | 107 ++++++++++-------- rpc/commands/compile.proto | 1 + 11 files changed, 147 insertions(+), 97 deletions(-) diff --git a/cli/compile/compile.go b/cli/compile/compile.go index afa73c55c09..6216cc99035 100644 --- a/cli/compile/compile.go +++ b/cli/compile/compile.go @@ -35,25 +35,26 @@ import ( ) var ( - fqbn string // Fully Qualified Board Name, e.g.: arduino:avr:uno. - showProperties bool // Show all build preferences used instead of compiling. - preprocess bool // Print preprocessed code to stdout. - buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused. - buildPath string // Path where to save compiled files. - buildProperties []string // List of custom build properties separated by commas. Or can be used multiple times for multiple properties. - warnings string // Used to tell gcc which warning level to use. - verbose bool // Turns on verbose mode. - quiet bool // Suppresses almost every output. - vidPid string // VID/PID specific build properties. - uploadAfterCompile bool // Upload the binary after the compilation. - port string // Upload port, e.g.: COM10 or /dev/ttyACM0. - verify bool // Upload, verify uploaded binary after the upload. - exportDir string // The compiled binary is written to this file - libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths. - optimizeForDebug bool // Optimize compile output for debug, not for release - programmer string // Use the specified programmer to upload - clean bool // Cleanup the build folder and do not use any cached build - exportBinaries bool // Copies compiled binaries to sketch folder when true + fqbn string // Fully Qualified Board Name, e.g.: arduino:avr:uno. + showProperties bool // Show all build preferences used instead of compiling. + preprocess bool // Print preprocessed code to stdout. + buildCachePath string // Builds of 'core.a' are saved into this path to be cached and reused. + buildPath string // Path where to save compiled files. + buildProperties []string // List of custom build properties separated by commas. Or can be used multiple times for multiple properties. + warnings string // Used to tell gcc which warning level to use. + verbose bool // Turns on verbose mode. + quiet bool // Suppresses almost every output. + vidPid string // VID/PID specific build properties. + uploadAfterCompile bool // Upload the binary after the compilation. + port string // Upload port, e.g.: COM10 or /dev/ttyACM0. + verify bool // Upload, verify uploaded binary after the upload. + exportDir string // The compiled binary is written to this file + libraries []string // List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths. + optimizeForDebug bool // Optimize compile output for debug, not for release + programmer string // Use the specified programmer to upload + clean bool // Cleanup the build folder and do not use any cached build + exportBinaries bool // Copies compiled binaries to sketch folder when true + compilationDatabaseOnly bool // Only create compilation database without actually compiling ) // NewCommand created a new `compile` command @@ -94,6 +95,7 @@ func NewCommand() *cobra.Command { "List of custom libraries paths separated by commas. Or can be used multiple times for multiple libraries paths.") command.Flags().BoolVar(&optimizeForDebug, "optimize-for-debug", false, "Optional, optimize compile output for debugging, rather than for release.") command.Flags().StringVarP(&programmer, "programmer", "P", "", "Optional, use the specified programmer to upload.") + command.Flags().BoolVar(&compilationDatabaseOnly, "only-compilation-database", false, "Just produce the compilation database, without actually compiling.") command.Flags().BoolVar(&clean, "clean", false, "Optional, cleanup the build folder and do not use any cached build.") // We must use the following syntax for this flag since it's also bound to settings, we could use the other one too // but it wouldn't make sense since we still must explicitly set the exportBinaries variable by reading from settings. @@ -127,23 +129,24 @@ func run(cmd *cobra.Command, args []string) { exportBinaries = configuration.Settings.GetBool("sketch.always_export_binaries") compileReq := &rpc.CompileReq{ - Instance: inst, - Fqbn: fqbn, - SketchPath: sketchPath.String(), - ShowProperties: showProperties, - Preprocess: preprocess, - BuildCachePath: buildCachePath, - BuildPath: buildPath, - BuildProperties: buildProperties, - Warnings: warnings, - Verbose: verbose, - Quiet: quiet, - VidPid: vidPid, - ExportDir: exportDir, - Libraries: libraries, - OptimizeForDebug: optimizeForDebug, - Clean: clean, - ExportBinaries: exportBinaries, + Instance: inst, + Fqbn: fqbn, + SketchPath: sketchPath.String(), + ShowProperties: showProperties, + Preprocess: preprocess, + BuildCachePath: buildCachePath, + BuildPath: buildPath, + BuildProperties: buildProperties, + Warnings: warnings, + Verbose: verbose, + Quiet: quiet, + VidPid: vidPid, + ExportDir: exportDir, + Libraries: libraries, + OptimizeForDebug: optimizeForDebug, + Clean: clean, + ExportBinaries: exportBinaries, + CreateCompilationDatabaseOnly: compilationDatabaseOnly, } compileOut := new(bytes.Buffer) compileErr := new(bytes.Buffer) diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 4d2cc89e083..7cca7363546 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -191,6 +191,7 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.ExecStderr = errStream builderCtx.SetLogger(i18n.LoggerToCustomStreams{Stdout: outStream, Stderr: errStream}) builderCtx.Clean = req.GetClean() + builderCtx.OnlyUpdateCompilationDatabase = req.GetCreateCompilationDatabaseOnly() // Use defer() to create an rpc.CompileResp with all the information available at the // moment of return. diff --git a/legacy/builder/builder_utils/utils.go b/legacy/builder/builder_utils/utils.go index 5e2e4bf98d4..72c4e66f717 100644 --- a/legacy/builder/builder_utils/utils.go +++ b/legacy/builder/builder_utils/utils.go @@ -247,21 +247,24 @@ func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *p if err != nil { return nil, errors.WithStack(err) } - if !objIsUpToDate { - command, err := PrepareCommandForRecipe(properties, recipe, false) - 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.ReplaceEntry(source, command) + } + if !objIsUpToDate && !ctx.OnlyUpdateCompilationDatabase { _, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */) if err != nil { return nil, errors.WithStack(err) } } else if ctx.Verbose { - if ctx.CompilationDatabase != nil { - ctx.CompilationDatabase.KeepEntry(source) + if objIsUpToDate { + logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_USING_PREVIOUS_COMPILED_FILE, objectFile) + } else { + logger.Println("info", "Skipping compile of: {0}", objectFile) } - logger.Println(constants.LOG_LEVEL_INFO, constants.MSG_USING_PREVIOUS_COMPILED_FILE, objectFile) } return objectFile, nil @@ -455,10 +458,15 @@ func ArchiveCompiledFiles(ctx *types.Context, buildPath *paths.Path, archiveFile logger := ctx.GetLogger() archiveFilePath := buildPath.JoinPath(archiveFile) - rebuildArchive := false + if ctx.OnlyUpdateCompilationDatabase { + if ctx.Verbose { + logger.Println("info", "Skipping archive creation of: {0}", 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()) { diff --git a/legacy/builder/merge_sketch_with_bootloader.go b/legacy/builder/merge_sketch_with_bootloader.go index bbd5d127b07..5aab32650ae 100644 --- a/legacy/builder/merge_sketch_with_bootloader.go +++ b/legacy/builder/merge_sketch_with_bootloader.go @@ -31,6 +31,10 @@ import ( type MergeSketchWithBootloader struct{} func (s *MergeSketchWithBootloader) Run(ctx *types.Context) error { + if ctx.OnlyUpdateCompilationDatabase { + return nil + } + buildProperties := ctx.BuildProperties if !buildProperties.ContainsKey(constants.BUILD_PROPERTIES_BOOTLOADER_NOBLINK) && !buildProperties.ContainsKey(constants.BUILD_PROPERTIES_BOOTLOADER_FILE) { return nil diff --git a/legacy/builder/phases/core_builder.go b/legacy/builder/phases/core_builder.go index 9fc85fc90cc..ef8e0b06e0d 100644 --- a/legacy/builder/phases/core_builder.go +++ b/legacy/builder/phases/core_builder.go @@ -94,7 +94,9 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path archivedCoreName := GetCachedCoreArchiveFileName(buildProperties.Get(constants.BUILD_PROPERTIES_FQBN), buildProperties.Get("compiler.optimization_flags"), realCoreFolder) targetArchivedCore = buildCachePath.Join(archivedCoreName) - canUseArchivedCore := !ctx.Clean && !builder_utils.CoreOrReferencedCoreHasChanged(realCoreFolder, targetCoreFolder, targetArchivedCore) + canUseArchivedCore := !ctx.OnlyUpdateCompilationDatabase && + !ctx.Clean && + !builder_utils.CoreOrReferencedCoreHasChanged(realCoreFolder, targetCoreFolder, targetArchivedCore) if canUseArchivedCore { // use archived core @@ -116,7 +118,7 @@ func compileCore(ctx *types.Context, buildPath *paths.Path, buildCachePath *path } // archive core.a - if targetArchivedCore != nil { + if targetArchivedCore != nil && !ctx.OnlyUpdateCompilationDatabase { err := archiveFile.CopyTo(targetArchivedCore) if ctx.Verbose { if err == nil { diff --git a/legacy/builder/phases/linker.go b/legacy/builder/phases/linker.go index 0d3c7756e0b..9c8fc15d98f 100644 --- a/legacy/builder/phases/linker.go +++ b/legacy/builder/phases/linker.go @@ -30,6 +30,13 @@ import ( type Linker struct{} func (s *Linker) Run(ctx *types.Context) error { + if ctx.OnlyUpdateCompilationDatabase { + if ctx.Verbose { + ctx.GetLogger().Println("info", "Skip linking of final executable.") + } + return nil + } + objectFilesSketch := ctx.SketchObjectFiles objectFilesLibraries := ctx.LibrariesObjectFiles objectFilesCore := ctx.CoreObjectsFiles diff --git a/legacy/builder/phases/sizer.go b/legacy/builder/phases/sizer.go index ad870163dcb..482fb5aa35b 100644 --- a/legacy/builder/phases/sizer.go +++ b/legacy/builder/phases/sizer.go @@ -32,7 +32,9 @@ type Sizer struct { } func (s *Sizer) Run(ctx *types.Context) error { - + if ctx.OnlyUpdateCompilationDatabase { + return nil + } if s.SketchError { return nil } diff --git a/legacy/builder/recipe_runner.go b/legacy/builder/recipe_runner.go index d7bb389b75f..9f04598d1e6 100644 --- a/legacy/builder/recipe_runner.go +++ b/legacy/builder/recipe_runner.go @@ -53,6 +53,13 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error { return errors.WithStack(err) } + if ctx.OnlyUpdateCompilationDatabase { + if ctx.Verbose { + ctx.GetLogger().Println("info", "Skipping: {0}", strings.Join(command.Args, " ")) + } + return nil + } + _, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */) if err != nil { return errors.WithStack(err) diff --git a/legacy/builder/types/context.go b/legacy/builder/types/context.go index 55fd3e4a705..906a0c1c365 100644 --- a/legacy/builder/types/context.go +++ b/legacy/builder/types/context.go @@ -167,6 +167,8 @@ type Context struct { // Compilation Database to build/update CompilationDatabase *builder.CompilationDatabase + // Set to true to skip build and produce only Compilation Database + OnlyUpdateCompilationDatabase bool } // ExecutableSectionSize represents a section of the executable output file diff --git a/rpc/commands/compile.pb.go b/rpc/commands/compile.pb.go index 269a37dcd6d..f5a6ce2bbee 100644 --- a/rpc/commands/compile.pb.go +++ b/rpc/commands/compile.pb.go @@ -45,24 +45,25 @@ type CompileReq struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Instance *Instance `protobuf:"bytes,1,opt,name=instance,proto3" json:"instance,omitempty"` // Arduino Core Service instance from the `Init` response. - Fqbn string `protobuf:"bytes,2,opt,name=fqbn,proto3" json:"fqbn,omitempty"` // Fully Qualified Board Name, e.g.: `arduino:avr:uno`. If this field is not defined, the FQBN of the board attached to the sketch via the `BoardAttach` method is used. - SketchPath string `protobuf:"bytes,3,opt,name=sketchPath,proto3" json:"sketchPath,omitempty"` // The path where the sketch is stored. - ShowProperties bool `protobuf:"varint,4,opt,name=showProperties,proto3" json:"showProperties,omitempty"` // Show all build preferences used instead of compiling. - Preprocess bool `protobuf:"varint,5,opt,name=preprocess,proto3" json:"preprocess,omitempty"` // Print preprocessed code to stdout instead of compiling. - BuildCachePath string `protobuf:"bytes,6,opt,name=buildCachePath,proto3" json:"buildCachePath,omitempty"` // Builds of 'core.a' are saved into this path to be cached and reused. - BuildPath string `protobuf:"bytes,7,opt,name=buildPath,proto3" json:"buildPath,omitempty"` // Path to use to store the files used for the compilation. If omitted, a directory will be created in the operating system's default temporary path. - BuildProperties []string `protobuf:"bytes,8,rep,name=buildProperties,proto3" json:"buildProperties,omitempty"` // List of custom build properties separated by commas. - Warnings string `protobuf:"bytes,9,opt,name=warnings,proto3" json:"warnings,omitempty"` // Used to tell gcc which warning level to use. The level names are: "none", "default", "more" and "all". - Verbose bool `protobuf:"varint,10,opt,name=verbose,proto3" json:"verbose,omitempty"` // Turns on verbose mode. - Quiet bool `protobuf:"varint,11,opt,name=quiet,proto3" json:"quiet,omitempty"` // Suppresses almost every output. - VidPid string `protobuf:"bytes,12,opt,name=vidPid,proto3" json:"vidPid,omitempty"` // VID/PID specific build properties. - Jobs int32 `protobuf:"varint,14,opt,name=jobs,proto3" json:"jobs,omitempty"` // The max number of concurrent compiler instances to run (as `make -jx`). If jobs is set to 0, it will use the number of available CPUs as the maximum. - Libraries []string `protobuf:"bytes,15,rep,name=libraries,proto3" json:"libraries,omitempty"` // List of custom libraries paths separated by commas. - OptimizeForDebug bool `protobuf:"varint,16,opt,name=optimizeForDebug,proto3" json:"optimizeForDebug,omitempty"` // Optimize compile output for debug, not for release. - ExportDir string `protobuf:"bytes,18,opt,name=export_dir,json=exportDir,proto3" json:"export_dir,omitempty"` // Optional: save the build artifacts in this directory, the directory must exist. - Clean bool `protobuf:"varint,19,opt,name=clean,proto3" json:"clean,omitempty"` // Optional: cleanup the build folder and do not use any previously cached build - ExportBinaries bool `protobuf:"varint,20,opt,name=export_binaries,json=exportBinaries,proto3" json:"export_binaries,omitempty"` // When set to `true` the compiled binary will be copied to the export directory. + Instance *Instance `protobuf:"bytes,1,opt,name=instance,proto3" json:"instance,omitempty"` // Arduino Core Service instance from the `Init` response. + Fqbn string `protobuf:"bytes,2,opt,name=fqbn,proto3" json:"fqbn,omitempty"` // Fully Qualified Board Name, e.g.: `arduino:avr:uno`. If this field is not defined, the FQBN of the board attached to the sketch via the `BoardAttach` method is used. + SketchPath string `protobuf:"bytes,3,opt,name=sketchPath,proto3" json:"sketchPath,omitempty"` // The path where the sketch is stored. + ShowProperties bool `protobuf:"varint,4,opt,name=showProperties,proto3" json:"showProperties,omitempty"` // Show all build preferences used instead of compiling. + Preprocess bool `protobuf:"varint,5,opt,name=preprocess,proto3" json:"preprocess,omitempty"` // Print preprocessed code to stdout instead of compiling. + BuildCachePath string `protobuf:"bytes,6,opt,name=buildCachePath,proto3" json:"buildCachePath,omitempty"` // Builds of 'core.a' are saved into this path to be cached and reused. + BuildPath string `protobuf:"bytes,7,opt,name=buildPath,proto3" json:"buildPath,omitempty"` // Path to use to store the files used for the compilation. If omitted, a directory will be created in the operating system's default temporary path. + BuildProperties []string `protobuf:"bytes,8,rep,name=buildProperties,proto3" json:"buildProperties,omitempty"` // List of custom build properties separated by commas. + Warnings string `protobuf:"bytes,9,opt,name=warnings,proto3" json:"warnings,omitempty"` // Used to tell gcc which warning level to use. The level names are: "none", "default", "more" and "all". + Verbose bool `protobuf:"varint,10,opt,name=verbose,proto3" json:"verbose,omitempty"` // Turns on verbose mode. + Quiet bool `protobuf:"varint,11,opt,name=quiet,proto3" json:"quiet,omitempty"` // Suppresses almost every output. + VidPid string `protobuf:"bytes,12,opt,name=vidPid,proto3" json:"vidPid,omitempty"` // VID/PID specific build properties. + Jobs int32 `protobuf:"varint,14,opt,name=jobs,proto3" json:"jobs,omitempty"` // The max number of concurrent compiler instances to run (as `make -jx`). If jobs is set to 0, it will use the number of available CPUs as the maximum. + Libraries []string `protobuf:"bytes,15,rep,name=libraries,proto3" json:"libraries,omitempty"` // List of custom libraries paths separated by commas. + OptimizeForDebug bool `protobuf:"varint,16,opt,name=optimizeForDebug,proto3" json:"optimizeForDebug,omitempty"` // Optimize compile output for debug, not for release. + ExportDir string `protobuf:"bytes,18,opt,name=export_dir,json=exportDir,proto3" json:"export_dir,omitempty"` // Optional: save the build artifacts in this directory, the directory must exist. + Clean bool `protobuf:"varint,19,opt,name=clean,proto3" json:"clean,omitempty"` // Optional: cleanup the build folder and do not use any previously cached build + ExportBinaries bool `protobuf:"varint,20,opt,name=export_binaries,json=exportBinaries,proto3" json:"export_binaries,omitempty"` // When set to `true` the compiled binary will be copied to the export directory. + CreateCompilationDatabaseOnly bool `protobuf:"varint,21,opt,name=create_compilation_database_only,json=createCompilationDatabaseOnly,proto3" json:"create_compilation_database_only,omitempty"` // When set to `true` only the compilation database will be produced and no actual build will be performed. } func (x *CompileReq) Reset() { @@ -223,6 +224,13 @@ func (x *CompileReq) GetExportBinaries() bool { return false } +func (x *CompileReq) GetCreateCompilationDatabaseOnly() bool { + if x != nil { + return x.CreateCompilationDatabaseOnly + } + return false +} + type CompileResp struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -373,7 +381,7 @@ var file_commands_compile_proto_rawDesc = []byte{ 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x1a, 0x15, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x73, 0x2f, 0x6c, 0x69, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd7, 0x04, 0x0a, + 0x64, 0x73, 0x2f, 0x6c, 0x69, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa0, 0x05, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x12, 0x3d, 0x0a, 0x08, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, @@ -411,34 +419,39 @@ var file_commands_compile_proto_rawDesc = []byte{ 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x12, 0x27, 0x0a, 0x0f, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x65, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x69, - 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x22, 0x9d, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x69, - 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, - 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, - 0x61, 0x74, 0x68, 0x12, 0x47, 0x0a, 0x0e, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x62, 0x72, - 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, - 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x75, - 0x73, 0x65, 0x64, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x68, 0x0a, 0x18, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, - 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x16, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x59, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, - 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, - 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, - 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x47, 0x0a, 0x20, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, + 0x61, 0x62, 0x61, 0x73, 0x65, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x1d, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x4f, 0x6e, 0x6c, 0x79, 0x22, + 0x9d, 0x02, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x12, + 0x1d, 0x0a, 0x0a, 0x6f, 0x75, 0x74, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6f, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, + 0x0a, 0x0a, 0x65, 0x72, 0x72, 0x5f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x09, 0x65, 0x72, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x0a, + 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x61, 0x74, 0x68, 0x12, 0x47, 0x0a, 0x0e, + 0x75, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, + 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x4c, + 0x69, 0x62, 0x72, 0x61, 0x72, 0x79, 0x52, 0x0d, 0x75, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x62, 0x72, + 0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x68, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x5f, 0x73, 0x69, 0x7a, + 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x63, 0x63, 0x2e, 0x61, 0x72, 0x64, + 0x75, 0x69, 0x6e, 0x6f, 0x2e, 0x63, 0x6c, 0x69, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, + 0x73, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x53, 0x69, 0x7a, 0x65, 0x22, + 0x59, 0x0a, 0x15, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x2d, 0x5a, 0x2b, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, + 0x2f, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6e, 0x6f, 0x2d, 0x63, 0x6c, 0x69, 0x2f, 0x72, 0x70, 0x63, + 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/rpc/commands/compile.proto b/rpc/commands/compile.proto index ef7ccccfcc1..60d18c81a8d 100644 --- a/rpc/commands/compile.proto +++ b/rpc/commands/compile.proto @@ -41,6 +41,7 @@ message CompileReq { string export_dir = 18; // Optional: save the build artifacts in this directory, the directory must exist. bool clean = 19; // Optional: cleanup the build folder and do not use any previously cached build bool export_binaries = 20; // When set to `true` the compiled binary will be copied to the export directory. + bool create_compilation_database_only = 21; // When set to `true` only the compilation database will be produced and no actual build will be performed. } message CompileResp { From f94d2fe9e5df33c306b41ea23771e89f2d9aaefc Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 11 Nov 2020 22:20:33 +0100 Subject: [PATCH 3/9] Save compilation database inside build path --- commands/compile/compile.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/commands/compile/compile.go b/commands/compile/compile.go index 7cca7363546..7eb8eefd9b3 100644 --- a/commands/compile/compile.go +++ b/commands/compile/compile.go @@ -116,9 +116,6 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W builderCtx.PackageManager = pm builderCtx.FQBN = fqbn builderCtx.SketchLocation = sketch.FullPath - builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( - sketch.FullPath.Join("compile_commands.json"), - ) // FIXME: This will be redundant when arduino-builder will be part of the cli builderCtx.HardwareDirs = configuration.HardwareDirectories(configuration.Settings) @@ -132,10 +129,12 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W } else { builderCtx.BuildPath = paths.New(req.GetBuildPath()) } - if err = builderCtx.BuildPath.MkdirAll(); err != nil { return nil, fmt.Errorf("cannot create build directory: %s", err) } + builderCtx.CompilationDatabase = bldr.NewCompilationDatabase( + builderCtx.BuildPath.Join("compile_commands.json"), + ) builderCtx.Verbose = req.GetVerbose() From 5240c0f8218b30928fe6a53c431fd859a3b086c1 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 11 Nov 2020 22:41:27 +0100 Subject: [PATCH 4/9] Removed 'Writing compilation database...' message Also slightly refactore code --- arduino/builder/compilation_database.go | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go index 50828c3add4..62660fa365e 100644 --- a/arduino/builder/compilation_database.go +++ b/arduino/builder/compilation_database.go @@ -3,10 +3,10 @@ package builder import ( "encoding/json" "fmt" - "github.com/arduino/go-paths-helper" - "io/ioutil" "os" "os/exec" + + "github.com/arduino/go-paths-helper" ) type compilationCommand struct { @@ -29,16 +29,10 @@ func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase { func (db *CompilationDatabase) UpdateFile(complete bool) { // TODO: Read any existing file and use its contents for any // kept files, or any files not in db.contents if !complete. - fmt.Printf("Writing compilation database to \"%s\"...\n", db.filename.String()) - - contents := db.contents - jsonContents, err := json.MarshalIndent(contents, "", " ") - if err != nil { + if jsonContents, err := json.MarshalIndent(db.contents, "", " "); err != nil { fmt.Printf("Error serializing compilation database: %s", err) return - } - err = ioutil.WriteFile(db.filename.String(), jsonContents, 0644) - if err != nil { + } else if err := db.filename.WriteFile(jsonContents); err != nil { fmt.Printf("Error writing compilation database: %s", err) } } From 33cfddfa0b4e557d9209aea2ec836b198577a49c Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 18 Nov 2020 15:50:31 +0100 Subject: [PATCH 5/9] Added missing (c) header --- arduino/builder/compilation_database.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go index 62660fa365e..b7cd3af189c 100644 --- a/arduino/builder/compilation_database.go +++ b/arduino/builder/compilation_database.go @@ -1,3 +1,18 @@ +// 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 ( From c50fa1caf04ff93b5825914bfa5fe2f738820564 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 18 Nov 2020 15:58:27 +0100 Subject: [PATCH 6/9] Renamed some functions, did some small cleanups --- arduino/builder/compilation_database.go | 49 ++++++++++++------------- legacy/builder/builder.go | 3 +- legacy/builder/builder_utils/utils.go | 2 +- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go index b7cd3af189c..aef7a274ee8 100644 --- a/arduino/builder/compilation_database.go +++ b/arduino/builder/compilation_database.go @@ -24,26 +24,29 @@ import ( "github.com/arduino/go-paths-helper" ) -type compilationCommand struct { +// CompilationDatabase keeps track of all the compile commands run by the builder +type CompilationDatabase struct { + contents []CompilationCommand + filename *paths.Path +} + +// CompilationCommand keeps track of a single run of a compile command +type CompilationCommand struct { Directory string `json:"directory"` Arguments []string `json:"arguments"` File string `json:"file"` } -type CompilationDatabase struct { - contents []compilationCommand - filename *paths.Path -} - +// NewCompilationDatabase creates an empty CompilationDatabase func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase { return &CompilationDatabase{ filename: filename, } } -func (db *CompilationDatabase) UpdateFile(complete bool) { - // TODO: Read any existing file and use its contents for any - // kept files, or any files not in db.contents if !complete. +// 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() { if jsonContents, err := json.MarshalIndent(db.contents, "", " "); err != nil { fmt.Printf("Error serializing compilation database: %s", err) return @@ -52,31 +55,27 @@ func (db *CompilationDatabase) UpdateFile(complete bool) { } } -func (db *CompilationDatabase) dirForCommand(command *exec.Cmd) string { +func dirForCommand(command *exec.Cmd) string { // This mimics what Cmd.Run also does: Use Dir if specified, // current directory otherwise if command.Dir != "" { return command.Dir - } else { - dir, err := os.Getwd() - if err != nil { - fmt.Printf("Error getting current directory for compilation database: %s", err) - return "" - } - return dir } + dir, err := os.Getwd() + if err != nil { + fmt.Printf("Error getting current directory for compilation database: %s", err) + return "" + } + return dir } -func (db *CompilationDatabase) ReplaceEntry(filename *paths.Path, command *exec.Cmd) { - entry := compilationCommand{ - Directory: db.dirForCommand(command), +// Add adds a new CompilationDatabase entry +func (db *CompilationDatabase) Add(target *paths.Path, command *exec.Cmd) { + entry := CompilationCommand{ + Directory: dirForCommand(command), Arguments: command.Args, - File: filename.String(), + File: target.String(), } db.contents = append(db.contents, entry) } - -func (db *CompilationDatabase) KeepEntry(filename *paths.Path) { - // TODO -} diff --git a/legacy/builder/builder.go b/legacy/builder/builder.go index 3d4a1c9e37c..27d93f81ca8 100644 --- a/legacy/builder/builder.go +++ b/legacy/builder/builder.go @@ -96,9 +96,8 @@ func (s *Builder) Run(ctx *types.Context) error { mainErr := runCommands(ctx, commands) - // TODO: Make proper step? if ctx.CompilationDatabase != nil { - ctx.CompilationDatabase.UpdateFile(mainErr != nil) + ctx.CompilationDatabase.SaveToFile() } commands = []types.Command{ diff --git a/legacy/builder/builder_utils/utils.go b/legacy/builder/builder_utils/utils.go index 72c4e66f717..fc2f50fa832 100644 --- a/legacy/builder/builder_utils/utils.go +++ b/legacy/builder/builder_utils/utils.go @@ -252,7 +252,7 @@ func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *p return nil, errors.WithStack(err) } if ctx.CompilationDatabase != nil { - ctx.CompilationDatabase.ReplaceEntry(source, command) + ctx.CompilationDatabase.Add(source, command) } if !objIsUpToDate && !ctx.OnlyUpdateCompilationDatabase { _, _, err = utils.ExecCommand(ctx, command, utils.ShowIfVerbose /* stdout */, utils.Show /* stderr */) From 355dd3e7ffcd499f4460552d73997032df96776e Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 23 Nov 2020 14:56:15 +0100 Subject: [PATCH 7/9] compilation database: made some fields public --- arduino/builder/compilation_database.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go index aef7a274ee8..b9930cba186 100644 --- a/arduino/builder/compilation_database.go +++ b/arduino/builder/compilation_database.go @@ -26,8 +26,8 @@ import ( // CompilationDatabase keeps track of all the compile commands run by the builder type CompilationDatabase struct { - contents []CompilationCommand - filename *paths.Path + Contents []CompilationCommand + File *paths.Path } // CompilationCommand keeps track of a single run of a compile command @@ -40,17 +40,17 @@ type CompilationCommand struct { // NewCompilationDatabase creates an empty CompilationDatabase func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase { return &CompilationDatabase{ - filename: filename, + File: filename, } } // 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() { - if jsonContents, err := json.MarshalIndent(db.contents, "", " "); err != nil { + if jsonContents, err := json.MarshalIndent(db.Contents, "", " "); err != nil { fmt.Printf("Error serializing compilation database: %s", err) return - } else if err := db.filename.WriteFile(jsonContents); err != nil { + } else if err := db.File.WriteFile(jsonContents); err != nil { fmt.Printf("Error writing compilation database: %s", err) } } @@ -77,5 +77,5 @@ func (db *CompilationDatabase) Add(target *paths.Path, command *exec.Cmd) { File: target.String(), } - db.contents = append(db.contents, entry) + db.Contents = append(db.Contents, entry) } From cf8fd8f7e6adfaa02c370a6406b166e5a1597cd9 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 23 Nov 2020 14:56:38 +0100 Subject: [PATCH 8/9] compilation database: added LoadCompilationDatabase method --- arduino/builder/compilation_database.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/arduino/builder/compilation_database.go b/arduino/builder/compilation_database.go index b9930cba186..b5d51d40f95 100644 --- a/arduino/builder/compilation_database.go +++ b/arduino/builder/compilation_database.go @@ -33,7 +33,8 @@ type CompilationDatabase struct { // CompilationCommand keeps track of a single run of a compile command type CompilationCommand struct { Directory string `json:"directory"` - Arguments []string `json:"arguments"` + Command string `json:"command,omitempty"` + Arguments []string `json:"arguments,omitempty"` File string `json:"file"` } @@ -44,6 +45,19 @@ func NewCompilationDatabase(filename *paths.Path) *CompilationDatabase { } } +// LoadCompilationDatabase reads a compilation database from a file +func LoadCompilationDatabase(file *paths.Path) (*CompilationDatabase, error) { + f, err := file.ReadFile() + if err != nil { + return nil, err + } + res := &CompilationDatabase{ + File: file, + Contents: []CompilationCommand{}, + } + 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() { From a22edfaa6ca9741ad79de3198e845f8561868779 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Mon, 14 Dec 2020 18:08:02 +0100 Subject: [PATCH 9/9] Added unit tests for compilation_database --- arduino/builder/compilation_database_test.go | 46 ++++++++++++++++++++ go.mod | 2 +- go.sum | 2 + 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 arduino/builder/compilation_database_test.go diff --git a/arduino/builder/compilation_database_test.go b/arduino/builder/compilation_database_test.go new file mode 100644 index 00000000000..c7f5bc3003d --- /dev/null +++ b/arduino/builder/compilation_database_test.go @@ -0,0 +1,46 @@ +// This file is part of arduino-cli. +// +// Copyright 2020 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of arduino-cli. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to +// modify or otherwise use the software for commercial activities involving the +// Arduino software without disclosing the source code of your own applications. +// To purchase a commercial license, send an email to license@arduino.cc. + +package builder + +import ( + "os/exec" + "testing" + + "github.com/arduino/go-paths-helper" + "github.com/stretchr/testify/require" +) + +func TestCompilationDatabase(t *testing.T) { + tmpfile, err := paths.WriteToTempFile([]byte{}, nil, "") + require.NoError(t, err) + defer tmpfile.Remove() + + cmd := exec.Command("gcc", "arg1", "arg2") + db := NewCompilationDatabase(tmpfile) + db.Add(paths.New("test"), cmd) + db.SaveToFile() + + db2, err := LoadCompilationDatabase(tmpfile) + require.NoError(t, err) + require.Equal(t, db, db2) + require.Len(t, db2.Contents, 1) + require.Equal(t, db2.Contents[0].File, "test") + require.Equal(t, db2.Contents[0].Command, "") + require.Equal(t, db2.Contents[0].Arguments, []string{"gcc", "arg1", "arg2"}) + cwd, err := paths.Getwd() + require.NoError(t, err) + require.Equal(t, db2.Contents[0].Directory, cwd.String()) +} diff --git a/go.mod b/go.mod index c7fd73d3721..49b5922219c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.14 require ( github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c - github.com/arduino/go-paths-helper v1.3.2 + github.com/arduino/go-paths-helper v1.4.0 github.com/arduino/go-properties-orderedmap v1.3.0 github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b diff --git a/go.sum b/go.sum index 662d4ff18bd..b494e29476a 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,8 @@ github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3 github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= github.com/arduino/go-paths-helper v1.3.2 h1:5U9TSKQODiwSVgTxskC0PNl0l0Vf40GUlp99Zy2SK8w= github.com/arduino/go-paths-helper v1.3.2/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= +github.com/arduino/go-paths-helper v1.4.0 h1:ilnseAdxmN1bFnLxxXHRtcdmt9jBf3O4jtYfWfqule4= +github.com/arduino/go-paths-helper v1.4.0/go.mod h1:V82BWgAAp4IbmlybxQdk9Bpkz8M4Qyx+RAFKaG9NuvU= github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o= github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk= github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4=