blob: 7195a97a6510ebddff09a88825cc7175e51c01ed [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"
Cole Faustbc65a3f2023-08-01 16:38:55 +000020 "encoding/json"
ustaa79afd72023-09-22 17:29:56 -040021 "errors"
Chris Parsonsaffbb602020-12-23 12:02:11 -050022 "fmt"
23 "path/filepath"
Chris Parsons0bfb1c02022-05-12 16:43:01 -040024 "reflect"
Chris Parsons0bfb1c02022-05-12 16:43:01 -040025 "sort"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050026 "strings"
Liz Kammera4655a92023-02-10 17:17:28 -050027 "sync"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050028
ustaa79afd72023-09-22 17:29:56 -040029 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
30
Liz Kammer690fbac2023-02-10 11:11:17 -050031 "github.com/google/blueprint/metrics"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050032 "github.com/google/blueprint/proptools"
Jason Wu118fd2b2022-10-27 18:41:15 +000033 "google.golang.org/protobuf/proto"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050034)
35
Usta Shrestha6298cc52022-05-27 17:40:21 -040036type artifactId int
37type depsetId int
38type pathFragmentId int
39
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050040// artifact contains relevant portions of Bazel's aquery proto, Artifact.
41// Represents a single artifact, whether it's a source file or a derived output file.
42type artifact struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040043 Id artifactId
44 PathFragmentId pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050045}
46
47type pathFragment struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040048 Id pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050049 Label string
Usta Shrestha6298cc52022-05-27 17:40:21 -040050 ParentId pathFragmentId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050051}
52
53// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
54type KeyValuePair struct {
55 Key string
56 Value string
57}
58
Chris Parsons1a7aca02022-04-25 22:35:15 -040059// AqueryDepset is a depset definition from Bazel's aquery response. This is
Chris Parsons0bfb1c02022-05-12 16:43:01 -040060// akin to the `depSetOfFiles` in the response proto, except:
Colin Crossd079e0b2022-08-16 10:27:33 -070061// - direct artifacts are enumerated by full path instead of by ID
62// - it has a hash of the depset contents, instead of an int ID (for determinism)
63//
Chris Parsons1a7aca02022-04-25 22:35:15 -040064// A depset is a data structure for efficient transitive handling of artifact
65// paths. A single depset consists of one or more artifact paths and one or
66// more "child" depsets.
67type AqueryDepset struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -040068 ContentHash string
69 DirectArtifacts []string
70 TransitiveDepSetHashes []string
Chris Parsons1a7aca02022-04-25 22:35:15 -040071}
72
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050073// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
74// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
75// data structure for storing large numbers of file paths.
76type depSetOfFiles struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040077 Id depsetId
78 DirectArtifactIds []artifactId
79 TransitiveDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050080}
81
82// action contains relevant portions of Bazel's aquery proto, Action.
83// Represents a single command line invocation in the Bazel build graph.
84type action struct {
85 Arguments []string
86 EnvironmentVariables []KeyValuePair
Usta Shrestha6298cc52022-05-27 17:40:21 -040087 InputDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050088 Mnemonic string
Usta Shrestha6298cc52022-05-27 17:40:21 -040089 OutputIds []artifactId
Wei Li455ba832021-11-04 22:58:12 +000090 TemplateContent string
91 Substitutions []KeyValuePair
Sasha Smundak1da064c2022-06-08 16:36:16 -070092 FileContents string
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050093}
94
95// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
96// An aquery response from Bazel contains a single ActionGraphContainer proto.
97type actionGraphContainer struct {
98 Artifacts []artifact
99 Actions []action
100 DepSetOfFiles []depSetOfFiles
Chris Parsonsaffbb602020-12-23 12:02:11 -0500101 PathFragments []pathFragment
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500102}
103
104// BuildStatement contains information to register a build statement corresponding (one to one)
105// with a Bazel action from Bazel's action graph.
106type BuildStatement struct {
Liz Kammerc49e6822021-06-08 15:04:11 -0400107 Command string
108 Depfile *string
109 OutputPaths []string
Liz Kammerc49e6822021-06-08 15:04:11 -0400110 SymlinkPaths []string
Liz Kammer00629db2023-02-09 14:28:15 -0500111 Env []*analysis_v2_proto.KeyValuePair
Liz Kammerc49e6822021-06-08 15:04:11 -0400112 Mnemonic string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400113
114 // Inputs of this build statement, either as unexpanded depsets or expanded
115 // input paths. There should be no overlap between these fields; an input
116 // path should either be included as part of an unexpanded depset or a raw
117 // input path string, but not both.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400118 InputDepsetHashes []string
119 InputPaths []string
Sasha Smundak1da064c2022-06-08 16:36:16 -0700120 FileContents string
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000121 // If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
122 // and run the mixed build action there
123 ShouldRunInSbox bool
Cole Faustbc65a3f2023-08-01 16:38:55 +0000124 // A list of files to add as implicit deps to the outputs of this BuildStatement.
125 // Unlike most properties in BuildStatement, these paths must be relative to the root of
126 // the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
127 ImplicitDeps []string
Cole Faust20f20302023-08-31 11:00:25 -0700128 IsExecutable bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500129}
130
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400131// A helper type for aquery processing which facilitates retrieval of path IDs from their
132// less readable Bazel structures (depset and path fragment).
133type aqueryArtifactHandler struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400134 // Maps depset id to AqueryDepset, a representation of depset which is
135 // post-processed for middleman artifact handling, unhandled artifact
136 // dropping, content hashing, etc.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400137 depsetIdToAqueryDepset map[depsetId]AqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500138 emptyDepsetIds map[depsetId]struct{}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400139 // Maps content hash to AqueryDepset.
140 depsetHashToAqueryDepset map[string]AqueryDepset
141
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400142 // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
143 // may be an expensive operation.
Liz Kammera4655a92023-02-10 17:17:28 -0500144 depsetHashToArtifactPathsCache sync.Map
Usta Shrestha6298cc52022-05-27 17:40:21 -0400145 // Maps artifact ids to fully expanded paths.
146 artifactIdToPath map[artifactId]string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400147}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500148
Wei Li455ba832021-11-04 22:58:12 +0000149// The tokens should be substituted with the value specified here, instead of the
150// one returned in 'substitutions' of TemplateExpand action.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400151var templateActionOverriddenTokens = map[string]string{
Wei Li455ba832021-11-04 22:58:12 +0000152 // Uses "python3" for %python_binary% instead of the value returned by aquery
153 // which is "py3wrapper.sh". See removePy3wrapperScript.
154 "%python_binary%": "python3",
155}
156
Liz Kammer00629db2023-02-09 14:28:15 -0500157const (
158 middlemanMnemonic = "Middleman"
159 // The file name of py3wrapper.sh, which is used by py_binary targets.
160 py3wrapperFileName = "/py3wrapper.sh"
161)
Wei Li455ba832021-11-04 22:58:12 +0000162
Usta Shrestha6298cc52022-05-27 17:40:21 -0400163func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
164 m := map[K]V{}
165 for _, v := range values {
166 m[keyFn(v)] = v
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500167 }
Usta Shrestha6298cc52022-05-27 17:40:21 -0400168 return m
169}
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400170
Liz Kammer00629db2023-02-09 14:28:15 -0500171func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
172 pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
173 return pathFragmentId(pf.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400174 })
175
Liz Kammer00629db2023-02-09 14:28:15 -0500176 artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
Chris Parsonsaffbb602020-12-23 12:02:11 -0500177 for _, artifact := range aqueryResult.Artifacts {
Liz Kammer00629db2023-02-09 14:28:15 -0500178 artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500179 if err != nil {
Chris Parsons4f069892021-01-15 12:22:41 -0500180 return nil, err
Chris Parsonsaffbb602020-12-23 12:02:11 -0500181 }
Cole Faustbdee8f92023-09-07 14:28:51 -0700182 if artifact.IsTreeArtifact &&
183 !strings.HasPrefix(artifactPath, "bazel-out/io_bazel_rules_go/") &&
184 !strings.HasPrefix(artifactPath, "bazel-out/rules_java_builtin/") {
185 // Since we're using ninja as an executor, we can't use tree artifacts. Ninja only
186 // considers a file/directory "dirty" when it's mtime changes. Directories' mtimes will
187 // only change when a file in the directory is added/removed, but not when files in
188 // the directory are changed, or when files in subdirectories are changed/added/removed.
189 // Bazel handles this by walking the directory and generating a hash for it after the
190 // action runs, which we would have to do as well if we wanted to support these
191 // artifacts in mixed builds.
192 //
193 // However, there are some bazel built-in rules that use tree artifacts. Allow those,
194 // but keep in mind that they'll have incrementality issues.
195 return nil, fmt.Errorf("tree artifacts are currently not supported in mixed builds: " + artifactPath)
196 }
Romain Jobredeauxe3989a12023-07-19 20:58:27 +0000197 artifactIdToPath[artifactId(artifact.Id)] = artifactPath
Chris Parsonsaffbb602020-12-23 12:02:11 -0500198 }
Chris Parsons943f2432021-01-19 11:36:50 -0500199
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400200 // Map middleman artifact ContentHash to input artifact depset ID.
Chris Parsons1a7aca02022-04-25 22:35:15 -0400201 // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
Usta Shrestha16ac1352022-06-22 11:01:55 -0400202 // if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400203 // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
204 // that action instead.
Liz Kammer00629db2023-02-09 14:28:15 -0500205 middlemanIdToDepsetIds := map[artifactId][]uint32{}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500206 for _, actionEntry := range aqueryResult.Actions {
Liz Kammer00629db2023-02-09 14:28:15 -0500207 if actionEntry.Mnemonic == middlemanMnemonic {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500208 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500209 middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
Chris Parsons8d6e4332021-02-22 16:13:50 -0500210 }
211 }
212 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400213
Liz Kammer00629db2023-02-09 14:28:15 -0500214 depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
215 return depsetId(d.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400216 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400217
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400218 aqueryHandler := aqueryArtifactHandler{
Usta Shrestha6298cc52022-05-27 17:40:21 -0400219 depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400220 depsetHashToAqueryDepset: map[string]AqueryDepset{},
Liz Kammera4655a92023-02-10 17:17:28 -0500221 depsetHashToArtifactPathsCache: sync.Map{},
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500222 emptyDepsetIds: make(map[depsetId]struct{}, 0),
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400223 artifactIdToPath: artifactIdToPath,
224 }
225
226 // Validate and adjust aqueryResult.DepSetOfFiles values.
227 for _, depset := range aqueryResult.DepSetOfFiles {
228 _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
229 if err != nil {
230 return nil, err
231 }
232 }
233
234 return &aqueryHandler, nil
235}
236
237// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
238// depset.
Liz Kammer00629db2023-02-09 14:28:15 -0500239func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
240 if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500241 return &aqueryDepset, nil
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400242 }
243 transitiveDepsetIds := depset.TransitiveDepSetIds
Liz Kammer00629db2023-02-09 14:28:15 -0500244 directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
245 for _, id := range depset.DirectArtifactIds {
246 aId := artifactId(id)
247 path, pathExists := a.artifactIdToPath[aId]
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400248 if !pathExists {
Liz Kammer00629db2023-02-09 14:28:15 -0500249 return nil, fmt.Errorf("undefined input artifactId %d", aId)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400250 }
251 // Filter out any inputs which are universally dropped, and swap middleman
252 // artifacts with their corresponding depsets.
Liz Kammer00629db2023-02-09 14:28:15 -0500253 if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400254 // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
255 transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
Usta Shresthaef922252022-06-02 14:23:02 -0400256 } else if strings.HasSuffix(path, py3wrapperFileName) ||
Usta Shresthaef922252022-06-02 14:23:02 -0400257 strings.HasPrefix(path, "../bazel_tools") {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500258 continue
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400259 // Drop these artifacts.
260 // See go/python-binary-host-mixed-build for more details.
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700261 // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
262 // TemplateExpandAction handles everything necessary to launch a Pythin application.
263 // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
Usta Shresthaef922252022-06-02 14:23:02 -0400264 // containing depset to always be considered newer than their outputs.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400265 } else {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400266 directArtifactPaths = append(directArtifactPaths, path)
267 }
268 }
269
Liz Kammer00629db2023-02-09 14:28:15 -0500270 childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
271 for _, id := range transitiveDepsetIds {
272 childDepsetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400273 childDepset, exists := depsetIdToDepset[childDepsetId]
274 if !exists {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500275 if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
276 continue
277 } else {
278 return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
279 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400280 }
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500281 if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
282 return nil, err
283 } else if childAqueryDepset == nil {
284 continue
285 } else {
286 childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400287 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400288 }
Usta Shresthaef922252022-06-02 14:23:02 -0400289 if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
Liz Kammer00629db2023-02-09 14:28:15 -0500290 a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500291 return nil, nil
Usta Shresthaef922252022-06-02 14:23:02 -0400292 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400293 aqueryDepset := AqueryDepset{
294 ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
295 DirectArtifacts: directArtifactPaths,
296 TransitiveDepSetHashes: childDepsetHashes,
297 }
Liz Kammer00629db2023-02-09 14:28:15 -0500298 a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400299 a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500300 return &aqueryDepset, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400301}
302
Chris Parsons1a7aca02022-04-25 22:35:15 -0400303// getInputPaths flattens the depsets of the given IDs and returns all transitive
304// input paths contained in these depsets.
305// This is a potentially expensive operation, and should not be invoked except
306// for actions which need specialized input handling.
Liz Kammer00629db2023-02-09 14:28:15 -0500307func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400308 var inputPaths []string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400309
Liz Kammer00629db2023-02-09 14:28:15 -0500310 for _, id := range depsetIds {
311 inputDepSetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400312 depset := a.depsetIdToAqueryDepset[inputDepSetId]
313 inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400314 if err != nil {
315 return nil, err
316 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400317 for _, inputPath := range inputArtifacts {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400318 inputPaths = append(inputPaths, inputPath)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400319 }
320 }
Wei Li455ba832021-11-04 22:58:12 +0000321
Chris Parsons1a7aca02022-04-25 22:35:15 -0400322 return inputPaths, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400323}
324
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400325func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
Liz Kammera4655a92023-02-10 17:17:28 -0500326 if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
327 return result.([]string), nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400328 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400329 if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
330 result := depset.DirectArtifacts
331 for _, childHash := range depset.TransitiveDepSetHashes {
332 childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400333 if err != nil {
334 return nil, err
335 }
336 result = append(result, childArtifactIds...)
337 }
Liz Kammera4655a92023-02-10 17:17:28 -0500338 a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400339 return result, nil
340 } else {
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400341 return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400342 }
343}
344
Chris Parsons1a7aca02022-04-25 22:35:15 -0400345// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
Usta Shrestha6298cc52022-05-27 17:40:21 -0400346// which should be registered (and output to a ninja file) to correspond with Bazel's
Chris Parsons1a7aca02022-04-25 22:35:15 -0400347// action graph, as described by the given action graph json proto.
348// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
349// are one-to-one with Bazel's depSetOfFiles objects.
Liz Kammera4655a92023-02-10 17:17:28 -0500350func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
Jason Wu118fd2b2022-10-27 18:41:15 +0000351 aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
352 err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400353 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400354 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400355 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500356
Liz Kammer690fbac2023-02-10 11:11:17 -0500357 var aqueryHandler *aqueryArtifactHandler
358 {
359 eventHandler.Begin("init_handler")
360 defer eventHandler.End("init_handler")
Liz Kammer00629db2023-02-09 14:28:15 -0500361 aqueryHandler, err = newAqueryHandler(aqueryProto)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400362 if err != nil {
363 return nil, nil, err
Chris Parsons8d6e4332021-02-22 16:13:50 -0500364 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500365 }
366
Liz Kammera4655a92023-02-10 17:17:28 -0500367 // allocate both length and capacity so each goroutine can write to an index independently without
368 // any need for synchronization for slice access.
369 buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
Liz Kammer690fbac2023-02-10 11:11:17 -0500370 {
371 eventHandler.Begin("build_statements")
372 defer eventHandler.End("build_statements")
Liz Kammera4655a92023-02-10 17:17:28 -0500373 wg := sync.WaitGroup{}
374 var errOnce sync.Once
375
376 for i, actionEntry := range aqueryProto.Actions {
377 wg.Add(1)
378 go func(i int, actionEntry *analysis_v2_proto.Action) {
ustaa79afd72023-09-22 17:29:56 -0400379 if buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry); aErr != nil {
Liz Kammera4655a92023-02-10 17:17:28 -0500380 errOnce.Do(func() {
ustaa79afd72023-09-22 17:29:56 -0400381 for _, t := range aqueryProto.Targets {
382 if t.GetId() == actionEntry.GetTargetId() {
383 aErr = fmt.Errorf("%s: [%s] [%s]", aErr.Error(), actionEntry.GetMnemonic(), t.GetLabel())
384 break
385 }
386 }
Liz Kammera4655a92023-02-10 17:17:28 -0500387 err = aErr
388 })
389 } else {
390 // set build statement at an index rather than appending such that each goroutine does not
391 // impact other goroutines
392 buildStatements[i] = buildStatement
393 }
394 wg.Done()
395 }(i, actionEntry)
Liz Kammer690fbac2023-02-10 11:11:17 -0500396 }
Liz Kammera4655a92023-02-10 17:17:28 -0500397 wg.Wait()
398 }
399 if err != nil {
400 return nil, nil, err
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500401 }
402
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400403 depsetsByHash := map[string]AqueryDepset{}
Liz Kammer00629db2023-02-09 14:28:15 -0500404 depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
Liz Kammer690fbac2023-02-10 11:11:17 -0500405 {
406 eventHandler.Begin("depsets")
407 defer eventHandler.End("depsets")
408 for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
409 if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
410 // Two depsets collide on hash. Ensure that their contents are identical.
411 if !reflect.DeepEqual(aqueryDepset, prevEntry) {
412 return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
413 }
414 } else {
415 depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
416 depsets = append(depsets, aqueryDepset)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400417 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400418 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400419 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400420
Liz Kammer690fbac2023-02-10 11:11:17 -0500421 eventHandler.Do("build_statement_sort", func() {
422 // Build Statements and depsets must be sorted by their content hash to
423 // preserve determinism between builds (this will result in consistent ninja file
424 // output). Note they are not sorted by their original IDs nor their Bazel ordering,
425 // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
426 sort.Slice(buildStatements, func(i, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -0500427 // Sort all nil statements to the end of the slice
428 if buildStatements[i] == nil {
429 return false
430 } else if buildStatements[j] == nil {
431 return true
432 }
433 //For build statements, compare output lists. In Bazel, each output file
Liz Kammer690fbac2023-02-10 11:11:17 -0500434 // may only have one action which generates it, so this will provide
435 // a deterministic ordering.
436 outputs_i := buildStatements[i].OutputPaths
437 outputs_j := buildStatements[j].OutputPaths
438 if len(outputs_i) != len(outputs_j) {
439 return len(outputs_i) < len(outputs_j)
440 }
441 if len(outputs_i) == 0 {
442 // No outputs for these actions, so compare commands.
443 return buildStatements[i].Command < buildStatements[j].Command
444 }
445 // There may be multiple outputs, but the output ordering is deterministic.
446 return outputs_i[0] < outputs_j[0]
447 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400448 })
Liz Kammer690fbac2023-02-10 11:11:17 -0500449 eventHandler.Do("depset_sort", func() {
450 sort.Slice(depsets, func(i, j int) bool {
451 return depsets[i].ContentHash < depsets[j].ContentHash
452 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400453 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400454 return buildStatements, depsets, nil
455}
456
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400457// depsetContentHash computes and returns a SHA256 checksum of the contents of
458// the given depset. This content hash may serve as the depset's identifier.
459// Using a content hash for an identifier is superior for determinism. (For example,
460// using an integer identifier which depends on the order in which the depsets are
461// created would result in nondeterministic depset IDs.)
462func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
463 h := sha256.New()
464 // Use newline as delimiter, as paths cannot contain newline.
465 h.Write([]byte(strings.Join(directPaths, "\n")))
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400466 h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
467 fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400468 return fullHash
469}
470
Liz Kammer00629db2023-02-09 14:28:15 -0500471func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400472 var hashes []string
Liz Kammer00629db2023-02-09 14:28:15 -0500473 for _, id := range inputDepsetIds {
474 dId := depsetId(id)
475 if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
476 if _, empty := a.emptyDepsetIds[dId]; !empty {
477 return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500478 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400479 } else {
480 hashes = append(hashes, aqueryDepset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400481 }
482 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400483 return hashes, nil
Chris Parsons1a7aca02022-04-25 22:35:15 -0400484}
485
Spandan Dasda724862023-06-16 23:35:55 +0000486// escapes the args received from aquery and creates a command string
487func commandString(actionEntry *analysis_v2_proto.Action) string {
488 switch actionEntry.Mnemonic {
Spandan Das2d93ebb2023-07-27 23:46:24 +0000489 case "GoCompilePkg", "GoStdlib":
Spandan Dasda724862023-06-16 23:35:55 +0000490 argsEscaped := []string{}
491 for _, arg := range actionEntry.Arguments {
492 if arg == "" {
493 // If this is an empty string, add ''
494 // And not
495 // 1. (literal empty)
496 // 2. `''\'''\'''` (escaped version of '')
497 //
498 // If we had used (1), then this would appear as a whitespace when we strings.Join
499 argsEscaped = append(argsEscaped, "''")
500 } else {
501 argsEscaped = append(argsEscaped, proptools.ShellEscapeIncludingSpaces(arg))
502 }
503 }
504 return strings.Join(argsEscaped, " ")
505 default:
506 return strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
507 }
508}
509
Liz Kammer00629db2023-02-09 14:28:15 -0500510func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Spandan Dasda724862023-06-16 23:35:55 +0000511 command := commandString(actionEntry)
Usta Shresthac2372492022-05-27 10:45:00 -0400512 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400513 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500514 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400515 }
Usta Shresthac2372492022-05-27 10:45:00 -0400516 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400517 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500518 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400519 }
520
Liz Kammer00629db2023-02-09 14:28:15 -0500521 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400522 Command: command,
523 Depfile: depfile,
524 OutputPaths: outputPaths,
525 InputDepsetHashes: inputDepsetHashes,
526 Env: actionEntry.EnvironmentVariables,
527 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400528 }
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000529 if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
530 // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
531 // This causes issues for `GOCACHE=$(mktemp -d) go build ...`
532 // To prevent this, sandbox this action in mixed builds as well
533 buildStatement.ShouldRunInSbox = true
534 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400535 return buildStatement, nil
536}
537
Liz Kammer00629db2023-02-09 14:28:15 -0500538func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400539 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400540 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500541 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400542 }
543 if len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500544 return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400545 }
546 expandedTemplateContent := expandTemplateContent(actionEntry)
547 // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
548 // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
549 // change \n to space and mess up the format of Python programs.
550 // sed is used to convert \\n back to \n before saving to output file.
551 // See go/python-binary-host-mixed-build for more details.
552 command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
553 escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
Usta Shresthac2372492022-05-27 10:45:00 -0400554 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400555 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500556 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400557 }
558
Liz Kammer00629db2023-02-09 14:28:15 -0500559 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400560 Command: command,
561 Depfile: depfile,
562 OutputPaths: outputPaths,
563 InputDepsetHashes: inputDepsetHashes,
564 Env: actionEntry.EnvironmentVariables,
565 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400566 }
567 return buildStatement, nil
568}
569
Liz Kammer00629db2023-02-09 14:28:15 -0500570func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundak1da064c2022-06-08 16:36:16 -0700571 outputPaths, _, err := a.getOutputPaths(actionEntry)
572 var depsetHashes []string
573 if err == nil {
574 depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
575 }
576 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500577 return nil, err
Sasha Smundak1da064c2022-06-08 16:36:16 -0700578 }
Liz Kammer00629db2023-02-09 14:28:15 -0500579 return &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -0700580 Depfile: nil,
581 OutputPaths: outputPaths,
582 Env: actionEntry.EnvironmentVariables,
583 Mnemonic: actionEntry.Mnemonic,
584 InputDepsetHashes: depsetHashes,
585 FileContents: actionEntry.FileContents,
Cole Faust20f20302023-08-31 11:00:25 -0700586 IsExecutable: actionEntry.IsExecutable,
Sasha Smundak1da064c2022-06-08 16:36:16 -0700587 }, nil
588}
589
Liz Kammer00629db2023-02-09 14:28:15 -0500590func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700591 outputPaths, _, err := a.getOutputPaths(actionEntry)
592 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500593 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700594 }
595 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
596 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500597 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700598 }
599 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500600 return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700601 }
602 // The actual command is generated in bazelSingleton.GenerateBuildActions
Liz Kammer00629db2023-02-09 14:28:15 -0500603 return &BuildStatement{
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700604 Depfile: nil,
605 OutputPaths: outputPaths,
606 Env: actionEntry.EnvironmentVariables,
607 Mnemonic: actionEntry.Mnemonic,
608 InputPaths: inputPaths,
609 }, nil
610}
611
Cole Faustbc65a3f2023-08-01 16:38:55 +0000612type bazelSandwichJson struct {
613 Target string `json:"target"`
614 DependOnTarget *bool `json:"depend_on_target,omitempty"`
615 ImplicitDeps []string `json:"implicit_deps"`
616}
617
618func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
619 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
620 if err != nil {
621 return nil, err
622 }
623 if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
624 return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
625 }
626 target := actionEntry.UnresolvedSymlinkTarget
627 if target == "" {
628 return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
629 }
630 if filepath.Clean(target) != target {
631 return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
632 }
633 if strings.HasPrefix(target, "/") {
634 return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
635 }
636
637 out := outputPaths[0]
638 outDir := filepath.Dir(out)
639 var implicitDeps []string
640 if strings.HasPrefix(target, "bazel_sandwich:") {
641 j := bazelSandwichJson{}
642 err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
643 if err != nil {
644 return nil, err
645 }
646 if proptools.BoolDefault(j.DependOnTarget, true) {
647 implicitDeps = append(implicitDeps, j.Target)
648 }
649 implicitDeps = append(implicitDeps, j.ImplicitDeps...)
650 dotDotsToReachCwd := ""
651 if outDir != "." {
652 dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
653 }
654 target = proptools.ShellEscapeIncludingSpaces(j.Target)
655 target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
656 } else {
657 target = proptools.ShellEscapeIncludingSpaces(target)
658 }
659
660 outDir = proptools.ShellEscapeIncludingSpaces(outDir)
661 out = proptools.ShellEscapeIncludingSpaces(out)
662 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
663 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
664 symlinkPaths := outputPaths[:]
665
666 buildStatement := &BuildStatement{
667 Command: command,
668 Depfile: depfile,
669 OutputPaths: outputPaths,
670 Env: actionEntry.EnvironmentVariables,
671 Mnemonic: actionEntry.Mnemonic,
672 SymlinkPaths: symlinkPaths,
673 ImplicitDeps: implicitDeps,
674 }
675 return buildStatement, nil
676}
677
Liz Kammer00629db2023-02-09 14:28:15 -0500678func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400679 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400680 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500681 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400682 }
683
Usta Shresthac2372492022-05-27 10:45:00 -0400684 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400685 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500686 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400687 }
688 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500689 return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400690 }
691 out := outputPaths[0]
692 outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
693 out = proptools.ShellEscapeIncludingSpaces(out)
694 in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
695 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
696 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
697 symlinkPaths := outputPaths[:]
698
Liz Kammer00629db2023-02-09 14:28:15 -0500699 buildStatement := &BuildStatement{
Chris Parsons1a7aca02022-04-25 22:35:15 -0400700 Command: command,
701 Depfile: depfile,
702 OutputPaths: outputPaths,
703 InputPaths: inputPaths,
704 Env: actionEntry.EnvironmentVariables,
705 Mnemonic: actionEntry.Mnemonic,
706 SymlinkPaths: symlinkPaths,
707 }
708 return buildStatement, nil
709}
710
Liz Kammer00629db2023-02-09 14:28:15 -0500711func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400712 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500713 outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400714 if !exists {
715 err = fmt.Errorf("undefined outputId %d", outputId)
716 return
717 }
718 ext := filepath.Ext(outputPath)
719 if ext == ".d" {
720 if depfile != nil {
721 err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
722 return
723 } else {
724 depfile = &outputPath
725 }
726 } else {
727 outputPaths = append(outputPaths, outputPath)
728 }
729 }
730 return
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500731}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500732
Wei Li455ba832021-11-04 22:58:12 +0000733// expandTemplateContent substitutes the tokens in a template.
Liz Kammer00629db2023-02-09 14:28:15 -0500734func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
735 replacerString := make([]string, len(actionEntry.Substitutions)*2)
736 for i, pair := range actionEntry.Substitutions {
Wei Li455ba832021-11-04 22:58:12 +0000737 value := pair.Value
Usta Shrestha6298cc52022-05-27 17:40:21 -0400738 if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
Wei Li455ba832021-11-04 22:58:12 +0000739 value = val
740 }
Liz Kammer00629db2023-02-09 14:28:15 -0500741 replacerString[i*2] = pair.Key
742 replacerString[i*2+1] = value
Wei Li455ba832021-11-04 22:58:12 +0000743 }
744 replacer := strings.NewReplacer(replacerString...)
745 return replacer.Replace(actionEntry.TemplateContent)
746}
747
Liz Kammerf15a0792023-02-09 14:28:36 -0500748// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
749var commandLineArgumentReplacer = strings.NewReplacer(
750 `\`, `\\`,
751 `$`, `\$`,
752 "`", "\\`",
753 `"`, `\"`,
754 "\n", "\\n",
755 `'`, `'"'"'`,
756)
757
Wei Li455ba832021-11-04 22:58:12 +0000758func escapeCommandlineArgument(str string) string {
Liz Kammerf15a0792023-02-09 14:28:36 -0500759 return commandLineArgumentReplacer.Replace(str)
Wei Li455ba832021-11-04 22:58:12 +0000760}
761
Liz Kammer00629db2023-02-09 14:28:15 -0500762func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
763 switch actionEntry.Mnemonic {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400764 // Middleman actions are not handled like other actions; they are handled separately as a
765 // preparatory step so that their inputs may be relayed to actions depending on middleman
766 // artifacts.
Liz Kammer00629db2023-02-09 14:28:15 -0500767 case middlemanMnemonic:
768 return nil, nil
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700769 // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
Liz Kammer00629db2023-02-09 14:28:15 -0500770 case "PythonZipper":
771 return nil, nil
Chris Parsons8d6e4332021-02-22 16:13:50 -0500772 // Skip "Fail" actions, which are placeholder actions designed to always fail.
Liz Kammer00629db2023-02-09 14:28:15 -0500773 case "Fail":
774 return nil, nil
775 case "BaselineCoverage":
776 return nil, nil
777 case "Symlink", "SolibSymlink", "ExecutableSymlink":
778 return a.symlinkActionBuildStatement(actionEntry)
779 case "TemplateExpand":
780 if len(actionEntry.Arguments) < 1 {
781 return a.templateExpandActionBuildStatement(actionEntry)
782 }
Cole Faust950689a2023-06-21 15:07:21 -0700783 case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
Liz Kammer00629db2023-02-09 14:28:15 -0500784 return a.fileWriteActionBuildStatement(actionEntry)
785 case "SymlinkTree":
786 return a.symlinkTreeActionBuildStatement(actionEntry)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000787 case "UnresolvedSymlink":
788 return a.unresolvedSymlinkActionBuildStatement(actionEntry)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500789 }
Liz Kammer00629db2023-02-09 14:28:15 -0500790
791 if len(actionEntry.Arguments) < 1 {
ustaa79afd72023-09-22 17:29:56 -0400792 return nil, errors.New("received action with no command")
Yu Liu8d82ac52022-05-17 15:13:28 -0700793 }
Liz Kammer00629db2023-02-09 14:28:15 -0500794 return a.normalActionBuildStatement(actionEntry)
795
Chris Parsons8d6e4332021-02-22 16:13:50 -0500796}
797
Liz Kammer00629db2023-02-09 14:28:15 -0500798func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400799 var labels []string
Chris Parsonsaffbb602020-12-23 12:02:11 -0500800 currId := id
801 // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
802 for currId > 0 {
803 currFragment, ok := pathFragmentsMap[currId]
804 if !ok {
Chris Parsons4f069892021-01-15 12:22:41 -0500805 return "", fmt.Errorf("undefined path fragment id %d", currId)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500806 }
807 labels = append([]string{currFragment.Label}, labels...)
Liz Kammer00629db2023-02-09 14:28:15 -0500808 parentId := pathFragmentId(currFragment.ParentId)
809 if currId == parentId {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700810 return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
Liz Kammerc49e6822021-06-08 15:04:11 -0400811 }
Liz Kammer00629db2023-02-09 14:28:15 -0500812 currId = parentId
Chris Parsonsaffbb602020-12-23 12:02:11 -0500813 }
814 return filepath.Join(labels...), nil
815}