// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "tools/gn/ninja_target_writer.h" #include #include "base/files/file_util.h" #include "base/strings/string_util.h" #include "tools/gn/err.h" #include "tools/gn/filesystem_utils.h" #include "tools/gn/ninja_action_target_writer.h" #include "tools/gn/ninja_binary_target_writer.h" #include "tools/gn/ninja_copy_target_writer.h" #include "tools/gn/ninja_group_target_writer.h" #include "tools/gn/ninja_utils.h" #include "tools/gn/output_file.h" #include "tools/gn/scheduler.h" #include "tools/gn/string_utils.h" #include "tools/gn/substitution_writer.h" #include "tools/gn/target.h" #include "tools/gn/trace.h" NinjaTargetWriter::NinjaTargetWriter(const Target* target, std::ostream& out) : settings_(target->settings()), target_(target), out_(out), path_output_(settings_->build_settings()->build_dir(), settings_->build_settings()->root_path_utf8(), ESCAPE_NINJA) { } NinjaTargetWriter::~NinjaTargetWriter() { } // static void NinjaTargetWriter::RunAndWriteFile(const Target* target) { const Settings* settings = target->settings(); ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, target->label().GetUserVisibleName(false)); trace.SetToolchain(settings->toolchain_label()); base::FilePath ninja_file(settings->build_settings()->GetFullPath( GetNinjaFileForTarget(target))); if (g_scheduler->verbose_logging()) g_scheduler->Log("Writing", FilePathToUTF8(ninja_file)); base::CreateDirectory(ninja_file.DirName()); // It's rediculously faster to write to a string and then write that to // disk in one operation than to use an fstream here. std::stringstream file; // Call out to the correct sub-type of writer. if (target->output_type() == Target::COPY_FILES) { NinjaCopyTargetWriter writer(target, file); writer.Run(); } else if (target->output_type() == Target::ACTION || target->output_type() == Target::ACTION_FOREACH) { NinjaActionTargetWriter writer(target, file); writer.Run(); } else if (target->output_type() == Target::GROUP) { NinjaGroupTargetWriter writer(target, file); writer.Run(); } else if (target->output_type() == Target::EXECUTABLE || target->output_type() == Target::STATIC_LIBRARY || target->output_type() == Target::SHARED_LIBRARY || target->output_type() == Target::SOURCE_SET) { NinjaBinaryTargetWriter writer(target, file); writer.Run(); } else { CHECK(0); } std::string contents = file.str(); base::WriteFile(ninja_file, contents.c_str(), static_cast(contents.size())); } void NinjaTargetWriter::WriteSharedVars(const SubstitutionBits& bits) { bool written_anything = false; // Target label. if (bits.used[SUBSTITUTION_LABEL]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_LABEL] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_LABEL) << std::endl; written_anything = true; } // Root gen dir. if (bits.used[SUBSTITUTION_ROOT_GEN_DIR]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_GEN_DIR] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_ROOT_GEN_DIR) << std::endl; written_anything = true; } // Root out dir. if (bits.used[SUBSTITUTION_ROOT_OUT_DIR]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_ROOT_OUT_DIR] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_ROOT_OUT_DIR) << std::endl; written_anything = true; } // Target gen dir. if (bits.used[SUBSTITUTION_TARGET_GEN_DIR]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_GEN_DIR] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_TARGET_GEN_DIR) << std::endl; written_anything = true; } // Target out dir. if (bits.used[SUBSTITUTION_TARGET_OUT_DIR]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUT_DIR] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_TARGET_OUT_DIR) << std::endl; written_anything = true; } // Target output name. if (bits.used[SUBSTITUTION_TARGET_OUTPUT_NAME]) { out_ << kSubstitutionNinjaNames[SUBSTITUTION_TARGET_OUTPUT_NAME] << " = " << SubstitutionWriter::GetTargetSubstitution( target_, SUBSTITUTION_TARGET_OUTPUT_NAME) << std::endl; written_anything = true; } // If we wrote any vars, separate them from the rest of the file that follows // with a blank line. if (written_anything) out_ << std::endl; } OutputFile NinjaTargetWriter::WriteInputDepsStampAndGetDep( const std::vector& extra_hard_deps) const { CHECK(target_->toolchain()) << "Toolchain not set on target " << target_->label().GetUserVisibleName(true); // For an action (where we run a script only once) the sources are the same // as the source prereqs. bool list_sources_as_input_deps = (target_->output_type() == Target::ACTION); // Actions get implicit dependencies on the script itself. bool add_script_source_as_dep = (target_->output_type() == Target::ACTION) || (target_->output_type() == Target::ACTION_FOREACH); if (!add_script_source_as_dep && extra_hard_deps.empty() && target_->inputs().empty() && target_->recursive_hard_deps().empty() && (!list_sources_as_input_deps || target_->sources().empty()) && target_->toolchain()->deps().empty()) return OutputFile(); // No input/hard deps. // One potential optimization is if there are few input dependencies (or // potentially few sources that depend on these) it's better to just write // all hard deps on each sources line than have this intermediate stamp. We // do the stamp file because duplicating all the order-only deps for each // source file can really explode the ninja file but this won't be the most // optimal thing in all cases. OutputFile input_stamp_file( RebasePath(GetTargetOutputDir(target_).value(), settings_->build_settings()->build_dir(), settings_->build_settings()->root_path_utf8())); input_stamp_file.value().append(target_->label().name()); input_stamp_file.value().append(".inputdeps.stamp"); out_ << "build "; path_output_.WriteFile(out_, input_stamp_file); out_ << ": " << GetNinjaRulePrefixForToolchain(settings_) << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP); // Script file (if applicable). if (add_script_source_as_dep) { out_ << " "; path_output_.WriteFile(out_, target_->action_values().script()); } // Input files are order-only deps. for (const auto& input : target_->inputs()) { out_ << " "; path_output_.WriteFile(out_, input); } if (list_sources_as_input_deps) { for (const auto& source : target_->sources()) { out_ << " "; path_output_.WriteFile(out_, source); } } // The different souces of input deps may duplicate some targets, so uniquify // them (ordering doesn't matter for this case). std::set unique_deps; // Hard dependencies that are direct or indirect dependencies. const std::set& hard_deps = target_->recursive_hard_deps(); for (const auto& dep : hard_deps) unique_deps.insert(dep); // Extra hard dependencies passed in. unique_deps.insert(extra_hard_deps.begin(), extra_hard_deps.end()); // Toolchain dependencies. These must be resolved before doing anything. // This just writs all toolchain deps for simplicity. If we find that // toolchains often have more than one dependency, we could consider writing // a toolchain-specific stamp file and only include the stamp here. const LabelTargetVector& toolchain_deps = target_->toolchain()->deps(); for (const auto& toolchain_dep : toolchain_deps) unique_deps.insert(toolchain_dep.ptr); for (const auto& dep : unique_deps) { DCHECK(!dep->dependency_output_file().value().empty()); out_ << " "; path_output_.WriteFile(out_, dep->dependency_output_file()); } out_ << "\n"; return input_stamp_file; } void NinjaTargetWriter::WriteStampForTarget( const std::vector& files, const std::vector& order_only_deps) { const OutputFile& stamp_file = target_->dependency_output_file(); // First validate that the target's dependency is a stamp file. Otherwise, // we shouldn't have gotten here! CHECK(base::EndsWith(stamp_file.value(), ".stamp", base::CompareCase::INSENSITIVE_ASCII)) << "Output should end in \".stamp\" for stamp file output. Instead got: " << "\"" << stamp_file.value() << "\""; out_ << "build "; path_output_.WriteFile(out_, stamp_file); out_ << ": " << GetNinjaRulePrefixForToolchain(settings_) << Toolchain::ToolTypeToName(Toolchain::TYPE_STAMP); path_output_.WriteFiles(out_, files); if (!order_only_deps.empty()) { out_ << " ||"; path_output_.WriteFiles(out_, order_only_deps); } out_ << std::endl; }