blob: 38f9319209fb3eb113c9c3259cdd1dfa45eb8dfa [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"
Chris Parsonsa798d962020-10-12 23:44:08 -040024 "path/filepath"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040025 "runtime"
26 "strings"
27 "sync"
Chris Parsonsa798d962020-10-12 23:44:08 -040028
Chris Parsons944e7d02021-03-11 11:08:46 -050029 "android/soong/bazel/cquery"
Jingwen Chen1e347862021-09-02 12:11:49 +000030 "android/soong/shared"
Chris Parsons1a7aca02022-04-25 22:35:15 -040031 "github.com/google/blueprint"
Liz Kammer8206d4f2021-03-03 16:40:52 -050032
Patrice Arruda05ab2d02020-12-12 06:24:26 +000033 "android/soong/bazel"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040034)
35
Liz Kammerf29df7c2021-04-02 13:37:39 -040036type cqueryRequest interface {
37 // Name returns a string name for this request type. Such request type names must be unique,
38 // and must only consist of alphanumeric characters.
39 Name() string
40
41 // StarlarkFunctionBody returns a starlark function body to process this request type.
42 // The returned string is the body of a Starlark function which obtains
43 // all request-relevant information about a target and returns a string containing
44 // this information.
45 // The function should have the following properties:
46 // - `target` is the only parameter to this function (a configured target).
47 // - The return value must be a string.
48 // - The function body should not be indented outside of its own scope.
49 StarlarkFunctionBody() string
50}
51
Chris Parsons787fb362021-10-14 18:43:51 -040052// Portion of cquery map key to describe target configuration.
53type configKey struct {
Liz Kammer0940b892022-03-18 15:55:04 -040054 arch string
55 osType OsType
Chris Parsons787fb362021-10-14 18:43:51 -040056}
57
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040058// Map key to describe bazel cquery requests.
59type cqueryKey struct {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040060 label string
Liz Kammerf29df7c2021-04-02 13:37:39 -040061 requestType cqueryRequest
Chris Parsons787fb362021-10-14 18:43:51 -040062 configKey configKey
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040063}
64
Alex Márquez Pérez Muñíz Díaz Púras Thaureaux0d990452021-08-11 16:46:13 +000065// bazelHandler is the interface for a helper object related to deferring to Bazel for
66// processing a module (during Bazel mixed builds). Individual module types should define
67// their own bazel handler if they support deferring to Bazel.
68type BazelHandler interface {
69 // Issue query to Bazel to retrieve information about Bazel's view of the current module.
70 // If Bazel returns this information, set module properties on the current module to reflect
71 // the returned information.
72 // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
73 GenerateBazelBuildActions(ctx ModuleContext, label string) bool
74}
75
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040076type BazelContext interface {
Usta Shrestha0b52d832022-02-04 21:37:39 -050077 // The methods below involve queuing cquery requests to be later invoked
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040078 // by bazel. If any of these methods return (_, false), then the request
79 // has been queued to be run later.
80
81 // Returns result files built by building the given bazel target label.
Chris Parsons787fb362021-10-14 18:43:51 -040082 GetOutputFiles(label string, cfgKey configKey) ([]string, bool)
Chris Parsons8d6e4332021-02-22 16:13:50 -050083
Chris Parsons944e7d02021-03-11 11:08:46 -050084 // TODO(cparsons): Other cquery-related methods should be added here.
85 // Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
Chris Parsons787fb362021-10-14 18:43:51 -040086 GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error)
Liz Kammer3f9e1552021-04-02 18:47:09 -040087
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +000088 // Returns the executable binary resultant from building together the python sources
Chris Parsons787fb362021-10-14 18:43:51 -040089 GetPythonBinary(label string, cfgKey configKey) (string, bool)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +000090
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040091 // ** End cquery methods
92
93 // Issues commands to Bazel to receive results for all cquery requests
94 // queued in the BazelContext.
95 InvokeBazel() error
96
97 // Returns true if bazel is enabled for the given configuration.
98 BazelEnabled() bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050099
100 // Returns the bazel output base (the root directory for all bazel intermediate outputs).
101 OutputBase() string
102
103 // Returns build statements which should get registered to reflect Bazel's outputs.
104 BuildStatementsToRegister() []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400105
106 // Returns the depsets defined in Bazel's aquery response.
107 AqueryDepsets() []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400108}
109
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400110type bazelRunner interface {
111 issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) (string, string, error)
112}
113
114type bazelPaths struct {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400115 homeDir string
116 bazelPath string
117 outputBase string
118 workspaceDir string
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200119 soongOutDir string
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000120 metricsDir string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400121}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400122
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400123// A context object which tracks queued requests that need to be made to Bazel,
124// and their results after the requests have been made.
125type bazelContext struct {
126 bazelRunner
127 paths *bazelPaths
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400128 requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
129 requestMutex sync.Mutex // requests can be written in parallel
130
131 results map[cqueryKey]string // Results of cquery requests after Bazel invocations
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500132
133 // Build statements which should get registered to reflect Bazel's outputs.
134 buildStatements []bazel.BuildStatement
Chris Parsons1a7aca02022-04-25 22:35:15 -0400135
136 // Depsets which should be used for Bazel's build statements.
137 depsets []bazel.AqueryDepset
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400138}
139
140var _ BazelContext = &bazelContext{}
141
142// A bazel context to use when Bazel is disabled.
143type noopBazelContext struct{}
144
145var _ BazelContext = noopBazelContext{}
146
147// A bazel context to use for tests.
148type MockBazelContext struct {
Liz Kammera92e8442021-04-07 20:25:21 -0400149 OutputBaseDir string
150
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000151 LabelToOutputFiles map[string][]string
152 LabelToCcInfo map[string]cquery.CcInfo
153 LabelToPythonBinary map[string]string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400154}
155
Chris Parsons787fb362021-10-14 18:43:51 -0400156func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
Liz Kammera92e8442021-04-07 20:25:21 -0400157 result, ok := m.LabelToOutputFiles[label]
Chris Parsons8d6e4332021-02-22 16:13:50 -0500158 return result, ok
159}
160
Chris Parsons787fb362021-10-14 18:43:51 -0400161func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
Liz Kammerb71794d2021-04-09 14:07:00 -0400162 result, ok := m.LabelToCcInfo[label]
Liz Kammerfe23bf32021-04-09 16:17:05 -0400163 return result, ok, nil
Liz Kammer3f9e1552021-04-02 18:47:09 -0400164}
165
Chris Parsons787fb362021-10-14 18:43:51 -0400166func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000167 result, ok := m.LabelToPythonBinary[label]
168 return result, ok
169}
170
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400171func (m MockBazelContext) InvokeBazel() error {
172 panic("unimplemented")
173}
174
175func (m MockBazelContext) BazelEnabled() bool {
176 return true
177}
178
Liz Kammera92e8442021-04-07 20:25:21 -0400179func (m MockBazelContext) OutputBase() string { return m.OutputBaseDir }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500180
181func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
182 return []bazel.BuildStatement{}
183}
184
Chris Parsons1a7aca02022-04-25 22:35:15 -0400185func (m MockBazelContext) AqueryDepsets() []bazel.AqueryDepset {
186 return []bazel.AqueryDepset{}
187}
188
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400189var _ BazelContext = MockBazelContext{}
190
Chris Parsons787fb362021-10-14 18:43:51 -0400191func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
192 rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, cfgKey)
Chris Parsons944e7d02021-03-11 11:08:46 -0500193 var ret []string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400194 if ok {
Chris Parsons944e7d02021-03-11 11:08:46 -0500195 bazelOutput := strings.TrimSpace(rawString)
Liz Kammerf29df7c2021-04-02 13:37:39 -0400196 ret = cquery.GetOutputFiles.ParseResult(bazelOutput)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400197 }
Chris Parsons944e7d02021-03-11 11:08:46 -0500198 return ret, ok
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400199}
200
Chris Parsons787fb362021-10-14 18:43:51 -0400201func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
202 result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, cfgKey)
Liz Kammer3f9e1552021-04-02 18:47:09 -0400203 if !ok {
Liz Kammerfe23bf32021-04-09 16:17:05 -0400204 return cquery.CcInfo{}, ok, nil
Liz Kammer3f9e1552021-04-02 18:47:09 -0400205 }
206
207 bazelOutput := strings.TrimSpace(result)
Liz Kammerfe23bf32021-04-09 16:17:05 -0400208 ret, err := cquery.GetCcInfo.ParseResult(bazelOutput)
209 return ret, ok, err
Liz Kammer3f9e1552021-04-02 18:47:09 -0400210}
211
Chris Parsons787fb362021-10-14 18:43:51 -0400212func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
213 rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, cfgKey)
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000214 var ret string
215 if ok {
216 bazelOutput := strings.TrimSpace(rawString)
217 ret = cquery.GetPythonBinary.ParseResult(bazelOutput)
218 }
219 return ret, ok
220}
221
Chris Parsons787fb362021-10-14 18:43:51 -0400222func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500223 panic("unimplemented")
224}
225
Chris Parsons787fb362021-10-14 18:43:51 -0400226func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
Chris Parsons808d84c2021-03-09 20:43:32 -0500227 panic("unimplemented")
228}
229
Chris Parsons787fb362021-10-14 18:43:51 -0400230func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
Alex Márquez Pérez Muñíz Díaz Púras Thaureauxa05a2552021-08-11 16:48:30 +0000231 panic("unimplemented")
232}
233
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400234func (n noopBazelContext) InvokeBazel() error {
235 panic("unimplemented")
236}
237
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500238func (m noopBazelContext) OutputBase() string {
239 return ""
240}
241
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400242func (n noopBazelContext) BazelEnabled() bool {
243 return false
244}
245
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500246func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
247 return []bazel.BuildStatement{}
248}
249
Chris Parsons1a7aca02022-04-25 22:35:15 -0400250func (m noopBazelContext) AqueryDepsets() []bazel.AqueryDepset {
251 return []bazel.AqueryDepset{}
252}
253
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400254func NewBazelContext(c *config) (BazelContext, error) {
Chris Parsons8b77a002020-10-27 18:59:25 -0400255 // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
256 // are production ready.
Jingwen Chen442b1a42021-06-17 07:02:15 +0000257 if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400258 return noopBazelContext{}, nil
259 }
260
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400261 p, err := bazelPathsFromConfig(c)
262 if err != nil {
263 return nil, err
264 }
265 return &bazelContext{
266 bazelRunner: &builtinBazelRunner{},
267 paths: p,
268 requests: make(map[cqueryKey]bool),
269 }, nil
270}
271
272func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
273 p := bazelPaths{
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200274 soongOutDir: c.soongOutDir,
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400275 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400276 missingEnvVars := []string{}
277 if len(c.Getenv("BAZEL_HOME")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400278 p.homeDir = c.Getenv("BAZEL_HOME")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400279 } else {
280 missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
281 }
282 if len(c.Getenv("BAZEL_PATH")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400283 p.bazelPath = c.Getenv("BAZEL_PATH")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400284 } else {
285 missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
286 }
287 if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400288 p.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400289 } else {
290 missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
291 }
292 if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400293 p.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400294 } else {
295 missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
296 }
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000297 if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400298 p.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000299 } else {
300 missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
301 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400302 if len(missingEnvVars) > 0 {
303 return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
304 } else {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400305 return &p, nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400306 }
307}
308
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400309func (p *bazelPaths) BazelMetricsDir() string {
310 return p.metricsDir
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000311}
312
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400313func (context *bazelContext) BazelEnabled() bool {
314 return true
315}
316
317// Adds a cquery request to the Bazel request queue, to be later invoked, or
318// returns the result of the given request if the request was already made.
319// If the given request was already made (and the results are available), then
320// returns (result, true). If the request is queued but no results are available,
321// then returns ("", false).
Liz Kammerf29df7c2021-04-02 13:37:39 -0400322func (context *bazelContext) cquery(label string, requestType cqueryRequest,
Chris Parsons787fb362021-10-14 18:43:51 -0400323 cfgKey configKey) (string, bool) {
324 key := cqueryKey{label, requestType, cfgKey}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400325 if result, ok := context.results[key]; ok {
326 return result, true
327 } else {
328 context.requestMutex.Lock()
329 defer context.requestMutex.Unlock()
330 context.requests[key] = true
331 return "", false
332 }
333}
334
335func pwdPrefix() string {
336 // Darwin doesn't have /proc
337 if runtime.GOOS != "darwin" {
338 return "PWD=/proc/self/cwd"
339 }
340 return ""
341}
342
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400343type bazelCommand struct {
344 command string
345 // query or label
346 expression string
347}
348
349type mockBazelRunner struct {
350 bazelCommandResults map[bazelCommand]string
351 commands []bazelCommand
352}
353
354func (r *mockBazelRunner) issueBazelCommand(paths *bazelPaths,
355 runName bazel.RunName,
356 command bazelCommand,
357 extraFlags ...string) (string, string, error) {
358 r.commands = append(r.commands, command)
359 if ret, ok := r.bazelCommandResults[command]; ok {
360 return ret, "", nil
361 }
362 return "", "", nil
363}
364
365type builtinBazelRunner struct{}
366
Chris Parsons808d84c2021-03-09 20:43:32 -0500367// Issues the given bazel command with given build label and additional flags.
368// Returns (stdout, stderr, error). The first and second return values are strings
369// containing the stdout and stderr of the run command, and an error is returned if
370// the invocation returned an error code.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400371func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
Chris Parsons808d84c2021-03-09 20:43:32 -0500372 extraFlags ...string) (string, string, error) {
Romain Jobredeaux41fd5e42021-08-27 15:59:39 +0000373 cmdFlags := []string{
374 // --noautodetect_server_javabase has the practical consequence of preventing Bazel from
375 // attempting to download rules_java, which is incompatible with
376 // --experimental_repository_disable_download set further below.
377 // rules_java is also not needed until mixed builds start building java targets.
378 // TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
379 "--noautodetect_server_javabase",
380 "--output_base=" + absolutePath(paths.outputBase),
381 command.command,
382 }
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400383 cmdFlags = append(cmdFlags, command.expression)
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400384 cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
Jingwen Chen91220d72021-03-24 02:18:33 -0400385
386 // Set default platforms to canonicalized values for mixed builds requests.
387 // If these are set in the bazelrc, they will have values that are
388 // non-canonicalized to @sourceroot labels, and thus be invalid when
389 // referenced from the buildroot.
390 //
391 // The actual platform values here may be overridden by configuration
392 // transitions from the buildroot.
Chris Parsonsee423b02021-02-08 23:04:59 -0500393 cmdFlags = append(cmdFlags,
Liz Kammerc0c66092021-07-26 17:38:47 -0400394 fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
Chris Parsonsee423b02021-02-08 23:04:59 -0500395 cmdFlags = append(cmdFlags,
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200396 fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
Jingwen Chen91220d72021-03-24 02:18:33 -0400397 // This should be parameterized on the host OS, but let's restrict to linux
398 // to keep things simple for now.
399 cmdFlags = append(cmdFlags,
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200400 fmt.Sprintf("--host_platform=%s", "//build/bazel/platforms:linux_x86_64"))
Jingwen Chen91220d72021-03-24 02:18:33 -0400401
Chris Parsons8d6e4332021-02-22 16:13:50 -0500402 // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
403 cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400404 cmdFlags = append(cmdFlags, extraFlags...)
405
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400406 bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200407 bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200408 bazelCmd.Env = append(os.Environ(),
409 "HOME="+paths.homeDir,
410 pwdPrefix(),
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200411 "BUILD_DIR="+absolutePath(paths.soongOutDir),
Jingwen Chen8c523582021-06-01 11:19:53 +0000412 // Make OUT_DIR absolute here so tools/bazel.sh uses the correct
413 // OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
414 "OUT_DIR="+absolutePath(paths.outDir()),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500415 // Disables local host detection of gcc; toolchain information is defined
416 // explicitly in BUILD files.
417 "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
Colin Crossff0278b2020-10-09 19:24:15 -0700418 stderr := &bytes.Buffer{}
419 bazelCmd.Stderr = stderr
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400420
421 if output, err := bazelCmd.Output(); err != nil {
Chris Parsons808d84c2021-03-09 20:43:32 -0500422 return "", string(stderr.Bytes()),
423 fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400424 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500425 return string(output), string(stderr.Bytes()), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400426 }
427}
428
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400429func (context *bazelContext) mainBzlFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500430 // TODO(cparsons): Define configuration transitions programmatically based
431 // on available archs.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400432 contents := `
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500433#####################################################
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400434# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500435#####################################################
436
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400437def _config_node_transition_impl(settings, attr):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500438 return {
Chris Parsons787fb362021-10-14 18:43:51 -0400439 "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500440 }
441
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400442_config_node_transition = transition(
443 implementation = _config_node_transition_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500444 inputs = [],
445 outputs = [
446 "//command_line_option:platforms",
447 ],
448)
449
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400450def _passthrough_rule_impl(ctx):
451 return [DefaultInfo(files = depset(ctx.files.deps))]
452
453config_node = rule(
454 implementation = _passthrough_rule_impl,
455 attrs = {
456 "arch" : attr.string(mandatory = True),
Chris Parsons787fb362021-10-14 18:43:51 -0400457 "os" : attr.string(mandatory = True),
458 "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400459 "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
460 },
Chris Parsons8d6e4332021-02-22 16:13:50 -0500461)
462
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400463
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500464# Rule representing the root of the build, to depend on all Bazel targets that
465# are required for the build. Building this target will build the entire Bazel
466# build tree.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400467mixed_build_root = rule(
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400468 implementation = _passthrough_rule_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500469 attrs = {
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400470 "deps" : attr.label_list(),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500471 },
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400472)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500473
474def _phony_root_impl(ctx):
475 return []
476
477# Rule to depend on other targets but build nothing.
478# This is useful as follows: building a target of this rule will generate
479# symlink forests for all dependencies of the target, without executing any
480# actions of the build.
481phony_root = rule(
482 implementation = _phony_root_impl,
483 attrs = {"deps" : attr.label_list()},
484)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400485`
486 return []byte(contents)
487}
488
489func (context *bazelContext) mainBuildFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500490 // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
491 // architecture mapping.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400492 formatString := `
493# This file is generated by soong_build. Do not edit.
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400494load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
495
496%s
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400497
498mixed_build_root(name = "buildroot",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400499 deps = [%s],
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400500)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500501
502phony_root(name = "phonyroot",
503 deps = [":buildroot"],
504)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400505`
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400506 configNodeFormatString := `
507config_node(name = "%s",
508 arch = "%s",
Chris Parsons787fb362021-10-14 18:43:51 -0400509 os = "%s",
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400510 deps = [%s],
511)
512`
513
514 configNodesSection := ""
515
Chris Parsons787fb362021-10-14 18:43:51 -0400516 labelsByConfig := map[string][]string{}
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400517 for val, _ := range context.requests {
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200518 labelString := fmt.Sprintf("\"@%s\"", val.label)
Chris Parsons787fb362021-10-14 18:43:51 -0400519 configString := getConfigString(val)
520 labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400521 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400522
Jingwen Chen1e347862021-09-02 12:11:49 +0000523 allLabels := []string{}
Chris Parsons787fb362021-10-14 18:43:51 -0400524 for configString, labels := range labelsByConfig {
525 configTokens := strings.Split(configString, "|")
526 if len(configTokens) != 2 {
527 panic(fmt.Errorf("Unexpected config string format: %s", configString))
Jingwen Chen1e347862021-09-02 12:11:49 +0000528 }
Chris Parsons787fb362021-10-14 18:43:51 -0400529 archString := configTokens[0]
530 osString := configTokens[1]
531 targetString := fmt.Sprintf("%s_%s", osString, archString)
532 allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
533 labelsString := strings.Join(labels, ",\n ")
534 configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
Chris Parsonsad0b5ba2021-03-29 21:09:24 -0400535 }
536
Jingwen Chen1e347862021-09-02 12:11:49 +0000537 return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n ")))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400538}
539
Chris Parsons944e7d02021-03-11 11:08:46 -0500540func indent(original string) string {
541 result := ""
542 for _, line := range strings.Split(original, "\n") {
543 result += " " + line + "\n"
544 }
545 return result
546}
547
Chris Parsons808d84c2021-03-09 20:43:32 -0500548// Returns the file contents of the buildroot.cquery file that should be used for the cquery
549// expression in order to obtain information about buildroot and its dependencies.
550// The contents of this file depend on the bazelContext's requests; requests are enumerated
551// and grouped by their request type. The data retrieved for each label depends on its
552// request type.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400553func (context *bazelContext) cqueryStarlarkFileContents() []byte {
Liz Kammerf29df7c2021-04-02 13:37:39 -0400554 requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
Chris Parsons944e7d02021-03-11 11:08:46 -0500555 for val, _ := range context.requests {
556 cqueryId := getCqueryId(val)
557 mapEntryString := fmt.Sprintf("%q : True", cqueryId)
558 requestTypeToCqueryIdEntries[val.requestType] =
559 append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
560 }
561 labelRegistrationMapSection := ""
562 functionDefSection := ""
563 mainSwitchSection := ""
564
565 mapDeclarationFormatString := `
566%s = {
567 %s
568}
569`
570 functionDefFormatString := `
571def %s(target):
572%s
573`
574 mainSwitchSectionFormatString := `
575 if id_string in %s:
576 return id_string + ">>" + %s(target)
577`
578
Usta Shrestha0b52d832022-02-04 21:37:39 -0500579 for requestType := range requestTypeToCqueryIdEntries {
Chris Parsons944e7d02021-03-11 11:08:46 -0500580 labelMapName := requestType.Name() + "_Labels"
581 functionName := requestType.Name() + "_Fn"
582 labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
583 labelMapName,
584 strings.Join(requestTypeToCqueryIdEntries[requestType], ",\n "))
585 functionDefSection += fmt.Sprintf(functionDefFormatString,
586 functionName,
587 indent(requestType.StarlarkFunctionBody()))
588 mainSwitchSection += fmt.Sprintf(mainSwitchSectionFormatString,
589 labelMapName, functionName)
590 }
591
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400592 formatString := `
593# This file is generated by soong_build. Do not edit.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400594
Chris Parsons944e7d02021-03-11 11:08:46 -0500595# Label Map Section
596%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500597
Chris Parsons944e7d02021-03-11 11:08:46 -0500598# Function Def Section
599%s
Chris Parsons8d6e4332021-02-22 16:13:50 -0500600
601def get_arch(target):
Chris Parsons787fb362021-10-14 18:43:51 -0400602 # TODO(b/199363072): filegroups and file targets aren't associated with any
603 # specific platform architecture in mixed builds. This is consistent with how
604 # Soong treats filegroups, but it may not be the case with manually-written
605 # filegroup BUILD targets.
Chris Parsons8d6e4332021-02-22 16:13:50 -0500606 buildoptions = build_options(target)
Jingwen Chen8f222742021-10-07 12:02:23 +0000607 if buildoptions == None:
608 # File targets do not have buildoptions. File targets aren't associated with
Chris Parsons787fb362021-10-14 18:43:51 -0400609 # any specific platform architecture in mixed builds, so use the host.
610 return "x86_64|linux"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500611 platforms = build_options(target)["//command_line_option:platforms"]
612 if len(platforms) != 1:
613 # An individual configured target should have only one platform architecture.
614 # Note that it's fine for there to be multiple architectures for the same label,
615 # but each is its own configured target.
616 fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
617 platform_name = build_options(target)["//command_line_option:platforms"][0].name
618 if platform_name == "host":
619 return "HOST"
Chris Parsons94a0bba2021-06-04 15:03:47 -0400620 elif platform_name.startswith("android_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400621 return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400622 elif platform_name.startswith("linux_"):
Chris Parsons787fb362021-10-14 18:43:51 -0400623 return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
Chris Parsons94a0bba2021-06-04 15:03:47 -0400624 else:
625 fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500626 return "UNKNOWN"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500627
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400628def format(target):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500629 id_string = str(target.label) + "|" + get_arch(target)
Chris Parsons944e7d02021-03-11 11:08:46 -0500630
631 # Main switch section
632 %s
633 # This target was not requested via cquery, and thus must be a dependency
634 # of a requested target.
635 return id_string + ">>NONE"
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400636`
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400637
Chris Parsons944e7d02021-03-11 11:08:46 -0500638 return []byte(fmt.Sprintf(formatString, labelRegistrationMapSection, functionDefSection,
639 mainSwitchSection))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400640}
641
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200642// Returns a path containing build-related metadata required for interfacing
643// with Bazel. Example: out/soong/bazel.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400644func (p *bazelPaths) intermediatesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200645 return filepath.Join(p.soongOutDir, "bazel")
Chris Parsons8ccdb632020-11-17 15:41:01 -0500646}
647
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200648// Returns the path where the contents of the @soong_injection repository live.
649// It is used by Soong to tell Bazel things it cannot over the command line.
650func (p *bazelPaths) injectedFilesDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200651 return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName)
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200652}
653
654// Returns the path of the synthetic Bazel workspace that contains a symlink
655// forest composed the whole source tree and BUILD files generated by bp2build.
656func (p *bazelPaths) syntheticWorkspaceDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200657 return filepath.Join(p.soongOutDir, "workspace")
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200658}
659
Jingwen Chen8c523582021-06-01 11:19:53 +0000660// Returns the path to the top level out dir ($OUT_DIR).
661func (p *bazelPaths) outDir() string {
Lukacs T. Berki9f6c24a2021-08-26 15:07:24 +0200662 return filepath.Dir(p.soongOutDir)
Jingwen Chen8c523582021-06-01 11:19:53 +0000663}
664
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400665// Issues commands to Bazel to receive results for all cquery requests
666// queued in the BazelContext.
667func (context *bazelContext) InvokeBazel() error {
668 context.results = make(map[cqueryKey]string)
669
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400670 var cqueryOutput string
Chris Parsons808d84c2021-03-09 20:43:32 -0500671 var cqueryErr string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400672 var err error
Chris Parsons8ccdb632020-11-17 15:41:01 -0500673
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200674 soongInjectionPath := absolutePath(context.paths.injectedFilesDir())
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200675 mixedBuildsPath := filepath.Join(soongInjectionPath, "mixed_builds")
676 if _, err := os.Stat(mixedBuildsPath); os.IsNotExist(err) {
677 err = os.MkdirAll(mixedBuildsPath, 0777)
Chris Parsons07c1e4a2021-01-19 17:19:16 -0500678 }
Chris Parsons8ccdb632020-11-17 15:41:01 -0500679 if err != nil {
680 return err
681 }
Usta Shrestha902fd172022-03-02 15:27:49 -0500682 if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
683 err = os.MkdirAll(metricsDir, 0777)
684 if err != nil {
685 return err
686 }
687 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200688 err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
689 if err != nil {
690 return err
691 }
692
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400693 err = ioutil.WriteFile(
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200694 filepath.Join(mixedBuildsPath, "main.bzl"),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400695 context.mainBzlFileContents(), 0666)
696 if err != nil {
697 return err
698 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200699
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400700 err = ioutil.WriteFile(
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200701 filepath.Join(mixedBuildsPath, "BUILD.bazel"),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400702 context.mainBuildFileContents(), 0666)
703 if err != nil {
704 return err
705 }
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200706 cqueryFileRelpath := filepath.Join(context.paths.injectedFilesDir(), "buildroot.cquery")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400707 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800708 absolutePath(cqueryFileRelpath),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400709 context.cqueryStarlarkFileContents(), 0666)
710 if err != nil {
711 return err
712 }
Jingwen Chen1e347862021-09-02 12:11:49 +0000713
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200714 buildrootLabel := "@soong_injection//mixed_builds:buildroot"
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400715 cqueryOutput, cqueryErr, err = context.issueBazelCommand(
716 context.paths,
717 bazel.CqueryBuildRootRunName,
Liz Kammerc19d5cd2021-10-06 18:16:58 -0400718 bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)},
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400719 "--output=starlark",
Lukacs T. Berkid6cd8132021-04-20 13:01:07 +0200720 "--starlark:file="+absolutePath(cqueryFileRelpath))
721 err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"),
Chris Parsons8d6e4332021-02-22 16:13:50 -0500722 []byte(cqueryOutput), 0666)
723 if err != nil {
724 return err
725 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400726
727 if err != nil {
728 return err
729 }
730
731 cqueryResults := map[string]string{}
732 for _, outputLine := range strings.Split(cqueryOutput, "\n") {
733 if strings.Contains(outputLine, ">>") {
734 splitLine := strings.SplitN(outputLine, ">>", 2)
735 cqueryResults[splitLine[0]] = splitLine[1]
736 }
737 }
738
Usta Shrestha902fd172022-03-02 15:27:49 -0500739 for val := range context.requests {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500740 if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
Usta Shrestha902fd172022-03-02 15:27:49 -0500741 context.results[val] = cqueryResult
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400742 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500743 return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
744 getCqueryId(val), cqueryOutput, cqueryErr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400745 }
746 }
747
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500748 // Issue an aquery command to retrieve action information about the bazel build tree.
749 //
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400750 // TODO(cparsons): Use --target_pattern_file to avoid command line limits.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500751 var aqueryOutput string
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400752 aqueryOutput, _, err = context.issueBazelCommand(
753 context.paths,
754 bazel.AqueryBuildRootRunName,
755 bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)},
756 // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
757 // proto sources, which would add a number of unnecessary dependencies.
758 "--output=jsonproto")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400759
760 if err != nil {
761 return err
762 }
763
Chris Parsons1a7aca02022-04-25 22:35:15 -0400764 context.buildStatements, context.depsets, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
Chris Parsons4f069892021-01-15 12:22:41 -0500765 if err != nil {
766 return err
767 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500768
769 // Issue a build command of the phony root to generate symlink forests for dependencies of the
770 // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
771 // but some of symlinks may be required to resolve source dependencies of the build.
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400772 _, _, err = context.issueBazelCommand(
773 context.paths,
774 bazel.BazelBuildPhonyRootRunName,
Lukacs T. Berki3069dd92021-05-11 16:54:29 +0200775 bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"})
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500776
777 if err != nil {
778 return err
779 }
780
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400781 // Clear requests.
782 context.requests = map[cqueryKey]bool{}
783 return nil
784}
Chris Parsonsa798d962020-10-12 23:44:08 -0400785
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500786func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
787 return context.buildStatements
788}
789
Chris Parsons1a7aca02022-04-25 22:35:15 -0400790func (context *bazelContext) AqueryDepsets() []bazel.AqueryDepset {
791 return context.depsets
792}
793
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500794func (context *bazelContext) OutputBase() string {
Liz Kammer8d62a4f2021-04-08 09:47:28 -0400795 return context.paths.outputBase
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500796}
797
Chris Parsonsa798d962020-10-12 23:44:08 -0400798// Singleton used for registering BUILD file ninja dependencies (needed
799// for correctness of builds which use Bazel.
800func BazelSingleton() Singleton {
801 return &bazelSingleton{}
802}
803
804type bazelSingleton struct{}
805
806func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500807 // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
808 if !ctx.Config().BazelContext.BazelEnabled() {
809 return
810 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400811
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500812 // Add ninja file dependencies for files which all bazel invocations require.
813 bazelBuildList := absolutePath(filepath.Join(
Lukacs T. Berkif9008072021-08-16 15:24:48 +0200814 filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500815 ctx.AddNinjaFileDeps(bazelBuildList)
816
817 data, err := ioutil.ReadFile(bazelBuildList)
818 if err != nil {
819 ctx.Errorf(err.Error())
820 }
821 files := strings.Split(strings.TrimSpace(string(data)), "\n")
822 for _, file := range files {
823 ctx.AddNinjaFileDeps(file)
824 }
825
Chris Parsons1a7aca02022-04-25 22:35:15 -0400826 for _, depset := range ctx.Config().BazelContext.AqueryDepsets() {
827 var outputs []Path
828 for _, depsetDepId := range depset.TransitiveDepSetIds {
829 otherDepsetName := bazelDepsetName(depsetDepId)
830 outputs = append(outputs, PathForPhony(ctx, otherDepsetName))
831 }
832 for _, artifactPath := range depset.DirectArtifacts {
833 outputs = append(outputs, PathForBazelOut(ctx, artifactPath))
834 }
835 thisDepsetName := bazelDepsetName(depset.Id)
836 ctx.Build(pctx, BuildParams{
837 Rule: blueprint.Phony,
838 Outputs: []WritablePath{PathForPhony(ctx, thisDepsetName)},
839 Implicits: outputs,
840 })
841 }
842
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500843 // Register bazel-owned build statements (obtained from the aquery invocation).
844 for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500845 if len(buildStatement.Command) < 1 {
Rupert Shuttlewortha29903f2021-04-06 16:17:33 +0000846 panic(fmt.Sprintf("unhandled build statement: %v", buildStatement))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500847 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500848 rule := NewRuleBuilder(pctx, ctx)
849 cmd := rule.Command()
Chris Parsons94a0bba2021-06-04 15:03:47 -0400850
851 // cd into Bazel's execution root, which is the action cwd.
Chris Parsonse37a4de2021-09-23 17:10:50 -0400852 cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ &&", ctx.Config().BazelContext.OutputBase()))
853
854 // Remove old outputs, as some actions might not rerun if the outputs are detected.
855 if len(buildStatement.OutputPaths) > 0 {
856 cmd.Text("rm -f")
857 for _, outputPath := range buildStatement.OutputPaths {
Liz Kammerd7d5b722021-10-01 10:33:12 -0400858 cmd.Text(outputPath)
Chris Parsonse37a4de2021-09-23 17:10:50 -0400859 }
860 cmd.Text("&&")
861 }
Chris Parsons94a0bba2021-06-04 15:03:47 -0400862
863 for _, pair := range buildStatement.Env {
864 // Set per-action env variables, if any.
865 cmd.Flag(pair.Key + "=" + pair.Value)
866 }
867
868 // The actual Bazel action.
869 cmd.Text(" " + buildStatement.Command)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500870
871 for _, outputPath := range buildStatement.OutputPaths {
872 cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400873 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500874 for _, inputPath := range buildStatement.InputPaths {
875 cmd.Implicit(PathForBazelOut(ctx, inputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400876 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400877 for _, inputDepsetId := range buildStatement.InputDepsetIds {
878 otherDepsetName := bazelDepsetName(inputDepsetId)
879 cmd.Implicit(PathForPhony(ctx, otherDepsetName))
880 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500881
Liz Kammerde116852021-03-25 16:42:37 -0400882 if depfile := buildStatement.Depfile; depfile != nil {
883 cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
884 }
885
Liz Kammerc49e6822021-06-08 15:04:11 -0400886 for _, symlinkPath := range buildStatement.SymlinkPaths {
887 cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
888 }
889
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500890 // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
891 // some Bazel builtins (such as files in the bazel_tools directory) have far-future
892 // timestamps. Without restat, Ninja would emit warnings that the input files of a
893 // build statement have later timestamps than the outputs.
894 rule.Restat()
895
Liz Kammer13548d72020-12-16 11:13:30 -0800896 rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
Chris Parsonsa798d962020-10-12 23:44:08 -0400897 }
898}
Chris Parsons8d6e4332021-02-22 16:13:50 -0500899
900func getCqueryId(key cqueryKey) string {
Chris Parsons787fb362021-10-14 18:43:51 -0400901 return key.label + "|" + getConfigString(key)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500902}
903
Chris Parsons787fb362021-10-14 18:43:51 -0400904func getConfigString(key cqueryKey) string {
Liz Kammer0940b892022-03-18 15:55:04 -0400905 arch := key.configKey.arch
Chris Parsons787fb362021-10-14 18:43:51 -0400906 if len(arch) == 0 || arch == "common" {
907 // Use host platform, which is currently hardcoded to be x86_64.
908 arch = "x86_64"
Chris Parsons8d6e4332021-02-22 16:13:50 -0500909 }
Chris Parsons787fb362021-10-14 18:43:51 -0400910 os := key.configKey.osType.Name
Chris Parsons494eef32021-11-09 10:29:52 -0500911 if len(os) == 0 || os == "common_os" || os == "linux_glibc" {
Chris Parsons787fb362021-10-14 18:43:51 -0400912 // Use host OS, which is currently hardcoded to be linux.
913 os = "linux"
914 }
915 return arch + "|" + os
916}
917
918func GetConfigKey(ctx ModuleContext) configKey {
Liz Kammer0940b892022-03-18 15:55:04 -0400919 return configKey{
920 // use string because Arch is not a valid key in go
921 arch: ctx.Arch().String(),
922 osType: ctx.Os(),
923 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500924}
Chris Parsons1a7aca02022-04-25 22:35:15 -0400925
926func bazelDepsetName(depsetId int) string {
927 return fmt.Sprintf("bazel_depset_%d", depsetId)
928}