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"
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"
30 extern const char kDotfile_Help
[] =
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"
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"
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"
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"
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"
58 " The secondary source root must be inside the main source tree.\n"
60 "Example .gn file contents\n"
62 " buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
64 " secondary_source = \"//build/config/temporary_buildfiles/\"\n";
69 const char kSwitchVerbose
[] = "v";
72 const char kSwitchArgs
[] = "args";
75 const char kSwitchRoot
[] = "root";
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
))
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() {
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();
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\\";
136 static const char kPathVarName
[] = "PATH";
137 const char* path
= getenv(kPathVarName
);
139 return base::FilePath();
141 std::vector
<std::string
> components
;
142 base::SplitString(path
, ':', &components
);
144 std::string ending_in1
= "depot_tools/";
146 base::FilePath::StringType ending_in2
= FILE_PATH_LITERAL("depot_tools");
148 base::FilePath found
= GetPathEndingIn(components
, ending_in1
);
151 return GetPathEndingIn(components
, ending_in2
);
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() {
178 if (check_for_bad_items_
) {
179 err
= build_settings_
.item_tree().CheckForBadItems();
180 if (err
.has_error()) {
186 if (!build_settings_
.build_args().VerifyAllOverridesUsed(&err
)) {
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
));
201 // Setup -----------------------------------------------------------------------
205 empty_settings_(&empty_build_settings_
, std::string()),
206 dotfile_scope_(&empty_settings_
) {
207 empty_settings_
.set_toolchain_label(Label());
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
))
221 if (!FillArguments(*cmdline
))
223 if (!FillSourceDir(*cmdline
))
225 if (!RunConfigFile())
227 if (!FillOtherConfig(*cmdline
))
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
));
240 // Default output dir.
241 build_settings_
.SetBuildDir(SourceDir("//out/Default/"));
249 if (!scheduler_
.Run())
251 return RunPostMessageLoop();
254 bool Setup::FillArguments(const CommandLine
& cmdline
) {
255 std::string args
= cmdline
.GetSwitchValueASCII(kSwitchArgs
);
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");
264 args_tokens_
= Tokenizer::Tokenize(args_input_file_
.get(), &err
);
265 if (err
.has_error()) {
270 args_root_
= Parser::Parse(args_tokens_
, &err
);
271 if (err
.has_error()) {
276 Scope
arg_scope(&empty_settings_
);
277 args_root_
->AsBlock()->ExecuteBlockInScope(&arg_scope
, &err
);
278 if (err
.has_error()) {
283 // Save the result of the command args.
284 Scope::KeyValueMap overrides
;
285 arg_scope
.GetCurrentScopeValues(&overrides
);
286 build_settings_
.build_args().AddArgOverrides(overrides
);
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
);
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.")
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
);
320 void Setup::FillPythonPath() {
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
);
336 if (scheduler_
.verbose_logging()) {
337 scheduler_
.Log("WARNING", "Could not find depot_tools on path, using "
338 "just " + FilePathToUTF8(kPythonName
));
341 static const char kPythonName
[] = "python";
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")
360 dotfile_tokens_
= Tokenizer::Tokenize(dotfile_input_file_
.get(), &err
);
361 if (err
.has_error()) {
366 dotfile_root_
= Parser::Parse(dotfile_tokens_
, &err
);
367 if (err
.has_error()) {
372 dotfile_root_
->AsBlock()->ExecuteBlockInScope(&dotfile_scope_
, &err
);
373 if (err
.has_error()) {
381 bool Setup::FillOtherConfig(const CommandLine
& cmdline
) {
384 // Secondary source path.
385 SourceDir secondary_source
;
386 if (cmdline
.HasSwitch(kSecondarySource
)) {
387 // Prefer the command line over the config file.
389 SourceDir(cmdline
.GetSwitchValueASCII(kSecondarySource
));
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
)) {
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();
412 } else if (!build_config_value
->VerifyTypeIs(Value::STRING
, &err
)) {
416 build_settings_
.set_build_config_file(
417 SourceFile(build_config_value
->string_value()));
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();