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/file_util.h" 11 #include "tools/gn/err.h" 12 #include "tools/gn/file_template.h" 13 #include "tools/gn/ninja_action_target_writer.h" 14 #include "tools/gn/ninja_binary_target_writer.h" 15 #include "tools/gn/ninja_copy_target_writer.h" 16 #include "tools/gn/ninja_group_target_writer.h" 17 #include "tools/gn/scheduler.h" 18 #include "tools/gn/string_utils.h" 19 #include "tools/gn/target.h" 20 #include "tools/gn/trace.h" 21 22 NinjaTargetWriter::NinjaTargetWriter(const Target* target, 23 const Toolchain* toolchain, 24 std::ostream& out) 25 : settings_(target->settings()), 26 target_(target), 27 toolchain_(toolchain), 28 out_(out), 29 path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA), 30 helper_(settings_->build_settings()) { 31 } 32 33 NinjaTargetWriter::~NinjaTargetWriter() { 34 } 35 36 // static 37 void NinjaTargetWriter::RunAndWriteFile(const Target* target, 38 const Toolchain* toolchain) { 39 const Settings* settings = target->settings(); 40 NinjaHelper helper(settings->build_settings()); 41 42 ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, 43 target->label().GetUserVisibleName(false)); 44 trace.SetToolchain(settings->toolchain_label()); 45 46 base::FilePath ninja_file(settings->build_settings()->GetFullPath( 47 helper.GetNinjaFileForTarget(target).GetSourceFile( 48 settings->build_settings()))); 49 50 if (g_scheduler->verbose_logging()) 51 g_scheduler->Log("Writing", FilePathToUTF8(ninja_file)); 52 53 base::CreateDirectory(ninja_file.DirName()); 54 55 // It's rediculously faster to write to a string and then write that to 56 // disk in one operation than to use an fstream here. 57 std::stringstream file; 58 59 // Call out to the correct sub-type of writer. 60 if (target->output_type() == Target::COPY_FILES) { 61 NinjaCopyTargetWriter writer(target, toolchain, file); 62 writer.Run(); 63 } else if (target->output_type() == Target::ACTION || 64 target->output_type() == Target::ACTION_FOREACH) { 65 NinjaActionTargetWriter writer(target, toolchain, file); 66 writer.Run(); 67 } else if (target->output_type() == Target::GROUP) { 68 NinjaGroupTargetWriter writer(target, toolchain, file); 69 writer.Run(); 70 } else if (target->output_type() == Target::EXECUTABLE || 71 target->output_type() == Target::STATIC_LIBRARY || 72 target->output_type() == Target::SHARED_LIBRARY || 73 target->output_type() == Target::SOURCE_SET) { 74 NinjaBinaryTargetWriter writer(target, toolchain, file); 75 writer.Run(); 76 } else { 77 CHECK(0); 78 } 79 80 std::string contents = file.str(); 81 base::WriteFile(ninja_file, contents.c_str(), 82 static_cast<int>(contents.size())); 83 } 84 85 std::string NinjaTargetWriter::WriteInputDepsStampAndGetDep( 86 const std::vector<const Target*>& extra_hard_deps) const { 87 // For an action (where we run a script only once) the sources are the same 88 // as the source prereqs. 89 bool list_sources_as_input_deps = target_->output_type() == Target::ACTION; 90 91 // Actions get implicit dependencies on the script itself. 92 bool add_script_source_as_dep = target_->output_type() == Target::ACTION || 93 target_->output_type() == Target::ACTION_FOREACH; 94 95 if (!add_script_source_as_dep && 96 extra_hard_deps.empty() && 97 target_->inputs().empty() && 98 target_->recursive_hard_deps().empty() && 99 (!list_sources_as_input_deps || target_->sources().empty())) 100 return std::string(); // No input/hard deps. 101 102 // One potential optimization is if there are few input dependencies (or 103 // potentially few sources that depend on these) it's better to just write 104 // all hard deps on each sources line than have this intermediate stamp. We 105 // do the stamp file because duplicating all the order-only deps for each 106 // source file can really explode the ninja file but this won't be the most 107 // optimal thing in all cases. 108 109 OutputFile input_stamp_file = helper_.GetTargetOutputDir(target_); 110 input_stamp_file.value().append(target_->label().name()); 111 input_stamp_file.value().append(".inputdeps.stamp"); 112 113 std::ostringstream stamp_file_stream; 114 path_output_.WriteFile(stamp_file_stream, input_stamp_file); 115 std::string stamp_file_string = stamp_file_stream.str(); 116 117 out_ << "build " << stamp_file_string << ": " + 118 helper_.GetRulePrefix(settings_) + "stamp"; 119 120 // Script file (if applicable). 121 if (add_script_source_as_dep) { 122 out_ << " "; 123 path_output_.WriteFile(out_, target_->action_values().script()); 124 } 125 126 // Input files are order-only deps. 127 const Target::FileList& prereqs = target_->inputs(); 128 for (size_t i = 0; i < prereqs.size(); i++) { 129 out_ << " "; 130 path_output_.WriteFile(out_, prereqs[i]); 131 } 132 if (list_sources_as_input_deps) { 133 const Target::FileList& sources = target_->sources(); 134 for (size_t i = 0; i < sources.size(); i++) { 135 out_ << " "; 136 path_output_.WriteFile(out_, sources[i]); 137 } 138 } 139 140 // Add on any hard deps that are direct or indirect dependencies. 141 const std::set<const Target*>& hard_deps = target_->recursive_hard_deps(); 142 for (std::set<const Target*>::const_iterator i = hard_deps.begin(); 143 i != hard_deps.end(); ++i) { 144 out_ << " "; 145 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(*i)); 146 } 147 148 // Extra hard deps passed in. 149 for (size_t i = 0; i < extra_hard_deps.size(); i++) { 150 out_ << " "; 151 path_output_.WriteFile(out_, 152 helper_.GetTargetOutputFile(extra_hard_deps[i])); 153 } 154 155 out_ << "\n"; 156 return " | " + stamp_file_string; 157 } 158 159 FileTemplate NinjaTargetWriter::GetOutputTemplate() const { 160 const Target::FileList& outputs = target_->action_values().outputs(); 161 std::vector<std::string> output_template_args; 162 for (size_t i = 0; i < outputs.size(); i++) { 163 // All outputs should be in the output dir. 164 output_template_args.push_back( 165 RemovePrefix(outputs[i].value(), 166 settings_->build_settings()->build_dir().value())); 167 } 168 return FileTemplate(target_->settings(), output_template_args); 169 } 170