1 // Copyright 2014 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 <set>
7
8 #include "base/command_line.h"
9 #include "base/strings/string_split.h"
10 #include "gn/commands.h"
11 #include "gn/metadata_walk.h"
12 #include "gn/setup.h"
13 #include "gn/standard_out.h"
14 #include "gn/switches.h"
15 #include "gn/target.h"
16
17 namespace commands {
18
19 const char kMeta[] = "meta";
20 const char kMeta_HelpShort[] = "meta: List target metadata collection results.";
21 const char kMeta_Help[] =
22 R"(gn meta
23
24 gn meta <out_dir> <target>* --data=<key>[,<key>*]* [--walk=<key>[,<key>*]*]
25 [--rebase=<dest dir>]
26
27 Lists collected metaresults of all given targets for the given data key(s),
28 collecting metadata dependencies as specified by the given walk key(s).
29
30 See `gn help generated_file` for more information on the walk.
31
32 Arguments
33
34 <target(s)>
35 A list of target labels from which to initiate the walk.
36
37 --data
38 A comma-separated list of keys from which to extract data. In each target
39 walked, its metadata scope is checked for the presence of these keys. If
40 present, the contents of those variable in the scope are appended to the
41 results list.
42
43 --walk (optional)
44 A comma-separated list of keys from which to control the walk. In each
45 target walked, its metadata scope is checked for the presence of any of
46 these keys. If present, the contents of those variables is checked to ensure
47 that it is a label of a valid dependency of the target and then added to the
48 set of targets to walk. If the empty string ("") is present in any of these
49 keys, all deps and data_deps are added to the walk set.
50
51 --rebase (optional)
52 A destination directory onto which to rebase any paths found. If set, all
53 collected metadata will be rebased onto this path. This option will throw errors
54 if collected metadata is not a list of strings.
55
56 Examples
57
58 gn meta out/Debug "//base/foo" --data=files
59 Lists collected metaresults for the `files` key in the //base/foo:foo
60 target and all of its dependency tree.
61
62 gn meta out/Debug "//base/foo" --data=files,other
63 Lists collected metaresults for the `files` and `other` keys in the
64 //base/foo:foo target and all of its dependency tree.
65
66 gn meta out/Debug "//base/foo" --data=files --walk=stop
67 Lists collected metaresults for the `files` key in the //base/foo:foo
68 target and all of the dependencies listed in the `stop` key (and so on).
69
70 gn meta out/Debug "//base/foo" --data=files --rebase="/"
71 Lists collected metaresults for the `files` key in the //base/foo:foo
72 target and all of its dependency tree, rebasing the strings in the `files`
73 key onto the source directory of the target's declaration relative to "/".
74 )";
75
RunMeta(const std::vector<std::string> & args)76 int RunMeta(const std::vector<std::string>& args) {
77 if (args.size() == 0) {
78 Err(Location(), "Unknown command format. See \"gn help meta\"",
79 "Usage: \"gn meta <out_dir> <target>* --data=<key>[,<key>*] "
80 "[--walk=<key>[,<key>*]*] [--rebase=<dest dir>]\"")
81 .PrintToStdout();
82 return 1;
83 }
84
85 Setup* setup = new Setup;
86 if (!setup->DoSetup(args[0], false) || !setup->Run())
87 return 1;
88
89 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
90 std::string rebase_dir = cmdline->GetSwitchValueASCII("rebase");
91 std::string data_keys_str = cmdline->GetSwitchValueASCII("data");
92 std::string walk_keys_str = cmdline->GetSwitchValueASCII("walk");
93
94 std::vector<std::string> inputs(args.begin() + 1, args.end());
95
96 UniqueVector<const Target*> targets;
97 for (const auto& input : inputs) {
98 const Target* target = ResolveTargetFromCommandLineString(setup, input);
99 if (!target) {
100 Err(Location(), "Unknown target " + input).PrintToStdout();
101 return 1;
102 }
103 targets.push_back(target);
104 }
105
106 std::vector<std::string> data_keys = base::SplitString(
107 data_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
108 if (data_keys.empty()) {
109 return 1;
110 }
111 std::vector<std::string> walk_keys = base::SplitString(
112 walk_keys_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
113 Err err;
114 TargetSet targets_walked;
115 SourceDir rebase_source_dir(rebase_dir);
116 // When SourceDir constructor is supplied with an empty string,
117 // a trailing slash will be added. This prevent SourceDir::is_null()
118 // from returning true. Explicitly remove this traling slash here.
119 if (rebase_dir.empty()) {
120 rebase_source_dir = SourceDir();
121 }
122 std::vector<Value> result =
123 WalkMetadata(targets, data_keys, walk_keys, rebase_source_dir,
124 &targets_walked, &err);
125 if (err.has_error()) {
126 err.PrintToStdout();
127 return 1;
128 }
129
130 OutputString("Metadata values\n", DECORATION_DIM);
131 for (const auto& value : result)
132 OutputString("\n" + value.ToString(false) + "\n");
133
134 // TODO(juliehockett): We should have better dep tracing and error support for
135 // this. Also possibly data about where different values came from.
136 OutputString("\nExtracted from:\n", DECORATION_DIM);
137 bool first = true;
138 for (const auto* target : targets_walked) {
139 if (!first) {
140 first = false;
141 OutputString(", ", DECORATION_DIM);
142 }
143 OutputString(target->label().GetUserVisibleName(true) + "\n");
144 }
145 OutputString("\nusing data keys:\n", DECORATION_DIM);
146 first = true;
147 for (const auto& key : data_keys) {
148 if (!first) {
149 first = false;
150 OutputString(", ");
151 }
152 OutputString(key + "\n");
153 }
154 if (!walk_keys.empty()) {
155 OutputString("\nand using walk keys:\n", DECORATION_DIM);
156 first = true;
157 for (const auto& key : walk_keys) {
158 if (!first) {
159 first = false;
160 OutputString(", ");
161 }
162 OutputString(key + "\n");
163 }
164 }
165 return 0;
166 }
167
168 } // namespace commands
169