blob: 76cd9725405fe85763e4ae43fb01e4672ab3d143 [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 }
Romain Jobredeauxe3989a12023-07-19 20:58:27 +0000180 artifactIdToPath[artifactId(artifact.Id)] = artifactPath
Chris Parsonsaffbb602020-12-23 12:02:11 -0500181 }
Chris Parsons943f2432021-01-19 11:36:50 -0500182
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400183 // Map middleman artifact ContentHash to input artifact depset ID.
Chris Parsons1a7aca02022-04-25 22:35:15 -0400184 // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
Usta Shrestha16ac1352022-06-22 11:01:55 -0400185 // if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400186 // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
187 // that action instead.
Liz Kammer00629db2023-02-09 14:28:15 -0500188 middlemanIdToDepsetIds := map[artifactId][]uint32{}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500189 for _, actionEntry := range aqueryResult.Actions {
Liz Kammer00629db2023-02-09 14:28:15 -0500190 if actionEntry.Mnemonic == middlemanMnemonic {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500191 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500192 middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
Chris Parsons8d6e4332021-02-22 16:13:50 -0500193 }
194 }
195 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400196
Liz Kammer00629db2023-02-09 14:28:15 -0500197 depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
198 return depsetId(d.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400199 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400200
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400201 aqueryHandler := aqueryArtifactHandler{
Usta Shrestha6298cc52022-05-27 17:40:21 -0400202 depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400203 depsetHashToAqueryDepset: map[string]AqueryDepset{},
Liz Kammera4655a92023-02-10 17:17:28 -0500204 depsetHashToArtifactPathsCache: sync.Map{},
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500205 emptyDepsetIds: make(map[depsetId]struct{}, 0),
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400206 artifactIdToPath: artifactIdToPath,
207 }
208
209 // Validate and adjust aqueryResult.DepSetOfFiles values.
210 for _, depset := range aqueryResult.DepSetOfFiles {
211 _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
212 if err != nil {
213 return nil, err
214 }
215 }
216
217 return &aqueryHandler, nil
218}
219
220// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
221// depset.
Liz Kammer00629db2023-02-09 14:28:15 -0500222func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
223 if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500224 return &aqueryDepset, nil
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400225 }
226 transitiveDepsetIds := depset.TransitiveDepSetIds
Liz Kammer00629db2023-02-09 14:28:15 -0500227 directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
228 for _, id := range depset.DirectArtifactIds {
229 aId := artifactId(id)
230 path, pathExists := a.artifactIdToPath[aId]
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400231 if !pathExists {
Liz Kammer00629db2023-02-09 14:28:15 -0500232 return nil, fmt.Errorf("undefined input artifactId %d", aId)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400233 }
234 // Filter out any inputs which are universally dropped, and swap middleman
235 // artifacts with their corresponding depsets.
Liz Kammer00629db2023-02-09 14:28:15 -0500236 if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400237 // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
238 transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
Usta Shresthaef922252022-06-02 14:23:02 -0400239 } else if strings.HasSuffix(path, py3wrapperFileName) ||
Usta Shresthaef922252022-06-02 14:23:02 -0400240 strings.HasPrefix(path, "../bazel_tools") {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500241 continue
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400242 // Drop these artifacts.
243 // See go/python-binary-host-mixed-build for more details.
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700244 // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
245 // TemplateExpandAction handles everything necessary to launch a Pythin application.
246 // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
Usta Shresthaef922252022-06-02 14:23:02 -0400247 // containing depset to always be considered newer than their outputs.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400248 } else {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400249 directArtifactPaths = append(directArtifactPaths, path)
250 }
251 }
252
Liz Kammer00629db2023-02-09 14:28:15 -0500253 childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
254 for _, id := range transitiveDepsetIds {
255 childDepsetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400256 childDepset, exists := depsetIdToDepset[childDepsetId]
257 if !exists {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500258 if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
259 continue
260 } else {
261 return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
262 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400263 }
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500264 if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
265 return nil, err
266 } else if childAqueryDepset == nil {
267 continue
268 } else {
269 childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400270 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400271 }
Usta Shresthaef922252022-06-02 14:23:02 -0400272 if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
Liz Kammer00629db2023-02-09 14:28:15 -0500273 a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500274 return nil, nil
Usta Shresthaef922252022-06-02 14:23:02 -0400275 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400276 aqueryDepset := AqueryDepset{
277 ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
278 DirectArtifacts: directArtifactPaths,
279 TransitiveDepSetHashes: childDepsetHashes,
280 }
Liz Kammer00629db2023-02-09 14:28:15 -0500281 a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400282 a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500283 return &aqueryDepset, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400284}
285
Chris Parsons1a7aca02022-04-25 22:35:15 -0400286// getInputPaths flattens the depsets of the given IDs and returns all transitive
287// input paths contained in these depsets.
288// This is a potentially expensive operation, and should not be invoked except
289// for actions which need specialized input handling.
Liz Kammer00629db2023-02-09 14:28:15 -0500290func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400291 var inputPaths []string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400292
Liz Kammer00629db2023-02-09 14:28:15 -0500293 for _, id := range depsetIds {
294 inputDepSetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400295 depset := a.depsetIdToAqueryDepset[inputDepSetId]
296 inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400297 if err != nil {
298 return nil, err
299 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400300 for _, inputPath := range inputArtifacts {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400301 inputPaths = append(inputPaths, inputPath)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400302 }
303 }
Wei Li455ba832021-11-04 22:58:12 +0000304
Chris Parsons1a7aca02022-04-25 22:35:15 -0400305 return inputPaths, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400306}
307
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400308func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
Liz Kammera4655a92023-02-10 17:17:28 -0500309 if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
310 return result.([]string), nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400311 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400312 if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
313 result := depset.DirectArtifacts
314 for _, childHash := range depset.TransitiveDepSetHashes {
315 childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400316 if err != nil {
317 return nil, err
318 }
319 result = append(result, childArtifactIds...)
320 }
Liz Kammera4655a92023-02-10 17:17:28 -0500321 a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400322 return result, nil
323 } else {
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400324 return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400325 }
326}
327
Chris Parsons1a7aca02022-04-25 22:35:15 -0400328// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
Usta Shrestha6298cc52022-05-27 17:40:21 -0400329// which should be registered (and output to a ninja file) to correspond with Bazel's
Chris Parsons1a7aca02022-04-25 22:35:15 -0400330// action graph, as described by the given action graph json proto.
331// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
332// are one-to-one with Bazel's depSetOfFiles objects.
Liz Kammera4655a92023-02-10 17:17:28 -0500333func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
Jason Wu118fd2b2022-10-27 18:41:15 +0000334 aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
335 err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400336 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400337 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400338 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500339
Liz Kammer690fbac2023-02-10 11:11:17 -0500340 var aqueryHandler *aqueryArtifactHandler
341 {
342 eventHandler.Begin("init_handler")
343 defer eventHandler.End("init_handler")
Liz Kammer00629db2023-02-09 14:28:15 -0500344 aqueryHandler, err = newAqueryHandler(aqueryProto)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400345 if err != nil {
346 return nil, nil, err
Chris Parsons8d6e4332021-02-22 16:13:50 -0500347 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500348 }
349
Liz Kammera4655a92023-02-10 17:17:28 -0500350 // allocate both length and capacity so each goroutine can write to an index independently without
351 // any need for synchronization for slice access.
352 buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
Liz Kammer690fbac2023-02-10 11:11:17 -0500353 {
354 eventHandler.Begin("build_statements")
355 defer eventHandler.End("build_statements")
Liz Kammera4655a92023-02-10 17:17:28 -0500356 wg := sync.WaitGroup{}
357 var errOnce sync.Once
358
359 for i, actionEntry := range aqueryProto.Actions {
360 wg.Add(1)
361 go func(i int, actionEntry *analysis_v2_proto.Action) {
362 buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry)
363 if aErr != nil {
364 errOnce.Do(func() {
365 err = aErr
366 })
367 } else {
368 // set build statement at an index rather than appending such that each goroutine does not
369 // impact other goroutines
370 buildStatements[i] = buildStatement
371 }
372 wg.Done()
373 }(i, actionEntry)
Liz Kammer690fbac2023-02-10 11:11:17 -0500374 }
Liz Kammera4655a92023-02-10 17:17:28 -0500375 wg.Wait()
376 }
377 if err != nil {
378 return nil, nil, err
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500379 }
380
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400381 depsetsByHash := map[string]AqueryDepset{}
Liz Kammer00629db2023-02-09 14:28:15 -0500382 depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
Liz Kammer690fbac2023-02-10 11:11:17 -0500383 {
384 eventHandler.Begin("depsets")
385 defer eventHandler.End("depsets")
386 for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
387 if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
388 // Two depsets collide on hash. Ensure that their contents are identical.
389 if !reflect.DeepEqual(aqueryDepset, prevEntry) {
390 return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
391 }
392 } else {
393 depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
394 depsets = append(depsets, aqueryDepset)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400395 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400396 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400397 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400398
Liz Kammer690fbac2023-02-10 11:11:17 -0500399 eventHandler.Do("build_statement_sort", func() {
400 // Build Statements and depsets must be sorted by their content hash to
401 // preserve determinism between builds (this will result in consistent ninja file
402 // output). Note they are not sorted by their original IDs nor their Bazel ordering,
403 // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
404 sort.Slice(buildStatements, func(i, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -0500405 // Sort all nil statements to the end of the slice
406 if buildStatements[i] == nil {
407 return false
408 } else if buildStatements[j] == nil {
409 return true
410 }
411 //For build statements, compare output lists. In Bazel, each output file
Liz Kammer690fbac2023-02-10 11:11:17 -0500412 // may only have one action which generates it, so this will provide
413 // a deterministic ordering.
414 outputs_i := buildStatements[i].OutputPaths
415 outputs_j := buildStatements[j].OutputPaths
416 if len(outputs_i) != len(outputs_j) {
417 return len(outputs_i) < len(outputs_j)
418 }
419 if len(outputs_i) == 0 {
420 // No outputs for these actions, so compare commands.
421 return buildStatements[i].Command < buildStatements[j].Command
422 }
423 // There may be multiple outputs, but the output ordering is deterministic.
424 return outputs_i[0] < outputs_j[0]
425 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400426 })
Liz Kammer690fbac2023-02-10 11:11:17 -0500427 eventHandler.Do("depset_sort", func() {
428 sort.Slice(depsets, func(i, j int) bool {
429 return depsets[i].ContentHash < depsets[j].ContentHash
430 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400431 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400432 return buildStatements, depsets, nil
433}
434
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400435// depsetContentHash computes and returns a SHA256 checksum of the contents of
436// the given depset. This content hash may serve as the depset's identifier.
437// Using a content hash for an identifier is superior for determinism. (For example,
438// using an integer identifier which depends on the order in which the depsets are
439// created would result in nondeterministic depset IDs.)
440func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
441 h := sha256.New()
442 // Use newline as delimiter, as paths cannot contain newline.
443 h.Write([]byte(strings.Join(directPaths, "\n")))
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400444 h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
445 fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400446 return fullHash
447}
448
Liz Kammer00629db2023-02-09 14:28:15 -0500449func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400450 var hashes []string
Liz Kammer00629db2023-02-09 14:28:15 -0500451 for _, id := range inputDepsetIds {
452 dId := depsetId(id)
453 if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
454 if _, empty := a.emptyDepsetIds[dId]; !empty {
455 return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500456 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400457 } else {
458 hashes = append(hashes, aqueryDepset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400459 }
460 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400461 return hashes, nil
Chris Parsons1a7aca02022-04-25 22:35:15 -0400462}
463
Spandan Dasda724862023-06-16 23:35:55 +0000464// escapes the args received from aquery and creates a command string
465func commandString(actionEntry *analysis_v2_proto.Action) string {
466 switch actionEntry.Mnemonic {
Spandan Das2d93ebb2023-07-27 23:46:24 +0000467 case "GoCompilePkg", "GoStdlib":
Spandan Dasda724862023-06-16 23:35:55 +0000468 argsEscaped := []string{}
469 for _, arg := range actionEntry.Arguments {
470 if arg == "" {
471 // If this is an empty string, add ''
472 // And not
473 // 1. (literal empty)
474 // 2. `''\'''\'''` (escaped version of '')
475 //
476 // If we had used (1), then this would appear as a whitespace when we strings.Join
477 argsEscaped = append(argsEscaped, "''")
478 } else {
479 argsEscaped = append(argsEscaped, proptools.ShellEscapeIncludingSpaces(arg))
480 }
481 }
482 return strings.Join(argsEscaped, " ")
483 default:
484 return strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
485 }
486}
487
Liz Kammer00629db2023-02-09 14:28:15 -0500488func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Spandan Dasda724862023-06-16 23:35:55 +0000489 command := commandString(actionEntry)
Usta Shresthac2372492022-05-27 10:45:00 -0400490 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400491 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500492 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400493 }
Usta Shresthac2372492022-05-27 10:45:00 -0400494 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400495 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500496 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400497 }
498
Liz Kammer00629db2023-02-09 14:28:15 -0500499 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400500 Command: command,
501 Depfile: depfile,
502 OutputPaths: outputPaths,
503 InputDepsetHashes: inputDepsetHashes,
504 Env: actionEntry.EnvironmentVariables,
505 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400506 }
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000507 if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
508 // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
509 // This causes issues for `GOCACHE=$(mktemp -d) go build ...`
510 // To prevent this, sandbox this action in mixed builds as well
511 buildStatement.ShouldRunInSbox = true
512 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400513 return buildStatement, nil
514}
515
Liz Kammer00629db2023-02-09 14:28:15 -0500516func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400517 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400518 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500519 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400520 }
521 if len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500522 return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400523 }
524 expandedTemplateContent := expandTemplateContent(actionEntry)
525 // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
526 // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
527 // change \n to space and mess up the format of Python programs.
528 // sed is used to convert \\n back to \n before saving to output file.
529 // See go/python-binary-host-mixed-build for more details.
530 command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
531 escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
Usta Shresthac2372492022-05-27 10:45:00 -0400532 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
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
Liz Kammer00629db2023-02-09 14:28:15 -0500537 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400538 Command: command,
539 Depfile: depfile,
540 OutputPaths: outputPaths,
541 InputDepsetHashes: inputDepsetHashes,
542 Env: actionEntry.EnvironmentVariables,
543 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400544 }
545 return buildStatement, nil
546}
547
Liz Kammer00629db2023-02-09 14:28:15 -0500548func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundak1da064c2022-06-08 16:36:16 -0700549 outputPaths, _, err := a.getOutputPaths(actionEntry)
550 var depsetHashes []string
551 if err == nil {
552 depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
553 }
554 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500555 return nil, err
Sasha Smundak1da064c2022-06-08 16:36:16 -0700556 }
Liz Kammer00629db2023-02-09 14:28:15 -0500557 return &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -0700558 Depfile: nil,
559 OutputPaths: outputPaths,
560 Env: actionEntry.EnvironmentVariables,
561 Mnemonic: actionEntry.Mnemonic,
562 InputDepsetHashes: depsetHashes,
563 FileContents: actionEntry.FileContents,
Cole Faust20f20302023-08-31 11:00:25 -0700564 IsExecutable: actionEntry.IsExecutable,
Sasha Smundak1da064c2022-06-08 16:36:16 -0700565 }, nil
566}
567
Liz Kammer00629db2023-02-09 14:28:15 -0500568func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700569 outputPaths, _, err := a.getOutputPaths(actionEntry)
570 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500571 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700572 }
573 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
574 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500575 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700576 }
577 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500578 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 -0700579 }
580 // The actual command is generated in bazelSingleton.GenerateBuildActions
Liz Kammer00629db2023-02-09 14:28:15 -0500581 return &BuildStatement{
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700582 Depfile: nil,
583 OutputPaths: outputPaths,
584 Env: actionEntry.EnvironmentVariables,
585 Mnemonic: actionEntry.Mnemonic,
586 InputPaths: inputPaths,
587 }, nil
588}
589
Cole Faustbc65a3f2023-08-01 16:38:55 +0000590type bazelSandwichJson struct {
591 Target string `json:"target"`
592 DependOnTarget *bool `json:"depend_on_target,omitempty"`
593 ImplicitDeps []string `json:"implicit_deps"`
594}
595
596func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
597 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
598 if err != nil {
599 return nil, err
600 }
601 if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
602 return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
603 }
604 target := actionEntry.UnresolvedSymlinkTarget
605 if target == "" {
606 return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
607 }
608 if filepath.Clean(target) != target {
609 return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
610 }
611 if strings.HasPrefix(target, "/") {
612 return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
613 }
614
615 out := outputPaths[0]
616 outDir := filepath.Dir(out)
617 var implicitDeps []string
618 if strings.HasPrefix(target, "bazel_sandwich:") {
619 j := bazelSandwichJson{}
620 err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
621 if err != nil {
622 return nil, err
623 }
624 if proptools.BoolDefault(j.DependOnTarget, true) {
625 implicitDeps = append(implicitDeps, j.Target)
626 }
627 implicitDeps = append(implicitDeps, j.ImplicitDeps...)
628 dotDotsToReachCwd := ""
629 if outDir != "." {
630 dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
631 }
632 target = proptools.ShellEscapeIncludingSpaces(j.Target)
633 target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
634 } else {
635 target = proptools.ShellEscapeIncludingSpaces(target)
636 }
637
638 outDir = proptools.ShellEscapeIncludingSpaces(outDir)
639 out = proptools.ShellEscapeIncludingSpaces(out)
640 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
641 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
642 symlinkPaths := outputPaths[:]
643
644 buildStatement := &BuildStatement{
645 Command: command,
646 Depfile: depfile,
647 OutputPaths: outputPaths,
648 Env: actionEntry.EnvironmentVariables,
649 Mnemonic: actionEntry.Mnemonic,
650 SymlinkPaths: symlinkPaths,
651 ImplicitDeps: implicitDeps,
652 }
653 return buildStatement, nil
654}
655
Liz Kammer00629db2023-02-09 14:28:15 -0500656func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400657 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400658 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500659 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400660 }
661
Usta Shresthac2372492022-05-27 10:45:00 -0400662 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400663 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500664 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400665 }
666 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500667 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 -0400668 }
669 out := outputPaths[0]
670 outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
671 out = proptools.ShellEscapeIncludingSpaces(out)
672 in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
673 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
674 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
675 symlinkPaths := outputPaths[:]
676
Liz Kammer00629db2023-02-09 14:28:15 -0500677 buildStatement := &BuildStatement{
Chris Parsons1a7aca02022-04-25 22:35:15 -0400678 Command: command,
679 Depfile: depfile,
680 OutputPaths: outputPaths,
681 InputPaths: inputPaths,
682 Env: actionEntry.EnvironmentVariables,
683 Mnemonic: actionEntry.Mnemonic,
684 SymlinkPaths: symlinkPaths,
685 }
686 return buildStatement, nil
687}
688
Liz Kammer00629db2023-02-09 14:28:15 -0500689func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400690 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500691 outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400692 if !exists {
693 err = fmt.Errorf("undefined outputId %d", outputId)
694 return
695 }
696 ext := filepath.Ext(outputPath)
697 if ext == ".d" {
698 if depfile != nil {
699 err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
700 return
701 } else {
702 depfile = &outputPath
703 }
704 } else {
705 outputPaths = append(outputPaths, outputPath)
706 }
707 }
708 return
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500709}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500710
Wei Li455ba832021-11-04 22:58:12 +0000711// expandTemplateContent substitutes the tokens in a template.
Liz Kammer00629db2023-02-09 14:28:15 -0500712func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
713 replacerString := make([]string, len(actionEntry.Substitutions)*2)
714 for i, pair := range actionEntry.Substitutions {
Wei Li455ba832021-11-04 22:58:12 +0000715 value := pair.Value
Usta Shrestha6298cc52022-05-27 17:40:21 -0400716 if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
Wei Li455ba832021-11-04 22:58:12 +0000717 value = val
718 }
Liz Kammer00629db2023-02-09 14:28:15 -0500719 replacerString[i*2] = pair.Key
720 replacerString[i*2+1] = value
Wei Li455ba832021-11-04 22:58:12 +0000721 }
722 replacer := strings.NewReplacer(replacerString...)
723 return replacer.Replace(actionEntry.TemplateContent)
724}
725
Liz Kammerf15a0792023-02-09 14:28:36 -0500726// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
727var commandLineArgumentReplacer = strings.NewReplacer(
728 `\`, `\\`,
729 `$`, `\$`,
730 "`", "\\`",
731 `"`, `\"`,
732 "\n", "\\n",
733 `'`, `'"'"'`,
734)
735
Wei Li455ba832021-11-04 22:58:12 +0000736func escapeCommandlineArgument(str string) string {
Liz Kammerf15a0792023-02-09 14:28:36 -0500737 return commandLineArgumentReplacer.Replace(str)
Wei Li455ba832021-11-04 22:58:12 +0000738}
739
Liz Kammer00629db2023-02-09 14:28:15 -0500740func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
741 switch actionEntry.Mnemonic {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400742 // Middleman actions are not handled like other actions; they are handled separately as a
743 // preparatory step so that their inputs may be relayed to actions depending on middleman
744 // artifacts.
Liz Kammer00629db2023-02-09 14:28:15 -0500745 case middlemanMnemonic:
746 return nil, nil
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700747 // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
Liz Kammer00629db2023-02-09 14:28:15 -0500748 case "PythonZipper":
749 return nil, nil
Chris Parsons8d6e4332021-02-22 16:13:50 -0500750 // Skip "Fail" actions, which are placeholder actions designed to always fail.
Liz Kammer00629db2023-02-09 14:28:15 -0500751 case "Fail":
752 return nil, nil
753 case "BaselineCoverage":
754 return nil, nil
755 case "Symlink", "SolibSymlink", "ExecutableSymlink":
756 return a.symlinkActionBuildStatement(actionEntry)
757 case "TemplateExpand":
758 if len(actionEntry.Arguments) < 1 {
759 return a.templateExpandActionBuildStatement(actionEntry)
760 }
Cole Faust950689a2023-06-21 15:07:21 -0700761 case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
Liz Kammer00629db2023-02-09 14:28:15 -0500762 return a.fileWriteActionBuildStatement(actionEntry)
763 case "SymlinkTree":
764 return a.symlinkTreeActionBuildStatement(actionEntry)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000765 case "UnresolvedSymlink":
766 return a.unresolvedSymlinkActionBuildStatement(actionEntry)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500767 }
Liz Kammer00629db2023-02-09 14:28:15 -0500768
769 if len(actionEntry.Arguments) < 1 {
770 return nil, fmt.Errorf("received action with no command: [%s]", actionEntry.Mnemonic)
Yu Liu8d82ac52022-05-17 15:13:28 -0700771 }
Liz Kammer00629db2023-02-09 14:28:15 -0500772 return a.normalActionBuildStatement(actionEntry)
773
Chris Parsons8d6e4332021-02-22 16:13:50 -0500774}
775
Liz Kammer00629db2023-02-09 14:28:15 -0500776func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400777 var labels []string
Chris Parsonsaffbb602020-12-23 12:02:11 -0500778 currId := id
779 // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
780 for currId > 0 {
781 currFragment, ok := pathFragmentsMap[currId]
782 if !ok {
Chris Parsons4f069892021-01-15 12:22:41 -0500783 return "", fmt.Errorf("undefined path fragment id %d", currId)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500784 }
785 labels = append([]string{currFragment.Label}, labels...)
Liz Kammer00629db2023-02-09 14:28:15 -0500786 parentId := pathFragmentId(currFragment.ParentId)
787 if currId == parentId {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700788 return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
Liz Kammerc49e6822021-06-08 15:04:11 -0400789 }
Liz Kammer00629db2023-02-09 14:28:15 -0500790 currId = parentId
Chris Parsonsaffbb602020-12-23 12:02:11 -0500791 }
792 return filepath.Join(labels...), nil
793}