blob: 66758407297ff860e8881c051e69eb1f2272f38c [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 Parsonsdbcb1ff2020-12-10 17:19:18 -050029 "github.com/google/blueprint/bootstrap"
30
Patrice Arruda05ab2d02020-12-12 06:24:26 +000031 "android/soong/bazel"
32 "android/soong/shared"
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040033)
34
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040035type CqueryRequestType int
36
37const (
38 getAllFiles CqueryRequestType = iota
Chris Parsons8d6e4332021-02-22 16:13:50 -050039 getCcObjectFiles
Chris Parsons808d84c2021-03-09 20:43:32 -050040 getAllFilesAndCcObjectFiles
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040041)
42
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040043// Map key to describe bazel cquery requests.
44type cqueryKey struct {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040045 label string
46 requestType CqueryRequestType
Chris Parsons8d6e4332021-02-22 16:13:50 -050047 archType ArchType
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040048}
49
50type BazelContext interface {
51 // The below methods involve queuing cquery requests to be later invoked
52 // by bazel. If any of these methods return (_, false), then the request
53 // has been queued to be run later.
54
55 // Returns result files built by building the given bazel target label.
Chris Parsons8d6e4332021-02-22 16:13:50 -050056 GetAllFiles(label string, archType ArchType) ([]string, bool)
57
58 // Returns object files produced by compiling the given cc-related target.
59 // Retrieves these files from Bazel's CcInfo provider.
60 GetCcObjectFiles(label string, archType ArchType) ([]string, bool)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040061
Chris Parsons808d84c2021-03-09 20:43:32 -050062 // Returns the results of GetAllFiles and GetCcObjectFiles in a single query (in that order).
63 GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool)
64
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040065 // ** End cquery methods
66
67 // Issues commands to Bazel to receive results for all cquery requests
68 // queued in the BazelContext.
69 InvokeBazel() error
70
71 // Returns true if bazel is enabled for the given configuration.
72 BazelEnabled() bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050073
74 // Returns the bazel output base (the root directory for all bazel intermediate outputs).
75 OutputBase() string
76
77 // Returns build statements which should get registered to reflect Bazel's outputs.
78 BuildStatementsToRegister() []bazel.BuildStatement
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040079}
80
81// A context object which tracks queued requests that need to be made to Bazel,
82// and their results after the requests have been made.
83type bazelContext struct {
84 homeDir string
85 bazelPath string
86 outputBase string
87 workspaceDir string
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040088 buildDir string
Patrice Arruda05ab2d02020-12-12 06:24:26 +000089 metricsDir string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040090
91 requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
92 requestMutex sync.Mutex // requests can be written in parallel
93
94 results map[cqueryKey]string // Results of cquery requests after Bazel invocations
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050095
96 // Build statements which should get registered to reflect Bazel's outputs.
97 buildStatements []bazel.BuildStatement
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040098}
99
100var _ BazelContext = &bazelContext{}
101
102// A bazel context to use when Bazel is disabled.
103type noopBazelContext struct{}
104
105var _ BazelContext = noopBazelContext{}
106
107// A bazel context to use for tests.
108type MockBazelContext struct {
109 AllFiles map[string][]string
110}
111
Chris Parsons8d6e4332021-02-22 16:13:50 -0500112func (m MockBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
113 result, ok := m.AllFiles[label]
114 return result, ok
115}
116
117func (m MockBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400118 result, ok := m.AllFiles[label]
119 return result, ok
120}
121
Chris Parsons808d84c2021-03-09 20:43:32 -0500122func (m MockBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
123 result, ok := m.AllFiles[label]
124 return result, result, ok
125}
126
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400127func (m MockBazelContext) InvokeBazel() error {
128 panic("unimplemented")
129}
130
131func (m MockBazelContext) BazelEnabled() bool {
132 return true
133}
134
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500135func (m MockBazelContext) OutputBase() string {
136 return "outputbase"
137}
138
139func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
140 return []bazel.BuildStatement{}
141}
142
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400143var _ BazelContext = MockBazelContext{}
144
Chris Parsons8d6e4332021-02-22 16:13:50 -0500145func (bazelCtx *bazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
146 result, ok := bazelCtx.cquery(label, getAllFiles, archType)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400147 if ok {
148 bazelOutput := strings.TrimSpace(result)
149 return strings.Split(bazelOutput, ", "), true
150 } else {
151 return nil, false
152 }
153}
154
Chris Parsons8d6e4332021-02-22 16:13:50 -0500155func (bazelCtx *bazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
156 result, ok := bazelCtx.cquery(label, getCcObjectFiles, archType)
157 if ok {
158 bazelOutput := strings.TrimSpace(result)
159 return strings.Split(bazelOutput, ", "), true
160 } else {
161 return nil, false
162 }
163}
164
Chris Parsons808d84c2021-03-09 20:43:32 -0500165func (bazelCtx *bazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
166 var allFiles []string
167 var ccObjects []string
168
169 result, ok := bazelCtx.cquery(label, getAllFilesAndCcObjectFiles, archType)
170 if ok {
171 bazelOutput := strings.TrimSpace(result)
172 splitString := strings.Split(bazelOutput, "|")
173 allFilesString := splitString[0]
174 ccObjectsString := splitString[1]
175 allFiles = strings.Split(allFilesString, ", ")
176 ccObjects = strings.Split(ccObjectsString, ", ")
177 }
178 return allFiles, ccObjects, ok
179}
180
Chris Parsons8d6e4332021-02-22 16:13:50 -0500181func (n noopBazelContext) GetAllFiles(label string, archType ArchType) ([]string, bool) {
182 panic("unimplemented")
183}
184
185func (n noopBazelContext) GetCcObjectFiles(label string, archType ArchType) ([]string, bool) {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400186 panic("unimplemented")
187}
188
Chris Parsons808d84c2021-03-09 20:43:32 -0500189func (n noopBazelContext) GetAllFilesAndCcObjectFiles(label string, archType ArchType) ([]string, []string, bool) {
190 panic("unimplemented")
191}
192
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400193func (n noopBazelContext) InvokeBazel() error {
194 panic("unimplemented")
195}
196
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500197func (m noopBazelContext) OutputBase() string {
198 return ""
199}
200
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400201func (n noopBazelContext) BazelEnabled() bool {
202 return false
203}
204
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500205func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
206 return []bazel.BuildStatement{}
207}
208
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400209func NewBazelContext(c *config) (BazelContext, error) {
Chris Parsons8b77a002020-10-27 18:59:25 -0400210 // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
211 // are production ready.
212 if c.Getenv("USE_BAZEL_ANALYSIS") != "1" {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400213 return noopBazelContext{}, nil
214 }
215
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400216 bazelCtx := bazelContext{buildDir: c.buildDir, requests: make(map[cqueryKey]bool)}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400217 missingEnvVars := []string{}
218 if len(c.Getenv("BAZEL_HOME")) > 1 {
219 bazelCtx.homeDir = c.Getenv("BAZEL_HOME")
220 } else {
221 missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
222 }
223 if len(c.Getenv("BAZEL_PATH")) > 1 {
224 bazelCtx.bazelPath = c.Getenv("BAZEL_PATH")
225 } else {
226 missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
227 }
228 if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
229 bazelCtx.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
230 } else {
231 missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
232 }
233 if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
234 bazelCtx.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
235 } else {
236 missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
237 }
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000238 if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
239 bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
240 } else {
241 missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
242 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400243 if len(missingEnvVars) > 0 {
244 return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
245 } else {
246 return &bazelCtx, nil
247 }
248}
249
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000250func (context *bazelContext) BazelMetricsDir() string {
251 return context.metricsDir
252}
253
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400254func (context *bazelContext) BazelEnabled() bool {
255 return true
256}
257
258// Adds a cquery request to the Bazel request queue, to be later invoked, or
259// returns the result of the given request if the request was already made.
260// If the given request was already made (and the results are available), then
261// returns (result, true). If the request is queued but no results are available,
262// then returns ("", false).
Chris Parsons8d6e4332021-02-22 16:13:50 -0500263func (context *bazelContext) cquery(label string, requestType CqueryRequestType,
264 archType ArchType) (string, bool) {
265 key := cqueryKey{label, requestType, archType}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400266 if result, ok := context.results[key]; ok {
267 return result, true
268 } else {
269 context.requestMutex.Lock()
270 defer context.requestMutex.Unlock()
271 context.requests[key] = true
272 return "", false
273 }
274}
275
276func pwdPrefix() string {
277 // Darwin doesn't have /proc
278 if runtime.GOOS != "darwin" {
279 return "PWD=/proc/self/cwd"
280 }
281 return ""
282}
283
Chris Parsons808d84c2021-03-09 20:43:32 -0500284// Issues the given bazel command with given build label and additional flags.
285// Returns (stdout, stderr, error). The first and second return values are strings
286// containing the stdout and stderr of the run command, and an error is returned if
287// the invocation returned an error code.
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000288func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
Chris Parsons808d84c2021-03-09 20:43:32 -0500289 extraFlags ...string) (string, string, error) {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400290
Jingwen Chen7c6089a2020-11-02 02:56:20 -0500291 cmdFlags := []string{"--output_base=" + context.outputBase, command}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400292 cmdFlags = append(cmdFlags, labels...)
Chris Parsons8ccdb632020-11-17 15:41:01 -0500293 cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000294 cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
Chris Parsonsee423b02021-02-08 23:04:59 -0500295 // Set default platforms to canonicalized values for mixed builds requests. If these are set
296 // in the bazelrc, they will have values that are non-canonicalized, and thus be invalid.
297 // The actual platform values here may be overridden by configuration transitions from the buildroot.
298 cmdFlags = append(cmdFlags,
299 fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:generic_x86_64")))
300 cmdFlags = append(cmdFlags,
301 fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all")))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500302 // Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
303 cmdFlags = append(cmdFlags, "--experimental_repository_disable_download")
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400304 cmdFlags = append(cmdFlags, extraFlags...)
305
306 bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
307 bazelCmd.Dir = context.workspaceDir
Chris Parsons8d6e4332021-02-22 16:13:50 -0500308 bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix(),
309 // Disables local host detection of gcc; toolchain information is defined
310 // explicitly in BUILD files.
311 "BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
Colin Crossff0278b2020-10-09 19:24:15 -0700312 stderr := &bytes.Buffer{}
313 bazelCmd.Stderr = stderr
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400314
315 if output, err := bazelCmd.Output(); err != nil {
Chris Parsons808d84c2021-03-09 20:43:32 -0500316 return "", string(stderr.Bytes()),
317 fmt.Errorf("bazel command failed. command: [%s], env: [%s], error [%s]", bazelCmd, bazelCmd.Env, stderr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400318 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500319 return string(output), string(stderr.Bytes()), nil
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400320 }
321}
322
Chris Parsons8ccdb632020-11-17 15:41:01 -0500323// Returns the string contents of a workspace file that should be output
324// adjacent to the main bzl file and build file.
325// This workspace file allows, via local_repository rule, sourcetree-level
326// BUILD targets to be referenced via @sourceroot.
327func (context *bazelContext) workspaceFileContents() []byte {
328 formatString := `
329# This file is generated by soong_build. Do not edit.
330local_repository(
331 name = "sourceroot",
332 path = "%s",
333)
334`
335 return []byte(fmt.Sprintf(formatString, context.workspaceDir))
336}
337
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400338func (context *bazelContext) mainBzlFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500339 // TODO(cparsons): Define configuration transitions programmatically based
340 // on available archs.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400341 contents := `
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500342#####################################################
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400343# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500344#####################################################
345
Chris Parsons8d6e4332021-02-22 16:13:50 -0500346def _x86_64_transition_impl(settings, attr):
347 return {
348 "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64",
349 }
350
351def _x86_transition_impl(settings, attr):
352 return {
353 "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86",
354 }
355
356def _arm64_transition_impl(settings, attr):
357 return {
358 "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm64",
359 }
360
361def _arm_transition_impl(settings, attr):
362 return {
363 "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm",
364 }
365
366x86_64_transition = transition(
367 implementation = _x86_64_transition_impl,
368 inputs = [],
369 outputs = [
370 "//command_line_option:platforms",
371 ],
372)
373
374x86_transition = transition(
375 implementation = _x86_transition_impl,
376 inputs = [],
377 outputs = [
378 "//command_line_option:platforms",
379 ],
380)
381
382arm64_transition = transition(
383 implementation = _arm64_transition_impl,
384 inputs = [],
385 outputs = [
386 "//command_line_option:platforms",
387 ],
388)
389
390arm_transition = transition(
391 implementation = _arm_transition_impl,
392 inputs = [],
393 outputs = [
394 "//command_line_option:platforms",
395 ],
396)
397
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400398def _mixed_build_root_impl(ctx):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500399 all_files = ctx.files.deps_x86_64 + ctx.files.deps_x86 + ctx.files.deps_arm64 + ctx.files.deps_arm
400 return [DefaultInfo(files = depset(all_files))]
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400401
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500402# Rule representing the root of the build, to depend on all Bazel targets that
403# are required for the build. Building this target will build the entire Bazel
404# build tree.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400405mixed_build_root = rule(
406 implementation = _mixed_build_root_impl,
Chris Parsons8d6e4332021-02-22 16:13:50 -0500407 attrs = {
408 "deps_x86_64" : attr.label_list(cfg = x86_64_transition),
409 "deps_x86" : attr.label_list(cfg = x86_transition),
410 "deps_arm64" : attr.label_list(cfg = arm64_transition),
411 "deps_arm" : attr.label_list(cfg = arm_transition),
412 "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
413 },
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400414)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500415
416def _phony_root_impl(ctx):
417 return []
418
419# Rule to depend on other targets but build nothing.
420# This is useful as follows: building a target of this rule will generate
421# symlink forests for all dependencies of the target, without executing any
422# actions of the build.
423phony_root = rule(
424 implementation = _phony_root_impl,
425 attrs = {"deps" : attr.label_list()},
426)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400427`
428 return []byte(contents)
429}
430
Chris Parsons8ccdb632020-11-17 15:41:01 -0500431// Returns a "canonicalized" corresponding to the given sourcetree-level label.
432// This abstraction is required because a sourcetree label such as //foo/bar:baz
433// must be referenced via the local repository prefix, such as
434// @sourceroot//foo/bar:baz.
435func canonicalizeLabel(label string) string {
436 if strings.HasPrefix(label, "//") {
437 return "@sourceroot" + label
438 } else {
439 return "@sourceroot//" + label
440 }
441}
442
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400443func (context *bazelContext) mainBuildFileContents() []byte {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500444 // TODO(cparsons): Map label to attribute programmatically; don't use hard-coded
445 // architecture mapping.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400446 formatString := `
447# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500448load(":main.bzl", "mixed_build_root", "phony_root")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400449
450mixed_build_root(name = "buildroot",
Chris Parsons8d6e4332021-02-22 16:13:50 -0500451 deps_x86_64 = [%s],
452 deps_x86 = [%s],
453 deps_arm64 = [%s],
454 deps_arm = [%s],
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400455)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500456
457phony_root(name = "phonyroot",
458 deps = [":buildroot"],
459)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400460`
Chris Parsons8d6e4332021-02-22 16:13:50 -0500461 var deps_x86_64 []string = nil
462 var deps_x86 []string = nil
463 var deps_arm64 []string = nil
464 var deps_arm []string = nil
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400465 for val, _ := range context.requests {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500466 labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))
467 switch getArchString(val) {
468 case "x86_64":
469 deps_x86_64 = append(deps_x86_64, labelString)
470 case "x86":
471 deps_x86 = append(deps_x86, labelString)
472 case "arm64":
473 deps_arm64 = append(deps_arm64, labelString)
474 case "arm":
475 deps_arm = append(deps_arm, labelString)
476 default:
Liz Kammer15b04e22021-03-04 09:45:21 -0500477 panic(fmt.Sprintf("unhandled architecture %s for %v", getArchString(val), val))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500478 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400479 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400480
Chris Parsons8d6e4332021-02-22 16:13:50 -0500481 return []byte(fmt.Sprintf(formatString,
482 strings.Join(deps_x86_64, ",\n "),
483 strings.Join(deps_x86, ",\n "),
484 strings.Join(deps_arm64, ",\n "),
485 strings.Join(deps_arm, ",\n ")))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400486}
487
Chris Parsons808d84c2021-03-09 20:43:32 -0500488// Returns the file contents of the buildroot.cquery file that should be used for the cquery
489// expression in order to obtain information about buildroot and its dependencies.
490// The contents of this file depend on the bazelContext's requests; requests are enumerated
491// and grouped by their request type. The data retrieved for each label depends on its
492// request type.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400493func (context *bazelContext) cqueryStarlarkFileContents() []byte {
494 formatString := `
495# This file is generated by soong_build. Do not edit.
496getAllFilesLabels = {
497 %s
498}
499
Chris Parsons8d6e4332021-02-22 16:13:50 -0500500getCcObjectFilesLabels = {
501 %s
502}
503
Chris Parsons808d84c2021-03-09 20:43:32 -0500504getAllFilesAndCcObjectFilesLabels = {
505 %s
506}
507
508def get_all_files(target):
509 return [f.path for f in target.files.to_list()]
510
Chris Parsons8d6e4332021-02-22 16:13:50 -0500511def get_cc_object_files(target):
512 result = []
513 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
514
515 for linker_input in linker_inputs:
516 for library in linker_input.libraries:
517 for object in library.objects:
518 result += [object.path]
519 return result
520
521def get_arch(target):
522 buildoptions = build_options(target)
523 platforms = build_options(target)["//command_line_option:platforms"]
524 if len(platforms) != 1:
525 # An individual configured target should have only one platform architecture.
526 # Note that it's fine for there to be multiple architectures for the same label,
527 # but each is its own configured target.
528 fail("expected exactly 1 platform for " + str(target.label) + " but got " + str(platforms))
529 platform_name = build_options(target)["//command_line_option:platforms"][0].name
530 if platform_name == "host":
531 return "HOST"
532 elif not platform_name.startswith("generic_"):
533 fail("expected platform name of the form 'generic_<arch>', but was " + str(platforms))
534 return "UNKNOWN"
535 return platform_name[len("generic_"):]
536
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400537def format(target):
Chris Parsons8d6e4332021-02-22 16:13:50 -0500538 id_string = str(target.label) + "|" + get_arch(target)
539 if id_string in getAllFilesLabels:
Chris Parsons808d84c2021-03-09 20:43:32 -0500540 return id_string + ">>" + ', '.join(get_all_files(target))
Chris Parsons8d6e4332021-02-22 16:13:50 -0500541 elif id_string in getCcObjectFilesLabels:
542 return id_string + ">>" + ', '.join(get_cc_object_files(target))
Chris Parsons808d84c2021-03-09 20:43:32 -0500543 elif id_string in getAllFilesAndCcObjectFilesLabels:
544 return id_string + ">>" + ', '.join(get_all_files(target)) + "|" + ', '.join(get_cc_object_files(target))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400545 else:
546 # This target was not requested via cquery, and thus must be a dependency
547 # of a requested target.
Chris Parsons8d6e4332021-02-22 16:13:50 -0500548 return id_string + ">>NONE"
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400549`
Chris Parsons8d6e4332021-02-22 16:13:50 -0500550 var getAllFilesDeps []string = nil
551 var getCcObjectFilesDeps []string = nil
Chris Parsons808d84c2021-03-09 20:43:32 -0500552 var getAllFilesAndCcObjectFilesDeps []string = nil
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400553
Chris Parsons8d6e4332021-02-22 16:13:50 -0500554 for val, _ := range context.requests {
555 labelWithArch := getCqueryId(val)
556 mapEntryString := fmt.Sprintf("%q : True", labelWithArch)
557 switch val.requestType {
558 case getAllFiles:
559 getAllFilesDeps = append(getAllFilesDeps, mapEntryString)
560 case getCcObjectFiles:
561 getCcObjectFilesDeps = append(getCcObjectFilesDeps, mapEntryString)
Chris Parsons808d84c2021-03-09 20:43:32 -0500562 case getAllFilesAndCcObjectFiles:
563 getAllFilesAndCcObjectFilesDeps = append(getAllFilesAndCcObjectFilesDeps, mapEntryString)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500564 }
565 }
566 getAllFilesDepsString := strings.Join(getAllFilesDeps, ",\n ")
567 getCcObjectFilesDepsString := strings.Join(getCcObjectFilesDeps, ",\n ")
Chris Parsons808d84c2021-03-09 20:43:32 -0500568 getAllFilesAndCcObjectFilesDepsString := strings.Join(getAllFilesAndCcObjectFilesDeps, ",\n ")
Chris Parsons8d6e4332021-02-22 16:13:50 -0500569
Chris Parsons808d84c2021-03-09 20:43:32 -0500570 return []byte(fmt.Sprintf(formatString, getAllFilesDepsString, getCcObjectFilesDepsString,
571 getAllFilesAndCcObjectFilesDepsString))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400572}
573
Chris Parsons8ccdb632020-11-17 15:41:01 -0500574// Returns a workspace-relative path containing build-related metadata required
575// for interfacing with Bazel. Example: out/soong/bazel.
576func (context *bazelContext) intermediatesDir() string {
577 return filepath.Join(context.buildDir, "bazel")
578}
579
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400580// Issues commands to Bazel to receive results for all cquery requests
581// queued in the BazelContext.
582func (context *bazelContext) InvokeBazel() error {
583 context.results = make(map[cqueryKey]string)
584
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400585 var cqueryOutput string
Chris Parsons808d84c2021-03-09 20:43:32 -0500586 var cqueryErr string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400587 var err error
Chris Parsons8ccdb632020-11-17 15:41:01 -0500588
Chris Parsons07c1e4a2021-01-19 17:19:16 -0500589 intermediatesDirPath := absolutePath(context.intermediatesDir())
590 if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
591 err = os.Mkdir(intermediatesDirPath, 0777)
592 }
593
Chris Parsons8ccdb632020-11-17 15:41:01 -0500594 if err != nil {
595 return err
596 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400597 err = ioutil.WriteFile(
Chris Parsons8ccdb632020-11-17 15:41:01 -0500598 absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400599 context.mainBzlFileContents(), 0666)
600 if err != nil {
601 return err
602 }
603 err = ioutil.WriteFile(
Chris Parsons8ccdb632020-11-17 15:41:01 -0500604 absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400605 context.mainBuildFileContents(), 0666)
606 if err != nil {
607 return err
608 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800609 cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400610 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800611 absolutePath(cqueryFileRelpath),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400612 context.cqueryStarlarkFileContents(), 0666)
613 if err != nil {
614 return err
615 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800616 workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
Chris Parsons8ccdb632020-11-17 15:41:01 -0500617 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800618 absolutePath(workspaceFileRelpath),
Chris Parsons8ccdb632020-11-17 15:41:01 -0500619 context.workspaceFileContents(), 0666)
620 if err != nil {
621 return err
622 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800623 buildrootLabel := "//:buildroot"
Chris Parsons808d84c2021-03-09 20:43:32 -0500624 cqueryOutput, cqueryErr, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
Chris Parsons8d6e4332021-02-22 16:13:50 -0500625 []string{fmt.Sprintf("kind(rule, deps(%s))", buildrootLabel)},
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400626 "--output=starlark",
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800627 "--starlark:file="+cqueryFileRelpath)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500628 err = ioutil.WriteFile(
629 absolutePath(filepath.Join(context.intermediatesDir(), "cquery.out")),
630 []byte(cqueryOutput), 0666)
631 if err != nil {
632 return err
633 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400634
635 if err != nil {
636 return err
637 }
638
639 cqueryResults := map[string]string{}
640 for _, outputLine := range strings.Split(cqueryOutput, "\n") {
641 if strings.Contains(outputLine, ">>") {
642 splitLine := strings.SplitN(outputLine, ">>", 2)
643 cqueryResults[splitLine[0]] = splitLine[1]
644 }
645 }
646
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400647 for val, _ := range context.requests {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500648 if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400649 context.results[val] = string(cqueryResult)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400650 } else {
Chris Parsons808d84c2021-03-09 20:43:32 -0500651 return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
652 getCqueryId(val), cqueryOutput, cqueryErr)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400653 }
654 }
655
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500656 // Issue an aquery command to retrieve action information about the bazel build tree.
657 //
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400658 // TODO(cparsons): Use --target_pattern_file to avoid command line limits.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500659 var aqueryOutput string
Chris Parsons808d84c2021-03-09 20:43:32 -0500660 aqueryOutput, _, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800661 []string{fmt.Sprintf("deps(%s)", buildrootLabel),
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500662 // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
663 // proto sources, which would add a number of unnecessary dependencies.
664 "--output=jsonproto"})
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400665
666 if err != nil {
667 return err
668 }
669
Chris Parsons4f069892021-01-15 12:22:41 -0500670 context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
671 if err != nil {
672 return err
673 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500674
675 // Issue a build command of the phony root to generate symlink forests for dependencies of the
676 // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
677 // but some of symlinks may be required to resolve source dependencies of the build.
Chris Parsons808d84c2021-03-09 20:43:32 -0500678 _, _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500679 []string{"//:phonyroot"})
680
681 if err != nil {
682 return err
683 }
684
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400685 // Clear requests.
686 context.requests = map[cqueryKey]bool{}
687 return nil
688}
Chris Parsonsa798d962020-10-12 23:44:08 -0400689
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500690func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
691 return context.buildStatements
692}
693
694func (context *bazelContext) OutputBase() string {
695 return context.outputBase
696}
697
Chris Parsonsa798d962020-10-12 23:44:08 -0400698// Singleton used for registering BUILD file ninja dependencies (needed
699// for correctness of builds which use Bazel.
700func BazelSingleton() Singleton {
701 return &bazelSingleton{}
702}
703
704type bazelSingleton struct{}
705
706func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500707 // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
708 if !ctx.Config().BazelContext.BazelEnabled() {
709 return
710 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400711
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500712 // Add ninja file dependencies for files which all bazel invocations require.
713 bazelBuildList := absolutePath(filepath.Join(
714 filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
715 ctx.AddNinjaFileDeps(bazelBuildList)
716
717 data, err := ioutil.ReadFile(bazelBuildList)
718 if err != nil {
719 ctx.Errorf(err.Error())
720 }
721 files := strings.Split(strings.TrimSpace(string(data)), "\n")
722 for _, file := range files {
723 ctx.AddNinjaFileDeps(file)
724 }
725
726 // Register bazel-owned build statements (obtained from the aquery invocation).
727 for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500728 if len(buildStatement.Command) < 1 {
729 panic(fmt.Sprintf("unhandled build statement: %s", buildStatement))
730 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500731 rule := NewRuleBuilder(pctx, ctx)
732 cmd := rule.Command()
733 cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
734 ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
735
736 for _, outputPath := range buildStatement.OutputPaths {
737 cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400738 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500739 for _, inputPath := range buildStatement.InputPaths {
740 cmd.Implicit(PathForBazelOut(ctx, inputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400741 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500742
743 // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
744 // some Bazel builtins (such as files in the bazel_tools directory) have far-future
745 // timestamps. Without restat, Ninja would emit warnings that the input files of a
746 // build statement have later timestamps than the outputs.
747 rule.Restat()
748
Liz Kammer13548d72020-12-16 11:13:30 -0800749 rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
Chris Parsonsa798d962020-10-12 23:44:08 -0400750 }
751}
Chris Parsons8d6e4332021-02-22 16:13:50 -0500752
753func getCqueryId(key cqueryKey) string {
754 return canonicalizeLabel(key.label) + "|" + getArchString(key)
755}
756
757func getArchString(key cqueryKey) string {
758 arch := key.archType.Name
759 if len(arch) > 0 {
760 return arch
761 } else {
762 return "x86_64"
763 }
764}