Correct blacklist entry message
[chromium-blink-merge.git] / tools / gn / setup.cc
blob47a243ace6da2558ac048af0dbb4c9923aa1d92c
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.
5 #include "tools/gn/setup.h"
7 #include <stdlib.h>
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "build/build_config.h"
15 #include "tools/gn/filesystem_utils.h"
16 #include "tools/gn/input_file.h"
17 #include "tools/gn/parse_tree.h"
18 #include "tools/gn/parser.h"
19 #include "tools/gn/source_dir.h"
20 #include "tools/gn/source_file.h"
21 #include "tools/gn/standard_out.h"
22 #include "tools/gn/tokenizer.h"
23 #include "tools/gn/trace.h"
24 #include "tools/gn/value.h"
26 #if defined(OS_WIN)
27 #include <windows.h>
28 #endif
30 extern const char kDotfile_Help[] =
31 ".gn file\n"
32 "\n"
33 " When gn starts, it will search the current directory and parent\n"
34 " directories for a file called \".gn\". This indicates the source root.\n"
35 " You can override this detection by using the --root command-line\n"
36 " argument\n"
37 "\n"
38 " The .gn file in the source root will be executed. The syntax is the\n"
39 " same as a buildfile, but with very limited build setup-specific\n"
40 " meaning.\n"
41 "\n"
42 "Variables\n"
43 "\n"
44 " buildconfig [required]\n"
45 " Label of the build config file. This file will be used to setup\n"
46 " the build file execution environment for each toolchain.\n"
47 "\n"
48 " secondary_source [optional]\n"
49 " Label of an alternate directory tree to find input files. When\n"
50 " searching for a BUILD.gn file (or the build config file discussed\n"
51 " above), the file fill first be looked for in the source root.\n"
52 " If it's not found, the secondary source root will be checked\n"
53 " (which would contain a parallel directory hierarchy).\n"
54 "\n"
55 " This behavior is intended to be used when BUILD.gn files can't be\n"
56 " checked in to certain source directories for whaever reason.\n"
57 "\n"
58 " The secondary source root must be inside the main source tree.\n"
59 "\n"
60 "Example .gn file contents\n"
61 "\n"
62 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
63 "\n"
64 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
66 namespace {
68 // More logging.
69 const char kSwitchVerbose[] = "v";
71 // Set build args.
72 const char kSwitchArgs[] = "args";
74 // Set root dir.
75 const char kSwitchRoot[] = "root";
77 // Enable timing.
78 const char kTimeSwitch[] = "time";
80 const char kTracelogSwitch[] = "tracelog";
82 // Set build output directory.
83 const char kSwitchBuildOutput[] = "output";
85 const char kSecondarySource[] = "secondary";
87 const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
89 base::FilePath FindDotFile(const base::FilePath& current_dir) {
90 base::FilePath try_this_file = current_dir.Append(kGnFile);
91 if (base::PathExists(try_this_file))
92 return try_this_file;
94 base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
95 base::FilePath up_one_dir = with_no_slash.DirName();
96 if (up_one_dir == current_dir)
97 return base::FilePath(); // Got to the top.
99 return FindDotFile(up_one_dir);
102 // Searches the list of strings, and returns the FilePat corresponding to the
103 // one ending in the given substring, or the empty path if none match.
104 base::FilePath GetPathEndingIn(
105 const std::vector<base::FilePath::StringType>& list,
106 const base::FilePath::StringType ending_in) {
107 for (size_t i = 0; i < list.size(); i++) {
108 if (EndsWith(list[i], ending_in, true))
109 return base::FilePath(list[i]);
111 return base::FilePath();
114 // Fins the depot tools directory in the path environment variable and returns
115 // its value. Returns an empty file path if not found.
117 // We detect the depot_tools path by looking for a directory with depot_tools
118 // at the end (optionally followed by a separator).
119 base::FilePath ExtractDepotToolsFromPath() {
120 #if defined(OS_WIN)
121 static const wchar_t kPathVarName[] = L"Path";
122 DWORD env_buf_size = GetEnvironmentVariable(kPathVarName, NULL, 0);
123 if (env_buf_size == 0)
124 return base::FilePath();
125 base::string16 path;
126 path.resize(env_buf_size);
127 GetEnvironmentVariable(kPathVarName, &path[0],
128 static_cast<DWORD>(path.size()));
129 path.resize(path.size() - 1); // Trim off null.
131 std::vector<base::string16> components;
132 base::SplitString(path, ';', &components);
134 base::string16 ending_in1 = L"depot_tools\\";
135 #else
136 static const char kPathVarName[] = "PATH";
137 const char* path = getenv(kPathVarName);
138 if (!path)
139 return base::FilePath();
141 std::vector<std::string> components;
142 base::SplitString(path, ':', &components);
144 std::string ending_in1 = "depot_tools/";
145 #endif
146 base::FilePath::StringType ending_in2 = FILE_PATH_LITERAL("depot_tools");
148 base::FilePath found = GetPathEndingIn(components, ending_in1);
149 if (!found.empty())
150 return found;
151 return GetPathEndingIn(components, ending_in2);
154 } // namespace
156 // CommonSetup -----------------------------------------------------------------
158 CommonSetup::CommonSetup()
159 : check_for_bad_items_(true) {
162 CommonSetup::CommonSetup(const CommonSetup& other)
163 : build_settings_(other.build_settings_),
164 check_for_bad_items_(other.check_for_bad_items_) {
167 CommonSetup::~CommonSetup() {
170 void CommonSetup::RunPreMessageLoop() {
171 // Load the root build file.
172 build_settings_.toolchain_manager().StartLoadingUnlocked(
173 SourceFile("//BUILD.gn"));
176 bool CommonSetup::RunPostMessageLoop() {
177 Err err;
178 if (check_for_bad_items_) {
179 err = build_settings_.item_tree().CheckForBadItems();
180 if (err.has_error()) {
181 err.PrintToStdout();
182 return false;
186 if (!build_settings_.build_args().VerifyAllOverridesUsed(&err)) {
187 err.PrintToStdout();
188 return false;
191 // Write out tracing and timing if requested.
192 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
193 if (cmdline->HasSwitch(kTimeSwitch))
194 PrintLongHelp(SummarizeTraces());
195 if (cmdline->HasSwitch(kTracelogSwitch))
196 SaveTraces(cmdline->GetSwitchValuePath(kTracelogSwitch));
198 return true;
201 // Setup -----------------------------------------------------------------------
203 Setup::Setup()
204 : CommonSetup(),
205 empty_settings_(&empty_build_settings_, std::string()),
206 dotfile_scope_(&empty_settings_) {
207 empty_settings_.set_toolchain_label(Label());
210 Setup::~Setup() {
213 bool Setup::DoSetup() {
214 CommandLine* cmdline = CommandLine::ForCurrentProcess();
216 scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
217 if (cmdline->HasSwitch(kTimeSwitch) ||
218 cmdline->HasSwitch(kTracelogSwitch))
219 EnableTracing();
221 if (!FillArguments(*cmdline))
222 return false;
223 if (!FillSourceDir(*cmdline))
224 return false;
225 if (!RunConfigFile())
226 return false;
227 if (!FillOtherConfig(*cmdline))
228 return false;
229 FillPythonPath();
231 base::FilePath build_path = cmdline->GetSwitchValuePath(kSwitchBuildOutput);
232 if (!build_path.empty()) {
233 // We accept either repo paths "//out/Debug" or raw source-root-relative
234 // paths "out/Debug".
235 std::string build_path_8 = FilePathToUTF8(build_path);
236 if (build_path_8.compare(0, 2, "//") != 0)
237 build_path_8.insert(0, "//");
238 build_settings_.SetBuildDir(SourceDir(build_path_8));
239 } else {
240 // Default output dir.
241 build_settings_.SetBuildDir(SourceDir("//out/Default/"));
244 return true;
247 bool Setup::Run() {
248 RunPreMessageLoop();
249 if (!scheduler_.Run())
250 return false;
251 return RunPostMessageLoop();
254 bool Setup::FillArguments(const CommandLine& cmdline) {
255 std::string args = cmdline.GetSwitchValueASCII(kSwitchArgs);
256 if (args.empty())
257 return true; // Nothing to set.
259 args_input_file_.reset(new InputFile(SourceFile()));
260 args_input_file_->SetContents(args);
261 args_input_file_->set_friendly_name("the command-line \"--args\" settings");
263 Err err;
264 args_tokens_ = Tokenizer::Tokenize(args_input_file_.get(), &err);
265 if (err.has_error()) {
266 err.PrintToStdout();
267 return false;
270 args_root_ = Parser::Parse(args_tokens_, &err);
271 if (err.has_error()) {
272 err.PrintToStdout();
273 return false;
276 Scope arg_scope(&empty_settings_);
277 args_root_->AsBlock()->ExecuteBlockInScope(&arg_scope, &err);
278 if (err.has_error()) {
279 err.PrintToStdout();
280 return false;
283 // Save the result of the command args.
284 Scope::KeyValueMap overrides;
285 arg_scope.GetCurrentScopeValues(&overrides);
286 build_settings_.build_args().AddArgOverrides(overrides);
287 return true;
290 bool Setup::FillSourceDir(const CommandLine& cmdline) {
291 // Find the .gn file.
292 base::FilePath root_path;
294 // Prefer the command line args to the config file.
295 base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
296 if (!relative_root_path.empty()) {
297 root_path = base::MakeAbsoluteFilePath(relative_root_path);
298 dotfile_name_ = root_path.Append(kGnFile);
299 } else {
300 base::FilePath cur_dir;
301 file_util::GetCurrentDirectory(&cur_dir);
302 dotfile_name_ = FindDotFile(cur_dir);
303 if (dotfile_name_.empty()) {
304 Err(Location(), "Can't find source root.",
305 "I could not find a \".gn\" file in the current directory or any "
306 "parent,\nand the --root command-line argument was not specified.")
307 .PrintToStdout();
308 return false;
310 root_path = dotfile_name_.DirName();
313 if (scheduler_.verbose_logging())
314 scheduler_.Log("Using source root", FilePathToUTF8(root_path));
315 build_settings_.SetRootPath(root_path);
317 return true;
320 void Setup::FillPythonPath() {
321 #if defined(OS_WIN)
322 // We use python from the depot tools which should be on the path. If we
323 // converted the python_path to a python_command_line then we could
324 // potentially use "cmd.exe /c python.exe" and remove this.
325 static const wchar_t kPythonName[] = L"python.exe";
326 base::FilePath depot_tools = ExtractDepotToolsFromPath();
327 if (!depot_tools.empty()) {
328 base::FilePath python =
329 depot_tools.Append(L"python_bin").Append(kPythonName);
330 if (scheduler_.verbose_logging())
331 scheduler_.Log("Using python", FilePathToUTF8(python));
332 build_settings_.set_python_path(python);
333 return;
336 if (scheduler_.verbose_logging()) {
337 scheduler_.Log("WARNING", "Could not find depot_tools on path, using "
338 "just " + FilePathToUTF8(kPythonName));
340 #else
341 static const char kPythonName[] = "python";
342 #endif
344 build_settings_.set_python_path(base::FilePath(kPythonName));
347 bool Setup::RunConfigFile() {
348 if (scheduler_.verbose_logging())
349 scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
351 dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
352 if (!dotfile_input_file_->Load(dotfile_name_)) {
353 Err(Location(), "Could not load dotfile.",
354 "The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
355 .PrintToStdout();
356 return false;
359 Err err;
360 dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
361 if (err.has_error()) {
362 err.PrintToStdout();
363 return false;
366 dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
367 if (err.has_error()) {
368 err.PrintToStdout();
369 return false;
372 dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
373 if (err.has_error()) {
374 err.PrintToStdout();
375 return false;
378 return true;
381 bool Setup::FillOtherConfig(const CommandLine& cmdline) {
382 Err err;
384 // Secondary source path.
385 SourceDir secondary_source;
386 if (cmdline.HasSwitch(kSecondarySource)) {
387 // Prefer the command line over the config file.
388 secondary_source =
389 SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
390 } else {
391 // Read from the config file if present.
392 const Value* secondary_value =
393 dotfile_scope_.GetValue("secondary_source", true);
394 if (secondary_value) {
395 if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
396 err.PrintToStdout();
397 return false;
399 build_settings_.SetSecondarySourcePath(
400 SourceDir(secondary_value->string_value()));
404 // Build config file.
405 const Value* build_config_value =
406 dotfile_scope_.GetValue("buildconfig", true);
407 if (!build_config_value) {
408 Err(Location(), "No build config file.",
409 "Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
410 "didn't specify a \"buildconfig\" value.").PrintToStdout();
411 return false;
412 } else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
413 err.PrintToStdout();
414 return false;
416 build_settings_.set_build_config_file(
417 SourceFile(build_config_value->string_value()));
419 return true;
422 // DependentSetup --------------------------------------------------------------
424 DependentSetup::DependentSetup(const Setup& main_setup)
425 : CommonSetup(main_setup) {
428 DependentSetup::~DependentSetup() {
431 void DependentSetup::RunPreMessageLoop() {
432 CommonSetup::RunPreMessageLoop();
435 bool DependentSetup::RunPostMessageLoop() {
436 return CommonSetup::RunPostMessageLoop();