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 |
| 21 namespace { | 24 namespace { |
| 22 | 25 |
| 23 // Returns the proper escape options for writing compiler and linker flags. | 26 // Returns the proper escape options for writing compiler and linker flags. |
| 24 EscapeOptions GetFlagOptions() { | 27 EscapeOptions GetFlagOptions() { |
| 25 EscapeOptions opts; | 28 EscapeOptions opts; |
| 26 opts.mode = ESCAPE_NINJA_COMMAND; | 29 opts.mode = ESCAPE_NINJA_COMMAND; |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 58 const std::string& path = path_out.str(); | 61 const std::string& path = path_out.str(); |
| 59 if (path[0] == '"') | 62 if (path[0] == '"') |
| 60 out << " \"-I" << path.substr(1); | 63 out << " \"-I" << path.substr(1); |
| 61 else | 64 else |
| 62 out << " -I" << path; | 65 out << " -I" << path; |
| 63 } | 66 } |
| 64 | 67 |
| 65 PathOutput& path_output_; | 68 PathOutput& path_output_; |
| 66 }; | 69 }; |
| 67 | 70 |
| 71 // Returns the language-specific prefix/suffix for precomiled header files. | |
| 72 const char* GetPCHLangForFlagType(SubstitutionType type) { | |
| 73 switch (type) { | |
| 74 case SUBSTITUTION_CFLAGS_C: | |
| 75 return "c"; | |
| 76 case SUBSTITUTION_CFLAGS_CC: | |
| 77 return "cc"; | |
| 78 case SUBSTITUTION_CFLAGS_OBJC: | |
| 79 return "m"; | |
| 80 case SUBSTITUTION_CFLAGS_OBJCC: | |
| 81 return "mm"; | |
| 82 default: | |
| 83 NOTREACHED() << "Not a valid language type"; | |
| 84 return ""; | |
| 85 } | |
| 86 } | |
| 87 | |
| 68 } // namespace | 88 } // namespace |
| 69 | 89 |
| 90 // Represents a set of tool types. | |
| 91 class NinjaBinaryTargetWriter::SourceFileTypeSet { | |
| 92 public: | |
| 93 SourceFileTypeSet() { | |
| 94 memset(flags_, 0, sizeof(bool) * static_cast<int>(SOURCE_NUMTYPES)); | |
| 95 } | |
| 96 | |
| 97 void Set(SourceFileType type) { | |
| 98 flags_[static_cast<int>(type)] = true; | |
| 99 } | |
| 100 bool Get(SourceFileType type) const { | |
| 101 return flags_[static_cast<int>(type)]; | |
| 102 } | |
| 103 | |
| 104 private: | |
| 105 bool flags_[static_cast<int>(SOURCE_NUMTYPES)]; | |
| 106 }; | |
| 107 | |
| 70 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, | 108 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, |
| 71 std::ostream& out) | 109 std::ostream& out) |
| 72 : NinjaTargetWriter(target, out), | 110 : NinjaTargetWriter(target, out), |
| 73 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)) { | 111 tool_(target->toolchain()->GetToolForTargetFinalOutput(target)), |
| 112 rule_prefix_(GetNinjaRulePrefixForToolchain(settings_)) { | |
| 74 } | 113 } |
| 75 | 114 |
| 76 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { | 115 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { |
| 77 } | 116 } |
| 78 | 117 |
| 79 void NinjaBinaryTargetWriter::Run() { | 118 void NinjaBinaryTargetWriter::Run() { |
| 80 WriteCompilerVars(); | 119 // Figure out what source types are needed. |
| 120 SourceFileTypeSet used_types; | |
| 121 for (const auto& source : target_->sources()) | |
| 122 used_types.Set(GetSourceFileType(source)); | |
| 81 | 123 |
| 124 WriteCompilerVars(used_types); | |
| 125 | |
| 126 // The input dependencies will be an order-only dependency. This will cause | |
| 127 // Ninja to make sure the inputs are up-to-date before compiling this source, | |
| 128 // but changes in the inputs deps won't cause the file to be recompiled. | |
| 129 // | |
| 130 // This is important to prevent changes in unrelated actions that are | |
| 131 // upstream of this target from causing everything to be recompiled | |
| 132 // | |
| 133 // Why can we get away with this rather than using implicit deps ("|", which | |
| 134 // will force rebuilds when the inputs change)? For source code, the | |
| 135 // computed dependencies of all headers will be computed by the compiler, | |
| 136 // which will cause source rebuilds if any "real" upstream dependencies | |
| 137 // change. | |
| 138 // | |
| 139 // If a .cc file is generated by an input dependency, Ninja will see the | |
| 140 // input to the build rule doesn't exist, and that it is an output from a | |
| 141 // previous step, and build the previous step first. This is a "real" | |
| 142 // dependency and doesn't need | or || to express. | |
| 143 // | |
| 144 // The only case where this rule matters is for the first build where no .d | |
| 145 // files exist, and Ninja doesn't know what that source file depends on. In | |
| 146 // this case it's sufficient to ensure that the upstream dependencies are | |
| 147 // built first. This is exactly what Ninja's order-only dependencies | |
| 148 // expresses. | |
| 149 OutputFile order_only_dep = | |
| 150 WriteInputDepsStampAndGetDep(std::vector<const Target*>()); | |
| 151 | |
| 152 std::vector<OutputFile> pch_obj_files; | |
| 153 WritePrecompiledHeaderCommands(used_types, order_only_dep, &pch_obj_files); | |
| 154 | |
| 155 // Treat all precompiled object files as explicit dependencies of all | |
| 156 // compiles. Some notes: | |
| 157 // | |
| 158 // - Technically only the language-specific one is required for any specific | |
| 159 // compile, but that's more difficult to express and the additional logic | |
| 160 // doesn't buy much reduced parallelism. Just list them all (there's | |
| 161 // usually only one anyway). | |
| 162 // | |
| 163 // - Technically the .pch file is the input to the compile, not the | |
| 164 // precompiled header's corresponding object file that we're using here. | |
| 165 // But Ninja's depslog doesn't support multiple outputs from the | |
| 166 // precompiled header compile step (it outputs both the .pch file and a | |
| 167 // corresponding .obj file). So we consistently list the .obj file and the | |
| 168 // .pch file we really need comes along with it. | |
| 82 std::vector<OutputFile> obj_files; | 169 std::vector<OutputFile> obj_files; |
| 83 std::vector<SourceFile> other_files; | 170 std::vector<SourceFile> other_files; |
| 84 WriteSources(&obj_files, &other_files); | 171 WriteSources(pch_obj_files, order_only_dep, &obj_files, &other_files); |
| 85 | 172 |
| 86 if (target_->output_type() == Target::SOURCE_SET) | 173 // Also link all pch object files. |
| 174 obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); | |
| 175 | |
| 176 if (target_->output_type() == Target::SOURCE_SET) { | |
| 87 WriteSourceSetStamp(obj_files); | 177 WriteSourceSetStamp(obj_files); |
| 88 else | 178 #ifndef NDEBUG |
| 179 // Verify that the function that separately computes a source set's object | |
| 180 // files match the object files just computed. | |
| 181 UniqueVector<OutputFile> computed_obj; | |
| 182 AddSourceSetObjectFiles(target_, &computed_obj); | |
| 183 DCHECK_EQ(obj_files.size(), computed_obj.size()); | |
| 184 for (const auto& obj : obj_files) | |
| 185 DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj)); | |
| 186 #endif | |
| 187 } else { | |
| 89 WriteLinkerStuff(obj_files, other_files); | 188 WriteLinkerStuff(obj_files, other_files); |
| 189 } | |
| 90 } | 190 } |
| 91 | 191 |
| 92 void NinjaBinaryTargetWriter::WriteCompilerVars() { | 192 void NinjaBinaryTargetWriter::WriteCompilerVars( |
| 193 const SourceFileTypeSet& used_types) { | |
| 93 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); | 194 const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); |
| 94 | 195 |
| 95 // Defines. | 196 // Defines. |
| 96 if (subst.used[SUBSTITUTION_DEFINES]) { | 197 if (subst.used[SUBSTITUTION_DEFINES]) { |
| 97 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; | 198 out_ << kSubstitutionNinjaNames[SUBSTITUTION_DEFINES] << " ="; |
| 98 RecursiveTargetConfigToStream<std::string>( | 199 RecursiveTargetConfigToStream<std::string>( |
| 99 target_, &ConfigValues::defines, DefineWriter(), out_); | 200 target_, &ConfigValues::defines, DefineWriter(), out_); |
| 100 out_ << std::endl; | 201 out_ << std::endl; |
| 101 } | 202 } |
| 102 | 203 |
| 103 // Include directories. | 204 // Include directories. |
| 104 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { | 205 if (subst.used[SUBSTITUTION_INCLUDE_DIRS]) { |
| 105 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; | 206 out_ << kSubstitutionNinjaNames[SUBSTITUTION_INCLUDE_DIRS] << " ="; |
| 106 PathOutput include_path_output( | 207 PathOutput include_path_output( |
| 107 path_output_.current_dir(), | 208 path_output_.current_dir(), |
| 108 settings_->build_settings()->root_path_utf8(), | 209 settings_->build_settings()->root_path_utf8(), |
| 109 ESCAPE_NINJA_COMMAND); | 210 ESCAPE_NINJA_COMMAND); |
| 110 RecursiveTargetConfigToStream<SourceDir>( | 211 RecursiveTargetConfigToStream<SourceDir>( |
| 111 target_, &ConfigValues::include_dirs, | 212 target_, &ConfigValues::include_dirs, |
| 112 IncludeWriter(include_path_output), out_); | 213 IncludeWriter(include_path_output), out_); |
| 113 out_ << std::endl; | 214 out_ << std::endl; |
| 114 } | 215 } |
| 115 | 216 |
| 116 // C flags and friends. | 217 // Some toolchains pass cflags to the assembler since it's the same command, |
| 117 EscapeOptions flag_escape_options = GetFlagOptions(); | 218 // and cflags_c might also be sent to the objective C compiler. |
| 118 #define WRITE_FLAGS(name, subst_enum) \ | 219 // |
| 119 if (subst.used[subst_enum]) { \ | 220 // TODO(brettw) remove the SOURCE_M from the CFLAGS_C writing once the Chrome |
| 120 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; \ | 221 // Mac build is updated not to pass cflags_c to .m files. |
| 121 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \ | 222 EscapeOptions opts = GetFlagOptions(); |
| 122 flag_escape_options, out_); \ | 223 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_CC) || |
| 123 out_ << std::endl; \ | 224 used_types.Get(SOURCE_M) || used_types.Get(SOURCE_MM) || |
| 124 } | 225 used_types.Get(SOURCE_ASM)) |
| 125 | 226 WriteOneFlag(SUBSTITUTION_CFLAGS, &ConfigValues::cflags, opts); |
| 126 WRITE_FLAGS(cflags, SUBSTITUTION_CFLAGS) | 227 if (used_types.Get(SOURCE_C) || used_types.Get(SOURCE_M) || |
| 127 WRITE_FLAGS(cflags_c, SUBSTITUTION_CFLAGS_C) | 228 used_types.Get(SOURCE_ASM)) |
| 128 WRITE_FLAGS(cflags_cc, SUBSTITUTION_CFLAGS_CC) | 229 WriteOneFlag(SUBSTITUTION_CFLAGS_C, &ConfigValues::cflags_c, opts); |
| 129 WRITE_FLAGS(cflags_objc, SUBSTITUTION_CFLAGS_OBJC) | 230 if (used_types.Get(SOURCE_CC)) |
| 130 WRITE_FLAGS(cflags_objcc, SUBSTITUTION_CFLAGS_OBJCC) | 231 WriteOneFlag(SUBSTITUTION_CFLAGS_CC, &ConfigValues::cflags_cc, opts); |
| 131 | 232 if (used_types.Get(SOURCE_M)) |
| 132 #undef WRITE_FLAGS | 233 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJC, &ConfigValues::cflags_objc, opts); |
| 234 if (used_types.Get(SOURCE_MM)) | |
| 235 WriteOneFlag(SUBSTITUTION_CFLAGS_OBJCC, &ConfigValues::cflags_objcc, opts); | |
| 133 | 236 |
| 134 WriteSharedVars(subst); | 237 WriteSharedVars(subst); |
| 135 } | 238 } |
| 136 | 239 |
| 240 void NinjaBinaryTargetWriter::WriteOneFlag( | |
| 241 SubstitutionType subst_enum, | |
| 242 const std::vector<std::string>& (ConfigValues::* getter)() const, | |
| 243 EscapeOptions flag_escape_options) { | |
| 244 if (!target_->toolchain()->substitution_bits().used[subst_enum]) | |
| 245 return; | |
| 246 | |
| 247 out_ << kSubstitutionNinjaNames[subst_enum] << " ="; | |
| 248 | |
| 249 // Write precompiled header flags. | |
| 250 if (target_->config_values().has_precompiled_headers() && | |
| 251 (subst_enum == SUBSTITUTION_CFLAGS_C || | |
| 252 subst_enum == SUBSTITUTION_CFLAGS_CC || | |
| 253 subst_enum == SUBSTITUTION_CFLAGS_OBJC || | |
| 254 subst_enum == SUBSTITUTION_CFLAGS_OBJCC)) { | |
| 255 // Name the .pch file. | |
| 256 out_ << " /Fp"; | |
| 257 path_output_.WriteFile(out_, GetWindowsPCHFile(subst_enum)); | |
| 258 | |
| 259 // Enables precompiled headers and names the .h file. It's a string rather | |
| 260 // than a file name that needs rebasing. | |
| 261 out_ << " /Yu" << target_->config_values().precompiled_header(); | |
| 262 } | |
| 263 | |
| 264 RecursiveTargetConfigStringsToStream(target_, getter, | |
| 265 flag_escape_options, out_); | |
| 266 out_ << std::endl; | |
| 267 } | |
| 268 | |
| 269 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommands( | |
| 270 const SourceFileTypeSet& used_types, | |
| 271 const OutputFile& order_only_dep, | |
| 272 std::vector<OutputFile>* object_files) { | |
| 273 if (!target_->config_values().has_precompiled_headers()) | |
| 274 return; | |
| 275 | |
| 276 if (used_types.Get(SOURCE_C)) { | |
| 277 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_C, | |
| 278 Toolchain::TYPE_CC, | |
| 279 order_only_dep, object_files); | |
| 280 } | |
| 281 if (used_types.Get(SOURCE_CC)) { | |
| 282 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_CC, | |
| 283 Toolchain::TYPE_CXX, | |
| 284 order_only_dep, object_files); | |
| 285 } | |
| 286 if (used_types.Get(SOURCE_M)) { | |
| 287 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_OBJC, | |
| 288 Toolchain::TYPE_OBJC, | |
| 289 order_only_dep, object_files); | |
| 290 } | |
| 291 if (used_types.Get(SOURCE_MM)) { | |
| 292 WritePrecompiledHeaderCommand(SUBSTITUTION_CFLAGS_OBJCC, | |
| 293 Toolchain::TYPE_OBJCXX, | |
| 294 order_only_dep, object_files); | |
| 295 } | |
| 296 } | |
| 297 | |
| 298 void NinjaBinaryTargetWriter::WritePrecompiledHeaderCommand( | |
| 299 SubstitutionType flag_type, | |
| 300 Toolchain::ToolType tool_type, | |
| 301 const OutputFile& order_only_dep, | |
| 302 std::vector<OutputFile>* object_files) { | |
| 303 // Compute the object file (it will be language-specific). | |
| 304 std::vector<OutputFile> outputs; | |
| 305 GetWindowsPCHObjectFiles(target_, flag_type, tool_type, &outputs); | |
| 306 if (outputs.empty()) | |
| 307 return; | |
| 308 object_files->insert(object_files->end(), outputs.begin(), outputs.end()); | |
| 309 | |
| 310 // Build line to compile the file. | |
| 311 WriteCompilerBuildLine(target_->config_values().precompiled_source(), | |
| 312 std::vector<OutputFile>(), order_only_dep, tool_type, | |
| 313 outputs); | |
| 314 | |
| 315 // This build line needs a custom language-specific flags value. It needs to | |
| 316 // include the switch to generate the .pch file in addition to the normal | |
| 317 // ones. Rule-specific variables are just indented underneath the rule line, | |
| 318 // and this defines the new one in terms of the old value. | |
| 319 out_ << " " << kSubstitutionNinjaNames[flag_type] << " ="; | |
| 320 out_ << " ${" << kSubstitutionNinjaNames[flag_type] << "}"; | |
| 321 | |
| 322 // Append the command to generate the .pch file. | |
| 323 out_ << " /Yc" << target_->config_values().precompiled_header(); | |
|
scottmg
2015/06/26 22:30:13
Don't you need /Fp here somewhere?
brettw
2015/06/29 21:36:10
No because this line will look like:
cflags_cc =
| |
| 324 out_ << std::endl; | |
| 325 } | |
| 326 | |
| 137 void NinjaBinaryTargetWriter::WriteSources( | 327 void NinjaBinaryTargetWriter::WriteSources( |
| 328 const std::vector<OutputFile>& extra_deps, | |
| 329 const OutputFile& order_only_dep, | |
| 138 std::vector<OutputFile>* object_files, | 330 std::vector<OutputFile>* object_files, |
| 139 std::vector<SourceFile>* other_files) { | 331 std::vector<SourceFile>* other_files) { |
| 140 object_files->reserve(target_->sources().size()); | 332 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 | 333 |
| 147 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. | 334 std::vector<OutputFile> tool_outputs; // Prevent reallocation in loop. |
| 148 for (const auto& source : target_->sources()) { | 335 for (const auto& source : target_->sources()) { |
| 149 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | 336 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; |
| 150 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { | 337 if (!GetOutputFilesForSource(target_, source, &tool_type, &tool_outputs)) { |
| 151 if (GetSourceFileType(source) == SOURCE_DEF) | 338 if (GetSourceFileType(source) == SOURCE_DEF) |
| 152 other_files->push_back(source); | 339 other_files->push_back(source); |
| 153 continue; // No output for this source. | 340 continue; // No output for this source. |
| 154 } | 341 } |
| 155 | 342 |
| 156 if (tool_type != Toolchain::TYPE_NONE) { | 343 if (tool_type != Toolchain::TYPE_NONE) { |
| 157 out_ << "build"; | 344 WriteCompilerBuildLine(source, extra_deps, order_only_dep, tool_type, |
| 158 path_output_.WriteFiles(out_, tool_outputs); | 345 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 } | 346 } |
| 193 | 347 |
| 194 // It's theoretically possible for a compiler to produce more than one | 348 // It's theoretically possible for a compiler to produce more than one |
| 195 // output, but we'll only link to the first output. | 349 // output, but we'll only link to the first output. |
| 196 object_files->push_back(tool_outputs[0]); | 350 object_files->push_back(tool_outputs[0]); |
| 197 } | 351 } |
| 198 out_ << std::endl; | 352 out_ << std::endl; |
| 199 } | 353 } |
| 200 | 354 |
| 355 void NinjaBinaryTargetWriter::WriteCompilerBuildLine( | |
| 356 const SourceFile& source, | |
| 357 const std::vector<OutputFile>& extra_deps, | |
| 358 const OutputFile& order_only_dep, | |
| 359 Toolchain::ToolType tool_type, | |
| 360 const std::vector<OutputFile>& outputs) { | |
| 361 out_ << "build"; | |
| 362 path_output_.WriteFiles(out_, outputs); | |
| 363 | |
| 364 out_ << ": " << rule_prefix_ << Toolchain::ToolTypeToName(tool_type); | |
| 365 out_ << " "; | |
| 366 path_output_.WriteFile(out_, source); | |
| 367 | |
| 368 if (!extra_deps.empty()) { | |
| 369 out_ << " |"; | |
| 370 for (const OutputFile& dep : extra_deps) { | |
| 371 out_ << " "; | |
| 372 path_output_.WriteFile(out_, dep); | |
| 373 } | |
| 374 } | |
| 375 | |
| 376 if (!order_only_dep.value().empty()) { | |
| 377 out_ << " || "; | |
| 378 path_output_.WriteFile(out_, order_only_dep); | |
| 379 } | |
| 380 out_ << std::endl; | |
| 381 } | |
| 382 | |
| 201 void NinjaBinaryTargetWriter::WriteLinkerStuff( | 383 void NinjaBinaryTargetWriter::WriteLinkerStuff( |
| 202 const std::vector<OutputFile>& object_files, | 384 const std::vector<OutputFile>& object_files, |
| 203 const std::vector<SourceFile>& other_files) { | 385 const std::vector<SourceFile>& other_files) { |
| 204 std::vector<OutputFile> output_files; | 386 std::vector<OutputFile> output_files; |
| 205 SubstitutionWriter::ApplyListToLinkerAsOutputFile( | 387 SubstitutionWriter::ApplyListToLinkerAsOutputFile( |
| 206 target_, tool_, tool_->outputs(), &output_files); | 388 target_, tool_, tool_->outputs(), &output_files); |
| 207 | 389 |
| 208 out_ << "build"; | 390 out_ << "build"; |
| 209 path_output_.WriteFiles(out_, output_files); | 391 path_output_.WriteFiles(out_, output_files); |
| 210 | 392 |
| 211 out_ << ": " | 393 out_ << ": " << rule_prefix_ |
| 212 << GetNinjaRulePrefixForToolchain(settings_) | |
| 213 << Toolchain::ToolTypeToName( | 394 << Toolchain::ToolTypeToName( |
| 214 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); | 395 target_->toolchain()->GetToolTypeForTargetFinalOutput(target_)); |
| 215 | 396 |
| 216 UniqueVector<OutputFile> extra_object_files; | 397 UniqueVector<OutputFile> extra_object_files; |
| 217 UniqueVector<const Target*> linkable_deps; | 398 UniqueVector<const Target*> linkable_deps; |
| 218 UniqueVector<const Target*> non_linkable_deps; | 399 UniqueVector<const Target*> non_linkable_deps; |
| 219 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); | 400 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); |
| 220 | 401 |
| 221 // Object files. | 402 // Object files. |
| 222 for (const auto& obj : object_files) { | 403 for (const auto& obj : object_files) { |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 435 // don't link at all. | 616 // don't link at all. |
| 436 bool can_link_libs = target_->IsFinal(); | 617 bool can_link_libs = target_->IsFinal(); |
| 437 | 618 |
| 438 if (dep->output_type() == Target::SOURCE_SET) { | 619 if (dep->output_type() == Target::SOURCE_SET) { |
| 439 // Source sets have their object files linked into final targets | 620 // Source sets have their object files linked into final targets |
| 440 // (shared libraries, executables, and complete static | 621 // (shared libraries, executables, and complete static |
| 441 // libraries). Intermediate static libraries and other source sets | 622 // libraries). Intermediate static libraries and other source sets |
| 442 // just forward the dependency, otherwise the files in the source | 623 // just forward the dependency, otherwise the files in the source |
| 443 // set can easily get linked more than once which will cause | 624 // set can easily get linked more than once which will cause |
| 444 // multiple definition errors. | 625 // multiple definition errors. |
| 445 if (can_link_libs) { | 626 if (can_link_libs) |
| 446 // Linking in a source set to an executable, shared library, or | 627 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 | 628 |
| 458 // Add the source set itself as a non-linkable dependency on the current | 629 // 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 | 630 // 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 | 631 // depends on (like data deps) are also built before the current target |
| 461 // can be complete. Otherwise, these will be skipped since this target | 632 // can be complete. Otherwise, these will be skipped since this target |
| 462 // will depend only on the source set's object files. | 633 // will depend only on the source set's object files. |
| 463 non_linkable_deps->push_back(dep); | 634 non_linkable_deps->push_back(dep); |
| 464 } else if (can_link_libs && dep->IsLinkable()) { | 635 } else if (can_link_libs && dep->IsLinkable()) { |
| 465 linkable_deps->push_back(dep); | 636 linkable_deps->push_back(dep); |
| 466 } else { | 637 } else { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 return false; // No tool for this file (it's a header file or something). | 675 return false; // No tool for this file (it's a header file or something). |
| 505 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); | 676 const Tool* tool = target->toolchain()->GetTool(*computed_tool_type); |
| 506 if (!tool) | 677 if (!tool) |
| 507 return false; // Tool does not apply for this toolchain.file. | 678 return false; // Tool does not apply for this toolchain.file. |
| 508 | 679 |
| 509 // Figure out what output(s) this compiler produces. | 680 // Figure out what output(s) this compiler produces. |
| 510 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | 681 SubstitutionWriter::ApplyListToCompilerAsOutputFile( |
| 511 target, source, tool->outputs(), outputs); | 682 target, source, tool->outputs(), outputs); |
| 512 return !outputs->empty(); | 683 return !outputs->empty(); |
| 513 } | 684 } |
| 685 | |
| 686 OutputFile NinjaBinaryTargetWriter::GetWindowsPCHFile( | |
| 687 SubstitutionType flag_type) const { | |
| 688 // Use "obj/{dir}/{target_name}.{lang}.pch" which ends up | |
| 689 // looking like "obj/chrome/browser/browser.cc.precompile.h.pch" | |
| 690 OutputFile ret = GetTargetOutputDirAsOutputFile(target_); | |
| 691 ret.value().append(target_->label().name()); | |
| 692 ret.value().push_back('.'); | |
| 693 ret.value().append(GetPCHLangForFlagType(flag_type)); | |
| 694 ret.value().append(".pch"); | |
| 695 | |
| 696 return ret; | |
| 697 } | |
| 698 | |
| 699 void NinjaBinaryTargetWriter::GetWindowsPCHObjectFiles( | |
| 700 const Target* target, | |
| 701 SubstitutionType flag_type, | |
| 702 Toolchain::ToolType tool_type, | |
| 703 std::vector<OutputFile>* outputs) const { | |
| 704 outputs->clear(); | |
| 705 | |
| 706 // Compute the tool. This must use the tool type passed in rather than the | |
| 707 // detected file type of the precompiled source file since the same | |
| 708 // precompiled source file will be used for separate C/C++ compiles. | |
| 709 const Tool* tool = target->toolchain()->GetTool(tool_type); | |
| 710 if (!tool) | |
| 711 return; | |
| 712 SubstitutionWriter::ApplyListToCompilerAsOutputFile( | |
| 713 target, target->config_values().precompiled_source(), | |
| 714 tool->outputs(), outputs); | |
| 715 | |
| 716 if (outputs->empty()) | |
| 717 return; | |
| 718 if (outputs->size() > 1) | |
| 719 outputs->resize(1); // Only link the first output from the compiler tool. | |
| 720 | |
| 721 // Need to annotate the obj files with the language type. For example: | |
| 722 // obj/foo/target_name.precompile.obj -> | |
| 723 // obj/foo/target_name.precompile.cc.obj | |
| 724 const char* lang_suffix = GetPCHLangForFlagType(flag_type); | |
| 725 std::string& output_value = (*outputs)[0].value(); | |
| 726 size_t extension_offset = FindExtensionOffset(output_value); | |
| 727 if (extension_offset == std::string::npos) { | |
| 728 NOTREACHED() << "No extension found"; | |
| 729 } else { | |
| 730 DCHECK(extension_offset >= 1); | |
| 731 DCHECK(output_value[extension_offset - 1] == '.'); | |
| 732 output_value.insert(extension_offset - 1, "."); | |
| 733 output_value.insert(extension_offset, lang_suffix); | |
| 734 } | |
| 735 } | |
| 736 | |
| 737 void NinjaBinaryTargetWriter::AddSourceSetObjectFiles( | |
| 738 const Target* source_set, | |
| 739 UniqueVector<OutputFile>* obj_files) const { | |
| 740 std::vector<OutputFile> tool_outputs; // Prevent allocation in loop. | |
| 741 SourceFileTypeSet used_types; | |
| 742 | |
| 743 // Compute object files for all sources. Only link the first output from | |
| 744 // teh tool if there are more than one. | |
| 745 for (const auto& source : source_set->sources()) { | |
| 746 Toolchain::ToolType tool_type = Toolchain::TYPE_NONE; | |
| 747 if (GetOutputFilesForSource(source_set, source, &tool_type, &tool_outputs)) | |
| 748 obj_files->push_back(tool_outputs[0]); | |
| 749 | |
| 750 used_types.Set(GetSourceFileType(source)); | |
| 751 } | |
| 752 | |
| 753 // Precompiled header object files. | |
| 754 if (source_set->config_values().has_precompiled_headers()) { | |
| 755 if (used_types.Get(SOURCE_C)) { | |
| 756 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_C, | |
| 757 Toolchain::TYPE_CC, &tool_outputs); | |
| 758 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 759 } | |
| 760 if (used_types.Get(SOURCE_CC)) { | |
| 761 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_CC, | |
| 762 Toolchain::TYPE_CXX, &tool_outputs); | |
| 763 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 764 } | |
| 765 if (used_types.Get(SOURCE_M)) { | |
| 766 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_OBJC, | |
| 767 Toolchain::TYPE_OBJC, &tool_outputs); | |
| 768 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 769 } | |
| 770 if (used_types.Get(SOURCE_MM)) { | |
| 771 GetWindowsPCHObjectFiles(source_set, SUBSTITUTION_CFLAGS_OBJCC, | |
| 772 Toolchain::TYPE_OBJCXX, &tool_outputs); | |
| 773 obj_files->Append(tool_outputs.begin(), tool_outputs.end()); | |
| 774 } | |
| 775 } | |
| 776 } | |
| OLD | NEW |