aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Light <allight@google.com>2018-04-17 16:50:48 -0700
committerAlex Light <allight@google.com>2018-04-24 08:15:02 -0700
commitec868fc42aefd299a2f26249f7751b3385840187 (patch)
tree8a06e0fc069662aa8ad4c648e762ff68e94bd617
parent2b8959ad5a37574226f5dbd07a027319e184f7c4 (diff)
downloadbuild_soong-ec868fc42aefd299a2f26249f7751b3385840187.tar.gz
build_soong-ec868fc42aefd299a2f26249f7751b3385840187.tar.bz2
build_soong-ec868fc42aefd299a2f26249f7751b3385840187.zip
Add support for generating Compdb file
Some tools (i.e. you-complete-me) make use of a 'compdb' file (compile_commands.json) that records (among other things) the arguments needed to compile a file. These tools can use this with libclang to provide semantic completions and perform other IDE actions. This CL adds support for soong to generate a (simple) compile_commands.json file. Test: make SOONG_GEN_COMPDB=1 SOONG_LINK_COMPDB_TO=$ANDROID_BUILD_TOP nothing examine $ANDROID_BUILD_TOP/compile_commands.json Change-Id: I751bb344b90dfcdad1dfd71c2a85bacd345f0464
-rw-r--r--Android.bp1
-rw-r--r--README.md1
-rw-r--r--cc/compdb.go192
-rw-r--r--docs/compdb.md27
4 files changed, 221 insertions, 0 deletions
diff --git a/Android.bp b/Android.bp
index 5396664b..aa652096 100644
--- a/Android.bp
+++ b/Android.bp
@@ -145,6 +145,7 @@ bootstrap_go_package {
"cc/vndk_prebuilt.go",
"cc/cmakelists.go",
+ "cc/compdb.go",
"cc/compiler.go",
"cc/installer.go",
"cc/linker.go",
diff --git a/README.md b/README.md
index b234c718..b3239e9a 100644
--- a/README.md
+++ b/README.md
@@ -175,6 +175,7 @@ written to a [ninja](http://ninja-build.org) build file.
* [Best Practices](docs/best_practices.md)
* [Build Performance](docs/perf.md)
* [Generating CLion Projects](docs/clion.md)
+* [Generating YouCompleteMe/VSCode compile\_commands.json file](docs/compdb.md)
* Make-specific documentation: [build/make/README.md](https://android.googlesource.com/platform/build/+/master/README.md)
## FAQ
diff --git a/cc/compdb.go b/cc/compdb.go
new file mode 100644
index 00000000..a9c5b5e9
--- /dev/null
+++ b/cc/compdb.go
@@ -0,0 +1,192 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+ "encoding/json"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "android/soong/android"
+)
+
+// This singleton generates a compile_commands.json file. It does so for each
+// blueprint Android.bp resulting in a cc.Module when either make, mm, mma, mmm
+// or mmma is called. It will only create a single compile_commands.json file
+// at out/development/ide/compdb/compile_commands.json. It will also symlink it
+// to ${SOONG_LINK_COMPDB_TO} if set. In general this should be created by running
+// make SOONG_GEN_COMPDB=1 nothing to get all targets.
+
+func init() {
+ android.RegisterSingletonType("compdb_generator", compDBGeneratorSingleton)
+}
+
+func compDBGeneratorSingleton() android.Singleton {
+ return &compdbGeneratorSingleton{}
+}
+
+type compdbGeneratorSingleton struct{}
+
+const (
+ compdbFilename = "compile_commands.json"
+ compdbOutputProjectsDirectory = "out/development/ide/compdb"
+
+ // Environment variables used to modify behavior of this singleton.
+ envVariableGenerateCompdb = "SOONG_GEN_COMPDB"
+ envVariableGenerateCompdbDebugInfo = "SOONG_GEN_COMPDB_DEBUG"
+ envVariableCompdbLink = "SOONG_LINK_COMPDB_TO"
+)
+
+// A compdb entry. The compile_commands.json file is a list of these.
+type compDbEntry struct {
+ Directory string `json:"directory"`
+ Arguments []string `json:"arguments"`
+ File string `json:"file"`
+ Output string `json:"output,omitempty"`
+}
+
+func (c *compdbGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+ if !ctx.Config().IsEnvTrue(envVariableGenerateCompdb) {
+ return
+ }
+
+ // Instruct the generator to indent the json file for easier debugging.
+ outputCompdbDebugInfo := ctx.Config().IsEnvTrue(envVariableGenerateCompdbDebugInfo)
+
+ // We only want one entry per file. We don't care what module/isa it's from
+ m := make(map[string]compDbEntry)
+ ctx.VisitAllModules(func(module android.Module) {
+ if ccModule, ok := module.(*Module); ok {
+ if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
+ generateCompdbProject(compiledModule, ctx, ccModule, m)
+ }
+ }
+ })
+
+ // Create the output file.
+ dir := filepath.Join(getCompdbAndroidSrcRootDirectory(ctx), compdbOutputProjectsDirectory)
+ os.MkdirAll(dir, 0777)
+ compDBFile := filepath.Join(dir, compdbFilename)
+ f, err := os.Create(compdbFilename)
+ if err != nil {
+ log.Fatalf("Could not create file %s: %s", filepath.Join(dir, compdbFilename), err)
+ }
+ defer f.Close()
+
+ v := make([]compDbEntry, 0, len(m))
+
+ for _, value := range m {
+ v = append(v, value)
+ }
+ var dat []byte
+ if outputCompdbDebugInfo {
+ dat, err = json.MarshalIndent(v, "", " ")
+ } else {
+ dat, err = json.Marshal(v)
+ }
+ if err != nil {
+ log.Fatalf("Failed to marshal: %s", err)
+ }
+ f.Write(dat)
+
+ finalLinkPath := filepath.Join(ctx.Config().Getenv(envVariableCompdbLink), compdbFilename)
+ if finalLinkPath != "" {
+ os.Remove(finalLinkPath)
+ if err := os.Symlink(compDBFile, finalLinkPath); err != nil {
+ log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
+ }
+ }
+}
+
+func expandAllVars(ctx android.SingletonContext, args []string) []string {
+ var out []string
+ for _, arg := range args {
+ if arg != "" {
+ if val, err := evalAndSplitVariable(ctx, arg); err == nil {
+ out = append(out, val...)
+ } else {
+ out = append(out, arg)
+ }
+ }
+ }
+ return out
+}
+
+func getArguments(src android.Path, ctx android.SingletonContext, ccModule *Module) []string {
+ var args []string
+ isCpp := false
+ isAsm := false
+ // TODO It would be better to ask soong for the types here.
+ switch src.Ext() {
+ case ".S", ".s", ".asm":
+ isAsm = true
+ isCpp = false
+ case ".c":
+ isAsm = false
+ isCpp = false
+ case ".cpp", ".cc", ".mm":
+ isAsm = false
+ isCpp = true
+ default:
+ log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
+ isAsm = true
+ isCpp = false
+ }
+ // The executable for the compilation doesn't matter but we need something there.
+ args = append(args, "/bin/false")
+ args = append(args, expandAllVars(ctx, ccModule.flags.GlobalFlags)...)
+ args = append(args, expandAllVars(ctx, ccModule.flags.CFlags)...)
+ if isCpp {
+ args = append(args, expandAllVars(ctx, ccModule.flags.CppFlags)...)
+ } else if !isAsm {
+ args = append(args, expandAllVars(ctx, ccModule.flags.ConlyFlags)...)
+ }
+ args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
+ args = append(args, src.String())
+ return args
+}
+
+func generateCompdbProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module, builds map[string]compDbEntry) {
+ srcs := compiledModule.Srcs()
+ if len(srcs) == 0 {
+ return
+ }
+
+ rootDir := getCompdbAndroidSrcRootDirectory(ctx)
+ for _, src := range srcs {
+ if _, ok := builds[src.String()]; !ok {
+ builds[src.String()] = compDbEntry{
+ Directory: rootDir,
+ Arguments: getArguments(src, ctx, ccModule),
+ File: src.String(),
+ }
+ }
+ }
+}
+
+func evalAndSplitVariable(ctx android.SingletonContext, str string) ([]string, error) {
+ evaluated, err := ctx.Eval(pctx, str)
+ if err == nil {
+ return strings.Split(evaluated, " "), nil
+ }
+ return []string{""}, err
+}
+
+func getCompdbAndroidSrcRootDirectory(ctx android.SingletonContext) string {
+ srcPath, _ := filepath.Abs(android.PathForSource(ctx).String())
+ return srcPath
+}
diff --git a/docs/compdb.md b/docs/compdb.md
new file mode 100644
index 00000000..68927ca1
--- /dev/null
+++ b/docs/compdb.md
@@ -0,0 +1,27 @@
+# Compdb (compile\_commands.json) Generator
+
+Soong can generate compdb files. This is intended for use with editing tools
+such as YouCompleteMe and other libclang based completers.
+
+compdb file generation is enabled via environment variable:
+
+```bash
+$ export SOONG_GEN_COMPDB=1
+$ export SOONG_GEN_COMPDB_DEBUG=1
+```
+
+One can make soong generate a symlink to the compdb file using an environment
+variable:
+
+```bash
+$ export SOONG_LINK_COMPDB_TO=$ANDROID_HOST_OUT
+```
+
+You can then trigger an empty build:
+
+```bash
+$ make nothing
+```
+
+Note that if you build using mm or other limited makes with these environment
+variables set the compdb will only include files in included modules.