blob: 05f6ed48c4b4e537374aa96d12481a56b396d13f [file] [log] [blame]
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -05001// 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 bazel
16
17import (
Chris Parsons0bfb1c02022-05-12 16:43:01 -040018 "crypto/sha256"
Usta Shrestha2ccdb422022-06-02 10:19:13 -040019 "encoding/base64"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050020 "encoding/json"
Chris Parsonsaffbb602020-12-23 12:02:11 -050021 "fmt"
22 "path/filepath"
Chris Parsons0bfb1c02022-05-12 16:43:01 -040023 "reflect"
Chris Parsons0bfb1c02022-05-12 16:43:01 -040024 "sort"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050025 "strings"
26
27 "github.com/google/blueprint/proptools"
28)
29
Usta Shrestha6298cc52022-05-27 17:40:21 -040030type artifactId int
31type depsetId int
32type pathFragmentId int
33
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050034// artifact contains relevant portions of Bazel's aquery proto, Artifact.
35// Represents a single artifact, whether it's a source file or a derived output file.
36type artifact struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040037 Id artifactId
38 PathFragmentId pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050039}
40
41type pathFragment struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040042 Id pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050043 Label string
Usta Shrestha6298cc52022-05-27 17:40:21 -040044 ParentId pathFragmentId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050045}
46
47// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
48type KeyValuePair struct {
49 Key string
50 Value string
51}
52
Chris Parsons1a7aca02022-04-25 22:35:15 -040053// AqueryDepset is a depset definition from Bazel's aquery response. This is
Chris Parsons0bfb1c02022-05-12 16:43:01 -040054// akin to the `depSetOfFiles` in the response proto, except:
Colin Crossd079e0b2022-08-16 10:27:33 -070055// - direct artifacts are enumerated by full path instead of by ID
56// - it has a hash of the depset contents, instead of an int ID (for determinism)
57//
Chris Parsons1a7aca02022-04-25 22:35:15 -040058// A depset is a data structure for efficient transitive handling of artifact
59// paths. A single depset consists of one or more artifact paths and one or
60// more "child" depsets.
61type AqueryDepset struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -040062 ContentHash string
63 DirectArtifacts []string
64 TransitiveDepSetHashes []string
Chris Parsons1a7aca02022-04-25 22:35:15 -040065}
66
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050067// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
68// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
69// data structure for storing large numbers of file paths.
70type depSetOfFiles struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040071 Id depsetId
72 DirectArtifactIds []artifactId
73 TransitiveDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050074}
75
76// action contains relevant portions of Bazel's aquery proto, Action.
77// Represents a single command line invocation in the Bazel build graph.
78type action struct {
79 Arguments []string
80 EnvironmentVariables []KeyValuePair
Usta Shrestha6298cc52022-05-27 17:40:21 -040081 InputDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050082 Mnemonic string
Usta Shrestha6298cc52022-05-27 17:40:21 -040083 OutputIds []artifactId
Wei Li455ba832021-11-04 22:58:12 +000084 TemplateContent string
85 Substitutions []KeyValuePair
Sasha Smundak1da064c2022-06-08 16:36:16 -070086 FileContents string
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050087}
88
89// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
90// An aquery response from Bazel contains a single ActionGraphContainer proto.
91type actionGraphContainer struct {
92 Artifacts []artifact
93 Actions []action
94 DepSetOfFiles []depSetOfFiles
Chris Parsonsaffbb602020-12-23 12:02:11 -050095 PathFragments []pathFragment
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050096}
97
98// BuildStatement contains information to register a build statement corresponding (one to one)
99// with a Bazel action from Bazel's action graph.
100type BuildStatement struct {
Liz Kammerc49e6822021-06-08 15:04:11 -0400101 Command string
102 Depfile *string
103 OutputPaths []string
Liz Kammerc49e6822021-06-08 15:04:11 -0400104 SymlinkPaths []string
105 Env []KeyValuePair
106 Mnemonic string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400107
108 // Inputs of this build statement, either as unexpanded depsets or expanded
109 // input paths. There should be no overlap between these fields; an input
110 // path should either be included as part of an unexpanded depset or a raw
111 // input path string, but not both.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400112 InputDepsetHashes []string
113 InputPaths []string
Sasha Smundak1da064c2022-06-08 16:36:16 -0700114 FileContents string
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500115}
116
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400117// A helper type for aquery processing which facilitates retrieval of path IDs from their
118// less readable Bazel structures (depset and path fragment).
119type aqueryArtifactHandler struct {
Usta Shresthaef922252022-06-02 14:23:02 -0400120 // Switches to true if any depset contains only `bazelToolsDependencySentinel`
121 bazelToolsDependencySentinelNeeded bool
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400122 // Maps depset id to AqueryDepset, a representation of depset which is
123 // post-processed for middleman artifact handling, unhandled artifact
124 // dropping, content hashing, etc.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400125 depsetIdToAqueryDepset map[depsetId]AqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400126 // Maps content hash to AqueryDepset.
127 depsetHashToAqueryDepset map[string]AqueryDepset
128
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400129 // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
130 // may be an expensive operation.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400131 depsetHashToArtifactPathsCache map[string][]string
Usta Shrestha6298cc52022-05-27 17:40:21 -0400132 // Maps artifact ids to fully expanded paths.
133 artifactIdToPath map[artifactId]string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400134}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500135
Wei Li455ba832021-11-04 22:58:12 +0000136// The tokens should be substituted with the value specified here, instead of the
137// one returned in 'substitutions' of TemplateExpand action.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400138var templateActionOverriddenTokens = map[string]string{
Wei Li455ba832021-11-04 22:58:12 +0000139 // Uses "python3" for %python_binary% instead of the value returned by aquery
140 // which is "py3wrapper.sh". See removePy3wrapperScript.
141 "%python_binary%": "python3",
142}
143
Wei Li455ba832021-11-04 22:58:12 +0000144// The file name of py3wrapper.sh, which is used by py_binary targets.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400145const py3wrapperFileName = "/py3wrapper.sh"
Wei Li455ba832021-11-04 22:58:12 +0000146
Usta Shresthaef922252022-06-02 14:23:02 -0400147// A file to be put into depsets that are otherwise empty
148const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
149
Usta Shrestha6298cc52022-05-27 17:40:21 -0400150func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
151 m := map[K]V{}
152 for _, v := range values {
153 m[keyFn(v)] = v
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500154 }
Usta Shrestha6298cc52022-05-27 17:40:21 -0400155 return m
156}
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400157
Usta Shrestha6298cc52022-05-27 17:40:21 -0400158func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
159 pathFragments := indexBy(aqueryResult.PathFragments, func(pf pathFragment) pathFragmentId {
160 return pf.Id
161 })
162
163 artifactIdToPath := map[artifactId]string{}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500164 for _, artifact := range aqueryResult.Artifacts {
165 artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
166 if err != nil {
Chris Parsons4f069892021-01-15 12:22:41 -0500167 return nil, err
Chris Parsonsaffbb602020-12-23 12:02:11 -0500168 }
169 artifactIdToPath[artifact.Id] = artifactPath
170 }
Chris Parsons943f2432021-01-19 11:36:50 -0500171
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400172 // Map middleman artifact ContentHash to input artifact depset ID.
Chris Parsons1a7aca02022-04-25 22:35:15 -0400173 // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
Usta Shrestha16ac1352022-06-22 11:01:55 -0400174 // if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400175 // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
176 // that action instead.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400177 middlemanIdToDepsetIds := map[artifactId][]depsetId{}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500178 for _, actionEntry := range aqueryResult.Actions {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500179 if actionEntry.Mnemonic == "Middleman" {
180 for _, outputId := range actionEntry.OutputIds {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400181 middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds
Chris Parsons8d6e4332021-02-22 16:13:50 -0500182 }
183 }
184 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400185
Usta Shrestha6298cc52022-05-27 17:40:21 -0400186 depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d depSetOfFiles) depsetId {
187 return d.Id
188 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400189
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400190 aqueryHandler := aqueryArtifactHandler{
Usta Shrestha6298cc52022-05-27 17:40:21 -0400191 depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400192 depsetHashToAqueryDepset: map[string]AqueryDepset{},
193 depsetHashToArtifactPathsCache: map[string][]string{},
194 artifactIdToPath: artifactIdToPath,
195 }
196
197 // Validate and adjust aqueryResult.DepSetOfFiles values.
198 for _, depset := range aqueryResult.DepSetOfFiles {
199 _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
200 if err != nil {
201 return nil, err
202 }
203 }
204
205 return &aqueryHandler, nil
206}
207
208// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
209// depset.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400210func (a *aqueryArtifactHandler) populateDepsetMaps(depset depSetOfFiles, middlemanIdToDepsetIds map[artifactId][]depsetId, depsetIdToDepset map[depsetId]depSetOfFiles) (AqueryDepset, error) {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400211 if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depset.Id]; containsDepset {
212 return aqueryDepset, nil
213 }
214 transitiveDepsetIds := depset.TransitiveDepSetIds
Usta Shrestha6298cc52022-05-27 17:40:21 -0400215 var directArtifactPaths []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400216 for _, artifactId := range depset.DirectArtifactIds {
217 path, pathExists := a.artifactIdToPath[artifactId]
218 if !pathExists {
219 return AqueryDepset{}, fmt.Errorf("undefined input artifactId %d", artifactId)
220 }
221 // Filter out any inputs which are universally dropped, and swap middleman
222 // artifacts with their corresponding depsets.
223 if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
224 // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
225 transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
Usta Shresthaef922252022-06-02 14:23:02 -0400226 } else if strings.HasSuffix(path, py3wrapperFileName) ||
Usta Shresthaef922252022-06-02 14:23:02 -0400227 strings.HasPrefix(path, "../bazel_tools") {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400228 // Drop these artifacts.
229 // See go/python-binary-host-mixed-build for more details.
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700230 // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
231 // TemplateExpandAction handles everything necessary to launch a Pythin application.
232 // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
Usta Shresthaef922252022-06-02 14:23:02 -0400233 // containing depset to always be considered newer than their outputs.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400234 } else {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400235 directArtifactPaths = append(directArtifactPaths, path)
236 }
237 }
238
Usta Shrestha6298cc52022-05-27 17:40:21 -0400239 var childDepsetHashes []string
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400240 for _, childDepsetId := range transitiveDepsetIds {
241 childDepset, exists := depsetIdToDepset[childDepsetId]
242 if !exists {
243 return AqueryDepset{}, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
244 }
245 childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset)
246 if err != nil {
247 return AqueryDepset{}, err
248 }
249 childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
250 }
Usta Shresthaef922252022-06-02 14:23:02 -0400251 if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
252 // We could omit this depset altogether but that requires cleanup on
253 // transitive dependents.
254 // As a simpler alternative, we use this sentinel file as a dependency.
255 directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
256 a.bazelToolsDependencySentinelNeeded = true
257 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400258 aqueryDepset := AqueryDepset{
259 ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
260 DirectArtifacts: directArtifactPaths,
261 TransitiveDepSetHashes: childDepsetHashes,
262 }
263 a.depsetIdToAqueryDepset[depset.Id] = aqueryDepset
264 a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
265 return aqueryDepset, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400266}
267
Chris Parsons1a7aca02022-04-25 22:35:15 -0400268// getInputPaths flattens the depsets of the given IDs and returns all transitive
269// input paths contained in these depsets.
270// This is a potentially expensive operation, and should not be invoked except
271// for actions which need specialized input handling.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400272func (a *aqueryArtifactHandler) getInputPaths(depsetIds []depsetId) ([]string, error) {
273 var inputPaths []string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400274
275 for _, inputDepSetId := range depsetIds {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400276 depset := a.depsetIdToAqueryDepset[inputDepSetId]
277 inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400278 if err != nil {
279 return nil, err
280 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400281 for _, inputPath := range inputArtifacts {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400282 inputPaths = append(inputPaths, inputPath)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400283 }
284 }
Wei Li455ba832021-11-04 22:58:12 +0000285
Chris Parsons1a7aca02022-04-25 22:35:15 -0400286 return inputPaths, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400287}
288
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400289func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
290 if result, exists := a.depsetHashToArtifactPathsCache[depsetHash]; exists {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400291 return result, nil
292 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400293 if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
294 result := depset.DirectArtifacts
295 for _, childHash := range depset.TransitiveDepSetHashes {
296 childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400297 if err != nil {
298 return nil, err
299 }
300 result = append(result, childArtifactIds...)
301 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400302 a.depsetHashToArtifactPathsCache[depsetHash] = result
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400303 return result, nil
304 } else {
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400305 return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400306 }
307}
308
Chris Parsons1a7aca02022-04-25 22:35:15 -0400309// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
Usta Shrestha6298cc52022-05-27 17:40:21 -0400310// which should be registered (and output to a ninja file) to correspond with Bazel's
Chris Parsons1a7aca02022-04-25 22:35:15 -0400311// action graph, as described by the given action graph json proto.
312// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
313// are one-to-one with Bazel's depSetOfFiles objects.
314func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, []AqueryDepset, error) {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400315 var aqueryResult actionGraphContainer
316 err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
317 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400318 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400319 }
320 aqueryHandler, err := newAqueryHandler(aqueryResult)
321 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400322 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400323 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500324
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400325 var buildStatements []BuildStatement
Usta Shresthaef922252022-06-02 14:23:02 -0400326 if aqueryHandler.bazelToolsDependencySentinelNeeded {
327 buildStatements = append(buildStatements, BuildStatement{
328 Command: fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
329 OutputPaths: []string{bazelToolsDependencySentinel},
330 Mnemonic: bazelToolsDependencySentinel,
331 })
332 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400333
Chris Parsons8d6e4332021-02-22 16:13:50 -0500334 for _, actionEntry := range aqueryResult.Actions {
335 if shouldSkipAction(actionEntry) {
336 continue
337 }
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400338
Chris Parsons1a7aca02022-04-25 22:35:15 -0400339 var buildStatement BuildStatement
Sasha Smundak1da064c2022-06-08 16:36:16 -0700340 if actionEntry.isSymlinkAction() {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400341 buildStatement, err = aqueryHandler.symlinkActionBuildStatement(actionEntry)
Sasha Smundak1da064c2022-06-08 16:36:16 -0700342 } else if actionEntry.isTemplateExpandAction() && len(actionEntry.Arguments) < 1 {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400343 buildStatement, err = aqueryHandler.templateExpandActionBuildStatement(actionEntry)
Sasha Smundak1da064c2022-06-08 16:36:16 -0700344 } else if actionEntry.isFileWriteAction() {
345 buildStatement, err = aqueryHandler.fileWriteActionBuildStatement(actionEntry)
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700346 } else if actionEntry.isSymlinkTreeAction() {
347 buildStatement, err = aqueryHandler.symlinkTreeActionBuildStatement(actionEntry)
Liz Kammerc49e6822021-06-08 15:04:11 -0400348 } else if len(actionEntry.Arguments) < 1 {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400349 return nil, nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
350 } else {
351 buildStatement, err = aqueryHandler.normalActionBuildStatement(actionEntry)
352 }
353
354 if err != nil {
355 return nil, nil, err
Chris Parsons8d6e4332021-02-22 16:13:50 -0500356 }
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500357 buildStatements = append(buildStatements, buildStatement)
358 }
359
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400360 depsetsByHash := map[string]AqueryDepset{}
Usta Shrestha6298cc52022-05-27 17:40:21 -0400361 var depsets []AqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400362 for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
363 if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
364 // Two depsets collide on hash. Ensure that their contents are identical.
365 if !reflect.DeepEqual(aqueryDepset, prevEntry) {
Usta Shrestha16ac1352022-06-22 11:01:55 -0400366 return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400367 }
368 } else {
369 depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
370 depsets = append(depsets, aqueryDepset)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400371 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400372 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400373
374 // Build Statements and depsets must be sorted by their content hash to
375 // preserve determinism between builds (this will result in consistent ninja file
376 // output). Note they are not sorted by their original IDs nor their Bazel ordering,
377 // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
378 sort.Slice(buildStatements, func(i, j int) bool {
379 // For build statements, compare output lists. In Bazel, each output file
380 // may only have one action which generates it, so this will provide
381 // a deterministic ordering.
382 outputs_i := buildStatements[i].OutputPaths
383 outputs_j := buildStatements[j].OutputPaths
384 if len(outputs_i) != len(outputs_j) {
385 return len(outputs_i) < len(outputs_j)
386 }
387 if len(outputs_i) == 0 {
388 // No outputs for these actions, so compare commands.
389 return buildStatements[i].Command < buildStatements[j].Command
390 }
391 // There may be multiple outputs, but the output ordering is deterministic.
392 return outputs_i[0] < outputs_j[0]
393 })
394 sort.Slice(depsets, func(i, j int) bool {
395 return depsets[i].ContentHash < depsets[j].ContentHash
396 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400397 return buildStatements, depsets, nil
398}
399
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400400// depsetContentHash computes and returns a SHA256 checksum of the contents of
401// the given depset. This content hash may serve as the depset's identifier.
402// Using a content hash for an identifier is superior for determinism. (For example,
403// using an integer identifier which depends on the order in which the depsets are
404// created would result in nondeterministic depset IDs.)
405func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
406 h := sha256.New()
407 // Use newline as delimiter, as paths cannot contain newline.
408 h.Write([]byte(strings.Join(directPaths, "\n")))
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400409 h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
410 fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400411 return fullHash
412}
413
Usta Shrestha6298cc52022-05-27 17:40:21 -0400414func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []depsetId) ([]string, error) {
415 var hashes []string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400416 for _, depsetId := range inputDepsetIds {
Usta Shresthac2372492022-05-27 10:45:00 -0400417 if aqueryDepset, exists := a.depsetIdToAqueryDepset[depsetId]; !exists {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400418 return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400419 } else {
420 hashes = append(hashes, aqueryDepset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400421 }
422 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400423 return hashes, nil
Chris Parsons1a7aca02022-04-25 22:35:15 -0400424}
425
Usta Shresthac2372492022-05-27 10:45:00 -0400426func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry action) (BuildStatement, error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400427 command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
Usta Shresthac2372492022-05-27 10:45:00 -0400428 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400429 if err != nil {
430 return BuildStatement{}, err
431 }
Usta Shresthac2372492022-05-27 10:45:00 -0400432 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400433 if err != nil {
434 return BuildStatement{}, err
435 }
436
437 buildStatement := BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400438 Command: command,
439 Depfile: depfile,
440 OutputPaths: outputPaths,
441 InputDepsetHashes: inputDepsetHashes,
442 Env: actionEntry.EnvironmentVariables,
443 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400444 }
445 return buildStatement, nil
446}
447
Usta Shresthac2372492022-05-27 10:45:00 -0400448func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry action) (BuildStatement, error) {
449 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400450 if err != nil {
451 return BuildStatement{}, err
452 }
453 if len(outputPaths) != 1 {
454 return BuildStatement{}, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
455 }
456 expandedTemplateContent := expandTemplateContent(actionEntry)
457 // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
458 // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
459 // change \n to space and mess up the format of Python programs.
460 // sed is used to convert \\n back to \n before saving to output file.
461 // See go/python-binary-host-mixed-build for more details.
462 command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
463 escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
Usta Shresthac2372492022-05-27 10:45:00 -0400464 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400465 if err != nil {
466 return BuildStatement{}, err
467 }
468
469 buildStatement := BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400470 Command: command,
471 Depfile: depfile,
472 OutputPaths: outputPaths,
473 InputDepsetHashes: inputDepsetHashes,
474 Env: actionEntry.EnvironmentVariables,
475 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400476 }
477 return buildStatement, nil
478}
479
Sasha Smundak1da064c2022-06-08 16:36:16 -0700480func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry action) (BuildStatement, error) {
481 outputPaths, _, err := a.getOutputPaths(actionEntry)
482 var depsetHashes []string
483 if err == nil {
484 depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
485 }
486 if err != nil {
487 return BuildStatement{}, err
488 }
489 return BuildStatement{
490 Depfile: nil,
491 OutputPaths: outputPaths,
492 Env: actionEntry.EnvironmentVariables,
493 Mnemonic: actionEntry.Mnemonic,
494 InputDepsetHashes: depsetHashes,
495 FileContents: actionEntry.FileContents,
496 }, nil
497}
498
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700499func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry action) (BuildStatement, error) {
500 outputPaths, _, err := a.getOutputPaths(actionEntry)
501 if err != nil {
502 return BuildStatement{}, err
503 }
504 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
505 if err != nil {
506 return BuildStatement{}, err
507 }
508 if len(inputPaths) != 1 || len(outputPaths) != 1 {
509 return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
510 }
511 // The actual command is generated in bazelSingleton.GenerateBuildActions
512 return BuildStatement{
513 Depfile: nil,
514 OutputPaths: outputPaths,
515 Env: actionEntry.EnvironmentVariables,
516 Mnemonic: actionEntry.Mnemonic,
517 InputPaths: inputPaths,
518 }, nil
519}
520
Usta Shresthac2372492022-05-27 10:45:00 -0400521func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry action) (BuildStatement, error) {
522 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400523 if err != nil {
524 return BuildStatement{}, err
525 }
526
Usta Shresthac2372492022-05-27 10:45:00 -0400527 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400528 if err != nil {
529 return BuildStatement{}, err
530 }
531 if len(inputPaths) != 1 || len(outputPaths) != 1 {
532 return BuildStatement{}, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
533 }
534 out := outputPaths[0]
535 outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
536 out = proptools.ShellEscapeIncludingSpaces(out)
537 in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
538 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
539 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
540 symlinkPaths := outputPaths[:]
541
542 buildStatement := BuildStatement{
543 Command: command,
544 Depfile: depfile,
545 OutputPaths: outputPaths,
546 InputPaths: inputPaths,
547 Env: actionEntry.EnvironmentVariables,
548 Mnemonic: actionEntry.Mnemonic,
549 SymlinkPaths: symlinkPaths,
550 }
551 return buildStatement, nil
552}
553
Usta Shresthac2372492022-05-27 10:45:00 -0400554func (a *aqueryArtifactHandler) getOutputPaths(actionEntry action) (outputPaths []string, depfile *string, err error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400555 for _, outputId := range actionEntry.OutputIds {
Usta Shresthac2372492022-05-27 10:45:00 -0400556 outputPath, exists := a.artifactIdToPath[outputId]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400557 if !exists {
558 err = fmt.Errorf("undefined outputId %d", outputId)
559 return
560 }
561 ext := filepath.Ext(outputPath)
562 if ext == ".d" {
563 if depfile != nil {
564 err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
565 return
566 } else {
567 depfile = &outputPath
568 }
569 } else {
570 outputPaths = append(outputPaths, outputPath)
571 }
572 }
573 return
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500574}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500575
Wei Li455ba832021-11-04 22:58:12 +0000576// expandTemplateContent substitutes the tokens in a template.
577func expandTemplateContent(actionEntry action) string {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700578 var replacerString []string
Wei Li455ba832021-11-04 22:58:12 +0000579 for _, pair := range actionEntry.Substitutions {
580 value := pair.Value
Usta Shrestha6298cc52022-05-27 17:40:21 -0400581 if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
Wei Li455ba832021-11-04 22:58:12 +0000582 value = val
583 }
584 replacerString = append(replacerString, pair.Key, value)
585 }
586 replacer := strings.NewReplacer(replacerString...)
587 return replacer.Replace(actionEntry.TemplateContent)
588}
589
590func escapeCommandlineArgument(str string) string {
591 // \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
592 replacer := strings.NewReplacer(
593 `\`, `\\`,
594 `$`, `\$`,
595 "`", "\\`",
596 `"`, `\"`,
597 "\n", "\\n",
598 `'`, `'"'"'`,
599 )
600 return replacer.Replace(str)
601}
602
Sasha Smundak1da064c2022-06-08 16:36:16 -0700603func (a action) isSymlinkAction() bool {
Trevor Radcliffeef9c9002022-05-13 20:55:35 +0000604 return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink" || a.Mnemonic == "ExecutableSymlink"
Liz Kammerc49e6822021-06-08 15:04:11 -0400605}
606
Sasha Smundak1da064c2022-06-08 16:36:16 -0700607func (a action) isTemplateExpandAction() bool {
Wei Li455ba832021-11-04 22:58:12 +0000608 return a.Mnemonic == "TemplateExpand"
609}
610
Sasha Smundak1da064c2022-06-08 16:36:16 -0700611func (a action) isFileWriteAction() bool {
612 return a.Mnemonic == "FileWrite" || a.Mnemonic == "SourceSymlinkManifest"
613}
614
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700615func (a action) isSymlinkTreeAction() bool {
616 return a.Mnemonic == "SymlinkTree"
617}
618
Chris Parsons8d6e4332021-02-22 16:13:50 -0500619func shouldSkipAction(a action) bool {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400620 // Middleman actions are not handled like other actions; they are handled separately as a
621 // preparatory step so that their inputs may be relayed to actions depending on middleman
622 // artifacts.
Chris Parsons8d6e4332021-02-22 16:13:50 -0500623 if a.Mnemonic == "Middleman" {
624 return true
625 }
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700626 // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
627 if a.Mnemonic == "PythonZipper" {
628 return true
629 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500630 // Skip "Fail" actions, which are placeholder actions designed to always fail.
631 if a.Mnemonic == "Fail" {
632 return true
633 }
Yu Liu8d82ac52022-05-17 15:13:28 -0700634 if a.Mnemonic == "BaselineCoverage" {
635 return true
636 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500637 return false
638}
639
Usta Shrestha6298cc52022-05-27 17:40:21 -0400640func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]pathFragment) (string, error) {
641 var labels []string
Chris Parsonsaffbb602020-12-23 12:02:11 -0500642 currId := id
643 // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
644 for currId > 0 {
645 currFragment, ok := pathFragmentsMap[currId]
646 if !ok {
Chris Parsons4f069892021-01-15 12:22:41 -0500647 return "", fmt.Errorf("undefined path fragment id %d", currId)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500648 }
649 labels = append([]string{currFragment.Label}, labels...)
Liz Kammerc49e6822021-06-08 15:04:11 -0400650 if currId == currFragment.ParentId {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700651 return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
Liz Kammerc49e6822021-06-08 15:04:11 -0400652 }
Chris Parsonsaffbb602020-12-23 12:02:11 -0500653 currId = currFragment.ParentId
654 }
655 return filepath.Join(labels...), nil
656}