1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 #include "base/files/file_util.h"
9 #include "base/strings/string_split.h"
10 #include "base/strings/string_util.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "tools/gn/err.h"
13 #include "tools/gn/filesystem_utils.h"
14 #include "tools/gn/functions.h"
15 #include "tools/gn/input_file.h"
16 #include "tools/gn/parse_tree.h"
17 #include "tools/gn/scheduler.h"
23 // On Windows, provide a custom implementation of base::WriteFile. Sometimes
24 // the base version would fail, and this alternate implementation provides
25 // additional logging. See http://crbug.com/468437
27 int DoWriteFile(const base::FilePath
& filename
, const char* data
, int size
) {
28 base::win::ScopedHandle
file(::CreateFile(
29 filename
.value().c_str(),
31 FILE_SHARE_READ
, // Not present in the base version, speculative fix.
36 if (!file
.IsValid()) {
37 PLOG(ERROR
) << "CreateFile failed for path "
38 << base::UTF16ToUTF8(filename
.value());
43 BOOL result
= ::WriteFile(file
.Get(), data
, size
, &written
, NULL
);
44 if (result
&& static_cast<int>(written
) == size
)
49 PLOG(ERROR
) << "writing file " << base::UTF16ToUTF8(filename
.value())
52 // Didn't write all the bytes.
53 LOG(ERROR
) << "wrote" << written
<< " bytes to "
54 << base::UTF16ToUTF8(filename
.value()) << " expected " << size
;
59 int DoWriteFile(const base::FilePath
& filename
, const char* data
, int size
) {
60 return base::WriteFile(filename
, data
, size
);
66 const char kWriteFile
[] = "write_file";
67 const char kWriteFile_HelpShort
[] =
68 "write_file: Write a file to disk.";
69 const char kWriteFile_Help
[] =
70 "write_file: Write a file to disk.\n"
72 " write_file(filename, data)\n"
74 " If data is a list, the list will be written one-item-per-line with no\n"
75 " quoting or brackets.\n"
77 " If the file exists and the contents are identical to that being\n"
78 " written, the file will not be updated. This will prevent unnecessary\n"
79 " rebuilds of targets that depend on this file.\n"
81 " TODO(brettw) we probably need an optional third argument to control\n"
87 " Filename to write. This must be within the output directory.\n"
90 " The list or string to write.\n";
92 Value
RunWriteFile(Scope
* scope
,
93 const FunctionCallNode
* function
,
94 const std::vector
<Value
>& args
,
96 if (args
.size() != 2) {
97 *err
= Err(function
->function(), "Wrong number of arguments to write_file",
98 "I expected two arguments.");
102 // Compute the file name and make sure it's in the output dir.
103 if (!args
[0].VerifyTypeIs(Value::STRING
, err
))
105 const SourceDir
& cur_dir
= scope
->GetSourceDir();
106 SourceFile source_file
= cur_dir
.ResolveRelativeFile(args
[0].string_value(),
107 scope
->settings()->build_settings()->root_path_utf8());
108 if (!EnsureStringIsInOutputDir(
109 scope
->settings()->build_settings()->build_dir(),
110 source_file
.value(), args
[0].origin(), err
))
114 std::ostringstream contents
;
115 if (args
[1].type() == Value::LIST
) {
116 const std::vector
<Value
>& list
= args
[1].list_value();
117 for (const auto& cur
: list
)
118 contents
<< cur
.ToString(false) << std::endl
;
120 contents
<< args
[1].ToString(false);
122 const std::string
& new_contents
= contents
.str();
123 base::FilePath file_path
=
124 scope
->settings()->build_settings()->GetFullPath(source_file
);
126 // Make sure we're not replacing the same contents.
127 std::string existing_contents
;
128 if (base::ReadFileToString(file_path
, &existing_contents
) &&
129 existing_contents
== new_contents
)
130 return Value(); // Nothing to do.
132 // Write file, creating the directory if necessary.
133 if (!base::CreateDirectory(file_path
.DirName())) {
134 *err
= Err(function
->function(), "Unable to create directory.",
135 "I was using \"" + FilePathToUTF8(file_path
.DirName()) + "\".");
139 int int_size
= static_cast<int>(new_contents
.size());
140 if (DoWriteFile(file_path
, new_contents
.c_str(), int_size
)
142 *err
= Err(function
->function(), "Unable to write file.",
143 "I was writing \"" + FilePathToUTF8(file_path
) + "\".");
149 } // namespace functions