xref: /aosp_15_r20/build/soong/cc/compdb.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2018 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage cc
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"encoding/json"
19*333d2b36SAndroid Build Coastguard Worker	"log"
20*333d2b36SAndroid Build Coastguard Worker	"os"
21*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
22*333d2b36SAndroid Build Coastguard Worker	"strings"
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Worker	"android/soong/android"
25*333d2b36SAndroid Build Coastguard Worker)
26*333d2b36SAndroid Build Coastguard Worker
27*333d2b36SAndroid Build Coastguard Worker// This singleton generates a compile_commands.json file. It does so for each
28*333d2b36SAndroid Build Coastguard Worker// blueprint Android.bp resulting in a cc.Module when either make, mm, mma, mmm
29*333d2b36SAndroid Build Coastguard Worker// or mmma is called. It will only create a single compile_commands.json file
30*333d2b36SAndroid Build Coastguard Worker// at ${OUT_DIR}/soong/development/ide/compdb/compile_commands.json. It will also symlink it
31*333d2b36SAndroid Build Coastguard Worker// to ${SOONG_LINK_COMPDB_TO} if set. In general this should be created by running
32*333d2b36SAndroid Build Coastguard Worker// make SOONG_GEN_COMPDB=1 nothing to get all targets.
33*333d2b36SAndroid Build Coastguard Worker
34*333d2b36SAndroid Build Coastguard Workerfunc init() {
35*333d2b36SAndroid Build Coastguard Worker	android.RegisterParallelSingletonType("compdb_generator", compDBGeneratorSingleton)
36*333d2b36SAndroid Build Coastguard Worker}
37*333d2b36SAndroid Build Coastguard Worker
38*333d2b36SAndroid Build Coastguard Workerfunc compDBGeneratorSingleton() android.Singleton {
39*333d2b36SAndroid Build Coastguard Worker	return &compdbGeneratorSingleton{}
40*333d2b36SAndroid Build Coastguard Worker}
41*333d2b36SAndroid Build Coastguard Worker
42*333d2b36SAndroid Build Coastguard Workertype compdbGeneratorSingleton struct{}
43*333d2b36SAndroid Build Coastguard Worker
44*333d2b36SAndroid Build Coastguard Workerconst (
45*333d2b36SAndroid Build Coastguard Worker	compdbFilename                = "compile_commands.json"
46*333d2b36SAndroid Build Coastguard Worker	compdbOutputProjectsDirectory = "development/ide/compdb"
47*333d2b36SAndroid Build Coastguard Worker
48*333d2b36SAndroid Build Coastguard Worker	// Environment variables used to modify behavior of this singleton.
49*333d2b36SAndroid Build Coastguard Worker	envVariableGenerateCompdb          = "SOONG_GEN_COMPDB"
50*333d2b36SAndroid Build Coastguard Worker	envVariableGenerateCompdbDebugInfo = "SOONG_GEN_COMPDB_DEBUG"
51*333d2b36SAndroid Build Coastguard Worker	envVariableCompdbLink              = "SOONG_LINK_COMPDB_TO"
52*333d2b36SAndroid Build Coastguard Worker)
53*333d2b36SAndroid Build Coastguard Worker
54*333d2b36SAndroid Build Coastguard Worker// A compdb entry. The compile_commands.json file is a list of these.
55*333d2b36SAndroid Build Coastguard Workertype compDbEntry struct {
56*333d2b36SAndroid Build Coastguard Worker	Directory string   `json:"directory"`
57*333d2b36SAndroid Build Coastguard Worker	Arguments []string `json:"arguments"`
58*333d2b36SAndroid Build Coastguard Worker	File      string   `json:"file"`
59*333d2b36SAndroid Build Coastguard Worker	Output    string   `json:"output,omitempty"`
60*333d2b36SAndroid Build Coastguard Worker}
61*333d2b36SAndroid Build Coastguard Worker
62*333d2b36SAndroid Build Coastguard Workerfunc (c *compdbGeneratorSingleton) GenerateBuildActions(ctx android.SingletonContext) {
63*333d2b36SAndroid Build Coastguard Worker	if !ctx.Config().IsEnvTrue(envVariableGenerateCompdb) {
64*333d2b36SAndroid Build Coastguard Worker		return
65*333d2b36SAndroid Build Coastguard Worker	}
66*333d2b36SAndroid Build Coastguard Worker
67*333d2b36SAndroid Build Coastguard Worker	// Instruct the generator to indent the json file for easier debugging.
68*333d2b36SAndroid Build Coastguard Worker	outputCompdbDebugInfo := ctx.Config().IsEnvTrue(envVariableGenerateCompdbDebugInfo)
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Worker	// We only want one entry per file. We don't care what module/isa it's from
71*333d2b36SAndroid Build Coastguard Worker	m := make(map[string]compDbEntry)
72*333d2b36SAndroid Build Coastguard Worker	ctx.VisitAllModules(func(module android.Module) {
73*333d2b36SAndroid Build Coastguard Worker		if ccModule, ok := module.(*Module); ok {
74*333d2b36SAndroid Build Coastguard Worker			if compiledModule, ok := ccModule.compiler.(CompiledInterface); ok {
75*333d2b36SAndroid Build Coastguard Worker				generateCompdbProject(compiledModule, ctx, ccModule, m)
76*333d2b36SAndroid Build Coastguard Worker			}
77*333d2b36SAndroid Build Coastguard Worker		}
78*333d2b36SAndroid Build Coastguard Worker	})
79*333d2b36SAndroid Build Coastguard Worker
80*333d2b36SAndroid Build Coastguard Worker	// Create the output file.
81*333d2b36SAndroid Build Coastguard Worker	dir := android.PathForOutput(ctx, compdbOutputProjectsDirectory)
82*333d2b36SAndroid Build Coastguard Worker	os.MkdirAll(filepath.Join(android.AbsSrcDirForExistingUseCases(), dir.String()), 0777)
83*333d2b36SAndroid Build Coastguard Worker	compDBFile := dir.Join(ctx, compdbFilename)
84*333d2b36SAndroid Build Coastguard Worker	f, err := os.Create(filepath.Join(android.AbsSrcDirForExistingUseCases(), compDBFile.String()))
85*333d2b36SAndroid Build Coastguard Worker	if err != nil {
86*333d2b36SAndroid Build Coastguard Worker		log.Fatalf("Could not create file %s: %s", compDBFile, err)
87*333d2b36SAndroid Build Coastguard Worker	}
88*333d2b36SAndroid Build Coastguard Worker	defer func() {
89*333d2b36SAndroid Build Coastguard Worker		if err := f.Close(); err != nil {
90*333d2b36SAndroid Build Coastguard Worker			log.Fatalf("Could not close file %s: %s", compDBFile, err)
91*333d2b36SAndroid Build Coastguard Worker		}
92*333d2b36SAndroid Build Coastguard Worker	}()
93*333d2b36SAndroid Build Coastguard Worker
94*333d2b36SAndroid Build Coastguard Worker	v := make([]compDbEntry, 0, len(m))
95*333d2b36SAndroid Build Coastguard Worker	for _, value := range m {
96*333d2b36SAndroid Build Coastguard Worker		v = append(v, value)
97*333d2b36SAndroid Build Coastguard Worker	}
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Worker	w := json.NewEncoder(f)
100*333d2b36SAndroid Build Coastguard Worker	if outputCompdbDebugInfo {
101*333d2b36SAndroid Build Coastguard Worker		w.SetIndent("", " ")
102*333d2b36SAndroid Build Coastguard Worker	}
103*333d2b36SAndroid Build Coastguard Worker	if err := w.Encode(v); err != nil {
104*333d2b36SAndroid Build Coastguard Worker		log.Fatalf("Failed to encode: %s", err)
105*333d2b36SAndroid Build Coastguard Worker	}
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Worker	if finalLinkDir := ctx.Config().Getenv(envVariableCompdbLink); finalLinkDir != "" {
108*333d2b36SAndroid Build Coastguard Worker		finalLinkPath := filepath.Join(finalLinkDir, compdbFilename)
109*333d2b36SAndroid Build Coastguard Worker		os.Remove(finalLinkPath)
110*333d2b36SAndroid Build Coastguard Worker		if err := os.Symlink(compDBFile.String(), finalLinkPath); err != nil {
111*333d2b36SAndroid Build Coastguard Worker			log.Fatalf("Unable to symlink %s to %s: %s", compDBFile, finalLinkPath, err)
112*333d2b36SAndroid Build Coastguard Worker		}
113*333d2b36SAndroid Build Coastguard Worker	}
114*333d2b36SAndroid Build Coastguard Worker}
115*333d2b36SAndroid Build Coastguard Worker
116*333d2b36SAndroid Build Coastguard Workerfunc expandAllVars(ctx android.SingletonContext, args []string) []string {
117*333d2b36SAndroid Build Coastguard Worker	var out []string
118*333d2b36SAndroid Build Coastguard Worker	for _, arg := range args {
119*333d2b36SAndroid Build Coastguard Worker		if arg != "" {
120*333d2b36SAndroid Build Coastguard Worker			if val, err := evalAndSplitVariable(ctx, arg); err == nil {
121*333d2b36SAndroid Build Coastguard Worker				out = append(out, val...)
122*333d2b36SAndroid Build Coastguard Worker			} else {
123*333d2b36SAndroid Build Coastguard Worker				out = append(out, arg)
124*333d2b36SAndroid Build Coastguard Worker			}
125*333d2b36SAndroid Build Coastguard Worker		}
126*333d2b36SAndroid Build Coastguard Worker	}
127*333d2b36SAndroid Build Coastguard Worker	return out
128*333d2b36SAndroid Build Coastguard Worker}
129*333d2b36SAndroid Build Coastguard Worker
130*333d2b36SAndroid Build Coastguard Workerfunc getArguments(src android.Path, ctx android.SingletonContext, ccModule *Module, ccPath string, cxxPath string) []string {
131*333d2b36SAndroid Build Coastguard Worker	var args []string
132*333d2b36SAndroid Build Coastguard Worker	isCpp := false
133*333d2b36SAndroid Build Coastguard Worker	isAsm := false
134*333d2b36SAndroid Build Coastguard Worker	// TODO It would be better to ask soong for the types here.
135*333d2b36SAndroid Build Coastguard Worker	var clangPath string
136*333d2b36SAndroid Build Coastguard Worker	switch src.Ext() {
137*333d2b36SAndroid Build Coastguard Worker	case ".S", ".s", ".asm":
138*333d2b36SAndroid Build Coastguard Worker		isAsm = true
139*333d2b36SAndroid Build Coastguard Worker		isCpp = false
140*333d2b36SAndroid Build Coastguard Worker		clangPath = ccPath
141*333d2b36SAndroid Build Coastguard Worker	case ".c":
142*333d2b36SAndroid Build Coastguard Worker		isAsm = false
143*333d2b36SAndroid Build Coastguard Worker		isCpp = false
144*333d2b36SAndroid Build Coastguard Worker		clangPath = ccPath
145*333d2b36SAndroid Build Coastguard Worker	case ".cpp", ".cc", ".cxx", ".mm":
146*333d2b36SAndroid Build Coastguard Worker		isAsm = false
147*333d2b36SAndroid Build Coastguard Worker		isCpp = true
148*333d2b36SAndroid Build Coastguard Worker		clangPath = cxxPath
149*333d2b36SAndroid Build Coastguard Worker	case ".o":
150*333d2b36SAndroid Build Coastguard Worker		return nil
151*333d2b36SAndroid Build Coastguard Worker	default:
152*333d2b36SAndroid Build Coastguard Worker		log.Print("Unknown file extension " + src.Ext() + " on file " + src.String())
153*333d2b36SAndroid Build Coastguard Worker		isAsm = true
154*333d2b36SAndroid Build Coastguard Worker		isCpp = false
155*333d2b36SAndroid Build Coastguard Worker		clangPath = ccPath
156*333d2b36SAndroid Build Coastguard Worker	}
157*333d2b36SAndroid Build Coastguard Worker	args = append(args, clangPath)
158*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CommonFlags)...)
159*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CommonFlags)...)
160*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CFlags)...)
161*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CFlags)...)
162*333d2b36SAndroid Build Coastguard Worker	if isCpp {
163*333d2b36SAndroid Build Coastguard Worker		args = append(args, expandAllVars(ctx, ccModule.flags.Global.CppFlags)...)
164*333d2b36SAndroid Build Coastguard Worker		args = append(args, expandAllVars(ctx, ccModule.flags.Local.CppFlags)...)
165*333d2b36SAndroid Build Coastguard Worker	} else if !isAsm {
166*333d2b36SAndroid Build Coastguard Worker		args = append(args, expandAllVars(ctx, ccModule.flags.Global.ConlyFlags)...)
167*333d2b36SAndroid Build Coastguard Worker		args = append(args, expandAllVars(ctx, ccModule.flags.Local.ConlyFlags)...)
168*333d2b36SAndroid Build Coastguard Worker	}
169*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
170*333d2b36SAndroid Build Coastguard Worker	args = append(args, expandAllVars(ctx, ccModule.flags.NoOverrideFlags)...)
171*333d2b36SAndroid Build Coastguard Worker	args = append(args, src.String())
172*333d2b36SAndroid Build Coastguard Worker	return args
173*333d2b36SAndroid Build Coastguard Worker}
174*333d2b36SAndroid Build Coastguard Worker
175*333d2b36SAndroid Build Coastguard Workerfunc generateCompdbProject(compiledModule CompiledInterface, ctx android.SingletonContext, ccModule *Module, builds map[string]compDbEntry) {
176*333d2b36SAndroid Build Coastguard Worker	srcs := compiledModule.Srcs()
177*333d2b36SAndroid Build Coastguard Worker	if len(srcs) == 0 {
178*333d2b36SAndroid Build Coastguard Worker		return
179*333d2b36SAndroid Build Coastguard Worker	}
180*333d2b36SAndroid Build Coastguard Worker
181*333d2b36SAndroid Build Coastguard Worker	pathToCC, err := ctx.Eval(pctx, "${config.ClangBin}")
182*333d2b36SAndroid Build Coastguard Worker	ccPath := "/bin/false"
183*333d2b36SAndroid Build Coastguard Worker	cxxPath := "/bin/false"
184*333d2b36SAndroid Build Coastguard Worker	if err == nil {
185*333d2b36SAndroid Build Coastguard Worker		ccPath = filepath.Join(pathToCC, "clang")
186*333d2b36SAndroid Build Coastguard Worker		cxxPath = filepath.Join(pathToCC, "clang++")
187*333d2b36SAndroid Build Coastguard Worker	}
188*333d2b36SAndroid Build Coastguard Worker	for _, src := range srcs {
189*333d2b36SAndroid Build Coastguard Worker		if _, ok := builds[src.String()]; !ok {
190*333d2b36SAndroid Build Coastguard Worker			args := getArguments(src, ctx, ccModule, ccPath, cxxPath)
191*333d2b36SAndroid Build Coastguard Worker			if args == nil {
192*333d2b36SAndroid Build Coastguard Worker				continue
193*333d2b36SAndroid Build Coastguard Worker			}
194*333d2b36SAndroid Build Coastguard Worker			builds[src.String()] = compDbEntry{
195*333d2b36SAndroid Build Coastguard Worker				Directory: android.AbsSrcDirForExistingUseCases(),
196*333d2b36SAndroid Build Coastguard Worker				Arguments: getArguments(src, ctx, ccModule, ccPath, cxxPath),
197*333d2b36SAndroid Build Coastguard Worker				File:      src.String(),
198*333d2b36SAndroid Build Coastguard Worker			}
199*333d2b36SAndroid Build Coastguard Worker		}
200*333d2b36SAndroid Build Coastguard Worker	}
201*333d2b36SAndroid Build Coastguard Worker}
202*333d2b36SAndroid Build Coastguard Worker
203*333d2b36SAndroid Build Coastguard Workerfunc evalAndSplitVariable(ctx android.SingletonContext, str string) ([]string, error) {
204*333d2b36SAndroid Build Coastguard Worker	evaluated, err := ctx.Eval(pctx, str)
205*333d2b36SAndroid Build Coastguard Worker	if err == nil {
206*333d2b36SAndroid Build Coastguard Worker		return strings.Fields(evaluated), nil
207*333d2b36SAndroid Build Coastguard Worker	}
208*333d2b36SAndroid Build Coastguard Worker	return []string{""}, err
209*333d2b36SAndroid Build Coastguard Worker}
210