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_toolchain_writer.h"
6
7#include <fstream>
8
9#include "base/files/file_util.h"
10#include "base/strings/stringize_macros.h"
11#include "tools/gn/build_settings.h"
12#include "tools/gn/filesystem_utils.h"
13#include "tools/gn/ninja_utils.h"
14#include "tools/gn/settings.h"
15#include "tools/gn/substitution_writer.h"
16#include "tools/gn/target.h"
17#include "tools/gn/toolchain.h"
18#include "tools/gn/trace.h"
19
20namespace {
21
22const char kIndent[] = "  ";
23
24}  // namespace
25
26NinjaToolchainWriter::NinjaToolchainWriter(
27    const Settings* settings,
28    const Toolchain* toolchain,
29    const std::vector<const Target*>& targets,
30    std::ostream& out)
31    : settings_(settings),
32      toolchain_(toolchain),
33      targets_(targets),
34      out_(out),
35      path_output_(settings_->build_settings()->build_dir(), ESCAPE_NINJA) {
36}
37
38NinjaToolchainWriter::~NinjaToolchainWriter() {
39}
40
41void NinjaToolchainWriter::Run() {
42  WriteRules();
43  WriteSubninjas();
44}
45
46// static
47bool NinjaToolchainWriter::RunAndWriteFile(
48    const Settings* settings,
49    const Toolchain* toolchain,
50    const std::vector<const Target*>& targets) {
51  base::FilePath ninja_file(settings->build_settings()->GetFullPath(
52      GetNinjaFileForToolchain(settings)));
53  ScopedTrace trace(TraceItem::TRACE_FILE_WRITE, FilePathToUTF8(ninja_file));
54
55  base::CreateDirectory(ninja_file.DirName());
56
57  std::ofstream file;
58  file.open(FilePathToUTF8(ninja_file).c_str(),
59            std::ios_base::out | std::ios_base::binary);
60  if (file.fail())
61    return false;
62
63  NinjaToolchainWriter gen(settings, toolchain, targets, file);
64  gen.Run();
65  return true;
66}
67
68void NinjaToolchainWriter::WriteRules() {
69  std::string rule_prefix = GetNinjaRulePrefixForToolchain(settings_);
70
71  for (int i = Toolchain::TYPE_NONE + 1; i < Toolchain::TYPE_NUMTYPES; i++) {
72    Toolchain::ToolType tool_type = static_cast<Toolchain::ToolType>(i);
73    const Tool* tool = toolchain_->GetTool(tool_type);
74    if (tool)
75      WriteToolRule(tool_type, tool, rule_prefix);
76  }
77  out_ << std::endl;
78}
79
80void NinjaToolchainWriter::WriteToolRule(const Toolchain::ToolType type,
81                                         const Tool* tool,
82                                         const std::string& rule_prefix) {
83  out_ << "rule " << rule_prefix << Toolchain::ToolTypeToName(type)
84       << std::endl;
85
86  // Rules explicitly include shell commands, so don't try to escape.
87  EscapeOptions options;
88  options.mode = ESCAPE_NINJA_PREFORMATTED_COMMAND;
89
90  CHECK(!tool->command().empty()) << "Command should not be empty";
91  WriteRulePattern("command", tool->command(), options);
92
93  WriteRulePattern("description", tool->description(), options);
94  WriteRulePattern("rspfile", tool->rspfile(), options);
95  WriteRulePattern("rspfile_content", tool->rspfile_content(), options);
96
97  if (tool->depsformat() == Tool::DEPS_GCC) {
98    // GCC-style deps require a depfile.
99    if (!tool->depfile().empty()) {
100      WriteRulePattern("depfile", tool->depfile(), options);
101      out_ << kIndent << "deps = gcc" << std::endl;
102    }
103  } else if (tool->depsformat() == Tool::DEPS_MSVC) {
104    // MSVC deps don't have a depfile.
105    out_ << kIndent << "deps = msvc" << std::endl;
106  }
107
108  // The link pool applies to linker tools. Don't count TYPE_ALINK since
109  // static libraries are not generally intensive to write.
110  if (type == Toolchain::TYPE_SOLINK || type == Toolchain::TYPE_LINK)
111    out_ << kIndent << "pool = link_pool\n";
112
113  if (tool->restat())
114    out_ << kIndent << "restat = 1" << std::endl;
115}
116
117void NinjaToolchainWriter::WriteRulePattern(const char* name,
118                                            const SubstitutionPattern& pattern,
119                                            const EscapeOptions& options) {
120  if (pattern.empty())
121    return;
122  out_ << kIndent << name << " = ";
123  SubstitutionWriter::WriteWithNinjaVariables(pattern, options, out_);
124  out_ << std::endl;
125}
126
127void NinjaToolchainWriter::WriteSubninjas() {
128  // Write subninja commands for each generated target.
129  for (size_t i = 0; i < targets_.size(); i++) {
130    OutputFile ninja_file(targets_[i]->settings()->build_settings(),
131                          GetNinjaFileForTarget(targets_[i]));
132    out_ << "subninja ";
133    path_output_.WriteFile(out_, ninja_file);
134    out_ << std::endl;
135  }
136  out_ << std::endl;
137}
138