mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 14:26:09 +03:00
272 lines
9.5 KiB
C++
272 lines
9.5 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 "base/command_line.h"
|
|
#include "base/files/file_util.h"
|
|
#include "base/logging.h"
|
|
#include "base/strings/string_number_conversions.h"
|
|
#include "base/strings/utf_string_conversions.h"
|
|
#include "base/threading/thread_restrictions.h"
|
|
#include "base/time/time.h"
|
|
#include "build/build_config.h"
|
|
#include "tools/gn/err.h"
|
|
#include "tools/gn/exec_process.h"
|
|
#include "tools/gn/filesystem_utils.h"
|
|
#include "tools/gn/functions.h"
|
|
#include "tools/gn/input_conversion.h"
|
|
#include "tools/gn/input_file.h"
|
|
#include "tools/gn/parse_tree.h"
|
|
#include "tools/gn/scheduler.h"
|
|
#include "tools/gn/trace.h"
|
|
#include "tools/gn/value.h"
|
|
|
|
namespace functions {
|
|
|
|
namespace {
|
|
|
|
bool CheckExecScriptPermissions(const BuildSettings* build_settings,
|
|
const FunctionCallNode* function,
|
|
Err* err) {
|
|
const std::set<SourceFile>* whitelist =
|
|
build_settings->exec_script_whitelist();
|
|
if (!whitelist)
|
|
return true; // No whitelist specified, don't check.
|
|
|
|
LocationRange function_range = function->GetRange();
|
|
if (!function_range.begin().file())
|
|
return true; // No file, might be some internal thing, implicitly pass.
|
|
|
|
if (whitelist->find(function_range.begin().file()->name()) !=
|
|
whitelist->end())
|
|
return true; // Whitelisted, this is OK.
|
|
|
|
// Disallowed case.
|
|
*err = Err(function, "Disallowed exec_script call.",
|
|
"The use of exec_script use is restricted in this build. exec_script\n"
|
|
"is discouraged because it can slow down the GN run and is easily\n"
|
|
"abused.\n"
|
|
"\n"
|
|
"Generally nontrivial work should be done as build steps rather than\n"
|
|
"when GN is run. For example, if you need to compute a nontrivial\n"
|
|
"preprocessor define, it will be better to have an action target\n"
|
|
"generate a header containing the define rather than blocking the GN\n"
|
|
"run to compute the value.\n"
|
|
"\n"
|
|
"The allowed callers of exec_script is maintained in the \"//.gn\" file\n"
|
|
"if you need to modify the whitelist.");
|
|
return false;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
const char kExecScript[] = "exec_script";
|
|
const char kExecScript_HelpShort[] =
|
|
"exec_script: Synchronously run a script and return the output.";
|
|
const char kExecScript_Help[] =
|
|
R"(exec_script: Synchronously run a script and return the output.
|
|
|
|
exec_script(filename,
|
|
arguments = [],
|
|
input_conversion = "",
|
|
file_dependencies = [])
|
|
|
|
Runs the given script, returning the stdout of the script. The build
|
|
generation will fail if the script does not exist or returns a nonzero exit
|
|
code.
|
|
|
|
The current directory when executing the script will be the root build
|
|
directory. If you are passing file names, you will want to use the
|
|
rebase_path() function to make file names relative to this path (see "gn help
|
|
rebase_path").
|
|
|
|
Arguments:
|
|
|
|
filename:
|
|
File name of python script to execute. Non-absolute names will be treated
|
|
as relative to the current build file.
|
|
|
|
arguments:
|
|
A list of strings to be passed to the script as arguments. May be
|
|
unspecified or the empty list which means no arguments.
|
|
|
|
input_conversion:
|
|
Controls how the file is read and parsed. See "gn help input_conversion".
|
|
|
|
If unspecified, defaults to the empty string which causes the script
|
|
result to be discarded. exec script will return None.
|
|
|
|
dependencies:
|
|
(Optional) A list of files that this script reads or otherwise depends
|
|
on. These dependencies will be added to the build result such that if any
|
|
of them change, the build will be regenerated and the script will be
|
|
re-run.
|
|
|
|
The script itself will be an implicit dependency so you do not need to
|
|
list it.
|
|
|
|
Example
|
|
|
|
all_lines = exec_script(
|
|
"myscript.py", [some_input], "list lines",
|
|
[ rebase_path("data_file.txt", root_build_dir) ])
|
|
|
|
# This example just calls the script with no arguments and discards the
|
|
# result.
|
|
exec_script("//foo/bar/myscript.py")
|
|
)";
|
|
|
|
class ExecScriptScopedAllowBaseSyncPrimitives
|
|
: public base::ScopedAllowBaseSyncPrimitives {};
|
|
|
|
Value RunExecScript(Scope* scope,
|
|
const FunctionCallNode* function,
|
|
const std::vector<Value>& args,
|
|
Err* err) {
|
|
if (args.size() < 1 || args.size() > 4) {
|
|
*err = Err(function->function(), "Wrong number of arguments to exec_script",
|
|
"I expected between one and four arguments.");
|
|
return Value();
|
|
}
|
|
|
|
const Settings* settings = scope->settings();
|
|
const BuildSettings* build_settings = settings->build_settings();
|
|
const SourceDir& cur_dir = scope->GetSourceDir();
|
|
|
|
if (!CheckExecScriptPermissions(build_settings, function, err))
|
|
return Value();
|
|
|
|
// Find the python script to run.
|
|
std::string script_source_path = cur_dir.ResolveRelativeAs(
|
|
true, args[0], err,
|
|
scope->settings()->build_settings()->root_path_utf8());
|
|
if (err->has_error())
|
|
return Value();
|
|
base::FilePath script_path =
|
|
build_settings->GetFullPath(script_source_path, true);
|
|
if (!build_settings->secondary_source_path().empty() &&
|
|
!base::PathExists(script_path)) {
|
|
// Fall back to secondary source root when the file doesn't exist.
|
|
script_path =
|
|
build_settings->GetFullPathSecondary(script_source_path, true);
|
|
}
|
|
|
|
ScopedTrace trace(TraceItem::TRACE_SCRIPT_EXECUTE, script_source_path);
|
|
trace.SetToolchain(settings->toolchain_label());
|
|
|
|
// Add all dependencies of this script, including the script itself, to the
|
|
// build deps.
|
|
g_scheduler->AddGenDependency(script_path);
|
|
if (args.size() == 4) {
|
|
const Value& deps_value = args[3];
|
|
if (!deps_value.VerifyTypeIs(Value::LIST, err))
|
|
return Value();
|
|
|
|
for (const auto& dep : deps_value.list_value()) {
|
|
if (!dep.VerifyTypeIs(Value::STRING, err))
|
|
return Value();
|
|
g_scheduler->AddGenDependency(build_settings->GetFullPath(
|
|
cur_dir.ResolveRelativeAs(
|
|
true, dep, err,
|
|
scope->settings()->build_settings()->root_path_utf8()),
|
|
true));
|
|
if (err->has_error())
|
|
return Value();
|
|
}
|
|
}
|
|
|
|
// Make the command line.
|
|
const base::FilePath& python_path = build_settings->python_path();
|
|
base::CommandLine cmdline(python_path);
|
|
|
|
// CommandLine tries to interpret arguments by default. Passing "--" disables
|
|
// this for everything following the "--", so pass this as the very first
|
|
// thing to python. Python ignores a -- before the .py file, and this makes
|
|
// CommandLine let through arguments without modifying them.
|
|
cmdline.AppendArg("--");
|
|
|
|
cmdline.AppendArgPath(script_path);
|
|
|
|
if (args.size() >= 2) {
|
|
// Optional command-line arguments to the script.
|
|
const Value& script_args = args[1];
|
|
if (!script_args.VerifyTypeIs(Value::LIST, err))
|
|
return Value();
|
|
for (const auto& arg : script_args.list_value()) {
|
|
if (!arg.VerifyTypeIs(Value::STRING, err))
|
|
return Value();
|
|
cmdline.AppendArg(arg.string_value());
|
|
}
|
|
}
|
|
|
|
// Log command line for debugging help.
|
|
trace.SetCommandLine(cmdline);
|
|
base::TimeTicks begin_exec;
|
|
if (g_scheduler->verbose_logging()) {
|
|
#if defined(OS_WIN)
|
|
g_scheduler->Log("Pythoning",
|
|
base::UTF16ToUTF8(cmdline.GetCommandLineString()));
|
|
#else
|
|
g_scheduler->Log("Pythoning", cmdline.GetCommandLineString());
|
|
#endif
|
|
begin_exec = base::TimeTicks::Now();
|
|
}
|
|
|
|
base::FilePath startup_dir =
|
|
build_settings->GetFullPath(build_settings->build_dir());
|
|
// The first time a build is run, no targets will have been written so the
|
|
// build output directory won't exist. We need to make sure it does before
|
|
// running any scripts with this as its startup directory, although it will
|
|
// be relatively rare that the directory won't exist by the time we get here.
|
|
//
|
|
// If this shows up on benchmarks, we can cache whether we've done this
|
|
// or not and skip creating the directory.
|
|
base::CreateDirectory(startup_dir);
|
|
|
|
// Execute the process.
|
|
// TODO(brettw) set the environment block.
|
|
std::string output;
|
|
std::string stderr_output;
|
|
int exit_code = 0;
|
|
{
|
|
ExecScriptScopedAllowBaseSyncPrimitives allow_base_sync_primitives;
|
|
if (!internal::ExecProcess(cmdline, startup_dir, &output, &stderr_output,
|
|
&exit_code)) {
|
|
*err = Err(
|
|
function->function(), "Could not execute python.",
|
|
"I was trying to execute \"" + FilePathToUTF8(python_path) + "\".");
|
|
return Value();
|
|
}
|
|
}
|
|
if (g_scheduler->verbose_logging()) {
|
|
g_scheduler->Log(
|
|
"Pythoning",
|
|
script_source_path + " took " +
|
|
base::Int64ToString(
|
|
(base::TimeTicks::Now() - begin_exec).InMilliseconds()) +
|
|
"ms");
|
|
}
|
|
|
|
if (exit_code != 0) {
|
|
std::string msg = "Current dir: " + FilePathToUTF8(startup_dir) +
|
|
"\nCommand: " + FilePathToUTF8(cmdline.GetCommandLineString()) +
|
|
"\nReturned " + base::IntToString(exit_code);
|
|
if (!output.empty())
|
|
msg += " and printed out:\n\n" + output;
|
|
else
|
|
msg += ".";
|
|
if (!stderr_output.empty())
|
|
msg += "\nstderr:\n\n" + stderr_output;
|
|
|
|
*err = Err(function->function(), "Script returned non-zero exit code.",
|
|
msg);
|
|
return Value();
|
|
}
|
|
|
|
// Default to None value for the input conversion if unspecified.
|
|
return ConvertInputToValue(scope->settings(), output, function,
|
|
args.size() >= 3 ? args[2] : Value(), err);
|
|
}
|
|
|
|
} // namespace functions
|