• 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" (optional): A list of the labels for targets
42      that we wish to rebuild, but aren't necessarily needed for testing. The
43      important difference between this field and "test_targets" is that if an
44      item in 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 "additional_compile_targets" is absent, it defaults to the empty list.
56 
57   If input_path is -, input is read from stdin.
58 
59   output_path is a path indicating where the results of the command are to be
60   written. The results will be a file containing a JSON object with one or more
61   of following fields:
62 
63    - "compile_targets": A list of the labels derived from the input
64      compile_targets list that are affected by the input files. Due to the way
65      the filtering works for compile targets as described above, this list may
66      contain targets that do not appear in the input list.
67 
68    - "test_targets": A list of the labels from the input test_targets list that
69      are affected by the input files. This list will be a proper subset of the
70      input list.
71 
72    - "invalid_targets": A list of any names from the input that do not exist in
73      the build graph. If this list is non-empty, the "error" field will also be
74      set to "Invalid targets".
75 
76    - "status": A string containing one of three values:
77 
78        - "Found dependency"
79        - "No dependency"
80        - "Found dependency (all)"
81 
82      In the first case, the lists returned in compile_targets and test_targets
83      should be passed to ninja to build. In the second case, nothing was
84      affected and no build is necessary. In the third case, GN could not
85      determine the correct answer and returned the input as the output in order
86      to be safe.
87 
88    - "error": This will only be present if an error occurred, and will contain
89      a string describing the error. This includes cases where the input file is
90      not in the right format, or contains invalid targets.
91 
92   If output_path is -, output is written to stdout.
93 
94   The command returns 1 if it is unable to read the input file or write the
95   output file, or if there is something wrong with the build such that gen
96   would also fail, and 0 otherwise. In particular, it returns 0 even if the
97   "error" key is non-empty and a non-fatal error occurred. In other words, it
98   tries really hard to always write something to the output JSON and convey
99   errors that way rather than via return codes.
100 )*";
101 
RunAnalyze(const std::vector<std::string> & args)102 int RunAnalyze(const std::vector<std::string>& args) {
103   if (args.size() != 3) {
104     Err(Location(), "Unknown command format. See \"gn help analyze\"",
105         "Usage: \"gn analyze <out_dir> <input_path> <output_path>")
106         .PrintToStdout();
107     return 1;
108   }
109 
110   std::string input;
111   if (args[1] == "-") {
112     input = ReadStdin();
113   } else {
114     bool ret = base::ReadFileToString(UTF8ToFilePath(args[1]), &input);
115     if (!ret) {
116       Err(Location(), "Input file " + args[1] + " not found.").PrintToStdout();
117       return 1;
118     }
119   }
120 
121   // Deliberately leaked to avoid expensive process teardown.
122   Setup* setup = new Setup;
123   if (!setup->DoSetup(args[0], false) || !setup->Run())
124     return 1;
125 
126   Err err;
127   Analyzer analyzer(
128       setup->builder(), setup->build_settings().build_config_file(),
129       setup->GetDotFile(),
130       setup->build_settings().build_args().build_args_dependency_files());
131   std::string output = analyzer.Analyze(input, &err);
132   if (err.has_error()) {
133     err.PrintToStdout();
134     return 1;
135   }
136 
137   if (args[2] == "-") {
138     OutputString(output + "\n");
139   } else {
140     WriteFile(UTF8ToFilePath(args[2]), output, &err);
141     if (err.has_error()) {
142       err.PrintToStdout();
143       return 1;
144     }
145   }
146 
147   return 0;
148 }
149 
150 }  // namespace commands
151