blob: c35571239702f71a64d264b607593d799eb8bfc4 [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"
Chris Parsonsaffbb602020-12-23 12:02:11 -050021 "fmt"
22 "path/filepath"
Cole Faustbc65a3f2023-08-01 16:38:55 +000023 analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
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
Liz Kammer690fbac2023-02-10 11:11:17 -050029 "github.com/google/blueprint/metrics"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050030 "github.com/google/blueprint/proptools"
Jason Wu118fd2b2022-10-27 18:41:15 +000031 "google.golang.org/protobuf/proto"
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050032)
33
Usta Shrestha6298cc52022-05-27 17:40:21 -040034type artifactId int
35type depsetId int
36type pathFragmentId int
37
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050038// artifact contains relevant portions of Bazel's aquery proto, Artifact.
39// Represents a single artifact, whether it's a source file or a derived output file.
40type artifact struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040041 Id artifactId
42 PathFragmentId pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050043}
44
45type pathFragment struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040046 Id pathFragmentId
Chris Parsonsaffbb602020-12-23 12:02:11 -050047 Label string
Usta Shrestha6298cc52022-05-27 17:40:21 -040048 ParentId pathFragmentId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050049}
50
51// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
52type KeyValuePair struct {
53 Key string
54 Value string
55}
56
Chris Parsons1a7aca02022-04-25 22:35:15 -040057// AqueryDepset is a depset definition from Bazel's aquery response. This is
Chris Parsons0bfb1c02022-05-12 16:43:01 -040058// akin to the `depSetOfFiles` in the response proto, except:
Colin Crossd079e0b2022-08-16 10:27:33 -070059// - direct artifacts are enumerated by full path instead of by ID
60// - it has a hash of the depset contents, instead of an int ID (for determinism)
61//
Chris Parsons1a7aca02022-04-25 22:35:15 -040062// A depset is a data structure for efficient transitive handling of artifact
63// paths. A single depset consists of one or more artifact paths and one or
64// more "child" depsets.
65type AqueryDepset struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -040066 ContentHash string
67 DirectArtifacts []string
68 TransitiveDepSetHashes []string
Chris Parsons1a7aca02022-04-25 22:35:15 -040069}
70
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050071// depSetOfFiles contains relevant portions of Bazel's aquery proto, DepSetOfFiles.
72// Represents a data structure containing one or more files. Depsets in Bazel are an efficient
73// data structure for storing large numbers of file paths.
74type depSetOfFiles struct {
Usta Shrestha6298cc52022-05-27 17:40:21 -040075 Id depsetId
76 DirectArtifactIds []artifactId
77 TransitiveDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050078}
79
80// action contains relevant portions of Bazel's aquery proto, Action.
81// Represents a single command line invocation in the Bazel build graph.
82type action struct {
83 Arguments []string
84 EnvironmentVariables []KeyValuePair
Usta Shrestha6298cc52022-05-27 17:40:21 -040085 InputDepSetIds []depsetId
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050086 Mnemonic string
Usta Shrestha6298cc52022-05-27 17:40:21 -040087 OutputIds []artifactId
Wei Li455ba832021-11-04 22:58:12 +000088 TemplateContent string
89 Substitutions []KeyValuePair
Sasha Smundak1da064c2022-06-08 16:36:16 -070090 FileContents string
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050091}
92
93// actionGraphContainer contains relevant portions of Bazel's aquery proto, ActionGraphContainer.
94// An aquery response from Bazel contains a single ActionGraphContainer proto.
95type actionGraphContainer struct {
96 Artifacts []artifact
97 Actions []action
98 DepSetOfFiles []depSetOfFiles
Chris Parsonsaffbb602020-12-23 12:02:11 -050099 PathFragments []pathFragment
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500100}
101
102// BuildStatement contains information to register a build statement corresponding (one to one)
103// with a Bazel action from Bazel's action graph.
104type BuildStatement struct {
Liz Kammerc49e6822021-06-08 15:04:11 -0400105 Command string
106 Depfile *string
107 OutputPaths []string
Liz Kammerc49e6822021-06-08 15:04:11 -0400108 SymlinkPaths []string
Liz Kammer00629db2023-02-09 14:28:15 -0500109 Env []*analysis_v2_proto.KeyValuePair
Liz Kammerc49e6822021-06-08 15:04:11 -0400110 Mnemonic string
Chris Parsons1a7aca02022-04-25 22:35:15 -0400111
112 // Inputs of this build statement, either as unexpanded depsets or expanded
113 // input paths. There should be no overlap between these fields; an input
114 // path should either be included as part of an unexpanded depset or a raw
115 // input path string, but not both.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400116 InputDepsetHashes []string
117 InputPaths []string
Sasha Smundak1da064c2022-06-08 16:36:16 -0700118 FileContents string
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000119 // If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
120 // and run the mixed build action there
121 ShouldRunInSbox bool
Cole Faustbc65a3f2023-08-01 16:38:55 +0000122 // A list of files to add as implicit deps to the outputs of this BuildStatement.
123 // Unlike most properties in BuildStatement, these paths must be relative to the root of
124 // the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
125 ImplicitDeps []string
Cole Faust20f20302023-08-31 11:00:25 -0700126 IsExecutable bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500127}
128
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400129// A helper type for aquery processing which facilitates retrieval of path IDs from their
130// less readable Bazel structures (depset and path fragment).
131type aqueryArtifactHandler struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400132 // Maps depset id to AqueryDepset, a representation of depset which is
133 // post-processed for middleman artifact handling, unhandled artifact
134 // dropping, content hashing, etc.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400135 depsetIdToAqueryDepset map[depsetId]AqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500136 emptyDepsetIds map[depsetId]struct{}
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400137 // Maps content hash to AqueryDepset.
138 depsetHashToAqueryDepset map[string]AqueryDepset
139
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400140 // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
141 // may be an expensive operation.
Liz Kammera4655a92023-02-10 17:17:28 -0500142 depsetHashToArtifactPathsCache sync.Map
Usta Shrestha6298cc52022-05-27 17:40:21 -0400143 // Maps artifact ids to fully expanded paths.
144 artifactIdToPath map[artifactId]string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400145}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500146
Wei Li455ba832021-11-04 22:58:12 +0000147// The tokens should be substituted with the value specified here, instead of the
148// one returned in 'substitutions' of TemplateExpand action.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400149var templateActionOverriddenTokens = map[string]string{
Wei Li455ba832021-11-04 22:58:12 +0000150 // Uses "python3" for %python_binary% instead of the value returned by aquery
151 // which is "py3wrapper.sh". See removePy3wrapperScript.
152 "%python_binary%": "python3",
153}
154
Liz Kammer00629db2023-02-09 14:28:15 -0500155const (
156 middlemanMnemonic = "Middleman"
157 // The file name of py3wrapper.sh, which is used by py_binary targets.
158 py3wrapperFileName = "/py3wrapper.sh"
159)
Wei Li455ba832021-11-04 22:58:12 +0000160
Usta Shrestha6298cc52022-05-27 17:40:21 -0400161func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
162 m := map[K]V{}
163 for _, v := range values {
164 m[keyFn(v)] = v
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500165 }
Usta Shrestha6298cc52022-05-27 17:40:21 -0400166 return m
167}
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400168
Liz Kammer00629db2023-02-09 14:28:15 -0500169func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
170 pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
171 return pathFragmentId(pf.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400172 })
173
Liz Kammer00629db2023-02-09 14:28:15 -0500174 artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
Chris Parsonsaffbb602020-12-23 12:02:11 -0500175 for _, artifact := range aqueryResult.Artifacts {
Liz Kammer00629db2023-02-09 14:28:15 -0500176 artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500177 if err != nil {
Chris Parsons4f069892021-01-15 12:22:41 -0500178 return nil, err
Chris Parsonsaffbb602020-12-23 12:02:11 -0500179 }
Cole Faustbdee8f92023-09-07 14:28:51 -0700180 if artifact.IsTreeArtifact &&
181 !strings.HasPrefix(artifactPath, "bazel-out/io_bazel_rules_go/") &&
182 !strings.HasPrefix(artifactPath, "bazel-out/rules_java_builtin/") {
183 // Since we're using ninja as an executor, we can't use tree artifacts. Ninja only
184 // considers a file/directory "dirty" when it's mtime changes. Directories' mtimes will
185 // only change when a file in the directory is added/removed, but not when files in
186 // the directory are changed, or when files in subdirectories are changed/added/removed.
187 // Bazel handles this by walking the directory and generating a hash for it after the
188 // action runs, which we would have to do as well if we wanted to support these
189 // artifacts in mixed builds.
190 //
191 // However, there are some bazel built-in rules that use tree artifacts. Allow those,
192 // but keep in mind that they'll have incrementality issues.
193 return nil, fmt.Errorf("tree artifacts are currently not supported in mixed builds: " + artifactPath)
194 }
Romain Jobredeauxe3989a12023-07-19 20:58:27 +0000195 artifactIdToPath[artifactId(artifact.Id)] = artifactPath
Chris Parsonsaffbb602020-12-23 12:02:11 -0500196 }
Chris Parsons943f2432021-01-19 11:36:50 -0500197
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400198 // Map middleman artifact ContentHash to input artifact depset ID.
Chris Parsons1a7aca02022-04-25 22:35:15 -0400199 // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
Usta Shrestha16ac1352022-06-22 11:01:55 -0400200 // if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400201 // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
202 // that action instead.
Liz Kammer00629db2023-02-09 14:28:15 -0500203 middlemanIdToDepsetIds := map[artifactId][]uint32{}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500204 for _, actionEntry := range aqueryResult.Actions {
Liz Kammer00629db2023-02-09 14:28:15 -0500205 if actionEntry.Mnemonic == middlemanMnemonic {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500206 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500207 middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
Chris Parsons8d6e4332021-02-22 16:13:50 -0500208 }
209 }
210 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400211
Liz Kammer00629db2023-02-09 14:28:15 -0500212 depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
213 return depsetId(d.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400214 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400215
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400216 aqueryHandler := aqueryArtifactHandler{
Usta Shrestha6298cc52022-05-27 17:40:21 -0400217 depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400218 depsetHashToAqueryDepset: map[string]AqueryDepset{},
Liz Kammera4655a92023-02-10 17:17:28 -0500219 depsetHashToArtifactPathsCache: sync.Map{},
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500220 emptyDepsetIds: make(map[depsetId]struct{}, 0),
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400221 artifactIdToPath: artifactIdToPath,
222 }
223
224 // Validate and adjust aqueryResult.DepSetOfFiles values.
225 for _, depset := range aqueryResult.DepSetOfFiles {
226 _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
227 if err != nil {
228 return nil, err
229 }
230 }
231
232 return &aqueryHandler, nil
233}
234
235// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
236// depset.
Liz Kammer00629db2023-02-09 14:28:15 -0500237func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
238 if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500239 return &aqueryDepset, nil
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400240 }
241 transitiveDepsetIds := depset.TransitiveDepSetIds
Liz Kammer00629db2023-02-09 14:28:15 -0500242 directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
243 for _, id := range depset.DirectArtifactIds {
244 aId := artifactId(id)
245 path, pathExists := a.artifactIdToPath[aId]
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400246 if !pathExists {
Liz Kammer00629db2023-02-09 14:28:15 -0500247 return nil, fmt.Errorf("undefined input artifactId %d", aId)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400248 }
249 // Filter out any inputs which are universally dropped, and swap middleman
250 // artifacts with their corresponding depsets.
Liz Kammer00629db2023-02-09 14:28:15 -0500251 if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400252 // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
253 transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
Usta Shresthaef922252022-06-02 14:23:02 -0400254 } else if strings.HasSuffix(path, py3wrapperFileName) ||
Usta Shresthaef922252022-06-02 14:23:02 -0400255 strings.HasPrefix(path, "../bazel_tools") {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500256 continue
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400257 // Drop these artifacts.
258 // See go/python-binary-host-mixed-build for more details.
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700259 // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
260 // TemplateExpandAction handles everything necessary to launch a Pythin application.
261 // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
Usta Shresthaef922252022-06-02 14:23:02 -0400262 // containing depset to always be considered newer than their outputs.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400263 } else {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400264 directArtifactPaths = append(directArtifactPaths, path)
265 }
266 }
267
Liz Kammer00629db2023-02-09 14:28:15 -0500268 childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
269 for _, id := range transitiveDepsetIds {
270 childDepsetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400271 childDepset, exists := depsetIdToDepset[childDepsetId]
272 if !exists {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500273 if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
274 continue
275 } else {
276 return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
277 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400278 }
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500279 if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
280 return nil, err
281 } else if childAqueryDepset == nil {
282 continue
283 } else {
284 childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400285 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400286 }
Usta Shresthaef922252022-06-02 14:23:02 -0400287 if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
Liz Kammer00629db2023-02-09 14:28:15 -0500288 a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500289 return nil, nil
Usta Shresthaef922252022-06-02 14:23:02 -0400290 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400291 aqueryDepset := AqueryDepset{
292 ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
293 DirectArtifacts: directArtifactPaths,
294 TransitiveDepSetHashes: childDepsetHashes,
295 }
Liz Kammer00629db2023-02-09 14:28:15 -0500296 a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400297 a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500298 return &aqueryDepset, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400299}
300
Chris Parsons1a7aca02022-04-25 22:35:15 -0400301// getInputPaths flattens the depsets of the given IDs and returns all transitive
302// input paths contained in these depsets.
303// This is a potentially expensive operation, and should not be invoked except
304// for actions which need specialized input handling.
Liz Kammer00629db2023-02-09 14:28:15 -0500305func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400306 var inputPaths []string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400307
Liz Kammer00629db2023-02-09 14:28:15 -0500308 for _, id := range depsetIds {
309 inputDepSetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400310 depset := a.depsetIdToAqueryDepset[inputDepSetId]
311 inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400312 if err != nil {
313 return nil, err
314 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400315 for _, inputPath := range inputArtifacts {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400316 inputPaths = append(inputPaths, inputPath)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400317 }
318 }
Wei Li455ba832021-11-04 22:58:12 +0000319
Chris Parsons1a7aca02022-04-25 22:35:15 -0400320 return inputPaths, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400321}
322
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400323func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
Liz Kammera4655a92023-02-10 17:17:28 -0500324 if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
325 return result.([]string), nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400326 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400327 if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
328 result := depset.DirectArtifacts
329 for _, childHash := range depset.TransitiveDepSetHashes {
330 childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400331 if err != nil {
332 return nil, err
333 }
334 result = append(result, childArtifactIds...)
335 }
Liz Kammera4655a92023-02-10 17:17:28 -0500336 a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400337 return result, nil
338 } else {
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400339 return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400340 }
341}
342
Chris Parsons1a7aca02022-04-25 22:35:15 -0400343// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
Usta Shrestha6298cc52022-05-27 17:40:21 -0400344// which should be registered (and output to a ninja file) to correspond with Bazel's
Chris Parsons1a7aca02022-04-25 22:35:15 -0400345// action graph, as described by the given action graph json proto.
346// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
347// are one-to-one with Bazel's depSetOfFiles objects.
Liz Kammera4655a92023-02-10 17:17:28 -0500348func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
Jason Wu118fd2b2022-10-27 18:41:15 +0000349 aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
350 err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400351 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400352 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400353 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500354
Liz Kammer690fbac2023-02-10 11:11:17 -0500355 var aqueryHandler *aqueryArtifactHandler
356 {
357 eventHandler.Begin("init_handler")
358 defer eventHandler.End("init_handler")
Liz Kammer00629db2023-02-09 14:28:15 -0500359 aqueryHandler, err = newAqueryHandler(aqueryProto)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400360 if err != nil {
361 return nil, nil, err
Chris Parsons8d6e4332021-02-22 16:13:50 -0500362 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500363 }
364
Liz Kammera4655a92023-02-10 17:17:28 -0500365 // allocate both length and capacity so each goroutine can write to an index independently without
366 // any need for synchronization for slice access.
367 buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
Liz Kammer690fbac2023-02-10 11:11:17 -0500368 {
369 eventHandler.Begin("build_statements")
370 defer eventHandler.End("build_statements")
Liz Kammera4655a92023-02-10 17:17:28 -0500371 wg := sync.WaitGroup{}
372 var errOnce sync.Once
373
374 for i, actionEntry := range aqueryProto.Actions {
375 wg.Add(1)
376 go func(i int, actionEntry *analysis_v2_proto.Action) {
377 buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry)
378 if aErr != nil {
379 errOnce.Do(func() {
380 err = aErr
381 })
382 } else {
383 // set build statement at an index rather than appending such that each goroutine does not
384 // impact other goroutines
385 buildStatements[i] = buildStatement
386 }
387 wg.Done()
388 }(i, actionEntry)
Liz Kammer690fbac2023-02-10 11:11:17 -0500389 }
Liz Kammera4655a92023-02-10 17:17:28 -0500390 wg.Wait()
391 }
392 if err != nil {
393 return nil, nil, err
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500394 }
395
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400396 depsetsByHash := map[string]AqueryDepset{}
Liz Kammer00629db2023-02-09 14:28:15 -0500397 depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
Liz Kammer690fbac2023-02-10 11:11:17 -0500398 {
399 eventHandler.Begin("depsets")
400 defer eventHandler.End("depsets")
401 for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
402 if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
403 // Two depsets collide on hash. Ensure that their contents are identical.
404 if !reflect.DeepEqual(aqueryDepset, prevEntry) {
405 return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
406 }
407 } else {
408 depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
409 depsets = append(depsets, aqueryDepset)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400410 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400411 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400412 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400413
Liz Kammer690fbac2023-02-10 11:11:17 -0500414 eventHandler.Do("build_statement_sort", func() {
415 // Build Statements and depsets must be sorted by their content hash to
416 // preserve determinism between builds (this will result in consistent ninja file
417 // output). Note they are not sorted by their original IDs nor their Bazel ordering,
418 // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
419 sort.Slice(buildStatements, func(i, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -0500420 // Sort all nil statements to the end of the slice
421 if buildStatements[i] == nil {
422 return false
423 } else if buildStatements[j] == nil {
424 return true
425 }
426 //For build statements, compare output lists. In Bazel, each output file
Liz Kammer690fbac2023-02-10 11:11:17 -0500427 // may only have one action which generates it, so this will provide
428 // a deterministic ordering.
429 outputs_i := buildStatements[i].OutputPaths
430 outputs_j := buildStatements[j].OutputPaths
431 if len(outputs_i) != len(outputs_j) {
432 return len(outputs_i) < len(outputs_j)
433 }
434 if len(outputs_i) == 0 {
435 // No outputs for these actions, so compare commands.
436 return buildStatements[i].Command < buildStatements[j].Command
437 }
438 // There may be multiple outputs, but the output ordering is deterministic.
439 return outputs_i[0] < outputs_j[0]
440 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400441 })
Liz Kammer690fbac2023-02-10 11:11:17 -0500442 eventHandler.Do("depset_sort", func() {
443 sort.Slice(depsets, func(i, j int) bool {
444 return depsets[i].ContentHash < depsets[j].ContentHash
445 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400446 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400447 return buildStatements, depsets, nil
448}
449
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400450// depsetContentHash computes and returns a SHA256 checksum of the contents of
451// the given depset. This content hash may serve as the depset's identifier.
452// Using a content hash for an identifier is superior for determinism. (For example,
453// using an integer identifier which depends on the order in which the depsets are
454// created would result in nondeterministic depset IDs.)
455func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
456 h := sha256.New()
457 // Use newline as delimiter, as paths cannot contain newline.
458 h.Write([]byte(strings.Join(directPaths, "\n")))
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400459 h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
460 fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400461 return fullHash
462}
463
Liz Kammer00629db2023-02-09 14:28:15 -0500464func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400465 var hashes []string
Liz Kammer00629db2023-02-09 14:28:15 -0500466 for _, id := range inputDepsetIds {
467 dId := depsetId(id)
468 if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
469 if _, empty := a.emptyDepsetIds[dId]; !empty {
470 return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500471 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400472 } else {
473 hashes = append(hashes, aqueryDepset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400474 }
475 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400476 return hashes, nil
Chris Parsons1a7aca02022-04-25 22:35:15 -0400477}
478
Spandan Dasda724862023-06-16 23:35:55 +0000479// escapes the args received from aquery and creates a command string
480func commandString(actionEntry *analysis_v2_proto.Action) string {
481 switch actionEntry.Mnemonic {
Spandan Das2d93ebb2023-07-27 23:46:24 +0000482 case "GoCompilePkg", "GoStdlib":
Spandan Dasda724862023-06-16 23:35:55 +0000483 argsEscaped := []string{}
484 for _, arg := range actionEntry.Arguments {
485 if arg == "" {
486 // If this is an empty string, add ''
487 // And not
488 // 1. (literal empty)
489 // 2. `''\'''\'''` (escaped version of '')
490 //
491 // If we had used (1), then this would appear as a whitespace when we strings.Join
492 argsEscaped = append(argsEscaped, "''")
493 } else {
494 argsEscaped = append(argsEscaped, proptools.ShellEscapeIncludingSpaces(arg))
495 }
496 }
497 return strings.Join(argsEscaped, " ")
498 default:
499 return strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
500 }
501}
502
Liz Kammer00629db2023-02-09 14:28:15 -0500503func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Spandan Dasda724862023-06-16 23:35:55 +0000504 command := commandString(actionEntry)
Usta Shresthac2372492022-05-27 10:45:00 -0400505 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400506 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500507 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400508 }
Usta Shresthac2372492022-05-27 10:45:00 -0400509 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400510 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500511 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400512 }
513
Liz Kammer00629db2023-02-09 14:28:15 -0500514 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400515 Command: command,
516 Depfile: depfile,
517 OutputPaths: outputPaths,
518 InputDepsetHashes: inputDepsetHashes,
519 Env: actionEntry.EnvironmentVariables,
520 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400521 }
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000522 if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
523 // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
524 // This causes issues for `GOCACHE=$(mktemp -d) go build ...`
525 // To prevent this, sandbox this action in mixed builds as well
526 buildStatement.ShouldRunInSbox = true
527 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400528 return buildStatement, nil
529}
530
Liz Kammer00629db2023-02-09 14:28:15 -0500531func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400532 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400533 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500534 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400535 }
536 if len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500537 return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400538 }
539 expandedTemplateContent := expandTemplateContent(actionEntry)
540 // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
541 // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
542 // change \n to space and mess up the format of Python programs.
543 // sed is used to convert \\n back to \n before saving to output file.
544 // See go/python-binary-host-mixed-build for more details.
545 command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
546 escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
Usta Shresthac2372492022-05-27 10:45:00 -0400547 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400548 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500549 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400550 }
551
Liz Kammer00629db2023-02-09 14:28:15 -0500552 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400553 Command: command,
554 Depfile: depfile,
555 OutputPaths: outputPaths,
556 InputDepsetHashes: inputDepsetHashes,
557 Env: actionEntry.EnvironmentVariables,
558 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400559 }
560 return buildStatement, nil
561}
562
Liz Kammer00629db2023-02-09 14:28:15 -0500563func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundak1da064c2022-06-08 16:36:16 -0700564 outputPaths, _, err := a.getOutputPaths(actionEntry)
565 var depsetHashes []string
566 if err == nil {
567 depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
568 }
569 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500570 return nil, err
Sasha Smundak1da064c2022-06-08 16:36:16 -0700571 }
Liz Kammer00629db2023-02-09 14:28:15 -0500572 return &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -0700573 Depfile: nil,
574 OutputPaths: outputPaths,
575 Env: actionEntry.EnvironmentVariables,
576 Mnemonic: actionEntry.Mnemonic,
577 InputDepsetHashes: depsetHashes,
578 FileContents: actionEntry.FileContents,
Cole Faust20f20302023-08-31 11:00:25 -0700579 IsExecutable: actionEntry.IsExecutable,
Sasha Smundak1da064c2022-06-08 16:36:16 -0700580 }, nil
581}
582
Liz Kammer00629db2023-02-09 14:28:15 -0500583func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700584 outputPaths, _, err := a.getOutputPaths(actionEntry)
585 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500586 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700587 }
588 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
589 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500590 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700591 }
592 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500593 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 -0700594 }
595 // The actual command is generated in bazelSingleton.GenerateBuildActions
Liz Kammer00629db2023-02-09 14:28:15 -0500596 return &BuildStatement{
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700597 Depfile: nil,
598 OutputPaths: outputPaths,
599 Env: actionEntry.EnvironmentVariables,
600 Mnemonic: actionEntry.Mnemonic,
601 InputPaths: inputPaths,
602 }, nil
603}
604
Cole Faustbc65a3f2023-08-01 16:38:55 +0000605type bazelSandwichJson struct {
606 Target string `json:"target"`
607 DependOnTarget *bool `json:"depend_on_target,omitempty"`
608 ImplicitDeps []string `json:"implicit_deps"`
609}
610
611func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
612 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
613 if err != nil {
614 return nil, err
615 }
616 if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
617 return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
618 }
619 target := actionEntry.UnresolvedSymlinkTarget
620 if target == "" {
621 return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
622 }
623 if filepath.Clean(target) != target {
624 return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
625 }
626 if strings.HasPrefix(target, "/") {
627 return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
628 }
629
630 out := outputPaths[0]
631 outDir := filepath.Dir(out)
632 var implicitDeps []string
633 if strings.HasPrefix(target, "bazel_sandwich:") {
634 j := bazelSandwichJson{}
635 err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
636 if err != nil {
637 return nil, err
638 }
639 if proptools.BoolDefault(j.DependOnTarget, true) {
640 implicitDeps = append(implicitDeps, j.Target)
641 }
642 implicitDeps = append(implicitDeps, j.ImplicitDeps...)
643 dotDotsToReachCwd := ""
644 if outDir != "." {
645 dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
646 }
647 target = proptools.ShellEscapeIncludingSpaces(j.Target)
648 target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
649 } else {
650 target = proptools.ShellEscapeIncludingSpaces(target)
651 }
652
653 outDir = proptools.ShellEscapeIncludingSpaces(outDir)
654 out = proptools.ShellEscapeIncludingSpaces(out)
655 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
656 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
657 symlinkPaths := outputPaths[:]
658
659 buildStatement := &BuildStatement{
660 Command: command,
661 Depfile: depfile,
662 OutputPaths: outputPaths,
663 Env: actionEntry.EnvironmentVariables,
664 Mnemonic: actionEntry.Mnemonic,
665 SymlinkPaths: symlinkPaths,
666 ImplicitDeps: implicitDeps,
667 }
668 return buildStatement, nil
669}
670
Liz Kammer00629db2023-02-09 14:28:15 -0500671func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400672 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400673 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500674 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400675 }
676
Usta Shresthac2372492022-05-27 10:45:00 -0400677 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400678 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500679 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400680 }
681 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500682 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 -0400683 }
684 out := outputPaths[0]
685 outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
686 out = proptools.ShellEscapeIncludingSpaces(out)
687 in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
688 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
689 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
690 symlinkPaths := outputPaths[:]
691
Liz Kammer00629db2023-02-09 14:28:15 -0500692 buildStatement := &BuildStatement{
Chris Parsons1a7aca02022-04-25 22:35:15 -0400693 Command: command,
694 Depfile: depfile,
695 OutputPaths: outputPaths,
696 InputPaths: inputPaths,
697 Env: actionEntry.EnvironmentVariables,
698 Mnemonic: actionEntry.Mnemonic,
699 SymlinkPaths: symlinkPaths,
700 }
701 return buildStatement, nil
702}
703
Liz Kammer00629db2023-02-09 14:28:15 -0500704func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400705 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500706 outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400707 if !exists {
708 err = fmt.Errorf("undefined outputId %d", outputId)
709 return
710 }
711 ext := filepath.Ext(outputPath)
712 if ext == ".d" {
713 if depfile != nil {
714 err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
715 return
716 } else {
717 depfile = &outputPath
718 }
719 } else {
720 outputPaths = append(outputPaths, outputPath)
721 }
722 }
723 return
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500724}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500725
Wei Li455ba832021-11-04 22:58:12 +0000726// expandTemplateContent substitutes the tokens in a template.
Liz Kammer00629db2023-02-09 14:28:15 -0500727func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
728 replacerString := make([]string, len(actionEntry.Substitutions)*2)
729 for i, pair := range actionEntry.Substitutions {
Wei Li455ba832021-11-04 22:58:12 +0000730 value := pair.Value
Usta Shrestha6298cc52022-05-27 17:40:21 -0400731 if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
Wei Li455ba832021-11-04 22:58:12 +0000732 value = val
733 }
Liz Kammer00629db2023-02-09 14:28:15 -0500734 replacerString[i*2] = pair.Key
735 replacerString[i*2+1] = value
Wei Li455ba832021-11-04 22:58:12 +0000736 }
737 replacer := strings.NewReplacer(replacerString...)
738 return replacer.Replace(actionEntry.TemplateContent)
739}
740
Liz Kammerf15a0792023-02-09 14:28:36 -0500741// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
742var commandLineArgumentReplacer = strings.NewReplacer(
743 `\`, `\\`,
744 `$`, `\$`,
745 "`", "\\`",
746 `"`, `\"`,
747 "\n", "\\n",
748 `'`, `'"'"'`,
749)
750
Wei Li455ba832021-11-04 22:58:12 +0000751func escapeCommandlineArgument(str string) string {
Liz Kammerf15a0792023-02-09 14:28:36 -0500752 return commandLineArgumentReplacer.Replace(str)
Wei Li455ba832021-11-04 22:58:12 +0000753}
754
Liz Kammer00629db2023-02-09 14:28:15 -0500755func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
756 switch actionEntry.Mnemonic {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400757 // Middleman actions are not handled like other actions; they are handled separately as a
758 // preparatory step so that their inputs may be relayed to actions depending on middleman
759 // artifacts.
Liz Kammer00629db2023-02-09 14:28:15 -0500760 case middlemanMnemonic:
761 return nil, nil
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700762 // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
Liz Kammer00629db2023-02-09 14:28:15 -0500763 case "PythonZipper":
764 return nil, nil
Chris Parsons8d6e4332021-02-22 16:13:50 -0500765 // Skip "Fail" actions, which are placeholder actions designed to always fail.
Liz Kammer00629db2023-02-09 14:28:15 -0500766 case "Fail":
767 return nil, nil
768 case "BaselineCoverage":
769 return nil, nil
770 case "Symlink", "SolibSymlink", "ExecutableSymlink":
771 return a.symlinkActionBuildStatement(actionEntry)
772 case "TemplateExpand":
773 if len(actionEntry.Arguments) < 1 {
774 return a.templateExpandActionBuildStatement(actionEntry)
775 }
Cole Faust950689a2023-06-21 15:07:21 -0700776 case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
Liz Kammer00629db2023-02-09 14:28:15 -0500777 return a.fileWriteActionBuildStatement(actionEntry)
778 case "SymlinkTree":
779 return a.symlinkTreeActionBuildStatement(actionEntry)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000780 case "UnresolvedSymlink":
781 return a.unresolvedSymlinkActionBuildStatement(actionEntry)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500782 }
Liz Kammer00629db2023-02-09 14:28:15 -0500783
784 if len(actionEntry.Arguments) < 1 {
785 return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
Yu Liu8d82ac52022-05-17 15:13:28 -0700786 }
Liz Kammer00629db2023-02-09 14:28:15 -0500787 return a.normalActionBuildStatement(actionEntry)
788
Chris Parsons8d6e4332021-02-22 16:13:50 -0500789}
790
Liz Kammer00629db2023-02-09 14:28:15 -0500791func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400792 var labels []string
Chris Parsonsaffbb602020-12-23 12:02:11 -0500793 currId := id
794 // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
795 for currId > 0 {
796 currFragment, ok := pathFragmentsMap[currId]
797 if !ok {
Chris Parsons4f069892021-01-15 12:22:41 -0500798 return "", fmt.Errorf("undefined path fragment id %d", currId)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500799 }
800 labels = append([]string{currFragment.Label}, labels...)
Liz Kammer00629db2023-02-09 14:28:15 -0500801 parentId := pathFragmentId(currFragment.ParentId)
802 if currId == parentId {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700803 return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
Liz Kammerc49e6822021-06-08 15:04:11 -0400804 }
Liz Kammer00629db2023-02-09 14:28:15 -0500805 currId = parentId
Chris Parsonsaffbb602020-12-23 12:02:11 -0500806 }
807 return filepath.Join(labels...), nil
808}