| #!/bin/bash |
| # |
| # Copyright 2018 The Bazel Authors. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # Load the test setup defined in the parent directory |
| CURRENT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| source "${CURRENT_DIR}/../integration_test_setup.sh" \ |
| || { echo "integration_test_setup.sh not found!" >&2; exit 1; } |
| |
| test_result_recorded() { |
| mkdir result_recorded && cd result_recorded |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| cd fetchrepo |
| cat > rule.bzl <<'EOF' |
| def _rule_impl(ctx): |
| ctx.symlink(ctx.attr.build_file, "BUILD") |
| return {"build_file": ctx.attr.build_file, "extra_arg": "foobar"} |
| |
| trivial_rule = repository_rule( |
| implementation = _rule_impl, |
| attrs = { "build_file" : attr.label() }, |
| ) |
| |
| EOF |
| cat > ext.BUILD <<'EOF' |
| genrule( |
| name = "foo", |
| outs = ["foo.txt"], |
| cmd = "echo bar > $@", |
| ) |
| EOF |
| touch BUILD |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "trivial_rule") |
| trivial_rule( |
| name = "ext", |
| build_file = "//:ext.BUILD", |
| ) |
| EOF |
| |
| bazel clean --expunge |
| bazel build --experimental_repository_resolved_file=../repo.bzl @ext//... \ |
| || fail "Expected success" |
| # some of the file systems on our test machines are really slow to |
| # notice the creation of a file---even after the call to sync(1). |
| bazel shutdown; sync; sleep 10 |
| |
| # Verify that bazel can read the generated repo.bzl file and that it contains |
| # the expected information |
| cd .. |
| echo; cat repo.bzl; echo; echo |
| mkdir analysisrepo |
| mv repo.bzl analysisrepo |
| cd analysisrepo |
| touch WORKSPACE |
| cat > BUILD <<'EOF' |
| load("//:repo.bzl", "resolved") |
| |
| [ genrule( |
| name = "out", |
| outs = ["out.txt"], |
| cmd = "echo %s > $@" % entry["repositories"][0]["attributes"]["extra_arg"], |
| ) for entry in resolved if entry["original_rule_class"] == "//:rule.bzl%trivial_rule" |
| ] |
| |
| [ genrule( |
| name = "origcount", |
| outs = ["origcount.txt"], |
| cmd = "echo %s > $@" % len(entry["original_attributes"]) |
| ) for entry in resolved if entry["original_rule_class"] == "//:rule.bzl%trivial_rule" |
| ] |
| EOF |
| bazel build :out :origcount || fail "Expected success" |
| grep "foobar" `bazel info bazel-genfiles`/out.txt \ |
| || fail "Did not find the expected value" |
| [ $(cat `bazel info bazel-genfiles`/origcount.txt) -eq 2 ] \ |
| || fail "Not the correct number of original attributes" |
| } |
| |
| test_git_return_value() { |
| EXTREPODIR=`pwd` |
| export GIT_CONFIG_NOSYSTEM=YES |
| |
| mkdir extgit |
| (cd extgit && git init \ |
| && git config user.email 'me@example.com' \ |
| && git config user.name 'E X Ample' ) |
| echo Hello World > extgit/hello.txt |
| (cd extgit |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'initial commit' |
| git tag mytag) |
| |
| # Check out the external git repository at the given tag, and record |
| # the return value of the git rule. |
| mkdir tagcheckout |
| cd tagcheckout |
| cat > WORKSPACE <<EOF |
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
| new_git_repository( |
| name="ext", |
| remote="file://${EXTREPODIR}/extgit/.git", |
| tag="mytag", |
| build_file_content="exports_files([\"hello.txt\"])", |
| ) |
| EOF |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| # some of the file systems on our test machines are really slow to |
| # notice the creation of a file---even after the call to sync(1). |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| |
| # Now add an additional commit to the upstream repository and |
| # force update the tag |
| echo CHANGED > extgit/hello.txt |
| (cd extgit |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'change hello.txt' |
| git tag -f mytag) |
| |
| # Verify that the recorded resolved information is what we expect. In |
| # particular, verify that we don't get the new upstream commit. |
| mkdir analysisrepo |
| cd analysisrepo |
| cp ../repo.bzl . |
| cat > workspace.bzl <<'EOF' |
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
| load("//:repo.bzl", "resolved") |
| |
| def repo(): |
| for entry in resolved: |
| if entry["original_attributes"]["name"] == "ext": |
| new_git_repository(**(entry["repositories"][0]["attributes"])) |
| EOF |
| cat > WORKSPACE <<'EOF' |
| load("//:workspace.bzl", "repo") |
| repo() |
| EOF |
| cat > BUILD <<'EOF' |
| genrule( |
| name = "out", |
| outs = ["out.txt"], |
| srcs = ["@ext//:hello.txt"], |
| cmd = "cp $< $@", |
| ) |
| EOF |
| bazel build //:out |
| grep "Hello World" `bazel info bazel-genfiles`/out.txt \ |
| || fail "ext not taken at the right commit" |
| grep "CHANGED" `bazel info bazel-genfiles`/out.txt \ |
| && fail "not taking the frozen commit" || : |
| } |
| |
| test_git_follow_branch() { |
| EXTREPODIR=`pwd` |
| export GIT_CONFIG_NOSYSTEM=YES |
| |
| mkdir extgit |
| (cd extgit && git init \ |
| && git config user.email 'me@example.com' \ |
| && git config user.name 'E X Ample' ) |
| echo Hello World > extgit/hello.txt |
| (cd extgit |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'initial commit') |
| # Check out the external git repository at the given branch, and record |
| # the return value of the git rule. |
| mkdir branchcheckout |
| cd branchcheckout |
| cat > WORKSPACE <<EOF |
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
| new_git_repository( |
| name="ext", |
| remote="file://${EXTREPODIR}/extgit/.git", |
| branch="master", |
| build_file_content="exports_files([\"hello.txt\"])", |
| ) |
| EOF |
| cat > BUILD <<'EOF' |
| genrule( |
| name = "out", |
| outs = ["out.txt"], |
| srcs = ["@ext//:hello.txt"], |
| cmd = "cp $< $@", |
| ) |
| EOF |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| bazel build :out |
| grep "CHANGED" `bazel info bazel-genfiles`/out.txt \ |
| && fail "Unexpected content in out.txt" || : |
| cd .. |
| echo; cat repo.bzl; echo |
| |
| # Now add an additional commit to the upstream repository |
| echo CHANGED > extgit/hello.txt |
| (cd extgit |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'change hello.txt') |
| |
| |
| # First verify that `bazel sync` sees the new commit (we don't record it). |
| cd branchcheckout |
| bazel sync |
| bazel build :out |
| grep "CHANGED" `bazel info bazel-genfiles`/out.txt \ |
| || fail "sync did not update the external repository" |
| bazel shutdown; sync; sleep 10 |
| cd .. |
| echo |
| |
| # Verify that the recorded resolved information is what we expect. In |
| # particular, verify that we don't get the new upstream commit. |
| mkdir analysisrepo |
| cd analysisrepo |
| cp ../repo.bzl . |
| cat > workspace.bzl <<'EOF' |
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
| load("//:repo.bzl", "resolved") |
| |
| def repo(): |
| for entry in resolved: |
| if entry["original_attributes"]["name"] == "ext": |
| new_git_repository(**(entry["repositories"][0]["attributes"])) |
| EOF |
| cat > WORKSPACE <<'EOF' |
| load("//:workspace.bzl", "repo") |
| repo() |
| EOF |
| cat > BUILD <<'EOF' |
| genrule( |
| name = "out", |
| outs = ["out.txt"], |
| srcs = ["@ext//:hello.txt"], |
| cmd = "cp $< $@", |
| ) |
| EOF |
| bazel build //:out |
| grep "Hello World" `bazel info bazel-genfiles`/out.txt \ |
| || fail "ext not taken at the right commit" |
| grep "CHANGED" `bazel info bazel-genfiles`/out.txt \ |
| && fail "not taking the frozen commit" || : |
| } |
| |
| test_sync_follows_git_branch() { |
| EXTREPODIR=`pwd` |
| export GIT_CONFIG_NOSYSTEM=YES |
| |
| rm -f gitdir |
| mkdir gitdir |
| (cd gitdir && git init \ |
| && git config user.email 'me@example.com' \ |
| && git config user.name 'E X Ample' ) |
| echo Hello World > gitdir/hello.txt |
| (cd gitdir |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'initial commit') |
| echo Hello Stable World > gitdir/hello.txt |
| (cd gitdir |
| git checkout -b stable |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'stable commit') |
| |
| # Follow the stable branch of the git repository |
| mkdir followbranch |
| cat > followbranch/WORKSPACE <<EOF |
| load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository") |
| new_git_repository( |
| name="ext", |
| remote="file://${EXTREPODIR}/gitdir/.git", |
| branch="stable", |
| build_file_content="exports_files([\"hello.txt\"])", |
| ) |
| EOF |
| cat > followbranch/BUILD <<'EOF' |
| genrule( |
| name = "out", |
| outs = ["out.txt"], |
| srcs = ["@ext//:hello.txt"], |
| cmd = "cp $< $@", |
| ) |
| EOF |
| (cd followbranch && bazel build :out \ |
| && cat `bazel info bazel-genfiles`/out.txt > "${TEST_log}") |
| expect_log 'Hello Stable World' |
| |
| # New upstream commits on the branch followed |
| echo CHANGED > gitdir/hello.txt |
| (cd gitdir |
| git checkout stable |
| git add . |
| git commit --author="A U Thor <author@example.com>" -m 'stable commit') |
| |
| # Verify that sync followed by build gets the correct version |
| (cd followbranch && bazel sync && bazel build :out \ |
| && cat `bazel info bazel-genfiles`/out.txt > "${TEST_log}") |
| expect_log 'CHANGED' |
| expect_not_log 'Hello Stable World' |
| } |
| |
| |
| test_sync_calls_all() { |
| mkdir sync_calls_all && cd sync_calls_all |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| rm -f repo.bzl |
| cd fetchrepo |
| cat > rule.bzl <<'EOF' |
| def _rule_impl(ctx): |
| ctx.file("foo.bzl", """ |
| it = "foo" |
| other = "bar" |
| """) |
| ctx.file("BUILD", "") |
| return {"comment" : ctx.attr.comment } |
| |
| trivial_rule = repository_rule( |
| implementation = _rule_impl, |
| attrs = { "comment" : attr.string() }, |
| ) |
| EOF |
| touch BUILD |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "trivial_rule") |
| trivial_rule(name = "a", comment = "bootstrap") |
| load("@a//:foo.bzl", "it") |
| trivial_rule(name = "b", comment = it) |
| trivial_rule(name = "c", comment = it) |
| load("@c//:foo.bzl", "other") |
| trivial_rule(name = "d", comment = other) |
| EOF |
| |
| bazel clean --expunge |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| # some of the file systems on our test machines are really slow to |
| # notice the creation of a file---even after the call to sync(1). |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| touch WORKSPACE |
| cat > BUILD <<'EOF' |
| load("//:repo.bzl", "resolved") |
| |
| names = [entry["original_attributes"]["name"] for entry in resolved] |
| |
| [ |
| genrule( |
| name = name, |
| outs = [ "%s.txt" % (name,) ], |
| cmd = "echo %s > $@" % (name,), |
| ) for name in names |
| ] |
| EOF |
| bazel build :a :b :c :d || fail "Expected all 4 repositories to be present" |
| } |
| |
| test_sync_call_invalidates() { |
| mkdir sync_call_invalidates && cd sync_call_invalidates |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| rm -f repo.bzl |
| touch BUILD |
| cat > rule.bzl <<'EOF' |
| def _rule_impl(ctx): |
| ctx.file("BUILD", """ |
| genrule( |
| name = "it", |
| outs = ["it.txt"], |
| cmd = "echo hello world > $@", |
| ) |
| """) |
| ctx.file("WORKSPACE", "") |
| |
| trivial_rule = repository_rule( |
| implementation = _rule_impl, |
| attrs = {}, |
| ) |
| EOF |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "trivial_rule") |
| |
| trivial_rule(name = "a") |
| trivial_rule(name = "b") |
| EOF |
| |
| bazel build @a//... @b//... |
| echo; echo sync run; echo |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| # some of the file systems on our test machines are really slow to |
| # notice the creation of a file---even after the call to sync(1). |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| touch WORKSPACE |
| cat > BUILD <<'EOF' |
| load("//:repo.bzl", "resolved") |
| |
| names = [entry["original_attributes"]["name"] for entry in resolved] |
| |
| [ |
| genrule( |
| name = name, |
| outs = [ "%s.txt" % (name,) ], |
| cmd = "echo %s > $@" % (name,), |
| ) for name in names |
| ] |
| EOF |
| bazel build :a :b || fail "Expected both repositories to be present" |
| } |
| |
| test_sync_load_errors_reported() { |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| cd fetchrepo |
| cat > WORKSPACE <<'EOF' |
| load("//does/not:exist.bzl", "randomfunction") |
| |
| radomfunction(name="foo") |
| EOF |
| bazel sync > "${TEST_log}" 2>&1 && fail "Expected failure" || : |
| expect_log '//does/not:exist.bzl' |
| } |
| |
| test_sync_debug_and_errors_printed() { |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| cd fetchrepo |
| cat > rule.bzl <<'EOF' |
| def _broken_rule_impl(ctx): |
| print("DEBUG-message") |
| fail("Failure-message") |
| |
| broken_rule = repository_rule( |
| implementation = _broken_rule_impl, |
| attrs = {}, |
| ) |
| EOF |
| touch BUILD |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "broken_rule") |
| |
| broken_rule(name = "broken") |
| EOF |
| bazel sync > "${TEST_log}" 2>&1 && fail "expected failure" || : |
| expect_log "DEBUG-message" |
| expect_log "Failure-message" |
| } |
| |
| test_indirect_call() { |
| rm -rf fetchrepo |
| mkdir fetchrepo |
| cd fetchrepo |
| touch BUILD |
| cat > rule.bzl <<'EOF' |
| def _trivial_rule_impl(ctx): |
| ctx.file("BUILD","genrule(name='hello', outs=['hello.txt'], cmd=' echo hello world > $@')") |
| |
| trivial_rule = repository_rule( |
| implementation = _trivial_rule_impl, |
| attrs = {}, |
| ) |
| EOF |
| cat > indirect.bzl <<'EOF' |
| def call(fn_name, **args): |
| fn_name(**args) |
| EOF |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "trivial_rule") |
| load("//:indirect.bzl", "call") |
| |
| call(trivial_rule, name="foo") |
| EOF |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| touch WORKSPACE |
| cat > BUILD <<'EOF' |
| load("//:repo.bzl", "resolved") |
| |
| ruleclass = "".join([entry["original_rule_class"] for entry in resolved if entry["original_attributes"]["name"]=="foo"]) |
| |
| genrule( |
| name = "ruleclass", |
| outs = ["ruleclass.txt"], |
| cmd = "echo %s > $@" % (ruleclass,) |
| ) |
| EOF |
| bazel build //:ruleclass |
| cat `bazel info bazel-genfiles`/ruleclass.txt > ${TEST_log} |
| expect_log '//:rule.bzl%trivial_rule' |
| expect_not_log 'fn_name' |
| } |
| |
| create_sample_repository() { |
| # Create, in the current direcotry, a repository that creates an external |
| # repository `foo` containing |
| # - file with fixed data, generated by ctx.file, |
| # - a BUILD file linked from the main repository |
| # - a symlink to ., and |
| # - danling absolute and reproducible symlink. |
| touch BUILD |
| cat > rule.bzl <<'EOF' |
| def _trivial_rule_impl(ctx): |
| ctx.symlink(ctx.attr.build_file, "BUILD") |
| ctx.file("data.txt", "some data") |
| ctx.execute(["ln", "-s", ".", "self_link"]) |
| ctx.execute(["ln", "-s", "/does/not/exist", "dangling"]) |
| |
| trivial_rule = repository_rule( |
| implementation = _trivial_rule_impl, |
| attrs = { "build_file" : attr.label() }, |
| ) |
| EOF |
| echo '# fixed contents' > BUILD.remote |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "trivial_rule") |
| |
| trivial_rule(name="foo", build_file="@//:BUILD.remote") |
| EOF |
| } |
| |
| test_hash_included_and_reproducible() { |
| # Verify that a hash of the output directory is included, that |
| # the hash is invariant under |
| # - change of the working directory, and |
| # - and current time. |
| |
| rm -rf fetchrepoA |
| mkdir fetchrepoA |
| cd fetchrepoA |
| create_sample_repository |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| touch WORKSPACE |
| cat > BUILD <<'EOF' |
| load("//:repo.bzl", "resolved") |
| hashes = [entry["repositories"][0]["output_tree_hash"] |
| for entry in resolved if entry["original_attributes"]["name"]=="foo"] |
| |
| [genrule( |
| name="hash", |
| outs=["hash.txt"], |
| cmd="echo '%s' > $@" % (hash,), |
| ) for hash in hashes] |
| EOF |
| bazel build //:hash |
| cp `bazel info bazel-genfiles`/hash.txt hashA.txt |
| cat hashA.txt > "${TEST_log}" |
| [ `cat hashA.txt | wc -c` -gt 2 ] \ |
| || fail "A hash of reasonable length expected" |
| bazel clean --expunge |
| rm repo.bzl |
| |
| |
| rm -rf fetchrepoB |
| mkdir fetchrepoB |
| cd fetchrepoB |
| create_sample_repository |
| bazel sync --experimental_repository_resolved_file=../repo.bzl |
| bazel shutdown; sync; sleep 10 |
| |
| cd .. |
| echo; cat repo.bzl; echo |
| bazel build //:hash |
| cp `bazel info bazel-genfiles`/hash.txt hashB.txt |
| cat hashB.txt > "${TEST_log}" |
| diff hashA.txt hashB.txt || fail "Expected hash to be reproducible" |
| } |
| |
| test_non_reproducibility_detected() { |
| # Verify that a non-reproducible rule is detected by hash verification |
| mkdir repo |
| cd repo |
| touch BUILD |
| cat > rule.bzl <<'EOF' |
| def _time_rule_impl(ctx): |
| ctx.execute(["bash", "-c", "date +%s > timestamp"]) |
| |
| time_rule = repository_rule( |
| implementation = _time_rule_impl, |
| attrs = {}, |
| ) |
| EOF |
| cat > WORKSPACE <<'EOF' |
| load("//:rule.bzl", "time_rule") |
| |
| time_rule(name="timestamprepo") |
| EOF |
| |
| bazel sync --experimental_repository_resolved_file=resolved.bzl |
| sync; sleep 10 |
| bazel sync --experimental_repository_hash_file=`pwd`/resolved.bzl \ |
| --experimental_verify_repository_rules='//:rule.bzl%time_rule' \ |
| > "${TEST_log}" 2>&1 && fail "expected failure" || : |
| expect_log "timestamprepo.*hash" |
| } |
| |
| run_suite "workspace_resolved_test tests" |