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.
4
5 #include <sstream>
6
7 #include "base/files/file_util.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "gn/build_settings.h"
12 #include "gn/err.h"
13 #include "gn/filesystem_utils.h"
14 #include "gn/functions.h"
15 #include "gn/input_file.h"
16 #include "gn/output_conversion.h"
17 #include "gn/parse_tree.h"
18 #include "gn/scheduler.h"
19 #include "gn/string_output_buffer.h"
20 #include "util/build_config.h"
21
22 namespace functions {
23
24 const char kWriteFile[] = "write_file";
25 const char kWriteFile_HelpShort[] = "write_file: Write a file to disk.";
26 const char kWriteFile_Help[] =
27 R"(write_file: Write a file to disk.
28
29 write_file(filename, data, output_conversion = "")
30
31 If data is a list, the list will be written one-item-per-line with no quoting
32 or brackets.
33
34 If the file exists and the contents are identical to that being written, the
35 file will not be updated. This will prevent unnecessary rebuilds of targets
36 that depend on this file.
37
38 One use for write_file is to write a list of inputs to an script that might
39 be too long for the command line. However, it is preferable to use response
40 files for this purpose. See "gn help response_file_contents".
41
42 Arguments
43
44 filename
45 Filename to write. This must be within the output directory.
46
47 data
48 The list or string to write.
49
50 output_conversion
51 Controls how the output is written. See `gn help io_conversion`.
52 )";
53
RunWriteFile(Scope * scope,const FunctionCallNode * function,const std::vector<Value> & args,Err * err)54 Value RunWriteFile(Scope* scope,
55 const FunctionCallNode* function,
56 const std::vector<Value>& args,
57 Err* err) {
58 if (args.size() != 3 && args.size() != 2) {
59 *err = Err(function->function(), "Wrong number of arguments to write_file",
60 "I expected two or three arguments.");
61 return Value();
62 }
63
64 // Compute the file name and make sure it's in the output dir.
65 const SourceDir& cur_dir = scope->GetSourceDir();
66 SourceFile source_file = cur_dir.ResolveRelativeFile(
67 args[0], err, scope->settings()->build_settings()->root_path_utf8());
68 if (err->has_error())
69 return Value();
70 if (!EnsureStringIsInOutputDir(
71 scope->settings()->build_settings()->build_dir(), source_file.value(),
72 args[0].origin(), err))
73 return Value();
74 g_scheduler->AddWrittenFile(source_file); // Track that we wrote this file.
75
76 // Track how to recreate this file, since we write it a gen time.
77 // Note this is a hack since the correct output is not a dependency proper,
78 // but an addition of this file to the output of the gn rule that writes it.
79 // This dependency will, however, cause the gen step to be re-run and the
80 // build restarted if the file is missing.
81 g_scheduler->AddGenDependency(
82 scope->settings()->build_settings()->GetFullPath(source_file));
83
84 // Extract conversion value.
85 Value output_conversion;
86 if (args.size() != 3)
87 output_conversion = Value();
88 else
89 output_conversion = args[2];
90
91 // Compute output.
92 StringOutputBuffer storage;
93 std::ostream contents(&storage);
94 ConvertValueToOutput(scope->settings(), args[1], output_conversion, contents,
95 err);
96 if (err->has_error())
97 return Value();
98
99 base::FilePath file_path =
100 scope->settings()->build_settings()->GetFullPath(source_file);
101
102 // Make sure we're not replacing the same contents.
103 if (!storage.WriteToFileIfChanged(file_path, err)) {
104 *err = Err(function->function(), err->message(), err->help_text());
105 return Value();
106 }
107
108 return Value();
109 }
110
111 } // namespace functions
112