diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 49af001..8d4cc7f 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -265,6 +265,23 @@ FileReader::Status RealDiskInterface::ReadFile(const string& path, } int RealDiskInterface::RemoveFile(const string& path) { +#ifdef _WIN32 + DWORD attributes = GetFileAttributes(path.c_str()); + if (attributes == INVALID_FILE_ATTRIBUTES && + GetLastError() == ERROR_FILE_NOT_FOUND) { + return 1; + } + if (attributes & FILE_ATTRIBUTE_READONLY) { + // On non-Windows systems remove will happily delete read-only files. On + // Windows Ninja should behave the same. See + // https://github.com/ninja-build/ninja/issues/1886 + SetFileAttributes(path.c_str(), attributes & ~FILE_ATTRIBUTE_READONLY); + } + if (!DeleteFile(path.c_str())) { + Error("remove(%s): %s", path.c_str(), GetLastErrorString().c_str()); + return -1; + } +#else if (remove(path.c_str()) < 0) { switch (errno) { case ENOENT: @@ -273,9 +290,9 @@ int RealDiskInterface::RemoveFile(const string& path) { Error("remove(%s): %s", path.c_str(), strerror(errno)); return -1; } - } else { - return 0; } +#endif + return 0; } void RealDiskInterface::AllowStatCache(bool allow) { diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 066c770..b424243 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -211,6 +211,12 @@ TEST_F(DiskInterfaceTest, RemoveFile) { EXPECT_EQ(0, disk_.RemoveFile(kFileName)); EXPECT_EQ(1, disk_.RemoveFile(kFileName)); EXPECT_EQ(1, disk_.RemoveFile("does not exist")); +#ifdef _WIN32 + ASSERT_TRUE(Touch(kFileName)); + EXPECT_EQ(0, system((std::string("attrib +R ") + kFileName).c_str())); + EXPECT_EQ(0, disk_.RemoveFile(kFileName)); + EXPECT_EQ(1, disk_.RemoveFile(kFileName)); +#endif } struct StatTest : public StateTestWithBuiltinRules,