233 lines
7.0 KiB
C++
233 lines
7.0 KiB
C++
|
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
// found in the LICENSE file.
|
||
|
|
||
|
#include "tools/gn/setup.h"
|
||
|
|
||
|
#include "base/command_line.h"
|
||
|
#include "base/file_util.h"
|
||
|
#include "base/files/file_path.h"
|
||
|
#include "tools/gn/filesystem_utils.h"
|
||
|
#include "tools/gn/input_file.h"
|
||
|
#include "tools/gn/parse_tree.h"
|
||
|
#include "tools/gn/parser.h"
|
||
|
#include "tools/gn/source_dir.h"
|
||
|
#include "tools/gn/source_file.h"
|
||
|
#include "tools/gn/tokenizer.h"
|
||
|
#include "tools/gn/value.h"
|
||
|
|
||
|
extern const char kDotfile_Help[] =
|
||
|
".gn file\n"
|
||
|
"\n"
|
||
|
" When gn starts, it will search the current directory and parent\n"
|
||
|
" directories for a file called \".gn\". This indicates the source root.\n"
|
||
|
" You can override this detection by using the --root command-line\n"
|
||
|
" argument\n"
|
||
|
"\n"
|
||
|
" The .gn file in the source root will be executed. The syntax is the\n"
|
||
|
" same as a buildfile, but with very limited build setup-specific\n"
|
||
|
" meaning.\n"
|
||
|
"\n"
|
||
|
"Variables:\n"
|
||
|
" buildconfig [required]\n"
|
||
|
" Label of the build config file. This file will be used to setup\n"
|
||
|
" the build file execution environment for each toolchain.\n"
|
||
|
"\n"
|
||
|
" secondary_source [optional]\n"
|
||
|
" Label of an alternate directory tree to find input files. When\n"
|
||
|
" searching for a BUILD.gn file (or the build config file discussed\n"
|
||
|
" above), the file fill first be looked for in the source root.\n"
|
||
|
" If it's not found, the secondary source root will be checked\n"
|
||
|
" (which would contain a parallel directory hierarchy).\n"
|
||
|
"\n"
|
||
|
" This behavior is intended to be used when BUILD.gn files can't be\n"
|
||
|
" checked in to certain source directories for whaever reason.\n"
|
||
|
"\n"
|
||
|
" The secondary source root must be inside the main source tree.\n"
|
||
|
"\n"
|
||
|
"Example .gn file contents:\n"
|
||
|
"\n"
|
||
|
" buildconfig = \"//build/config/BUILDCONFIG.gn\"\n"
|
||
|
"\n"
|
||
|
" secondary_source = \"//build/config/temporary_buildfiles/\"\n";
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
// More logging.
|
||
|
const char kSwitchVerbose[] = "v";
|
||
|
|
||
|
const char kSwitchRoot[] = "root";
|
||
|
const char kSecondarySource[] = "secondary";
|
||
|
|
||
|
const base::FilePath::CharType kGnFile[] = FILE_PATH_LITERAL(".gn");
|
||
|
|
||
|
base::FilePath FindDotFile(const base::FilePath& current_dir) {
|
||
|
base::FilePath try_this_file = current_dir.Append(kGnFile);
|
||
|
if (base::PathExists(try_this_file))
|
||
|
return try_this_file;
|
||
|
|
||
|
base::FilePath with_no_slash = current_dir.StripTrailingSeparators();
|
||
|
base::FilePath up_one_dir = with_no_slash.DirName();
|
||
|
if (up_one_dir == current_dir)
|
||
|
return base::FilePath(); // Got to the top.
|
||
|
|
||
|
return FindDotFile(up_one_dir);
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
Setup::Setup()
|
||
|
: dotfile_toolchain_(Label()),
|
||
|
dotfile_settings_(&dotfile_build_settings_, &dotfile_toolchain_,
|
||
|
std::string()),
|
||
|
dotfile_scope_(&dotfile_settings_) {
|
||
|
}
|
||
|
|
||
|
Setup::~Setup() {
|
||
|
}
|
||
|
|
||
|
bool Setup::DoSetup() {
|
||
|
CommandLine* cmdline = CommandLine::ForCurrentProcess();
|
||
|
|
||
|
scheduler_.set_verbose_logging(cmdline->HasSwitch(kSwitchVerbose));
|
||
|
|
||
|
if (!FillSourceDir(*cmdline))
|
||
|
return false;
|
||
|
if (!RunConfigFile())
|
||
|
return false;
|
||
|
if (!FillOtherConfig(*cmdline))
|
||
|
return false;
|
||
|
|
||
|
// FIXME(brettw) get python path!
|
||
|
#if defined(OS_WIN)
|
||
|
build_settings_.set_python_path(
|
||
|
base::FilePath(FILE_PATH_LITERAL("cmd.exe /c python")));
|
||
|
#else
|
||
|
build_settings_.set_python_path(base::FilePath(FILE_PATH_LITERAL("python")));
|
||
|
#endif
|
||
|
|
||
|
build_settings_.SetBuildDir(SourceDir("//out/gn/"));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Setup::Run() {
|
||
|
// Load the root build file and start runnung.
|
||
|
build_settings_.toolchain_manager().StartLoadingUnlocked(
|
||
|
SourceFile("//BUILD.gn"));
|
||
|
if (!scheduler_.Run())
|
||
|
return false;
|
||
|
|
||
|
Err err = build_settings_.item_tree().CheckForBadItems();
|
||
|
if (err.has_error()) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Setup::FillSourceDir(const CommandLine& cmdline) {
|
||
|
// Find the .gn file.
|
||
|
base::FilePath root_path;
|
||
|
|
||
|
// Prefer the command line args to the config file.
|
||
|
base::FilePath relative_root_path = cmdline.GetSwitchValuePath(kSwitchRoot);
|
||
|
if (!relative_root_path.empty()) {
|
||
|
root_path = base::MakeAbsoluteFilePath(relative_root_path);
|
||
|
dotfile_name_ = root_path.Append(kGnFile);
|
||
|
} else {
|
||
|
base::FilePath cur_dir;
|
||
|
file_util::GetCurrentDirectory(&cur_dir);
|
||
|
dotfile_name_ = FindDotFile(cur_dir);
|
||
|
if (dotfile_name_.empty()) {
|
||
|
Err(Location(), "Can't find source root.",
|
||
|
"I could not find a \".gn\" file in the current directory or any "
|
||
|
"parent,\nand the --root command-line argument was not specified.")
|
||
|
.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
root_path = dotfile_name_.DirName();
|
||
|
}
|
||
|
|
||
|
if (scheduler_.verbose_logging())
|
||
|
scheduler_.Log("Using source root", FilePathToUTF8(root_path));
|
||
|
build_settings_.set_root_path(root_path);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Setup::RunConfigFile() {
|
||
|
if (scheduler_.verbose_logging())
|
||
|
scheduler_.Log("Got dotfile", FilePathToUTF8(dotfile_name_));
|
||
|
|
||
|
dotfile_input_file_.reset(new InputFile(SourceFile("//.gn")));
|
||
|
if (!dotfile_input_file_->Load(dotfile_name_)) {
|
||
|
Err(Location(), "Could not load dotfile.",
|
||
|
"The file \"" + FilePathToUTF8(dotfile_name_) + "\" cound't be loaded")
|
||
|
.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Err err;
|
||
|
dotfile_tokens_ = Tokenizer::Tokenize(dotfile_input_file_.get(), &err);
|
||
|
if (err.has_error()) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
dotfile_root_ = Parser::Parse(dotfile_tokens_, &err);
|
||
|
if (err.has_error()) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
dotfile_root_->AsBlock()->ExecuteBlockInScope(&dotfile_scope_, &err);
|
||
|
if (err.has_error()) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool Setup::FillOtherConfig(const CommandLine& cmdline) {
|
||
|
Err err;
|
||
|
|
||
|
// Secondary source path.
|
||
|
SourceDir secondary_source;
|
||
|
if (cmdline.HasSwitch(kSecondarySource)) {
|
||
|
// Prefer the command line over the config file.
|
||
|
secondary_source =
|
||
|
SourceDir(cmdline.GetSwitchValueASCII(kSecondarySource));
|
||
|
} else {
|
||
|
// Read from the config file if present.
|
||
|
const Value* secondary_value =
|
||
|
dotfile_scope_.GetValue("secondary_source", true);
|
||
|
if (secondary_value) {
|
||
|
if (!secondary_value->VerifyTypeIs(Value::STRING, &err)) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
build_settings_.SetSecondarySourcePath(
|
||
|
SourceDir(secondary_value->string_value()));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Build config file.
|
||
|
const Value* build_config_value =
|
||
|
dotfile_scope_.GetValue("buildconfig", true);
|
||
|
if (!build_config_value) {
|
||
|
Err(Location(), "No build config file.",
|
||
|
"Your .gn file (\"" + FilePathToUTF8(dotfile_name_) + "\")\n"
|
||
|
"didn't specify a \"buildconfig\" value.").PrintToStdout();
|
||
|
return false;
|
||
|
} else if (!build_config_value->VerifyTypeIs(Value::STRING, &err)) {
|
||
|
err.PrintToStdout();
|
||
|
return false;
|
||
|
}
|
||
|
build_settings_.set_build_config_file(
|
||
|
SourceFile(build_config_value->string_value()));
|
||
|
|
||
|
return true;
|
||
|
}
|