1#!/bin/bash -eu 2 3set -o pipefail 4 5# This test exercises the bootstrapping process of the build system 6# in a source tree that only contains enough files for Bazel and Soong to work. 7 8source "$(dirname "$0")/lib.sh" 9 10function test_smoke { 11 setup 12 run_soong 13} 14 15function test_null_build() { 16 setup 17 run_soong 18 local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja) 19 local output_mtime1=$(stat -c "%y" out/soong/build.ninja) 20 run_soong 21 local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja) 22 local output_mtime2=$(stat -c "%y" out/soong/build.ninja) 23 24 if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then 25 # Bootstrapping is always done. It doesn't take a measurable amount of time. 26 fail "Bootstrap Ninja file did not change on null build" 27 fi 28 29 if [[ "$output_mtime1" != "$output_mtime2" ]]; then 30 fail "Output Ninja file changed on null build" 31 fi 32} 33 34function test_soong_build_rebuilt_if_blueprint_changes() { 35 setup 36 run_soong 37 local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja) 38 39 sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go 40 41 run_soong 42 local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja) 43 44 if [[ "$mtime1" == "$mtime2" ]]; then 45 fail "Bootstrap Ninja file did not change" 46 fi 47} 48 49function test_change_android_bp() { 50 setup 51 mkdir -p a 52 cat > a/Android.bp <<'EOF' 53python_binary_host { 54 name: "my_little_binary_host", 55 srcs: ["my_little_binary_host.py"] 56} 57EOF 58 touch a/my_little_binary_host.py 59 run_soong 60 61 grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found" 62 63 cat > a/Android.bp <<'EOF' 64python_binary_host { 65 name: "my_great_binary_host", 66 srcs: ["my_great_binary_host.py"] 67} 68EOF 69 touch a/my_great_binary_host.py 70 run_soong 71 72 grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found" 73 grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found" 74} 75 76 77function test_add_android_bp() { 78 setup 79 run_soong 80 local mtime1=$(stat -c "%y" out/soong/build.ninja) 81 82 mkdir -p a 83 cat > a/Android.bp <<'EOF' 84python_binary_host { 85 name: "my_little_binary_host", 86 srcs: ["my_little_binary_host.py"] 87} 88EOF 89 touch a/my_little_binary_host.py 90 run_soong 91 92 local mtime2=$(stat -c "%y" out/soong/build.ninja) 93 if [[ "$mtime1" == "$mtime2" ]]; then 94 fail "Output Ninja file did not change" 95 fi 96 97 grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output" 98 99 run_soong 100} 101 102function test_delete_android_bp() { 103 setup 104 mkdir -p a 105 cat > a/Android.bp <<'EOF' 106python_binary_host { 107 name: "my_little_binary_host", 108 srcs: ["my_little_binary_host.py"] 109} 110EOF 111 touch a/my_little_binary_host.py 112 run_soong 113 114 grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output" 115 116 rm a/Android.bp 117 run_soong 118 119 if grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja; then 120 fail "Old module in output" 121 fi 122} 123 124# Test that an incremental build with a glob doesn't rerun soong_build, and 125# only regenerates the globs on the first but not the second incremental build. 126function test_glob_noop_incremental() { 127 setup 128 129 # This test needs to start from a clean build, but setup creates an 130 # initialized tree that has already been built once. Clear the out 131 # directory to start from scratch (see b/185591972) 132 rm -rf out 133 134 mkdir -p a 135 cat > a/Android.bp <<'EOF' 136python_binary_host { 137 name: "my_little_binary_host", 138 srcs: ["*.py"], 139} 140EOF 141 touch a/my_little_binary_host.py 142 run_soong 143 local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja) 144 145 local glob_deps_file=out/soong/.primary/globs/0.d 146 147 if [ -e "$glob_deps_file" ]; then 148 fail "Glob deps file unexpectedly written on first build" 149 fi 150 151 run_soong 152 local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja) 153 154 # There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update 155 # the entry in the .ninja_log. It doesn't update the output file, but we can detect the rerun 156 # by checking if the deps file was created. 157 if [ ! -e "$glob_deps_file" ]; then 158 fail "Glob deps file missing after second build" 159 fi 160 161 local glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file") 162 163 if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then 164 fail "Ninja file rewritten on null incremental build" 165 fi 166 167 run_soong 168 local ninja_mtime3=$(stat -c "%y" out/soong/build.ninja) 169 local glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file") 170 171 if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then 172 fail "Ninja file rewritten on null incremental build" 173 fi 174 175 # The bpglob commands should not rerun after the first incremental build. 176 if [[ "$glob_deps_mtime2" != "$glob_deps_mtime3" ]]; then 177 fail "Glob deps file rewritten on second null incremental build" 178 fi 179} 180 181function test_add_file_to_glob() { 182 setup 183 184 mkdir -p a 185 cat > a/Android.bp <<'EOF' 186python_binary_host { 187 name: "my_little_binary_host", 188 srcs: ["*.py"], 189} 190EOF 191 touch a/my_little_binary_host.py 192 run_soong 193 local mtime1=$(stat -c "%y" out/soong/build.ninja) 194 195 touch a/my_little_library.py 196 run_soong 197 198 local mtime2=$(stat -c "%y" out/soong/build.ninja) 199 if [[ "$mtime1" == "$mtime2" ]]; then 200 fail "Output Ninja file did not change" 201 fi 202 203 grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output" 204} 205 206function test_soong_build_rerun_iff_environment_changes() { 207 setup 208 209 mkdir -p cherry 210 cat > cherry/Android.bp <<'EOF' 211bootstrap_go_package { 212 name: "cherry", 213 pkgPath: "android/soong/cherry", 214 deps: [ 215 "blueprint", 216 "soong", 217 "soong-android", 218 ], 219 srcs: [ 220 "cherry.go", 221 ], 222 pluginFor: ["soong_build"], 223} 224EOF 225 226 cat > cherry/cherry.go <<'EOF' 227package cherry 228 229import ( 230 "android/soong/android" 231 "github.com/google/blueprint" 232) 233 234var ( 235 pctx = android.NewPackageContext("cherry") 236) 237 238func init() { 239 android.RegisterSingletonType("cherry", CherrySingleton) 240} 241 242func CherrySingleton() android.Singleton { 243 return &cherrySingleton{} 244} 245 246type cherrySingleton struct{} 247 248func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) { 249 cherryRule := ctx.Rule(pctx, "cherry", 250 blueprint.RuleParams{ 251 Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}", 252 CommandDeps: []string{}, 253 Description: "Cherry", 254 }) 255 256 outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt") 257 var deps android.Paths 258 259 ctx.Build(pctx, android.BuildParams{ 260 Rule: cherryRule, 261 Output: outputFile, 262 Inputs: deps, 263 }) 264} 265EOF 266 267 export CHERRY=TASTY 268 run_soong 269 grep -q "CHERRY IS TASTY" out/soong/build.ninja \ 270 || fail "first value of environment variable is not used" 271 272 export CHERRY=RED 273 run_soong 274 grep -q "CHERRY IS RED" out/soong/build.ninja \ 275 || fail "second value of environment variable not used" 276 local mtime1=$(stat -c "%y" out/soong/build.ninja) 277 278 run_soong 279 local mtime2=$(stat -c "%y" out/soong/build.ninja) 280 if [[ "$mtime1" != "$mtime2" ]]; then 281 fail "Output Ninja file changed when environment variable did not" 282 fi 283 284} 285 286function test_add_file_to_soong_build() { 287 setup 288 run_soong 289 local mtime1=$(stat -c "%y" out/soong/build.ninja) 290 291 mkdir -p a 292 cat > a/Android.bp <<'EOF' 293bootstrap_go_package { 294 name: "picard-soong-rules", 295 pkgPath: "android/soong/picard", 296 deps: [ 297 "blueprint", 298 "soong", 299 "soong-android", 300 ], 301 srcs: [ 302 "picard.go", 303 ], 304 pluginFor: ["soong_build"], 305} 306EOF 307 308 cat > a/picard.go <<'EOF' 309package picard 310 311import ( 312 "android/soong/android" 313 "github.com/google/blueprint" 314) 315 316var ( 317 pctx = android.NewPackageContext("picard") 318) 319 320func init() { 321 android.RegisterSingletonType("picard", PicardSingleton) 322} 323 324func PicardSingleton() android.Singleton { 325 return &picardSingleton{} 326} 327 328type picardSingleton struct{} 329 330func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) { 331 picardRule := ctx.Rule(pctx, "picard", 332 blueprint.RuleParams{ 333 Command: "echo Make it so. > ${out}", 334 CommandDeps: []string{}, 335 Description: "Something quotable", 336 }) 337 338 outputFile := android.PathForOutput(ctx, "picard", "picard.txt") 339 var deps android.Paths 340 341 ctx.Build(pctx, android.BuildParams{ 342 Rule: picardRule, 343 Output: outputFile, 344 Inputs: deps, 345 }) 346} 347 348EOF 349 350 run_soong 351 local mtime2=$(stat -c "%y" out/soong/build.ninja) 352 if [[ "$mtime1" == "$mtime2" ]]; then 353 fail "Output Ninja file did not change" 354 fi 355 356 grep -q "Make it so" out/soong/build.ninja || fail "New action not present" 357} 358 359# Tests a glob in a build= statement in an Android.bp file, which is interpreted 360# during bootstrapping. 361function test_glob_during_bootstrapping() { 362 setup 363 364 mkdir -p a 365 cat > a/Android.bp <<'EOF' 366build=["foo*.bp"] 367EOF 368 cat > a/fooa.bp <<'EOF' 369bootstrap_go_package { 370 name: "picard-soong-rules", 371 pkgPath: "android/soong/picard", 372 deps: [ 373 "blueprint", 374 "soong", 375 "soong-android", 376 ], 377 srcs: [ 378 "picard.go", 379 ], 380 pluginFor: ["soong_build"], 381} 382EOF 383 384 cat > a/picard.go <<'EOF' 385package picard 386 387import ( 388 "android/soong/android" 389 "github.com/google/blueprint" 390) 391 392var ( 393 pctx = android.NewPackageContext("picard") 394) 395 396func init() { 397 android.RegisterSingletonType("picard", PicardSingleton) 398} 399 400func PicardSingleton() android.Singleton { 401 return &picardSingleton{} 402} 403 404type picardSingleton struct{} 405 406var Message = "Make it so." 407 408func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) { 409 picardRule := ctx.Rule(pctx, "picard", 410 blueprint.RuleParams{ 411 Command: "echo " + Message + " > ${out}", 412 CommandDeps: []string{}, 413 Description: "Something quotable", 414 }) 415 416 outputFile := android.PathForOutput(ctx, "picard", "picard.txt") 417 var deps android.Paths 418 419 ctx.Build(pctx, android.BuildParams{ 420 Rule: picardRule, 421 Output: outputFile, 422 Inputs: deps, 423 }) 424} 425 426EOF 427 428 run_soong 429 local mtime1=$(stat -c "%y" out/soong/build.ninja) 430 431 grep -q "Make it so" out/soong/build.ninja || fail "Original action not present" 432 433 cat > a/foob.bp <<'EOF' 434bootstrap_go_package { 435 name: "worf-soong-rules", 436 pkgPath: "android/soong/worf", 437 deps: [ 438 "blueprint", 439 "soong", 440 "soong-android", 441 "picard-soong-rules", 442 ], 443 srcs: [ 444 "worf.go", 445 ], 446 pluginFor: ["soong_build"], 447} 448EOF 449 450 cat > a/worf.go <<'EOF' 451package worf 452 453import "android/soong/picard" 454 455func init() { 456 picard.Message = "Engage." 457} 458EOF 459 460 run_soong 461 local mtime2=$(stat -c "%y" out/soong/build.ninja) 462 if [[ "$mtime1" == "$mtime2" ]]; then 463 fail "Output Ninja file did not change" 464 fi 465 466 grep -q "Engage" out/soong/build.ninja || fail "New action not present" 467 468 if grep -q "Make it so" out/soong/build.ninja; then 469 fail "Original action still present" 470 fi 471} 472 473function test_null_build_after_docs { 474 setup 475 run_soong 476 local mtime1=$(stat -c "%y" out/soong/build.ninja) 477 478 prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs 479 run_soong 480 local mtime2=$(stat -c "%y" out/soong/build.ninja) 481 482 if [[ "$mtime1" != "$mtime2" ]]; then 483 fail "Output Ninja file changed on null build" 484 fi 485} 486 487function test_bp2build_smoke { 488 setup 489 GENERATE_BAZEL_FILES=1 run_soong 490 [[ -e out/soong/.bootstrap/bp2build_workspace_marker ]] || fail "bp2build marker file not created" 491 [[ -e out/soong/workspace ]] || fail "Bazel workspace not created" 492} 493 494function test_bp2build_add_android_bp { 495 setup 496 497 mkdir -p a 498 touch a/a.txt 499 cat > a/Android.bp <<'EOF' 500filegroup { 501 name: "a", 502 srcs: ["a.txt"], 503 bazel_module: { bp2build_available: true }, 504} 505EOF 506 507 GENERATE_BAZEL_FILES=1 run_soong 508 [[ -e out/soong/bp2build/a/BUILD ]] || fail "a/BUILD not created" 509 [[ -L out/soong/workspace/a/BUILD ]] || fail "a/BUILD not symlinked" 510 511 mkdir -p b 512 touch b/b.txt 513 cat > b/Android.bp <<'EOF' 514filegroup { 515 name: "b", 516 srcs: ["b.txt"], 517 bazel_module: { bp2build_available: true }, 518} 519EOF 520 521 GENERATE_BAZEL_FILES=1 run_soong 522 [[ -e out/soong/bp2build/b/BUILD ]] || fail "a/BUILD not created" 523 [[ -L out/soong/workspace/b/BUILD ]] || fail "a/BUILD not symlinked" 524} 525 526function test_bp2build_null_build { 527 setup 528 529 GENERATE_BAZEL_FILES=1 run_soong 530 local mtime1=$(stat -c "%y" out/soong/build.ninja) 531 532 GENERATE_BAZEL_FILES=1 run_soong 533 local mtime2=$(stat -c "%y" out/soong/build.ninja) 534 535 if [[ "$mtime1" != "$mtime2" ]]; then 536 fail "Output Ninja file changed on null build" 537 fi 538} 539 540function test_bp2build_add_to_glob { 541 setup 542 543 mkdir -p a 544 touch a/a1.txt 545 cat > a/Android.bp <<'EOF' 546filegroup { 547 name: "a", 548 srcs: ["*.txt"], 549 bazel_module: { bp2build_available: true }, 550} 551EOF 552 553 GENERATE_BAZEL_FILES=1 run_soong 554 grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file" 555 556 touch a/a2.txt 557 GENERATE_BAZEL_FILES=1 run_soong 558 grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file" 559} 560 561function test_dump_json_module_graph() { 562 setup 563 SOONG_DUMP_JSON_MODULE_GRAPH="$MOCK_TOP/modules.json" run_soong 564 if [[ ! -r "$MOCK_TOP/modules.json" ]]; then 565 fail "JSON file was not created" 566 fi 567} 568 569function test_bp2build_bazel_workspace_structure { 570 setup 571 572 mkdir -p a/b 573 touch a/a.txt 574 touch a/b/b.txt 575 cat > a/b/Android.bp <<'EOF' 576filegroup { 577 name: "b", 578 srcs: ["b.txt"], 579 bazel_module: { bp2build_available: true }, 580} 581EOF 582 583 GENERATE_BAZEL_FILES=1 run_soong 584 [[ -e out/soong/workspace ]] || fail "Bazel workspace not created" 585 [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory" 586 [[ -L out/soong/workspace/a/b/BUILD ]] || fail "BUILD file not symlinked" 587 [[ "$(readlink -f out/soong/workspace/a/b/BUILD)" =~ bp2build/a/b/BUILD$ ]] \ 588 || fail "BUILD files symlinked at the wrong place" 589 [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked" 590 [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked" 591 [[ ! -e out/soong/workspace/out ]] || fail "out directory symlinked" 592} 593 594function test_bp2build_bazel_workspace_add_file { 595 setup 596 597 mkdir -p a 598 touch a/a.txt 599 cat > a/Android.bp <<EOF 600filegroup { 601 name: "a", 602 srcs: ["a.txt"], 603 bazel_module: { bp2build_available: true }, 604} 605EOF 606 607 GENERATE_BAZEL_FILES=1 run_soong 608 609 touch a/a2.txt # No reference in the .bp file needed 610 GENERATE_BAZEL_FILES=1 run_soong 611 [[ -L out/soong/workspace/a/a2.txt ]] || fail "a/a2.txt not symlinked" 612} 613 614function test_bp2build_build_file_precedence { 615 setup 616 617 mkdir -p a 618 touch a/a.txt 619 touch a/BUILD 620 cat > a/Android.bp <<EOF 621filegroup { 622 name: "a", 623 srcs: ["a.txt"], 624 bazel_module: { bp2build_available: true }, 625} 626EOF 627 628 GENERATE_BAZEL_FILES=1 run_soong 629 [[ -L out/soong/workspace/a/BUILD ]] || fail "BUILD file not symlinked" 630 [[ "$(readlink -f out/soong/workspace/a/BUILD)" =~ bp2build/a/BUILD$ ]] \ 631 || fail "BUILD files symlinked to the wrong place" 632} 633 634function test_bp2build_reports_multiple_errors { 635 setup 636 637 mkdir -p a/BUILD 638 touch a/a.txt 639 cat > a/Android.bp <<EOF 640filegroup { 641 name: "a", 642 srcs: ["a.txt"], 643 bazel_module: { bp2build_available: true }, 644} 645EOF 646 647 mkdir -p b/BUILD 648 touch b/b.txt 649 cat > b/Android.bp <<EOF 650filegroup { 651 name: "b", 652 srcs: ["b.txt"], 653 bazel_module: { bp2build_available: true }, 654} 655EOF 656 657 if GENERATE_BAZEL_FILES=1 run_soong >& "$MOCK_TOP/errors"; then 658 fail "Build should have failed" 659 fi 660 661 grep -q "a/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for a/BUILD not found" 662 grep -q "b/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for b/BUILD not found" 663} 664 665test_smoke 666test_null_build 667test_null_build_after_docs 668test_soong_build_rebuilt_if_blueprint_changes 669test_glob_noop_incremental 670test_add_file_to_glob 671test_add_android_bp 672test_change_android_bp 673test_delete_android_bp 674test_add_file_to_soong_build 675test_glob_during_bootstrapping 676test_soong_build_rerun_iff_environment_changes 677test_dump_json_module_graph 678test_bp2build_smoke 679test_bp2build_null_build 680test_bp2build_add_android_bp 681test_bp2build_add_to_glob 682test_bp2build_bazel_workspace_structure 683test_bp2build_bazel_workspace_add_file 684test_bp2build_build_file_precedence 685test_bp2build_reports_multiple_errors 686