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_helper.h" 6 7 #include "base/logging.h" 8 #include "tools/gn/filesystem_utils.h" 9 #include "tools/gn/string_utils.h" 10 #include "tools/gn/target.h" 11 12 namespace { 13 14 const char kLibDirWithSlash[] = "lib/"; 15 const char kObjectDirNoSlash[] = "obj"; 16 17 } // namespace 18 19 NinjaHelper::NinjaHelper(const BuildSettings* build_settings) 20 : build_settings_(build_settings) { 21 build_to_src_no_last_slash_ = build_settings->build_to_source_dir_string(); 22 if (!build_to_src_no_last_slash_.empty() && 23 build_to_src_no_last_slash_[build_to_src_no_last_slash_.size() - 1] == 24 '/') 25 build_to_src_no_last_slash_.resize(build_to_src_no_last_slash_.size() - 1); 26 27 build_to_src_system_no_last_slash_ = build_to_src_no_last_slash_; 28 ConvertPathToSystem(&build_to_src_system_no_last_slash_); 29 } 30 31 NinjaHelper::~NinjaHelper() { 32 } 33 34 std::string NinjaHelper::GetTopleveOutputDir() const { 35 return kObjectDirNoSlash; 36 } 37 38 std::string NinjaHelper::GetTargetOutputDir(const Target* target) const { 39 return kObjectDirNoSlash + target->label().dir().SourceAbsoluteWithOneSlash(); 40 } 41 42 OutputFile NinjaHelper::GetNinjaFileForTarget(const Target* target) const { 43 OutputFile ret(target->settings()->toolchain_output_subdir()); 44 ret.value().append(kObjectDirNoSlash); 45 AppendStringPiece(&ret.value(), 46 target->label().dir().SourceAbsoluteWithOneSlash()); 47 ret.value().append(target->label().name()); 48 ret.value().append(".ninja"); 49 return ret; 50 } 51 52 OutputFile NinjaHelper::GetNinjaFileForToolchain( 53 const Settings* settings) const { 54 OutputFile ret; 55 ret.value().append(settings->toolchain_output_subdir().value()); 56 ret.value().append("toolchain.ninja"); 57 return ret; 58 } 59 60 // In Python, GypPathToUniqueOutput does the qualification. The only case where 61 // the Python version doesn't qualify the name is for target outputs, which we 62 // handle in a separate function. 63 OutputFile NinjaHelper::GetOutputFileForSource( 64 const Target* target, 65 const SourceFile& source, 66 SourceFileType type) const { 67 // Extract the filename and remove the extension (keep the dot). 68 base::StringPiece filename = FindFilename(&source.value()); 69 std::string name(filename.data(), filename.size()); 70 size_t extension_offset = FindExtensionOffset(name); 71 CHECK(extension_offset != std::string::npos); 72 name.resize(extension_offset); 73 74 // Append the new extension. 75 switch (type) { 76 case SOURCE_ASM: 77 case SOURCE_C: 78 case SOURCE_CC: 79 case SOURCE_M: 80 case SOURCE_MM: 81 case SOURCE_S: 82 name.append(target->settings()->IsWin() ? "obj" : "o"); 83 break; 84 85 case SOURCE_RC: 86 name.append("res"); 87 break; 88 89 case SOURCE_H: 90 case SOURCE_UNKNOWN: 91 NOTREACHED(); 92 return OutputFile(); 93 } 94 95 // Use the scheme <path>/<target>.<name>.<extension> so that all output 96 // names are unique to different targets. 97 98 // This will look like "obj" or "toolchain_name/obj". 99 OutputFile ret(target->settings()->toolchain_output_subdir()); 100 ret.value().append(kObjectDirNoSlash); 101 102 // Find the directory, assume it starts with two slashes, and trim to one. 103 base::StringPiece dir = FindDir(&source.value()); 104 CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/') 105 << "Source file isn't in the source repo: " << dir; 106 AppendStringPiece(&ret.value(), dir.substr(1)); 107 108 ret.value().append(target->label().name()); 109 ret.value().append("."); 110 ret.value().append(name); 111 return ret; 112 } 113 114 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const { 115 OutputFile ret; 116 117 // Use the output name if given, fall back to target name if not. 118 const std::string& name = target->output_name().empty() ? 119 target->label().name() : target->output_name(); 120 121 // This is prepended to the output file name. Some platforms get "lib" 122 // prepended to library names. but be careful not to make a duplicate (e.g. 123 // some targets like "libxml" already have the "lib" in the name). 124 const char* prefix; 125 if (!target->settings()->IsWin() && 126 (target->output_type() == Target::SHARED_LIBRARY || 127 target->output_type() == Target::STATIC_LIBRARY) && 128 name.compare(0, 3, "lib") != 0) 129 prefix = "lib"; 130 else 131 prefix = ""; 132 133 const char* extension; 134 if (target->output_type() == Target::GROUP || 135 target->output_type() == Target::SOURCE_SET || 136 target->output_type() == Target::COPY_FILES || 137 target->output_type() == Target::CUSTOM) { 138 extension = "stamp"; 139 } else { 140 extension = GetExtensionForOutputType(target->output_type(), 141 target->settings()->target_os()); 142 } 143 144 // Everything goes into the toolchain directory (which will be empty for the 145 // default toolchain, and will end in a slash otherwise). 146 ret.value().append(target->settings()->toolchain_output_subdir().value()); 147 148 // Binaries and loadable libraries go into the toolchain root. 149 if (target->output_type() == Target::EXECUTABLE || 150 (target->settings()->IsMac() && 151 (target->output_type() == Target::SHARED_LIBRARY || 152 target->output_type() == Target::STATIC_LIBRARY)) || 153 (target->settings()->IsWin() && 154 target->output_type() == Target::SHARED_LIBRARY)) { 155 // Generate a name like "<toolchain>/<prefix><name>.<extension>". 156 ret.value().append(prefix); 157 ret.value().append(name); 158 if (extension[0]) { 159 ret.value().push_back('.'); 160 ret.value().append(extension); 161 } 162 return ret; 163 } 164 165 // Libraries go into the library subdirectory like 166 // "<toolchain>/lib/<prefix><name>.<extension>". 167 if (target->output_type() == Target::SHARED_LIBRARY) { 168 ret.value().append(kLibDirWithSlash); 169 ret.value().append(prefix); 170 ret.value().append(name); 171 if (extension[0]) { 172 ret.value().push_back('.'); 173 ret.value().append(extension); 174 } 175 return ret; 176 } 177 178 // Everything else goes next to the target's .ninja file like 179 // "<toolchain>/obj/<path>/<name>.<extension>". 180 ret.value().append(kObjectDirNoSlash); 181 AppendStringPiece(&ret.value(), 182 target->label().dir().SourceAbsoluteWithOneSlash()); 183 ret.value().append(prefix); 184 ret.value().append(name); 185 if (extension[0]) { 186 ret.value().push_back('.'); 187 ret.value().append(extension); 188 } 189 return ret; 190 } 191 192 std::string NinjaHelper::GetRulePrefix(const Settings* settings) const { 193 // Don't prefix the default toolchain so it looks prettier, prefix everything 194 // else. 195 if (settings->is_default()) 196 return std::string(); // Default toolchain has no prefix. 197 return settings->toolchain_label().name() + "_"; 198 } 199 200 std::string NinjaHelper::GetRuleForSourceType(const Settings* settings, 201 SourceFileType type) const { 202 // This function may be hot since it will be called for every source file 203 // in the tree. We could cache the results to avoid making a string for 204 // every invocation. 205 std::string prefix = GetRulePrefix(settings); 206 207 if (type == SOURCE_C) 208 return prefix + "cc"; 209 if (type == SOURCE_CC) 210 return prefix + "cxx"; 211 212 // TODO(brettw) asm files. 213 214 if (settings->IsMac()) { 215 if (type == SOURCE_M) 216 return prefix + "objc"; 217 if (type == SOURCE_MM) 218 return prefix + "objcxx"; 219 } 220 221 if (settings->IsWin()) { 222 if (type == SOURCE_RC) 223 return prefix + "rc"; 224 } else { 225 if (type == SOURCE_S) 226 return prefix + "cc"; // Assembly files just get compiled by CC. 227 } 228 229 return std::string(); 230 } 231