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