aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/cpp/blaze_util_windows.cc36
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java15
-rw-r--r--src/test/py/bazel/BUILD7
-rw-r--r--src/test/py/bazel/bazel_clean_test.py57
4 files changed, 103 insertions, 12 deletions
diff --git a/src/main/cpp/blaze_util_windows.cc b/src/main/cpp/blaze_util_windows.cc
index fb4f3ecb85..c07fc13ef9 100644
--- a/src/main/cpp/blaze_util_windows.cc
+++ b/src/main/cpp/blaze_util_windows.cc
@@ -628,12 +628,7 @@ static HANDLE CreateJvmOutputFile(const wstring& path,
HANDLE handle = ::CreateFileW(
/* lpFileName */ path.c_str(),
/* dwDesiredAccess */ GENERIC_READ | GENERIC_WRITE,
- // Share for reading and also for deletion, so `bazel clean
- // --expunge/--expunge_async` can delete this file while the JVM holds
- // an open file descriptor to it (via stdout). Although subsequent
- // writes would not recreate the file after it's deleted, this is fine
- // because --expunge/--expunge_async shut down the Bazel server.
- /* dwShareMode */ FILE_SHARE_READ | FILE_SHARE_DELETE,
+ /* dwShareMode */ FILE_SHARE_READ,
/* lpSecurityAttributes */ sa,
/* dwCreationDisposition */ CREATE_ALWAYS,
/* dwFlagsAndAttributes */ FILE_ATTRIBUTE_NORMAL,
@@ -711,8 +706,8 @@ void ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
// We redirect stdin to the NUL device, and redirect stdout and stderr to
- // `output_file` (opened below) by telling CreateProcess to use these file
- // handles, so they must be inheritable.
+ // `stdout_file` and `stderr_file` (opened below) by telling CreateProcess to
+ // use these file handles, so they must be inheritable.
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
@@ -724,19 +719,36 @@ void ExecuteDaemon(const string& exe, const std::vector<string>& args_vector,
"ExecuteDaemon: Could not open NUL device");
}
- windows_util::AutoHandle output_file(
+ windows_util::AutoHandle stdout_file(
CreateJvmOutputFile(wdaemon_output.c_str(), &sa));
- if (!output_file.IsValid()) {
+ if (!stdout_file.IsValid()) {
pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
"ExecuteDaemon: CreateJvmOutputFile %ls", wdaemon_output.c_str());
}
+ HANDLE stderr_handle;
+ // We must duplicate the handle to stdout, otherwise "bazel clean --expunge"
+ // won't work, because when it tries to close stdout then stderr, the former
+ // will succeed but the latter will appear to be valid yet still fail to
+ // close.
+ if (!DuplicateHandle(
+ /* hSourceProcessHandle */ GetCurrentProcess(),
+ /* hSourceHandle */ stdout_file,
+ /* hTargetProcessHandle */ GetCurrentProcess(),
+ /* lpTargetHandle */ &stderr_handle,
+ /* dwDesiredAccess */ 0,
+ /* bInheritHandle */ TRUE,
+ /* dwOptions */ DUPLICATE_SAME_ACCESS)) {
+ pdie(blaze_exit_code::LOCAL_ENVIRONMENTAL_ERROR,
+ "ExecuteDaemon: DuplicateHandle %ls", wdaemon_output.c_str());
+ }
+ windows_util::AutoHandle stderr_file(stderr_handle);
PROCESS_INFORMATION processInfo = {0};
STARTUPINFOA startupInfo = {0};
startupInfo.hStdInput = devnull;
- startupInfo.hStdError = output_file;
- startupInfo.hStdOutput = output_file;
+ startupInfo.hStdError = stdout_file;
+ startupInfo.hStdOutput = stderr_handle;
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
CmdLine cmdline;
CreateCommandLine(&cmdline, exe, args_vector);
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java
index 93952a1825..352c19635e 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java
@@ -34,7 +34,10 @@ import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsProvider;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.logging.LogManager;
import java.util.logging.Logger;
/** Implements 'blaze clean'. */
@@ -227,6 +230,18 @@ public final class CleanCommand implements BlazeCommand {
if (expunge) {
LOG.info("Expunging...");
env.getRuntime().prepareForAbruptShutdown();
+ // Close java.log.
+ LogManager.getLogManager().reset();
+ // Close the default stdout/stderr.
+ if (FileDescriptor.out.valid()) {
+ new FileOutputStream(FileDescriptor.out).close();
+ }
+ if (FileDescriptor.err.valid()) {
+ new FileOutputStream(FileDescriptor.err).close();
+ }
+ // Close the redirected stdout/stderr.
+ System.out.close();
+ System.err.close();
// Delete the big subdirectories with the important content first--this
// will take the most time. Then quickly delete the little locks, logs
// and links right before we exit. Once the lock file is gone there will
diff --git a/src/test/py/bazel/BUILD b/src/test/py/bazel/BUILD
index edda44d8a8..428e97d629 100644
--- a/src/test/py/bazel/BUILD
+++ b/src/test/py/bazel/BUILD
@@ -25,3 +25,10 @@ py_test(
srcs = ["bazel_server_mode_test.py"],
deps = [":test_base"],
)
+
+py_test(
+ name = "bazel_clean_test",
+ size = "medium",
+ srcs = ["bazel_clean_test.py"],
+ deps = [":test_base"],
+)
diff --git a/src/test/py/bazel/bazel_clean_test.py b/src/test/py/bazel/bazel_clean_test.py
new file mode 100644
index 0000000000..51f085f10f
--- /dev/null
+++ b/src/test/py/bazel/bazel_clean_test.py
@@ -0,0 +1,57 @@
+# Copyright 2017 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.
+
+import os
+import unittest
+from src.test.py.bazel import test_base
+
+
+class BazelCleanTest(test_base.TestBase):
+
+ def testBazelClean(self):
+ self.ScratchFile('WORKSPACE')
+ self.ScratchFile('foo/BUILD', [
+ 'genrule(', ' name = "x",', ' outs = ["x.out"],',
+ ' cmd = "touch $@",', ')'
+ ])
+
+ exit_code, stdout, _ = self.RunBazel(['info', 'bazel-genfiles'])
+ self.assertEqual(exit_code, 0)
+ bazel_genfiles = stdout[0]
+
+ exit_code, stdout, _ = self.RunBazel(['info', 'output_base'])
+ self.assertEqual(exit_code, 0)
+ output_base = stdout[0]
+
+ exit_code, _, _ = self.RunBazel(['build', '//foo:x'])
+ self.assertEqual(exit_code, 0)
+ self.assertTrue(os.path.exists(os.path.join(bazel_genfiles, 'foo/x.out')))
+
+ exit_code, _, _ = self.RunBazel(['clean'])
+ self.assertEqual(exit_code, 0)
+ self.assertFalse(os.path.exists(os.path.join(bazel_genfiles, 'foo/x.out')))
+ self.assertTrue(os.path.exists(output_base))
+
+ exit_code, _, _ = self.RunBazel(['build', '//foo:x'])
+ self.assertEqual(exit_code, 0)
+ self.assertTrue(os.path.exists(os.path.join(bazel_genfiles, 'foo/x.out')))
+
+ exit_code, _, _ = self.RunBazel(['clean', '--expunge'])
+ self.assertEqual(exit_code, 0)
+ self.assertFalse(os.path.exists(os.path.join(bazel_genfiles, 'foo/x.out')))
+ self.assertFalse(os.path.exists(output_base))
+
+
+if __name__ == '__main__':
+ unittest.main()