blob: 5c6c1939730a378ca00131083ab2e36870a0a4d8 [file] [log] [blame]
// 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 <algorithm>
#include <iterator>
#include <set>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "gn/analyzer.h"
#include "gn/commands.h"
#include "gn/filesystem_utils.h"
#include "gn/location.h"
#include "gn/setup.h"
#include "gn/standard_out.h"
#include "gn/string_utils.h"
namespace commands {
const char kAnalyze[] = "analyze";
const char kAnalyze_HelpShort[] =
"analyze: Analyze which targets are affected by a list of files.";
const char kAnalyze_Help[] =
R"*(gn analyze <out_dir> <input_path> <output_path>
Analyze which targets are affected by a list of files.
This command takes three arguments:
out_dir is the path to the build directory.
input_path is a path to a file containing a JSON object with three fields:
- "files": A list of the filenames to check.
- "test_targets": A list of the labels for targets that are needed to run
the tests we wish to run.
- "additional_compile_targets": A list of the labels for targets that we
wish to rebuild, but aren't necessarily needed for testing. The important
difference between this field and "test_targets" is that if an item in
the additional_compile_targets list refers to a group, then any
dependencies of that group will be returned if they are out of date, but
the group itself does not need to be. If the dependencies themselves are
groups, the same filtering is repeated. This filtering can be used to
avoid rebuilding dependencies of a group that are unaffected by the input
files. The list may also contain the string "all" to refer to a
pseudo-group that contains every root target in the build graph.
This filtering behavior is also known as "pruning" the list of compile
targets.
If input_path is -, input is read from stdin.
output_path is a path indicating where the results of the command are to be
written. The results will be a file containing a JSON object with one or more
of following fields:
- "compile_targets": A list of the labels derived from the input
compile_targets list that are affected by the input files. Due to the way
the filtering works for compile targets as described above, this list may
contain targets that do not appear in the input list.
- "test_targets": A list of the labels from the input test_targets list that
are affected by the input files. This list will be a proper subset of the
input list.
- "invalid_targets": A list of any names from the input that do not exist in
the build graph. If this list is non-empty, the "error" field will also be
set to "Invalid targets".
- "status": A string containing one of three values:
- "Found dependency"
- "No dependency"
- "Found dependency (all)"
In the first case, the lists returned in compile_targets and test_targets
should be passed to ninja to build. In the second case, nothing was
affected and no build is necessary. In the third case, GN could not
determine the correct answer and returned the input as the output in order
to be safe.
- "error": This will only be present if an error occurred, and will contain
a string describing the error. This includes cases where the input file is
not in the right format, or contains invalid targets.
If output_path is -, output is written to stdout.
The command returns 1 if it is unable to read the input file or write the
output file, or if there is something wrong with the build such that gen
would also fail, and 0 otherwise. In particular, it returns 0 even if the
"error" key is non-empty and a non-fatal error occurred. In other words, it
tries really hard to always write something to the output JSON and convey
errors that way rather than via return codes.
)*";
int RunAnalyze(const std::vector<std::string>& args) {
if (args.size() != 3) {
Err(Location(), "Unknown command format. See \"gn help analyze\"",
"Usage: \"gn analyze <out_dir> <input_path> <output_path>")
.PrintToStdout();
return 1;
}
std::string input;
if (args[1] == "-") {
input = ReadStdin();
} else {
bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
if (!ret) {
Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
return 1;
}
}
// Deliberately leaked to avoid expensive process teardown.
Setup* setup = new Setup;
if (!setup->DoSetup(args[0], false) || !setup->Run())
return 1;
Err err;
Analyzer analyzer(
setup->builder(), setup->build_settings().build_config_file(),
setup->GetDotFile(),
setup->build_settings().build_args().build_args_dependency_files());
std::string output = analyzer.Analyze(input, &err);
if (err.has_error()) {
err.PrintToStdout();
return 1;
}
if (args[2] == "-") {
OutputString(output + "\n");
} else {
WriteFile(UTF8ToFilePath(args[2]), output, &err);
if (err.has_error()) {
err.PrintToStdout();
return 1;
}
}
return 0;
}
} // namespace commands