blob: fa8810f0d4e14d85506a1b7834602b4ac8e82c92 [file] [log] [blame]
Chris Parsons4f069892021-01-15 12:22:41 -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 (
18 "fmt"
19 "reflect"
20 "testing"
21)
22
23func TestAqueryMultiArchGenrule(t *testing.T) {
24 // This input string is retrieved from a real build of bionic-related genrules.
25 const inputString = `
26{
27 "artifacts": [{
28 "id": 1,
29 "pathFragmentId": 1
30 }, {
31 "id": 2,
32 "pathFragmentId": 6
33 }, {
34 "id": 3,
35 "pathFragmentId": 8
36 }, {
37 "id": 4,
38 "pathFragmentId": 12
39 }, {
40 "id": 5,
41 "pathFragmentId": 19
42 }, {
43 "id": 6,
44 "pathFragmentId": 20
45 }, {
46 "id": 7,
47 "pathFragmentId": 21
48 }],
49 "actions": [{
50 "targetId": 1,
51 "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
52 "mnemonic": "Genrule",
53 "configurationId": 1,
54 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm.S"],
55 "environmentVariables": [{
56 "key": "PATH",
57 "value": "/bin:/usr/bin:/usr/local/bin"
58 }],
59 "inputDepSetIds": [1],
60 "outputIds": [4],
61 "primaryOutputId": 4
62 }, {
63 "targetId": 2,
64 "actionKey": "9f4309ce165dac458498cb92811c18b0b7919782cc37b82a42d2141b8cc90826",
65 "mnemonic": "Genrule",
66 "configurationId": 1,
67 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86.S"],
68 "environmentVariables": [{
69 "key": "PATH",
70 "value": "/bin:/usr/bin:/usr/local/bin"
71 }],
72 "inputDepSetIds": [2],
73 "outputIds": [5],
74 "primaryOutputId": 5
75 }, {
76 "targetId": 3,
77 "actionKey": "50d6c586103ebeed3a218195540bcc30d329464eae36377eb82f8ce7c36ac342",
78 "mnemonic": "Genrule",
79 "configurationId": 1,
80 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py x86_64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-x86_64.S"],
81 "environmentVariables": [{
82 "key": "PATH",
83 "value": "/bin:/usr/bin:/usr/local/bin"
84 }],
85 "inputDepSetIds": [3],
86 "outputIds": [6],
87 "primaryOutputId": 6
88 }, {
89 "targetId": 4,
90 "actionKey": "f30cbe442f5216f4223cf16a39112cad4ec56f31f49290d85cff587e48647ffa",
91 "mnemonic": "Genrule",
92 "configurationId": 1,
93 "arguments": ["/bin/bash", "-c", "source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py arm64 ../sourceroot/bionic/libc/SYSCALLS.TXT \u003e bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-arm64.S"],
94 "environmentVariables": [{
95 "key": "PATH",
96 "value": "/bin:/usr/bin:/usr/local/bin"
97 }],
98 "inputDepSetIds": [4],
99 "outputIds": [7],
100 "primaryOutputId": 7
101 }],
102 "targets": [{
103 "id": 1,
104 "label": "@sourceroot//bionic/libc:syscalls-arm",
105 "ruleClassId": 1
106 }, {
107 "id": 2,
108 "label": "@sourceroot//bionic/libc:syscalls-x86",
109 "ruleClassId": 1
110 }, {
111 "id": 3,
112 "label": "@sourceroot//bionic/libc:syscalls-x86_64",
113 "ruleClassId": 1
114 }, {
115 "id": 4,
116 "label": "@sourceroot//bionic/libc:syscalls-arm64",
117 "ruleClassId": 1
118 }],
119 "depSetOfFiles": [{
120 "id": 1,
121 "directArtifactIds": [1, 2, 3]
122 }, {
123 "id": 2,
124 "directArtifactIds": [1, 2, 3]
125 }, {
126 "id": 3,
127 "directArtifactIds": [1, 2, 3]
128 }, {
129 "id": 4,
130 "directArtifactIds": [1, 2, 3]
131 }],
132 "configuration": [{
133 "id": 1,
134 "mnemonic": "k8-fastbuild",
135 "platformName": "k8",
136 "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
137 }],
138 "ruleClasses": [{
139 "id": 1,
140 "name": "genrule"
141 }],
142 "pathFragments": [{
143 "id": 5,
144 "label": ".."
145 }, {
146 "id": 4,
147 "label": "sourceroot",
148 "parentId": 5
149 }, {
150 "id": 3,
151 "label": "bionic",
152 "parentId": 4
153 }, {
154 "id": 2,
155 "label": "libc",
156 "parentId": 3
157 }, {
158 "id": 1,
159 "label": "SYSCALLS.TXT",
160 "parentId": 2
161 }, {
162 "id": 7,
163 "label": "tools",
164 "parentId": 2
165 }, {
166 "id": 6,
167 "label": "gensyscalls.py",
168 "parentId": 7
169 }, {
170 "id": 11,
171 "label": "bazel_tools",
172 "parentId": 5
173 }, {
174 "id": 10,
175 "label": "tools",
176 "parentId": 11
177 }, {
178 "id": 9,
179 "label": "genrule",
180 "parentId": 10
181 }, {
182 "id": 8,
183 "label": "genrule-setup.sh",
184 "parentId": 9
185 }, {
186 "id": 18,
187 "label": "bazel-out"
188 }, {
189 "id": 17,
190 "label": "sourceroot",
191 "parentId": 18
192 }, {
193 "id": 16,
194 "label": "k8-fastbuild",
195 "parentId": 17
196 }, {
197 "id": 15,
198 "label": "bin",
199 "parentId": 16
200 }, {
201 "id": 14,
202 "label": "bionic",
203 "parentId": 15
204 }, {
205 "id": 13,
206 "label": "libc",
207 "parentId": 14
208 }, {
209 "id": 12,
210 "label": "syscalls-arm.S",
211 "parentId": 13
212 }, {
213 "id": 19,
214 "label": "syscalls-x86.S",
215 "parentId": 13
216 }, {
217 "id": 20,
218 "label": "syscalls-x86_64.S",
219 "parentId": 13
220 }, {
221 "id": 21,
222 "label": "syscalls-arm64.S",
223 "parentId": 13
224 }]
225}`
226 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
227 expectedBuildStatements := []BuildStatement{}
228 for _, arch := range []string{"arm", "arm64", "x86", "x86_64"} {
229 expectedBuildStatements = append(expectedBuildStatements,
230 BuildStatement{
231 Command: fmt.Sprintf(
232 "/bin/bash -c 'source ../bazel_tools/tools/genrule/genrule-setup.sh; ../sourceroot/bionic/libc/tools/gensyscalls.py %s ../sourceroot/bionic/libc/SYSCALLS.TXT > bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S'",
233 arch, arch),
234 OutputPaths: []string{
235 fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/bionic/libc/syscalls-%s.S", arch),
236 },
237 InputPaths: []string{
238 "../sourceroot/bionic/libc/SYSCALLS.TXT",
239 "../sourceroot/bionic/libc/tools/gensyscalls.py",
240 "../bazel_tools/tools/genrule/genrule-setup.sh",
241 },
242 Env: []KeyValuePair{
243 KeyValuePair{Key: "PATH", Value: "/bin:/usr/bin:/usr/local/bin"},
244 },
245 Mnemonic: "Genrule",
246 })
247 }
248 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
249}
250
251func TestInvalidOutputId(t *testing.T) {
252 const inputString = `
253{
254 "artifacts": [{
255 "id": 1,
256 "pathFragmentId": 1
257 }, {
258 "id": 2,
259 "pathFragmentId": 2
260 }],
261 "actions": [{
262 "targetId": 1,
263 "actionKey": "x",
264 "mnemonic": "x",
265 "arguments": ["touch", "foo"],
266 "inputDepSetIds": [1],
267 "outputIds": [3],
268 "primaryOutputId": 3
269 }],
270 "depSetOfFiles": [{
271 "id": 1,
272 "directArtifactIds": [1, 2]
273 }],
274 "pathFragments": [{
275 "id": 1,
276 "label": "one"
277 }, {
278 "id": 2,
279 "label": "two"
280 }]
281}`
282
283 _, err := AqueryBuildStatements([]byte(inputString))
284 assertError(t, err, "undefined outputId 3")
285}
286
287func TestInvalidInputDepsetId(t *testing.T) {
288 const inputString = `
289{
290 "artifacts": [{
291 "id": 1,
292 "pathFragmentId": 1
293 }, {
294 "id": 2,
295 "pathFragmentId": 2
296 }],
297 "actions": [{
298 "targetId": 1,
299 "actionKey": "x",
300 "mnemonic": "x",
301 "arguments": ["touch", "foo"],
302 "inputDepSetIds": [2],
303 "outputIds": [1],
304 "primaryOutputId": 1
305 }],
306 "depSetOfFiles": [{
307 "id": 1,
308 "directArtifactIds": [1, 2]
309 }],
310 "pathFragments": [{
311 "id": 1,
312 "label": "one"
313 }, {
314 "id": 2,
315 "label": "two"
316 }]
317}`
318
319 _, err := AqueryBuildStatements([]byte(inputString))
320 assertError(t, err, "undefined input depsetId 2")
321}
322
323func TestInvalidInputArtifactId(t *testing.T) {
324 const inputString = `
325{
326 "artifacts": [{
327 "id": 1,
328 "pathFragmentId": 1
329 }, {
330 "id": 2,
331 "pathFragmentId": 2
332 }],
333 "actions": [{
334 "targetId": 1,
335 "actionKey": "x",
336 "mnemonic": "x",
337 "arguments": ["touch", "foo"],
338 "inputDepSetIds": [1],
339 "outputIds": [1],
340 "primaryOutputId": 1
341 }],
342 "depSetOfFiles": [{
343 "id": 1,
344 "directArtifactIds": [1, 3]
345 }],
346 "pathFragments": [{
347 "id": 1,
348 "label": "one"
349 }, {
350 "id": 2,
351 "label": "two"
352 }]
353}`
354
355 _, err := AqueryBuildStatements([]byte(inputString))
356 assertError(t, err, "undefined input artifactId 3")
357}
358
359func TestInvalidPathFragmentId(t *testing.T) {
360 const inputString = `
361{
362 "artifacts": [{
363 "id": 1,
364 "pathFragmentId": 1
365 }, {
366 "id": 2,
367 "pathFragmentId": 2
368 }],
369 "actions": [{
370 "targetId": 1,
371 "actionKey": "x",
372 "mnemonic": "x",
373 "arguments": ["touch", "foo"],
374 "inputDepSetIds": [1],
375 "outputIds": [1],
376 "primaryOutputId": 1
377 }],
378 "depSetOfFiles": [{
379 "id": 1,
380 "directArtifactIds": [1, 2]
381 }],
382 "pathFragments": [{
383 "id": 1,
384 "label": "one"
385 }, {
386 "id": 2,
387 "label": "two",
388 "parentId": 3
389 }]
390}`
391
392 _, err := AqueryBuildStatements([]byte(inputString))
393 assertError(t, err, "undefined path fragment id 3")
394}
395
Liz Kammerde116852021-03-25 16:42:37 -0400396func TestDepfiles(t *testing.T) {
397 const inputString = `
398{
399 "artifacts": [{
400 "id": 1,
401 "pathFragmentId": 1
402 }, {
403 "id": 2,
404 "pathFragmentId": 2
405 }, {
406 "id": 3,
407 "pathFragmentId": 3
408 }],
409 "actions": [{
410 "targetId": 1,
411 "actionKey": "x",
412 "mnemonic": "x",
413 "arguments": ["touch", "foo"],
414 "inputDepSetIds": [1],
415 "outputIds": [2, 3],
416 "primaryOutputId": 2
417 }],
418 "depSetOfFiles": [{
419 "id": 1,
420 "directArtifactIds": [1, 2, 3]
421 }],
422 "pathFragments": [{
423 "id": 1,
424 "label": "one"
425 }, {
426 "id": 2,
427 "label": "two"
428 }, {
429 "id": 3,
430 "label": "two.d"
431 }]
432}`
433
434 actual, err := AqueryBuildStatements([]byte(inputString))
435 if err != nil {
436 t.Errorf("Unexpected error %q", err)
437 }
438 if expected := 1; len(actual) != expected {
439 t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
440 }
441
442 bs := actual[0]
443 expectedDepfile := "two.d"
444 if bs.Depfile == nil {
445 t.Errorf("Expected depfile %q, but there was none found", expectedDepfile)
446 } else if *bs.Depfile != expectedDepfile {
447 t.Errorf("Expected depfile %q, but got %q", expectedDepfile, *bs.Depfile)
448 }
449}
450
451func TestMultipleDepfiles(t *testing.T) {
452 const inputString = `
453{
454 "artifacts": [{
455 "id": 1,
456 "pathFragmentId": 1
457 }, {
458 "id": 2,
459 "pathFragmentId": 2
460 }, {
461 "id": 3,
462 "pathFragmentId": 3
463 }, {
464 "id": 4,
465 "pathFragmentId": 4
466 }],
467 "actions": [{
468 "targetId": 1,
469 "actionKey": "x",
470 "mnemonic": "x",
471 "arguments": ["touch", "foo"],
472 "inputDepSetIds": [1],
473 "outputIds": [2,3,4],
474 "primaryOutputId": 2
475 }],
476 "depSetOfFiles": [{
477 "id": 1,
478 "directArtifactIds": [1, 2, 3, 4]
479 }],
480 "pathFragments": [{
481 "id": 1,
482 "label": "one"
483 }, {
484 "id": 2,
485 "label": "two"
486 }, {
487 "id": 3,
488 "label": "two.d"
489 }, {
490 "id": 4,
491 "label": "other.d"
492 }]
493}`
494
495 _, err := AqueryBuildStatements([]byte(inputString))
496 assertError(t, err, `found multiple potential depfiles "two.d", "other.d"`)
497}
498
Chris Parsons943f2432021-01-19 11:36:50 -0500499func TestTransitiveInputDepsets(t *testing.T) {
500 // The input aquery for this test comes from a proof-of-concept starlark rule which registers
501 // a single action with many inputs given via a deep depset.
502 const inputString = `
503{
504 "artifacts": [{
505 "id": 1,
506 "pathFragmentId": 1
507 }, {
508 "id": 2,
509 "pathFragmentId": 7
510 }, {
511 "id": 3,
512 "pathFragmentId": 8
513 }, {
514 "id": 4,
515 "pathFragmentId": 9
516 }, {
517 "id": 5,
518 "pathFragmentId": 10
519 }, {
520 "id": 6,
521 "pathFragmentId": 11
522 }, {
523 "id": 7,
524 "pathFragmentId": 12
525 }, {
526 "id": 8,
527 "pathFragmentId": 13
528 }, {
529 "id": 9,
530 "pathFragmentId": 14
531 }, {
532 "id": 10,
533 "pathFragmentId": 15
534 }, {
535 "id": 11,
536 "pathFragmentId": 16
537 }, {
538 "id": 12,
539 "pathFragmentId": 17
540 }, {
541 "id": 13,
542 "pathFragmentId": 18
543 }, {
544 "id": 14,
545 "pathFragmentId": 19
546 }, {
547 "id": 15,
548 "pathFragmentId": 20
549 }, {
550 "id": 16,
551 "pathFragmentId": 21
552 }, {
553 "id": 17,
554 "pathFragmentId": 22
555 }, {
556 "id": 18,
557 "pathFragmentId": 23
558 }, {
559 "id": 19,
560 "pathFragmentId": 24
561 }, {
562 "id": 20,
563 "pathFragmentId": 25
564 }, {
565 "id": 21,
566 "pathFragmentId": 26
567 }],
568 "actions": [{
569 "targetId": 1,
570 "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
571 "mnemonic": "Action",
572 "configurationId": 1,
573 "arguments": ["/bin/bash", "-c", "touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"],
574 "inputDepSetIds": [1],
575 "outputIds": [21],
576 "primaryOutputId": 21
577 }],
578 "depSetOfFiles": [{
579 "id": 3,
580 "directArtifactIds": [1, 2, 3, 4, 5]
581 }, {
582 "id": 4,
583 "directArtifactIds": [6, 7, 8, 9, 10]
584 }, {
585 "id": 2,
586 "transitiveDepSetIds": [3, 4],
587 "directArtifactIds": [11, 12, 13, 14, 15]
588 }, {
589 "id": 5,
590 "directArtifactIds": [16, 17, 18, 19]
591 }, {
592 "id": 1,
593 "transitiveDepSetIds": [2, 5],
594 "directArtifactIds": [20]
595 }],
596 "pathFragments": [{
597 "id": 6,
598 "label": "bazel-out"
599 }, {
600 "id": 5,
601 "label": "sourceroot",
602 "parentId": 6
603 }, {
604 "id": 4,
605 "label": "k8-fastbuild",
606 "parentId": 5
607 }, {
608 "id": 3,
609 "label": "bin",
610 "parentId": 4
611 }, {
612 "id": 2,
613 "label": "testpkg",
614 "parentId": 3
615 }, {
616 "id": 1,
617 "label": "test_1",
618 "parentId": 2
619 }, {
620 "id": 7,
621 "label": "test_2",
622 "parentId": 2
623 }, {
624 "id": 8,
625 "label": "test_3",
626 "parentId": 2
627 }, {
628 "id": 9,
629 "label": "test_4",
630 "parentId": 2
631 }, {
632 "id": 10,
633 "label": "test_5",
634 "parentId": 2
635 }, {
636 "id": 11,
637 "label": "test_6",
638 "parentId": 2
639 }, {
640 "id": 12,
641 "label": "test_7",
642 "parentId": 2
643 }, {
644 "id": 13,
645 "label": "test_8",
646 "parentId": 2
647 }, {
648 "id": 14,
649 "label": "test_9",
650 "parentId": 2
651 }, {
652 "id": 15,
653 "label": "test_10",
654 "parentId": 2
655 }, {
656 "id": 16,
657 "label": "test_11",
658 "parentId": 2
659 }, {
660 "id": 17,
661 "label": "test_12",
662 "parentId": 2
663 }, {
664 "id": 18,
665 "label": "test_13",
666 "parentId": 2
667 }, {
668 "id": 19,
669 "label": "test_14",
670 "parentId": 2
671 }, {
672 "id": 20,
673 "label": "test_15",
674 "parentId": 2
675 }, {
676 "id": 21,
677 "label": "test_16",
678 "parentId": 2
679 }, {
680 "id": 22,
681 "label": "test_17",
682 "parentId": 2
683 }, {
684 "id": 23,
685 "label": "test_18",
686 "parentId": 2
687 }, {
688 "id": 24,
689 "label": "test_19",
690 "parentId": 2
691 }, {
692 "id": 25,
693 "label": "test_root",
694 "parentId": 2
695 }, {
696 "id": 26,
697 "label": "test_out",
698 "parentId": 2
699 }]
700}`
701
702 actualbuildStatements, _ := AqueryBuildStatements([]byte(inputString))
703 // Inputs for the action are test_{i} from 1 to 20, and test_root. These inputs
704 // are given via a deep depset, but the depset is flattened when returned as a
705 // BuildStatement slice.
706 inputPaths := []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_root"}
707 for i := 1; i < 20; i++ {
708 inputPaths = append(inputPaths, fmt.Sprintf("bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_%d", i))
709 }
710 expectedBuildStatements := []BuildStatement{
711 BuildStatement{
712 Command: "/bin/bash -c touch bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out",
713 OutputPaths: []string{"bazel-out/sourceroot/k8-fastbuild/bin/testpkg/test_out"},
714 InputPaths: inputPaths,
715 Mnemonic: "Action",
716 },
717 }
718 assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
719}
720
Chris Parsons4f069892021-01-15 12:22:41 -0500721func assertError(t *testing.T, err error, expected string) {
Chris Parsons943f2432021-01-19 11:36:50 -0500722 if err == nil {
723 t.Errorf("expected error '%s', but got no error", expected)
724 } else if err.Error() != expected {
725 t.Errorf("expected error '%s', but got: %s", expected, err.Error())
Chris Parsons4f069892021-01-15 12:22:41 -0500726 }
727}
728
729// Asserts that the given actual build statements match the given expected build statements.
730// Build statement equivalence is determined using buildStatementEquals.
731func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
732 if len(expected) != len(actual) {
Liz Kammerde116852021-03-25 16:42:37 -0400733 t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
Chris Parsons4f069892021-01-15 12:22:41 -0500734 len(expected), len(actual), expected, actual)
735 return
736 }
737ACTUAL_LOOP:
738 for _, actualStatement := range actual {
739 for _, expectedStatement := range expected {
740 if buildStatementEquals(actualStatement, expectedStatement) {
741 continue ACTUAL_LOOP
742 }
743 }
Liz Kammerde116852021-03-25 16:42:37 -0400744 t.Errorf("unexpected build statement %v.\n expected: %v",
Chris Parsons4f069892021-01-15 12:22:41 -0500745 actualStatement, expected)
746 return
747 }
748}
749
750func buildStatementEquals(first BuildStatement, second BuildStatement) bool {
751 if first.Mnemonic != second.Mnemonic {
752 return false
753 }
754 if first.Command != second.Command {
755 return false
756 }
757 // Ordering is significant for environment variables.
758 if !reflect.DeepEqual(first.Env, second.Env) {
759 return false
760 }
761 // Ordering is irrelevant for input and output paths, so compare sets.
762 if !reflect.DeepEqual(stringSet(first.InputPaths), stringSet(second.InputPaths)) {
763 return false
764 }
765 if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
766 return false
767 }
768 return true
769}
770
771func stringSet(stringSlice []string) map[string]struct{} {
772 stringMap := make(map[string]struct{})
773 for _, s := range stringSlice {
774 stringMap[s] = struct{}{}
775 }
776 return stringMap
777}