blob: 00e3205e6d71e89d96307ae2f462142c6a4d50e5 [file] [log] [blame]
// Copyright 2020 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.buildtool;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.devtools.build.lib.actions.BuildFailedException;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.buildtool.util.GoogleBuildIntegrationTestCase;
import com.google.devtools.build.lib.vfs.Path;
import java.io.IOException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* This test case tests that we handle things properly when a BUILD file is
* modified.
*/
@RunWith(JUnit4.class)
public class ModifyBuildFileTest extends GoogleBuildIntegrationTestCase {
private void writeBuildFileAndSetMtime(long mtime) throws IOException {
Path buildFile = write("modify_build_file_test/BUILD",
"cc_binary(name = 'foo', srcs = ['foo.cc'])");
buildFile.setLastModifiedTime(mtime);
}
private void updateBuildFileAndSetMtime(long mtime)
throws IOException {
// put an error in the BUILD file (linkshared = 1 requires name to end with ".so").
Path buildFile = write("modify_build_file_test/BUILD",
"cc_binary(name = 'foo', linkshared = 1, srcs = ['foo.cc'])");
buildFile.setLastModifiedTime(mtime);
// other files remain unchanged
}
private void writeCcFile() throws IOException {
write("modify_build_file_test/foo.cc",
"#include <stdio.h>",
"int main() {",
" printf(\"In modify_build_file_test/foo.cc main()\\n\");",
" return 0;",
"}");
}
@Test
public void testModifyBuildFile() throws Exception {
//
// Initial build: this should work
//
writeBuildFileAndSetMtime(1000);
writeCcFile();
buildTarget("//modify_build_file_test:foo");
Path executable = getExecutableLocation("//modify_build_file_test:foo");
String firstOutput = run(executable);
assertThat(firstOutput).isEqualTo("In modify_build_file_test/foo.cc main()\n");
//
// Put a syntax error in the BUILD file and rebuild;
// this is supposed to fail.
//
updateBuildFileAndSetMtime(2000);
assertThrows(
ViewCreationFailedException.class, () -> buildTarget("//modify_build_file_test:foo"));
//
// Restore the original contents BUILD file and rebuild;
// this is supposed to work.
//
writeBuildFileAndSetMtime(3000);
buildTarget("//modify_build_file_test:foo");
Path executable2 = getExecutableLocation("//modify_build_file_test:foo");
// the location of the executable shouldn't have changed
assertThat(executable2).isEqualTo(executable);
// the output shouldn't have changed
assertThat(run(executable)).isEqualTo(firstOutput);
}
/**
* Regression test for bug #1953951: .h file left over in genfiles shadows
* static .h file even if there is no build rule for it anymore.
*/
@Test
public void testChangeGeneratedHeaderToNonGenerated() throws Exception {
// Include test.h created in genrule, should succeed.
write("x/BUILD",
"genrule(name = 'gen',",
" outs = ['test.h'],",
" cmd = 'echo //test > $@')",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc', 'test.h'])");
write("x/bin.cc",
"#include \"x/test.h\"",
"int main() {}");
buildTarget("//x:bin");
// Remove genrule, build should fail.
write("x/BUILD",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc', 'test.h'])");
assertThrows(BuildFailedException.class, () -> buildTarget("//x:bin"));
events.assertContainsError("missing input file '//x:test.h'");
// Make test.h undeclared, build should still fail.
events.collector().clear();
write("x/BUILD",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc'])");
assertThrows(BuildFailedException.class, () -> buildTarget("//x:bin"));
events.assertContainsError("undeclared inclusion(s) in rule '//x:bin':");
// Create header source file, build is still undeclared.
write("x/test.h");
assertThrows(BuildFailedException.class, () -> buildTarget("//x:bin"));
events.assertContainsError("undeclared inclusion(s) in rule '//x:bin':");
// Declare the header, watch the build succeed.
write("x/BUILD",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc', 'test.h'])");
buildTarget("//x:bin");
}
@Test
public void testChangeHeaderToGenerated() throws Exception {
// Should pick up the source header.
write("x/BUILD",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc', 'test.h'])");
write("x/test.h",
"#define VALUE 1");
write("x/bin.cc",
"#include <stdio.h>",
"#include \"x/test.h\"",
"int main() { printf(\"%d\", VALUE); }");
assertThat(buildAndRun("//x:bin")).isEqualTo("1");
// Should pick up the generated header.
write("x/BUILD",
"genrule(name = 'gen',",
" outs = ['test.h'],",
" cmd = 'echo \"#define VALUE 2\" > $@')",
"cc_binary(name = 'bin',",
" srcs = ['bin.cc', 'test.h'])");
delete("x/test.h");
assertThat(buildAndRun("//x:bin")).isEqualTo("2");
}
private void delete(String path) throws IOException {
getCommandEnvironment().getWorkspace().getRelative(path).delete();
}
private String buildAndRun(String target) throws Exception {
buildTarget(target);
return run(getExecutableLocation(target));
}
}