1*9e94795aSAndroid Build Coastguard Worker/* 2*9e94795aSAndroid Build Coastguard Worker * Copyright (C) 2024 The Android Open Source Project 3*9e94795aSAndroid Build Coastguard Worker * 4*9e94795aSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*9e94795aSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*9e94795aSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*9e94795aSAndroid Build Coastguard Worker * 8*9e94795aSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*9e94795aSAndroid Build Coastguard Worker * 10*9e94795aSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*9e94795aSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*9e94795aSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*9e94795aSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*9e94795aSAndroid Build Coastguard Worker * limitations under the License. 15*9e94795aSAndroid Build Coastguard Worker */ 16*9e94795aSAndroid Build Coastguard Worker 17*9e94795aSAndroid Build Coastguard Worker// Binary ide_query generates and analyzes build artifacts. 18*9e94795aSAndroid Build Coastguard Worker// The produced result can be consumed by IDEs to provide language features. 19*9e94795aSAndroid Build Coastguard Workerpackage main 20*9e94795aSAndroid Build Coastguard Worker 21*9e94795aSAndroid Build Coastguard Workerimport ( 22*9e94795aSAndroid Build Coastguard Worker "bytes" 23*9e94795aSAndroid Build Coastguard Worker "container/list" 24*9e94795aSAndroid Build Coastguard Worker "context" 25*9e94795aSAndroid Build Coastguard Worker "encoding/json" 26*9e94795aSAndroid Build Coastguard Worker "flag" 27*9e94795aSAndroid Build Coastguard Worker "fmt" 28*9e94795aSAndroid Build Coastguard Worker "log" 29*9e94795aSAndroid Build Coastguard Worker "os" 30*9e94795aSAndroid Build Coastguard Worker "os/exec" 31*9e94795aSAndroid Build Coastguard Worker "path" 32*9e94795aSAndroid Build Coastguard Worker "slices" 33*9e94795aSAndroid Build Coastguard Worker "strings" 34*9e94795aSAndroid Build Coastguard Worker 35*9e94795aSAndroid Build Coastguard Worker "google.golang.org/protobuf/proto" 36*9e94795aSAndroid Build Coastguard Worker apb "ide_query/cc_analyzer_proto" 37*9e94795aSAndroid Build Coastguard Worker pb "ide_query/ide_query_proto" 38*9e94795aSAndroid Build Coastguard Worker) 39*9e94795aSAndroid Build Coastguard Worker 40*9e94795aSAndroid Build Coastguard Worker// Env contains information about the current environment. 41*9e94795aSAndroid Build Coastguard Workertype Env struct { 42*9e94795aSAndroid Build Coastguard Worker LunchTarget LunchTarget 43*9e94795aSAndroid Build Coastguard Worker RepoDir string 44*9e94795aSAndroid Build Coastguard Worker OutDir string 45*9e94795aSAndroid Build Coastguard Worker ClangToolsRoot string 46*9e94795aSAndroid Build Coastguard Worker} 47*9e94795aSAndroid Build Coastguard Worker 48*9e94795aSAndroid Build Coastguard Worker// LunchTarget is a parsed Android lunch target. 49*9e94795aSAndroid Build Coastguard Worker// Input format: <product_name>-<release_type>-<build_variant> 50*9e94795aSAndroid Build Coastguard Workertype LunchTarget struct { 51*9e94795aSAndroid Build Coastguard Worker Product string 52*9e94795aSAndroid Build Coastguard Worker Release string 53*9e94795aSAndroid Build Coastguard Worker Variant string 54*9e94795aSAndroid Build Coastguard Worker} 55*9e94795aSAndroid Build Coastguard Worker 56*9e94795aSAndroid Build Coastguard Workervar _ flag.Value = (*LunchTarget)(nil) 57*9e94795aSAndroid Build Coastguard Worker 58*9e94795aSAndroid Build Coastguard Worker// // Get implements flag.Value. 59*9e94795aSAndroid Build Coastguard Worker// func (l *LunchTarget) Get() any { 60*9e94795aSAndroid Build Coastguard Worker// return l 61*9e94795aSAndroid Build Coastguard Worker// } 62*9e94795aSAndroid Build Coastguard Worker 63*9e94795aSAndroid Build Coastguard Worker// Set implements flag.Value. 64*9e94795aSAndroid Build Coastguard Workerfunc (l *LunchTarget) Set(s string) error { 65*9e94795aSAndroid Build Coastguard Worker parts := strings.Split(s, "-") 66*9e94795aSAndroid Build Coastguard Worker if len(parts) != 3 { 67*9e94795aSAndroid Build Coastguard Worker return fmt.Errorf("invalid lunch target: %q, must have form <product_name>-<release_type>-<build_variant>", s) 68*9e94795aSAndroid Build Coastguard Worker } 69*9e94795aSAndroid Build Coastguard Worker *l = LunchTarget{ 70*9e94795aSAndroid Build Coastguard Worker Product: parts[0], 71*9e94795aSAndroid Build Coastguard Worker Release: parts[1], 72*9e94795aSAndroid Build Coastguard Worker Variant: parts[2], 73*9e94795aSAndroid Build Coastguard Worker } 74*9e94795aSAndroid Build Coastguard Worker return nil 75*9e94795aSAndroid Build Coastguard Worker} 76*9e94795aSAndroid Build Coastguard Worker 77*9e94795aSAndroid Build Coastguard Worker// String implements flag.Value. 78*9e94795aSAndroid Build Coastguard Workerfunc (l *LunchTarget) String() string { 79*9e94795aSAndroid Build Coastguard Worker return fmt.Sprintf("%s-%s-%s", l.Product, l.Release, l.Variant) 80*9e94795aSAndroid Build Coastguard Worker} 81*9e94795aSAndroid Build Coastguard Worker 82*9e94795aSAndroid Build Coastguard Workerfunc main() { 83*9e94795aSAndroid Build Coastguard Worker var env Env 84*9e94795aSAndroid Build Coastguard Worker env.OutDir = strings.TrimSuffix(os.Getenv("OUT_DIR"), "/") 85*9e94795aSAndroid Build Coastguard Worker env.RepoDir = os.Getenv("ANDROID_BUILD_TOP") 86*9e94795aSAndroid Build Coastguard Worker env.ClangToolsRoot = os.Getenv("PREBUILTS_CLANG_TOOLS_ROOT") 87*9e94795aSAndroid Build Coastguard Worker flag.Var(&env.LunchTarget, "lunch_target", "The lunch target to query") 88*9e94795aSAndroid Build Coastguard Worker flag.Parse() 89*9e94795aSAndroid Build Coastguard Worker files := flag.Args() 90*9e94795aSAndroid Build Coastguard Worker if len(files) == 0 { 91*9e94795aSAndroid Build Coastguard Worker fmt.Println("No files provided.") 92*9e94795aSAndroid Build Coastguard Worker os.Exit(1) 93*9e94795aSAndroid Build Coastguard Worker return 94*9e94795aSAndroid Build Coastguard Worker } 95*9e94795aSAndroid Build Coastguard Worker 96*9e94795aSAndroid Build Coastguard Worker var ccFiles, javaFiles []string 97*9e94795aSAndroid Build Coastguard Worker for _, f := range files { 98*9e94795aSAndroid Build Coastguard Worker switch { 99*9e94795aSAndroid Build Coastguard Worker case strings.HasSuffix(f, ".java") || strings.HasSuffix(f, ".kt"): 100*9e94795aSAndroid Build Coastguard Worker javaFiles = append(javaFiles, f) 101*9e94795aSAndroid Build Coastguard Worker case strings.HasSuffix(f, ".cc") || strings.HasSuffix(f, ".cpp") || strings.HasSuffix(f, ".h"): 102*9e94795aSAndroid Build Coastguard Worker ccFiles = append(ccFiles, f) 103*9e94795aSAndroid Build Coastguard Worker default: 104*9e94795aSAndroid Build Coastguard Worker log.Printf("File %q is supported - will be skipped.", f) 105*9e94795aSAndroid Build Coastguard Worker } 106*9e94795aSAndroid Build Coastguard Worker } 107*9e94795aSAndroid Build Coastguard Worker 108*9e94795aSAndroid Build Coastguard Worker ctx := context.Background() 109*9e94795aSAndroid Build Coastguard Worker // TODO(michaelmerg): Figure out if module_bp_java_deps.json and compile_commands.json is outdated. 110*9e94795aSAndroid Build Coastguard Worker runMake(ctx, env, "nothing") 111*9e94795aSAndroid Build Coastguard Worker 112*9e94795aSAndroid Build Coastguard Worker javaModules, err := loadJavaModules(env) 113*9e94795aSAndroid Build Coastguard Worker if err != nil { 114*9e94795aSAndroid Build Coastguard Worker log.Printf("Failed to load java modules: %v", err) 115*9e94795aSAndroid Build Coastguard Worker } 116*9e94795aSAndroid Build Coastguard Worker 117*9e94795aSAndroid Build Coastguard Worker var targets []string 118*9e94795aSAndroid Build Coastguard Worker javaTargetsByFile := findJavaModules(javaFiles, javaModules) 119*9e94795aSAndroid Build Coastguard Worker for _, t := range javaTargetsByFile { 120*9e94795aSAndroid Build Coastguard Worker targets = append(targets, t) 121*9e94795aSAndroid Build Coastguard Worker } 122*9e94795aSAndroid Build Coastguard Worker 123*9e94795aSAndroid Build Coastguard Worker ccTargets, err := getCCTargets(ctx, env, ccFiles) 124*9e94795aSAndroid Build Coastguard Worker if err != nil { 125*9e94795aSAndroid Build Coastguard Worker log.Fatalf("Failed to query cc targets: %v", err) 126*9e94795aSAndroid Build Coastguard Worker } 127*9e94795aSAndroid Build Coastguard Worker targets = append(targets, ccTargets...) 128*9e94795aSAndroid Build Coastguard Worker if len(targets) == 0 { 129*9e94795aSAndroid Build Coastguard Worker fmt.Println("No targets found.") 130*9e94795aSAndroid Build Coastguard Worker os.Exit(1) 131*9e94795aSAndroid Build Coastguard Worker return 132*9e94795aSAndroid Build Coastguard Worker } 133*9e94795aSAndroid Build Coastguard Worker 134*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "Running make for modules: %v\n", strings.Join(targets, ", ")) 135*9e94795aSAndroid Build Coastguard Worker if err := runMake(ctx, env, targets...); err != nil { 136*9e94795aSAndroid Build Coastguard Worker log.Printf("Building modules failed: %v", err) 137*9e94795aSAndroid Build Coastguard Worker } 138*9e94795aSAndroid Build Coastguard Worker 139*9e94795aSAndroid Build Coastguard Worker var analysis pb.IdeAnalysis 140*9e94795aSAndroid Build Coastguard Worker results, units := getJavaInputs(env, javaTargetsByFile, javaModules) 141*9e94795aSAndroid Build Coastguard Worker analysis.Results = results 142*9e94795aSAndroid Build Coastguard Worker analysis.Units = units 143*9e94795aSAndroid Build Coastguard Worker if err != nil && analysis.Error == nil { 144*9e94795aSAndroid Build Coastguard Worker analysis.Error = &pb.AnalysisError{ 145*9e94795aSAndroid Build Coastguard Worker ErrorMessage: err.Error(), 146*9e94795aSAndroid Build Coastguard Worker } 147*9e94795aSAndroid Build Coastguard Worker } 148*9e94795aSAndroid Build Coastguard Worker 149*9e94795aSAndroid Build Coastguard Worker results, units, err = getCCInputs(ctx, env, ccFiles) 150*9e94795aSAndroid Build Coastguard Worker analysis.Results = append(analysis.Results, results...) 151*9e94795aSAndroid Build Coastguard Worker analysis.Units = append(analysis.Units, units...) 152*9e94795aSAndroid Build Coastguard Worker if err != nil && analysis.Error == nil { 153*9e94795aSAndroid Build Coastguard Worker analysis.Error = &pb.AnalysisError{ 154*9e94795aSAndroid Build Coastguard Worker ErrorMessage: err.Error(), 155*9e94795aSAndroid Build Coastguard Worker } 156*9e94795aSAndroid Build Coastguard Worker } 157*9e94795aSAndroid Build Coastguard Worker 158*9e94795aSAndroid Build Coastguard Worker analysis.BuildOutDir = env.OutDir 159*9e94795aSAndroid Build Coastguard Worker data, err := proto.Marshal(&analysis) 160*9e94795aSAndroid Build Coastguard Worker if err != nil { 161*9e94795aSAndroid Build Coastguard Worker log.Fatalf("Failed to marshal result proto: %v", err) 162*9e94795aSAndroid Build Coastguard Worker } 163*9e94795aSAndroid Build Coastguard Worker 164*9e94795aSAndroid Build Coastguard Worker _, err = os.Stdout.Write(data) 165*9e94795aSAndroid Build Coastguard Worker if err != nil { 166*9e94795aSAndroid Build Coastguard Worker log.Fatalf("Failed to write result proto: %v", err) 167*9e94795aSAndroid Build Coastguard Worker } 168*9e94795aSAndroid Build Coastguard Worker 169*9e94795aSAndroid Build Coastguard Worker for _, r := range analysis.Results { 170*9e94795aSAndroid Build Coastguard Worker fmt.Fprintf(os.Stderr, "%s: %+v\n", r.GetSourceFilePath(), r.GetStatus()) 171*9e94795aSAndroid Build Coastguard Worker } 172*9e94795aSAndroid Build Coastguard Worker} 173*9e94795aSAndroid Build Coastguard Worker 174*9e94795aSAndroid Build Coastguard Workerfunc repoState(env Env, filePaths []string) *apb.RepoState { 175*9e94795aSAndroid Build Coastguard Worker const compDbPath = "soong/development/ide/compdb/compile_commands.json" 176*9e94795aSAndroid Build Coastguard Worker return &apb.RepoState{ 177*9e94795aSAndroid Build Coastguard Worker RepoDir: env.RepoDir, 178*9e94795aSAndroid Build Coastguard Worker ActiveFilePath: filePaths, 179*9e94795aSAndroid Build Coastguard Worker OutDir: env.OutDir, 180*9e94795aSAndroid Build Coastguard Worker CompDbPath: path.Join(env.OutDir, compDbPath), 181*9e94795aSAndroid Build Coastguard Worker } 182*9e94795aSAndroid Build Coastguard Worker} 183*9e94795aSAndroid Build Coastguard Worker 184*9e94795aSAndroid Build Coastguard Workerfunc runCCanalyzer(ctx context.Context, env Env, mode string, in []byte) ([]byte, error) { 185*9e94795aSAndroid Build Coastguard Worker ccAnalyzerPath := path.Join(env.ClangToolsRoot, "bin/ide_query_cc_analyzer") 186*9e94795aSAndroid Build Coastguard Worker outBuffer := new(bytes.Buffer) 187*9e94795aSAndroid Build Coastguard Worker 188*9e94795aSAndroid Build Coastguard Worker inBuffer := new(bytes.Buffer) 189*9e94795aSAndroid Build Coastguard Worker inBuffer.Write(in) 190*9e94795aSAndroid Build Coastguard Worker 191*9e94795aSAndroid Build Coastguard Worker cmd := exec.CommandContext(ctx, ccAnalyzerPath, "--mode="+mode) 192*9e94795aSAndroid Build Coastguard Worker cmd.Dir = env.RepoDir 193*9e94795aSAndroid Build Coastguard Worker 194*9e94795aSAndroid Build Coastguard Worker cmd.Stdin = inBuffer 195*9e94795aSAndroid Build Coastguard Worker cmd.Stdout = outBuffer 196*9e94795aSAndroid Build Coastguard Worker cmd.Stderr = os.Stderr 197*9e94795aSAndroid Build Coastguard Worker 198*9e94795aSAndroid Build Coastguard Worker err := cmd.Run() 199*9e94795aSAndroid Build Coastguard Worker 200*9e94795aSAndroid Build Coastguard Worker return outBuffer.Bytes(), err 201*9e94795aSAndroid Build Coastguard Worker} 202*9e94795aSAndroid Build Coastguard Worker 203*9e94795aSAndroid Build Coastguard Worker// Execute cc_analyzer and get all the targets that needs to be build for analyzing files. 204*9e94795aSAndroid Build Coastguard Workerfunc getCCTargets(ctx context.Context, env Env, filePaths []string) ([]string, error) { 205*9e94795aSAndroid Build Coastguard Worker state, err := proto.Marshal(repoState(env, filePaths)) 206*9e94795aSAndroid Build Coastguard Worker if err != nil { 207*9e94795aSAndroid Build Coastguard Worker log.Fatalln("Failed to serialize state:", err) 208*9e94795aSAndroid Build Coastguard Worker } 209*9e94795aSAndroid Build Coastguard Worker 210*9e94795aSAndroid Build Coastguard Worker resp := new(apb.DepsResponse) 211*9e94795aSAndroid Build Coastguard Worker result, err := runCCanalyzer(ctx, env, "deps", state) 212*9e94795aSAndroid Build Coastguard Worker if err != nil { 213*9e94795aSAndroid Build Coastguard Worker return nil, err 214*9e94795aSAndroid Build Coastguard Worker } 215*9e94795aSAndroid Build Coastguard Worker 216*9e94795aSAndroid Build Coastguard Worker if err := proto.Unmarshal(result, resp); err != nil { 217*9e94795aSAndroid Build Coastguard Worker return nil, fmt.Errorf("malformed response from cc_analyzer: %v", err) 218*9e94795aSAndroid Build Coastguard Worker } 219*9e94795aSAndroid Build Coastguard Worker 220*9e94795aSAndroid Build Coastguard Worker var targets []string 221*9e94795aSAndroid Build Coastguard Worker if resp.Status != nil && resp.Status.Code != apb.Status_OK { 222*9e94795aSAndroid Build Coastguard Worker return targets, fmt.Errorf("cc_analyzer failed: %v", resp.Status.Message) 223*9e94795aSAndroid Build Coastguard Worker } 224*9e94795aSAndroid Build Coastguard Worker 225*9e94795aSAndroid Build Coastguard Worker for _, deps := range resp.Deps { 226*9e94795aSAndroid Build Coastguard Worker targets = append(targets, deps.BuildTarget...) 227*9e94795aSAndroid Build Coastguard Worker } 228*9e94795aSAndroid Build Coastguard Worker return targets, nil 229*9e94795aSAndroid Build Coastguard Worker} 230*9e94795aSAndroid Build Coastguard Worker 231*9e94795aSAndroid Build Coastguard Workerfunc getCCInputs(ctx context.Context, env Env, filePaths []string) ([]*pb.AnalysisResult, []*pb.BuildableUnit, error) { 232*9e94795aSAndroid Build Coastguard Worker state, err := proto.Marshal(repoState(env, filePaths)) 233*9e94795aSAndroid Build Coastguard Worker if err != nil { 234*9e94795aSAndroid Build Coastguard Worker log.Fatalln("Failed to serialize state:", err) 235*9e94795aSAndroid Build Coastguard Worker } 236*9e94795aSAndroid Build Coastguard Worker 237*9e94795aSAndroid Build Coastguard Worker resp := new(apb.IdeAnalysis) 238*9e94795aSAndroid Build Coastguard Worker result, err := runCCanalyzer(ctx, env, "inputs", state) 239*9e94795aSAndroid Build Coastguard Worker if err != nil { 240*9e94795aSAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("cc_analyzer failed:", err) 241*9e94795aSAndroid Build Coastguard Worker } 242*9e94795aSAndroid Build Coastguard Worker if err := proto.Unmarshal(result, resp); err != nil { 243*9e94795aSAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("malformed response from cc_analyzer: %v", err) 244*9e94795aSAndroid Build Coastguard Worker } 245*9e94795aSAndroid Build Coastguard Worker if resp.Status != nil && resp.Status.Code != apb.Status_OK { 246*9e94795aSAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("cc_analyzer failed: %v", resp.Status.Message) 247*9e94795aSAndroid Build Coastguard Worker } 248*9e94795aSAndroid Build Coastguard Worker 249*9e94795aSAndroid Build Coastguard Worker var results []*pb.AnalysisResult 250*9e94795aSAndroid Build Coastguard Worker var units []*pb.BuildableUnit 251*9e94795aSAndroid Build Coastguard Worker for _, s := range resp.Sources { 252*9e94795aSAndroid Build Coastguard Worker status := &pb.AnalysisResult_Status{ 253*9e94795aSAndroid Build Coastguard Worker Code: pb.AnalysisResult_Status_CODE_OK, 254*9e94795aSAndroid Build Coastguard Worker } 255*9e94795aSAndroid Build Coastguard Worker if s.GetStatus().GetCode() != apb.Status_OK { 256*9e94795aSAndroid Build Coastguard Worker status.Code = pb.AnalysisResult_Status_CODE_BUILD_FAILED 257*9e94795aSAndroid Build Coastguard Worker status.StatusMessage = proto.String(s.GetStatus().GetMessage()) 258*9e94795aSAndroid Build Coastguard Worker } 259*9e94795aSAndroid Build Coastguard Worker 260*9e94795aSAndroid Build Coastguard Worker result := &pb.AnalysisResult{ 261*9e94795aSAndroid Build Coastguard Worker SourceFilePath: s.GetPath(), 262*9e94795aSAndroid Build Coastguard Worker UnitId: s.GetPath(), 263*9e94795aSAndroid Build Coastguard Worker Status: status, 264*9e94795aSAndroid Build Coastguard Worker } 265*9e94795aSAndroid Build Coastguard Worker results = append(results, result) 266*9e94795aSAndroid Build Coastguard Worker 267*9e94795aSAndroid Build Coastguard Worker var generated []*pb.GeneratedFile 268*9e94795aSAndroid Build Coastguard Worker for _, f := range s.Generated { 269*9e94795aSAndroid Build Coastguard Worker generated = append(generated, &pb.GeneratedFile{ 270*9e94795aSAndroid Build Coastguard Worker Path: f.GetPath(), 271*9e94795aSAndroid Build Coastguard Worker Contents: f.GetContents(), 272*9e94795aSAndroid Build Coastguard Worker }) 273*9e94795aSAndroid Build Coastguard Worker } 274*9e94795aSAndroid Build Coastguard Worker genUnit := &pb.BuildableUnit{ 275*9e94795aSAndroid Build Coastguard Worker Id: "genfiles_for_" + s.GetPath(), 276*9e94795aSAndroid Build Coastguard Worker SourceFilePaths: s.GetDeps(), 277*9e94795aSAndroid Build Coastguard Worker GeneratedFiles: generated, 278*9e94795aSAndroid Build Coastguard Worker } 279*9e94795aSAndroid Build Coastguard Worker 280*9e94795aSAndroid Build Coastguard Worker unit := &pb.BuildableUnit{ 281*9e94795aSAndroid Build Coastguard Worker Id: s.GetPath(), 282*9e94795aSAndroid Build Coastguard Worker Language: pb.Language_LANGUAGE_CPP, 283*9e94795aSAndroid Build Coastguard Worker SourceFilePaths: []string{s.GetPath()}, 284*9e94795aSAndroid Build Coastguard Worker CompilerArguments: s.GetCompilerArguments(), 285*9e94795aSAndroid Build Coastguard Worker DependencyIds: []string{genUnit.GetId()}, 286*9e94795aSAndroid Build Coastguard Worker } 287*9e94795aSAndroid Build Coastguard Worker units = append(units, unit, genUnit) 288*9e94795aSAndroid Build Coastguard Worker } 289*9e94795aSAndroid Build Coastguard Worker return results, units, nil 290*9e94795aSAndroid Build Coastguard Worker} 291*9e94795aSAndroid Build Coastguard Worker 292*9e94795aSAndroid Build Coastguard Worker// findJavaModules tries to find the modules that cover the given file paths. 293*9e94795aSAndroid Build Coastguard Worker// If a file is covered by multiple modules, the first module is returned. 294*9e94795aSAndroid Build Coastguard Workerfunc findJavaModules(paths []string, modules map[string]*javaModule) map[string]string { 295*9e94795aSAndroid Build Coastguard Worker ret := make(map[string]string) 296*9e94795aSAndroid Build Coastguard Worker // A file may be part of multiple modules. To make the result deterministic, 297*9e94795aSAndroid Build Coastguard Worker // check the modules in sorted order. 298*9e94795aSAndroid Build Coastguard Worker keys := make([]string, 0, len(modules)) 299*9e94795aSAndroid Build Coastguard Worker for name := range modules { 300*9e94795aSAndroid Build Coastguard Worker keys = append(keys, name) 301*9e94795aSAndroid Build Coastguard Worker } 302*9e94795aSAndroid Build Coastguard Worker slices.Sort(keys) 303*9e94795aSAndroid Build Coastguard Worker for _, name := range keys { 304*9e94795aSAndroid Build Coastguard Worker if strings.HasSuffix(name, ".impl") { 305*9e94795aSAndroid Build Coastguard Worker continue 306*9e94795aSAndroid Build Coastguard Worker } 307*9e94795aSAndroid Build Coastguard Worker 308*9e94795aSAndroid Build Coastguard Worker module := modules[name] 309*9e94795aSAndroid Build Coastguard Worker for i, p := range paths { 310*9e94795aSAndroid Build Coastguard Worker if slices.Contains(module.Srcs, p) { 311*9e94795aSAndroid Build Coastguard Worker ret[p] = name 312*9e94795aSAndroid Build Coastguard Worker paths = append(paths[:i], paths[i+1:]...) 313*9e94795aSAndroid Build Coastguard Worker break 314*9e94795aSAndroid Build Coastguard Worker } 315*9e94795aSAndroid Build Coastguard Worker } 316*9e94795aSAndroid Build Coastguard Worker if len(paths) == 0 { 317*9e94795aSAndroid Build Coastguard Worker break 318*9e94795aSAndroid Build Coastguard Worker } 319*9e94795aSAndroid Build Coastguard Worker } 320*9e94795aSAndroid Build Coastguard Worker return ret 321*9e94795aSAndroid Build Coastguard Worker} 322*9e94795aSAndroid Build Coastguard Worker 323*9e94795aSAndroid Build Coastguard Workerfunc getJavaInputs(env Env, modulesByPath map[string]string, modules map[string]*javaModule) ([]*pb.AnalysisResult, []*pb.BuildableUnit) { 324*9e94795aSAndroid Build Coastguard Worker var results []*pb.AnalysisResult 325*9e94795aSAndroid Build Coastguard Worker unitsById := make(map[string]*pb.BuildableUnit) 326*9e94795aSAndroid Build Coastguard Worker for p, moduleName := range modulesByPath { 327*9e94795aSAndroid Build Coastguard Worker r := &pb.AnalysisResult{ 328*9e94795aSAndroid Build Coastguard Worker SourceFilePath: p, 329*9e94795aSAndroid Build Coastguard Worker } 330*9e94795aSAndroid Build Coastguard Worker results = append(results, r) 331*9e94795aSAndroid Build Coastguard Worker 332*9e94795aSAndroid Build Coastguard Worker m := modules[moduleName] 333*9e94795aSAndroid Build Coastguard Worker if m == nil { 334*9e94795aSAndroid Build Coastguard Worker r.Status = &pb.AnalysisResult_Status{ 335*9e94795aSAndroid Build Coastguard Worker Code: pb.AnalysisResult_Status_CODE_NOT_FOUND, 336*9e94795aSAndroid Build Coastguard Worker StatusMessage: proto.String("File not found in any module."), 337*9e94795aSAndroid Build Coastguard Worker } 338*9e94795aSAndroid Build Coastguard Worker continue 339*9e94795aSAndroid Build Coastguard Worker } 340*9e94795aSAndroid Build Coastguard Worker 341*9e94795aSAndroid Build Coastguard Worker r.UnitId = moduleName 342*9e94795aSAndroid Build Coastguard Worker r.Status = &pb.AnalysisResult_Status{Code: pb.AnalysisResult_Status_CODE_OK} 343*9e94795aSAndroid Build Coastguard Worker if unitsById[r.UnitId] != nil { 344*9e94795aSAndroid Build Coastguard Worker // File is covered by an already created unit. 345*9e94795aSAndroid Build Coastguard Worker continue 346*9e94795aSAndroid Build Coastguard Worker } 347*9e94795aSAndroid Build Coastguard Worker 348*9e94795aSAndroid Build Coastguard Worker u := &pb.BuildableUnit{ 349*9e94795aSAndroid Build Coastguard Worker Id: moduleName, 350*9e94795aSAndroid Build Coastguard Worker Language: pb.Language_LANGUAGE_JAVA, 351*9e94795aSAndroid Build Coastguard Worker SourceFilePaths: m.Srcs, 352*9e94795aSAndroid Build Coastguard Worker GeneratedFiles: genFiles(env, m), 353*9e94795aSAndroid Build Coastguard Worker DependencyIds: m.Deps, 354*9e94795aSAndroid Build Coastguard Worker } 355*9e94795aSAndroid Build Coastguard Worker unitsById[u.Id] = u 356*9e94795aSAndroid Build Coastguard Worker 357*9e94795aSAndroid Build Coastguard Worker q := list.New() 358*9e94795aSAndroid Build Coastguard Worker for _, d := range m.Deps { 359*9e94795aSAndroid Build Coastguard Worker q.PushBack(d) 360*9e94795aSAndroid Build Coastguard Worker } 361*9e94795aSAndroid Build Coastguard Worker for q.Len() > 0 { 362*9e94795aSAndroid Build Coastguard Worker name := q.Remove(q.Front()).(string) 363*9e94795aSAndroid Build Coastguard Worker mod := modules[name] 364*9e94795aSAndroid Build Coastguard Worker if mod == nil || unitsById[name] != nil { 365*9e94795aSAndroid Build Coastguard Worker continue 366*9e94795aSAndroid Build Coastguard Worker } 367*9e94795aSAndroid Build Coastguard Worker 368*9e94795aSAndroid Build Coastguard Worker unitsById[name] = &pb.BuildableUnit{ 369*9e94795aSAndroid Build Coastguard Worker Id: name, 370*9e94795aSAndroid Build Coastguard Worker SourceFilePaths: mod.Srcs, 371*9e94795aSAndroid Build Coastguard Worker GeneratedFiles: genFiles(env, mod), 372*9e94795aSAndroid Build Coastguard Worker DependencyIds: mod.Deps, 373*9e94795aSAndroid Build Coastguard Worker } 374*9e94795aSAndroid Build Coastguard Worker 375*9e94795aSAndroid Build Coastguard Worker for _, d := range mod.Deps { 376*9e94795aSAndroid Build Coastguard Worker q.PushBack(d) 377*9e94795aSAndroid Build Coastguard Worker } 378*9e94795aSAndroid Build Coastguard Worker } 379*9e94795aSAndroid Build Coastguard Worker } 380*9e94795aSAndroid Build Coastguard Worker 381*9e94795aSAndroid Build Coastguard Worker units := make([]*pb.BuildableUnit, 0, len(unitsById)) 382*9e94795aSAndroid Build Coastguard Worker for _, u := range unitsById { 383*9e94795aSAndroid Build Coastguard Worker units = append(units, u) 384*9e94795aSAndroid Build Coastguard Worker } 385*9e94795aSAndroid Build Coastguard Worker return results, units 386*9e94795aSAndroid Build Coastguard Worker} 387*9e94795aSAndroid Build Coastguard Worker 388*9e94795aSAndroid Build Coastguard Worker// genFiles returns the generated files (paths that start with outDir/) for the 389*9e94795aSAndroid Build Coastguard Worker// given module. Generated files that do not exist are ignored. 390*9e94795aSAndroid Build Coastguard Workerfunc genFiles(env Env, mod *javaModule) []*pb.GeneratedFile { 391*9e94795aSAndroid Build Coastguard Worker var paths []string 392*9e94795aSAndroid Build Coastguard Worker paths = append(paths, mod.Srcs...) 393*9e94795aSAndroid Build Coastguard Worker paths = append(paths, mod.SrcJars...) 394*9e94795aSAndroid Build Coastguard Worker paths = append(paths, mod.Jars...) 395*9e94795aSAndroid Build Coastguard Worker 396*9e94795aSAndroid Build Coastguard Worker prefix := env.OutDir + "/" 397*9e94795aSAndroid Build Coastguard Worker var ret []*pb.GeneratedFile 398*9e94795aSAndroid Build Coastguard Worker for _, p := range paths { 399*9e94795aSAndroid Build Coastguard Worker relPath, ok := strings.CutPrefix(p, prefix) 400*9e94795aSAndroid Build Coastguard Worker if !ok { 401*9e94795aSAndroid Build Coastguard Worker continue 402*9e94795aSAndroid Build Coastguard Worker } 403*9e94795aSAndroid Build Coastguard Worker 404*9e94795aSAndroid Build Coastguard Worker contents, err := os.ReadFile(path.Join(env.RepoDir, p)) 405*9e94795aSAndroid Build Coastguard Worker if err != nil { 406*9e94795aSAndroid Build Coastguard Worker continue 407*9e94795aSAndroid Build Coastguard Worker } 408*9e94795aSAndroid Build Coastguard Worker 409*9e94795aSAndroid Build Coastguard Worker ret = append(ret, &pb.GeneratedFile{ 410*9e94795aSAndroid Build Coastguard Worker Path: relPath, 411*9e94795aSAndroid Build Coastguard Worker Contents: contents, 412*9e94795aSAndroid Build Coastguard Worker }) 413*9e94795aSAndroid Build Coastguard Worker } 414*9e94795aSAndroid Build Coastguard Worker return ret 415*9e94795aSAndroid Build Coastguard Worker} 416*9e94795aSAndroid Build Coastguard Worker 417*9e94795aSAndroid Build Coastguard Worker// runMake runs Soong build for the given modules. 418*9e94795aSAndroid Build Coastguard Workerfunc runMake(ctx context.Context, env Env, modules ...string) error { 419*9e94795aSAndroid Build Coastguard Worker args := []string{ 420*9e94795aSAndroid Build Coastguard Worker "--make-mode", 421*9e94795aSAndroid Build Coastguard Worker "ANDROID_BUILD_ENVIRONMENT_CONFIG=googler-cog", 422*9e94795aSAndroid Build Coastguard Worker "SOONG_GEN_COMPDB=1", 423*9e94795aSAndroid Build Coastguard Worker "TARGET_PRODUCT=" + env.LunchTarget.Product, 424*9e94795aSAndroid Build Coastguard Worker "TARGET_RELEASE=" + env.LunchTarget.Release, 425*9e94795aSAndroid Build Coastguard Worker "TARGET_BUILD_VARIANT=" + env.LunchTarget.Variant, 426*9e94795aSAndroid Build Coastguard Worker "TARGET_BUILD_TYPE=release", 427*9e94795aSAndroid Build Coastguard Worker "-k", 428*9e94795aSAndroid Build Coastguard Worker } 429*9e94795aSAndroid Build Coastguard Worker args = append(args, modules...) 430*9e94795aSAndroid Build Coastguard Worker cmd := exec.CommandContext(ctx, "build/soong/soong_ui.bash", args...) 431*9e94795aSAndroid Build Coastguard Worker cmd.Dir = env.RepoDir 432*9e94795aSAndroid Build Coastguard Worker cmd.Stdout = os.Stderr 433*9e94795aSAndroid Build Coastguard Worker cmd.Stderr = os.Stderr 434*9e94795aSAndroid Build Coastguard Worker return cmd.Run() 435*9e94795aSAndroid Build Coastguard Worker} 436*9e94795aSAndroid Build Coastguard Worker 437*9e94795aSAndroid Build Coastguard Workertype javaModule struct { 438*9e94795aSAndroid Build Coastguard Worker Path []string `json:"path,omitempty"` 439*9e94795aSAndroid Build Coastguard Worker Deps []string `json:"dependencies,omitempty"` 440*9e94795aSAndroid Build Coastguard Worker Srcs []string `json:"srcs,omitempty"` 441*9e94795aSAndroid Build Coastguard Worker Jars []string `json:"jars,omitempty"` 442*9e94795aSAndroid Build Coastguard Worker SrcJars []string `json:"srcjars,omitempty"` 443*9e94795aSAndroid Build Coastguard Worker} 444*9e94795aSAndroid Build Coastguard Worker 445*9e94795aSAndroid Build Coastguard Workerfunc loadJavaModules(env Env) (map[string]*javaModule, error) { 446*9e94795aSAndroid Build Coastguard Worker javaDepsPath := path.Join(env.RepoDir, env.OutDir, "soong/module_bp_java_deps.json") 447*9e94795aSAndroid Build Coastguard Worker data, err := os.ReadFile(javaDepsPath) 448*9e94795aSAndroid Build Coastguard Worker if err != nil { 449*9e94795aSAndroid Build Coastguard Worker return nil, err 450*9e94795aSAndroid Build Coastguard Worker } 451*9e94795aSAndroid Build Coastguard Worker 452*9e94795aSAndroid Build Coastguard Worker var ret map[string]*javaModule // module name -> module 453*9e94795aSAndroid Build Coastguard Worker if err = json.Unmarshal(data, &ret); err != nil { 454*9e94795aSAndroid Build Coastguard Worker return nil, err 455*9e94795aSAndroid Build Coastguard Worker } 456*9e94795aSAndroid Build Coastguard Worker 457*9e94795aSAndroid Build Coastguard Worker // Add top level java_sdk_library for .impl modules. 458*9e94795aSAndroid Build Coastguard Worker for name, module := range ret { 459*9e94795aSAndroid Build Coastguard Worker if striped := strings.TrimSuffix(name, ".impl"); striped != name { 460*9e94795aSAndroid Build Coastguard Worker ret[striped] = module 461*9e94795aSAndroid Build Coastguard Worker } 462*9e94795aSAndroid Build Coastguard Worker } 463*9e94795aSAndroid Build Coastguard Worker return ret, nil 464*9e94795aSAndroid Build Coastguard Worker} 465