diff options
-rw-r--r-- | src/main/cpp/blaze_util_windows.cc | 36 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/runtime/commands/CleanCommand.java | 15 | ||||
-rw-r--r-- | src/test/py/bazel/BUILD | 7 | ||||
-rw-r--r-- | src/test/py/bazel/bazel_clean_test.py | 57 |
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() |