blob: 5ba6d622a97868c920b07132f71788fababbaee2 [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
39)
40
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040041// Map key to describe bazel cquery requests.
42type cqueryKey struct {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040043 label string
44 requestType CqueryRequestType
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040045}
46
47type BazelContext interface {
48 // The below methods involve queuing cquery requests to be later invoked
49 // by bazel. If any of these methods return (_, false), then the request
50 // has been queued to be run later.
51
52 // Returns result files built by building the given bazel target label.
53 GetAllFiles(label string) ([]string, bool)
54
55 // TODO(cparsons): Other cquery-related methods should be added here.
56 // ** End cquery methods
57
58 // Issues commands to Bazel to receive results for all cquery requests
59 // queued in the BazelContext.
60 InvokeBazel() error
61
62 // Returns true if bazel is enabled for the given configuration.
63 BazelEnabled() bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050064
65 // Returns the bazel output base (the root directory for all bazel intermediate outputs).
66 OutputBase() string
67
68 // Returns build statements which should get registered to reflect Bazel's outputs.
69 BuildStatementsToRegister() []bazel.BuildStatement
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040070}
71
72// A context object which tracks queued requests that need to be made to Bazel,
73// and their results after the requests have been made.
74type bazelContext struct {
75 homeDir string
76 bazelPath string
77 outputBase string
78 workspaceDir string
Chris Parsonsb0f8ac42020-10-23 16:48:08 -040079 buildDir string
Patrice Arruda05ab2d02020-12-12 06:24:26 +000080 metricsDir string
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040081
82 requests map[cqueryKey]bool // cquery requests that have not yet been issued to Bazel
83 requestMutex sync.Mutex // requests can be written in parallel
84
85 results map[cqueryKey]string // Results of cquery requests after Bazel invocations
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050086
87 // Build statements which should get registered to reflect Bazel's outputs.
88 buildStatements []bazel.BuildStatement
Chris Parsonsf3c96ef2020-09-29 02:23:17 -040089}
90
91var _ BazelContext = &bazelContext{}
92
93// A bazel context to use when Bazel is disabled.
94type noopBazelContext struct{}
95
96var _ BazelContext = noopBazelContext{}
97
98// A bazel context to use for tests.
99type MockBazelContext struct {
100 AllFiles map[string][]string
101}
102
103func (m MockBazelContext) GetAllFiles(label string) ([]string, bool) {
104 result, ok := m.AllFiles[label]
105 return result, ok
106}
107
108func (m MockBazelContext) InvokeBazel() error {
109 panic("unimplemented")
110}
111
112func (m MockBazelContext) BazelEnabled() bool {
113 return true
114}
115
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500116func (m MockBazelContext) OutputBase() string {
117 return "outputbase"
118}
119
120func (m MockBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
121 return []bazel.BuildStatement{}
122}
123
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400124var _ BazelContext = MockBazelContext{}
125
126func (bazelCtx *bazelContext) GetAllFiles(label string) ([]string, bool) {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400127 result, ok := bazelCtx.cquery(label, getAllFiles)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400128 if ok {
129 bazelOutput := strings.TrimSpace(result)
130 return strings.Split(bazelOutput, ", "), true
131 } else {
132 return nil, false
133 }
134}
135
136func (n noopBazelContext) GetAllFiles(label string) ([]string, bool) {
137 panic("unimplemented")
138}
139
140func (n noopBazelContext) InvokeBazel() error {
141 panic("unimplemented")
142}
143
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500144func (m noopBazelContext) OutputBase() string {
145 return ""
146}
147
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400148func (n noopBazelContext) BazelEnabled() bool {
149 return false
150}
151
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500152func (m noopBazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
153 return []bazel.BuildStatement{}
154}
155
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400156func NewBazelContext(c *config) (BazelContext, error) {
Chris Parsons8b77a002020-10-27 18:59:25 -0400157 // TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
158 // are production ready.
159 if c.Getenv("USE_BAZEL_ANALYSIS") != "1" {
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400160 return noopBazelContext{}, nil
161 }
162
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400163 bazelCtx := bazelContext{buildDir: c.buildDir, requests: make(map[cqueryKey]bool)}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400164 missingEnvVars := []string{}
165 if len(c.Getenv("BAZEL_HOME")) > 1 {
166 bazelCtx.homeDir = c.Getenv("BAZEL_HOME")
167 } else {
168 missingEnvVars = append(missingEnvVars, "BAZEL_HOME")
169 }
170 if len(c.Getenv("BAZEL_PATH")) > 1 {
171 bazelCtx.bazelPath = c.Getenv("BAZEL_PATH")
172 } else {
173 missingEnvVars = append(missingEnvVars, "BAZEL_PATH")
174 }
175 if len(c.Getenv("BAZEL_OUTPUT_BASE")) > 1 {
176 bazelCtx.outputBase = c.Getenv("BAZEL_OUTPUT_BASE")
177 } else {
178 missingEnvVars = append(missingEnvVars, "BAZEL_OUTPUT_BASE")
179 }
180 if len(c.Getenv("BAZEL_WORKSPACE")) > 1 {
181 bazelCtx.workspaceDir = c.Getenv("BAZEL_WORKSPACE")
182 } else {
183 missingEnvVars = append(missingEnvVars, "BAZEL_WORKSPACE")
184 }
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000185 if len(c.Getenv("BAZEL_METRICS_DIR")) > 1 {
186 bazelCtx.metricsDir = c.Getenv("BAZEL_METRICS_DIR")
187 } else {
188 missingEnvVars = append(missingEnvVars, "BAZEL_METRICS_DIR")
189 }
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400190 if len(missingEnvVars) > 0 {
191 return nil, errors.New(fmt.Sprintf("missing required env vars to use bazel: %s", missingEnvVars))
192 } else {
193 return &bazelCtx, nil
194 }
195}
196
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000197func (context *bazelContext) BazelMetricsDir() string {
198 return context.metricsDir
199}
200
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400201func (context *bazelContext) BazelEnabled() bool {
202 return true
203}
204
205// Adds a cquery request to the Bazel request queue, to be later invoked, or
206// returns the result of the given request if the request was already made.
207// If the given request was already made (and the results are available), then
208// returns (result, true). If the request is queued but no results are available,
209// then returns ("", false).
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400210func (context *bazelContext) cquery(label string, requestType CqueryRequestType) (string, bool) {
211 key := cqueryKey{label, requestType}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400212 if result, ok := context.results[key]; ok {
213 return result, true
214 } else {
215 context.requestMutex.Lock()
216 defer context.requestMutex.Unlock()
217 context.requests[key] = true
218 return "", false
219 }
220}
221
222func pwdPrefix() string {
223 // Darwin doesn't have /proc
224 if runtime.GOOS != "darwin" {
225 return "PWD=/proc/self/cwd"
226 }
227 return ""
228}
229
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000230func (context *bazelContext) issueBazelCommand(runName bazel.RunName, command string, labels []string,
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400231 extraFlags ...string) (string, error) {
232
Jingwen Chen7c6089a2020-11-02 02:56:20 -0500233 cmdFlags := []string{"--output_base=" + context.outputBase, command}
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400234 cmdFlags = append(cmdFlags, labels...)
Chris Parsons8ccdb632020-11-17 15:41:01 -0500235 cmdFlags = append(cmdFlags, "--package_path=%workspace%/"+context.intermediatesDir())
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000236 cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(context, runName))
Chris Parsonsee423b02021-02-08 23:04:59 -0500237 // Set default platforms to canonicalized values for mixed builds requests. If these are set
238 // in the bazelrc, they will have values that are non-canonicalized, and thus be invalid.
239 // The actual platform values here may be overridden by configuration transitions from the buildroot.
240 cmdFlags = append(cmdFlags,
241 fmt.Sprintf("--platforms=%s", canonicalizeLabel("//build/bazel/platforms:generic_x86_64")))
242 cmdFlags = append(cmdFlags,
243 fmt.Sprintf("--extra_toolchains=%s", canonicalizeLabel("//prebuilts/clang/host/linux-x86:all")))
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400244 cmdFlags = append(cmdFlags, extraFlags...)
245
246 bazelCmd := exec.Command(context.bazelPath, cmdFlags...)
247 bazelCmd.Dir = context.workspaceDir
248 bazelCmd.Env = append(os.Environ(), "HOME="+context.homeDir, pwdPrefix())
249
Colin Crossff0278b2020-10-09 19:24:15 -0700250 stderr := &bytes.Buffer{}
251 bazelCmd.Stderr = stderr
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400252
253 if output, err := bazelCmd.Output(); err != nil {
254 return "", fmt.Errorf("bazel command failed. command: [%s], error [%s]", bazelCmd, stderr)
255 } else {
256 return string(output), nil
257 }
258}
259
Chris Parsons8ccdb632020-11-17 15:41:01 -0500260// Returns the string contents of a workspace file that should be output
261// adjacent to the main bzl file and build file.
262// This workspace file allows, via local_repository rule, sourcetree-level
263// BUILD targets to be referenced via @sourceroot.
264func (context *bazelContext) workspaceFileContents() []byte {
265 formatString := `
266# This file is generated by soong_build. Do not edit.
267local_repository(
268 name = "sourceroot",
269 path = "%s",
270)
271`
272 return []byte(fmt.Sprintf(formatString, context.workspaceDir))
273}
274
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400275func (context *bazelContext) mainBzlFileContents() []byte {
276 contents := `
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500277#####################################################
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400278# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500279#####################################################
280
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400281def _mixed_build_root_impl(ctx):
282 return [DefaultInfo(files = depset(ctx.files.deps))]
283
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500284# Rule representing the root of the build, to depend on all Bazel targets that
285# are required for the build. Building this target will build the entire Bazel
286# build tree.
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400287mixed_build_root = rule(
288 implementation = _mixed_build_root_impl,
289 attrs = {"deps" : attr.label_list()},
290)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500291
292def _phony_root_impl(ctx):
293 return []
294
295# Rule to depend on other targets but build nothing.
296# This is useful as follows: building a target of this rule will generate
297# symlink forests for all dependencies of the target, without executing any
298# actions of the build.
299phony_root = rule(
300 implementation = _phony_root_impl,
301 attrs = {"deps" : attr.label_list()},
302)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400303`
304 return []byte(contents)
305}
306
Chris Parsons8ccdb632020-11-17 15:41:01 -0500307// Returns a "canonicalized" corresponding to the given sourcetree-level label.
308// This abstraction is required because a sourcetree label such as //foo/bar:baz
309// must be referenced via the local repository prefix, such as
310// @sourceroot//foo/bar:baz.
311func canonicalizeLabel(label string) string {
312 if strings.HasPrefix(label, "//") {
313 return "@sourceroot" + label
314 } else {
315 return "@sourceroot//" + label
316 }
317}
318
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400319func (context *bazelContext) mainBuildFileContents() []byte {
320 formatString := `
321# This file is generated by soong_build. Do not edit.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500322load(":main.bzl", "mixed_build_root", "phony_root")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400323
324mixed_build_root(name = "buildroot",
325 deps = [%s],
326)
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500327
328phony_root(name = "phonyroot",
329 deps = [":buildroot"],
330)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400331`
332 var buildRootDeps []string = nil
333 for val, _ := range context.requests {
Chris Parsons8ccdb632020-11-17 15:41:01 -0500334 buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label)))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400335 }
336 buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
337
338 return []byte(fmt.Sprintf(formatString, buildRootDepsString))
339}
340
341func (context *bazelContext) cqueryStarlarkFileContents() []byte {
342 formatString := `
343# This file is generated by soong_build. Do not edit.
344getAllFilesLabels = {
345 %s
346}
347
348def format(target):
349 if str(target.label) in getAllFilesLabels:
350 return str(target.label) + ">>" + ', '.join([f.path for f in target.files.to_list()])
351 else:
352 # This target was not requested via cquery, and thus must be a dependency
353 # of a requested target.
354 return ""
355`
356 var buildRootDeps []string = nil
357 // TODO(cparsons): Sort by request type instead of assuming all requests
358 // are of GetAllFiles type.
359 for val, _ := range context.requests {
Chris Parsons8ccdb632020-11-17 15:41:01 -0500360 buildRootDeps = append(buildRootDeps, fmt.Sprintf("\"%s\" : True", canonicalizeLabel(val.label)))
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400361 }
362 buildRootDepsString := strings.Join(buildRootDeps, ",\n ")
363
364 return []byte(fmt.Sprintf(formatString, buildRootDepsString))
365}
366
Chris Parsons8ccdb632020-11-17 15:41:01 -0500367// Returns a workspace-relative path containing build-related metadata required
368// for interfacing with Bazel. Example: out/soong/bazel.
369func (context *bazelContext) intermediatesDir() string {
370 return filepath.Join(context.buildDir, "bazel")
371}
372
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400373// Issues commands to Bazel to receive results for all cquery requests
374// queued in the BazelContext.
375func (context *bazelContext) InvokeBazel() error {
376 context.results = make(map[cqueryKey]string)
377
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400378 var cqueryOutput string
379 var err error
Chris Parsons8ccdb632020-11-17 15:41:01 -0500380
Chris Parsons07c1e4a2021-01-19 17:19:16 -0500381 intermediatesDirPath := absolutePath(context.intermediatesDir())
382 if _, err := os.Stat(intermediatesDirPath); os.IsNotExist(err) {
383 err = os.Mkdir(intermediatesDirPath, 0777)
384 }
385
Chris Parsons8ccdb632020-11-17 15:41:01 -0500386 if err != nil {
387 return err
388 }
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400389 err = ioutil.WriteFile(
Chris Parsons8ccdb632020-11-17 15:41:01 -0500390 absolutePath(filepath.Join(context.intermediatesDir(), "main.bzl")),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400391 context.mainBzlFileContents(), 0666)
392 if err != nil {
393 return err
394 }
395 err = ioutil.WriteFile(
Chris Parsons8ccdb632020-11-17 15:41:01 -0500396 absolutePath(filepath.Join(context.intermediatesDir(), "BUILD.bazel")),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400397 context.mainBuildFileContents(), 0666)
398 if err != nil {
399 return err
400 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800401 cqueryFileRelpath := filepath.Join(context.intermediatesDir(), "buildroot.cquery")
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400402 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800403 absolutePath(cqueryFileRelpath),
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400404 context.cqueryStarlarkFileContents(), 0666)
405 if err != nil {
406 return err
407 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800408 workspaceFileRelpath := filepath.Join(context.intermediatesDir(), "WORKSPACE.bazel")
Chris Parsons8ccdb632020-11-17 15:41:01 -0500409 err = ioutil.WriteFile(
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800410 absolutePath(workspaceFileRelpath),
Chris Parsons8ccdb632020-11-17 15:41:01 -0500411 context.workspaceFileContents(), 0666)
412 if err != nil {
413 return err
414 }
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800415 buildrootLabel := "//:buildroot"
Patrice Arruda05ab2d02020-12-12 06:24:26 +0000416 cqueryOutput, err = context.issueBazelCommand(bazel.CqueryBuildRootRunName, "cquery",
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800417 []string{fmt.Sprintf("deps(%s)", buildrootLabel)},
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400418 "--output=starlark",
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800419 "--starlark:file="+cqueryFileRelpath)
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400420
421 if err != nil {
422 return err
423 }
424
425 cqueryResults := map[string]string{}
426 for _, outputLine := range strings.Split(cqueryOutput, "\n") {
427 if strings.Contains(outputLine, ">>") {
428 splitLine := strings.SplitN(outputLine, ">>", 2)
429 cqueryResults[splitLine[0]] = splitLine[1]
430 }
431 }
432
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400433 for val, _ := range context.requests {
Chris Parsons8ccdb632020-11-17 15:41:01 -0500434 if cqueryResult, ok := cqueryResults[canonicalizeLabel(val.label)]; ok {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400435 context.results[val] = string(cqueryResult)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400436 } else {
Chris Parsonsb0f8ac42020-10-23 16:48:08 -0400437 return fmt.Errorf("missing result for bazel target %s", val.label)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400438 }
439 }
440
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500441 // Issue an aquery command to retrieve action information about the bazel build tree.
442 //
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400443 // TODO(cparsons): Use --target_pattern_file to avoid command line limits.
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500444 var aqueryOutput string
445 aqueryOutput, err = context.issueBazelCommand(bazel.AqueryBuildRootRunName, "aquery",
Jaewoong Jung18aefc12020-12-21 09:11:10 -0800446 []string{fmt.Sprintf("deps(%s)", buildrootLabel),
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500447 // Use jsonproto instead of proto; actual proto parsing would require a dependency on Bazel's
448 // proto sources, which would add a number of unnecessary dependencies.
449 "--output=jsonproto"})
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400450
451 if err != nil {
452 return err
453 }
454
Chris Parsons4f069892021-01-15 12:22:41 -0500455 context.buildStatements, err = bazel.AqueryBuildStatements([]byte(aqueryOutput))
456 if err != nil {
457 return err
458 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500459
460 // Issue a build command of the phony root to generate symlink forests for dependencies of the
461 // Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
462 // but some of symlinks may be required to resolve source dependencies of the build.
463 _, err = context.issueBazelCommand(bazel.BazelBuildPhonyRootRunName, "build",
464 []string{"//:phonyroot"})
465
466 if err != nil {
467 return err
468 }
469
470 fmt.Printf("Build statements %s", context.buildStatements)
Chris Parsonsf3c96ef2020-09-29 02:23:17 -0400471 // Clear requests.
472 context.requests = map[cqueryKey]bool{}
473 return nil
474}
Chris Parsonsa798d962020-10-12 23:44:08 -0400475
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500476func (context *bazelContext) BuildStatementsToRegister() []bazel.BuildStatement {
477 return context.buildStatements
478}
479
480func (context *bazelContext) OutputBase() string {
481 return context.outputBase
482}
483
Chris Parsonsa798d962020-10-12 23:44:08 -0400484// Singleton used for registering BUILD file ninja dependencies (needed
485// for correctness of builds which use Bazel.
486func BazelSingleton() Singleton {
487 return &bazelSingleton{}
488}
489
490type bazelSingleton struct{}
491
492func (c *bazelSingleton) GenerateBuildActions(ctx SingletonContext) {
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500493 // bazelSingleton is a no-op if mixed-soong-bazel-builds are disabled.
494 if !ctx.Config().BazelContext.BazelEnabled() {
495 return
496 }
Chris Parsonsa798d962020-10-12 23:44:08 -0400497
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500498 // Add ninja file dependencies for files which all bazel invocations require.
499 bazelBuildList := absolutePath(filepath.Join(
500 filepath.Dir(bootstrap.ModuleListFile), "bazel.list"))
501 ctx.AddNinjaFileDeps(bazelBuildList)
502
503 data, err := ioutil.ReadFile(bazelBuildList)
504 if err != nil {
505 ctx.Errorf(err.Error())
506 }
507 files := strings.Split(strings.TrimSpace(string(data)), "\n")
508 for _, file := range files {
509 ctx.AddNinjaFileDeps(file)
510 }
511
512 // Register bazel-owned build statements (obtained from the aquery invocation).
513 for index, buildStatement := range ctx.Config().BazelContext.BuildStatementsToRegister() {
514 rule := NewRuleBuilder(pctx, ctx)
515 cmd := rule.Command()
516 cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
517 ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
518
519 for _, outputPath := range buildStatement.OutputPaths {
520 cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400521 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500522 for _, inputPath := range buildStatement.InputPaths {
523 cmd.Implicit(PathForBazelOut(ctx, inputPath))
Chris Parsonsa798d962020-10-12 23:44:08 -0400524 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500525
526 // This is required to silence warnings pertaining to unexpected timestamps. Particularly,
527 // some Bazel builtins (such as files in the bazel_tools directory) have far-future
528 // timestamps. Without restat, Ninja would emit warnings that the input files of a
529 // build statement have later timestamps than the outputs.
530 rule.Restat()
531
Liz Kammer13548d72020-12-16 11:13:30 -0800532 rule.Build(fmt.Sprintf("bazel %d", index), buildStatement.Mnemonic)
Chris Parsonsa798d962020-10-12 23:44:08 -0400533 }
534}