| // Copyright 2019 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "gn/ninja_c_binary_target_writer.h" | 
 |  | 
 | #include <stddef.h> | 
 | #include <string.h> | 
 |  | 
 | #include <cstring> | 
 | #include <set> | 
 | #include <sstream> | 
 | #include <unordered_set> | 
 |  | 
 | #include "base/strings/string_util.h" | 
 | #include "gn/c_substitution_type.h" | 
 | #include "gn/config_values_extractors.h" | 
 | #include "gn/deps_iterator.h" | 
 | #include "gn/err.h" | 
 | #include "gn/escape.h" | 
 | #include "gn/filesystem_utils.h" | 
 | #include "gn/general_tool.h" | 
 | #include "gn/ninja_target_command_util.h" | 
 | #include "gn/ninja_utils.h" | 
 | #include "gn/scheduler.h" | 
 | #include "gn/settings.h" | 
 | #include "gn/string_utils.h" | 
 | #include "gn/substitution_writer.h" | 
 | #include "gn/target.h" | 
 |  | 
 | namespace { | 
 |  | 
 | // Returns the proper escape options for writing compiler and linker flags. | 
 | EscapeOptions GetFlagOptions() { | 
 |   EscapeOptions opts; | 
 |   opts.mode = ESCAPE_NINJA_COMMAND; | 
 |   return opts; | 
 | } | 
 |  | 
 | // Returns the language-specific lang recognized by gcc’s -x flag for | 
 | // precompiled header files. | 
 | const char* GetPCHLangForToolType(const char* name) { | 
 |   if (name == CTool::kCToolCc) | 
 |     return "c-header"; | 
 |   if (name == CTool::kCToolCxx) | 
 |     return "c++-header"; | 
 |   if (name == CTool::kCToolObjC) | 
 |     return "objective-c-header"; | 
 |   if (name == CTool::kCToolObjCxx) | 
 |     return "objective-c++-header"; | 
 |   NOTREACHED() << "Not a valid PCH tool type: " << name; | 
 |   return ""; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | NinjaCBinaryTargetWriter::NinjaCBinaryTargetWriter(const Target* target, | 
 |                                                    std::ostream& out) | 
 |     : NinjaBinaryTargetWriter(target, out), | 
 |       tool_(target->toolchain()->GetToolForTargetFinalOutputAsC(target)) {} | 
 |  | 
 | NinjaCBinaryTargetWriter::~NinjaCBinaryTargetWriter() = default; | 
 |  | 
 | void NinjaCBinaryTargetWriter::Run() { | 
 |   WriteCompilerVars(); | 
 |  | 
 |   size_t num_stamp_uses = target_->sources().size(); | 
 |  | 
 |   std::vector<OutputFile> input_deps = WriteInputsStampAndGetDep( | 
 |       num_stamp_uses); | 
 |  | 
 |   // The input dependencies will be an order-only dependency. This will cause | 
 |   // Ninja to make sure the inputs are up to date before compiling this source, | 
 |   // but changes in the inputs deps won't cause the file to be recompiled. | 
 |   // | 
 |   // This is important to prevent changes in unrelated actions that are | 
 |   // upstream of this target from causing everything to be recompiled. | 
 |   // | 
 |   // Why can we get away with this rather than using implicit deps ("|", which | 
 |   // will force rebuilds when the inputs change)? For source code, the | 
 |   // computed dependencies of all headers will be computed by the compiler, | 
 |   // which will cause source rebuilds if any "real" upstream dependencies | 
 |   // change. | 
 |   // | 
 |   // If a .cc file is generated by an input dependency, Ninja will see the | 
 |   // input to the build rule doesn't exist, and that it is an output from a | 
 |   // previous step, and build the previous step first. This is a "real" | 
 |   // dependency and doesn't need | or || to express. | 
 |   // | 
 |   // The only case where this rule matters is for the first build where no .d | 
 |   // files exist, and Ninja doesn't know what that source file depends on. In | 
 |   // this case it's sufficient to ensure that the upstream dependencies are | 
 |   // built first. This is exactly what Ninja's order-only dependencies | 
 |   // expresses. | 
 |   // | 
 |   // The order only deps are referenced by each source file compile, | 
 |   // but also by PCH compiles.  The latter are annoying to count, so omit | 
 |   // them here.  This means that binary targets with a single source file | 
 |   // that also use PCH files won't have a stamp file even though having | 
 |   // one would make output ninja file size a bit lower. That's ok, binary | 
 |   // targets with a single source are rare. | 
 |   std::vector<OutputFile> order_only_deps = WriteInputDepsStampAndGetDep( | 
 |       std::vector<const Target*>(), num_stamp_uses); | 
 |  | 
 |   // For GCC builds, the .gch files are not object files, but still need to be | 
 |   // added as explicit dependencies below. The .gch output files are placed in | 
 |   // |pch_other_files|. This is to prevent linking against them. | 
 |   std::vector<OutputFile> pch_obj_files; | 
 |   std::vector<OutputFile> pch_other_files; | 
 |   WritePCHCommands(input_deps, order_only_deps, &pch_obj_files, | 
 |                    &pch_other_files); | 
 |   std::vector<OutputFile>* pch_files = | 
 |       !pch_obj_files.empty() ? &pch_obj_files : &pch_other_files; | 
 |  | 
 |   // Treat all pch output files as explicit dependencies of all | 
 |   // compiles that support them. Some notes: | 
 |   // | 
 |   //  - On Windows, the .pch file is the input to the compile, not the | 
 |   //    precompiled header's corresponding object file that we're using here. | 
 |   //    But Ninja's depslog doesn't support multiple outputs from the | 
 |   //    precompiled header compile step (it outputs both the .pch file and a | 
 |   //    corresponding .obj file). So we consistently list the .obj file and the | 
 |   //    .pch file we really need comes along with it. | 
 |   // | 
 |   //  - GCC .gch files are not object files, therefore they are not added to the | 
 |   //    object file list. | 
 |   std::vector<OutputFile> obj_files; | 
 |   std::vector<SourceFile> other_files; | 
 |   WriteSources(*pch_files, input_deps, order_only_deps, &obj_files, | 
 |                &other_files); | 
 |  | 
 |   // Link all MSVC pch object files. The vector will be empty on GCC toolchains. | 
 |   obj_files.insert(obj_files.end(), pch_obj_files.begin(), pch_obj_files.end()); | 
 |   if (!CheckForDuplicateObjectFiles(obj_files)) | 
 |     return; | 
 |  | 
 |   if (target_->output_type() == Target::SOURCE_SET) { | 
 |     WriteSourceSetStamp(obj_files); | 
 | #ifndef NDEBUG | 
 |     // Verify that the function that separately computes a source set's object | 
 |     // files match the object files just computed. | 
 |     UniqueVector<OutputFile> computed_obj; | 
 |     AddSourceSetFiles(target_, &computed_obj); | 
 |     DCHECK_EQ(obj_files.size(), computed_obj.size()); | 
 |     for (const auto& obj : obj_files) | 
 |       DCHECK_NE(static_cast<size_t>(-1), computed_obj.IndexOf(obj)); | 
 | #endif | 
 |   } else { | 
 |     WriteLinkerStuff(obj_files, other_files, input_deps); | 
 |   } | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteCompilerVars() { | 
 |   const SubstitutionBits& subst = target_->toolchain()->substitution_bits(); | 
 |  | 
 |   // Defines. | 
 |   if (subst.used.count(&CSubstitutionDefines)) { | 
 |     out_ << CSubstitutionDefines.ninja_name << " ="; | 
 |     RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines, | 
 |                                                DefineWriter(), out_); | 
 |     out_ << std::endl; | 
 |   } | 
 |  | 
 |   // Framework search path. | 
 |   if (subst.used.count(&CSubstitutionFrameworkDirs)) { | 
 |     const Tool* tool = target_->toolchain()->GetTool(CTool::kCToolLink); | 
 |  | 
 |     out_ << CSubstitutionFrameworkDirs.ninja_name << " ="; | 
 |     PathOutput framework_dirs_output( | 
 |         path_output_.current_dir(), | 
 |         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND); | 
 |     RecursiveTargetConfigToStream<SourceDir>( | 
 |         target_, &ConfigValues::framework_dirs, | 
 |         FrameworkDirsWriter(framework_dirs_output, | 
 |                             tool->framework_dir_switch()), | 
 |         out_); | 
 |     out_ << std::endl; | 
 |   } | 
 |  | 
 |   // Include directories. | 
 |   if (subst.used.count(&CSubstitutionIncludeDirs)) { | 
 |     out_ << CSubstitutionIncludeDirs.ninja_name << " ="; | 
 |     PathOutput include_path_output( | 
 |         path_output_.current_dir(), | 
 |         settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA_COMMAND); | 
 |     RecursiveTargetConfigToStream<SourceDir>( | 
 |         target_, &ConfigValues::include_dirs, | 
 |         IncludeWriter(include_path_output), out_); | 
 |     out_ << std::endl; | 
 |   } | 
 |  | 
 |   bool has_precompiled_headers = | 
 |       target_->config_values().has_precompiled_headers(); | 
 |  | 
 |   EscapeOptions opts = GetFlagOptions(); | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_S) || | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_ASM)) { | 
 |     WriteOneFlag(target_, &CSubstitutionAsmFlags, false, Tool::kToolNone, | 
 |                  &ConfigValues::asmflags, opts, path_output_, out_); | 
 |   } | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_C) || | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_CPP) || | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_M) || | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_MM)) { | 
 |     WriteOneFlag(target_, &CSubstitutionCFlags, false, Tool::kToolNone, | 
 |                  &ConfigValues::cflags, opts, path_output_, out_); | 
 |   } | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_C)) { | 
 |     WriteOneFlag(target_, &CSubstitutionCFlagsC, has_precompiled_headers, | 
 |                  CTool::kCToolCc, &ConfigValues::cflags_c, opts, path_output_, | 
 |                  out_); | 
 |   } | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_CPP)) { | 
 |     WriteOneFlag(target_, &CSubstitutionCFlagsCc, has_precompiled_headers, | 
 |                  CTool::kCToolCxx, &ConfigValues::cflags_cc, opts, path_output_, | 
 |                  out_); | 
 |   } | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_M)) { | 
 |     WriteOneFlag(target_, &CSubstitutionCFlagsObjC, has_precompiled_headers, | 
 |                  CTool::kCToolObjC, &ConfigValues::cflags_objc, opts, | 
 |                  path_output_, out_); | 
 |   } | 
 |   if (target_->source_types_used().Get(SourceFile::SOURCE_MM)) { | 
 |     WriteOneFlag(target_, &CSubstitutionCFlagsObjCc, has_precompiled_headers, | 
 |                  CTool::kCToolObjCxx, &ConfigValues::cflags_objcc, opts, | 
 |                  path_output_, out_); | 
 |   } | 
 |  | 
 |   WriteSharedVars(subst); | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WritePCHCommands( | 
 |     const std::vector<OutputFile>& input_deps, | 
 |     const std::vector<OutputFile>& order_only_deps, | 
 |     std::vector<OutputFile>* object_files, | 
 |     std::vector<OutputFile>* other_files) { | 
 |   if (!target_->config_values().has_precompiled_headers()) | 
 |     return; | 
 |  | 
 |   const CTool* tool_c = target_->toolchain()->GetToolAsC(CTool::kCToolCc); | 
 |   if (tool_c && tool_c->precompiled_header_type() != CTool::PCH_NONE && | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_C)) { | 
 |     WritePCHCommand(&CSubstitutionCFlagsC, CTool::kCToolCc, | 
 |                     tool_c->precompiled_header_type(), input_deps, | 
 |                     order_only_deps, object_files, other_files); | 
 |   } | 
 |   const CTool* tool_cxx = target_->toolchain()->GetToolAsC(CTool::kCToolCxx); | 
 |   if (tool_cxx && tool_cxx->precompiled_header_type() != CTool::PCH_NONE && | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_CPP)) { | 
 |     WritePCHCommand(&CSubstitutionCFlagsCc, CTool::kCToolCxx, | 
 |                     tool_cxx->precompiled_header_type(), input_deps, | 
 |                     order_only_deps, object_files, other_files); | 
 |   } | 
 |  | 
 |   const CTool* tool_objc = target_->toolchain()->GetToolAsC(CTool::kCToolObjC); | 
 |   if (tool_objc && tool_objc->precompiled_header_type() == CTool::PCH_GCC && | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_M)) { | 
 |     WritePCHCommand(&CSubstitutionCFlagsObjC, CTool::kCToolObjC, | 
 |                     tool_objc->precompiled_header_type(), input_deps, | 
 |                     order_only_deps, object_files, other_files); | 
 |   } | 
 |  | 
 |   const CTool* tool_objcxx = | 
 |       target_->toolchain()->GetToolAsC(CTool::kCToolObjCxx); | 
 |   if (tool_objcxx && tool_objcxx->precompiled_header_type() == CTool::PCH_GCC && | 
 |       target_->source_types_used().Get(SourceFile::SOURCE_MM)) { | 
 |     WritePCHCommand(&CSubstitutionCFlagsObjCc, CTool::kCToolObjCxx, | 
 |                     tool_objcxx->precompiled_header_type(), input_deps, | 
 |                     order_only_deps, object_files, other_files); | 
 |   } | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WritePCHCommand( | 
 |     const Substitution* flag_type, | 
 |     const char* tool_name, | 
 |     CTool::PrecompiledHeaderType header_type, | 
 |     const std::vector<OutputFile>& input_deps, | 
 |     const std::vector<OutputFile>& order_only_deps, | 
 |     std::vector<OutputFile>* object_files, | 
 |     std::vector<OutputFile>* other_files) { | 
 |   switch (header_type) { | 
 |     case CTool::PCH_MSVC: | 
 |       WriteWindowsPCHCommand(flag_type, tool_name, input_deps, order_only_deps, | 
 |                              object_files); | 
 |       break; | 
 |     case CTool::PCH_GCC: | 
 |       WriteGCCPCHCommand(flag_type, tool_name, input_deps, order_only_deps, | 
 |                          other_files); | 
 |       break; | 
 |     case CTool::PCH_NONE: | 
 |       NOTREACHED() << "Cannot write a PCH command with no PCH header type"; | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteGCCPCHCommand( | 
 |     const Substitution* flag_type, | 
 |     const char* tool_name, | 
 |     const std::vector<OutputFile>& input_deps, | 
 |     const std::vector<OutputFile>& order_only_deps, | 
 |     std::vector<OutputFile>* gch_files) { | 
 |   // Compute the pch output file (it will be language-specific). | 
 |   std::vector<OutputFile> outputs; | 
 |   GetPCHOutputFiles(target_, tool_name, &outputs); | 
 |   if (outputs.empty()) | 
 |     return; | 
 |  | 
 |   gch_files->insert(gch_files->end(), outputs.begin(), outputs.end()); | 
 |  | 
 |   std::vector<OutputFile> extra_deps; | 
 |   std::copy(input_deps.begin(), input_deps.end(), | 
 |             std::back_inserter(extra_deps)); | 
 |  | 
 |   // Build line to compile the file. | 
 |   WriteCompilerBuildLine({target_->config_values().precompiled_source()}, | 
 |                          extra_deps, order_only_deps, tool_name, outputs); | 
 |  | 
 |   // This build line needs a custom language-specific flags value. Rule-specific | 
 |   // variables are just indented underneath the rule line. | 
 |   out_ << "  " << flag_type->ninja_name << " ="; | 
 |  | 
 |   // Each substitution flag is overwritten in the target rule to replace the | 
 |   // implicitly generated -include flag with the -x <header lang> flag required | 
 |   // for .gch targets. | 
 |   EscapeOptions opts = GetFlagOptions(); | 
 |   if (tool_name == CTool::kCToolCc) { | 
 |     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_c, opts, | 
 |                                          out_); | 
 |   } else if (tool_name == CTool::kCToolCxx) { | 
 |     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_cc, | 
 |                                          opts, out_); | 
 |   } else if (tool_name == CTool::kCToolObjC) { | 
 |     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objc, | 
 |                                          opts, out_); | 
 |   } else if (tool_name == CTool::kCToolObjCxx) { | 
 |     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::cflags_objcc, | 
 |                                          opts, out_); | 
 |   } | 
 |  | 
 |   // Append the command to specify the language of the .gch file. | 
 |   out_ << " -x " << GetPCHLangForToolType(tool_name); | 
 |  | 
 |   // Write two blank lines to help separate the PCH build lines from the | 
 |   // regular source build lines. | 
 |   out_ << std::endl << std::endl; | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteWindowsPCHCommand( | 
 |     const Substitution* flag_type, | 
 |     const char* tool_name, | 
 |     const std::vector<OutputFile>& input_deps, | 
 |     const std::vector<OutputFile>& order_only_deps, | 
 |     std::vector<OutputFile>* object_files) { | 
 |   // Compute the pch output file (it will be language-specific). | 
 |   std::vector<OutputFile> outputs; | 
 |   GetPCHOutputFiles(target_, tool_name, &outputs); | 
 |   if (outputs.empty()) | 
 |     return; | 
 |  | 
 |   object_files->insert(object_files->end(), outputs.begin(), outputs.end()); | 
 |  | 
 |   std::vector<OutputFile> extra_deps; | 
 |   std::copy(input_deps.begin(), input_deps.end(), | 
 |             std::back_inserter(extra_deps)); | 
 |  | 
 |   // Build line to compile the file. | 
 |   WriteCompilerBuildLine({target_->config_values().precompiled_source()}, | 
 |                          extra_deps, order_only_deps, tool_name, outputs); | 
 |  | 
 |   // This build line needs a custom language-specific flags value. Rule-specific | 
 |   // variables are just indented underneath the rule line. | 
 |   out_ << "  " << flag_type->ninja_name << " ="; | 
 |  | 
 |   // Append the command to generate the .pch file. | 
 |   // This adds the value to the existing flag instead of overwriting it. | 
 |   out_ << " ${" << flag_type->ninja_name << "}"; | 
 |   out_ << " /Yc" << target_->config_values().precompiled_header(); | 
 |  | 
 |   // Write two blank lines to help separate the PCH build lines from the | 
 |   // regular source build lines. | 
 |   out_ << std::endl << std::endl; | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteSources( | 
 |     const std::vector<OutputFile>& pch_deps, | 
 |     const std::vector<OutputFile>& input_deps, | 
 |     const std::vector<OutputFile>& order_only_deps, | 
 |     std::vector<OutputFile>* object_files, | 
 |     std::vector<SourceFile>* other_files) { | 
 |   object_files->reserve(object_files->size() + target_->sources().size()); | 
 |  | 
 |   std::vector<OutputFile> tool_outputs;  // Prevent reallocation in loop. | 
 |   std::vector<OutputFile> deps; | 
 |   for (const auto& source : target_->sources()) { | 
 |     // Clear the vector but maintain the max capacity to prevent reallocations. | 
 |     deps.resize(0); | 
 |     const char* tool_name = Tool::kToolNone; | 
 |     if (!target_->GetOutputFilesForSource(source, &tool_name, &tool_outputs)) { | 
 |       if (source.type() == SourceFile::SOURCE_DEF) | 
 |         other_files->push_back(source); | 
 |       continue;  // No output for this source. | 
 |     } | 
 |  | 
 |  | 
 |     std::copy(input_deps.begin(), input_deps.end(), std::back_inserter(deps)); | 
 |  | 
 |     if (tool_name != Tool::kToolNone) { | 
 |       // Only include PCH deps that correspond to the tool type, for instance, | 
 |       // do not specify target_name.precompile.cc.obj (a CXX PCH file) as a dep | 
 |       // for the output of a C tool type. | 
 |       // | 
 |       // This makes the assumption that pch_deps only contains pch output files | 
 |       // with the naming scheme specified in GetWindowsPCHObjectExtension or | 
 |       // GetGCCPCHOutputExtension. | 
 |       const CTool* tool = target_->toolchain()->GetToolAsC(tool_name); | 
 |       if (tool->precompiled_header_type() != CTool::PCH_NONE) { | 
 |         for (const auto& dep : pch_deps) { | 
 |           const std::string& output_value = dep.value(); | 
 |           size_t extension_offset = FindExtensionOffset(output_value); | 
 |           if (extension_offset == std::string::npos) | 
 |             continue; | 
 |           std::string output_extension; | 
 |           if (tool->precompiled_header_type() == CTool::PCH_MSVC) { | 
 |             output_extension = GetWindowsPCHObjectExtension( | 
 |                 tool_name, output_value.substr(extension_offset - 1)); | 
 |           } else if (tool->precompiled_header_type() == CTool::PCH_GCC) { | 
 |             output_extension = GetGCCPCHOutputExtension(tool_name); | 
 |           } | 
 |           if (output_value.compare( | 
 |                   output_value.size() - output_extension.size(), | 
 |                   output_extension.size(), output_extension) == 0) { | 
 |             deps.push_back(dep); | 
 |           } | 
 |         } | 
 |       } | 
 |       WriteCompilerBuildLine({source}, deps, order_only_deps, tool_name, | 
 |                              tool_outputs); | 
 |     } | 
 |  | 
 |     // It's theoretically possible for a compiler to produce more than one | 
 |     // output, but we'll only link to the first output. | 
 |     object_files->push_back(tool_outputs[0]); | 
 |   } | 
 |   out_ << std::endl; | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteLinkerStuff( | 
 |     const std::vector<OutputFile>& object_files, | 
 |     const std::vector<SourceFile>& other_files, | 
 |     const std::vector<OutputFile>& input_deps) { | 
 |   std::vector<OutputFile> output_files; | 
 |   SubstitutionWriter::ApplyListToLinkerAsOutputFile( | 
 |       target_, tool_, tool_->outputs(), &output_files); | 
 |  | 
 |   out_ << "build"; | 
 |   path_output_.WriteFiles(out_, output_files); | 
 |  | 
 |   out_ << ": " << rule_prefix_ | 
 |        << Tool::GetToolTypeForTargetFinalOutput(target_); | 
 |  | 
 |   ClassifiedDeps classified_deps = GetClassifiedDeps(); | 
 |  | 
 |   // Object files. | 
 |   path_output_.WriteFiles(out_, object_files); | 
 |   path_output_.WriteFiles(out_, classified_deps.extra_object_files); | 
 |  | 
 |   // Dependencies. | 
 |   std::vector<OutputFile> implicit_deps; | 
 |   std::vector<OutputFile> solibs; | 
 |   for (const Target* cur : classified_deps.linkable_deps) { | 
 |     // All linkable deps should have a link output file. | 
 |     DCHECK(!cur->link_output_file().value().empty()) | 
 |         << "No link output file for " | 
 |         << target_->label().GetUserVisibleName(false); | 
 |  | 
 |     if (cur->output_type() == Target::RUST_LIBRARY || | 
 |         cur->output_type() == Target::RUST_PROC_MACRO) | 
 |       continue; | 
 |  | 
 |     if (cur->dependency_output_file().value() != | 
 |         cur->link_output_file().value()) { | 
 |       // This is a shared library with separate link and deps files. Save for | 
 |       // later. | 
 |       implicit_deps.push_back(cur->dependency_output_file()); | 
 |       solibs.push_back(cur->link_output_file()); | 
 |     } else { | 
 |       // Normal case, just link to this target. | 
 |       out_ << " "; | 
 |       path_output_.WriteFile(out_, cur->link_output_file()); | 
 |     } | 
 |   } | 
 |  | 
 |   const SourceFile* optional_def_file = nullptr; | 
 |   if (!other_files.empty()) { | 
 |     for (const SourceFile& src_file : other_files) { | 
 |       if (src_file.type() == SourceFile::SOURCE_DEF) { | 
 |         optional_def_file = &src_file; | 
 |         implicit_deps.push_back( | 
 |             OutputFile(settings_->build_settings(), src_file)); | 
 |         break;  // Only one def file is allowed. | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Libraries specified by paths. | 
 |   const OrderedSet<LibFile>& libs = target_->all_libs(); | 
 |   for (size_t i = 0; i < libs.size(); i++) { | 
 |     if (libs[i].is_source_file()) { | 
 |       implicit_deps.push_back( | 
 |           OutputFile(settings_->build_settings(), libs[i].source_file())); | 
 |     } | 
 |   } | 
 |  | 
 |   // If any target creates a framework bundle, then treat it as an implicit | 
 |   // dependency via the .stamp file. This is a pessimisation as it is not | 
 |   // always necessary to relink the current target if one of the framework | 
 |   // is regenerated, but it ensure that if one of the framework API changes, | 
 |   // any dependent target will relink it (see crbug.com/1037607). | 
 |   for (const Target* dep : classified_deps.framework_deps) { | 
 |     implicit_deps.push_back(dep->dependency_output_file()); | 
 |   } | 
 |  | 
 |   // The input dependency is only needed if there are no object files, as the | 
 |   // dependency is normally provided transitively by the source files. | 
 |   std::copy(input_deps.begin(), input_deps.end(), | 
 |             std::back_inserter(implicit_deps)); | 
 |  | 
 |   // Any C++ target which depends on a Rust .rlib has to depend on its | 
 |   // entire tree of transitive rlibs. | 
 |   std::vector<OutputFile> transitive_rustlibs; | 
 |   if (target_->IsFinal()) { | 
 |     for (const auto* dep : | 
 |          target_->rust_values().transitive_libs().GetOrdered()) { | 
 |       if (dep->output_type() == Target::RUST_LIBRARY) { | 
 |         transitive_rustlibs.push_back(dep->dependency_output_file()); | 
 |         implicit_deps.push_back(dep->dependency_output_file()); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Append implicit dependencies collected above. | 
 |   if (!implicit_deps.empty()) { | 
 |     out_ << " |"; | 
 |     path_output_.WriteFiles(out_, implicit_deps); | 
 |   } | 
 |  | 
 |   // Append data dependencies as order-only dependencies. | 
 |   // | 
 |   // This will include data dependencies and input dependencies (like when | 
 |   // this target depends on an action). Having the data dependencies in this | 
 |   // list ensures that the data is available at runtime when the user builds | 
 |   // this target. | 
 |   // | 
 |   // The action dependencies are not strictly necessary in this case. They | 
 |   // should also have been collected via the input deps stamp that each source | 
 |   // file has for an order-only dependency, and since this target depends on | 
 |   // the sources, there is already an implicit order-only dependency. However, | 
 |   // it's extra work to separate these out and there's no disadvantage to | 
 |   // listing them again. | 
 |   WriteOrderOnlyDependencies(classified_deps.non_linkable_deps); | 
 |  | 
 |   // End of the link "build" line. | 
 |   out_ << std::endl; | 
 |  | 
 |   // The remaining things go in the inner scope of the link line. | 
 |   if (target_->output_type() == Target::EXECUTABLE || | 
 |       target_->output_type() == Target::SHARED_LIBRARY || | 
 |       target_->output_type() == Target::LOADABLE_MODULE) { | 
 |     out_ << "  ldflags ="; | 
 |     WriteLinkerFlags(out_, tool_, optional_def_file); | 
 |     out_ << std::endl; | 
 |     out_ << "  libs ="; | 
 |     WriteLibs(out_, tool_); | 
 |     out_ << std::endl; | 
 |     out_ << "  frameworks ="; | 
 |     WriteFrameworks(out_, tool_); | 
 |     out_ << std::endl; | 
 |   } else if (target_->output_type() == Target::STATIC_LIBRARY) { | 
 |     out_ << "  arflags ="; | 
 |     RecursiveTargetConfigStringsToStream(target_, &ConfigValues::arflags, | 
 |                                          GetFlagOptions(), out_); | 
 |     out_ << std::endl; | 
 |   } | 
 |   WriteOutputSubstitutions(); | 
 |   WriteLibsList("solibs", solibs); | 
 |   WriteLibsList("rlibs", transitive_rustlibs); | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteOutputSubstitutions() { | 
 |   out_ << "  output_extension = " | 
 |        << SubstitutionWriter::GetLinkerSubstitution( | 
 |               target_, tool_, &SubstitutionOutputExtension); | 
 |   out_ << std::endl; | 
 |   out_ << "  output_dir = " | 
 |        << SubstitutionWriter::GetLinkerSubstitution(target_, tool_, | 
 |                                                     &SubstitutionOutputDir); | 
 |   out_ << std::endl; | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteLibsList( | 
 |     const std::string& label, | 
 |     const std::vector<OutputFile>& libs) { | 
 |   if (libs.empty()) | 
 |     return; | 
 |  | 
 |   out_ << "  " << label << " ="; | 
 |   path_output_.WriteFiles(out_, libs); | 
 |   out_ << std::endl; | 
 | } | 
 |  | 
 | void NinjaCBinaryTargetWriter::WriteOrderOnlyDependencies( | 
 |     const UniqueVector<const Target*>& non_linkable_deps) { | 
 |   if (!non_linkable_deps.empty()) { | 
 |     out_ << " ||"; | 
 |  | 
 |     // Non-linkable targets. | 
 |     for (auto* non_linkable_dep : non_linkable_deps) { | 
 |       out_ << " "; | 
 |       path_output_.WriteFile(out_, non_linkable_dep->dependency_output_file()); | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool NinjaCBinaryTargetWriter::CheckForDuplicateObjectFiles( | 
 |     const std::vector<OutputFile>& files) const { | 
 |   std::unordered_set<std::string> set; | 
 |   for (const auto& file : files) { | 
 |     if (!set.insert(file.value()).second) { | 
 |       Err err( | 
 |           target_->defined_from(), "Duplicate object file", | 
 |           "The target " + target_->label().GetUserVisibleName(false) + | 
 |               "\ngenerates two object files with the same name:\n  " + | 
 |               file.value() + | 
 |               "\n" | 
 |               "\n" | 
 |               "It could be you accidentally have a file listed twice in the\n" | 
 |               "sources. Or, depending on how your toolchain maps sources to\n" | 
 |               "object files, two source files with the same name in different\n" | 
 |               "directories could map to the same object file.\n" | 
 |               "\n" | 
 |               "In the latter case, either rename one of the files or move one " | 
 |               "of\n" | 
 |               "the sources to a separate source_set to avoid them both being " | 
 |               "in\n" | 
 |               "the same target."); | 
 |       g_scheduler->FailWithError(err); | 
 |       return false; | 
 |     } | 
 |   } | 
 |   return true; | 
 | } |