2021-03-16 08:55:23 +01:00
|
|
|
#!/bin/bash -eu
|
|
|
|
|
2021-04-21 13:10:09 +02:00
|
|
|
set -o pipefail
|
|
|
|
|
2021-03-16 08:55:23 +01:00
|
|
|
# This test exercises the bootstrapping process of the build system
|
|
|
|
# in a source tree that only contains enough files for Bazel and Soong to work.
|
|
|
|
|
2021-04-08 13:21:13 +02:00
|
|
|
source "$(dirname "$0")/lib.sh"
|
2021-03-16 08:55:23 +01:00
|
|
|
|
2021-05-18 13:47:15 +02:00
|
|
|
readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
|
|
|
|
|
2021-03-16 08:55:23 +01:00
|
|
|
function test_smoke {
|
|
|
|
setup
|
|
|
|
run_soong
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_null_build() {
|
|
|
|
setup
|
|
|
|
run_soong
|
2021-11-02 14:42:04 +01:00
|
|
|
local bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
|
2021-03-16 08:55:23 +01:00
|
|
|
local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
run_soong
|
2021-11-02 14:42:04 +01:00
|
|
|
local bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
|
2021-03-16 08:55:23 +01:00
|
|
|
local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
|
|
|
|
# Bootstrapping is always done. It doesn't take a measurable amount of time.
|
|
|
|
fail "Bootstrap Ninja file did not change on null build"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed on null build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_soong_build_rebuilt_if_blueprint_changes() {
|
|
|
|
setup
|
|
|
|
run_soong
|
2021-11-02 14:42:04 +01:00
|
|
|
local mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
|
2021-03-16 08:55:23 +01:00
|
|
|
|
|
|
|
sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
|
|
|
|
|
|
|
|
run_soong
|
2021-11-02 14:42:04 +01:00
|
|
|
local mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
|
2021-03-16 08:55:23 +01:00
|
|
|
|
|
|
|
if [[ "$mtime1" == "$mtime2" ]]; then
|
|
|
|
fail "Bootstrap Ninja file did not change"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_change_android_bp() {
|
|
|
|
setup
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_little_binary_host",
|
|
|
|
srcs: ["my_little_binary_host.py"]
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_little_binary_host.py
|
|
|
|
run_soong
|
|
|
|
|
|
|
|
grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja || fail "module not found"
|
|
|
|
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_great_binary_host",
|
|
|
|
srcs: ["my_great_binary_host.py"]
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_great_binary_host.py
|
|
|
|
run_soong
|
|
|
|
|
|
|
|
grep -q "^# Module:.*my_little_binary_host" out/soong/build.ninja && fail "old module found"
|
|
|
|
grep -q "^# Module:.*my_great_binary_host" out/soong/build.ninja || fail "new module not found"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_add_android_bp() {
|
|
|
|
setup
|
|
|
|
run_soong
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_little_binary_host",
|
|
|
|
srcs: ["my_little_binary_host.py"]
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_little_binary_host.py
|
|
|
|
run_soong
|
|
|
|
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$mtime1" == "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file did not change"
|
|
|
|
fi
|
|
|
|
|
|
|
|
grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "New module not in output"
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_delete_android_bp() {
|
|
|
|
setup
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_little_binary_host",
|
|
|
|
srcs: ["my_little_binary_host.py"]
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_little_binary_host.py
|
|
|
|
run_soong
|
|
|
|
|
|
|
|
grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja || fail "Module not in output"
|
|
|
|
|
|
|
|
rm a/Android.bp
|
|
|
|
run_soong
|
|
|
|
|
2021-04-14 10:31:00 +02:00
|
|
|
if grep -q "^# Module:.*my_little_binary_host$" out/soong/build.ninja; then
|
|
|
|
fail "Old module in output"
|
|
|
|
fi
|
2021-03-16 08:55:23 +01:00
|
|
|
}
|
|
|
|
|
2021-04-13 03:59:18 +02:00
|
|
|
# Test that an incremental build with a glob doesn't rerun soong_build, and
|
|
|
|
# only regenerates the globs on the first but not the second incremental build.
|
|
|
|
function test_glob_noop_incremental() {
|
|
|
|
setup
|
|
|
|
|
2021-04-16 19:09:54 +02:00
|
|
|
# This test needs to start from a clean build, but setup creates an
|
|
|
|
# initialized tree that has already been built once. Clear the out
|
2021-04-16 09:16:19 +02:00
|
|
|
# directory to start from scratch (see b/185591972)
|
2021-04-16 19:09:54 +02:00
|
|
|
rm -rf out
|
|
|
|
|
2021-04-13 03:59:18 +02:00
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_little_binary_host",
|
|
|
|
srcs: ["*.py"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_little_binary_host.py
|
|
|
|
run_soong
|
|
|
|
local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
2021-09-01 08:57:48 +02:00
|
|
|
local glob_deps_file=out/soong/globs/build/0.d
|
2021-04-13 03:59:18 +02:00
|
|
|
|
|
|
|
if [ -e "$glob_deps_file" ]; then
|
|
|
|
fail "Glob deps file unexpectedly written on first build"
|
|
|
|
fi
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
# There is an ineffiencency in glob that requires bpglob to rerun once for each glob to update
|
|
|
|
# the entry in the .ninja_log. It doesn't update the output file, but we can detect the rerun
|
|
|
|
# by checking if the deps file was created.
|
|
|
|
if [ ! -e "$glob_deps_file" ]; then
|
|
|
|
fail "Glob deps file missing after second build"
|
|
|
|
fi
|
|
|
|
|
|
|
|
local glob_deps_mtime2=$(stat -c "%y" "$glob_deps_file")
|
|
|
|
|
|
|
|
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
|
|
|
|
fail "Ninja file rewritten on null incremental build"
|
|
|
|
fi
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local ninja_mtime3=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
local glob_deps_mtime3=$(stat -c "%y" "$glob_deps_file")
|
|
|
|
|
|
|
|
if [[ "$ninja_mtime2" != "$ninja_mtime3" ]]; then
|
|
|
|
fail "Ninja file rewritten on null incremental build"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# The bpglob commands should not rerun after the first incremental build.
|
|
|
|
if [[ "$glob_deps_mtime2" != "$glob_deps_mtime3" ]]; then
|
|
|
|
fail "Glob deps file rewritten on second null incremental build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-03-16 08:55:23 +01:00
|
|
|
function test_add_file_to_glob() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
python_binary_host {
|
|
|
|
name: "my_little_binary_host",
|
|
|
|
srcs: ["*.py"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
touch a/my_little_binary_host.py
|
|
|
|
run_soong
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
touch a/my_little_library.py
|
|
|
|
run_soong
|
|
|
|
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$mtime1" == "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file did not change"
|
|
|
|
fi
|
|
|
|
|
|
|
|
grep -q my_little_library.py out/soong/build.ninja || fail "new file is not in output"
|
|
|
|
}
|
|
|
|
|
2021-03-23 11:46:47 +01:00
|
|
|
function test_soong_build_rerun_iff_environment_changes() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p cherry
|
|
|
|
cat > cherry/Android.bp <<'EOF'
|
|
|
|
bootstrap_go_package {
|
|
|
|
name: "cherry",
|
|
|
|
pkgPath: "android/soong/cherry",
|
|
|
|
deps: [
|
|
|
|
"blueprint",
|
|
|
|
"soong",
|
|
|
|
"soong-android",
|
|
|
|
],
|
|
|
|
srcs: [
|
|
|
|
"cherry.go",
|
|
|
|
],
|
|
|
|
pluginFor: ["soong_build"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
cat > cherry/cherry.go <<'EOF'
|
|
|
|
package cherry
|
|
|
|
|
|
|
|
import (
|
|
|
|
"android/soong/android"
|
|
|
|
"github.com/google/blueprint"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
pctx = android.NewPackageContext("cherry")
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
android.RegisterSingletonType("cherry", CherrySingleton)
|
|
|
|
}
|
|
|
|
|
|
|
|
func CherrySingleton() android.Singleton {
|
|
|
|
return &cherrySingleton{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type cherrySingleton struct{}
|
|
|
|
|
|
|
|
func (p *cherrySingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|
|
|
cherryRule := ctx.Rule(pctx, "cherry",
|
|
|
|
blueprint.RuleParams{
|
|
|
|
Command: "echo CHERRY IS " + ctx.Config().Getenv("CHERRY") + " > ${out}",
|
|
|
|
CommandDeps: []string{},
|
|
|
|
Description: "Cherry",
|
|
|
|
})
|
|
|
|
|
|
|
|
outputFile := android.PathForOutput(ctx, "cherry", "cherry.txt")
|
|
|
|
var deps android.Paths
|
|
|
|
|
|
|
|
ctx.Build(pctx, android.BuildParams{
|
|
|
|
Rule: cherryRule,
|
|
|
|
Output: outputFile,
|
|
|
|
Inputs: deps,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
export CHERRY=TASTY
|
|
|
|
run_soong
|
|
|
|
grep -q "CHERRY IS TASTY" out/soong/build.ninja \
|
|
|
|
|| fail "first value of environment variable is not used"
|
|
|
|
|
|
|
|
export CHERRY=RED
|
|
|
|
run_soong
|
|
|
|
grep -q "CHERRY IS RED" out/soong/build.ninja \
|
|
|
|
|| fail "second value of environment variable not used"
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$mtime1" != "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed when environment variable did not"
|
|
|
|
fi
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-16 08:55:23 +01:00
|
|
|
function test_add_file_to_soong_build() {
|
|
|
|
setup
|
|
|
|
run_soong
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
bootstrap_go_package {
|
|
|
|
name: "picard-soong-rules",
|
|
|
|
pkgPath: "android/soong/picard",
|
|
|
|
deps: [
|
|
|
|
"blueprint",
|
|
|
|
"soong",
|
|
|
|
"soong-android",
|
|
|
|
],
|
|
|
|
srcs: [
|
|
|
|
"picard.go",
|
|
|
|
],
|
|
|
|
pluginFor: ["soong_build"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
cat > a/picard.go <<'EOF'
|
|
|
|
package picard
|
|
|
|
|
|
|
|
import (
|
|
|
|
"android/soong/android"
|
|
|
|
"github.com/google/blueprint"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
pctx = android.NewPackageContext("picard")
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
android.RegisterSingletonType("picard", PicardSingleton)
|
|
|
|
}
|
|
|
|
|
|
|
|
func PicardSingleton() android.Singleton {
|
|
|
|
return &picardSingleton{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type picardSingleton struct{}
|
|
|
|
|
|
|
|
func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|
|
|
picardRule := ctx.Rule(pctx, "picard",
|
|
|
|
blueprint.RuleParams{
|
|
|
|
Command: "echo Make it so. > ${out}",
|
|
|
|
CommandDeps: []string{},
|
|
|
|
Description: "Something quotable",
|
|
|
|
})
|
|
|
|
|
|
|
|
outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
|
|
|
|
var deps android.Paths
|
|
|
|
|
|
|
|
ctx.Build(pctx, android.BuildParams{
|
|
|
|
Rule: picardRule,
|
|
|
|
Output: outputFile,
|
|
|
|
Inputs: deps,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$mtime1" == "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file did not change"
|
|
|
|
fi
|
|
|
|
|
|
|
|
grep -q "Make it so" out/soong/build.ninja || fail "New action not present"
|
|
|
|
}
|
|
|
|
|
2021-04-08 19:34:16 +02:00
|
|
|
# Tests a glob in a build= statement in an Android.bp file, which is interpreted
|
|
|
|
# during bootstrapping.
|
|
|
|
function test_glob_during_bootstrapping() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
build=["foo*.bp"]
|
|
|
|
EOF
|
|
|
|
cat > a/fooa.bp <<'EOF'
|
|
|
|
bootstrap_go_package {
|
|
|
|
name: "picard-soong-rules",
|
|
|
|
pkgPath: "android/soong/picard",
|
|
|
|
deps: [
|
|
|
|
"blueprint",
|
|
|
|
"soong",
|
|
|
|
"soong-android",
|
|
|
|
],
|
|
|
|
srcs: [
|
|
|
|
"picard.go",
|
|
|
|
],
|
|
|
|
pluginFor: ["soong_build"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
cat > a/picard.go <<'EOF'
|
|
|
|
package picard
|
|
|
|
|
|
|
|
import (
|
|
|
|
"android/soong/android"
|
|
|
|
"github.com/google/blueprint"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
pctx = android.NewPackageContext("picard")
|
|
|
|
)
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
android.RegisterSingletonType("picard", PicardSingleton)
|
|
|
|
}
|
|
|
|
|
|
|
|
func PicardSingleton() android.Singleton {
|
|
|
|
return &picardSingleton{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type picardSingleton struct{}
|
|
|
|
|
|
|
|
var Message = "Make it so."
|
|
|
|
|
|
|
|
func (p *picardSingleton) GenerateBuildActions(ctx android.SingletonContext) {
|
|
|
|
picardRule := ctx.Rule(pctx, "picard",
|
|
|
|
blueprint.RuleParams{
|
|
|
|
Command: "echo " + Message + " > ${out}",
|
|
|
|
CommandDeps: []string{},
|
|
|
|
Description: "Something quotable",
|
|
|
|
})
|
|
|
|
|
|
|
|
outputFile := android.PathForOutput(ctx, "picard", "picard.txt")
|
|
|
|
var deps android.Paths
|
|
|
|
|
|
|
|
ctx.Build(pctx, android.BuildParams{
|
|
|
|
Rule: picardRule,
|
|
|
|
Output: outputFile,
|
|
|
|
Inputs: deps,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
|
|
|
grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
|
|
|
|
|
|
|
|
cat > a/foob.bp <<'EOF'
|
|
|
|
bootstrap_go_package {
|
|
|
|
name: "worf-soong-rules",
|
|
|
|
pkgPath: "android/soong/worf",
|
|
|
|
deps: [
|
|
|
|
"blueprint",
|
|
|
|
"soong",
|
|
|
|
"soong-android",
|
|
|
|
"picard-soong-rules",
|
|
|
|
],
|
|
|
|
srcs: [
|
|
|
|
"worf.go",
|
|
|
|
],
|
|
|
|
pluginFor: ["soong_build"],
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
cat > a/worf.go <<'EOF'
|
|
|
|
package worf
|
|
|
|
|
|
|
|
import "android/soong/picard"
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
picard.Message = "Engage."
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$mtime1" == "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file did not change"
|
|
|
|
fi
|
|
|
|
|
|
|
|
grep -q "Engage" out/soong/build.ninja || fail "New action not present"
|
|
|
|
|
2021-04-14 10:31:00 +02:00
|
|
|
if grep -q "Make it so" out/soong/build.ninja; then
|
|
|
|
fail "Original action still present"
|
|
|
|
fi
|
2021-04-08 19:34:16 +02:00
|
|
|
}
|
|
|
|
|
2021-09-06 18:31:46 +02:00
|
|
|
function test_soong_docs_smoke() {
|
2021-03-23 11:46:47 +01:00
|
|
|
setup
|
2021-09-06 18:31:46 +02:00
|
|
|
|
|
|
|
run_soong soong_docs
|
|
|
|
|
|
|
|
[[ -e "out/soong/docs/soong_build.html" ]] || fail "Documentation for main page not created"
|
|
|
|
[[ -e "out/soong/docs/cc.html" ]] || fail "Documentation for C++ modules not created"
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_null_build_after_soong_docs() {
|
|
|
|
setup
|
|
|
|
|
2021-03-23 11:46:47 +01:00
|
|
|
run_soong
|
2021-09-06 18:31:46 +02:00
|
|
|
local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
|
2021-03-23 11:46:47 +01:00
|
|
|
|
2021-09-06 18:31:46 +02:00
|
|
|
run_soong soong_docs
|
|
|
|
local docs_mtime1=$(stat -c "%y" out/soong/docs/soong_build.html)
|
|
|
|
|
|
|
|
run_soong soong_docs
|
|
|
|
local docs_mtime2=$(stat -c "%y" out/soong/docs/soong_build.html)
|
|
|
|
|
|
|
|
if [[ "$docs_mtime1" != "$docs_mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed on null build"
|
|
|
|
fi
|
2021-08-19 16:24:30 +02:00
|
|
|
|
2021-03-23 11:46:47 +01:00
|
|
|
run_soong
|
2021-09-06 18:31:46 +02:00
|
|
|
local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
|
2021-03-23 11:46:47 +01:00
|
|
|
|
2021-09-06 18:31:46 +02:00
|
|
|
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
|
2021-03-23 11:46:47 +01:00
|
|
|
fail "Output Ninja file changed on null build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-06-25 03:39:04 +02:00
|
|
|
function test_write_to_source_tree {
|
|
|
|
setup
|
|
|
|
mkdir -p a
|
|
|
|
cat > a/Android.bp <<EOF
|
|
|
|
genrule {
|
|
|
|
name: "write_to_source_tree",
|
|
|
|
out: ["write_to_source_tree"],
|
|
|
|
cmd: "touch file_in_source_tree && touch \$(out)",
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
readonly EXPECTED_OUT=out/soong/.intermediates/a/write_to_source_tree/gen/write_to_source_tree
|
|
|
|
readonly ERROR_LOG=${MOCK_TOP}/out/error.log
|
|
|
|
readonly ERROR_MSG="Read-only file system"
|
|
|
|
readonly ERROR_HINT_PATTERN="BUILD_BROKEN_SRC_DIR"
|
|
|
|
# Test in ReadOnly source tree
|
|
|
|
run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=false ${EXPECTED_OUT} &> /dev/null && \
|
|
|
|
fail "Write to source tree should not work in a ReadOnly source tree"
|
|
|
|
|
|
|
|
if grep -q "${ERROR_MSG}" ${ERROR_LOG} && grep -q "${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
|
|
|
|
echo Error message and error hint found in logs >/dev/null
|
|
|
|
else
|
|
|
|
fail "Did not find Read-only error AND error hint in error.log"
|
|
|
|
fi
|
|
|
|
|
|
|
|
# Test in ReadWrite source tree
|
|
|
|
run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=true ${EXPECTED_OUT} &> /dev/null || \
|
|
|
|
fail "Write to source tree did not succeed in a ReadWrite source tree"
|
|
|
|
|
|
|
|
if grep -q "${ERROR_MSG}\|${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
|
|
|
|
fail "Found read-only error OR error hint in error.log"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_smoke {
|
2021-04-14 10:31:00 +02:00
|
|
|
setup
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-11-02 14:42:04 +01:00
|
|
|
[[ -e out/soong/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
|
2021-04-16 13:47:36 +02:00
|
|
|
[[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
|
2021-04-14 13:49:50 +02:00
|
|
|
}
|
|
|
|
|
2021-08-12 14:03:55 +02:00
|
|
|
function test_bp2build_generates_marker_file {
|
2021-05-25 04:40:29 +02:00
|
|
|
setup
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-25 04:40:29 +02:00
|
|
|
|
2022-10-26 09:26:50 +02:00
|
|
|
if [[ ! -f "./out/soong/bp2build_files_marker" ]]; then
|
|
|
|
fail "bp2build marker file was not generated"
|
|
|
|
fi
|
|
|
|
|
2021-11-02 14:42:04 +01:00
|
|
|
if [[ ! -f "./out/soong/bp2build_workspace_marker" ]]; then
|
2022-10-26 09:26:50 +02:00
|
|
|
fail "symlink forest marker file was not generated"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_bp2build_add_irrelevant_file {
|
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a/b
|
|
|
|
touch a/b/c.txt
|
|
|
|
cat > a/b/Android.bp <<'EOF'
|
|
|
|
filegroup {
|
|
|
|
name: "c",
|
|
|
|
srcs: ["c.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
|
|
|
run_soong bp2build
|
|
|
|
if [[ ! -e out/soong/bp2build/a/b/BUILD.bazel ]]; then
|
|
|
|
fail "BUILD file in symlink forest was not created";
|
|
|
|
fi
|
|
|
|
|
|
|
|
local mtime1=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
|
|
|
|
|
|
|
|
touch a/irrelevant.txt
|
|
|
|
run_soong bp2build
|
|
|
|
local mtime2=$(stat -c "%y" out/soong/bp2build/a/b/BUILD.bazel)
|
|
|
|
|
|
|
|
if [[ "$mtime1" != "$mtime2" ]]; then
|
|
|
|
fail "BUILD.bazel file was regenerated"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ ! -e "out/soong/workspace/a/irrelevant.txt" ]]; then
|
|
|
|
fail "New file was not symlinked into symlink forest"
|
2021-05-25 04:40:29 +02:00
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_add_android_bp {
|
2021-04-14 13:49:50 +02:00
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
touch a/a.txt
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
filegroup {
|
|
|
|
name: "a",
|
|
|
|
srcs: ["a.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-18 13:47:15 +02:00
|
|
|
[[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
|
|
|
|
[[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
|
2021-04-14 13:49:50 +02:00
|
|
|
|
|
|
|
mkdir -p b
|
|
|
|
touch b/b.txt
|
|
|
|
cat > b/Android.bp <<'EOF'
|
|
|
|
filegroup {
|
|
|
|
name: "b",
|
|
|
|
srcs: ["b.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-18 13:47:15 +02:00
|
|
|
[[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
|
|
|
|
[[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
|
2021-04-14 10:31:00 +02:00
|
|
|
}
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_null_build {
|
2021-04-14 10:31:00 +02:00
|
|
|
setup
|
2021-04-14 13:49:50 +02:00
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-11-02 14:42:04 +01:00
|
|
|
local mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
|
2021-04-14 10:31:00 +02:00
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-11-02 14:42:04 +01:00
|
|
|
local mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
|
2021-04-14 10:31:00 +02:00
|
|
|
|
|
|
|
if [[ "$mtime1" != "$mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed on null build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_add_to_glob {
|
2021-04-14 13:49:50 +02:00
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
touch a/a1.txt
|
|
|
|
cat > a/Android.bp <<'EOF'
|
|
|
|
filegroup {
|
|
|
|
name: "a",
|
|
|
|
srcs: ["*.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-18 13:47:15 +02:00
|
|
|
grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file"
|
2021-04-14 13:49:50 +02:00
|
|
|
|
|
|
|
touch a/a2.txt
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-18 13:47:15 +02:00
|
|
|
grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file"
|
2021-04-14 13:49:50 +02:00
|
|
|
}
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
function test_multiple_soong_build_modes() {
|
|
|
|
setup
|
|
|
|
run_soong json-module-graph bp2build nothing
|
2021-11-02 14:42:04 +01:00
|
|
|
if [[ ! -f "out/soong/bp2build_workspace_marker" ]]; then
|
2021-09-02 17:23:06 +02:00
|
|
|
fail "bp2build marker file was not generated"
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [[ ! -f "out/soong/module-graph.json" ]]; then
|
|
|
|
fail "JSON file was not created"
|
|
|
|
fi
|
|
|
|
|
|
|
|
if [[ ! -f "out/soong/build.ninja" ]]; then
|
|
|
|
fail "Main build.ninja file was not created"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-04-01 18:28:45 +02:00
|
|
|
function test_dump_json_module_graph() {
|
|
|
|
setup
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong json-module-graph
|
|
|
|
if [[ ! -r "out/soong/module-graph.json" ]]; then
|
2021-04-01 18:28:45 +02:00
|
|
|
fail "JSON file was not created"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-08-25 14:14:13 +02:00
|
|
|
function test_json_module_graph_back_and_forth_null_build() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong json-module-graph
|
2021-08-25 14:14:13 +02:00
|
|
|
local json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed after writing JSON module graph"
|
|
|
|
fi
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong json-module-graph
|
2021-08-25 14:14:13 +02:00
|
|
|
local json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
|
|
|
|
if [[ "$json_mtime1" != "$json_mtime2" ]]; then
|
|
|
|
fail "JSON module graph file changed after writing Ninja file"
|
|
|
|
fi
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_bazel_workspace_structure {
|
2021-04-16 13:47:36 +02:00
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a/b
|
|
|
|
touch a/a.txt
|
|
|
|
touch a/b/b.txt
|
|
|
|
cat > a/b/Android.bp <<'EOF'
|
|
|
|
filegroup {
|
|
|
|
name: "b",
|
|
|
|
srcs: ["b.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-04-16 13:47:36 +02:00
|
|
|
[[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
|
|
|
|
[[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory"
|
2021-05-18 13:47:15 +02:00
|
|
|
[[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
|
|
|
|
[[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \
|
2021-04-16 13:47:36 +02:00
|
|
|
|| fail "BUILD files symlinked at the wrong place"
|
|
|
|
[[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked"
|
|
|
|
[[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked"
|
|
|
|
[[ ! -e out/soong/workspace/out ]] || fail "out directory symlinked"
|
|
|
|
}
|
|
|
|
|
2021-04-20 21:32:07 +02:00
|
|
|
function test_bp2build_bazel_workspace_add_file {
|
2021-04-16 13:47:36 +02:00
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
touch a/a.txt
|
|
|
|
cat > a/Android.bp <<EOF
|
|
|
|
filegroup {
|
|
|
|
name: "a",
|
|
|
|
srcs: ["a.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-04-16 13:47:36 +02:00
|
|
|
|
|
|
|
touch a/a2.txt # No reference in the .bp file needed
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-04-16 13:47:36 +02:00
|
|
|
[[ -L out/soong/workspace/a/a2.txt ]] || fail "a/a2.txt not symlinked"
|
|
|
|
}
|
|
|
|
|
2021-04-21 11:58:36 +02:00
|
|
|
function test_bp2build_build_file_precedence {
|
|
|
|
setup
|
|
|
|
|
|
|
|
mkdir -p a
|
|
|
|
touch a/a.txt
|
2021-05-18 13:47:15 +02:00
|
|
|
touch a/${GENERATED_BUILD_FILE_NAME}
|
2021-04-21 11:58:36 +02:00
|
|
|
cat > a/Android.bp <<EOF
|
|
|
|
filegroup {
|
|
|
|
name: "a",
|
|
|
|
srcs: ["a.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-05-18 13:47:15 +02:00
|
|
|
[[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
|
|
|
|
[[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \
|
|
|
|
|| fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
|
2021-04-21 11:58:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function test_bp2build_reports_multiple_errors {
|
|
|
|
setup
|
|
|
|
|
2021-05-18 13:47:15 +02:00
|
|
|
mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
|
2021-04-21 11:58:36 +02:00
|
|
|
touch a/a.txt
|
|
|
|
cat > a/Android.bp <<EOF
|
|
|
|
filegroup {
|
|
|
|
name: "a",
|
|
|
|
srcs: ["a.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-05-18 13:47:15 +02:00
|
|
|
mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
|
2021-04-21 11:58:36 +02:00
|
|
|
touch b/b.txt
|
|
|
|
cat > b/Android.bp <<EOF
|
|
|
|
filegroup {
|
|
|
|
name: "b",
|
|
|
|
srcs: ["b.txt"],
|
|
|
|
bazel_module: { bp2build_available: true },
|
|
|
|
}
|
|
|
|
EOF
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
if run_soong bp2build >& "$MOCK_TOP/errors"; then
|
2021-04-21 11:58:36 +02:00
|
|
|
fail "Build should have failed"
|
|
|
|
fi
|
|
|
|
|
2021-05-18 13:47:15 +02:00
|
|
|
grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
|
|
|
|
grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
|
2021-04-21 11:58:36 +02:00
|
|
|
}
|
|
|
|
|
2021-08-12 14:03:55 +02:00
|
|
|
function test_bp2build_back_and_forth_null_build {
|
|
|
|
setup
|
|
|
|
|
|
|
|
run_soong
|
|
|
|
local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-08-12 14:03:55 +02:00
|
|
|
local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
|
|
|
|
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
|
|
|
|
fail "Output Ninja file changed when switching to bp2build"
|
|
|
|
fi
|
|
|
|
|
2021-11-02 14:42:04 +01:00
|
|
|
local marker_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
|
2021-08-12 14:03:55 +02:00
|
|
|
|
|
|
|
run_soong
|
|
|
|
local output_mtime3=$(stat -c "%y" out/soong/build.ninja)
|
2021-11-02 14:42:04 +01:00
|
|
|
local marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
|
2021-08-12 14:03:55 +02:00
|
|
|
if [[ "$output_mtime1" != "$output_mtime3" ]]; then
|
|
|
|
fail "Output Ninja file changed when switching to regular build from bp2build"
|
|
|
|
fi
|
|
|
|
if [[ "$marker_mtime1" != "$marker_mtime2" ]]; then
|
|
|
|
fail "bp2build marker file changed when switching to regular build from bp2build"
|
|
|
|
fi
|
|
|
|
|
2021-09-02 17:23:06 +02:00
|
|
|
run_soong bp2build
|
2021-08-12 14:03:55 +02:00
|
|
|
local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
|
2021-11-02 14:42:04 +01:00
|
|
|
local marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
|
2021-08-12 14:03:55 +02:00
|
|
|
if [[ "$output_mtime1" != "$output_mtime4" ]]; then
|
|
|
|
fail "Output Ninja file changed when switching back to bp2build"
|
|
|
|
fi
|
|
|
|
if [[ "$marker_mtime1" != "$marker_mtime3" ]]; then
|
|
|
|
fail "bp2build marker file changed when switching back to bp2build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-09-06 17:08:02 +02:00
|
|
|
function test_queryview_smoke() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
run_soong queryview
|
|
|
|
[[ -e out/soong/queryview/WORKSPACE ]] || fail "queryview WORKSPACE file not created"
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function test_queryview_null_build() {
|
|
|
|
setup
|
|
|
|
|
|
|
|
run_soong queryview
|
|
|
|
local output_mtime1=$(stat -c "%y" out/soong/queryview.marker)
|
|
|
|
|
|
|
|
run_soong queryview
|
|
|
|
local output_mtime2=$(stat -c "%y" out/soong/queryview.marker)
|
|
|
|
|
|
|
|
if [[ "$output_mtime1" != "$output_mtime2" ]]; then
|
|
|
|
fail "Queryview marker file changed on null build"
|
|
|
|
fi
|
|
|
|
}
|
|
|
|
|
2021-03-16 08:55:23 +01:00
|
|
|
test_smoke
|
|
|
|
test_null_build
|
2021-09-06 18:31:46 +02:00
|
|
|
test_soong_docs_smoke
|
|
|
|
test_null_build_after_soong_docs
|
2021-03-16 08:55:23 +01:00
|
|
|
test_soong_build_rebuilt_if_blueprint_changes
|
2021-04-21 12:05:08 +02:00
|
|
|
test_glob_noop_incremental
|
2021-03-16 08:55:23 +01:00
|
|
|
test_add_file_to_glob
|
|
|
|
test_add_android_bp
|
|
|
|
test_change_android_bp
|
|
|
|
test_delete_android_bp
|
|
|
|
test_add_file_to_soong_build
|
2021-04-08 19:34:16 +02:00
|
|
|
test_glob_during_bootstrapping
|
2021-03-23 11:46:47 +01:00
|
|
|
test_soong_build_rerun_iff_environment_changes
|
2021-09-02 17:23:06 +02:00
|
|
|
test_multiple_soong_build_modes
|
2021-04-01 18:28:45 +02:00
|
|
|
test_dump_json_module_graph
|
2021-08-25 14:14:13 +02:00
|
|
|
test_json_module_graph_back_and_forth_null_build
|
2021-06-25 03:39:04 +02:00
|
|
|
test_write_to_source_tree
|
2021-09-06 17:08:02 +02:00
|
|
|
test_queryview_smoke
|
|
|
|
test_queryview_null_build
|
2021-04-20 21:32:07 +02:00
|
|
|
test_bp2build_smoke
|
2021-08-12 14:03:55 +02:00
|
|
|
test_bp2build_generates_marker_file
|
2021-04-20 21:32:07 +02:00
|
|
|
test_bp2build_null_build
|
2021-08-12 14:03:55 +02:00
|
|
|
test_bp2build_back_and_forth_null_build
|
2021-04-20 21:32:07 +02:00
|
|
|
test_bp2build_add_android_bp
|
2022-10-26 09:26:50 +02:00
|
|
|
test_bp2build_add_irrelevant_file
|
2021-04-20 21:32:07 +02:00
|
|
|
test_bp2build_add_to_glob
|
|
|
|
test_bp2build_bazel_workspace_structure
|
|
|
|
test_bp2build_bazel_workspace_add_file
|
2021-04-21 11:58:36 +02:00
|
|
|
test_bp2build_build_file_precedence
|
|
|
|
test_bp2build_reports_multiple_errors
|