Index: tools/gn/filesystem_utils.cc |
diff --git a/tools/gn/filesystem_utils.cc b/tools/gn/filesystem_utils.cc |
index 5905cdb9f1ec73749a03e15428d5109542712b22..2ceca81677dbc68d0e041271ea9702ec1c3302c0 100644 |
--- a/tools/gn/filesystem_utils.cc |
+++ b/tools/gn/filesystem_utils.cc |
@@ -687,6 +687,86 @@ std::string GetOutputSubdirName(const Label& toolchain_label, bool is_default) { |
return toolchain_label.name() + "/"; |
} |
+bool ContentsEqual(const base::FilePath& file_path, const std::string& data) { |
+ // Compare file and stream sizes first. Quick and will save us some time if |
+ // they are different sizes. |
+ int64_t file_size; |
+ if (!base::GetFileSize(file_path, &file_size) || |
+ static_cast<size_t>(file_size) != data.size()) { |
+ return false; |
+ } |
+ |
+ std::string file_data; |
+ file_data.resize(file_size); |
+ if (!base::ReadFileToString(file_path, &file_data)) |
+ return false; |
+ |
+ return file_data == data; |
+} |
+ |
+bool WriteFileIfChanged(const base::FilePath& file_path, |
+ const std::string& data, |
+ Err* err) { |
+ if (ContentsEqual(file_path, data)) |
+ return true; |
+ |
+ // Create the directory if necessary. |
+ if (!base::CreateDirectory(file_path.DirName())) { |
+ if (err) { |
+ *err = |
+ Err(Location(), "Unable to create directory.", |
+ "I was using \"" + FilePathToUTF8(file_path.DirName()) + "\"."); |
+ } |
+ return false; |
+ } |
+ |
+ int size = static_cast<int>(data.size()); |
+ bool write_success = false; |
+ |
+#if defined(OS_WIN) |
+ // On Windows, provide a custom implementation of base::WriteFile. Sometimes |
+ // the base version fails, especially on the bots. The guess is that Windows |
+ // Defender or other antivirus programs still have the file open (after |
+ // checking for the read) when the write happens immediately after. This |
+ // version opens with FILE_SHARE_READ (normally not what you want when |
+ // replacing the entire contents of the file) which lets us continue even if |
+ // another program has the file open for reading. See http://crbug.com/468437 |
+ base::win::ScopedHandle file(::CreateFile(file_path.value().c_str(), |
+ GENERIC_WRITE, FILE_SHARE_READ, |
+ NULL, CREATE_ALWAYS, 0, NULL)); |
+ if (file.IsValid()) { |
+ DWORD written; |
+ BOOL result = ::WriteFile(file.Get(), data.c_str(), size, &written, NULL); |
+ if (result) { |
+ if (static_cast<int>(written) == size) { |
+ write_success = true; |
+ } else { |
+ // Didn't write all the bytes. |
+ LOG(ERROR) << "wrote" << written << " bytes to " |
+ << base::UTF16ToUTF8(file_path.value()) << " expected " |
+ << size; |
+ } |
+ } else { |
+ // WriteFile failed. |
+ PLOG(ERROR) << "writing file " << base::UTF16ToUTF8(file_path.value()) |
+ << " failed"; |
+ } |
+ } else { |
+ PLOG(ERROR) << "CreateFile failed for path " |
+ << base::UTF16ToUTF8(file_path.value()); |
+ } |
+#else |
+ write_success = base::WriteFile(file_path, data.c_str(), size) == size; |
+#endif |
+ |
+ if (!write_success && err) { |
+ *err = Err(Location(), "Unable to write file.", |
+ "I was writing \"" + FilePathToUTF8(file_path) + "\"."); |
+ } |
+ |
+ return write_success; |
+} |
+ |
SourceDir GetToolchainOutputDir(const Settings* settings) { |
return settings->toolchain_output_subdir().AsSourceDir( |
settings->build_settings()); |