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.
5 #include "tools/gn/ninja_build_writer.h"
10 #include "base/command_line.h"
11 #include "base/file_util.h"
12 #include "base/path_service.h"
13 #include "base/process/process_handle.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "build/build_config.h"
17 #include "tools/gn/build_settings.h"
18 #include "tools/gn/escape.h"
19 #include "tools/gn/filesystem_utils.h"
20 #include "tools/gn/input_file_manager.h"
21 #include "tools/gn/ninja_utils.h"
22 #include "tools/gn/scheduler.h"
23 #include "tools/gn/target.h"
24 #include "tools/gn/trace.h"
32 std::string
GetSelfInvocationCommand(const BuildSettings
* build_settings
) {
33 base::FilePath executable
;
34 PathService::Get(base::FILE_EXE
, &executable
);
36 CommandLine
cmdline(executable
.NormalizePathSeparatorsTo('/'));
37 cmdline
.AppendArg("gen");
38 cmdline
.AppendArg(build_settings
->build_dir().value());
39 cmdline
.AppendSwitchPath("--root", build_settings
->root_path());
40 cmdline
.AppendSwitch("-q"); // Don't write output.
42 EscapeOptions escape_shell
;
43 escape_shell
.mode
= ESCAPE_NINJA_COMMAND
;
45 // The command line code quoting varies by platform. We have one string,
46 // possibly with spaces, that we want to quote. The Windows command line
47 // quotes again, so we don't want quoting. The Posix one doesn't.
48 escape_shell
.inhibit_quoting
= true;
51 const CommandLine
& our_cmdline
= *CommandLine::ForCurrentProcess();
52 const CommandLine::SwitchMap
& switches
= our_cmdline
.GetSwitches();
53 for (CommandLine::SwitchMap::const_iterator i
= switches
.begin();
54 i
!= switches
.end(); ++i
) {
55 // Only write arguments we haven't already written. Always skip "args"
56 // since those will have been written to the file and will be used
57 // implicitly in the future. Keeping --args would mean changes to the file
59 if (i
->first
!= "q" && i
->first
!= "root" && i
->first
!= "args") {
60 std::string escaped_value
=
61 EscapeString(FilePathToUTF8(i
->second
), escape_shell
, NULL
);
62 cmdline
.AppendSwitchASCII(i
->first
, escaped_value
);
67 return base::WideToUTF8(cmdline
.GetCommandLineString());
69 return cmdline
.GetCommandLineString();
75 NinjaBuildWriter::NinjaBuildWriter(
76 const BuildSettings
* build_settings
,
77 const std::vector
<const Settings
*>& all_settings
,
78 const Toolchain
* default_toolchain
,
79 const std::vector
<const Target
*>& default_toolchain_targets
,
81 std::ostream
& dep_out
)
82 : build_settings_(build_settings
),
83 all_settings_(all_settings
),
84 default_toolchain_(default_toolchain
),
85 default_toolchain_targets_(default_toolchain_targets
),
88 path_output_(build_settings
->build_dir(), ESCAPE_NINJA
) {
91 NinjaBuildWriter::~NinjaBuildWriter() {
94 void NinjaBuildWriter::Run() {
98 WritePhonyAndAllRules();
102 bool NinjaBuildWriter::RunAndWriteFile(
103 const BuildSettings
* build_settings
,
104 const std::vector
<const Settings
*>& all_settings
,
105 const Toolchain
* default_toolchain
,
106 const std::vector
<const Target
*>& default_toolchain_targets
) {
107 ScopedTrace
trace(TraceItem::TRACE_FILE_WRITE
, "build.ninja");
109 base::FilePath
ninja_file(build_settings
->GetFullPath(
110 SourceFile(build_settings
->build_dir().value() + "build.ninja")));
111 base::CreateDirectory(ninja_file
.DirName());
114 file
.open(FilePathToUTF8(ninja_file
).c_str(),
115 std::ios_base::out
| std::ios_base::binary
);
119 std::ofstream depfile
;
120 depfile
.open((FilePathToUTF8(ninja_file
) + ".d").c_str(),
121 std::ios_base::out
| std::ios_base::binary
);
125 NinjaBuildWriter
gen(build_settings
, all_settings
, default_toolchain
,
126 default_toolchain_targets
, file
, depfile
);
131 void NinjaBuildWriter::WriteNinjaRules() {
133 out_
<< " command = " << GetSelfInvocationCommand(build_settings_
) << "\n";
134 out_
<< " description = Regenerating ninja files\n\n";
136 // This rule will regenerate the ninja files when any input file has changed.
137 out_
<< "build build.ninja: gn\n"
138 << " generator = 1\n"
139 << " depfile = build.ninja.d\n";
141 // Input build files. These go in the ".d" file. If we write them as
142 // dependencies in the .ninja file itself, ninja will expect the files to
143 // exist and will error if they don't. When files are listed in a depfile,
144 // missing files are ignored.
145 dep_out_
<< "build.ninja:";
146 std::vector
<base::FilePath
> input_files
;
147 g_scheduler
->input_file_manager()->GetAllPhysicalInputFileNames(&input_files
);
148 for (size_t i
= 0; i
< input_files
.size(); i
++)
149 dep_out_
<< " " << FilePathToUTF8(input_files
[i
]);
151 // Other files read by the build.
152 std::vector
<base::FilePath
> other_files
= g_scheduler
->GetGenDependencies();
153 for (size_t i
= 0; i
< other_files
.size(); i
++)
154 dep_out_
<< " " << FilePathToUTF8(other_files
[i
]);
159 void NinjaBuildWriter::WriteLinkPool() {
160 out_
<< "pool link_pool\n"
161 << " depth = " << default_toolchain_
->concurrent_links() << std::endl
165 void NinjaBuildWriter::WriteSubninjas() {
166 for (size_t i
= 0; i
< all_settings_
.size(); i
++) {
168 path_output_
.WriteFile(out_
, GetNinjaFileForToolchain(all_settings_
[i
]));
174 void NinjaBuildWriter::WritePhonyAndAllRules() {
175 std::string all_rules
;
177 // Write phony rules for all uniquely-named targets in the default toolchain.
178 // Don't do other toolchains or we'll get naming conflicts, and if the name
179 // isn't unique, also skip it. The exception is for the toplevel targets
180 // which we also find.
181 std::map
<std::string
, int> small_name_count
;
182 std::vector
<const Target
*> toplevel_targets
;
183 for (size_t i
= 0; i
< default_toolchain_targets_
.size(); i
++) {
184 const Target
* target
= default_toolchain_targets_
[i
];
185 const Label
& label
= target
->label();
186 small_name_count
[label
.name()]++;
188 // Look for targets with a name of the form
189 // dir = "//foo/", name = "foo"
190 // i.e. where the target name matches the top level directory. We will
191 // always write phony rules for these even if there is another target with
192 // the same short name.
193 const std::string
& dir_string
= label
.dir().value();
194 if (dir_string
.size() == label
.name().size() + 3 && // Size matches.
195 dir_string
[0] == '/' && dir_string
[1] == '/' && // "//" at beginning.
196 dir_string
[dir_string
.size() - 1] == '/' && // "/" at end.
197 dir_string
.compare(2, label
.name().size(), label
.name()) == 0)
198 toplevel_targets
.push_back(target
);
201 for (size_t i
= 0; i
< default_toolchain_targets_
.size(); i
++) {
202 const Target
* target
= default_toolchain_targets_
[i
];
203 const Label
& label
= target
->label();
204 OutputFile
target_file(target
->dependency_output_file());
205 // The output files may have leading "./" so normalize those away.
206 NormalizePath(&target_file
.value());
208 // Write the long name "foo/bar:baz" for the target "//foo/bar:baz".
209 std::string long_name
= label
.GetUserVisibleName(false);
210 base::TrimString(long_name
, "/", &long_name
);
211 WritePhonyRule(target
, target_file
, long_name
);
213 // Write the directory name with no target name if they match
214 // (e.g. "//foo/bar:bar" -> "foo/bar").
215 if (FindLastDirComponent(label
.dir()) == label
.name()) {
216 std::string medium_name
= DirectoryWithNoLastSlash(label
.dir());
217 base::TrimString(medium_name
, "/", &medium_name
);
218 // That may have generated a name the same as the short name of the
219 // target which we already wrote.
220 if (medium_name
!= label
.name())
221 WritePhonyRule(target
, target_file
, medium_name
);
224 // Write short names for ones which are unique.
225 if (small_name_count
[label
.name()] == 1)
226 WritePhonyRule(target
, target_file
, label
.name());
228 if (!all_rules
.empty())
229 all_rules
.append(" $\n ");
230 all_rules
.append(target_file
.value());
233 // Pick up phony rules for the toplevel targets with non-unique names (which
234 // would have been skipped in the above loop).
235 for (size_t i
= 0; i
< toplevel_targets
.size(); i
++) {
236 if (small_name_count
[toplevel_targets
[i
]->label().name()] > 1) {
237 const Target
* target
= toplevel_targets
[i
];
238 WritePhonyRule(target
, target
->dependency_output_file(),
239 target
->label().name());
243 if (!all_rules
.empty()) {
244 out_
<< "\nbuild all: phony " << all_rules
<< std::endl
;
245 out_
<< "default all" << std::endl
;
249 void NinjaBuildWriter::WritePhonyRule(const Target
* target
,
250 const OutputFile
& target_file
,
251 const std::string
& phony_name
) {
252 if (target_file
.value() == phony_name
)
253 return; // No need for a phony rule.
255 EscapeOptions ninja_escape
;
256 ninja_escape
.mode
= ESCAPE_NINJA
;
258 // Escape for special chars Ninja will handle.
259 std::string escaped
= EscapeString(phony_name
, ninja_escape
, NULL
);
261 out_
<< "build " << escaped
<< ": phony ";
262 path_output_
.WriteFile(out_
, target_file
);