| // 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" (optional): 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 "additional_compile_targets" is absent, it defaults to the empty list. |
| |
| 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 |