1// Copyright 2016 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 "gn/functions.h"
6#include "gn/rust_tool.h"
7#include "gn/scheduler.h"
8#include "gn/test_with_scheduler.h"
9#include "gn/test_with_scope.h"
10#include "util/test/test.h"
11
12using FunctionToolchain = TestWithScheduler;
13
14TEST_F(FunctionToolchain, NoArguments) {
15  TestWithScope setup;
16
17  // Check that creating a toolchain with no name reports an error.
18  {
19    TestParseInput input(R"(toolchain() {})");
20    ASSERT_FALSE(input.has_error());
21
22    Err err;
23    input.parsed()->Execute(setup.scope(), &err);
24    ASSERT_TRUE(err.has_error()) << err.message();
25  }
26
27  // Check that creating a toolchain with too many arguments is an error.
28  {
29    TestParseInput input(R"(toolchain("too", "many", "arguments") {})");
30    ASSERT_FALSE(input.has_error());
31
32    Err err;
33    input.parsed()->Execute(setup.scope(), &err);
34    ASSERT_TRUE(err.has_error()) << err.message();
35  }
36}
37
38TEST_F(FunctionToolchain, RuntimeOutputs) {
39  TestWithScope setup;
40
41  // These runtime outputs are a subset of the outputs so are OK.
42  {
43    TestParseInput input(
44        R"(toolchain("good") {
45          tool("link") {
46            command = "link"
47            outputs = [ "foo" ]
48            runtime_outputs = [ "foo" ]
49          }
50        })");
51    ASSERT_FALSE(input.has_error());
52
53    Err err;
54    input.parsed()->Execute(setup.scope(), &err);
55    ASSERT_FALSE(err.has_error()) << err.message();
56
57    // It should have generated a toolchain.
58    ASSERT_EQ(1u, setup.items().size());
59    const Toolchain* toolchain = setup.items()[0]->AsToolchain();
60    ASSERT_TRUE(toolchain);
61
62    // The toolchain should have a link tool with the two outputs.
63    const Tool* link = toolchain->GetTool(CTool::kCToolLink);
64    ASSERT_TRUE(link);
65    ASSERT_EQ(1u, link->outputs().list().size());
66    EXPECT_EQ("foo", link->outputs().list()[0].AsString());
67    ASSERT_EQ(1u, link->runtime_outputs().list().size());
68    EXPECT_EQ("foo", link->runtime_outputs().list()[0].AsString());
69  }
70
71  // This one is not a subset so should throw an error.
72  {
73    TestParseInput input(
74        R"(toolchain("bad") {
75          tool("link") {
76            outputs = [ "foo" ]
77            runtime_outputs = [ "bar" ]
78          }
79        })");
80    ASSERT_FALSE(input.has_error());
81
82    Err err;
83    input.parsed()->Execute(setup.scope(), &err);
84    ASSERT_TRUE(err.has_error()) << err.message();
85  }
86}
87
88TEST_F(FunctionToolchain, Rust) {
89  TestWithScope setup;
90
91  // These runtime outputs are a subset of the outputs so are OK.
92  {
93    TestParseInput input(
94        R"(toolchain("rust") {
95          tool("rust_bin") {
96            command = "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin {{rustflags}} -o {{output}} {{externs}} {{source}}"
97            description = "RUST {{output}}"
98          }
99        })");
100    ASSERT_FALSE(input.has_error());
101
102    Err err;
103    input.parsed()->Execute(setup.scope(), &err);
104    ASSERT_FALSE(err.has_error()) << err.message();
105
106    // It should have generated a toolchain.
107    ASSERT_EQ(1u, setup.items().size());
108    const Toolchain* toolchain = setup.items()[0]->AsToolchain();
109    ASSERT_TRUE(toolchain);
110
111    const Tool* rust = toolchain->GetTool(RustTool::kRsToolBin);
112    ASSERT_TRUE(rust);
113    ASSERT_EQ(rust->command().AsString(),
114              "{{rustenv}} rustc --crate-name {{crate_name}} --crate-type bin "
115              "{{rustflags}} -o {{output}} {{externs}} {{source}}");
116    ASSERT_EQ(rust->description().AsString(), "RUST {{output}}");
117  }
118}
119
120TEST_F(FunctionToolchain, Command) {
121  TestWithScope setup;
122
123  TestParseInput input(
124      R"(toolchain("missing_command") {
125        tool("cxx") {}
126      })");
127  ASSERT_FALSE(input.has_error());
128
129  Err err;
130  input.parsed()->Execute(setup.scope(), &err);
131  ASSERT_TRUE(err.has_error()) << err.message();
132}
133
134TEST_F(FunctionToolchain, CommandLauncher) {
135  TestWithScope setup;
136
137  TestParseInput input(
138      R"(toolchain("good") {
139        tool("cxx") {
140          command = "cxx"
141          command_launcher = "/usr/goma/gomacc"
142        }
143      })");
144  ASSERT_FALSE(input.has_error());
145
146  Err err;
147  input.parsed()->Execute(setup.scope(), &err);
148  ASSERT_FALSE(err.has_error()) << err.message();
149
150  // It should have generated a toolchain.
151  ASSERT_EQ(1u, setup.items().size());
152  const Toolchain* toolchain = setup.items()[0]->AsToolchain();
153  ASSERT_TRUE(toolchain);
154
155  // The toolchain should have a link tool with the two outputs.
156  const Tool* link = toolchain->GetTool(CTool::kCToolCxx);
157  ASSERT_TRUE(link);
158  EXPECT_EQ("/usr/goma/gomacc", link->command_launcher());
159}
160