Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "tools/gn/ninja_binary_target_writer.h" | 5 #include "tools/gn/ninja_binary_target_writer.h" |
| 6 | 6 |
| 7 #include <cstring> | |
| 7 #include <set> | 8 #include <set> |
| 8 #include <sstream> | 9 #include <sstream> |
| 9 | 10 |
| 10 #include "base/strings/string_util.h" | 11 #include "base/strings/string_util.h" |
| 11 #include "tools/gn/config_values_extractors.h" | 12 #include "tools/gn/config_values_extractors.h" |
| 12 #include "tools/gn/deps_iterator.h" | 13 #include "tools/gn/deps_iterator.h" |
| 13 #include "tools/gn/err.h" | 14 #include "tools/gn/err.h" |
| 14 #include "tools/gn/escape.h" | 15 #include "tools/gn/escape.h" |
| 16 #include "tools/gn/filesystem_utils.h" | |
| 15 #include "tools/gn/ninja_utils.h" | 17 #include "tools/gn/ninja_utils.h" |
| 16 #include "tools/gn/settings.h" | 18 #include "tools/gn/settings.h" |
| 19 #include "tools/gn/source_file_type.h" | |
| 17 #include "tools/gn/string_utils.h" | 20 #include "tools/gn/string_utils.h" |
| 18 #include "tools/gn/substitution_writer.h" | 21 #include "tools/gn/substitution_writer.h" |
| 19 #include "tools/gn/target.h" | 22 #include "tools/gn/target.h" |
| 20 | 23 |
| 24 // Represents a set of tool types. Must be first since it is also shared by | |
| 25 // some helper functions in the anonymous namespace below. | |
| 26 class NinjaBinaryTargetWriter::SourceFileTypeSet { | |
| 27 public: | |
| 28 SourceFileTypeSet() { | |
| 29 memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES)); | |
| 30 } | |
| 31 | |
| 32 void Set(SourceFileType type) { | |
| 33 flags_[static_cast<int>(type)] = true; | |
| 34 } | |
| 35 bool Get(SourceFileType type) const { | |
| 36 return flags_[static_cast<int>(type)]; | |
| 37 } | |
| 38 | |
| 39 private: | |
| 40 bool flags_[static_cast<int>(SOURCE_NUMTYPES)]; | |
| 41 }; | |
| 42 | |
| 21 namespace { | 43 namespace { |
| 22 | 44 |
| 23 // Returns the proper escape options for writing compiler and linker flags. | 45 // Returns the proper escape options for writing compiler and linker flags. |
| 24 EscapeOptions GetFlagOptions() { | 46 EscapeOptions GetFlagOptions() { |
| 25 EscapeOptions opts; | 47 EscapeOptions opts; |
| 26 opts.mode = ESCAPE_NINJA_COMMAND; | 48 opts.mode = ESCAPE_NINJA_COMMAND; |
| 27 | 49 |
| 28 // Some flag strings are actually multiple flags that expect to be just | 50 // Some flag strings are actually multiple flags that expect to be just |
| 29 // added to the command line. We assume that quoting is done by the | 51 // added to the command line. We assume that quoting is done by the |
| 30 // buildfiles if it wants such things quoted. | 52 // buildfiles if it wants such things quoted. |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 58 const std::string& path = path_out.str(); | 80 const std::string& path = path_out.str(); |
| 59 if (path[0] == '"') | 81 if (path[0] == '"') |
| 60 out << " \"-I" << path.substr(1); | 82 out << " \"-I" << path.substr(1); |
| 61 else | 83 else |
| 62 out << " -I" << path; | 84 out << " -I" << path; |
| 63 } | 85 } |
| 64 | 86 |
| 65 PathOutput& path_output_; | 87 PathOutput& path_output_; |
| 66 }; | 88 }; |
| 67 | 89 |
| 90 // Computes the set of output files resulting from compiling the given source | |
| 91 // file. If the file can be compiled and the tool exists, fills the outputs in | |
| 92 // and writes the tool type to computed_tool_type. If the file is not | |
| 93 // compilable, returns false. | |
| 94 // | |
| 95 // The target that the source belongs to is passed as an argument. In the case | |
| 96 // of linking to source sets, this can be different than the target this class | |
| 97 // is currently writing. | |
| 98 // | |
| 99 // The function can succeed with a "NONE" tool type for object files which are | |
| 100 // just passed to the output. The output will always be overwritten, not | |
| 101 // appended to. | |
| 102 bool GetOutputFilesForSource(const Target* target, | |
|
brettw
2015/06/29 21:36:10
This was moved (unchanged) from the bottom of the
| |
| 103 const SourceFile& source, | |
| 104 Toolchain::ToolType* computed_tool_type, | |
| 105 std::vector<OutputFile>* outputs) { | |
| 106 outputs->clear(); | |
| 107 *computed_tool_type = Toolchain::TYPE_NONE; | |
| 108 | |
| 109 SourceFileType file_type = GetSourceFileType(source); | |
| 110 if (file_type == SOURCE_UNKNOWN) | |
| 111 return false; | |
| 112 if (file_type == SOURCE_O) { | |
| 113 // Object files just get passed to the output and not compiled. | |
| 114 outputs->push_back( | |
| 115 OutputFile(target->settings()->build_settings(), source)); | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 *computed_tool_type = | |
| 120 target->toolchain()->GetToolTypeForSourceType(file_type); | |
| 121 if (*computed_tool_type == Toolchain::TYPE_NONE) | |
| 122 return false; // No tool for this file (it's a header file or something). | |
| 123 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | |
| 124 if (!tool) | |
| 125 return false; // Tool does not apply for this toolchain.file. | |
| 126 | |
| 127 // Figure out what output(s) this compiler produces. | |
| 128 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
| 129 target, source, tool->outputs(), outputs); | |
| 130 return !outputs->empty(); | |
| 131 } | |
| 132 | |
| 133 // Returns the language-specific prefix/suffix for precomiled header files. | |
| 134 const char* GetPCHLangForToolType(Toolchain::ToolType type) { | |
| 135 switch (type) { | |
| 136 case Toolchain::TYPE_CC: | |
| 137 return "c"; | |
| 138 case Toolchain::TYPE_CXX: | |
| 139 return "cc"; | |
| 140 case Toolchain::TYPE_OBJC: | |
| 141 return "m"; | |
| 142 case Toolchain::TYPE_OBJCXX: | |
| 143 return "mm"; | |
| 144 default: | |
| 145 NOTREACHED() << "Not a valid PCH tool type type"; | |
| 146 return ""; | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 // Returns the object files for the precompiled header of the given type (flag | |
| 151 // type and tool type must match). | |
| 152 void GetWindowsPCHObjectFiles(const Target* target, | |
| 153 Toolchain::ToolType tool_type, | |
| 154 std::vector<OutputFile>* outputs) { | |
| 155 outputs->clear(); | |
| 156 | |
| 157 // Compute the tool. This must use the tool type passed in rather than the | |
| 158 // detected file type of the precompiled source file since the same | |
| 159 // precompiled source file will be used for separate C/C++ compiles. | |
| 160 const Tool* tool = target->toolchain()->GetTool(tool_type); | |
| 161 if (!tool) | |
| 162 return; | |
| 163 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
| 164 target, target->config_values().precompiled_source(), | |
| 165 tool->outputs(), outputs); | |
| 166 | |
| 167 if (outputs->empty()) | |
| 168 return; | |
| 169 if (outputs->size() > 1) | |
| 170 outputs->resize(1); // Only link the first output from the compiler tool. | |
| 171 | |
| 172 // Need to annotate the obj files with the language type. For example: | |
| 173 // obj/foo/target_name.precompile.obj -> | |
| 174 // obj/foo/target_name.precompile.cc.obj | |
| 175 const char* lang_suffix = GetPCHLangForToolType(tool_type); | |
| 176 std::string& output_value = (*outputs)[0].value(); | |
| 177 size_t extension_offset = FindExtensionOffset(output_value); | |
| 178 if (extension_offset == std::string::npos) { | |
| 179 NOTREACHED() << "No extension found"; | |
| 180 } else { | |
| 181 DCHECK(extension_offset >= 1); | |
| 182 DCHECK(output_value[extension_offset - 1] == '.'); | |
| 183 output_value.insert(extension_offset - 1, "."); | |
| 184 output_value.insert(extension_offset, lang_suffix); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 // Appends the object files generated by the given source set to the given | |
| 189 // output vector. | |
| 190 void AddSourceSetObjectFiles(const Target* source_set, | |
| 191 UniqueVector<OutputFile>* obj_files) { | |
| 192 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
| 193 NinjaBinaryTargetWriter::SourceFileTypeSet used_types; | |
| 194 | |
| 195 // Compute object files for all sources. Only link the first output from | |
| 196 // teh tool if there are more than one. | |
|
scottmg
2015/06/29 21:49:17
teh
| |
| 197 for (const auto& source : source_set->sources()) { | |
| 198 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
| 199 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) | |
| 200 obj_files->push_back(tool_outputs[0]); | |
| 201 | |
| 202 used_types.Set(GetSourceFileType(source)); | |
| 203 } | |
| 204 | |
| 205 // Precompiled header object files. | |
| 206 if (source_set->config_values().has_precompiled_headers()) { | |
| 207 if (used_types.Get(SOURCE_C)) { | |
| 208 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CC, &tool_outputs); | |
| 209 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 210 } | |
| 211 if (used_types.Get(SOURCE_CPP)) { | |
| 212 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_CXX, &tool_outputs); | |
| 213 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 214 } | |
| 215 if (used_types.Get(SOURCE_M)) { | |
| 216 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJC, &tool_outputs); | |
| 217 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 218 } | |
| 219 if (used_types.Get(SOURCE_MM)) { | |
| 220 GetWindowsPCHObjectFiles(source_set, Toolchain::TYPE_OBJCXX, | |
| 221 &tool_outputs); | |
| 222 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 223 } | |
| 224 } | |
| 225 } | |
| 226 | |
| 68 } // namespace | 227 } // namespace |
| 69 | 228 |
| 70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, | 229 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, |
| 71 std::ostream& out) | 230 std::ostream& out) |
| 72 : NinjaTargetWriter(target, out), | 231 : NinjaTargetWriter(target, out), |
| 73 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) { | 232 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)), |
| 233 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) { | |
| 74 } | 234 } |
| 75 | 235 |
| 76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { | 236 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { |
| 77 } | 237 } |
| 78 | 238 |
| 79 void NinjaBinaryTargetWriter::Run() { | 239 void NinjaBinaryTargetWriter::Run() { |
| 80 WriteCompilerVars(); | 240 // Figure out what source types are needed. |
| 81 | 241 SourceFileTypeSet used_types; |
| 242 for (const auto& source : target_->sources()) | |
| 243 used_types.Set(GetSourceFileType(source)); | |
| 244 | |
| 245 WriteCompilerVars(used_types); | |
| 246 | |
| 247 // The input dependencies will be an order-only dependency. This will cause | |
|
brettw
2015/06/29 21:36:10
This comment was moved unchanged from below.
| |
| 248 // Ninja to make sure the inputs are up-to-date before compiling this source, | |
| 249 // but changes in the inputs deps won't cause the file to be recompiled. | |
| 250 // | |
| 251 // This is important to prevent changes in unrelated actions that are | |
| 252 // upstream of this target from causing everything to be recompiled | |
| 253 // | |
| 254 // Why can we get away with this rather than using implicit deps ("|", which | |
| 255 // will force rebuilds when the inputs change)? For source code, the | |
| 256 // computed dependencies of all headers will be computed by the compiler, | |
| 257 // which will cause source rebuilds if any "real" upstream dependencies | |
| 258 // change. | |
| 259 // | |
| 260 // If a .cc file is generated by an input dependency, Ninja will see the | |
| 261 // input to the build rule doesn't exist, and that it is an output from a | |
| 262 // previous step, and build the previous step first. This is a "real" | |
| 263 // dependency and doesn't need | or || to express. | |
| 264 // | |
| 265 // The only case where this rule matters is for the first build where no .d | |
| 266 // files exist, and Ninja doesn't know what that source file depends on. In | |
| 267 // this case it's sufficient to ensure that the upstream dependencies are | |
| 268 // built first. This is exactly what Ninja's order-only dependencies | |
| 269 // expresses. | |
| 270 OutputFile order_only_dep = | |
| 271 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
| 272 | |
| 273 std::vector<OutputFile> pch_obj_files; | |
| 274 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files); | |
| 275 | |
| 276 // Treat all precompiled object files as explicit dependencies of all | |
| 277 // compiles. Some notes: | |
| 278 // | |
| 279 // - Technically only the language-specific one is required for any specific | |
| 280 // compile, but that's more difficult to express and the additional logic | |
| 281 // doesn't buy much reduced parallelism. Just list them all (there's | |
| 282 // usually only one anyway). | |
| 283 // | |
| 284 // - Technically the .pch file is the input to the compile, not the | |
| 285 // precompiled header's corresponding object file that we're using here. | |
| 286 // But Ninja's depslog doesn't support multiple outputs from the | |
| 287 // precompiled header compile step (it outputs both the .pch file and a | |
| 288 // corresponding .obj file). So we consistently list the .obj file and the | |
| 289 // .pch file we really need comes along with it. | |
| 82 std::vector<OutputFile> obj_files; | 290 std::vector<OutputFile> obj_files; |
| 83 std::vector<SourceFile> other_files; | 291 std::vector<SourceFile> other_files; |
| 84 WriteSources(&obj_files, &other_files); | 292 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files); |
| 85 | 293 |
| 86 if (target_->output_type() == Target::SOURCE_SET) | 294 // Also link all pch object files. |
| 295 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); | |
| 296 | |
| 297 if (target_->output_type() == Target::SOURCE_SET) { | |
| 87 WriteSourceSetStamp(obj_files); | 298 WriteSourceSetStamp(obj_files); |
| 88 else | 299 #ifndef NDEBUG |
| 300 // Verify that the function that separately computes a source set's object | |
| 301 // files match the object files just computed. | |
| 302 UniqueVector<OutputFile> computed_obj; | |
| 303 AddSourceSetObjectFiles(target_, &computed_obj); | |
| 304 DCHECK_EQ(obj_files.size(), computed_obj.size()); | |
| 305 for (const auto& obj : obj_files) | |
| 306 DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj)); | |
| 307 #endif | |
| 308 } else { | |
| 89 WriteLinkerStuff(obj_files, other_files); | 309 WriteLinkerStuff(obj_files, other_files); |
| 90 } | 310 } |
| 91 | 311 } |
| 92 void NinjaBinaryTargetWriter::WriteCompilerVars() { | 312 |
| 313 void NinjaBinaryTargetWriter::WriteCompilerVars( | |
| 314 const SourceFileTypeSet& used_types) { | |
| 93 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); | 315 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); |
| 94 | 316 |
| 95 // Defines. | 317 // Defines. |
| 96 if (subst.used[SUBSTITUTION_DEFINES]) { | 318 if (subst.used[SUBSTITUTION_DEFINES]) { |
| 97 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; | 319 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; |
| 98 RecursiveTargetConfigToStream<std::string>( | 320 RecursiveTargetConfigToStream<std::string>( |
| 99 target_, &ConfigValues::defines, DefineWriter(), out_); | 321 target_, &ConfigValues::defines, DefineWriter(), out_); |
| 100 out_ << std::endl; | 322 out_ << std::endl; |
| 101 } | 323 } |
| 102 | 324 |
| 103 // Include directories. | 325 // Include directories. |
| 104 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { | 326 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { |
| 105 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; | 327 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; |
| 106 PathOutput include_path_output( | 328 PathOutput include_path_output( |
| 107 path_output_.current_dir(), | 329 path_output_.current_dir(), |
| 108 settings_->build_settings()->root_path_utf8(), | 330 settings_->build_settings()->root_path_utf8(), |
| 109 ESCAPE_NINJA_COMMAND); | 331 ESCAPE_NINJA_COMMAND); |
| 110 RecursiveTargetConfigToStream<SourceDir>( | 332 RecursiveTargetConfigToStream<SourceDir>( |
| 111 target_, &ConfigValues::include_dirs, | 333 target_, &ConfigValues::include_dirs, |
| 112 IncludeWriter(include_path_output), out_); | 334 IncludeWriter(include_path_output), out_); |
| 113 out_ << std::endl; | 335 out_ << std::endl; |
| 114 } | 336 } |
| 115 | 337 |
| 116 // C flags and friends. | 338 bool has_precompiled_headers = |
| 117 EscapeOptions flag_escape_options = GetFlagOptions(); | 339 target_->config_values().has_precompiled_headers(); |
| 118 #define WRITE_FLAGS(name, subst_enum) \ | |
| 119 if (subst.used[subst_enum]) { \ | |
| 120 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \ | |
| 121 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \ | |
| 122 flag_escape_options, out_); \ | |
| 123 out_ << std::endl; \ | |
| 124 } | |
| 125 | 340 |
| 126 WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS) | 341 // Some toolchains pass cflags to the assembler since it's the same command, |
| 127 WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C) | 342 // and cflags_c might also be sent to the objective C compiler. |
| 128 WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC) | 343 // |
| 129 WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC) | 344 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome |
| 130 WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC) | 345 // Mac build is updated not to pass cflags_c to .m files. |
| 131 | 346 EscapeOptions opts = GetFlagOptions(); |
| 132 #undef WRITE_FLAGS | 347 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CPP) || |
| 348 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) || | |
| 349 used_types.Get(SOURCE_ASM)) { | |
| 350 WriteOneFlag(SUBSTITUTION_CFLAGS, false, Toolchain::TYPE_NONE, | |
| 351 &ConfigValues::cflags, opts); | |
| 352 } | |
| 353 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) || | |
| 354 used_types.Get(SOURCE_ASM)) { | |
| 355 WriteOneFlag(SUBSTITUTION_CFLAGS_C, has_precompiled_headers, | |
| 356 Toolchain::TYPE_CC, &ConfigValues::cflags_c, opts); | |
| 357 } | |
| 358 if (used_types.Get(SOURCE_CPP)) { | |
| 359 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, has_precompiled_headers, | |
| 360 Toolchain::TYPE_CXX, &ConfigValues::cflags_cc, opts); | |
| 361 } | |
| 362 if (used_types.Get(SOURCE_M)) { | |
| 363 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, has_precompiled_headers, | |
| 364 Toolchain::TYPE_OBJC, &ConfigValues::cflags_objc, opts); | |
| 365 } | |
| 366 if (used_types.Get(SOURCE_MM)) { | |
| 367 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJCC, has_precompiled_headers, | |
| 368 Toolchain::TYPE_OBJCXX, &ConfigValues::cflags_objcc, opts); | |
| 369 } | |
| 133 | 370 |
| 134 WriteSharedVars(subst); | 371 WriteSharedVars(subst); |
| 135 } | 372 } |
| 136 | 373 |
| 374 void NinjaBinaryTargetWriter::WriteOneFlag( | |
| 375 SubstitutionType subst_enum, | |
| 376 bool has_precompiled_headers, | |
| 377 Toolchain::ToolType tool_type, | |
| 378 const std::vector<std::string>& (ConfigValues::* getter)() const, | |
| 379 EscapeOptions flag_escape_options) { | |
| 380 if (!target_->toolchain()->substitution_bits().used[subst_enum]) | |
| 381 return; | |
| 382 | |
| 383 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; | |
| 384 | |
| 385 if (has_precompiled_headers) { | |
| 386 const Tool* tool = target_->toolchain()->GetTool(tool_type); | |
| 387 if (tool && tool->precompiled_header_type() == Tool::PCH_MSVC) { | |
| 388 // Name the .pch file. | |
| 389 out_ << " /Fp"; | |
| 390 path_output_.WriteFile(out_, GetWindowsPCHFile(tool_type)); | |
| 391 | |
| 392 // Enables precompiled headers and names the .h file. It's a string | |
| 393 // rather than a file name (so no need to rebase or use path_output_). | |
| 394 out_ << " /Yu" << target_->config_values().precompiled_header(); | |
| 395 } | |
| 396 } | |
| 397 | |
| 398 RecursiveTargetConfigStringsToStream(target_, getter, | |
| 399 flag_escape_options, out_); | |
| 400 out_ << std::endl; | |
| 401 } | |
| 402 | |
| 403 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands( | |
| 404 const SourceFileTypeSet& used_types, | |
| 405 const OutputFile& order_only_dep, | |
| 406 std::vector<OutputFile>* object_files) { | |
| 407 if (!target_->config_values().has_precompiled_headers()) | |
| 408 return; | |
| 409 | |
| 410 const Tool* tool_c = target_->toolchain()->GetTool(Toolchain::TYPE_CC); | |
| 411 if (tool_c && | |
| 412 tool_c->precompiled_header_type() == Tool::PCH_MSVC && | |
| 413 used_types.Get(SOURCE_C)) { | |
| 414 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_C, | |
| 415 Toolchain::TYPE_CC, | |
| 416 order_only_dep, object_files); | |
| 417 } | |
| 418 const Tool* tool_cxx = target_->toolchain()->GetTool(Toolchain::TYPE_CXX); | |
| 419 if (tool_cxx && | |
| 420 tool_cxx->precompiled_header_type() == Tool::PCH_MSVC && | |
| 421 used_types.Get(SOURCE_CPP)) { | |
| 422 WriteWindowsPCHCommand(SUBSTITUTION_CFLAGS_CC, | |
| 423 Toolchain::TYPE_CXX, | |
| 424 order_only_dep, object_files); | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 void NinjaBinaryTargetWriter::WriteWindowsPCHCommand( | |
| 429 SubstitutionType flag_type, | |
| 430 Toolchain::ToolType tool_type, | |
| 431 const OutputFile& order_only_dep, | |
| 432 std::vector<OutputFile>* object_files) { | |
| 433 // Compute the object file (it will be language-specific). | |
| 434 std::vector<OutputFile> outputs; | |
| 435 GetWindowsPCHObjectFiles(target_, tool_type, &outputs); | |
| 436 if (outputs.empty()) | |
| 437 return; | |
| 438 object_files->insert(object_files->end(), outputs.begin(), outputs.end()); | |
| 439 | |
| 440 // Build line to compile the file. | |
| 441 WriteCompilerBuildLine(target_->config_values().precompiled_source(), | |
| 442 std::vector<OutputFile>(), order_only_dep, tool_type, | |
| 443 outputs); | |
| 444 | |
| 445 // This build line needs a custom language-specific flags value. It needs to | |
| 446 // include the switch to generate the .pch file in addition to the normal | |
| 447 // ones. Rule-specific variables are just indented underneath the rule line, | |
| 448 // and this defines the new one in terms of the old value. | |
| 449 out_ << " " << kSubstitutionNinjaNames[flag_type] << " ="; | |
| 450 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}"; | |
| 451 | |
| 452 // Append the command to generate the .pch file. | |
| 453 out_ << " /Yc" << target_->config_values().precompiled_header(); | |
| 454 | |
| 455 // Write two blank lines to help separate the PCH build lines from the | |
| 456 // regular source build lines. | |
| 457 out_ << std::endl << std::endl; | |
| 458 } | |
| 459 | |
| 137 void NinjaBinaryTargetWriter::WriteSources( | 460 void NinjaBinaryTargetWriter::WriteSources( |
| 461 const std::vector<OutputFile>& extra_deps, | |
| 462 const OutputFile& order_only_dep, | |
| 138 std::vector<OutputFile>* object_files, | 463 std::vector<OutputFile>* object_files, |
| 139 std::vector<SourceFile>* other_files) { | 464 std::vector<SourceFile>* other_files) { |
| 140 object_files->reserve(target_->sources().size()); | 465 object_files->reserve(object_files->size() + target_->sources().size()); |
| 141 | |
| 142 OutputFile input_dep = | |
| 143 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
| 144 | |
| 145 std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_); | |
| 146 | 466 |
| 147 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. | 467 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. |
| 148 for (const auto& source : target_->sources()) { | 468 for (const auto& source : target_->sources()) { |
| 149 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | 469 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; |
| 150 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { | 470 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { |
| 151 if (GetSourceFileType(source) == SOURCE_DEF) | 471 if (GetSourceFileType(source) == SOURCE_DEF) |
| 152 other_files->push_back(source); | 472 other_files->push_back(source); |
| 153 continue; // No output for this source. | 473 continue; // No output for this source. |
| 154 } | 474 } |
| 155 | 475 |
| 156 if (tool_type != Toolchain::TYPE_NONE) { | 476 if (tool_type != Toolchain::TYPE_NONE) { |
| 157 out_ << "build"; | 477 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type, |
| 158 path_output_.WriteFiles(out_, tool_outputs); | 478 tool_outputs); |
| 159 | |
| 160 out_ << ": " << rule_prefix << Toolchain::ToolTypeToName(tool_type); | |
| 161 out_ << " "; | |
| 162 path_output_.WriteFile(out_, source); | |
| 163 if (!input_dep.value().empty()) { | |
| 164 // Write out the input dependencies as an order-only dependency. This | |
| 165 // will cause Ninja to make sure the inputs are up-to-date before | |
| 166 // compiling this source, but changes in the inputs deps won't cause | |
| 167 // the file to be recompiled. | |
| 168 // | |
| 169 // This is important to prevent changes in unrelated actions that | |
| 170 // are upstream of this target from causing everything to be recompiled. | |
| 171 // | |
| 172 // Why can we get away with this rather than using implicit deps ("|", | |
| 173 // which will force rebuilds when the inputs change)? For source code, | |
| 174 // the computed dependencies of all headers will be computed by the | |
| 175 // compiler, which will cause source rebuilds if any "real" upstream | |
| 176 // dependencies change. | |
| 177 // | |
| 178 // If a .cc file is generated by an input dependency, Ninja will see | |
| 179 // the input to the build rule doesn't exist, and that it is an output | |
| 180 // from a previous step, and build the previous step first. This is a | |
| 181 // "real" dependency and doesn't need | or || to express. | |
| 182 // | |
| 183 // The only case where this rule matters is for the first build where | |
| 184 // no .d files exist, and Ninja doesn't know what that source file | |
| 185 // depends on. In this case it's sufficient to ensure that the upstream | |
| 186 // dependencies are built first. This is exactly what Ninja's order- | |
| 187 // only dependencies expresses. | |
| 188 out_ << " || "; | |
| 189 path_output_.WriteFile(out_, input_dep); | |
| 190 } | |
| 191 out_ << std::endl; | |
| 192 } | 479 } |
| 193 | 480 |
| 194 // It's theoretically possible for a compiler to produce more than one | 481 // It's theoretically possible for a compiler to produce more than one |
| 195 // output, but we'll only link to the first output. | 482 // output, but we'll only link to the first output. |
| 196 object_files->push_back(tool_outputs[0]); | 483 object_files->push_back(tool_outputs[0]); |
| 197 } | 484 } |
| 198 out_ << std::endl; | 485 out_ << std::endl; |
| 199 } | 486 } |
| 200 | 487 |
| 488 void NinjaBinaryTargetWriter::WriteCompilerBuildLine( | |
|
brettw
2015/06/29 21:36:10
This was factored out of WriteSources so it could
| |
| 489 const SourceFile& source, | |
| 490 const std::vector<OutputFile>& extra_deps, | |
| 491 const OutputFile& order_only_dep, | |
| 492 Toolchain::ToolType tool_type, | |
| 493 const std::vector<OutputFile>& outputs) { | |
| 494 out_ << "build"; | |
| 495 path_output_.WriteFiles(out_, outputs); | |
| 496 | |
| 497 out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type); | |
| 498 out_ << " "; | |
| 499 path_output_.WriteFile(out_, source); | |
| 500 | |
| 501 if (!extra_deps.empty()) { | |
| 502 out_ << " |"; | |
| 503 for (const OutputFile& dep : extra_deps) { | |
| 504 out_ << " "; | |
| 505 path_output_.WriteFile(out_, dep); | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 if (!order_only_dep.value().empty()) { | |
| 510 out_ << " || "; | |
| 511 path_output_.WriteFile(out_, order_only_dep); | |
| 512 } | |
| 513 out_ << std::endl; | |
| 514 } | |
| 515 | |
| 201 void NinjaBinaryTargetWriter::WriteLinkerStuff( | 516 void NinjaBinaryTargetWriter::WriteLinkerStuff( |
| 202 const std::vector<OutputFile>& object_files, | 517 const std::vector<OutputFile>& object_files, |
| 203 const std::vector<SourceFile>& other_files) { | 518 const std::vector<SourceFile>& other_files) { |
| 204 std::vector<OutputFile> output_files; | 519 std::vector<OutputFile> output_files; |
| 205 SubstitutionWriter::ApplyListToLinkerAsOutputFile( | 520 SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
| 206 target_, tool_, tool_->outputs(), &output_files); | 521 target_, tool_, tool_->outputs(), &output_files); |
| 207 | 522 |
| 208 out_ << "build"; | 523 out_ << "build"; |
| 209 path_output_.WriteFiles(out_, output_files); | 524 path_output_.WriteFiles(out_, output_files); |
| 210 | 525 |
| 211 out_ << ": " | 526 out_ << ": " << rule_prefix_ |
| 212 << GetNinjaRulePrefixForToolchain(settings_) | |
| 213 << Toolchain::ToolTypeToName( | 527 << Toolchain::ToolTypeToName( |
| 214 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); | 528 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); |
| 215 | 529 |
| 216 UniqueVector<OutputFile> extra_object_files; | 530 UniqueVector<OutputFile> extra_object_files; |
| 217 UniqueVector<const Target*> linkable_deps; | 531 UniqueVector<const Target*> linkable_deps; |
| 218 UniqueVector<const Target*> non_linkable_deps; | 532 UniqueVector<const Target*> non_linkable_deps; |
| 219 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); | 533 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); |
| 220 | 534 |
| 221 // Object files. | 535 // Object files. |
| 222 for (const auto& obj : object_files) { | 536 path_output_.WriteFiles(out_, object_files); |
| 223 out_ << " "; | 537 path_output_.WriteFiles(out_, extra_object_files); |
| 224 path_output_.WriteFile(out_, obj); | |
| 225 } | |
| 226 for (const auto& obj : extra_object_files) { | |
| 227 out_ << " "; | |
| 228 path_output_.WriteFile(out_, obj); | |
| 229 } | |
| 230 | 538 |
| 539 // Dependencies. | |
| 231 std::vector<OutputFile> implicit_deps; | 540 std::vector<OutputFile> implicit_deps; |
| 232 std::vector<OutputFile> solibs; | 541 std::vector<OutputFile> solibs; |
| 233 | |
| 234 for (const Target* cur : linkable_deps) { | 542 for (const Target* cur : linkable_deps) { |
| 235 // All linkable deps should have a link output file. | 543 // All linkable deps should have a link output file. |
| 236 DCHECK(!cur->link_output_file().value().empty()) | 544 DCHECK(!cur->link_output_file().value().empty()) |
| 237 << "No link output file for " | 545 << "No link output file for " |
| 238 << target_->label().GetUserVisibleName(false); | 546 << target_->label().GetUserVisibleName(false); |
| 239 | 547 |
| 240 if (cur->dependency_output_file().value() != | 548 if (cur->dependency_output_file().value() != |
| 241 cur->link_output_file().value()) { | 549 cur->link_output_file().value()) { |
| 242 // This is a shared library with separate link and deps files. Save for | 550 // This is a shared library with separate link and deps files. Save for |
| 243 // later. | 551 // later. |
| (...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 // don't link at all. | 743 // don't link at all. |
| 436 bool can_link_libs = target_->IsFinal(); | 744 bool can_link_libs = target_->IsFinal(); |
| 437 | 745 |
| 438 if (dep->output_type() == Target::SOURCE_SET) { | 746 if (dep->output_type() == Target::SOURCE_SET) { |
| 439 // Source sets have their object files linked into final targets | 747 // Source sets have their object files linked into final targets |
| 440 // (shared libraries, executables, and complete static | 748 // (shared libraries, executables, and complete static |
| 441 // libraries). Intermediate static libraries and other source sets | 749 // libraries). Intermediate static libraries and other source sets |
| 442 // just forward the dependency, otherwise the files in the source | 750 // just forward the dependency, otherwise the files in the source |
| 443 // set can easily get linked more than once which will cause | 751 // set can easily get linked more than once which will cause |
| 444 // multiple definition errors. | 752 // multiple definition errors. |
| 445 if (can_link_libs) { | 753 if (can_link_libs) |
| 446 // Linking in a source set to an executable, shared library, or | 754 AddSourceSetObjectFiles(dep, extra_object_files); |
| 447 // complete static library, so copy its object files. | |
| 448 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
| 449 for (const auto& source : dep->sources()) { | |
| 450 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
| 451 if (GetOutputFilesForSource(dep, source, &tool_type, &tool_outputs)) { | |
| 452 // Only link the first output if there are more than one. | |
| 453 extra_object_files->push_back(tool_outputs[0]); | |
| 454 } | |
| 455 } | |
| 456 } | |
| 457 | 755 |
| 458 // Add the source set itself as a non-linkable dependency on the current | 756 // Add the source set itself as a non-linkable dependency on the current |
| 459 // target. This will make sure that anything the source set's stamp file | 757 // target. This will make sure that anything the source set's stamp file |
| 460 // depends on (like data deps) are also built before the current target | 758 // depends on (like data deps) are also built before the current target |
| 461 // can be complete. Otherwise, these will be skipped since this target | 759 // can be complete. Otherwise, these will be skipped since this target |
| 462 // will depend only on the source set's object files. | 760 // will depend only on the source set's object files. |
| 463 non_linkable_deps->push_back(dep); | 761 non_linkable_deps->push_back(dep); |
| 464 } else if (can_link_libs && dep->IsLinkable()) { | 762 } else if (can_link_libs && dep->IsLinkable()) { |
| 465 linkable_deps->push_back(dep); | 763 linkable_deps->push_back(dep); |
| 466 } else { | 764 } else { |
| 467 non_linkable_deps->push_back(dep); | 765 non_linkable_deps->push_back(dep); |
| 468 } | 766 } |
| 469 } | 767 } |
| 470 | 768 |
| 471 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies( | 769 void NinjaBinaryTargetWriter::WriteOrderOnlyDependencies( |
| 472 const UniqueVector<const Target*>& non_linkable_deps) { | 770 const UniqueVector<const Target*>& non_linkable_deps) { |
| 473 if (!non_linkable_deps.empty()) { | 771 if (!non_linkable_deps.empty()) { |
| 474 out_ << " ||"; | 772 out_ << " ||"; |
| 475 | 773 |
| 476 // Non-linkable targets. | 774 // Non-linkable targets. |
| 477 for (const auto& non_linkable_dep : non_linkable_deps) { | 775 for (const auto& non_linkable_dep : non_linkable_deps) { |
| 478 out_ << " "; | 776 out_ << " "; |
| 479 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); | 777 path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); |
| 480 } | 778 } |
| 481 } | 779 } |
| 482 } | 780 } |
| 483 | 781 |
| 484 bool NinjaBinaryTargetWriter::GetOutputFilesForSource( | 782 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile( |
| 485 const Target* target, | 783 Toolchain::ToolType tool_type) const { |
| 486 const SourceFile& source, | 784 // Use "obj/{dir}/{target_name}_{lang}.pch" which ends up |
| 487 Toolchain::ToolType* computed_tool_type, | 785 // looking like "obj/chrome/browser/browser.cc.pch" |
| 488 std::vector<OutputFile>* outputs) const { | 786 OutputFile ret = GetTargetOutputDirAsOutputFile(target_); |
| 489 outputs->clear(); | 787 ret.value().append(target_->label().name()); |
| 490 *computed_tool_type = Toolchain::TYPE_NONE; | 788 ret.value().push_back('_'); |
| 789 ret.value().append(GetPCHLangForToolType(tool_type)); | |
| 790 ret.value().append(".pch"); | |
| 491 | 791 |
| 492 SourceFileType file_type = GetSourceFileType(source); | 792 return ret; |
| 493 if (file_type == SOURCE_UNKNOWN) | |
| 494 return false; | |
| 495 if (file_type == SOURCE_O) { | |
| 496 // Object files just get passed to the output and not compiled. | |
| 497 outputs->push_back(OutputFile(settings_->build_settings(), source)); | |
| 498 return true; | |
| 499 } | |
| 500 | |
| 501 *computed_tool_type = | |
| 502 target->toolchain()->GetToolTypeForSourceType(file_type); | |
| 503 if (*computed_tool_type == Toolchain::TYPE_NONE) | |
| 504 return false; // No tool for this file (it's a header file or something). | |
| 505 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | |
| 506 if (!tool) | |
| 507 return false; // Tool does not apply for this toolchain.file. | |
| 508 | |
| 509 // Figure out what output(s) this compiler produces. | |
| 510 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
| 511 target, source, tool->outputs(), outputs); | |
| 512 return !outputs->empty(); | |
| 513 } | 793 } |
| OLD | NEW |