Skip to content

Generate compile_commands.json compilation database #944

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
73 changes: 73 additions & 0 deletions arduino/builder/compilation_database.go
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions commands/compile/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"strconv"
"strings"

clibuilder "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/sketches"
Expand Down Expand Up @@ -119,6 +120,12 @@ func Compile(ctx context.Context, req *rpc.CompileReq, outStream, errStream io.W
builderCtx.PackageManager = pm
builderCtx.FQBN = fqbn
builderCtx.SketchLocation = sketch.FullPath
// TODO: Make optional using commandline option?
if !req.GetDryRun() {
builderCtx.CompilationDatabase = clibuilder.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()
Expand Down
5 changes: 5 additions & 0 deletions legacy/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,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},

Expand Down
17 changes: 12 additions & 5 deletions legacy/builder/builder_utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,17 @@ func compileFileWithRecipe(ctx *types.Context, sourcePath *paths.Path, source *p
return nil, errors.WithStack(err)
}
if !objIsUpToDate {
_, _, err = ExecRecipe(ctx, properties, recipe, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
command, _, _, err := ExecRecipe(ctx, properties, recipe, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
if ctx.CompilationDatabase != nil {
ctx.CompilationDatabase.ReplaceEntry(source, command)
}
if err != nil {
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)
}

Expand Down Expand Up @@ -480,22 +486,23 @@ func ArchiveCompiledFiles(ctx *types.Context, buildPath *paths.Path, archiveFile
properties.SetPath(constants.BUILD_PROPERTIES_ARCHIVE_FILE_PATH, archiveFilePath)
properties.SetPath(constants.BUILD_PROPERTIES_OBJECT_FILE, objectFile)

if _, _, err := ExecRecipe(ctx, properties, constants.RECIPE_AR_PATTERN, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show); err != nil {
if _, _, _, err := ExecRecipe(ctx, properties, constants.RECIPE_AR_PATTERN, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show); err != nil {
return nil, errors.WithStack(err)
}
}

return archiveFilePath, nil
}

func ExecRecipe(ctx *types.Context, buildProperties *properties.Map, recipe string, removeUnsetProperties bool, stdout int, stderr int) ([]byte, []byte, error) {
func ExecRecipe(ctx *types.Context, buildProperties *properties.Map, recipe string, removeUnsetProperties bool, stdout int, stderr int) (*exec.Cmd, []byte, []byte, error) {
// See util.ExecCommand for stdout/stderr arguments
command, err := PrepareCommandForRecipe(ctx, buildProperties, recipe, removeUnsetProperties)
if err != nil {
return nil, nil, errors.WithStack(err)
return nil, nil, nil, errors.WithStack(err)
}

return utils.ExecCommand(ctx, command, stdout, stderr)
outbytes, errbytes, err := utils.ExecCommand(ctx, command, stdout, stderr)
return command, outbytes, errbytes, err
}

const COMMANDLINE_LIMIT = 30000
Expand Down
2 changes: 1 addition & 1 deletion legacy/builder/phases/linker.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func link(ctx *types.Context, objectFiles paths.PathList, coreDotARelPath *paths
properties.Set(constants.BUILD_PROPERTIES_ARCHIVE_FILE_PATH, coreArchiveFilePath.String())
properties.Set(constants.BUILD_PROPERTIES_OBJECT_FILES, objectFileList)

_, _, err := builder_utils.ExecRecipe(ctx, properties, constants.RECIPE_C_COMBINE_PATTERN, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
_, _, _, err := builder_utils.ExecRecipe(ctx, properties, constants.RECIPE_C_COMBINE_PATTERN, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
return err
}

Expand Down
2 changes: 1 addition & 1 deletion legacy/builder/phases/sizer.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func checkSize(ctx *types.Context, buildProperties *properties.Map) error {
}

func execSizeRecipe(ctx *types.Context, properties *properties.Map) (textSize int, dataSize int, eepromSize int, resErr error) {
out, _, err := builder_utils.ExecRecipe(ctx, properties, constants.RECIPE_SIZE_PATTERN, false /* stdout */, utils.Capture /* stderr */, utils.Show)
_, out, _, err := builder_utils.ExecRecipe(ctx, properties, constants.RECIPE_SIZE_PATTERN, false /* stdout */, utils.Capture /* stderr */, utils.Show)
if err != nil {
resErr = errors.New("Error while determining sketch size: " + err.Error())
return
Expand Down
2 changes: 1 addition & 1 deletion legacy/builder/recipe_runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (s *RecipeByPrefixSuffixRunner) Run(ctx *types.Context) error {
if ctx.DebugLevel >= 10 {
logger.Fprintln(os.Stdout, constants.LOG_LEVEL_DEBUG, constants.MSG_RUNNING_RECIPE, recipe)
}
_, _, err := builder_utils.ExecRecipe(ctx, properties, recipe, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
_, _, _, err := builder_utils.ExecRecipe(ctx, properties, recipe, false /* stdout */, utils.ShowIfVerbose /* stderr */, utils.Show)
if err != nil {
return errors.WithStack(err)
}
Expand Down
4 changes: 4 additions & 0 deletions legacy/builder/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -158,6 +159,9 @@ type Context struct {
// Out and Err stream to redirect all Exec commands
ExecStdout io.Writer
ExecStderr io.Writer

// Compilation Database to build/update
CompilationDatabase *builder.CompilationDatabase
}

func (ctx *Context) ExtractBuildOptions() *properties.Map {
Expand Down