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