blob: 27255d1942ea1c5fe19a5de7f5f3f213a2e225af [file] [log] [blame]
Chris Parsonsf3c96ef2020-09-29 02:23:17 -04001// Copyright 2020 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18 "bytes"
19 "errors"
20 "fmt"
Chris Parsonsa798d962020-10-12 23:44:08 -040021 "io/ioutil"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040022 "os"
23 "os/exec"
Usta Shresthaacd5a0c2022-06-22 11:20:50 -040024 "path"
Chris Parsonsa798d962020-10-12 23:44:08 -040025 "path/filepath"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040026 "runtime"
27 "strings"
28 "sync"
Chris Parsonsa798d962020-10-12 23:44:08 -040029
Chris Parsons944e7d02021-03-11 11:08:46 -050030 "android/soong/bazel/cquery"
Jingwen Chen1e347862021-09-02 12:11:49 +000031 "android/soong/shared"
Chris Parsons1a7aca02022-04-25 22:35:15 -040032 "github.com/google/blueprint"
Liz Kammer8206d4f2021-03-03 16:40:52 -050033
Patrice Arruda05ab2d02020-12-12 06:24:26 +000034 "android/soong/bazel"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040035)
36
Chris Parsonsf874e462022-05-10 13:50:12 -040037func init() {
38 RegisterMixedBuildsMutator(InitRegistrationContext)
39}
40
41func RegisterMixedBuildsMutator(ctx RegistrationContext) {
42 ctx.PostDepsMutators(func(ctx RegisterMutatorsContext) {
43 ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
44 })
45}
46
47func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
48 if m := ctx.Module(); m.Enabled() {
49 if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
50 if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
51 mixedBuildMod.QueueBazelCall(ctx)
52 }
53 }
54 }
55}
56
Liz Kammerf29df7c2021-04-02 13:37:39 -040057type cqueryRequest interface {
58 // Name returns a string name for this request type. Such request type names must be unique,
59 // and must only consist of alphanumeric characters.
60 Name() string
61
62 // StarlarkFunctionBody returns a starlark function body to process this request type.
63 // The returned string is the body of a Starlark function which obtains
64 // all request-relevant information about a target and returns a string containing
65 // this information.
66 // The function should have the following properties:
67 // - `target` is the only parameter to this function (a configured target).
68 // - The return value must be a string.
69 // - The function body should not be indented outside of its own scope.
70 StarlarkFunctionBody() string
71}
72
Chris Parsons787fb362021-10-14 18:43:51 -040073// Portion of cquery map key to describe target configuration.
74type configKey struct {
Liz Kammer0940b892022-03-18 15:55:04 -040075 arch string
76 osType OsType
Chris Parsons787fb362021-10-14 18:43:51 -040077}
78
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040079// Map key to describe bazel cquery requests.
80type cqueryKey struct {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040081 label string
Liz Kammerf29df7c2021-04-02 13:37:39 -040082 requestType cqueryRequest
Chris Parsons787fb362021-10-14 18:43:51 -040083 configKey configKey
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040084}
85
Chris Parsonsf874e462022-05-10 13:50:12 -040086// BazelContext is a context object useful for interacting with Bazel during
87// the course of a build. Use of Bazel to evaluate part of the build graph
88// is referred to as a "mixed build". (Some modules are managed by Soong,
89// some are managed by Bazel). To facilitate interop between these build
90// subgraphs, Soong may make requests to Bazel and evaluate their responses
91// so that Soong modules may accurately depend on Bazel targets.
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040092type BazelContext interface {
Chris Parsonsf874e462022-05-10 13:50:12 -040093 // Add a cquery request to the bazel request queue. All queued requests
94 // will be sent to Bazel on a subsequent invocation of InvokeBazel.
95 QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey)
96
97 // ** Cquery Results Retrieval Functions
98 // The below functions pertain to retrieving cquery results from a prior
99 // InvokeBazel function call and parsing the results.
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400100
101 // Returns result files built by building the given bazel target label.
Chris Parsonsf874e462022-05-10 13:50:12 -0400102 GetOutputFiles(label string, cfgKey configKey) ([]string, error)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500103
Chris Parsons944e7d02021-03-11 11:08:46 -0500104 // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
Chris Parsonsf874e462022-05-10 13:50:12 -0400105 GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
Liz Kammer3f9e1552021-04-02 18:47:09 -0400106
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000107 // Returns the executable binary resultant from building together the python sources
Chris Parsonsf874e462022-05-10 13:50:12 -0400108 // TODO(b/232976601): Remove.
109 GetPythonBinary(label string, cfgKey configKey) (string, error)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000110
Chris Parsonsf874e462022-05-10 13:50:12 -0400111 // ** end Cquery Results Retrieval Functions
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400112
113 // Issues commands to Bazel to receive results for all cquery requests
114 // queued in the BazelContext.
Yu Liu8d82ac52022-05-17 15:13:28 -0700115 InvokeBazel(config Config) error
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400116
117 // Returns true if bazel is enabled for the given configuration.
118 BazelEnabled() bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500119
120 // Returns the bazel output base (the root directory for all bazel intermediate outputs).
121 OutputBase() string
122
123 // Returns build statements which should get registered to reflect Bazel's outputs.
124 BuildStatementsToRegister() []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400125
126 // Returns the depsets defined in Bazel's aquery response.
127 AqueryDepsets() []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400128}
129
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400130type bazelRunner interface {
131 issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
132}
133
134type bazelPaths struct {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400135 homeDir string
136 bazelPath string
137 outputBase string
138 workspaceDir string
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200139 soongOutDir string
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000140 metricsDir string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400141}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400142
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400143// A context object which tracks queued requests that need to be made to Bazel,
144// and their results after the requests have been made.
145type bazelContext struct {
146 bazelRunner
147 paths *bazelPaths
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400148 requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
149 requestMutex sync.Mutex // requests can be written in parallel
150
151 results map[cqueryKey]string // Results of cquery requests after Bazel invocations
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500152
153 // Build statements which should get registered to reflect Bazel's outputs.
154 buildStatements []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400155
156 // Depsets which should be used for Bazel's build statements.
157 depsets []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400158}
159
160var _ BazelContext = &bazelContext{}
161
162// A bazel context to use when Bazel is disabled.
163type noopBazelContext struct{}
164
165var _ BazelContext = noopBazelContext{}
166
167// A bazel context to use for tests.
168type MockBazelContext struct {
Liz Kammera92e8442021-04-07 20:25:21 -0400169 OutputBaseDir string
170
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000171 LabelToOutputFiles map[string][]string
172 LabelToCcInfo map[string]cquery.CcInfo
173 LabelToPythonBinary map[string]string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400174}
175
Chris Parsonsf874e462022-05-10 13:50:12 -0400176func (m MockBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
177 panic("unimplemented")
Chris Parsons8d6e4332021-02-22 16:13:50 -0500178}
179
Chris Parsonsf874e462022-05-10 13:50:12 -0400180func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
181 result, _ := m.LabelToOutputFiles[label]
182 return result, nil
Liz Kammer3f9e1552021-04-02 18:47:09 -0400183}
184
Chris Parsonsf874e462022-05-10 13:50:12 -0400185func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
186 result, _ := m.LabelToCcInfo[label]
187 return result, nil
188}
189
190func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
191 result, _ := m.LabelToPythonBinary[label]
192 return result, nil
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000193}
194
Yu Liu8d82ac52022-05-17 15:13:28 -0700195func (m MockBazelContext) InvokeBazel(config Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400196 panic("unimplemented")
197}
198
199func (m MockBazelContext) BazelEnabled() bool {
200 return true
201}
202
Liz Kammera92e8442021-04-07 20:25:21 -0400203func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500204
205func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
206 return []bazel.BuildStatement{}
207}
208
Chris Parsons1a7aca02022-04-25 22:35:15 -0400209func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
210 return []bazel.AqueryDepset{}
211}
212
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400213var _ BazelContext = MockBazelContext{}
214
Chris Parsonsf874e462022-05-10 13:50:12 -0400215func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
216 key := cqueryKey{label, requestType, cfgKey}
217 bazelCtx.requestMutex.Lock()
218 defer bazelCtx.requestMutex.Unlock()
219 bazelCtx.requests[key] = true
220}
221
222func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
223 key := cqueryKey{label, cquery.GetOutputFiles, cfgKey}
224 if rawString, ok := bazelCtx.results[key]; ok {
Chris Parsons944e7d02021-03-11 11:08:46 -0500225 bazelOutput := strings.TrimSpace(rawString)
Chris Parsonsf874e462022-05-10 13:50:12 -0400226 return cquery.GetOutputFiles.ParseResult(bazelOutput), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400227 }
Chris Parsonsf874e462022-05-10 13:50:12 -0400228 return nil, fmt.Errorf("no bazel response found for %v", key)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400229}
230
Chris Parsonsf874e462022-05-10 13:50:12 -0400231func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
232 key := cqueryKey{label, cquery.GetCcInfo, cfgKey}
233 if rawString, ok := bazelCtx.results[key]; ok {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000234 bazelOutput := strings.TrimSpace(rawString)
Chris Parsonsf874e462022-05-10 13:50:12 -0400235 return cquery.GetCcInfo.ParseResult(bazelOutput)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000236 }
Chris Parsonsf874e462022-05-10 13:50:12 -0400237 return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000238}
239
Chris Parsonsf874e462022-05-10 13:50:12 -0400240func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
241 key := cqueryKey{label, cquery.GetPythonBinary, cfgKey}
242 if rawString, ok := bazelCtx.results[key]; ok {
243 bazelOutput := strings.TrimSpace(rawString)
244 return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
245 }
246 return "", fmt.Errorf("no bazel response found for %v", key)
247}
248
249func (n noopBazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500250 panic("unimplemented")
251}
252
Chris Parsonsf874e462022-05-10 13:50:12 -0400253func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
Chris Parsons808d84c2021-03-09 20:43:32 -0500254 panic("unimplemented")
255}
256
Chris Parsonsf874e462022-05-10 13:50:12 -0400257func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
258 panic("unimplemented")
259}
260
261func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000262 panic("unimplemented")
263}
264
Yu Liu8d82ac52022-05-17 15:13:28 -0700265func (n noopBazelContext) InvokeBazel(config Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400266 panic("unimplemented")
267}
268
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500269func (m noopBazelContext) OutputBase() string {
270 return ""
271}
272
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400273func (n noopBazelContext) BazelEnabled() bool {
274 return false
275}
276
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500277func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
278 return []bazel.BuildStatement{}
279}
280
Chris Parsons1a7aca02022-04-25 22:35:15 -0400281func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
282 return []bazel.AqueryDepset{}
283}
284
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400285func NewBazelContext(c *config) (BazelContext, error) {
Chris Parsons8b77a002020-10-27 18:59:25 -0400286 // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
287 // are production ready.
Jingwen Chen442b1a42021-06-17 07:02:15 +0000288 if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400289 return noopBazelContext{}, nil
290 }
291
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400292 p, err := bazelPathsFromConfig(c)
293 if err != nil {
294 return nil, err
295 }
296 return &bazelContext{
297 bazelRunner: &builtinBazelRunner{},
298 paths: p,
299 requests: make(map[cqueryKey]bool),
300 }, nil
301}
302
303func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
304 p := bazelPaths{
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200305 soongOutDir: c.soongOutDir,
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400306 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400307 missingEnvVars := []string{}
308 if len(c.Getenv("BAZEL_HOME")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400309 p.homeDir = c.Getenv("BAZEL_HOME")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400310 } else {
311 missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
312 }
313 if len(c.Getenv("BAZEL_PATH")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400314 p.bazelPath = c.Getenv("BAZEL_PATH")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400315 } else {
316 missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
317 }
318 if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400319 p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400320 } else {
321 missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
322 }
323 if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400324 p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400325 } else {
326 missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
327 }
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000328 if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400329 p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000330 } else {
331 missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
332 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400333 if len(missingEnvVars) > 0 {
334 return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
335 } else {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400336 return &p, nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400337 }
338}
339
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400340func (p *bazelPaths) BazelMetricsDir() string {
341 return p.metricsDir
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000342}
343
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400344func (context *bazelContext) BazelEnabled() bool {
345 return true
346}
347
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400348func pwdPrefix() string {
349 // Darwin doesn't have /proc
350 if runtime.GOOS != "darwin" {
351 return "PWD=/proc/self/cwd"
352 }
353 return ""
354}
355
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400356type bazelCommand struct {
357 command string
358 // query or label
359 expression string
360}
361
362type mockBazelRunner struct {
363 bazelCommandResults map[bazelCommand]string
364 commands []bazelCommand
Yu Liu8d82ac52022-05-17 15:13:28 -0700365 extraFlags []string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400366}
367
368func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
369 runName bazel.RunName,
370 command bazelCommand,
371 extraFlags ...string) (string, string, error) {
372 r.commands = append(r.commands, command)
Yu Liu8d82ac52022-05-17 15:13:28 -0700373 r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400374 if ret, ok := r.bazelCommandResults[command]; ok {
375 return ret, "", nil
376 }
377 return "", "", nil
378}
379
380type builtinBazelRunner struct{}
381
Chris Parsons808d84c2021-03-09 20:43:32 -0500382// Issues the given bazel command with given build label and additional flags.
383// Returns (stdout, stderr, error). The first and second return values are strings
384// containing the stdout and stderr of the run command, and an error is returned if
385// the invocation returned an error code.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400386func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
Chris Parsons808d84c2021-03-09 20:43:32 -0500387 extraFlags ...string) (string, string, error) {
Romain Jobredeaux41fd5e42021-08-27 15:59:39 +0000388 cmdFlags := []string{
389 // --noautodetect_server_javabase has the practical consequence of preventing Bazel from
390 // attempting to download rules_java, which is incompatible with
391 // --experimental_repository_disable_download set further below.
392 // rules_java is also not needed until mixed builds start building java targets.
393 // TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
394 "--noautodetect_server_javabase",
395 "--output_base=" + absolutePath(paths.outputBase),
396 command.command,
397 }
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400398 cmdFlags = append(cmdFlags, command.expression)
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400399 cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
Jingwen Chen91220d72021-03-24 02:18:33 -0400400
401 // Set default platforms to canonicalized values for mixed builds requests.
402 // If these are set in the bazelrc, they will have values that are
403 // non-canonicalized to @sourceroot labels, and thus be invalid when
404 // referenced from the buildroot.
405 //
406 // The actual platform values here may be overridden by configuration
407 // transitions from the buildroot.
Chris Parsonsee423b02021-02-08 23:04:59 -0500408 cmdFlags = append(cmdFlags,
Liz Kammerc0c66092021-07-26 17:38:47 -0400409 fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
Chris Parsonsee423b02021-02-08 23:04:59 -0500410 cmdFlags = append(cmdFlags,
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200411 fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
Jingwen Chen91220d72021-03-24 02:18:33 -0400412 // This should be parameterized on the host OS, but let's restrict to linux
413 // to keep things simple for now.
414 cmdFlags = append(cmdFlags,
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200415 fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"))
Jingwen Chen91220d72021-03-24 02:18:33 -0400416
Chris Parsons8d6e4332021-02-22 16:13:50 -0500417 // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
418 cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400419 cmdFlags = append(cmdFlags, extraFlags...)
420
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400421 bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200422 bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200423 bazelCmd.Env = append(os.Environ(),
424 "HOME="+paths.homeDir,
425 pwdPrefix(),
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200426 "BUILD_DIR="+absolutePath(paths.soongOutDir),
Jingwen Chen8c523582021-06-01 11:19:53 +0000427 // Make OUT_DIR absolute here so tools/bazel.sh uses the correct
428 // OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
429 "OUT_DIR="+absolutePath(paths.outDir()),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500430 // Disables local host detection of gcc; toolchain information is defined
431 // explicitly in BUILD files.
432 "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
Colin Crossff0278b2020-10-09 19:24:15 -0700433 stderr := &bytes.Buffer{}
434 bazelCmd.Stderr = stderr
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400435
436 if output, err := bazelCmd.Output(); err != nil {
Chris Parsons808d84c2021-03-09 20:43:32 -0500437 return "", string(stderr.Bytes()),
438 fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400439 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500440 return string(output), string(stderr.Bytes()), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400441 }
442}
443
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400444func (context *bazelContext) mainBzlFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500445 // TODO(cparsons): Define configuration transitions programmatically based
446 // on available archs.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400447 contents := `
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500448#####################################################
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400449# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500450#####################################################
451
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400452def _config_node_transition_impl(settings, attr):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500453 return {
Chris Parsons787fb362021-10-14 18:43:51 -0400454 "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500455 }
456
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400457_config_node_transition = transition(
458 implementation = _config_node_transition_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500459 inputs = [],
460 outputs = [
461 "//command_line_option:platforms",
462 ],
463)
464
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400465def _passthrough_rule_impl(ctx):
466 return [DefaultInfo(files = depset(ctx.files.deps))]
467
468config_node = rule(
469 implementation = _passthrough_rule_impl,
470 attrs = {
471 "arch" : attr.string(mandatory = True),
Chris Parsons787fb362021-10-14 18:43:51 -0400472 "os" : attr.string(mandatory = True),
473 "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400474 "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
475 },
Chris Parsons8d6e4332021-02-22 16:13:50 -0500476)
477
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400478
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500479# Rule representing the root of the build, to depend on all Bazel targets that
480# are required for the build. Building this target will build the entire Bazel
481# build tree.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400482mixed_build_root = rule(
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400483 implementation = _passthrough_rule_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500484 attrs = {
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400485 "deps" : attr.label_list(),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500486 },
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400487)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500488
489def _phony_root_impl(ctx):
490 return []
491
492# Rule to depend on other targets but build nothing.
493# This is useful as follows: building a target of this rule will generate
494# symlink forests for all dependencies of the target, without executing any
495# actions of the build.
496phony_root = rule(
497 implementation = _phony_root_impl,
498 attrs = {"deps" : attr.label_list()},
499)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400500`
501 return []byte(contents)
502}
503
504func (context *bazelContext) mainBuildFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500505 // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
506 // architecture mapping.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400507 formatString := `
508# This file is generated by soong_build. Do not edit.
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400509load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
510
511%s
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400512
513mixed_build_root(name = "buildroot",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400514 deps = [%s],
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400515)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500516
517phony_root(name = "phonyroot",
518 deps = [":buildroot"],
519)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400520`
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400521 configNodeFormatString := `
522config_node(name = "%s",
523 arch = "%s",
Chris Parsons787fb362021-10-14 18:43:51 -0400524 os = "%s",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400525 deps = [%s],
526)
527`
528
529 configNodesSection := ""
530
Chris Parsons787fb362021-10-14 18:43:51 -0400531 labelsByConfig := map[string][]string{}
Usta Shrestha2bc1cd92022-06-23 13:45:24 -0400532 for val := range context.requests {
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200533 labelString := fmt.Sprintf("\"@%s\"", val.label)
Chris Parsons787fb362021-10-14 18:43:51 -0400534 configString := getConfigString(val)
535 labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400536 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400537
Jingwen Chen1e347862021-09-02 12:11:49 +0000538 allLabels := []string{}
Chris Parsons787fb362021-10-14 18:43:51 -0400539 for configString, labels := range labelsByConfig {
540 configTokens := strings.Split(configString, "|")
541 if len(configTokens) != 2 {
542 panic(fmt.Errorf("Unexpected config string format: %s", configString))
Jingwen Chen1e347862021-09-02 12:11:49 +0000543 }
Chris Parsons787fb362021-10-14 18:43:51 -0400544 archString := configTokens[0]
545 osString := configTokens[1]
546 targetString := fmt.Sprintf("%s_%s", osString, archString)
547 allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
548 labelsString := strings.Join(labels, ",\n ")
549 configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400550 }
551
Jingwen Chen1e347862021-09-02 12:11:49 +0000552 return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n ")))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400553}
554
Chris Parsons944e7d02021-03-11 11:08:46 -0500555func indent(original string) string {
556 result := ""
557 for _, line := range strings.Split(original, "\n") {
558 result += " " + line + "\n"
559 }
560 return result
561}
562
Chris Parsons808d84c2021-03-09 20:43:32 -0500563// Returns the file contents of the buildroot.cquery file that should be used for the cquery
564// expression in order to obtain information about buildroot and its dependencies.
565// The contents of this file depend on the bazelContext's requests; requests are enumerated
566// and grouped by their request type. The data retrieved for each label depends on its
567// request type.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400568func (context *bazelContext) cqueryStarlarkFileContents() []byte {
Liz Kammerf29df7c2021-04-02 13:37:39 -0400569 requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
Usta Shrestha2bc1cd92022-06-23 13:45:24 -0400570 for val := range context.requests {
Chris Parsons944e7d02021-03-11 11:08:46 -0500571 cqueryId := getCqueryId(val)
572 mapEntryString := fmt.Sprintf("%q : True", cqueryId)
573 requestTypeToCqueryIdEntries[val.requestType] =
574 append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
575 }
576 labelRegistrationMapSection := ""
577 functionDefSection := ""
578 mainSwitchSection := ""
579
580 mapDeclarationFormatString := `
581%s = {
582 %s
583}
584`
585 functionDefFormatString := `
586def %s(target):
587%s
588`
589 mainSwitchSectionFormatString := `
590 if id_string in %s:
591 return id_string + ">>" + %s(target)
592`
593
Usta Shrestha0b52d832022-02-04 21:37:39 -0500594 for requestType := range requestTypeToCqueryIdEntries {
Chris Parsons944e7d02021-03-11 11:08:46 -0500595 labelMapName := requestType.Name() + "_Labels"
596 functionName := requestType.Name() + "_Fn"
597 labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
598 labelMapName,
599 strings.Join(requestTypeToCqueryIdEntries[requestType], ",\n "))
600 functionDefSection += fmt.Sprintf(functionDefFormatString,
601 functionName,
602 indent(requestType.StarlarkFunctionBody()))
603 mainSwitchSection += fmt.Sprintf(mainSwitchSectionFormatString,
604 labelMapName, functionName)
605 }
606
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400607 formatString := `
608# This file is generated by soong_build. Do not edit.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400609
Chris Parsons944e7d02021-03-11 11:08:46 -0500610# Label Map Section
611%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500612
Chris Parsons944e7d02021-03-11 11:08:46 -0500613# Function Def Section
614%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500615
616def get_arch(target):
Chris Parsons787fb362021-10-14 18:43:51 -0400617 # TODO(b/199363072): filegroups and file targets aren't associated with any
618 # specific platform architecture in mixed builds. This is consistent with how
619 # Soong treats filegroups, but it may not be the case with manually-written
620 # filegroup BUILD targets.
Chris Parsons8d6e4332021-02-22 16:13:50 -0500621 buildoptions = build_options(target)
Jingwen Chen8f222742021-10-07 12:02:23 +0000622 if buildoptions == None:
623 # File targets do not have buildoptions. File targets aren't associated with
Chris Parsons787fb362021-10-14 18:43:51 -0400624 # any specific platform architecture in mixed builds, so use the host.
625 return "x86_64|linux"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500626 platforms = build_options(target)["//command_line_option:platforms"]
627 if len(platforms) != 1:
628 # An individual configured target should have only one platform architecture.
629 # Note that it's fine for there to be multiple architectures for the same label,
630 # but each is its own configured target.
631 fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
632 platform_name = build_options(target)["//command_line_option:platforms"][0].name
633 if platform_name == "host":
634 return "HOST"
Chris Parsons94a0bba2021-06-04 15:03:47 -0400635 elif platform_name.startswith("android_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400636 return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400637 elif platform_name.startswith("linux_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400638 return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400639 else:
640 fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500641 return "UNKNOWN"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500642
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400643def format(target):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500644 id_string = str(target.label) + "|" + get_arch(target)
Chris Parsons944e7d02021-03-11 11:08:46 -0500645
646 # Main switch section
647 %s
648 # This target was not requested via cquery, and thus must be a dependency
649 # of a requested target.
650 return id_string + ">>NONE"
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400651`
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400652
Chris Parsons944e7d02021-03-11 11:08:46 -0500653 return []byte(fmt.Sprintf(formatString, labelRegistrationMapSection, functionDefSection,
654 mainSwitchSection))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400655}
656
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200657// Returns a path containing build-related metadata required for interfacing
658// with Bazel. Example: out/soong/bazel.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400659func (p *bazelPaths) intermediatesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200660 return filepath.Join(p.soongOutDir, "bazel")
Chris Parsons8ccdb632020-11-17 15:41:01 -0500661}
662
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200663// Returns the path where the contents of the @soong_injection repository live.
664// It is used by Soong to tell Bazel things it cannot over the command line.
665func (p *bazelPaths) injectedFilesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200666 return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200667}
668
669// Returns the path of the synthetic Bazel workspace that contains a symlink
670// forest composed the whole source tree and BUILD files generated by bp2build.
671func (p *bazelPaths) syntheticWorkspaceDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200672 return filepath.Join(p.soongOutDir, "workspace")
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200673}
674
Jingwen Chen8c523582021-06-01 11:19:53 +0000675// Returns the path to the top level out dir ($OUT_DIR).
676func (p *bazelPaths) outDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200677 return filepath.Dir(p.soongOutDir)
Jingwen Chen8c523582021-06-01 11:19:53 +0000678}
679
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400680// Issues commands to Bazel to receive results for all cquery requests
681// queued in the BazelContext.
Yu Liu8d82ac52022-05-17 15:13:28 -0700682func (context *bazelContext) InvokeBazel(config Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400683 context.results = make(map[cqueryKey]string)
684
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400685 var cqueryOutput string
Chris Parsons808d84c2021-03-09 20:43:32 -0500686 var cqueryErr string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400687 var err error
Chris Parsons8ccdb632020-11-17 15:41:01 -0500688
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200689 soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200690 mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
691 if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
692 err = os.MkdirAll(mixedBuildsPath, 0777)
Chris Parsons07c1e4a2021-01-19 17:19:16 -0500693 }
Chris Parsons8ccdb632020-11-17 15:41:01 -0500694 if err != nil {
695 return err
696 }
Usta Shrestha902fd172022-03-02 15:27:49 -0500697 if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
698 err = os.MkdirAll(metricsDir, 0777)
699 if err != nil {
700 return err
701 }
702 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200703 err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
704 if err != nil {
705 return err
706 }
707
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400708 err = ioutil.WriteFile(
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200709 filepath.Join(mixedBuildsPath, "main.bzl"),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400710 context.mainBzlFileContents(), 0666)
711 if err != nil {
712 return err
713 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200714
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400715 err = ioutil.WriteFile(
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200716 filepath.Join(mixedBuildsPath, "BUILD.bazel"),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400717 context.mainBuildFileContents(), 0666)
718 if err != nil {
719 return err
720 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200721 cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400722 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800723 absolutePath(cqueryFileRelpath),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400724 context.cqueryStarlarkFileContents(), 0666)
725 if err != nil {
726 return err
727 }
Jingwen Chen1e347862021-09-02 12:11:49 +0000728
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200729 buildrootLabel := "@soong_injection//mixed_builds:buildroot"
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400730 cqueryOutput, cqueryErr, err = context.issueBazelCommand(
731 context.paths,
732 bazel.CqueryBuildRootRunName,
Liz Kammerc19d5cd2021-10-06 18:16:58 -0400733 bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400734 "--output=starlark",
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200735 "--starlark:file="+absolutePath(cqueryFileRelpath))
736 err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500737 []byte(cqueryOutput), 0666)
738 if err != nil {
739 return err
740 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400741
742 if err != nil {
743 return err
744 }
745
746 cqueryResults := map[string]string{}
747 for _, outputLine := range strings.Split(cqueryOutput, "\n") {
748 if strings.Contains(outputLine, ">>") {
749 splitLine := strings.SplitN(outputLine, ">>", 2)
750 cqueryResults[splitLine[0]] = splitLine[1]
751 }
752 }
753
Usta Shrestha902fd172022-03-02 15:27:49 -0500754 for val := range context.requests {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500755 if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
Usta Shrestha902fd172022-03-02 15:27:49 -0500756 context.results[val] = cqueryResult
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400757 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500758 return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
759 getCqueryId(val), cqueryOutput, cqueryErr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400760 }
761 }
762
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500763 // Issue an aquery command to retrieve action information about the bazel build tree.
764 //
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500765 var aqueryOutput string
Yu Liu8d82ac52022-05-17 15:13:28 -0700766 var coverageFlags []string
767 if Bool(config.productVariables.ClangCoverage) {
768 coverageFlags = append(coverageFlags, "--collect_code_coverage")
769 if len(config.productVariables.NativeCoveragePaths) > 0 ||
770 len(config.productVariables.NativeCoverageExcludePaths) > 0 {
771 includePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoveragePaths, "+", ",")
772 excludePaths := JoinWithPrefixAndSeparator(config.productVariables.NativeCoverageExcludePaths, "-", ",")
773 if len(includePaths) > 0 && len(excludePaths) > 0 {
774 includePaths += ","
775 }
776 coverageFlags = append(coverageFlags, fmt.Sprintf(`--instrumentation_filter=%s`,
777 includePaths+excludePaths))
778 }
779 }
780
781 extraFlags := append([]string{"--output=jsonproto"}, coverageFlags...)
782
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400783 aqueryOutput, _, err = context.issueBazelCommand(
784 context.paths,
785 bazel.AqueryBuildRootRunName,
786 bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
787 // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
788 // proto sources, which would add a number of unnecessary dependencies.
Yu Liu8d82ac52022-05-17 15:13:28 -0700789 extraFlags...)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400790
791 if err != nil {
792 return err
793 }
794
Chris Parsons1a7aca02022-04-25 22:35:15 -0400795 context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
Chris Parsons4f069892021-01-15 12:22:41 -0500796 if err != nil {
797 return err
798 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500799
800 // Issue a build command of the phony root to generate symlink forests for dependencies of the
801 // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
802 // but some of symlinks may be required to resolve source dependencies of the build.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400803 _, _, err = context.issueBazelCommand(
804 context.paths,
805 bazel.BazelBuildPhonyRootRunName,
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200806 bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"})
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500807
808 if err != nil {
809 return err
810 }
811
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400812 // Clear requests.
813 context.requests = map[cqueryKey]bool{}
814 return nil
815}
Chris Parsonsa798d962020-10-12 23:44:08 -0400816
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500817func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
818 return context.buildStatements
819}
820
Chris Parsons1a7aca02022-04-25 22:35:15 -0400821func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
822 return context.depsets
823}
824
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500825func (context *bazelContext) OutputBase() string {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400826 return context.paths.outputBase
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500827}
828
Chris Parsonsa798d962020-10-12 23:44:08 -0400829// Singleton used for registering BUILD file ninja dependencies (needed
830// for correctness of builds which use Bazel.
831func BazelSingleton() Singleton {
832 return &bazelSingleton{}
833}
834
835type bazelSingleton struct{}
836
837func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500838 // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
839 if !ctx.Config().BazelContext.BazelEnabled() {
840 return
841 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400842
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500843 // Add ninja file dependencies for files which all bazel invocations require.
844 bazelBuildList := absolutePath(filepath.Join(
Lukacs T. Berkif9008072021-08-16 15:24:48 +0200845 filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500846 ctx.AddNinjaFileDeps(bazelBuildList)
847
848 data, err := ioutil.ReadFile(bazelBuildList)
849 if err != nil {
850 ctx.Errorf(err.Error())
851 }
852 files := strings.Split(strings.TrimSpace(string(data)), "\n")
853 for _, file := range files {
854 ctx.AddNinjaFileDeps(file)
855 }
856
Chris Parsons1a7aca02022-04-25 22:35:15 -0400857 for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
858 var outputs []Path
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400859 for _, depsetDepHash := range depset.TransitiveDepSetHashes {
860 otherDepsetName := bazelDepsetName(depsetDepHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400861 outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
862 }
863 for _, artifactPath := range depset.DirectArtifacts {
864 outputs = append(outputs, PathForBazelOut(ctx, artifactPath))
865 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400866 thisDepsetName := bazelDepsetName(depset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400867 ctx.Build(pctx, BuildParams{
868 Rule: blueprint.Phony,
869 Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)},
870 Implicits: outputs,
871 })
872 }
873
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400874 executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
875 bazelOutDir := path.Join(executionRoot, "bazel-out")
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500876 for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500877 if len(buildStatement.Command) < 1 {
Rupert Shuttlewortha29903f2021-04-06 16:17:33 +0000878 panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500879 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500880 rule := NewRuleBuilder(pctx, ctx)
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400881 createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500882 // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
883 // some Bazel builtins (such as files in the bazel_tools directory) have far-future
884 // timestamps. Without restat, Ninja would emit warnings that the input files of a
885 // build statement have later timestamps than the outputs.
886 rule.Restat()
887
Chris Parsons155d7682022-02-18 16:32:24 -0500888 desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
889 rule.Build(fmt.Sprintf("bazel %d", index), desc)
Chris Parsonsa798d962020-10-12 23:44:08 -0400890 }
891}
Chris Parsons8d6e4332021-02-22 16:13:50 -0500892
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400893// Register bazel-owned build statements (obtained from the aquery invocation).
894func createCommand(cmd *RuleBuilderCommand, buildStatement bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx PathContext) {
895 // executionRoot is the action cwd.
896 cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
897
898 // Remove old outputs, as some actions might not rerun if the outputs are detected.
899 if len(buildStatement.OutputPaths) > 0 {
900 cmd.Text("rm -f")
901 for _, outputPath := range buildStatement.OutputPaths {
902 cmd.Text(outputPath)
903 }
904 cmd.Text("&&")
905 }
906
907 for _, pair := range buildStatement.Env {
908 // Set per-action env variables, if any.
909 cmd.Flag(pair.Key + "=" + pair.Value)
910 }
911
912 // The actual Bazel action.
913 cmd.Text(buildStatement.Command)
914
915 for _, outputPath := range buildStatement.OutputPaths {
916 cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
917 }
918 for _, inputPath := range buildStatement.InputPaths {
919 cmd.Implicit(PathForBazelOut(ctx, inputPath))
920 }
921 for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
922 otherDepsetName := bazelDepsetName(inputDepsetHash)
923 cmd.Implicit(PathForPhony(ctx, otherDepsetName))
924 }
925
926 if depfile := buildStatement.Depfile; depfile != nil {
927 // The paths in depfile are relative to `executionRoot`.
928 // Hence, they need to be corrected by replacing "bazel-out"
929 // with the full `bazelOutDir`.
930 // Otherwise, implicit outputs and implicit inputs under "bazel-out/"
931 // would be deemed missing.
932 // (Note: The regexp uses a capture group because the version of sed
933 // does not support a look-behind pattern.)
934 replacement := fmt.Sprintf(`&& sed -i'' -E 's@(^|\s|")bazel-out/@\1%s/@g' '%s'`,
935 bazelOutDir, *depfile)
936 cmd.Text(replacement)
937 cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
938 }
939
940 for _, symlinkPath := range buildStatement.SymlinkPaths {
941 cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
942 }
943}
944
Chris Parsons8d6e4332021-02-22 16:13:50 -0500945func getCqueryId(key cqueryKey) string {
Chris Parsons787fb362021-10-14 18:43:51 -0400946 return key.label + "|" + getConfigString(key)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500947}
948
Chris Parsons787fb362021-10-14 18:43:51 -0400949func getConfigString(key cqueryKey) string {
Liz Kammer0940b892022-03-18 15:55:04 -0400950 arch := key.configKey.arch
Chris Parsons787fb362021-10-14 18:43:51 -0400951 if len(arch) == 0 || arch == "common" {
952 // Use host platform, which is currently hardcoded to be x86_64.
953 arch = "x86_64"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500954 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400955 osName := key.configKey.osType.Name
956 if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" {
Chris Parsons787fb362021-10-14 18:43:51 -0400957 // Use host OS, which is currently hardcoded to be linux.
Usta Shrestha16ac1352022-06-22 11:01:55 -0400958 osName = "linux"
Chris Parsons787fb362021-10-14 18:43:51 -0400959 }
Usta Shrestha16ac1352022-06-22 11:01:55 -0400960 return arch + "|" + osName
Chris Parsons787fb362021-10-14 18:43:51 -0400961}
962
Chris Parsonsf874e462022-05-10 13:50:12 -0400963func GetConfigKey(ctx BaseModuleContext) configKey {
Liz Kammer0940b892022-03-18 15:55:04 -0400964 return configKey{
965 // use string because Arch is not a valid key in go
966 arch: ctx.Arch().String(),
967 osType: ctx.Os(),
968 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500969}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400970
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400971func bazelDepsetName(contentHash string) string {
972 return fmt.Sprintf("bazel_depset_%s", contentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400973}