1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "tools/gn/ninja_target_writer.h" 6 7 #include <fstream> 8 #include <sstream> 9 10 #include "base/files/file_util.h" 11 #include "base/strings/string_util.h" 12 #include "tools/gn/err.h" 13 #include "tools/gn/filesystem_utils.h" 14 #include "tools/gn/ninja_action_target_writer.h" 15 #include "tools/gn/ninja_binary_target_writer.h" 16 #include "tools/gn/ninja_copy_target_writer.h" 17 #include "tools/gn/ninja_group_target_writer.h" 18 #include "tools/gn/ninja_utils.h" 19 #include "tools/gn/output_file.h" 20 #include "tools/gn/scheduler.h" 21 #include "tools/gn/string_utils.h" 22 #include "tools/gn/substitution_writer.h" 23 #include "tools/gn/target.h" 24 #include "tools/gn/trace.h" 25 26 NinjaTargetWriter::NinjaTargetWriter(const Target* target, 27 std::ostream& out) 28 : settings_(target->settings()), 29 target_(target), 30 out_(out), 31 path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA) { 32 } 33 34 NinjaTargetWriter::~NinjaTargetWriter() { 35 } 36 37 // static 38 void NinjaTargetWriter::RunAndWriteFile(const Target* target) { 39 const Settings* settings = target->settings(); 40 41 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, 42 target->label().GetUserVisibleName(false)); 43 trace.SetToolchain(settings->toolchain_label()); 44 45 base::FilePath ninja_file(settings->build_settings()->GetFullPath( 46 GetNinjaFileForTarget(target))); 47 48 if (g_scheduler->verbose_logging()) 49 g_scheduler->Log("Writing", FilePathToUTF8(ninja_file)); 50 51 base::CreateDirectory(ninja_file.DirName()); 52 53 // It's rediculously faster to write to a string and then write that to 54 // disk in one operation than to use an fstream here. 55 std::stringstream file; 56 57 // Call out to the correct sub-type of writer. 58 if (target->output_type() == Target::COPY_FILES) { 59 NinjaCopyTargetWriter writer(target, file); 60 writer.Run(); 61 } else if (target->output_type() == Target::ACTION || 62 target->output_type() == Target::ACTION_FOREACH) { 63 NinjaActionTargetWriter writer(target, file); 64 writer.Run(); 65 } else if (target->output_type() == Target::GROUP) { 66 NinjaGroupTargetWriter writer(target, file); 67 writer.Run(); 68 } else if (target->output_type() == Target::EXECUTABLE || 69 target->output_type() == Target::STATIC_LIBRARY || 70 target->output_type() == Target::SHARED_LIBRARY || 71 target->output_type() == Target::SOURCE_SET) { 72 NinjaBinaryTargetWriter writer(target, file); 73 writer.Run(); 74 } else { 75 CHECK(0); 76 } 77 78 std::string contents = file.str(); 79 base::WriteFile(ninja_file, contents.c_str(), 80 static_cast<int>(contents.size())); 81 } 82 83 void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) { 84 bool written_anything = false; 85 86 // Target label. 87 if (bits.used[SUBSTITUTION_LABEL]) { 88 out_ << kSubstitutionNinjaNames[SUBSTITUTION_LABEL] << " = " 89 << SubstitutionWriter::GetTargetSubstitution( 90 target_, SUBSTITUTION_LABEL) 91 << std::endl; 92 written_anything = true; 93 } 94 95 // Root gen dir. 96 if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) { 97 out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_GEN_DIR] << " = " 98 << SubstitutionWriter::GetTargetSubstitution( 99 target_, SUBSTITUTION_ROOT_GEN_DIR) 100 << std::endl; 101 written_anything = true; 102 } 103 104 // Root out dir. 105 if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) { 106 out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_OUT_DIR] << " = " 107 << SubstitutionWriter::GetTargetSubstitution( 108 target_, SUBSTITUTION_ROOT_OUT_DIR) 109 << std::endl; 110 written_anything = true; 111 } 112 113 // Target gen dir. 114 if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) { 115 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_GEN_DIR] << " = " 116 << SubstitutionWriter::GetTargetSubstitution( 117 target_, SUBSTITUTION_TARGET_GEN_DIR) 118 << std::endl; 119 written_anything = true; 120 } 121 122 // Target out dir. 123 if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) { 124 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUT_DIR] << " = " 125 << SubstitutionWriter::GetTargetSubstitution( 126 target_, SUBSTITUTION_TARGET_OUT_DIR) 127 << std::endl; 128 written_anything = true; 129 } 130 131 // Target output name. 132 if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) { 133 out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUTPUT_NAME] << " = " 134 << SubstitutionWriter::GetTargetSubstitution( 135 target_, SUBSTITUTION_TARGET_OUTPUT_NAME) 136 << std::endl; 137 written_anything = true; 138 } 139 140 // If we wrote any vars, separate them from the rest of the file that follows 141 // with a blank line. 142 if (written_anything) 143 out_ << std::endl; 144 } 145 146 OutputFile NinjaTargetWriter::WriteInputDepsStampAndGetDep( 147 const std::vector<const Target*>& extra_hard_deps) const { 148 CHECK(target_->toolchain()) 149 << "Toolchain not set on target " 150 << target_->label().GetUserVisibleName(true); 151 152 // For an action (where we run a script only once) the sources are the same 153 // as the source prereqs. 154 bool list_sources_as_input_deps = (target_->output_type() == Target::ACTION); 155 156 // Actions get implicit dependencies on the script itself. 157 bool add_script_source_as_dep = 158 (target_->output_type() == Target::ACTION) || 159 (target_->output_type() == Target::ACTION_FOREACH); 160 161 if (!add_script_source_as_dep && 162 extra_hard_deps.empty() && 163 target_->inputs().empty() && 164 target_->recursive_hard_deps().empty() && 165 (!list_sources_as_input_deps || target_->sources().empty()) && 166 target_->toolchain()->deps().empty()) 167 return OutputFile(); // No input/hard deps. 168 169 // One potential optimization is if there are few input dependencies (or 170 // potentially few sources that depend on these) it's better to just write 171 // all hard deps on each sources line than have this intermediate stamp. We 172 // do the stamp file because duplicating all the order-only deps for each 173 // source file can really explode the ninja file but this won't be the most 174 // optimal thing in all cases. 175 176 OutputFile input_stamp_file( 177 RebaseSourceAbsolutePath(GetTargetOutputDir(target_).value(), 178 settings_->build_settings()->build_dir())); 179 input_stamp_file.value().append(target_->label().name()); 180 input_stamp_file.value().append(".inputdeps.stamp"); 181 182 out_ << "build "; 183 path_output_.WriteFile(out_, input_stamp_file); 184 out_ << ": " 185 << GetNinjaRulePrefixForToolchain(settings_) 186 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP); 187 188 // Script file (if applicable). 189 if (add_script_source_as_dep) { 190 out_ << " "; 191 path_output_.WriteFile(out_, target_->action_values().script()); 192 } 193 194 // Input files are order-only deps. 195 const Target::FileList& prereqs = target_->inputs(); 196 for (size_t i = 0; i < prereqs.size(); i++) { 197 out_ << " "; 198 path_output_.WriteFile(out_, prereqs[i]); 199 } 200 if (list_sources_as_input_deps) { 201 const Target::FileList& sources = target_->sources(); 202 for (size_t i = 0; i < sources.size(); i++) { 203 out_ << " "; 204 path_output_.WriteFile(out_, sources[i]); 205 } 206 } 207 208 // The different souces of input deps may duplicate some targets, so uniquify 209 // them (ordering doesn't matter for this case). 210 std::set<const Target*> unique_deps; 211 212 // Hard dependencies that are direct or indirect dependencies. 213 const std::set<const Target*>& hard_deps = target_->recursive_hard_deps(); 214 for (std::set<const Target*>::const_iterator i = hard_deps.begin(); 215 i != hard_deps.end(); ++i) { 216 unique_deps.insert(*i); 217 } 218 219 // Extra hard dependencies passed in. 220 unique_deps.insert(extra_hard_deps.begin(), extra_hard_deps.end()); 221 222 // Toolchain dependencies. These must be resolved before doing anything. 223 // This just writs all toolchain deps for simplicity. If we find that 224 // toolchains often have more than one dependency, we could consider writing 225 // a toolchain-specific stamp file and only include the stamp here. 226 const LabelTargetVector& toolchain_deps = target_->toolchain()->deps(); 227 for (size_t i = 0; i < toolchain_deps.size(); i++) 228 unique_deps.insert(toolchain_deps[i].ptr); 229 230 for (std::set<const Target*>::const_iterator i = unique_deps.begin(); 231 i != unique_deps.end(); ++i) { 232 DCHECK(!(*i)->dependency_output_file().value().empty()); 233 out_ << " "; 234 path_output_.WriteFile(out_, (*i)->dependency_output_file()); 235 } 236 237 out_ << "\n"; 238 return input_stamp_file; 239 } 240 241 void NinjaTargetWriter::WriteStampForTarget( 242 const std::vector<OutputFile>& files, 243 const std::vector<OutputFile>& order_only_deps) { 244 const OutputFile& stamp_file = target_->dependency_output_file(); 245 246 // First validate that the target's dependency is a stamp file. Otherwise, 247 // we shouldn't have gotten here! 248 CHECK(EndsWith(stamp_file.value(), ".stamp", false)) 249 << "Output should end in \".stamp\" for stamp file output. Instead got: " 250 << "\"" << stamp_file.value() << "\""; 251 252 out_ << "build "; 253 path_output_.WriteFile(out_, stamp_file); 254 255 out_ << ": " 256 << GetNinjaRulePrefixForToolchain(settings_) 257 << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP); 258 path_output_.WriteFiles(out_, files); 259 260 if (!order_only_deps.empty()) { 261 out_ << " ||"; 262 path_output_.WriteFiles(out_, order_only_deps); 263 } 264 out_ << std::endl; 265 } 266