xref: /aosp_15_r20/build/make/tools/ide_query/ide_query.go (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
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