bootstrap_test.sh 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. #!/bin/bash -eu
  2. # This test exercises the bootstrapping process of the build system
  3. # in a source tree that only contains enough files for Bazel and Soong to work.
  4. HARDWIRED_MOCK_TOP=
  5. # Uncomment this to be able to view the source tree after a test is run
  6. # HARDWIRED_MOCK_TOP=/tmp/td
  7. REAL_TOP="$(readlink -f "$(dirname "$0")"/../..)"
  8. function fail {
  9. echo ERROR: $1
  10. exit 1
  11. }
  12. function copy_directory() {
  13. local dir="$1"
  14. local parent="$(dirname "$dir")"
  15. mkdir -p "$MOCK_TOP/$parent"
  16. cp -R "$REAL_TOP/$dir" "$MOCK_TOP/$parent"
  17. }
  18. function symlink_file() {
  19. local file="$1"
  20. mkdir -p "$MOCK_TOP/$(dirname "$file")"
  21. ln -s "$REAL_TOP/$file" "$MOCK_TOP/$file"
  22. }
  23. function symlink_directory() {
  24. local dir="$1"
  25. mkdir -p "$MOCK_TOP/$dir"
  26. # We need to symlink the contents of the directory individually instead of
  27. # using one symlink for the whole directory because finder.go doesn't follow
  28. # symlinks when looking for Android.bp files
  29. for i in $(ls "$REAL_TOP/$dir"); do
  30. local target="$MOCK_TOP/$dir/$i"
  31. local source="$REAL_TOP/$dir/$i"
  32. if [[ -e "$target" ]]; then
  33. if [[ ! -d "$source" || ! -d "$target" ]]; then
  34. fail "Trying to symlink $dir twice"
  35. fi
  36. else
  37. ln -s "$REAL_TOP/$dir/$i" "$MOCK_TOP/$dir/$i";
  38. fi
  39. done
  40. }
  41. function setup_bazel() {
  42. copy_directory build/bazel
  43. symlink_directory prebuilts/bazel
  44. symlink_directory prebuilts/jdk
  45. symlink_file WORKSPACE
  46. symlink_file tools/bazel
  47. }
  48. function setup() {
  49. if [[ ! -z "$HARDWIRED_MOCK_TOP" ]]; then
  50. MOCK_TOP="$HARDWIRED_MOCK_TOP"
  51. rm -fr "$MOCK_TOP"
  52. mkdir -p "$MOCK_TOP"
  53. else
  54. MOCK_TOP=$(mktemp -t -d st.XXXXX)
  55. trap 'echo cd / && echo rm -fr "$MOCK_TOP"' EXIT
  56. fi
  57. echo "Test case: ${FUNCNAME[1]}, mock top path: $MOCK_TOP"
  58. cd "$MOCK_TOP"
  59. copy_directory build/blueprint
  60. copy_directory build/soong
  61. symlink_directory prebuilts/go
  62. symlink_directory prebuilts/build-tools
  63. symlink_directory external/golang-protobuf
  64. touch "$MOCK_TOP/Android.bp"
  65. export ALLOW_MISSING_DEPENDENCIES=true
  66. mkdir -p out/soong
  67. }
  68. function run_soong() {
  69. build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests
  70. }
  71. function test_smoke {
  72. setup
  73. run_soong
  74. }
  75. function test_bazel_smoke {
  76. setup
  77. setup_bazel
  78. tools/bazel info
  79. }
  80. function test_null_build() {
  81. setup
  82. run_soong
  83. local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
  84. local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
  85. run_soong
  86. local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
  87. local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
  88. if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
  89. # Bootstrapping is always done. It doesn't take a measurable amount of time.
  90. fail "Bootstrap Ninja file did not change on null build"
  91. fi
  92. if [[ "$output_mtime1" != "$output_mtime2" ]]; then
  93. fail "Output Ninja file changed on null build"
  94. fi
  95. }
  96. function test_soong_build_rebuilt_if_blueprint_changes() {
  97. setup
  98. run_soong
  99. local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
  100. sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
  101. run_soong
  102. local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
  103. if [[ "$mtime1" == "$mtime2" ]]; then
  104. fail "Bootstrap Ninja file did not change"
  105. fi
  106. }
  107. function test_change_android_bp() {
  108. setup
  109. mkdir -p a
  110. cat > a/Android.bp <<'EOF'
  111. python_binary_host {
  112. name: "my_little_binary_host",
  113. srcs: ["my_little_binary_host.py"]
  114. }
  115. EOF
  116. touch a/my_little_binary_host.py
  117. run_soong
  118. grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found"
  119. cat > a/Android.bp <<'EOF'
  120. python_binary_host {
  121. name: "my_great_binary_host",
  122. srcs: ["my_great_binary_host.py"]
  123. }
  124. EOF
  125. touch a/my_great_binary_host.py
  126. run_soong
  127. grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found"
  128. grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
  129. }
  130. function test_add_android_bp() {
  131. setup
  132. run_soong
  133. local mtime1=$(stat -c "%y" out/soong/build.ninja)
  134. mkdir -p a
  135. cat > a/Android.bp <<'EOF'
  136. python_binary_host {
  137. name: "my_little_binary_host",
  138. srcs: ["my_little_binary_host.py"]
  139. }
  140. EOF
  141. touch a/my_little_binary_host.py
  142. run_soong
  143. local mtime2=$(stat -c "%y" out/soong/build.ninja)
  144. if [[ "$mtime1" == "$mtime2" ]]; then
  145. fail "Output Ninja file did not change"
  146. fi
  147. grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output"
  148. run_soong
  149. }
  150. function test_delete_android_bp() {
  151. setup
  152. mkdir -p a
  153. cat > a/Android.bp <<'EOF'
  154. python_binary_host {
  155. name: "my_little_binary_host",
  156. srcs: ["my_little_binary_host.py"]
  157. }
  158. EOF
  159. touch a/my_little_binary_host.py
  160. run_soong
  161. grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output"
  162. rm a/Android.bp
  163. run_soong
  164. grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja && fail "Old module in output"
  165. }
  166. function test_add_file_to_glob() {
  167. setup
  168. mkdir -p a
  169. cat > a/Android.bp <<'EOF'
  170. python_binary_host {
  171. name: "my_little_binary_host",
  172. srcs: ["*.py"],
  173. }
  174. EOF
  175. touch a/my_little_binary_host.py
  176. run_soong
  177. local mtime1=$(stat -c "%y" out/soong/build.ninja)
  178. touch a/my_little_library.py
  179. run_soong
  180. local mtime2=$(stat -c "%y" out/soong/build.ninja)
  181. if [[ "$mtime1" == "$mtime2" ]]; then
  182. fail "Output Ninja file did not change"
  183. fi
  184. grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
  185. }
  186. function test_soong_build_rerun_iff_environment_changes() {
  187. setup
  188. mkdir -p cherry
  189. cat > cherry/Android.bp <<'EOF'
  190. bootstrap_go_package {
  191. name: "cherry",
  192. pkgPath: "android/soong/cherry",
  193. deps: [
  194. "blueprint",
  195. "soong",
  196. "soong-android",
  197. ],
  198. srcs: [
  199. "cherry.go",
  200. ],
  201. pluginFor: ["soong_build"],
  202. }
  203. EOF
  204. cat > cherry/cherry.go <<'EOF'
  205. package cherry
  206. import (
  207. "android/soong/android"
  208. "github.com/google/blueprint"
  209. )
  210. var (
  211. pctx = android.NewPackageContext("cherry")
  212. )
  213. func init() {
  214. android.RegisterSingletonType("cherry", CherrySingleton)
  215. }
  216. func CherrySingleton() android.Singleton {
  217. return &cherrySingleton{}
  218. }
  219. type cherrySingleton struct{}
  220. func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
  221. cherryRule := ctx.Rule(pctx, "cherry",
  222. blueprint.RuleParams{
  223. Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
  224. CommandDeps: []string{},
  225. Description: "Cherry",
  226. })
  227. outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
  228. var deps android.Paths
  229. ctx.Build(pctx, android.BuildParams{
  230. Rule: cherryRule,
  231. Output: outputFile,
  232. Inputs: deps,
  233. })
  234. }
  235. EOF
  236. export CHERRY=TASTY
  237. run_soong
  238. grep -q "CHERRY IS TASTY" out/soong/build.ninja \
  239. || fail "first value of environment variable is not used"
  240. export CHERRY=RED
  241. run_soong
  242. grep -q "CHERRY IS RED" out/soong/build.ninja \
  243. || fail "second value of environment variable not used"
  244. local mtime1=$(stat -c "%y" out/soong/build.ninja)
  245. run_soong
  246. local mtime2=$(stat -c "%y" out/soong/build.ninja)
  247. if [[ "$mtime1" != "$mtime2" ]]; then
  248. fail "Output Ninja file changed when environment variable did not"
  249. fi
  250. }
  251. function test_add_file_to_soong_build() {
  252. setup
  253. run_soong
  254. local mtime1=$(stat -c "%y" out/soong/build.ninja)
  255. mkdir -p a
  256. cat > a/Android.bp <<'EOF'
  257. bootstrap_go_package {
  258. name: "picard-soong-rules",
  259. pkgPath: "android/soong/picard",
  260. deps: [
  261. "blueprint",
  262. "soong",
  263. "soong-android",
  264. ],
  265. srcs: [
  266. "picard.go",
  267. ],
  268. pluginFor: ["soong_build"],
  269. }
  270. EOF
  271. cat > a/picard.go <<'EOF'
  272. package picard
  273. import (
  274. "android/soong/android"
  275. "github.com/google/blueprint"
  276. )
  277. var (
  278. pctx = android.NewPackageContext("picard")
  279. )
  280. func init() {
  281. android.RegisterSingletonType("picard", PicardSingleton)
  282. }
  283. func PicardSingleton() android.Singleton {
  284. return &picardSingleton{}
  285. }
  286. type picardSingleton struct{}
  287. func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
  288. picardRule := ctx.Rule(pctx, "picard",
  289. blueprint.RuleParams{
  290. Command: "echo Make it so. > ${out}",
  291. CommandDeps: []string{},
  292. Description: "Something quotable",
  293. })
  294. outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
  295. var deps android.Paths
  296. ctx.Build(pctx, android.BuildParams{
  297. Rule: picardRule,
  298. Output: outputFile,
  299. Inputs: deps,
  300. })
  301. }
  302. EOF
  303. run_soong
  304. local mtime2=$(stat -c "%y" out/soong/build.ninja)
  305. if [[ "$mtime1" == "$mtime2" ]]; then
  306. fail "Output Ninja file did not change"
  307. fi
  308. grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
  309. }
  310. function test_null_build_after_docs {
  311. setup
  312. run_soong
  313. local mtime1=$(stat -c "%y" out/soong/build.ninja)
  314. prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
  315. run_soong
  316. local mtime2=$(stat -c "%y" out/soong/build.ninja)
  317. if [[ "$mtime1" != "$mtime2" ]]; then
  318. fail "Output Ninja file changed on null build"
  319. fi
  320. }
  321. test_bazel_smoke
  322. test_smoke
  323. test_null_build
  324. test_null_build_after_docs
  325. test_soong_build_rebuilt_if_blueprint_changes
  326. test_add_file_to_glob
  327. test_add_android_bp
  328. test_change_android_bp
  329. test_delete_android_bp
  330. test_add_file_to_soong_build
  331. test_soong_build_rerun_iff_environment_changes