blob: 27ccb20c89d068410fec6eaceaadb26affc89520 [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// KeyValuePair represents Bazel's aquery proto, KeyValuePair.
41type KeyValuePair struct {
42 Key string
43 Value string
44}
45
Chris Parsons1a7aca02022-04-25 22:35:15 -040046// AqueryDepset is a depset definition from Bazel's aquery response. This is
Chris Parsons0bfb1c02022-05-12 16:43:01 -040047// akin to the `depSetOfFiles` in the response proto, except:
Colin Crossd079e0b2022-08-16 10:27:33 -070048// - direct artifacts are enumerated by full path instead of by ID
49// - it has a hash of the depset contents, instead of an int ID (for determinism)
50//
Chris Parsons1a7aca02022-04-25 22:35:15 -040051// A depset is a data structure for efficient transitive handling of artifact
52// paths. A single depset consists of one or more artifact paths and one or
53// more "child" depsets.
54type AqueryDepset struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -040055 ContentHash string
56 DirectArtifacts []string
57 TransitiveDepSetHashes []string
Chris Parsons1a7aca02022-04-25 22:35:15 -040058}
59
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050060// BuildStatement contains information to register a build statement corresponding (one to one)
61// with a Bazel action from Bazel's action graph.
62type BuildStatement struct {
Liz Kammerc49e6822021-06-08 15:04:11 -040063 Command string
64 Depfile *string
65 OutputPaths []string
Liz Kammerc49e6822021-06-08 15:04:11 -040066 SymlinkPaths []string
Liz Kammer00629db2023-02-09 14:28:15 -050067 Env []*analysis_v2_proto.KeyValuePair
Liz Kammerc49e6822021-06-08 15:04:11 -040068 Mnemonic string
Chris Parsons1a7aca02022-04-25 22:35:15 -040069
70 // Inputs of this build statement, either as unexpanded depsets or expanded
71 // input paths. There should be no overlap between these fields; an input
72 // path should either be included as part of an unexpanded depset or a raw
73 // input path string, but not both.
Chris Parsons0bfb1c02022-05-12 16:43:01 -040074 InputDepsetHashes []string
75 InputPaths []string
Sasha Smundak1da064c2022-06-08 16:36:16 -070076 FileContents string
Spandan Dasaf4ccaa2023-06-29 01:15:51 +000077 // If ShouldRunInSbox is true, Soong will use sbox to created an isolated environment
78 // and run the mixed build action there
79 ShouldRunInSbox bool
Cole Faustbc65a3f2023-08-01 16:38:55 +000080 // A list of files to add as implicit deps to the outputs of this BuildStatement.
81 // Unlike most properties in BuildStatement, these paths must be relative to the root of
82 // the whole out/ folder, instead of relative to ctx.Config().BazelContext.OutputBase()
83 ImplicitDeps []string
Cole Faust20f20302023-08-31 11:00:25 -070084 IsExecutable bool
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -050085}
86
Chris Parsonsc4fb1332021-05-18 12:31:25 -040087// A helper type for aquery processing which facilitates retrieval of path IDs from their
88// less readable Bazel structures (depset and path fragment).
89type aqueryArtifactHandler struct {
Chris Parsons0bfb1c02022-05-12 16:43:01 -040090 // Maps depset id to AqueryDepset, a representation of depset which is
91 // post-processed for middleman artifact handling, unhandled artifact
92 // dropping, content hashing, etc.
Usta Shrestha6298cc52022-05-27 17:40:21 -040093 depsetIdToAqueryDepset map[depsetId]AqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -050094 emptyDepsetIds map[depsetId]struct{}
Chris Parsons0bfb1c02022-05-12 16:43:01 -040095 // Maps content hash to AqueryDepset.
96 depsetHashToAqueryDepset map[string]AqueryDepset
97
Chris Parsonsc4fb1332021-05-18 12:31:25 -040098 // depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
99 // may be an expensive operation.
Liz Kammera4655a92023-02-10 17:17:28 -0500100 depsetHashToArtifactPathsCache sync.Map
Usta Shrestha6298cc52022-05-27 17:40:21 -0400101 // Maps artifact ids to fully expanded paths.
102 artifactIdToPath map[artifactId]string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400103}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500104
Wei Li455ba832021-11-04 22:58:12 +0000105// The tokens should be substituted with the value specified here, instead of the
106// one returned in 'substitutions' of TemplateExpand action.
Usta Shrestha6298cc52022-05-27 17:40:21 -0400107var templateActionOverriddenTokens = map[string]string{
Wei Li455ba832021-11-04 22:58:12 +0000108 // Uses "python3" for %python_binary% instead of the value returned by aquery
109 // which is "py3wrapper.sh". See removePy3wrapperScript.
110 "%python_binary%": "python3",
111}
112
Liz Kammer00629db2023-02-09 14:28:15 -0500113const (
114 middlemanMnemonic = "Middleman"
115 // The file name of py3wrapper.sh, which is used by py_binary targets.
116 py3wrapperFileName = "/py3wrapper.sh"
117)
Wei Li455ba832021-11-04 22:58:12 +0000118
Usta Shrestha6298cc52022-05-27 17:40:21 -0400119func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
120 m := map[K]V{}
121 for _, v := range values {
122 m[keyFn(v)] = v
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500123 }
Usta Shrestha6298cc52022-05-27 17:40:21 -0400124 return m
125}
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400126
Liz Kammer00629db2023-02-09 14:28:15 -0500127func newAqueryHandler(aqueryResult *analysis_v2_proto.ActionGraphContainer) (*aqueryArtifactHandler, error) {
128 pathFragments := indexBy(aqueryResult.PathFragments, func(pf *analysis_v2_proto.PathFragment) pathFragmentId {
129 return pathFragmentId(pf.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400130 })
131
Liz Kammer00629db2023-02-09 14:28:15 -0500132 artifactIdToPath := make(map[artifactId]string, len(aqueryResult.Artifacts))
Chris Parsonsaffbb602020-12-23 12:02:11 -0500133 for _, artifact := range aqueryResult.Artifacts {
Liz Kammer00629db2023-02-09 14:28:15 -0500134 artifactPath, err := expandPathFragment(pathFragmentId(artifact.PathFragmentId), pathFragments)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500135 if err != nil {
Chris Parsons4f069892021-01-15 12:22:41 -0500136 return nil, err
Chris Parsonsaffbb602020-12-23 12:02:11 -0500137 }
Cole Faustbdee8f92023-09-07 14:28:51 -0700138 if artifact.IsTreeArtifact &&
139 !strings.HasPrefix(artifactPath, "bazel-out/io_bazel_rules_go/") &&
140 !strings.HasPrefix(artifactPath, "bazel-out/rules_java_builtin/") {
141 // Since we're using ninja as an executor, we can't use tree artifacts. Ninja only
142 // considers a file/directory "dirty" when it's mtime changes. Directories' mtimes will
143 // only change when a file in the directory is added/removed, but not when files in
144 // the directory are changed, or when files in subdirectories are changed/added/removed.
145 // Bazel handles this by walking the directory and generating a hash for it after the
146 // action runs, which we would have to do as well if we wanted to support these
147 // artifacts in mixed builds.
148 //
149 // However, there are some bazel built-in rules that use tree artifacts. Allow those,
150 // but keep in mind that they'll have incrementality issues.
151 return nil, fmt.Errorf("tree artifacts are currently not supported in mixed builds: " + artifactPath)
152 }
Romain Jobredeauxe3989a12023-07-19 20:58:27 +0000153 artifactIdToPath[artifactId(artifact.Id)] = artifactPath
Chris Parsonsaffbb602020-12-23 12:02:11 -0500154 }
Chris Parsons943f2432021-01-19 11:36:50 -0500155
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400156 // Map middleman artifact ContentHash to input artifact depset ID.
Chris Parsons1a7aca02022-04-25 22:35:15 -0400157 // Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
Usta Shrestha16ac1352022-06-22 11:01:55 -0400158 // if we find a middleman action which has inputs [foo, bar], and output [baz_middleman], then,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400159 // for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
160 // that action instead.
Liz Kammer00629db2023-02-09 14:28:15 -0500161 middlemanIdToDepsetIds := map[artifactId][]uint32{}
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500162 for _, actionEntry := range aqueryResult.Actions {
Liz Kammer00629db2023-02-09 14:28:15 -0500163 if actionEntry.Mnemonic == middlemanMnemonic {
Chris Parsons8d6e4332021-02-22 16:13:50 -0500164 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500165 middlemanIdToDepsetIds[artifactId(outputId)] = actionEntry.InputDepSetIds
Chris Parsons8d6e4332021-02-22 16:13:50 -0500166 }
167 }
168 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400169
Liz Kammer00629db2023-02-09 14:28:15 -0500170 depsetIdToDepset := indexBy(aqueryResult.DepSetOfFiles, func(d *analysis_v2_proto.DepSetOfFiles) depsetId {
171 return depsetId(d.Id)
Usta Shrestha6298cc52022-05-27 17:40:21 -0400172 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400173
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400174 aqueryHandler := aqueryArtifactHandler{
Usta Shrestha6298cc52022-05-27 17:40:21 -0400175 depsetIdToAqueryDepset: map[depsetId]AqueryDepset{},
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400176 depsetHashToAqueryDepset: map[string]AqueryDepset{},
Liz Kammera4655a92023-02-10 17:17:28 -0500177 depsetHashToArtifactPathsCache: sync.Map{},
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500178 emptyDepsetIds: make(map[depsetId]struct{}, 0),
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400179 artifactIdToPath: artifactIdToPath,
180 }
181
182 // Validate and adjust aqueryResult.DepSetOfFiles values.
183 for _, depset := range aqueryResult.DepSetOfFiles {
184 _, err := aqueryHandler.populateDepsetMaps(depset, middlemanIdToDepsetIds, depsetIdToDepset)
185 if err != nil {
186 return nil, err
187 }
188 }
189
190 return &aqueryHandler, nil
191}
192
193// Ensures that the handler's depsetIdToAqueryDepset map contains an entry for the given
194// depset.
Liz Kammer00629db2023-02-09 14:28:15 -0500195func (a *aqueryArtifactHandler) populateDepsetMaps(depset *analysis_v2_proto.DepSetOfFiles, middlemanIdToDepsetIds map[artifactId][]uint32, depsetIdToDepset map[depsetId]*analysis_v2_proto.DepSetOfFiles) (*AqueryDepset, error) {
196 if aqueryDepset, containsDepset := a.depsetIdToAqueryDepset[depsetId(depset.Id)]; containsDepset {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500197 return &aqueryDepset, nil
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400198 }
199 transitiveDepsetIds := depset.TransitiveDepSetIds
Liz Kammer00629db2023-02-09 14:28:15 -0500200 directArtifactPaths := make([]string, 0, len(depset.DirectArtifactIds))
201 for _, id := range depset.DirectArtifactIds {
202 aId := artifactId(id)
203 path, pathExists := a.artifactIdToPath[aId]
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400204 if !pathExists {
Liz Kammer00629db2023-02-09 14:28:15 -0500205 return nil, fmt.Errorf("undefined input artifactId %d", aId)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400206 }
207 // Filter out any inputs which are universally dropped, and swap middleman
208 // artifacts with their corresponding depsets.
Liz Kammer00629db2023-02-09 14:28:15 -0500209 if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[aId]; isMiddleman {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400210 // Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
211 transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
Usta Shresthaef922252022-06-02 14:23:02 -0400212 } else if strings.HasSuffix(path, py3wrapperFileName) ||
Usta Shresthaef922252022-06-02 14:23:02 -0400213 strings.HasPrefix(path, "../bazel_tools") {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500214 continue
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400215 // Drop these artifacts.
216 // See go/python-binary-host-mixed-build for more details.
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700217 // 1) Drop py3wrapper.sh, just use python binary, the launcher script generated by the
218 // TemplateExpandAction handles everything necessary to launch a Pythin application.
219 // 2) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
Usta Shresthaef922252022-06-02 14:23:02 -0400220 // containing depset to always be considered newer than their outputs.
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400221 } else {
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400222 directArtifactPaths = append(directArtifactPaths, path)
223 }
224 }
225
Liz Kammer00629db2023-02-09 14:28:15 -0500226 childDepsetHashes := make([]string, 0, len(transitiveDepsetIds))
227 for _, id := range transitiveDepsetIds {
228 childDepsetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400229 childDepset, exists := depsetIdToDepset[childDepsetId]
230 if !exists {
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500231 if _, empty := a.emptyDepsetIds[childDepsetId]; empty {
232 continue
233 } else {
234 return nil, fmt.Errorf("undefined input depsetId %d (referenced by depsetId %d)", childDepsetId, depset.Id)
235 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400236 }
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500237 if childAqueryDepset, err := a.populateDepsetMaps(childDepset, middlemanIdToDepsetIds, depsetIdToDepset); err != nil {
238 return nil, err
239 } else if childAqueryDepset == nil {
240 continue
241 } else {
242 childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400243 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400244 }
Usta Shresthaef922252022-06-02 14:23:02 -0400245 if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
Liz Kammer00629db2023-02-09 14:28:15 -0500246 a.emptyDepsetIds[depsetId(depset.Id)] = struct{}{}
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500247 return nil, nil
Usta Shresthaef922252022-06-02 14:23:02 -0400248 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400249 aqueryDepset := AqueryDepset{
250 ContentHash: depsetContentHash(directArtifactPaths, childDepsetHashes),
251 DirectArtifacts: directArtifactPaths,
252 TransitiveDepSetHashes: childDepsetHashes,
253 }
Liz Kammer00629db2023-02-09 14:28:15 -0500254 a.depsetIdToAqueryDepset[depsetId(depset.Id)] = aqueryDepset
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400255 a.depsetHashToAqueryDepset[aqueryDepset.ContentHash] = aqueryDepset
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500256 return &aqueryDepset, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400257}
258
Chris Parsons1a7aca02022-04-25 22:35:15 -0400259// getInputPaths flattens the depsets of the given IDs and returns all transitive
260// input paths contained in these depsets.
261// This is a potentially expensive operation, and should not be invoked except
262// for actions which need specialized input handling.
Liz Kammer00629db2023-02-09 14:28:15 -0500263func (a *aqueryArtifactHandler) getInputPaths(depsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400264 var inputPaths []string
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400265
Liz Kammer00629db2023-02-09 14:28:15 -0500266 for _, id := range depsetIds {
267 inputDepSetId := depsetId(id)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400268 depset := a.depsetIdToAqueryDepset[inputDepSetId]
269 inputArtifacts, err := a.artifactPathsFromDepsetHash(depset.ContentHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400270 if err != nil {
271 return nil, err
272 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400273 for _, inputPath := range inputArtifacts {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400274 inputPaths = append(inputPaths, inputPath)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400275 }
276 }
Wei Li455ba832021-11-04 22:58:12 +0000277
Chris Parsons1a7aca02022-04-25 22:35:15 -0400278 return inputPaths, nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400279}
280
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400281func (a *aqueryArtifactHandler) artifactPathsFromDepsetHash(depsetHash string) ([]string, error) {
Liz Kammera4655a92023-02-10 17:17:28 -0500282 if result, exists := a.depsetHashToArtifactPathsCache.Load(depsetHash); exists {
283 return result.([]string), nil
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400284 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400285 if depset, exists := a.depsetHashToAqueryDepset[depsetHash]; exists {
286 result := depset.DirectArtifacts
287 for _, childHash := range depset.TransitiveDepSetHashes {
288 childArtifactIds, err := a.artifactPathsFromDepsetHash(childHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400289 if err != nil {
290 return nil, err
291 }
292 result = append(result, childArtifactIds...)
293 }
Liz Kammera4655a92023-02-10 17:17:28 -0500294 a.depsetHashToArtifactPathsCache.Store(depsetHash, result)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400295 return result, nil
296 } else {
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400297 return nil, fmt.Errorf("undefined input depset hash %s", depsetHash)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400298 }
299}
300
Chris Parsons1a7aca02022-04-25 22:35:15 -0400301// AqueryBuildStatements returns a slice of BuildStatements and a slice of AqueryDepset
Usta Shrestha6298cc52022-05-27 17:40:21 -0400302// which should be registered (and output to a ninja file) to correspond with Bazel's
Chris Parsons1a7aca02022-04-25 22:35:15 -0400303// action graph, as described by the given action graph json proto.
304// BuildStatements are one-to-one with actions in the given action graph, and AqueryDepsets
305// are one-to-one with Bazel's depSetOfFiles objects.
Liz Kammera4655a92023-02-10 17:17:28 -0500306func AqueryBuildStatements(aqueryJsonProto []byte, eventHandler *metrics.EventHandler) ([]*BuildStatement, []AqueryDepset, error) {
Jason Wu118fd2b2022-10-27 18:41:15 +0000307 aqueryProto := &analysis_v2_proto.ActionGraphContainer{}
308 err := proto.Unmarshal(aqueryJsonProto, aqueryProto)
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400309 if err != nil {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400310 return nil, nil, err
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400311 }
Chris Parsons8d6e4332021-02-22 16:13:50 -0500312
Liz Kammer690fbac2023-02-10 11:11:17 -0500313 var aqueryHandler *aqueryArtifactHandler
314 {
315 eventHandler.Begin("init_handler")
316 defer eventHandler.End("init_handler")
Liz Kammer00629db2023-02-09 14:28:15 -0500317 aqueryHandler, err = newAqueryHandler(aqueryProto)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400318 if err != nil {
319 return nil, nil, err
Chris Parsons8d6e4332021-02-22 16:13:50 -0500320 }
Liz Kammer690fbac2023-02-10 11:11:17 -0500321 }
322
Liz Kammera4655a92023-02-10 17:17:28 -0500323 // allocate both length and capacity so each goroutine can write to an index independently without
324 // any need for synchronization for slice access.
325 buildStatements := make([]*BuildStatement, len(aqueryProto.Actions))
Liz Kammer690fbac2023-02-10 11:11:17 -0500326 {
327 eventHandler.Begin("build_statements")
328 defer eventHandler.End("build_statements")
Liz Kammera4655a92023-02-10 17:17:28 -0500329 wg := sync.WaitGroup{}
330 var errOnce sync.Once
ustac4547812023-09-25 17:34:15 -0400331 id2targets := make(map[uint32]string, len(aqueryProto.Targets))
332 for _, t := range aqueryProto.Targets {
333 id2targets[t.GetId()] = t.GetLabel()
334 }
Liz Kammera4655a92023-02-10 17:17:28 -0500335 for i, actionEntry := range aqueryProto.Actions {
336 wg.Add(1)
337 go func(i int, actionEntry *analysis_v2_proto.Action) {
ustac4547812023-09-25 17:34:15 -0400338 if strings.HasPrefix(id2targets[actionEntry.TargetId], "@bazel_tools//") {
339 // bazel_tools are removed depsets in `populateDepsetMaps()` so skipping
340 // conversion to build statements as well
341 buildStatements[i] = nil
342 } else if buildStatement, aErr := aqueryHandler.actionToBuildStatement(actionEntry); aErr != nil {
Liz Kammera4655a92023-02-10 17:17:28 -0500343 errOnce.Do(func() {
ustac4547812023-09-25 17:34:15 -0400344 aErr = fmt.Errorf("%s: [%s] [%s]", aErr.Error(), actionEntry.GetMnemonic(), id2targets[actionEntry.TargetId])
Liz Kammera4655a92023-02-10 17:17:28 -0500345 err = aErr
346 })
347 } else {
348 // set build statement at an index rather than appending such that each goroutine does not
349 // impact other goroutines
350 buildStatements[i] = buildStatement
351 }
352 wg.Done()
353 }(i, actionEntry)
Liz Kammer690fbac2023-02-10 11:11:17 -0500354 }
Liz Kammera4655a92023-02-10 17:17:28 -0500355 wg.Wait()
356 }
357 if err != nil {
358 return nil, nil, err
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500359 }
360
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400361 depsetsByHash := map[string]AqueryDepset{}
Liz Kammer00629db2023-02-09 14:28:15 -0500362 depsets := make([]AqueryDepset, 0, len(aqueryHandler.depsetIdToAqueryDepset))
Liz Kammer690fbac2023-02-10 11:11:17 -0500363 {
364 eventHandler.Begin("depsets")
365 defer eventHandler.End("depsets")
366 for _, aqueryDepset := range aqueryHandler.depsetIdToAqueryDepset {
367 if prevEntry, hasKey := depsetsByHash[aqueryDepset.ContentHash]; hasKey {
368 // Two depsets collide on hash. Ensure that their contents are identical.
369 if !reflect.DeepEqual(aqueryDepset, prevEntry) {
370 return nil, nil, fmt.Errorf("two different depsets have the same hash: %v, %v", prevEntry, aqueryDepset)
371 }
372 } else {
373 depsetsByHash[aqueryDepset.ContentHash] = aqueryDepset
374 depsets = append(depsets, aqueryDepset)
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400375 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400376 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400377 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400378
Liz Kammer690fbac2023-02-10 11:11:17 -0500379 eventHandler.Do("build_statement_sort", func() {
380 // Build Statements and depsets must be sorted by their content hash to
381 // preserve determinism between builds (this will result in consistent ninja file
382 // output). Note they are not sorted by their original IDs nor their Bazel ordering,
383 // as Bazel gives nondeterministic ordering / identifiers in aquery responses.
384 sort.Slice(buildStatements, func(i, j int) bool {
Liz Kammera4655a92023-02-10 17:17:28 -0500385 // Sort all nil statements to the end of the slice
386 if buildStatements[i] == nil {
387 return false
388 } else if buildStatements[j] == nil {
389 return true
390 }
391 //For build statements, compare output lists. In Bazel, each output file
Liz Kammer690fbac2023-02-10 11:11:17 -0500392 // may only have one action which generates it, so this will provide
393 // a deterministic ordering.
394 outputs_i := buildStatements[i].OutputPaths
395 outputs_j := buildStatements[j].OutputPaths
396 if len(outputs_i) != len(outputs_j) {
397 return len(outputs_i) < len(outputs_j)
398 }
399 if len(outputs_i) == 0 {
400 // No outputs for these actions, so compare commands.
401 return buildStatements[i].Command < buildStatements[j].Command
402 }
403 // There may be multiple outputs, but the output ordering is deterministic.
404 return outputs_i[0] < outputs_j[0]
405 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400406 })
Liz Kammer690fbac2023-02-10 11:11:17 -0500407 eventHandler.Do("depset_sort", func() {
408 sort.Slice(depsets, func(i, j int) bool {
409 return depsets[i].ContentHash < depsets[j].ContentHash
410 })
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400411 })
Chris Parsons1a7aca02022-04-25 22:35:15 -0400412 return buildStatements, depsets, nil
413}
414
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400415// depsetContentHash computes and returns a SHA256 checksum of the contents of
416// the given depset. This content hash may serve as the depset's identifier.
417// Using a content hash for an identifier is superior for determinism. (For example,
418// using an integer identifier which depends on the order in which the depsets are
419// created would result in nondeterministic depset IDs.)
420func depsetContentHash(directPaths []string, transitiveDepsetHashes []string) string {
421 h := sha256.New()
422 // Use newline as delimiter, as paths cannot contain newline.
423 h.Write([]byte(strings.Join(directPaths, "\n")))
Usta Shrestha2ccdb422022-06-02 10:19:13 -0400424 h.Write([]byte(strings.Join(transitiveDepsetHashes, "")))
425 fullHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400426 return fullHash
427}
428
Liz Kammer00629db2023-02-09 14:28:15 -0500429func (a *aqueryArtifactHandler) depsetContentHashes(inputDepsetIds []uint32) ([]string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400430 var hashes []string
Liz Kammer00629db2023-02-09 14:28:15 -0500431 for _, id := range inputDepsetIds {
432 dId := depsetId(id)
433 if aqueryDepset, exists := a.depsetIdToAqueryDepset[dId]; !exists {
434 if _, empty := a.emptyDepsetIds[dId]; !empty {
435 return nil, fmt.Errorf("undefined (not even empty) input depsetId %d", dId)
Usta Shrestha13fd5ae2023-01-27 10:55:34 -0500436 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400437 } else {
438 hashes = append(hashes, aqueryDepset.ContentHash)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400439 }
440 }
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400441 return hashes, nil
Chris Parsons1a7aca02022-04-25 22:35:15 -0400442}
443
Spandan Dasda724862023-06-16 23:35:55 +0000444// escapes the args received from aquery and creates a command string
445func commandString(actionEntry *analysis_v2_proto.Action) string {
446 switch actionEntry.Mnemonic {
Spandan Das2d93ebb2023-07-27 23:46:24 +0000447 case "GoCompilePkg", "GoStdlib":
Spandan Dasda724862023-06-16 23:35:55 +0000448 argsEscaped := []string{}
449 for _, arg := range actionEntry.Arguments {
450 if arg == "" {
451 // If this is an empty string, add ''
452 // And not
453 // 1. (literal empty)
454 // 2. `''\'''\'''` (escaped version of '')
455 //
456 // If we had used (1), then this would appear as a whitespace when we strings.Join
457 argsEscaped = append(argsEscaped, "''")
458 } else {
459 argsEscaped = append(argsEscaped, proptools.ShellEscapeIncludingSpaces(arg))
460 }
461 }
462 return strings.Join(argsEscaped, " ")
463 default:
464 return strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
465 }
466}
467
Liz Kammer00629db2023-02-09 14:28:15 -0500468func (a *aqueryArtifactHandler) normalActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Spandan Dasda724862023-06-16 23:35:55 +0000469 command := commandString(actionEntry)
Usta Shresthac2372492022-05-27 10:45:00 -0400470 inputDepsetHashes, err := a.depsetContentHashes(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400471 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500472 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400473 }
Usta Shresthac2372492022-05-27 10:45:00 -0400474 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400475 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500476 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400477 }
478
Liz Kammer00629db2023-02-09 14:28:15 -0500479 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400480 Command: command,
481 Depfile: depfile,
482 OutputPaths: outputPaths,
483 InputDepsetHashes: inputDepsetHashes,
484 Env: actionEntry.EnvironmentVariables,
485 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400486 }
Spandan Dasaf4ccaa2023-06-29 01:15:51 +0000487 if buildStatement.Mnemonic == "GoToolchainBinaryBuild" {
488 // Unlike b's execution root, mixed build execution root contains a symlink to prebuilts/go
489 // This causes issues for `GOCACHE=$(mktemp -d) go build ...`
490 // To prevent this, sandbox this action in mixed builds as well
491 buildStatement.ShouldRunInSbox = true
492 }
Chris Parsons1a7aca02022-04-25 22:35:15 -0400493 return buildStatement, nil
494}
495
Liz Kammer00629db2023-02-09 14:28:15 -0500496func (a *aqueryArtifactHandler) templateExpandActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400497 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400498 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500499 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400500 }
501 if len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500502 return nil, fmt.Errorf("Expect 1 output to template expand action, got: output %q", outputPaths)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400503 }
504 expandedTemplateContent := expandTemplateContent(actionEntry)
505 // The expandedTemplateContent is escaped for being used in double quotes and shell unescape,
506 // and the new line characters (\n) are also changed to \\n which avoids some Ninja escape on \n, which might
507 // change \n to space and mess up the format of Python programs.
508 // sed is used to convert \\n back to \n before saving to output file.
509 // See go/python-binary-host-mixed-build for more details.
510 command := fmt.Sprintf(`/bin/bash -c 'echo "%[1]s" | sed "s/\\\\n/\\n/g" > %[2]s && chmod a+x %[2]s'`,
511 escapeCommandlineArgument(expandedTemplateContent), outputPaths[0])
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 }
516
Liz Kammer00629db2023-02-09 14:28:15 -0500517 buildStatement := &BuildStatement{
Chris Parsons0bfb1c02022-05-12 16:43:01 -0400518 Command: command,
519 Depfile: depfile,
520 OutputPaths: outputPaths,
521 InputDepsetHashes: inputDepsetHashes,
522 Env: actionEntry.EnvironmentVariables,
523 Mnemonic: actionEntry.Mnemonic,
Chris Parsons1a7aca02022-04-25 22:35:15 -0400524 }
525 return buildStatement, nil
526}
527
Liz Kammer00629db2023-02-09 14:28:15 -0500528func (a *aqueryArtifactHandler) fileWriteActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundak1da064c2022-06-08 16:36:16 -0700529 outputPaths, _, err := a.getOutputPaths(actionEntry)
530 var depsetHashes []string
531 if err == nil {
532 depsetHashes, err = a.depsetContentHashes(actionEntry.InputDepSetIds)
533 }
534 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500535 return nil, err
Sasha Smundak1da064c2022-06-08 16:36:16 -0700536 }
Liz Kammer00629db2023-02-09 14:28:15 -0500537 return &BuildStatement{
Sasha Smundak1da064c2022-06-08 16:36:16 -0700538 Depfile: nil,
539 OutputPaths: outputPaths,
540 Env: actionEntry.EnvironmentVariables,
541 Mnemonic: actionEntry.Mnemonic,
542 InputDepsetHashes: depsetHashes,
543 FileContents: actionEntry.FileContents,
Cole Faust20f20302023-08-31 11:00:25 -0700544 IsExecutable: actionEntry.IsExecutable,
Sasha Smundak1da064c2022-06-08 16:36:16 -0700545 }, nil
546}
547
Liz Kammer00629db2023-02-09 14:28:15 -0500548func (a *aqueryArtifactHandler) symlinkTreeActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700549 outputPaths, _, err := a.getOutputPaths(actionEntry)
550 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500551 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700552 }
553 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
554 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500555 return nil, err
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700556 }
557 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500558 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 -0700559 }
560 // The actual command is generated in bazelSingleton.GenerateBuildActions
Liz Kammer00629db2023-02-09 14:28:15 -0500561 return &BuildStatement{
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700562 Depfile: nil,
563 OutputPaths: outputPaths,
564 Env: actionEntry.EnvironmentVariables,
565 Mnemonic: actionEntry.Mnemonic,
566 InputPaths: inputPaths,
567 }, nil
568}
569
Cole Faustbc65a3f2023-08-01 16:38:55 +0000570type bazelSandwichJson struct {
571 Target string `json:"target"`
572 DependOnTarget *bool `json:"depend_on_target,omitempty"`
573 ImplicitDeps []string `json:"implicit_deps"`
574}
575
576func (a *aqueryArtifactHandler) unresolvedSymlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
577 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
578 if err != nil {
579 return nil, err
580 }
581 if len(actionEntry.InputDepSetIds) != 0 || len(outputPaths) != 1 {
582 return nil, fmt.Errorf("expected 0 inputs and 1 output to symlink action, got: input %q, output %q", actionEntry.InputDepSetIds, outputPaths)
583 }
584 target := actionEntry.UnresolvedSymlinkTarget
585 if target == "" {
586 return nil, fmt.Errorf("expected an unresolved_symlink_target, but didn't get one")
587 }
588 if filepath.Clean(target) != target {
589 return nil, fmt.Errorf("expected %q, got %q", filepath.Clean(target), target)
590 }
591 if strings.HasPrefix(target, "/") {
592 return nil, fmt.Errorf("no absolute symlinks allowed: %s", target)
593 }
594
595 out := outputPaths[0]
596 outDir := filepath.Dir(out)
597 var implicitDeps []string
598 if strings.HasPrefix(target, "bazel_sandwich:") {
599 j := bazelSandwichJson{}
600 err := json.Unmarshal([]byte(target[len("bazel_sandwich:"):]), &j)
601 if err != nil {
602 return nil, err
603 }
604 if proptools.BoolDefault(j.DependOnTarget, true) {
605 implicitDeps = append(implicitDeps, j.Target)
606 }
607 implicitDeps = append(implicitDeps, j.ImplicitDeps...)
608 dotDotsToReachCwd := ""
609 if outDir != "." {
610 dotDotsToReachCwd = strings.Repeat("../", strings.Count(outDir, "/")+1)
611 }
612 target = proptools.ShellEscapeIncludingSpaces(j.Target)
613 target = "{DOTDOTS_TO_OUTPUT_ROOT}" + dotDotsToReachCwd + target
614 } else {
615 target = proptools.ShellEscapeIncludingSpaces(target)
616 }
617
618 outDir = proptools.ShellEscapeIncludingSpaces(outDir)
619 out = proptools.ShellEscapeIncludingSpaces(out)
620 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
621 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, target)
622 symlinkPaths := outputPaths[:]
623
624 buildStatement := &BuildStatement{
625 Command: command,
626 Depfile: depfile,
627 OutputPaths: outputPaths,
628 Env: actionEntry.EnvironmentVariables,
629 Mnemonic: actionEntry.Mnemonic,
630 SymlinkPaths: symlinkPaths,
631 ImplicitDeps: implicitDeps,
632 }
633 return buildStatement, nil
634}
635
Liz Kammer00629db2023-02-09 14:28:15 -0500636func (a *aqueryArtifactHandler) symlinkActionBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
Usta Shresthac2372492022-05-27 10:45:00 -0400637 outputPaths, depfile, err := a.getOutputPaths(actionEntry)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400638 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500639 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400640 }
641
Usta Shresthac2372492022-05-27 10:45:00 -0400642 inputPaths, err := a.getInputPaths(actionEntry.InputDepSetIds)
Chris Parsons1a7aca02022-04-25 22:35:15 -0400643 if err != nil {
Liz Kammer00629db2023-02-09 14:28:15 -0500644 return nil, err
Chris Parsons1a7aca02022-04-25 22:35:15 -0400645 }
646 if len(inputPaths) != 1 || len(outputPaths) != 1 {
Liz Kammer00629db2023-02-09 14:28:15 -0500647 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 -0400648 }
649 out := outputPaths[0]
650 outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
651 out = proptools.ShellEscapeIncludingSpaces(out)
652 in := filepath.Join("$PWD", proptools.ShellEscapeIncludingSpaces(inputPaths[0]))
653 // Use absolute paths, because some soong actions don't play well with relative paths (for example, `cp -d`).
654 command := fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -sf %[3]s %[2]s", outDir, out, in)
655 symlinkPaths := outputPaths[:]
656
Liz Kammer00629db2023-02-09 14:28:15 -0500657 buildStatement := &BuildStatement{
Chris Parsons1a7aca02022-04-25 22:35:15 -0400658 Command: command,
659 Depfile: depfile,
660 OutputPaths: outputPaths,
661 InputPaths: inputPaths,
662 Env: actionEntry.EnvironmentVariables,
663 Mnemonic: actionEntry.Mnemonic,
664 SymlinkPaths: symlinkPaths,
665 }
666 return buildStatement, nil
667}
668
Liz Kammer00629db2023-02-09 14:28:15 -0500669func (a *aqueryArtifactHandler) getOutputPaths(actionEntry *analysis_v2_proto.Action) (outputPaths []string, depfile *string, err error) {
Chris Parsons1a7aca02022-04-25 22:35:15 -0400670 for _, outputId := range actionEntry.OutputIds {
Liz Kammer00629db2023-02-09 14:28:15 -0500671 outputPath, exists := a.artifactIdToPath[artifactId(outputId)]
Chris Parsons1a7aca02022-04-25 22:35:15 -0400672 if !exists {
673 err = fmt.Errorf("undefined outputId %d", outputId)
674 return
675 }
676 ext := filepath.Ext(outputPath)
677 if ext == ".d" {
678 if depfile != nil {
679 err = fmt.Errorf("found multiple potential depfiles %q, %q", *depfile, outputPath)
680 return
681 } else {
682 depfile = &outputPath
683 }
684 } else {
685 outputPaths = append(outputPaths, outputPath)
686 }
687 }
688 return
Chris Parsonsdbcb1ff2020-12-10 17:19:18 -0500689}
Chris Parsonsaffbb602020-12-23 12:02:11 -0500690
Wei Li455ba832021-11-04 22:58:12 +0000691// expandTemplateContent substitutes the tokens in a template.
Liz Kammer00629db2023-02-09 14:28:15 -0500692func expandTemplateContent(actionEntry *analysis_v2_proto.Action) string {
693 replacerString := make([]string, len(actionEntry.Substitutions)*2)
694 for i, pair := range actionEntry.Substitutions {
Wei Li455ba832021-11-04 22:58:12 +0000695 value := pair.Value
Usta Shrestha6298cc52022-05-27 17:40:21 -0400696 if val, ok := templateActionOverriddenTokens[pair.Key]; ok {
Wei Li455ba832021-11-04 22:58:12 +0000697 value = val
698 }
Liz Kammer00629db2023-02-09 14:28:15 -0500699 replacerString[i*2] = pair.Key
700 replacerString[i*2+1] = value
Wei Li455ba832021-11-04 22:58:12 +0000701 }
702 replacer := strings.NewReplacer(replacerString...)
703 return replacer.Replace(actionEntry.TemplateContent)
704}
705
Liz Kammerf15a0792023-02-09 14:28:36 -0500706// \->\\, $->\$, `->\`, "->\", \n->\\n, '->'"'"'
707var commandLineArgumentReplacer = strings.NewReplacer(
708 `\`, `\\`,
709 `$`, `\$`,
710 "`", "\\`",
711 `"`, `\"`,
712 "\n", "\\n",
713 `'`, `'"'"'`,
714)
715
Wei Li455ba832021-11-04 22:58:12 +0000716func escapeCommandlineArgument(str string) string {
Liz Kammerf15a0792023-02-09 14:28:36 -0500717 return commandLineArgumentReplacer.Replace(str)
Wei Li455ba832021-11-04 22:58:12 +0000718}
719
Liz Kammer00629db2023-02-09 14:28:15 -0500720func (a *aqueryArtifactHandler) actionToBuildStatement(actionEntry *analysis_v2_proto.Action) (*BuildStatement, error) {
721 switch actionEntry.Mnemonic {
Chris Parsonsc4fb1332021-05-18 12:31:25 -0400722 // Middleman actions are not handled like other actions; they are handled separately as a
723 // preparatory step so that their inputs may be relayed to actions depending on middleman
724 // artifacts.
Liz Kammer00629db2023-02-09 14:28:15 -0500725 case middlemanMnemonic:
726 return nil, nil
Sasha Smundakc180dbd2022-07-03 14:55:58 -0700727 // PythonZipper is bogus action returned by aquery, ignore it (b/236198693)
Liz Kammer00629db2023-02-09 14:28:15 -0500728 case "PythonZipper":
729 return nil, nil
Chris Parsons8d6e4332021-02-22 16:13:50 -0500730 // Skip "Fail" actions, which are placeholder actions designed to always fail.
Liz Kammer00629db2023-02-09 14:28:15 -0500731 case "Fail":
732 return nil, nil
733 case "BaselineCoverage":
734 return nil, nil
735 case "Symlink", "SolibSymlink", "ExecutableSymlink":
736 return a.symlinkActionBuildStatement(actionEntry)
737 case "TemplateExpand":
738 if len(actionEntry.Arguments) < 1 {
739 return a.templateExpandActionBuildStatement(actionEntry)
740 }
Cole Faust950689a2023-06-21 15:07:21 -0700741 case "FileWrite", "SourceSymlinkManifest", "RepoMappingManifest":
Liz Kammer00629db2023-02-09 14:28:15 -0500742 return a.fileWriteActionBuildStatement(actionEntry)
743 case "SymlinkTree":
744 return a.symlinkTreeActionBuildStatement(actionEntry)
Cole Faustbc65a3f2023-08-01 16:38:55 +0000745 case "UnresolvedSymlink":
746 return a.unresolvedSymlinkActionBuildStatement(actionEntry)
Chris Parsons8d6e4332021-02-22 16:13:50 -0500747 }
Liz Kammer00629db2023-02-09 14:28:15 -0500748
749 if len(actionEntry.Arguments) < 1 {
ustaa79afd72023-09-22 17:29:56 -0400750 return nil, errors.New("received action with no command")
Yu Liu8d82ac52022-05-17 15:13:28 -0700751 }
Liz Kammer00629db2023-02-09 14:28:15 -0500752 return a.normalActionBuildStatement(actionEntry)
753
Chris Parsons8d6e4332021-02-22 16:13:50 -0500754}
755
Liz Kammer00629db2023-02-09 14:28:15 -0500756func expandPathFragment(id pathFragmentId, pathFragmentsMap map[pathFragmentId]*analysis_v2_proto.PathFragment) (string, error) {
Usta Shrestha6298cc52022-05-27 17:40:21 -0400757 var labels []string
Chris Parsonsaffbb602020-12-23 12:02:11 -0500758 currId := id
759 // Only positive IDs are valid for path fragments. An ID of zero indicates a terminal node.
760 for currId > 0 {
761 currFragment, ok := pathFragmentsMap[currId]
762 if !ok {
Chris Parsons4f069892021-01-15 12:22:41 -0500763 return "", fmt.Errorf("undefined path fragment id %d", currId)
Chris Parsonsaffbb602020-12-23 12:02:11 -0500764 }
765 labels = append([]string{currFragment.Label}, labels...)
Liz Kammer00629db2023-02-09 14:28:15 -0500766 parentId := pathFragmentId(currFragment.ParentId)
767 if currId == parentId {
Sasha Smundakfe9a5b82022-07-27 14:51:45 -0700768 return "", fmt.Errorf("fragment cannot refer to itself as parent %#v", currFragment)
Liz Kammerc49e6822021-06-08 15:04:11 -0400769 }
Liz Kammer00629db2023-02-09 14:28:15 -0500770 currId = parentId
Chris Parsonsaffbb602020-12-23 12:02:11 -0500771 }
772 return filepath.Join(labels...), nil
773}