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