Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(12)

Side by Side Diff: tools/gn/function_exec_script.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/function_define_rule.cc ('k') | tools/gn/function_process_file_template.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "base/command_line.h"
6 #include "base/file_util.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "build/build_config.h"
9 #include "tools/gn/err.h"
10 #include "tools/gn/filesystem_utils.h"
11 #include "tools/gn/functions.h"
12 #include "tools/gn/input_conversion.h"
13 #include "tools/gn/input_file.h"
14 #include "tools/gn/parse_tree.h"
15 #include "tools/gn/scheduler.h"
16 #include "tools/gn/value.h"
17
18 #if defined(OS_WIN)
19 #include <windows.h>
20
21 #include "base/win/scoped_handle.h"
22 #include "base/win/scoped_process_information.h"
23 #endif
24
25 /*
26 exec_script: Synchronously run a script and return the output.
27
28 exec_script(filename, arguments, input_conversion, [file_dependencies])
29
30 Runs the given script, returning the stdout of the script. The build
31 generation will fail if the script does not exist or returns a nonzero
32 exit code.
33
34 Arguments:
35
36 filename:
37 File name of python script to execute, relative to the build file.
38
39 arguments:
40 A list of strings to be passed to the scripe as arguments.
41
42 input_conversion:
43 Controls how the file is read and parsed. See "help input_conversion".
44
45 dependencies:
46 (Optional) A list of files that this script reads or otherwise depends
47 on. These dependencies will be added to the build result such that if
48 any of them change, the build will be regenerated and the script will
49 be re-run.
50
51 The script itself will be an implicit dependency so you do not need to
52 list it.
53
54 Example:
55
56 all_lines = exec_script("myscript.py", [some_input], "list lines",
57 ["data_file.txt"])
58 */
59
60 namespace {
61
62 #if defined(OS_WIN)
63 bool ExecProcess(const CommandLine& cmdline,
64 const base::FilePath& startup_dir,
65 std::string* std_out,
66 std::string* std_err,
67 int* exit_code) {
68 SECURITY_ATTRIBUTES sa_attr;
69 // Set the bInheritHandle flag so pipe handles are inherited.
70 sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES);
71 sa_attr.bInheritHandle = TRUE;
72 sa_attr.lpSecurityDescriptor = NULL;
73
74 // Create the pipe for the child process's STDOUT.
75 HANDLE out_read = NULL;
76 HANDLE out_write = NULL;
77 if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) {
78 NOTREACHED() << "Failed to create pipe";
79 return false;
80 }
81 base::win::ScopedHandle scoped_out_read(out_read);
82 base::win::ScopedHandle scoped_out_write(out_write);
83
84 // Create the pipe for the child process's STDERR.
85 HANDLE err_read = NULL;
86 HANDLE err_write = NULL;
87 if (!CreatePipe(&err_read, &err_write, &sa_attr, 0)) {
88 NOTREACHED() << "Failed to create pipe";
89 return false;
90 }
91 base::win::ScopedHandle scoped_err_read(err_read);
92 base::win::ScopedHandle scoped_err_write(err_write);
93
94 // Ensure the read handle to the pipe for STDOUT/STDERR is not inherited.
95 if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) {
96 NOTREACHED() << "Failed to disabled pipe inheritance";
97 return false;
98 }
99 if (!SetHandleInformation(err_read, HANDLE_FLAG_INHERIT, 0)) {
100 NOTREACHED() << "Failed to disabled pipe inheritance";
101 return false;
102 }
103
104 base::FilePath::StringType cmdline_str(cmdline.GetCommandLineString());
105
106 base::win::ScopedProcessInformation proc_info;
107 STARTUPINFO start_info = { 0 };
108
109 start_info.cb = sizeof(STARTUPINFO);
110 start_info.hStdOutput = out_write;
111 // Keep the normal stdin.
112 start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
113 // FIXME(brettw) set stderr here when we actually read it below.
114 //start_info.hStdError = err_write;
115 start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
116 start_info.dwFlags |= STARTF_USESTDHANDLES;
117
118 // Create the child process.
119 if (!CreateProcess(NULL,
120 &cmdline_str[0],
121 NULL, NULL,
122 TRUE, // Handles are inherited.
123 0, NULL,
124 startup_dir.value().c_str(),
125 &start_info, proc_info.Receive())) {
126 return false;
127 }
128
129 // Close our writing end of pipes now. Otherwise later read would not be able
130 // to detect end of child's output.
131 scoped_out_write.Close();
132 scoped_err_write.Close();
133
134 // Read output from the child process's pipe for STDOUT
135 const int kBufferSize = 1024;
136 char buffer[kBufferSize];
137
138 // FIXME(brettw) read from stderr here! This is complicated because we want
139 // to read both of them at the same time, probably need overlapped I/O.
140 // Also uncomment start_info code above.
141 for (;;) {
142 DWORD bytes_read = 0;
143 BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL);
144 if (!success || bytes_read == 0)
145 break;
146 std_out->append(buffer, bytes_read);
147 }
148
149 // Let's wait for the process to finish.
150 WaitForSingleObject(proc_info.process_handle(), INFINITE);
151
152 DWORD dw_exit_code;
153 GetExitCodeProcess(proc_info.process_handle(), &dw_exit_code);
154 *exit_code = static_cast<int>(dw_exit_code);
155
156 return true;
157 }
158 #else
159 bool ExecProcess(const CommandLine& cmdline,
160 const base::FilePath& startup_dir,
161 std::string* std_out,
162 std::string* std_err,
163 int* exit_code) {
164 //NOTREACHED() << "Implement me.";
165 return false;
166 }
167 #endif
168
169 } // namespace
170
171 Value ExecuteExecScript(Scope* scope,
172 const FunctionCallNode* function,
173 const std::vector<Value>& args,
174 Err* err) {
175 if (args.size() != 3 && args.size() != 4) {
176 *err = Err(function->function(), "Wrong number of args to write_file",
177 "I expected three or four arguments.");
178 return Value();
179 }
180
181 const Settings* settings = scope->settings();
182 const BuildSettings* build_settings = settings->build_settings();
183 const SourceDir& cur_dir = SourceDirForFunctionCall(function);
184
185 // Find the python script to run.
186 if (!args[0].VerifyTypeIs(Value::STRING, err))
187 return Value();
188 SourceFile script_source =
189 cur_dir.ResolveRelativeFile(args[0].string_value());
190 base::FilePath script_path = build_settings->GetFullPath(script_source);
191 if (!build_settings->secondary_source_path().empty() &&
192 !base::PathExists(script_path)) {
193 // Fall back to secondary source root when the file doesn't exist.
194 script_path = build_settings->GetFullPathSecondary(script_source);
195 }
196
197 // Add all dependencies of this script, including the script itself, to the
198 // build deps.
199 g_scheduler->AddGenDependency(script_source);
200 if (args.size() == 4) {
201 const Value& deps_value = args[3];
202 if (!deps_value.VerifyTypeIs(Value::LIST, err))
203 return Value();
204
205 for (size_t i = 0; i < deps_value.list_value().size(); i++) {
206 if (!deps_value.list_value()[0].VerifyTypeIs(Value::STRING, err))
207 return Value();
208 g_scheduler->AddGenDependency(cur_dir.ResolveRelativeFile(
209 deps_value.list_value()[0].string_value()));
210 }
211 }
212
213 // Make the command line.
214 const base::FilePath& python_path = build_settings->python_path();
215 CommandLine cmdline(python_path);
216 cmdline.AppendArgPath(script_path);
217
218 const Value& script_args = args[1];
219 if (!script_args.VerifyTypeIs(Value::LIST, err))
220 return Value();
221 for (size_t i = 0; i < script_args.list_value().size(); i++) {
222 if (!script_args.list_value()[i].VerifyTypeIs(Value::STRING, err))
223 return Value();
224 cmdline.AppendArg(script_args.list_value()[i].string_value());
225 }
226
227 // Execute the process.
228 // TODO(brettw) set the environment block.
229 std::string output;
230 std::string stderr_output; // TODO(brettw) not hooked up, see above.
231 int exit_code = 0;
232 if (!ExecProcess(cmdline, build_settings->GetFullPath(cur_dir),
233 &output, &stderr_output, &exit_code)) {
234 *err = Err(function->function(), "Could not execute python.",
235 "I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
236 return Value();
237 }
238
239 // TODO(brettw) maybe we need stderr also for reasonable stack dumps.
240 if (exit_code != 0) {
241 std::string msg =
242 std::string("I was running \"") + FilePathToUTF8(script_path) + "\"\n"
243 "and it returned " + base::IntToString(exit_code);
244 if (!output.empty())
245 msg += " and printed out:\n\n" + output;
246 else
247 msg += ".";
248 *err = Err(function->function(), "Script returned non-zero exit code.",
249 msg);
250 return Value();
251 }
252
253 return ConvertInputToValue(output, function, args[2], err);
254 }
OLDNEW
« no previous file with comments | « tools/gn/function_define_rule.cc ('k') | tools/gn/function_process_file_template.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698