blob: 0ed973a45ba49a81f366b02fca730eb12b503148 [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"
Liz Kammer337e9032022-08-03 15:49:43 -040032
Chris Parsons1a7aca02022-04-25 22:35:15 -040033 "github.com/google/blueprint"
Liz Kammer8206d4f2021-03-03 16:40:52 -050034
Patrice Arruda05ab2d02020-12-12 06:24:26 +000035 "android/soong/bazel"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040036)
37
Sasha Smundak1da064c2022-06-08 16:36:16 -070038var (
39 writeBazelFile = pctx.AndroidStaticRule("bazelWriteFileRule", blueprint.RuleParams{
40 Command: `sed "s/\\\\n/\n/g" ${out}.rsp >${out}`,
41 Rspfile: "${out}.rsp",
42 RspfileContent: "${content}",
43 }, "content")
Sasha Smundakc180dbd2022-07-03 14:55:58 -070044 _ = pctx.HostBinToolVariable("bazelBuildRunfilesTool", "build-runfiles")
45 buildRunfilesRule = pctx.AndroidStaticRule("bazelBuildRunfiles", blueprint.RuleParams{
46 Command: "${bazelBuildRunfilesTool} ${in} ${outDir}",
47 Depfile: "",
48 Description: "",
49 CommandDeps: []string{"${bazelBuildRunfilesTool}"},
50 }, "outDir")
Sasha Smundak1da064c2022-06-08 16:36:16 -070051)
52
Chris Parsonsf874e462022-05-10 13:50:12 -040053func init() {
54 RegisterMixedBuildsMutator(InitRegistrationContext)
55}
56
57func RegisterMixedBuildsMutator(ctx RegistrationContext) {
Liz Kammer337e9032022-08-03 15:49:43 -040058 ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
Chris Parsonsf874e462022-05-10 13:50:12 -040059 ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
60 })
61}
62
63func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
64 if m := ctx.Module(); m.Enabled() {
65 if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
66 if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
67 mixedBuildMod.QueueBazelCall(ctx)
68 }
69 }
70 }
71}
72
Liz Kammerf29df7c2021-04-02 13:37:39 -040073type cqueryRequest interface {
74 // Name returns a string name for this request type. Such request type names must be unique,
75 // and must only consist of alphanumeric characters.
76 Name() string
77
78 // StarlarkFunctionBody returns a starlark function body to process this request type.
79 // The returned string is the body of a Starlark function which obtains
80 // all request-relevant information about a target and returns a string containing
81 // this information.
82 // The function should have the following properties:
83 // - `target` is the only parameter to this function (a configured target).
84 // - The return value must be a string.
85 // - The function body should not be indented outside of its own scope.
86 StarlarkFunctionBody() string
87}
88
Chris Parsons787fb362021-10-14 18:43:51 -040089// Portion of cquery map key to describe target configuration.
90type configKey struct {
Liz Kammer0940b892022-03-18 15:55:04 -040091 arch string
92 osType OsType
Chris Parsons787fb362021-10-14 18:43:51 -040093}
94
Sasha Smundakfe9a5b82022-07-27 14:51:45 -070095func (c configKey) String() string {
96 return fmt.Sprintf("%s::%s", c.arch, c.osType)
97}
98
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040099// Map key to describe bazel cquery requests.
100type cqueryKey struct {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400101 label string
Liz Kammerf29df7c2021-04-02 13:37:39 -0400102 requestType cqueryRequest
Chris Parsons787fb362021-10-14 18:43:51 -0400103 configKey configKey
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400104}
105
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700106func (c cqueryKey) String() string {
107 return fmt.Sprintf("cquery(%s,%s,%s)", c.label, c.requestType.Name(), c.configKey)
108
109}
110
Chris Parsonsf874e462022-05-10 13:50:12 -0400111// BazelContext is a context object useful for interacting with Bazel during
112// the course of a build. Use of Bazel to evaluate part of the build graph
113// is referred to as a "mixed build". (Some modules are managed by Soong,
114// some are managed by Bazel). To facilitate interop between these build
115// subgraphs, Soong may make requests to Bazel and evaluate their responses
116// so that Soong modules may accurately depend on Bazel targets.
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400117type BazelContext interface {
Chris Parsonsf874e462022-05-10 13:50:12 -0400118 // Add a cquery request to the bazel request queue. All queued requests
119 // will be sent to Bazel on a subsequent invocation of InvokeBazel.
120 QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey)
121
122 // ** Cquery Results Retrieval Functions
123 // The below functions pertain to retrieving cquery results from a prior
124 // InvokeBazel function call and parsing the results.
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400125
126 // Returns result files built by building the given bazel target label.
Chris Parsonsf874e462022-05-10 13:50:12 -0400127 GetOutputFiles(label string, cfgKey configKey) ([]string, error)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500128
Chris Parsons944e7d02021-03-11 11:08:46 -0500129 // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
Chris Parsonsf874e462022-05-10 13:50:12 -0400130 GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
Liz Kammer3f9e1552021-04-02 18:47:09 -0400131
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000132 // Returns the executable binary resultant from building together the python sources
Chris Parsonsf874e462022-05-10 13:50:12 -0400133 // TODO(b/232976601): Remove.
134 GetPythonBinary(label string, cfgKey configKey) (string, error)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000135
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700136 // Returns the results of the GetApexInfo query (including output files)
137 GetApexInfo(label string, cfgkey configKey) (cquery.ApexCqueryInfo, error)
138
Chris Parsonsf874e462022-05-10 13:50:12 -0400139 // ** end Cquery Results Retrieval Functions
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400140
141 // Issues commands to Bazel to receive results for all cquery requests
142 // queued in the BazelContext.
Yu Liu8d82ac52022-05-17 15:13:28 -0700143 InvokeBazel(config Config) error
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400144
145 // Returns true if bazel is enabled for the given configuration.
146 BazelEnabled() bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500147
148 // Returns the bazel output base (the root directory for all bazel intermediate outputs).
149 OutputBase() string
150
151 // Returns build statements which should get registered to reflect Bazel's outputs.
152 BuildStatementsToRegister() []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400153
154 // Returns the depsets defined in Bazel's aquery response.
155 AqueryDepsets() []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400156}
157
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400158type bazelRunner interface {
159 issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
160}
161
162type bazelPaths struct {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400163 homeDir string
164 bazelPath string
165 outputBase string
166 workspaceDir string
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200167 soongOutDir string
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000168 metricsDir string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400169}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400170
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400171// A context object which tracks queued requests that need to be made to Bazel,
172// and their results after the requests have been made.
173type bazelContext struct {
174 bazelRunner
175 paths *bazelPaths
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400176 requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
177 requestMutex sync.Mutex // requests can be written in parallel
178
179 results map[cqueryKey]string // Results of cquery requests after Bazel invocations
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500180
181 // Build statements which should get registered to reflect Bazel's outputs.
182 buildStatements []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400183
184 // Depsets which should be used for Bazel's build statements.
185 depsets []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400186}
187
188var _ BazelContext = &bazelContext{}
189
190// A bazel context to use when Bazel is disabled.
191type noopBazelContext struct{}
192
193var _ BazelContext = noopBazelContext{}
194
195// A bazel context to use for tests.
196type MockBazelContext struct {
Liz Kammera92e8442021-04-07 20:25:21 -0400197 OutputBaseDir string
198
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000199 LabelToOutputFiles map[string][]string
200 LabelToCcInfo map[string]cquery.CcInfo
201 LabelToPythonBinary map[string]string
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700202 LabelToApexInfo map[string]cquery.ApexCqueryInfo
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400203}
204
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700205func (m MockBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
Chris Parsonsf874e462022-05-10 13:50:12 -0400206 panic("unimplemented")
Chris Parsons8d6e4332021-02-22 16:13:50 -0500207}
208
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700209func (m MockBazelContext) GetOutputFiles(label string, _ configKey) ([]string, error) {
Chris Parsonsf874e462022-05-10 13:50:12 -0400210 result, _ := m.LabelToOutputFiles[label]
211 return result, nil
Liz Kammer3f9e1552021-04-02 18:47:09 -0400212}
213
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700214func (m MockBazelContext) GetCcInfo(label string, _ configKey) (cquery.CcInfo, error) {
Chris Parsonsf874e462022-05-10 13:50:12 -0400215 result, _ := m.LabelToCcInfo[label]
216 return result, nil
217}
218
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700219func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
Chris Parsonsf874e462022-05-10 13:50:12 -0400220 result, _ := m.LabelToPythonBinary[label]
221 return result, nil
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000222}
223
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700224func (n MockBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
225 panic("unimplemented")
226}
227
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700228func (m MockBazelContext) InvokeBazel(_ Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400229 panic("unimplemented")
230}
231
232func (m MockBazelContext) BazelEnabled() bool {
233 return true
234}
235
Liz Kammera92e8442021-04-07 20:25:21 -0400236func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500237
238func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
239 return []bazel.BuildStatement{}
240}
241
Chris Parsons1a7aca02022-04-25 22:35:15 -0400242func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
243 return []bazel.AqueryDepset{}
244}
245
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400246var _ BazelContext = MockBazelContext{}
247
Chris Parsonsf874e462022-05-10 13:50:12 -0400248func (bazelCtx *bazelContext) QueueBazelRequest(label string, requestType cqueryRequest, cfgKey configKey) {
249 key := cqueryKey{label, requestType, cfgKey}
250 bazelCtx.requestMutex.Lock()
251 defer bazelCtx.requestMutex.Unlock()
252 bazelCtx.requests[key] = true
253}
254
255func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, error) {
256 key := cqueryKey{label, cquery.GetOutputFiles, cfgKey}
257 if rawString, ok := bazelCtx.results[key]; ok {
Chris Parsons944e7d02021-03-11 11:08:46 -0500258 bazelOutput := strings.TrimSpace(rawString)
Chris Parsonsf874e462022-05-10 13:50:12 -0400259 return cquery.GetOutputFiles.ParseResult(bazelOutput), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400260 }
Chris Parsonsf874e462022-05-10 13:50:12 -0400261 return nil, fmt.Errorf("no bazel response found for %v", key)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400262}
263
Chris Parsonsf874e462022-05-10 13:50:12 -0400264func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error) {
265 key := cqueryKey{label, cquery.GetCcInfo, cfgKey}
266 if rawString, ok := bazelCtx.results[key]; ok {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000267 bazelOutput := strings.TrimSpace(rawString)
Chris Parsonsf874e462022-05-10 13:50:12 -0400268 return cquery.GetCcInfo.ParseResult(bazelOutput)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000269 }
Chris Parsonsf874e462022-05-10 13:50:12 -0400270 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 +0000271}
272
Chris Parsonsf874e462022-05-10 13:50:12 -0400273func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
274 key := cqueryKey{label, cquery.GetPythonBinary, cfgKey}
275 if rawString, ok := bazelCtx.results[key]; ok {
276 bazelOutput := strings.TrimSpace(rawString)
277 return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
278 }
279 return "", fmt.Errorf("no bazel response found for %v", key)
280}
281
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700282func (bazelCtx *bazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexCqueryInfo, error) {
283 key := cqueryKey{label, cquery.GetApexInfo, cfgKey}
284 if rawString, ok := bazelCtx.results[key]; ok {
285 return cquery.GetApexInfo.ParseResult(strings.TrimSpace(rawString)), nil
286 }
287 return cquery.ApexCqueryInfo{}, fmt.Errorf("no bazel response found for %v", key)
288}
289
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700290func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500291 panic("unimplemented")
292}
293
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700294func (n noopBazelContext) GetOutputFiles(_ string, _ configKey) ([]string, error) {
Chris Parsons808d84c2021-03-09 20:43:32 -0500295 panic("unimplemented")
296}
297
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700298func (n noopBazelContext) GetCcInfo(_ string, _ configKey) (cquery.CcInfo, error) {
Chris Parsonsf874e462022-05-10 13:50:12 -0400299 panic("unimplemented")
300}
301
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700302func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000303 panic("unimplemented")
304}
305
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700306func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexCqueryInfo, error) {
307 panic("unimplemented")
308}
309
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700310func (n noopBazelContext) InvokeBazel(_ Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400311 panic("unimplemented")
312}
313
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500314func (m noopBazelContext) OutputBase() string {
315 return ""
316}
317
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400318func (n noopBazelContext) BazelEnabled() bool {
319 return false
320}
321
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500322func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
323 return []bazel.BuildStatement{}
324}
325
Chris Parsons1a7aca02022-04-25 22:35:15 -0400326func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
327 return []bazel.AqueryDepset{}
328}
329
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400330func NewBazelContext(c *config) (BazelContext, error) {
Chris Parsons8b77a002020-10-27 18:59:25 -0400331 // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
332 // are production ready.
Jingwen Chen442b1a42021-06-17 07:02:15 +0000333 if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400334 return noopBazelContext{}, nil
335 }
336
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400337 p, err := bazelPathsFromConfig(c)
338 if err != nil {
339 return nil, err
340 }
341 return &bazelContext{
342 bazelRunner: &builtinBazelRunner{},
343 paths: p,
344 requests: make(map[cqueryKey]bool),
345 }, nil
346}
347
348func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
349 p := bazelPaths{
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200350 soongOutDir: c.soongOutDir,
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400351 }
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700352 var missingEnvVars []string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400353 if len(c.Getenv("BAZEL_HOME")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400354 p.homeDir = c.Getenv("BAZEL_HOME")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400355 } else {
356 missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
357 }
358 if len(c.Getenv("BAZEL_PATH")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400359 p.bazelPath = c.Getenv("BAZEL_PATH")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400360 } else {
361 missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
362 }
363 if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400364 p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400365 } else {
366 missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
367 }
368 if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400369 p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400370 } else {
371 missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
372 }
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000373 if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400374 p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000375 } else {
376 missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
377 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400378 if len(missingEnvVars) > 0 {
379 return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
380 } else {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400381 return &p, nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400382 }
383}
384
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400385func (p *bazelPaths) BazelMetricsDir() string {
386 return p.metricsDir
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000387}
388
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400389func (context *bazelContext) BazelEnabled() bool {
390 return true
391}
392
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400393func pwdPrefix() string {
394 // Darwin doesn't have /proc
395 if runtime.GOOS != "darwin" {
396 return "PWD=/proc/self/cwd"
397 }
398 return ""
399}
400
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400401type bazelCommand struct {
402 command string
403 // query or label
404 expression string
405}
406
407type mockBazelRunner struct {
408 bazelCommandResults map[bazelCommand]string
409 commands []bazelCommand
Yu Liu8d82ac52022-05-17 15:13:28 -0700410 extraFlags []string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400411}
412
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700413func (r *mockBazelRunner) issueBazelCommand(_ *bazelPaths, _ bazel.RunName,
414 command bazelCommand, extraFlags ...string) (string, string, error) {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400415 r.commands = append(r.commands, command)
Yu Liu8d82ac52022-05-17 15:13:28 -0700416 r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400417 if ret, ok := r.bazelCommandResults[command]; ok {
418 return ret, "", nil
419 }
420 return "", "", nil
421}
422
423type builtinBazelRunner struct{}
424
Chris Parsons808d84c2021-03-09 20:43:32 -0500425// Issues the given bazel command with given build label and additional flags.
426// Returns (stdout, stderr, error). The first and second return values are strings
427// containing the stdout and stderr of the run command, and an error is returned if
428// the invocation returned an error code.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400429func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
Chris Parsons808d84c2021-03-09 20:43:32 -0500430 extraFlags ...string) (string, string, error) {
Romain Jobredeaux41fd5e42021-08-27 15:59:39 +0000431 cmdFlags := []string{
Romain Jobredeaux41fd5e42021-08-27 15:59:39 +0000432 "--output_base=" + absolutePath(paths.outputBase),
433 command.command,
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700434 command.expression,
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700435 // TODO(asmundak): is it needed in every build?
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700436 "--profile=" + shared.BazelMetricsFilename(paths, runName),
Jingwen Chen91220d72021-03-24 02:18:33 -0400437
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700438 // Set default platforms to canonicalized values for mixed builds requests.
439 // If these are set in the bazelrc, they will have values that are
440 // non-canonicalized to @sourceroot labels, and thus be invalid when
441 // referenced from the buildroot.
442 //
443 // The actual platform values here may be overridden by configuration
444 // transitions from the buildroot.
445 fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"),
446 fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"),
Jingwen Chen91220d72021-03-24 02:18:33 -0400447
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700448 // This should be parameterized on the host OS, but let's restrict to linux
449 // to keep things simple for now.
450 fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"),
451
452 // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
453 "--experimental_repository_disable_download",
454
455 // Suppress noise
456 "--ui_event_filters=-INFO",
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700457 "--noshow_progress"}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400458 cmdFlags = append(cmdFlags, extraFlags...)
459
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400460 bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200461 bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700462 extraEnv := []string{
463 "HOME=" + paths.homeDir,
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200464 pwdPrefix(),
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700465 "BUILD_DIR=" + absolutePath(paths.soongOutDir),
Jingwen Chen8c523582021-06-01 11:19:53 +0000466 // Make OUT_DIR absolute here so tools/bazel.sh uses the correct
467 // OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700468 "OUT_DIR=" + absolutePath(paths.outDir()),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500469 // Disables local host detection of gcc; toolchain information is defined
470 // explicitly in BUILD files.
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700471 "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
472 }
473 bazelCmd.Env = append(os.Environ(), extraEnv...)
Colin Crossff0278b2020-10-09 19:24:15 -0700474 stderr := &bytes.Buffer{}
475 bazelCmd.Stderr = stderr
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400476
477 if output, err := bazelCmd.Output(); err != nil {
Chris Parsons808d84c2021-03-09 20:43:32 -0500478 return "", string(stderr.Bytes()),
479 fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400480 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500481 return string(output), string(stderr.Bytes()), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400482 }
483}
484
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400485func (context *bazelContext) mainBzlFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500486 // TODO(cparsons): Define configuration transitions programmatically based
487 // on available archs.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400488 contents := `
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500489#####################################################
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400490# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500491#####################################################
492
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400493def _config_node_transition_impl(settings, attr):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500494 return {
Chris Parsons787fb362021-10-14 18:43:51 -0400495 "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500496 }
497
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400498_config_node_transition = transition(
499 implementation = _config_node_transition_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500500 inputs = [],
501 outputs = [
502 "//command_line_option:platforms",
503 ],
504)
505
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400506def _passthrough_rule_impl(ctx):
507 return [DefaultInfo(files = depset(ctx.files.deps))]
508
509config_node = rule(
510 implementation = _passthrough_rule_impl,
511 attrs = {
512 "arch" : attr.string(mandatory = True),
Chris Parsons787fb362021-10-14 18:43:51 -0400513 "os" : attr.string(mandatory = True),
514 "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400515 "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
516 },
Chris Parsons8d6e4332021-02-22 16:13:50 -0500517)
518
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400519
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500520# Rule representing the root of the build, to depend on all Bazel targets that
521# are required for the build. Building this target will build the entire Bazel
522# build tree.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400523mixed_build_root = rule(
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400524 implementation = _passthrough_rule_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500525 attrs = {
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400526 "deps" : attr.label_list(),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500527 },
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400528)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500529
530def _phony_root_impl(ctx):
531 return []
532
533# Rule to depend on other targets but build nothing.
534# This is useful as follows: building a target of this rule will generate
535# symlink forests for all dependencies of the target, without executing any
536# actions of the build.
537phony_root = rule(
538 implementation = _phony_root_impl,
539 attrs = {"deps" : attr.label_list()},
540)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400541`
542 return []byte(contents)
543}
544
545func (context *bazelContext) mainBuildFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500546 // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
547 // architecture mapping.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400548 formatString := `
549# This file is generated by soong_build. Do not edit.
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400550load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
551
552%s
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400553
554mixed_build_root(name = "buildroot",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400555 deps = [%s],
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400556)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500557
558phony_root(name = "phonyroot",
559 deps = [":buildroot"],
560)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400561`
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400562 configNodeFormatString := `
563config_node(name = "%s",
564 arch = "%s",
Chris Parsons787fb362021-10-14 18:43:51 -0400565 os = "%s",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400566 deps = [%s],
567)
568`
569
570 configNodesSection := ""
571
Chris Parsons787fb362021-10-14 18:43:51 -0400572 labelsByConfig := map[string][]string{}
Usta Shrestha2bc1cd92022-06-23 13:45:24 -0400573 for val := range context.requests {
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200574 labelString := fmt.Sprintf("\"@%s\"", val.label)
Chris Parsons787fb362021-10-14 18:43:51 -0400575 configString := getConfigString(val)
576 labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400577 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400578
Jingwen Chen1e347862021-09-02 12:11:49 +0000579 allLabels := []string{}
Chris Parsons787fb362021-10-14 18:43:51 -0400580 for configString, labels := range labelsByConfig {
581 configTokens := strings.Split(configString, "|")
582 if len(configTokens) != 2 {
583 panic(fmt.Errorf("Unexpected config string format: %s", configString))
Jingwen Chen1e347862021-09-02 12:11:49 +0000584 }
Chris Parsons787fb362021-10-14 18:43:51 -0400585 archString := configTokens[0]
586 osString := configTokens[1]
587 targetString := fmt.Sprintf("%s_%s", osString, archString)
588 allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
589 labelsString := strings.Join(labels, ",\n ")
590 configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400591 }
592
Jingwen Chen1e347862021-09-02 12:11:49 +0000593 return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n ")))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400594}
595
Chris Parsons944e7d02021-03-11 11:08:46 -0500596func indent(original string) string {
597 result := ""
598 for _, line := range strings.Split(original, "\n") {
599 result += " " + line + "\n"
600 }
601 return result
602}
603
Chris Parsons808d84c2021-03-09 20:43:32 -0500604// Returns the file contents of the buildroot.cquery file that should be used for the cquery
605// expression in order to obtain information about buildroot and its dependencies.
606// The contents of this file depend on the bazelContext's requests; requests are enumerated
607// and grouped by their request type. The data retrieved for each label depends on its
608// request type.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400609func (context *bazelContext) cqueryStarlarkFileContents() []byte {
Liz Kammerf29df7c2021-04-02 13:37:39 -0400610 requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
Usta Shrestha2bc1cd92022-06-23 13:45:24 -0400611 for val := range context.requests {
Chris Parsons944e7d02021-03-11 11:08:46 -0500612 cqueryId := getCqueryId(val)
613 mapEntryString := fmt.Sprintf("%q : True", cqueryId)
614 requestTypeToCqueryIdEntries[val.requestType] =
615 append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
616 }
617 labelRegistrationMapSection := ""
618 functionDefSection := ""
619 mainSwitchSection := ""
620
621 mapDeclarationFormatString := `
622%s = {
623 %s
624}
625`
626 functionDefFormatString := `
627def %s(target):
628%s
629`
630 mainSwitchSectionFormatString := `
631 if id_string in %s:
632 return id_string + ">>" + %s(target)
633`
634
Usta Shrestha0b52d832022-02-04 21:37:39 -0500635 for requestType := range requestTypeToCqueryIdEntries {
Chris Parsons944e7d02021-03-11 11:08:46 -0500636 labelMapName := requestType.Name() + "_Labels"
637 functionName := requestType.Name() + "_Fn"
638 labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
639 labelMapName,
640 strings.Join(requestTypeToCqueryIdEntries[requestType], ",\n "))
641 functionDefSection += fmt.Sprintf(functionDefFormatString,
642 functionName,
643 indent(requestType.StarlarkFunctionBody()))
644 mainSwitchSection += fmt.Sprintf(mainSwitchSectionFormatString,
645 labelMapName, functionName)
646 }
647
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400648 formatString := `
649# This file is generated by soong_build. Do not edit.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400650
Chris Parsons944e7d02021-03-11 11:08:46 -0500651# Label Map Section
652%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500653
Chris Parsons944e7d02021-03-11 11:08:46 -0500654# Function Def Section
655%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500656
657def get_arch(target):
Chris Parsons787fb362021-10-14 18:43:51 -0400658 # TODO(b/199363072): filegroups and file targets aren't associated with any
659 # specific platform architecture in mixed builds. This is consistent with how
660 # Soong treats filegroups, but it may not be the case with manually-written
661 # filegroup BUILD targets.
Chris Parsons8d6e4332021-02-22 16:13:50 -0500662 buildoptions = build_options(target)
Jingwen Chen8f222742021-10-07 12:02:23 +0000663 if buildoptions == None:
664 # File targets do not have buildoptions. File targets aren't associated with
Chris Parsons787fb362021-10-14 18:43:51 -0400665 # any specific platform architecture in mixed builds, so use the host.
666 return "x86_64|linux"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500667 platforms = build_options(target)["//command_line_option:platforms"]
668 if len(platforms) != 1:
669 # An individual configured target should have only one platform architecture.
670 # Note that it's fine for there to be multiple architectures for the same label,
671 # but each is its own configured target.
672 fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
673 platform_name = build_options(target)["//command_line_option:platforms"][0].name
674 if platform_name == "host":
675 return "HOST"
Chris Parsons94a0bba2021-06-04 15:03:47 -0400676 elif platform_name.startswith("android_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400677 return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400678 elif platform_name.startswith("linux_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400679 return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400680 else:
681 fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500682 return "UNKNOWN"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500683
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700684def json_for_file(key, file):
685 return '"' + key + '":"' + file.path + '"'
686
687def json_for_files(key, files):
688 return '"' + key + '":[' + ",".join(['"' + f.path + '"' for f in files]) + ']'
689
690def json_for_labels(key, ll):
691 return '"' + key + '":[' + ",".join(['"' + str(x) + '"' for x in ll]) + ']'
692
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400693def format(target):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500694 id_string = str(target.label) + "|" + get_arch(target)
Chris Parsons944e7d02021-03-11 11:08:46 -0500695
696 # Main switch section
697 %s
698 # This target was not requested via cquery, and thus must be a dependency
699 # of a requested target.
700 return id_string + ">>NONE"
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400701`
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400702
Chris Parsons944e7d02021-03-11 11:08:46 -0500703 return []byte(fmt.Sprintf(formatString, labelRegistrationMapSection, functionDefSection,
704 mainSwitchSection))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400705}
706
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200707// Returns a path containing build-related metadata required for interfacing
708// with Bazel. Example: out/soong/bazel.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400709func (p *bazelPaths) intermediatesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200710 return filepath.Join(p.soongOutDir, "bazel")
Chris Parsons8ccdb632020-11-17 15:41:01 -0500711}
712
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200713// Returns the path where the contents of the @soong_injection repository live.
714// It is used by Soong to tell Bazel things it cannot over the command line.
715func (p *bazelPaths) injectedFilesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200716 return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200717}
718
719// Returns the path of the synthetic Bazel workspace that contains a symlink
720// forest composed the whole source tree and BUILD files generated by bp2build.
721func (p *bazelPaths) syntheticWorkspaceDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200722 return filepath.Join(p.soongOutDir, "workspace")
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200723}
724
Jingwen Chen8c523582021-06-01 11:19:53 +0000725// Returns the path to the top level out dir ($OUT_DIR).
726func (p *bazelPaths) outDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200727 return filepath.Dir(p.soongOutDir)
Jingwen Chen8c523582021-06-01 11:19:53 +0000728}
729
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400730// Issues commands to Bazel to receive results for all cquery requests
731// queued in the BazelContext.
Yu Liu8d82ac52022-05-17 15:13:28 -0700732func (context *bazelContext) InvokeBazel(config Config) error {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400733 context.results = make(map[cqueryKey]string)
734
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400735 var err error
Chris Parsons8ccdb632020-11-17 15:41:01 -0500736
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200737 soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200738 mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
739 if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
740 err = os.MkdirAll(mixedBuildsPath, 0777)
Chris Parsons07c1e4a2021-01-19 17:19:16 -0500741 }
Chris Parsons8ccdb632020-11-17 15:41:01 -0500742 if err != nil {
743 return err
744 }
Usta Shrestha902fd172022-03-02 15:27:49 -0500745 if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
746 err = os.MkdirAll(metricsDir, 0777)
747 if err != nil {
748 return err
749 }
750 }
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700751 if err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666); err != nil {
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200752 return err
753 }
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700754 if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "main.bzl"), context.mainBzlFileContents(), 0666); err != nil {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400755 return err
756 }
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700757 if err = ioutil.WriteFile(filepath.Join(mixedBuildsPath, "BUILD.bazel"), context.mainBuildFileContents(), 0666); err != nil {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400758 return err
759 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200760 cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700761 if err = ioutil.WriteFile(absolutePath(cqueryFileRelpath), context.cqueryStarlarkFileContents(), 0666); err != nil {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400762 return err
763 }
Jingwen Chen1e347862021-09-02 12:11:49 +0000764
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700765 const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
766 cqueryCmd := bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
767 cqueryOutput, cqueryErr, err := context.issueBazelCommand(context.paths, bazel.CqueryBuildRootRunName, cqueryCmd,
768 "--output=starlark", "--starlark:file="+absolutePath(cqueryFileRelpath))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500769 if err != nil {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700770 _ = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryOutput), 0666)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500771 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400772 if err != nil {
773 return err
774 }
775
776 cqueryResults := map[string]string{}
777 for _, outputLine := range strings.Split(cqueryOutput, "\n") {
778 if strings.Contains(outputLine, ">>") {
779 splitLine := strings.SplitN(outputLine, ">>", 2)
780 cqueryResults[splitLine[0]] = splitLine[1]
781 }
782 }
Usta Shrestha902fd172022-03-02 15:27:49 -0500783 for val := range context.requests {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500784 if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
Usta Shrestha902fd172022-03-02 15:27:49 -0500785 context.results[val] = cqueryResult
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400786 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500787 return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
788 getCqueryId(val), cqueryOutput, cqueryErr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400789 }
790 }
791
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500792 // Issue an aquery command to retrieve action information about the bazel build tree.
793 //
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700794 // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
795 // proto sources, which would add a number of unnecessary dependencies.
796 extraFlags := []string{"--output=jsonproto", "--include_file_write_contents"}
Yu Liu8d82ac52022-05-17 15:13:28 -0700797 if Bool(config.productVariables.ClangCoverage) {
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700798 extraFlags = append(extraFlags, "--collect_code_coverage")
799 paths := make([]string, 0, 2)
800 if p := config.productVariables.NativeCoveragePaths; len(p) > 0 {
801 paths = append(paths, JoinWithPrefixAndSeparator(p, "+", ","))
802 }
803 if p := config.productVariables.NativeCoverageExcludePaths; len(p) > 0 {
804 paths = append(paths, JoinWithPrefixAndSeparator(p, "-", ","))
805 }
806 if len(paths) > 0 {
807 extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
Yu Liu8d82ac52022-05-17 15:13:28 -0700808 }
809 }
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700810 aqueryCmd := bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
811 if aqueryOutput, _, err := context.issueBazelCommand(context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
812 extraFlags...); err == nil {
813 context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400814 }
Chris Parsons4f069892021-01-15 12:22:41 -0500815 if err != nil {
816 return err
817 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500818
819 // Issue a build command of the phony root to generate symlink forests for dependencies of the
820 // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
821 // but some of symlinks may be required to resolve source dependencies of the build.
Sasha Smundakb43ae1e2022-07-03 15:57:36 -0700822 buildCmd := bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
823 if _, _, err = context.issueBazelCommand(context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd); err != nil {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500824 return err
825 }
826
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400827 // Clear requests.
828 context.requests = map[cqueryKey]bool{}
829 return nil
830}
Chris Parsonsa798d962020-10-12 23:44:08 -0400831
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500832func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
833 return context.buildStatements
834}
835
Chris Parsons1a7aca02022-04-25 22:35:15 -0400836func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
837 return context.depsets
838}
839
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500840func (context *bazelContext) OutputBase() string {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400841 return context.paths.outputBase
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500842}
843
Chris Parsonsa798d962020-10-12 23:44:08 -0400844// Singleton used for registering BUILD file ninja dependencies (needed
845// for correctness of builds which use Bazel.
846func BazelSingleton() Singleton {
847 return &bazelSingleton{}
848}
849
850type bazelSingleton struct{}
851
852func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500853 // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
854 if !ctx.Config().BazelContext.BazelEnabled() {
855 return
856 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400857
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500858 // Add ninja file dependencies for files which all bazel invocations require.
859 bazelBuildList := absolutePath(filepath.Join(
Lukacs T. Berkif9008072021-08-16 15:24:48 +0200860 filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500861 ctx.AddNinjaFileDeps(bazelBuildList)
862
863 data, err := ioutil.ReadFile(bazelBuildList)
864 if err != nil {
865 ctx.Errorf(err.Error())
866 }
867 files := strings.Split(strings.TrimSpace(string(data)), "\n")
868 for _, file := range files {
869 ctx.AddNinjaFileDeps(file)
870 }
871
Chris Parsons1a7aca02022-04-25 22:35:15 -0400872 for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
873 var outputs []Path
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400874 for _, depsetDepHash := range depset.TransitiveDepSetHashes {
875 otherDepsetName := bazelDepsetName(depsetDepHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400876 outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
877 }
878 for _, artifactPath := range depset.DirectArtifacts {
879 outputs = append(outputs, PathForBazelOut(ctx, artifactPath))
880 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400881 thisDepsetName := bazelDepsetName(depset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400882 ctx.Build(pctx, BuildParams{
883 Rule: blueprint.Phony,
884 Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)},
885 Implicits: outputs,
886 })
887 }
888
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400889 executionRoot := path.Join(ctx.Config().BazelContext.OutputBase(), "execroot", "__main__")
890 bazelOutDir := path.Join(executionRoot, "bazel-out")
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500891 for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
Sasha Smundak1da064c2022-06-08 16:36:16 -0700892 if len(buildStatement.Command) > 0 {
893 rule := NewRuleBuilder(pctx, ctx)
894 createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
895 desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
896 rule.Build(fmt.Sprintf("bazel %d", index), desc)
897 continue
898 }
899 // Certain actions returned by aquery (for instance FileWrite) do not contain a command
900 // and thus require special treatment. If BuildStatement were an interface implementing
901 // buildRule(ctx) function, the code here would just call it.
902 // Unfortunately, the BuildStatement is defined in
903 // the 'bazel' package, which cannot depend on 'android' package where ctx is defined,
904 // because this would cause circular dependency. So, until we move aquery processing
905 // to the 'android' package, we need to handle special cases here.
906 if buildStatement.Mnemonic == "FileWrite" || buildStatement.Mnemonic == "SourceSymlinkManifest" {
907 // Pass file contents as the value of the rule's "content" argument.
908 // Escape newlines and $ in the contents (the action "writeBazelFile" restores "\\n"
909 // back to the newline, and Ninja reads $$ as $.
910 escaped := strings.ReplaceAll(strings.ReplaceAll(buildStatement.FileContents, "\n", "\\n"),
911 "$", "$$")
912 ctx.Build(pctx, BuildParams{
913 Rule: writeBazelFile,
914 Output: PathForBazelOut(ctx, buildStatement.OutputPaths[0]),
915 Description: fmt.Sprintf("%s %s", buildStatement.Mnemonic, buildStatement.OutputPaths[0]),
916 Args: map[string]string{
917 "content": escaped,
918 },
919 })
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700920 } else if buildStatement.Mnemonic == "SymlinkTree" {
921 // build-runfiles arguments are the manifest file and the target directory
922 // where it creates the symlink tree according to this manifest (and then
923 // writes the MANIFEST file to it).
924 outManifest := PathForBazelOut(ctx, buildStatement.OutputPaths[0])
925 outManifestPath := outManifest.String()
926 if !strings.HasSuffix(outManifestPath, "MANIFEST") {
927 panic("the base name of the symlink tree action should be MANIFEST, got " + outManifestPath)
928 }
929 outDir := filepath.Dir(outManifestPath)
930 ctx.Build(pctx, BuildParams{
931 Rule: buildRunfilesRule,
932 Output: outManifest,
933 Inputs: []Path{PathForBazelOut(ctx, buildStatement.InputPaths[0])},
934 Description: "symlink tree for " + outDir,
935 Args: map[string]string{
936 "outDir": outDir,
937 },
938 })
Sasha Smundak1da064c2022-06-08 16:36:16 -0700939 } else {
Rupert Shuttlewortha29903f2021-04-06 16:17:33 +0000940 panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500941 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400942 }
943}
Chris Parsons8d6e4332021-02-22 16:13:50 -0500944
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400945// Register bazel-owned build statements (obtained from the aquery invocation).
946func createCommand(cmd *RuleBuilderCommand, buildStatement bazel.BuildStatement, executionRoot string, bazelOutDir string, ctx PathContext) {
947 // executionRoot is the action cwd.
948 cmd.Text(fmt.Sprintf("cd '%s' &&", executionRoot))
949
950 // Remove old outputs, as some actions might not rerun if the outputs are detected.
951 if len(buildStatement.OutputPaths) > 0 {
952 cmd.Text("rm -f")
953 for _, outputPath := range buildStatement.OutputPaths {
Usta Shresthaef922252022-06-02 14:23:02 -0400954 cmd.Text(fmt.Sprintf("'%s'", outputPath))
Usta Shresthaacd5a0c2022-06-22 11:20:50 -0400955 }
956 cmd.Text("&&")
957 }
958
959 for _, pair := range buildStatement.Env {
960 // Set per-action env variables, if any.
961 cmd.Flag(pair.Key + "=" + pair.Value)
962 }
963
964 // The actual Bazel action.
965 cmd.Text(buildStatement.Command)
966
967 for _, outputPath := range buildStatement.OutputPaths {
968 cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
969 }
970 for _, inputPath := range buildStatement.InputPaths {
971 cmd.Implicit(PathForBazelOut(ctx, inputPath))
972 }
973 for _, inputDepsetHash := range buildStatement.InputDepsetHashes {
974 otherDepsetName := bazelDepsetName(inputDepsetHash)
975 cmd.Implicit(PathForPhony(ctx, otherDepsetName))
976 }
977
978 if depfile := buildStatement.Depfile; depfile != nil {
979 // The paths in depfile are relative to `executionRoot`.
980 // Hence, they need to be corrected by replacing "bazel-out"
981 // with the full `bazelOutDir`.
982 // Otherwise, implicit outputs and implicit inputs under "bazel-out/"
983 // would be deemed missing.
984 // (Note: The regexp uses a capture group because the version of sed
985 // does not support a look-behind pattern.)
986 replacement := fmt.Sprintf(`&& sed -i'' -E 's@(^|\s|")bazel-out/@\1%s/@g' '%s'`,
987 bazelOutDir, *depfile)
988 cmd.Text(replacement)
989 cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
990 }
991
992 for _, symlinkPath := range buildStatement.SymlinkPaths {
993 cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
994 }
995}
996
Chris Parsons8d6e4332021-02-22 16:13:50 -0500997func getCqueryId(key cqueryKey) string {
Chris Parsons787fb362021-10-14 18:43:51 -0400998 return key.label + "|" + getConfigString(key)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500999}
1000
Chris Parsons787fb362021-10-14 18:43:51 -04001001func getConfigString(key cqueryKey) string {
Liz Kammer0940b892022-03-18 15:55:04 -04001002 arch := key.configKey.arch
Chris Parsons787fb362021-10-14 18:43:51 -04001003 if len(arch) == 0 || arch == "common" {
Sasha Smundak9d46dcf2022-06-08 12:10:36 -07001004 if key.configKey.osType.Class == Device {
1005 // For the generic Android, the expected result is "target|android", which
1006 // corresponds to the product_variable_config named "android_target" in
1007 // build/bazel/platforms/BUILD.bazel.
1008 arch = "target"
1009 } else {
1010 // Use host platform, which is currently hardcoded to be x86_64.
1011 arch = "x86_64"
1012 }
Chris Parsons8d6e4332021-02-22 16:13:50 -05001013 }
Usta Shrestha16ac1352022-06-22 11:01:55 -04001014 osName := key.configKey.osType.Name
1015 if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" {
Chris Parsons787fb362021-10-14 18:43:51 -04001016 // Use host OS, which is currently hardcoded to be linux.
Usta Shrestha16ac1352022-06-22 11:01:55 -04001017 osName = "linux"
Chris Parsons787fb362021-10-14 18:43:51 -04001018 }
Usta Shrestha16ac1352022-06-22 11:01:55 -04001019 return arch + "|" + osName
Chris Parsons787fb362021-10-14 18:43:51 -04001020}
1021
Chris Parsonsf874e462022-05-10 13:50:12 -04001022func GetConfigKey(ctx BaseModuleContext) configKey {
Liz Kammer0940b892022-03-18 15:55:04 -04001023 return configKey{
1024 // use string because Arch is not a valid key in go
1025 arch: ctx.Arch().String(),
1026 osType: ctx.Os(),
1027 }
Chris Parsons8d6e4332021-02-22 16:13:50 -05001028}
Chris Parsons1a7aca02022-04-25 22:35:15 -04001029
Chris Parsons0bfb1c02022-05-12 16:43:01 -04001030func bazelDepsetName(contentHash string) string {
1031 return fmt.Sprintf("bazel_depset_%s", contentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -04001032}