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