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_binary_target_writer.h" 6 7 #include <set> 8 9 #include "base/strings/string_util.h" 10 #include "tools/gn/config_values_extractors.h" 11 #include "tools/gn/err.h" 12 #include "tools/gn/escape.h" 13 #include "tools/gn/string_utils.h" 14 15 namespace { 16 17 // Returns the proper escape options for writing compiler and linker flags. 18 EscapeOptions GetFlagOptions() { 19 EscapeOptions opts; 20 opts.mode = ESCAPE_NINJA; 21 22 // Some flag strings are actually multiple flags that expect to be just 23 // added to the command line. We assume that quoting is done by the 24 // buildfiles if it wants such things quoted. 25 opts.inhibit_quoting = true; 26 27 return opts; 28 } 29 30 struct DefineWriter { 31 DefineWriter() { 32 options.mode = ESCAPE_SHELL; 33 } 34 35 void operator()(const std::string& s, std::ostream& out) const { 36 out << " -D"; 37 EscapeStringToStream(out, s, options); 38 } 39 40 EscapeOptions options; 41 }; 42 43 struct IncludeWriter { 44 IncludeWriter(PathOutput& path_output, 45 const NinjaHelper& h) 46 : helper(h), 47 path_output_(path_output), 48 old_inhibit_quoting_(path_output.inhibit_quoting()) { 49 // Inhibit quoting since we'll put quotes around the whole thing ourselves. 50 // Since we're writing in NINJA escaping mode, this won't actually do 51 // anything, but I think we may need to change to shell-and-then-ninja 52 // escaping for this in the future. 53 path_output_.set_inhibit_quoting(true); 54 } 55 ~IncludeWriter() { 56 path_output_.set_inhibit_quoting(old_inhibit_quoting_); 57 } 58 59 void operator()(const SourceDir& d, std::ostream& out) const { 60 out << " \"-I"; 61 // It's important not to include the trailing slash on directories or on 62 // Windows it will be a backslash and the compiler might think we're 63 // escaping the quote! 64 path_output_.WriteDir(out, d, PathOutput::DIR_NO_LAST_SLASH); 65 out << "\""; 66 } 67 68 const NinjaHelper& helper; 69 PathOutput& path_output_; 70 bool old_inhibit_quoting_; // So we can put the PathOutput back. 71 }; 72 73 Toolchain::ToolType GetToolTypeForTarget(const Target* target) { 74 switch (target->output_type()) { 75 case Target::STATIC_LIBRARY: 76 return Toolchain::TYPE_ALINK; 77 case Target::SHARED_LIBRARY: 78 return Toolchain::TYPE_SOLINK; 79 case Target::EXECUTABLE: 80 return Toolchain::TYPE_LINK; 81 default: 82 return Toolchain::TYPE_NONE; 83 } 84 } 85 86 } // namespace 87 88 NinjaBinaryTargetWriter::NinjaBinaryTargetWriter(const Target* target, 89 const Toolchain* toolchain, 90 std::ostream& out) 91 : NinjaTargetWriter(target, toolchain, out), 92 tool_type_(GetToolTypeForTarget(target)){ 93 } 94 95 NinjaBinaryTargetWriter::~NinjaBinaryTargetWriter() { 96 } 97 98 void NinjaBinaryTargetWriter::Run() { 99 WriteCompilerVars(); 100 101 std::vector<OutputFile> obj_files; 102 WriteSources(&obj_files); 103 104 if (target_->output_type() == Target::SOURCE_SET) 105 WriteSourceSetStamp(obj_files); 106 else 107 WriteLinkerStuff(obj_files); 108 } 109 110 void NinjaBinaryTargetWriter::WriteCompilerVars() { 111 // Defines. 112 out_ << "defines ="; 113 RecursiveTargetConfigToStream<std::string>(target_, &ConfigValues::defines, 114 DefineWriter(), out_); 115 out_ << std::endl; 116 117 // Include directories. 118 out_ << "includes ="; 119 RecursiveTargetConfigToStream<SourceDir>(target_, &ConfigValues::include_dirs, 120 IncludeWriter(path_output_, helper_), 121 out_); 122 123 out_ << std::endl; 124 125 // C flags and friends. 126 EscapeOptions flag_escape_options = GetFlagOptions(); 127 #define WRITE_FLAGS(name) \ 128 out_ << #name " ="; \ 129 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::name, \ 130 flag_escape_options, out_); \ 131 out_ << std::endl; 132 133 WRITE_FLAGS(cflags) 134 WRITE_FLAGS(cflags_c) 135 WRITE_FLAGS(cflags_cc) 136 WRITE_FLAGS(cflags_objc) 137 WRITE_FLAGS(cflags_objcc) 138 139 #undef WRITE_FLAGS 140 141 out_ << std::endl; 142 } 143 144 void NinjaBinaryTargetWriter::WriteSources( 145 std::vector<OutputFile>* object_files) { 146 const Target::FileList& sources = target_->sources(); 147 object_files->reserve(sources.size()); 148 149 std::string implicit_deps = GetSourcesImplicitDeps(); 150 151 for (size_t i = 0; i < sources.size(); i++) { 152 const SourceFile& input_file = sources[i]; 153 154 SourceFileType input_file_type = GetSourceFileType(input_file, 155 settings_->target_os()); 156 if (input_file_type == SOURCE_UNKNOWN) 157 continue; // Skip unknown file types. 158 std::string command = 159 helper_.GetRuleForSourceType(settings_, input_file_type); 160 if (command.empty()) 161 continue; // Skip files not needing compilation. 162 163 OutputFile output_file = helper_.GetOutputFileForSource( 164 target_, input_file, input_file_type); 165 object_files->push_back(output_file); 166 167 out_ << "build "; 168 path_output_.WriteFile(out_, output_file); 169 out_ << ": " << command << " "; 170 path_output_.WriteFile(out_, input_file); 171 out_ << implicit_deps << std::endl; 172 } 173 out_ << std::endl; 174 } 175 176 void NinjaBinaryTargetWriter::WriteLinkerStuff( 177 const std::vector<OutputFile>& object_files) { 178 // Manifest file on Windows. 179 // TODO(brettw) this seems not to be necessary for static libs, skip in 180 // that case? 181 OutputFile windows_manifest; 182 if (settings_->IsWin()) { 183 windows_manifest.value().assign(helper_.GetTargetOutputDir(target_)); 184 windows_manifest.value().append(target_->label().name()); 185 windows_manifest.value().append(".intermediate.manifest"); 186 out_ << "manifests = "; 187 path_output_.WriteFile(out_, windows_manifest); 188 out_ << std::endl; 189 } 190 191 const Toolchain::Tool& tool = toolchain_->GetTool(tool_type_); 192 WriteLinkerFlags(tool, windows_manifest); 193 WriteLibs(tool); 194 195 // The external output file is the one that other libs depend on. 196 OutputFile external_output_file = helper_.GetTargetOutputFile(target_); 197 198 // The internal output file is the "main thing" we think we're making. In 199 // the case of shared libraries, this is the shared library and the external 200 // output file is the import library. In other cases, the internal one and 201 // the external one are the same. 202 OutputFile internal_output_file; 203 if (target_->output_type() == Target::SHARED_LIBRARY) { 204 if (settings_->IsWin()) { 205 internal_output_file = OutputFile(target_->label().name() + ".dll"); 206 } else { 207 internal_output_file = external_output_file; 208 } 209 } else { 210 internal_output_file = external_output_file; 211 } 212 213 // In Python see "self.ninja.build(output, command, input," 214 WriteLinkCommand(external_output_file, internal_output_file, object_files); 215 216 if (target_->output_type() == Target::SHARED_LIBRARY) { 217 // The shared object name doesn't include a path. 218 out_ << " soname = "; 219 out_ << FindFilename(&internal_output_file.value()); 220 out_ << std::endl; 221 222 out_ << " lib = "; 223 path_output_.WriteFile(out_, internal_output_file); 224 out_ << std::endl; 225 226 if (settings_->IsWin()) { 227 out_ << " dll = "; 228 path_output_.WriteFile(out_, internal_output_file); 229 out_ << std::endl; 230 } 231 232 if (settings_->IsWin()) { 233 out_ << " implibflag = /IMPLIB:"; 234 path_output_.WriteFile(out_, external_output_file); 235 out_ << std::endl; 236 } 237 238 // TODO(brettw) postbuild steps. 239 if (settings_->IsMac()) 240 out_ << " postbuilds = $ && (export BUILT_PRODUCTS_DIR=/Users/brettw/prj/src/out/gn; export CONFIGURATION=Debug; export DYLIB_INSTALL_NAME_BASE=@rpath; export EXECUTABLE_NAME=libbase.dylib; export EXECUTABLE_PATH=libbase.dylib; export FULL_PRODUCT_NAME=libbase.dylib; export LD_DYLIB_INSTALL_NAME=@rpath/libbase.dylib; export MACH_O_TYPE=mh_dylib; export PRODUCT_NAME=base; export PRODUCT_TYPE=com.apple.product-type.library.dynamic; export SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk; export SRCROOT=/Users/brettw/prj/src/out/gn/../../base; export SOURCE_ROOT=\"$${SRCROOT}\"; export TARGET_BUILD_DIR=/Users/brettw/prj/src/out/gn; export TEMP_DIR=\"$${TMPDIR}\"; (cd ../../base && ../build/mac/strip_from_xcode); G=$$?; ((exit $$G) || rm -rf libbase.dylib) && exit $$G)"; 241 } 242 243 out_ << std::endl; 244 } 245 246 void NinjaBinaryTargetWriter::WriteLinkerFlags( 247 const Toolchain::Tool& tool, 248 const OutputFile& windows_manifest) { 249 out_ << "ldflags ="; 250 251 // First the ldflags from the target and its config. 252 EscapeOptions flag_options = GetFlagOptions(); 253 RecursiveTargetConfigStringsToStream(target_, &ConfigValues::ldflags, 254 flag_options, out_); 255 256 // Followed by library search paths that have been recursively pushed 257 // through the dependency tree. 258 const OrderedSet<SourceDir> all_lib_dirs = target_->all_lib_dirs(); 259 if (!all_lib_dirs.empty()) { 260 // Since we're passing these on the command line to the linker and not 261 // to Ninja, we need to do shell escaping. 262 PathOutput lib_path_output(path_output_.current_dir(), ESCAPE_NINJA_SHELL, 263 true); 264 for (size_t i = 0; i < all_lib_dirs.size(); i++) { 265 out_ << " " << tool.lib_dir_prefix; 266 lib_path_output.WriteDir(out_, all_lib_dirs[i], 267 PathOutput::DIR_NO_LAST_SLASH); 268 } 269 } 270 271 // Append manifest flag on Windows to reference our file. 272 // HACK ERASEME BRETTW FIXME 273 if (settings_->IsWin()) { 274 out_ << " /MANIFEST /ManifestFile:"; 275 path_output_.WriteFile(out_, windows_manifest); 276 } 277 out_ << std::endl; 278 } 279 280 void NinjaBinaryTargetWriter::WriteLibs(const Toolchain::Tool& tool) { 281 out_ << "libs ="; 282 283 // Libraries that have been recursively pushed through the dependency tree. 284 EscapeOptions lib_escape_opts; 285 lib_escape_opts.mode = ESCAPE_NINJA_SHELL; 286 const OrderedSet<std::string> all_libs = target_->all_libs(); 287 const std::string framework_ending(".framework"); 288 for (size_t i = 0; i < all_libs.size(); i++) { 289 if (settings_->IsMac() && EndsWith(all_libs[i], framework_ending, false)) { 290 // Special-case libraries ending in ".framework" on Mac. Add the 291 // -framework switch and don't add the extension to the output. 292 out_ << " -framework "; 293 EscapeStringToStream(out_, 294 all_libs[i].substr(0, all_libs[i].size() - framework_ending.size()), 295 lib_escape_opts); 296 } else { 297 out_ << " " << tool.lib_prefix; 298 EscapeStringToStream(out_, all_libs[i], lib_escape_opts); 299 } 300 } 301 out_ << std::endl; 302 } 303 304 void NinjaBinaryTargetWriter::WriteLinkCommand( 305 const OutputFile& external_output_file, 306 const OutputFile& internal_output_file, 307 const std::vector<OutputFile>& object_files) { 308 out_ << "build "; 309 path_output_.WriteFile(out_, internal_output_file); 310 if (external_output_file != internal_output_file) { 311 out_ << " "; 312 path_output_.WriteFile(out_, external_output_file); 313 } 314 out_ << ": " 315 << helper_.GetRulePrefix(target_->settings()) 316 << Toolchain::ToolTypeToName(tool_type_); 317 318 std::set<OutputFile> extra_object_files; 319 std::vector<const Target*> linkable_deps; 320 std::vector<const Target*> non_linkable_deps; 321 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); 322 323 // Object files. 324 for (size_t i = 0; i < object_files.size(); i++) { 325 out_ << " "; 326 path_output_.WriteFile(out_, object_files[i]); 327 } 328 for (std::set<OutputFile>::iterator i = extra_object_files.begin(); 329 i != extra_object_files.end(); ++i) { 330 out_ << " "; 331 path_output_.WriteFile(out_, *i); 332 } 333 334 // Libs. 335 for (size_t i = 0; i < linkable_deps.size(); i++) { 336 out_ << " "; 337 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(linkable_deps[i])); 338 } 339 340 // Append data dependencies as implicit dependencies. 341 WriteImplicitDependencies(non_linkable_deps); 342 343 out_ << std::endl; 344 } 345 346 void NinjaBinaryTargetWriter::WriteSourceSetStamp( 347 const std::vector<OutputFile>& object_files) { 348 // The stamp rule for source sets is generally not used, since targets that 349 // depend on this will reference the object files directly. However, writing 350 // this rule allows the user to type the name of the target and get a build 351 // which can be convenient for development. 352 out_ << "build "; 353 path_output_.WriteFile(out_, helper_.GetTargetOutputFile(target_)); 354 out_ << ": " 355 << helper_.GetRulePrefix(target_->settings()) 356 << "stamp"; 357 358 std::set<OutputFile> extra_object_files; 359 std::vector<const Target*> linkable_deps; 360 std::vector<const Target*> non_linkable_deps; 361 GetDeps(&extra_object_files, &linkable_deps, &non_linkable_deps); 362 363 // The classifier should never put extra object files in a source set: 364 // any source sets that we depend on should appear in our non-linkable 365 // deps instead. 366 DCHECK(extra_object_files.empty()); 367 368 for (size_t i = 0; i < object_files.size(); i++) { 369 out_ << " "; 370 path_output_.WriteFile(out_, object_files[i]); 371 } 372 373 // Append data dependencies as implicit dependencies. 374 WriteImplicitDependencies(non_linkable_deps); 375 376 out_ << std::endl; 377 } 378 379 void NinjaBinaryTargetWriter::GetDeps( 380 std::set<OutputFile>* extra_object_files, 381 std::vector<const Target*>* linkable_deps, 382 std::vector<const Target*>* non_linkable_deps) const { 383 const LabelTargetVector& deps = target_->deps(); 384 const std::set<const Target*>& inherited = target_->inherited_libraries(); 385 386 // Normal deps. 387 for (size_t i = 0; i < deps.size(); i++) { 388 if (inherited.find(deps[i].ptr) != inherited.end()) 389 continue; // Don't add dupes. 390 ClassifyDependency(deps[i].ptr, extra_object_files, 391 linkable_deps, non_linkable_deps); 392 } 393 394 // Inherited libraries. 395 for (std::set<const Target*>::const_iterator i = inherited.begin(); 396 i != inherited.end(); ++i) { 397 ClassifyDependency(*i, extra_object_files, 398 linkable_deps, non_linkable_deps); 399 } 400 401 // Data deps. 402 const LabelTargetVector& datadeps = target_->datadeps(); 403 for (size_t i = 0; i < datadeps.size(); i++) 404 non_linkable_deps->push_back(datadeps[i].ptr); 405 } 406 407 void NinjaBinaryTargetWriter::ClassifyDependency( 408 const Target* dep, 409 std::set<OutputFile>* extra_object_files, 410 std::vector<const Target*>* linkable_deps, 411 std::vector<const Target*>* non_linkable_deps) const { 412 // Only these types of outputs have libraries linked into them. Child deps of 413 // static libraries get pushed up the dependency tree until one of these is 414 // reached, and source sets don't link at all. 415 bool can_link_libs = 416 (target_->output_type() == Target::EXECUTABLE || 417 target_->output_type() == Target::SHARED_LIBRARY); 418 419 if (dep->output_type() == Target::SOURCE_SET) { 420 if (target_->output_type() == Target::SOURCE_SET) { 421 // When a source set depends on another source set, add it as a data 422 // dependency so if the user says "ninja second_source_set" it will 423 // also compile the first (what you would expect) even though we'll 424 // never do anything with the first one's files. 425 non_linkable_deps->push_back(dep); 426 } else { 427 // Linking in a source set, copy its object files. 428 for (size_t i = 0; i < dep->sources().size(); i++) { 429 SourceFileType input_file_type = GetSourceFileType( 430 dep->sources()[i], dep->settings()->target_os()); 431 if (input_file_type != SOURCE_UNKNOWN && 432 input_file_type != SOURCE_H) { 433 // Note we need to specify the target as the source_set target 434 // itself, since this is used to prefix the object file name. 435 extra_object_files->insert(helper_.GetOutputFileForSource( 436 dep, dep->sources()[i], input_file_type)); 437 } 438 } 439 } 440 } else if (can_link_libs && dep->IsLinkable()) { 441 linkable_deps->push_back(dep); 442 } else { 443 non_linkable_deps->push_back(dep); 444 } 445 } 446 447 void NinjaBinaryTargetWriter::WriteImplicitDependencies( 448 const std::vector<const Target*>& non_linkable_deps) { 449 const std::vector<SourceFile>& data = target_->data(); 450 if (!non_linkable_deps.empty() || !data.empty()) { 451 out_ << " ||"; 452 453 // Non-linkable targets. 454 for (size_t i = 0; i < non_linkable_deps.size(); i++) { 455 out_ << " "; 456 path_output_.WriteFile(out_, 457 helper_.GetTargetOutputFile(non_linkable_deps[i])); 458 } 459 460 // Data files. 461 const std::vector<SourceFile>& data = target_->data(); 462 for (size_t i = 0; i < data.size(); i++) { 463 out_ << " "; 464 path_output_.WriteFile(out_, data[i]); 465 } 466 } 467 } 468