• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <algorithm>
6 #include <iterator>
7 #include <set>
8 #include <vector>
9 
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "gn/analyzer.h"
13 #include "gn/commands.h"
14 #include "gn/filesystem_utils.h"
15 #include "gn/location.h"
16 #include "gn/setup.h"
17 #include "gn/standard_out.h"
18 #include "gn/string_utils.h"
19 
20 namespace commands {
21 
22 const char kAnalyze[] = "analyze";
23 const char kAnalyze_HelpShort[] =
24     "analyze: Analyze which targets are affected by a list of files.";
25 const char kAnalyze_Help[] =
26     R"(gn analyze <out_dir> <input_path> <output_path>
27 
28   Analyze which targets are affected by a list of files.
29 
30   This command takes three arguments:
31 
32   out_dir is the path to the build directory.
33 
34   input_path is a path to a file containing a JSON object with three fields:
35 
36    - "files": A list of the filenames to check.
37 
38    - "test_targets": A list of the labels for targets that are needed to run
39      the tests we wish to run.
40 
41    - "additional_compile_targets": A list of the labels for targets that we
42      wish to rebuild, but aren't necessarily needed for testing. The important
43      difference between this field and "test_targets" is that if an item in
44      the additional_compile_targets list refers to a group, then any
45      dependencies of that group will be returned if they are out of date, but
46      the group itself does not need to be. If the dependencies themselves are
47      groups, the same filtering is repeated. This filtering can be used to
48      avoid rebuilding dependencies of a group that are unaffected by the input
49      files. The list may also contain the string "all" to refer to a
50      pseudo-group that contains every root target in the build graph.
51 
52      This filtering behavior is also known as "pruning" the list of compile
53      targets.
54 
55   If input_path is -, input is read from stdin.
56 
57   output_path is a path indicating where the results of the command are to be
58   written. The results will be a file containing a JSON object with one or more
59   of following fields:
60 
61    - "compile_targets": A list of the labels derived from the input
62      compile_targets list that are affected by the input files. Due to the way
63      the filtering works for compile targets as described above, this list may
64      contain targets that do not appear in the input list.
65 
66    - "test_targets": A list of the labels from the input test_targets list that
67      are affected by the input files. This list will be a proper subset of the
68      input list.
69 
70    - "invalid_targets": A list of any names from the input that do not exist in
71      the build graph. If this list is non-empty, the "error" field will also be
72      set to "Invalid targets".
73 
74    - "status": A string containing one of three values:
75 
76        - "Found dependency"
77        - "No dependency"
78        - "Found dependency (all) "
79 
80      In the first case, the lists returned in compile_targets and test_targets
81      should be passed to ninja to build. In the second case, nothing was
82      affected and no build is necessary. In the third case, GN could not
83      determine the correct answer and returned the input as the output in order
84      to be safe.
85 
86    - "error": This will only be present if an error occurred, and will contain
87      a string describing the error. This includes cases where the input file is
88      not in the right format, or contains invalid targets.
89 
90   If output_path is -, output is written to stdout.
91 
92   The command returns 1 if it is unable to read the input file or write the
93   output file, or if there is something wrong with the build such that gen
94   would also fail, and 0 otherwise. In particular, it returns 0 even if the
95   "error" key is non-empty and a non-fatal error occurred. In other words, it
96   tries really hard to always write something to the output JSON and convey
97   errors that way rather than via return codes.
98 )";
99 
RunAnalyze(const std::vector<std::string> & args)100 int RunAnalyze(const std::vector<std::string>& args) {
101   if (args.size() != 3) {
102     Err(Location(), "You're holding it wrong.",
103         "Usage: \"gn analyze <out_dir> <input_path> <output_path>")
104         .PrintToStdout();
105     return 1;
106   }
107 
108   std::string input;
109   if (args[1] == "-") {
110     input = ReadStdin();
111   } else {
112     bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
113     if (!ret) {
114       Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
115       return 1;
116     }
117   }
118 
119   // Deliberately leaked to avoid expensive process teardown.
120   Setup* setup = new Setup;
121   if (!setup->DoSetup(args[0], false) || !setup->Run())
122     return 1;
123 
124   Err err;
125   Analyzer analyzer(
126       setup->builder(), setup->build_settings().build_config_file(),
127       setup->GetDotFile(),
128       setup->build_settings().build_args().build_args_dependency_files());
129   std::string output = analyzer.Analyze(input, &err);
130   if (err.has_error()) {
131     err.PrintToStdout();
132     return 1;
133   }
134 
135   if (args[2] == "-") {
136     OutputString(output + "\n");
137   } else {
138     WriteFile(UTF8ToFilePath(args[2]), output, &err);
139     if (err.has_error()) {
140       err.PrintToStdout();
141       return 1;
142     }
143   }
144 
145   return 0;
146 }
147 
148 }  // namespace commands
149