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 name.append(target->settings()->IsWin() ? "obj" : "o"); 82 break; 83 84 case SOURCE_RC: 85 name.append("res"); 86 break; 87 88 case SOURCE_H: 89 case SOURCE_UNKNOWN: 90 NOTREACHED(); 91 return OutputFile(); 92 } 93 94 // Use the scheme <path>/<target>.<name>.<extension> so that all output 95 // names are unique to different targets. 96 OutputFile ret(kObjectDirNoSlash); 97 98 // Find the directory, assume it starts with two slashes, and trim to one. 99 base::StringPiece dir = FindDir(&source.value()); 100 CHECK(dir.size() >= 2 && dir[0] == '/' && dir[1] == '/') 101 << "Source file isn't in the source repo: " << dir; 102 AppendStringPiece(&ret.value(), dir.substr(1)); 103 104 ret.value().append(target->label().name()); 105 ret.value().append("."); 106 ret.value().append(name); 107 return ret; 108 } 109 110 OutputFile NinjaHelper::GetTargetOutputFile(const Target* target) const { 111 OutputFile ret; 112 if (target->output_type() == Target::NONE) { 113 NOTREACHED(); 114 return ret; 115 } 116 117 // This is prepended to the output file name. 118 const char* prefix; 119 if (!target->settings()->IsWin() && 120 (target->output_type() == Target::SHARED_LIBRARY || 121 target->output_type() == Target::STATIC_LIBRARY)) 122 prefix = "lib"; 123 else 124 prefix = ""; 125 126 const char* extension; 127 if (target->output_type() == Target::NONE || 128 target->output_type() == Target::COPY_FILES || 129 target->output_type() == Target::CUSTOM) { 130 extension = "stamp"; 131 } else { 132 extension = GetExtensionForOutputType(target->output_type(), 133 target->settings()->target_os()); 134 } 135 136 // Everything goes into the toolchain directory (which will be empty for the 137 // default toolchain, and will end in a slash otherwise). 138 ret.value().append(target->settings()->toolchain_output_subdir().value()); 139 140 // Binaries and loadable libraries go into the toolchain root. 141 if (target->output_type() == Target::EXECUTABLE || 142 target->output_type() == Target::LOADABLE_MODULE || 143 (target->settings()->IsMac() && 144 (target->output_type() == Target::SHARED_LIBRARY || 145 target->output_type() == Target::STATIC_LIBRARY)) || 146 (target->settings()->IsWin() && 147 target->output_type() == Target::SHARED_LIBRARY)) { 148 // Generate a name like "<toolchain>/<prefix><name>.<extension>". 149 ret.value().append(prefix); 150 ret.value().append(target->label().name()); 151 if (extension[0]) { 152 ret.value().push_back('.'); 153 ret.value().append(extension); 154 } 155 return ret; 156 } 157 158 // Libraries go into the library subdirectory like 159 // "<toolchain>/lib/<prefix><name>.<extension>". 160 if (target->output_type() == Target::SHARED_LIBRARY) { 161 ret.value().append(kLibDirWithSlash); 162 ret.value().append(prefix); 163 ret.value().append(target->label().name()); 164 if (extension[0]) { 165 ret.value().push_back('.'); 166 ret.value().append(extension); 167 } 168 return ret; 169 } 170 171 // Everything else goes next to the target's .ninja file like 172 // "<toolchain>/obj/<path>/<name>.<extension>". 173 ret.value().append(kObjectDirNoSlash); 174 AppendStringPiece(&ret.value(), 175 target->label().dir().SourceAbsoluteWithOneSlash()); 176 ret.value().append(target->label().name()); 177 if (extension[0]) { 178 ret.value().push_back('.'); 179 ret.value().append(extension); 180 } 181 return ret; 182 } 183