| // 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.common.collect.ImmutableList; |
| 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.io.IOException; |
| import java.nio.file.Path; |
| import org.junit.Test; |
| |
| /** |
| * End to end test of patch API we exposed in @bazel_tools//tools/build_defs/repo:utils.bzl. The |
| * patch API is used in http_repository and git_repository. |
| * |
| * <p>The idea is to use a custom repository rules that use the API to patch existing files. |
| */ |
| public class PatchApiBlackBoxTest extends AbstractBlackBoxTest { |
| |
| private void setUpPatchTestRepo( |
| ImmutableList<String> patchArgs, String patchTool, boolean hasPatchCmdsWin) |
| throws IOException { |
| StringBuilder patchArgsStr = new StringBuilder("\"-p1\""); |
| if (patchArgs != null) { |
| patchArgsStr = new StringBuilder(); |
| for (String arg : patchArgs) { |
| patchArgsStr.append("\"").append(arg).append("\", "); |
| } |
| } |
| context() |
| .write( |
| "patched_repo.bzl", |
| "load(", |
| " \"@bazel_tools//tools/build_defs/repo:utils.bzl\",", |
| " \"workspace_and_buildfile\",", |
| " \"patch\",", |
| ")", |
| "", |
| "_common_attrs = {", |
| " \"files\": attr.string_dict(default = {}),", |
| " \"patches\": attr.label_list(default = []),", |
| " \"patch_tool\": attr.string(default = \"\"),", |
| " \"patch_args\": attr.string_list(default = []),", |
| " \"patch_cmds\": attr.string_list(default = []),", |
| " \"patch_cmds_win\": attr.string_list(default = []),", |
| " \"build_file\": attr.label(allow_single_file = True),", |
| " \"build_file_content\": attr.string(),", |
| " \"workspace_file\": attr.label(),", |
| " \"workspace_file_content\": attr.string(),", |
| "}", |
| "", |
| "def _patched_repo_implementation(ctx):", |
| " for file_name, label in ctx.attr.files.items():", |
| " ctx.template(file_name, ctx.path(Label(label)))", |
| " workspace_and_buildfile(ctx)", |
| " patch(ctx)", |
| "", |
| "", |
| "patched_repo = repository_rule(", |
| " implementation = _patched_repo_implementation,", |
| " attrs = _common_attrs,", |
| ")"); |
| context() |
| .write( |
| WORKSPACE, |
| "load(\":patched_repo.bzl\", \"patched_repo\")", |
| "", |
| "patched_repo(", |
| " name = \"test\",", |
| " files = {\"foo.sh\" : \"//:foo.sh\"},", |
| " patches = [\"//:remove-dragons.patch\"],", |
| String.format(" patch_args = [%s],", patchArgsStr.toString()), |
| (patchTool == null ? "" : String.format(" patch_tool = \"%s\",", patchTool)), |
| " patch_cmds = [", |
| " \"find . -name '*.sh' -exec sed -i.bak '1s|/usr/bin/env sh|/bin/sh|' {} +\",", |
| " \"chmod u+x ./foo.sh\",", |
| " ],", |
| (hasPatchCmdsWin |
| ? " patch_cmds_win = [\"(Get-Content -path foo.sh) -replace '/usr/bin/env" |
| + " sh','/bin/sh' | Set-Content -Path foo.sh\"]," |
| : ""), |
| " build_file_content =", |
| " \"\"\"", |
| "filegroup(", |
| " name = \"foo\",", |
| " srcs = [\"foo.sh\"],", |
| ")", |
| " \"\"\"", |
| ")"); |
| context().write("BUILD"); |
| context().write("foo.sh", "#!/usr/bin/env sh", "", "echo Here be dragons...", ""); |
| context() |
| .write( |
| "remove-dragons.patch", |
| "#!/usr/bin/env sh", |
| "diff --git a/foo.sh b/foo.sh", |
| "index 1f4c41e..9d548ff 100644", |
| "--- a/foo.sh", |
| "+++ b/foo.sh", |
| "@@ -1,3 +1,3 @@", |
| " #!/usr/bin/env sh", |
| "", |
| "-echo Here be dragons...", |
| "+echo New version of foo.sh, no more dangerous animals...", |
| ""); |
| } |
| |
| @Test |
| public void testPatchApiUsingNativePatch() throws Exception { |
| setUpPatchTestRepo(null, null, true); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| bazel.build("@test//:foo"); |
| assertFooIsPatched(bazel); |
| } |
| |
| @Test |
| public void testPatchApiUsingNativePatchFailed() throws Exception { |
| // Using -p2 should cause an error |
| setUpPatchTestRepo(ImmutableList.of("-p2"), null, true); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()).shouldFail(); |
| ProcessResult result = bazel.build("@test//:foo"); |
| assertThat(result.errString()) |
| .contains("Cannot determine file name with strip = 2 at line 4:\n--- a/foo.sh"); |
| } |
| |
| @Test |
| public void testFallBackToPatchToolDueToPatchArgs() throws Exception { |
| // Native patch doesn't support -b argument, should fallback to patch command line tool. |
| setUpPatchTestRepo(ImmutableList.of("-p1", "-b"), null, true); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| if (isWindows()) { |
| // On Windows, we expect no patch tool in PATH after removing MSYS paths from PATH env var. |
| bazel.shouldFail(); |
| } |
| ProcessResult result = bazel.build("@test//:foo"); |
| if (isWindows()) { |
| assertThat(result.errString()) |
| .contains("CreateProcessW(\"C:\\foo\\bar\\usr\\bin\\bash.exe\" -c \"patch '-p1' '-b'"); |
| assertThat(result.errString()).contains("The system cannot find the file specified."); |
| } else { |
| assertFooIsPatched(bazel); |
| // foo.sh.orig should be generated due to "-b" argument. |
| Path fooOrig = context().resolveExecRootPath(bazel, "external/test/foo.sh.orig"); |
| assertThat(fooOrig.toFile().exists()).isTrue(); |
| } |
| } |
| |
| @Test |
| public void testFallBackToPatchToolWhenItIsSpecified() throws Exception { |
| // Should fallback to the specified patch tool. |
| setUpPatchTestRepo(null, "patch", true); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| if (isWindows()) { |
| // On Windows, we expect no patch tool in PATH after removing MSYS paths from PATH env var. |
| bazel.shouldFail(); |
| } |
| ProcessResult result = bazel.build("@test//:foo"); |
| if (isWindows()) { |
| assertThat(result.errString()) |
| .contains("CreateProcessW(\"C:\\foo\\bar\\usr\\bin\\bash.exe\" -c \"patch '-p1'"); |
| assertThat(result.errString()).contains("The system cannot find the file specified."); |
| } else { |
| assertFooIsPatched(bazel); |
| } |
| } |
| |
| @Test |
| public void testFallBackToPatchCmdsWhenPatchCmdsWinNotSpecified() throws Exception { |
| setUpPatchTestRepo(null, null, false); |
| BuilderRunner bazel = WorkspaceTestUtils.bazel(context()); |
| if (isWindows()) { |
| // On Windows, we expect no bash tool in PATH after removing MSYS paths from PATH env var. |
| bazel.shouldFail(); |
| } |
| ProcessResult result = bazel.build("@test//:foo"); |
| if (isWindows()) { |
| assertThat(result.errString()) |
| .contains("CreateProcessW(\"C:\\foo\\bar\\usr\\bin\\bash.exe\" -c"); |
| assertThat(result.errString()).contains("The system cannot find the file specified."); |
| } else { |
| assertFooIsPatched(bazel); |
| } |
| } |
| |
| private void assertFooIsPatched(BuilderRunner bazel) throws Exception { |
| Path foo = context().resolveExecRootPath(bazel, "external/test/foo.sh"); |
| assertThat(foo.toFile().exists()).isTrue(); |
| ImmutableList<String> patchedFoo = |
| ImmutableList.of( |
| "#!/bin/sh", "", "echo New version of foo.sh, no more dangerous animals...", ""); |
| assertThat(PathUtils.readFile(foo)).containsExactlyElementsIn(patchedFoo); |
| } |
| } |