| // Copyright 2019 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. |
| |
| package com.google.devtools.build.lib.blackbox.tests.workspace; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import com.google.devtools.build.lib.blackbox.framework.BlackBoxTestEnvironment; |
| import com.google.devtools.build.lib.blackbox.framework.BuilderRunner; |
| import com.google.devtools.build.lib.blackbox.framework.PathUtils; |
| import com.google.devtools.build.lib.blackbox.framework.ProcessResult; |
| import com.google.devtools.build.lib.blackbox.junit.AbstractBlackBoxTest; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.List; |
| import org.junit.Test; |
| |
| /** End to end test of workspace-related functionality. */ |
| public class WorkspaceBlackBoxTest extends AbstractBlackBoxTest { |
| |
| @Test |
| public void testNotInMsys() throws Exception { |
| context() |
| .write( |
| "repo_rule.bzl", |
| "def _impl(rctx):", |
| " result = rctx.execute(['bash', '-c', 'which bash > out.txt'])", |
| " if result.return_code != 0:", |
| " fail('Execute bash failed: ' + result.stderr)", |
| " rctx.file('BUILD', 'exports_files([\"out.txt\"])')", |
| "check_bash = repository_rule(implementation = _impl)"); |
| |
| context() |
| .write( |
| WORKSPACE, |
| "workspace(name='subdir')", |
| "load(':repo_rule.bzl', 'check_bash')", |
| "check_bash(name = 'check_bash_target')"); |
| |
| // To make repository rule target be computed, depend on it in debug_rule |
| context() |
| .write( |
| "BUILD", |
| "load(':rule.bzl', 'debug_rule')", |
| "debug_rule(name = 'check', dep = '@check_bash_target//:out.txt')"); |
| |
| context() |
| .write( |
| "rule.bzl", |
| "def _impl(ctx):", |
| " out = ctx.actions.declare_file('does_not_matter')", |
| " ctx.actions.do_nothing(mnemonic = 'UseInput', inputs = ctx.attr.dep.files)", |
| " ctx.actions.write(out, 'Hi')", |
| " return [DefaultInfo(files = depset([out]))]", |
| "", |
| "debug_rule = rule(", |
| " implementation = _impl,", |
| " attrs = {", |
| " \"dep\": attr.label(allow_single_file = True),", |
| " }", |
| ")"); |
| |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| // The build using "bash" should fail on Windows, and pass on Linux and Mac OS |
| if (isWindows()) { |
| bazel.shouldFail(); |
| } |
| bazel.build("check"); |
| } |
| |
| @Test |
| public void testExecuteInWorkingDirectory() throws Exception { |
| String pwd = isWindows() ? "['cmd', '/c', 'echo %cd%']" : "['pwd']"; |
| String buildFileText = |
| "\"\"\"" |
| + String.join( |
| "\n", |
| RepoWithRuleWritingTextGenerator.loadRule("@main"), |
| RepoWithRuleWritingTextGenerator.callRule("debug_me", "out", "%s")) |
| + "\"\"\" % stdout"; |
| context() |
| .write( |
| "repo_rule.bzl", |
| "def _impl(rctx):", |
| String.format( |
| " result = rctx.execute(%s, working_directory=rctx.attr.working_directory)", pwd), |
| " if result.return_code != 0:", |
| " fail('Execute failed: ' + result.stderr)", |
| // we want to compare the real paths, |
| // otherwise it is not clear how to verify the relative path variant |
| " wd = str(rctx.path(rctx.attr.working_directory))", |
| // pwd returns the path with '\n' in the end of the line; cut it |
| " stdout = result.stdout.strip(' \\n\\r').replace('\\\\', '/')", |
| " if wd != stdout:", |
| " fail('Wrong current directory: **%s**, expecting **%s**' % (stdout, wd))", |
| // create BUILD file with a target so we can call it; |
| // rule of a target is defined in the main repository |
| " rctx.file('BUILD', " + buildFileText + ")", |
| "check_wd = repository_rule(implementation = _impl,", |
| " attrs = { 'working_directory': attr.string() }", |
| ")"); |
| |
| context() |
| .write( |
| RepoWithRuleWritingTextGenerator.HELPER_FILE, |
| RepoWithRuleWritingTextGenerator.WRITE_TEXT_TO_FILE); |
| context().write("BUILD"); |
| |
| Path tempDirectory = Files.createTempDirectory("temp-execute"); |
| context() |
| .write( |
| WORKSPACE, |
| "workspace(name = 'main')", |
| "load(':repo_rule.bzl', 'check_wd')", |
| "check_wd(name = 'relative', working_directory = 'relative')", |
| "check_wd(name = 'relative2', working_directory = '../relative2')", |
| String.format( |
| "check_wd(name = 'absolute', working_directory = '%s')", |
| PathUtils.pathForStarlarkFile(tempDirectory)), |
| String.format( |
| "check_wd(name = 'absolute2', working_directory = '%s')", |
| PathUtils.pathForStarlarkFile(tempDirectory.resolve("non_existent_child")))); |
| |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| bazel.build("@relative//:debug_me"); |
| Path outFile = context().resolveBinPath(bazel, "external/relative/out"); |
| assertThat(outFile.toFile().exists()).isTrue(); |
| List<String> lines = PathUtils.readFile(outFile); |
| assertThat(lines.size()).isEqualTo(1); |
| assertThat(Paths.get(lines.get(0)).endsWith(Paths.get("external/relative/relative"))).isTrue(); |
| |
| bazel.build("@relative2//:debug_me"); |
| bazel.build("@absolute//:debug_me"); |
| |
| bazel.build("@absolute2//:debug_me"); |
| Path outFile2 = context().resolveBinPath(bazel, "external/absolute2/out"); |
| assertThat(outFile2.toFile().exists()).isTrue(); |
| List<String> lines2 = PathUtils.readFile(outFile2); |
| assertThat(lines2.size()).isEqualTo(1); |
| assertThat(Paths.get(lines2.get(0)).equals(tempDirectory.resolve("non_existent_child"))) |
| .isTrue(); |
| } |
| |
| @Test |
| public void testWorkspaceChanges() throws Exception { |
| Path repoA = context().getTmpDir().resolve("a"); |
| new RepoWithRuleWritingTextGenerator(repoA).withOutputText("hi").setupRepository(); |
| |
| Path repoB = context().getTmpDir().resolve("b"); |
| new RepoWithRuleWritingTextGenerator(repoB).withOutputText("bye").setupRepository(); |
| |
| context() |
| .write( |
| WORKSPACE, |
| String.format( |
| "local_repository(name = 'x', path = '%s',)", |
| PathUtils.pathForStarlarkFile(repoA))); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| bazel.build("@x//:" + RepoWithRuleWritingTextGenerator.TARGET); |
| |
| Path xPath = context().resolveBinPath(bazel, "external/x/out"); |
| WorkspaceTestUtils.assertLinesExactly(xPath, "hi"); |
| |
| context() |
| .write( |
| WORKSPACE, |
| String.format( |
| "local_repository(name = 'x', path = '%s',)", |
| PathUtils.pathForStarlarkFile(repoB))); |
| bazel.build("@x//:" + RepoWithRuleWritingTextGenerator.TARGET); |
| |
| WorkspaceTestUtils.assertLinesExactly(xPath, "bye"); |
| } |
| |
| @Test |
| public void testNoPackageLoadingOnBenignWorkspaceChanges() throws Exception { |
| Path repo = context().getTmpDir().resolve(testName.getMethodName()); |
| new RepoWithRuleWritingTextGenerator(repo).withOutputText("hi").setupRepository(); |
| |
| context() |
| .write( |
| WORKSPACE, |
| String.format( |
| "local_repository(name = 'ext', path = '%s',)", |
| PathUtils.pathForStarlarkFile(repo))); |
| |
| BuilderRunner bazel = |
| WorkspaceTestUtils.bazel(context()) |
| // This combination of flags ensures all progress events get into stdout |
| // and Bazel recognizes that there is a terminal, so progress events will be displayed |
| .withFlags("--experimental_ui_debug_all_events", "--curses=yes"); |
| |
| final String progressMessage = "PROGRESS <no location>: Loading package: @ext//"; |
| |
| ProcessResult result = bazel.query("@ext//:all"); |
| assertThat(result.outString()).contains(progressMessage); |
| |
| result = bazel.query("@ext//:all"); |
| assertThat(result.outString()).doesNotContain(progressMessage); |
| |
| Path workspaceFile = context().getWorkDir().resolve(WORKSPACE); |
| PathUtils.append(workspaceFile, "# comment"); |
| |
| result = bazel.query("@ext//:all"); |
| assertThat(result.outString()).doesNotContain(progressMessage); |
| } |
| |
| @Test |
| public void testPathWithSpace() throws Exception { |
| context().write("a b/WORKSPACE"); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| bazel.info(); |
| bazel.help(); |
| } |
| |
| @Test |
| public void testWorkspaceFileIsSymlink() throws Exception { |
| if (isWindows()) { |
| // Do not test file symlinks on Windows. |
| return; |
| } |
| Path repo = context().getTmpDir().resolve(testName.getMethodName()); |
| new RepoWithRuleWritingTextGenerator(repo).withOutputText("hi").setupRepository(); |
| |
| Path workspaceFile = context().getWorkDir().resolve(WORKSPACE); |
| assertThat(workspaceFile.toFile().delete()).isTrue(); |
| |
| Path tempWorkspace = Files.createTempFile(context().getTmpDir(), WORKSPACE, ""); |
| PathUtils.writeFile( |
| tempWorkspace, |
| "workspace(name = 'abc')", |
| BlackBoxTestEnvironment.getWorkspaceWithDefaultRepos(), |
| String.format( |
| "local_repository(name = 'ext', path = '%s',)", PathUtils.pathForStarlarkFile(repo))); |
| Files.createSymbolicLink(workspaceFile, tempWorkspace); |
| |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| bazel.build("@ext//:all"); |
| PathUtils.append(workspaceFile, "# comment"); |
| // At this point, there is already some cache workspace file/file state value. |
| bazel.build("@ext//:all"); |
| } |
| // TODO(ichern) move other tests from workspace_test.sh here. |
| |
| } |