blob: 4132e090be71d98a5aa019cc460c7a253af0c33e [file] [log] [blame]
Alex Lightec868fc2018-04-17 16:50:48 -07001// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package cc
16
17import (
18 "encoding/json"
19 "log"
20 "os"
21 "path/filepath"
22 "strings"
23
24 "android/soong/android"
25)
26
27// This singleton generates a compile_commands.json file. It does so for each
28// blueprint Android.bp resulting in a cc.Module when either make, mm, mma, mmm
29// or mmma is called. It will only create a single compile_commands.json file
Changyeon Jo96279252019-12-19 23:26:16 +000030// at ${OUT_DIR}/soong/development/ide/compdb/compile_commands.json. It will also symlink it
Alex Lightec868fc2018-04-17 16:50:48 -070031// to ${SOONG_LINK_COMPDB_TO} if set. In general this should be created by running
32// make SOONG_GEN_COMPDB=1 nothing to get all targets.
33
34func init() {
LaMont Jones0c10e4d2023-05-16 00:58:37 +000035 android.RegisterParallelSingletonType("compdb_generator", compDBGeneratorSingleton)
Alex Lightec868fc2018-04-17 16:50:48 -070036}
37
38func compDBGeneratorSingleton() android.Singleton {
39 return &compdbGeneratorSingleton{}
40}
41
42type compdbGeneratorSingleton struct{}
43
44const (
45 compdbFilename = "compile_commands.json"
Changyeon Jo96279252019-12-19 23:26:16 +000046 compdbOutputProjectsDirectory = "development/ide/compdb"
Alex Lightec868fc2018-04-17 16:50:48 -070047
48 // Environment variables used to modify behavior of this singleton.
49 envVariableGenerateCompdb = "SOONG_GEN_COMPDB"
50 envVariableGenerateCompdbDebugInfo = "SOONG_GEN_COMPDB_DEBUG"
51 envVariableCompdbLink = "SOONG_LINK_COMPDB_TO"
52)
53
54// A compdb entry. The compile_commands.json file is a list of these.
55type compDbEntry struct {
56 Directory string `json:"directory"`
57 Arguments []string `json:"arguments"`
58 File string `json:"file"`
59 Output string `json:"output,omitempty"`
60}
61
62func (c *compdbGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
63 if !ctx.Config().IsEnvTrue(envVariableGenerateCompdb) {
64 return
65 }
66
67 // Instruct the generator to indent the json file for easier debugging.
68 outputCompdbDebugInfo := ctx.Config().IsEnvTrue(envVariableGenerateCompdbDebugInfo)
69
70 // We only want one entry per file. We don't care what module/isa it's from
71 m := make(map[string]compDbEntry)
72 ctx.VisitAllModules(func(module android.Module) {
73 if ccModule, ok := module.(*Module); ok {
74 if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
75 generateCompdbProject(compiledModule, ctx, ccModule, m)
76 }
77 }
78 })
79
80 // Create the output file.
Changyeon Jo96279252019-12-19 23:26:16 +000081 dir := android.PathForOutput(ctx, compdbOutputProjectsDirectory)
Colin Cross988414c2020-01-11 01:11:46 +000082 os.MkdirAll(filepath.Join(android.AbsSrcDirForExistingUseCases(), dir.String()), 0777)
Changyeon Jo96279252019-12-19 23:26:16 +000083 compDBFile := dir.Join(ctx, compdbFilename)
Colin Cross988414c2020-01-11 01:11:46 +000084 f, err := os.Create(filepath.Join(android.AbsSrcDirForExistingUseCases(), compDBFile.String()))
Alex Lightec868fc2018-04-17 16:50:48 -070085 if err != nil {
Changyeon Jo96279252019-12-19 23:26:16 +000086 log.Fatalf("Could not create file %s: %s", compDBFile, err)
Alex Lightec868fc2018-04-17 16:50:48 -070087 }
Tamir Dubersteincc5ce652024-08-09 08:50:57 -070088 defer func() {
89 if err := f.Close(); err != nil {
90 log.Fatalf("Could not close file %s: %s", compDBFile, err)
91 }
92 }()
Alex Lightec868fc2018-04-17 16:50:48 -070093
94 v := make([]compDbEntry, 0, len(m))
Alex Lightec868fc2018-04-17 16:50:48 -070095 for _, value := range m {
96 v = append(v, value)
97 }
Tamir Dubersteincc5ce652024-08-09 08:50:57 -070098
99 w := json.NewEncoder(f)
Alex Lightec868fc2018-04-17 16:50:48 -0700100 if outputCompdbDebugInfo {
Tamir Dubersteincc5ce652024-08-09 08:50:57 -0700101 w.SetIndent("", " ")
Alex Lightec868fc2018-04-17 16:50:48 -0700102 }
Tamir Dubersteincc5ce652024-08-09 08:50:57 -0700103 if err := w.Encode(v); err != nil {
104 log.Fatalf("Failed to encode: %s", err)
Alex Lightec868fc2018-04-17 16:50:48 -0700105 }
Alex Lightec868fc2018-04-17 16:50:48 -0700106
Colin Cross988414c2020-01-11 01:11:46 +0000107 if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
108 finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
Alex Lightec868fc2018-04-17 16:50:48 -0700109 os.Remove(finalLinkPath)
Changyeon Jo96279252019-12-19 23:26:16 +0000110 if err := os.Symlink(compDBFile.String(), finalLinkPath); err != nil {
Alex Lightec868fc2018-04-17 16:50:48 -0700111 log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
112 }
113 }
114}
115
116func expandAllVars(ctx android.SingletonContext, args []string) []string {
117 var out []string
118 for _, arg := range args {
119 if arg != "" {
120 if val, err := evalAndSplitVariable(ctx, arg); err == nil {
121 out = append(out, val...)
122 } else {
123 out = append(out, arg)
124 }
125 }
126 }
127 return out
128}
129
Alex Lightbe96aea2018-11-07 11:35:47 -0800130func getArguments(src android.Path, ctx android.SingletonContext, ccModule *Module, ccPath string, cxxPath string) []string {
Alex Lightec868fc2018-04-17 16:50:48 -0700131 var args []string
132 isCpp := false
133 isAsm := false
134 // TODO It would be better to ask soong for the types here.
Alex Lightbe96aea2018-11-07 11:35:47 -0800135 var clangPath string
Alex Lightec868fc2018-04-17 16:50:48 -0700136 switch src.Ext() {
137 case ".S", ".s", ".asm":
138 isAsm = true
139 isCpp = false
Alex Lightbe96aea2018-11-07 11:35:47 -0800140 clangPath = ccPath
Alex Lightec868fc2018-04-17 16:50:48 -0700141 case ".c":
142 isAsm = false
143 isCpp = false
Alex Lightbe96aea2018-11-07 11:35:47 -0800144 clangPath = ccPath
Colin Crossd34ab7c2019-06-27 14:46:10 -0700145 case ".cpp", ".cc", ".cxx", ".mm":
Alex Lightec868fc2018-04-17 16:50:48 -0700146 isAsm = false
147 isCpp = true
Alex Lightbe96aea2018-11-07 11:35:47 -0800148 clangPath = cxxPath
Jiyong Parkbee2e212024-11-11 16:23:02 +0900149 case ".o":
150 return nil
Alex Lightec868fc2018-04-17 16:50:48 -0700151 default:
152 log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
153 isAsm = true
154 isCpp = false
Alex Lightbe96aea2018-11-07 11:35:47 -0800155 clangPath = ccPath
Alex Lightec868fc2018-04-17 16:50:48 -0700156 }
Alex Lightbe96aea2018-11-07 11:35:47 -0800157 args = append(args, clangPath)
Colin Cross4af21ed2019-11-04 09:37:55 -0800158 args = append(args, expandAllVars(ctx, ccModule.flags.Global.CommonFlags)...)
159 args = append(args, expandAllVars(ctx, ccModule.flags.Local.CommonFlags)...)
160 args = append(args, expandAllVars(ctx, ccModule.flags.Global.CFlags)...)
161 args = append(args, expandAllVars(ctx, ccModule.flags.Local.CFlags)...)
Alex Lightec868fc2018-04-17 16:50:48 -0700162 if isCpp {
Colin Cross4af21ed2019-11-04 09:37:55 -0800163 args = append(args, expandAllVars(ctx, ccModule.flags.Global.CppFlags)...)
164 args = append(args, expandAllVars(ctx, ccModule.flags.Local.CppFlags)...)
Alex Lightec868fc2018-04-17 16:50:48 -0700165 } else if !isAsm {
Colin Cross4af21ed2019-11-04 09:37:55 -0800166 args = append(args, expandAllVars(ctx, ccModule.flags.Global.ConlyFlags)...)
167 args = append(args, expandAllVars(ctx, ccModule.flags.Local.ConlyFlags)...)
Alex Lightec868fc2018-04-17 16:50:48 -0700168 }
169 args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
Luis Useche342fa6b2024-04-01 19:33:18 -0700170 args = append(args, expandAllVars(ctx, ccModule.flags.NoOverrideFlags)...)
Alex Lightec868fc2018-04-17 16:50:48 -0700171 args = append(args, src.String())
172 return args
173}
174
175func generateCompdbProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module, builds map[string]compDbEntry) {
176 srcs := compiledModule.Srcs()
177 if len(srcs) == 0 {
178 return
179 }
180
Colin Cross988414c2020-01-11 01:11:46 +0000181 pathToCC, err := ctx.Eval(pctx, "${config.ClangBin}")
Alex Lightbe96aea2018-11-07 11:35:47 -0800182 ccPath := "/bin/false"
183 cxxPath := "/bin/false"
184 if err == nil {
Colin Cross988414c2020-01-11 01:11:46 +0000185 ccPath = filepath.Join(pathToCC, "clang")
186 cxxPath = filepath.Join(pathToCC, "clang++")
Alex Lightbe96aea2018-11-07 11:35:47 -0800187 }
Alex Lightec868fc2018-04-17 16:50:48 -0700188 for _, src := range srcs {
189 if _, ok := builds[src.String()]; !ok {
Jiyong Parkbee2e212024-11-11 16:23:02 +0900190 args := getArguments(src, ctx, ccModule, ccPath, cxxPath)
191 if args == nil {
192 continue
193 }
Alex Lightec868fc2018-04-17 16:50:48 -0700194 builds[src.String()] = compDbEntry{
Colin Cross988414c2020-01-11 01:11:46 +0000195 Directory: android.AbsSrcDirForExistingUseCases(),
Alex Lightbe96aea2018-11-07 11:35:47 -0800196 Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
Alex Lightec868fc2018-04-17 16:50:48 -0700197 File: src.String(),
198 }
199 }
200 }
201}
202
203func evalAndSplitVariable(ctx android.SingletonContext, str string) ([]string, error) {
204 evaluated, err := ctx.Eval(pctx, str)
205 if err == nil {
Alex Light0c7cc1c2018-10-02 11:19:46 -0700206 return strings.Fields(evaluated), nil
Alex Lightec868fc2018-04-17 16:50:48 -0700207 }
208 return []string{""}, err
209}