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 <sstream>
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "tools/gn/ninja_binary_target_writer.h"
9#include "tools/gn/target.h"
10#include "tools/gn/test_with_scope.h"
11
12TEST(NinjaBinaryTargetWriter, SourceSet) {
13  TestWithScope setup;
14  Err err;
15
16  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
17  setup.settings()->set_target_os(Settings::WIN);
18
19  Target target(setup.settings(), Label(SourceDir("//foo/"), "bar"));
20  target.set_output_type(Target::SOURCE_SET);
21  target.visibility().SetPublic();
22  target.sources().push_back(SourceFile("//foo/input1.cc"));
23  target.sources().push_back(SourceFile("//foo/input2.cc"));
24  // Also test object files, which should be just passed through to the
25  // dependents to link.
26  target.sources().push_back(SourceFile("//foo/input3.o"));
27  target.sources().push_back(SourceFile("//foo/input4.obj"));
28  target.SetToolchain(setup.toolchain());
29  ASSERT_TRUE(target.OnResolved(&err));
30
31  // Source set itself.
32  {
33    std::ostringstream out;
34    NinjaBinaryTargetWriter writer(&target, out);
35    writer.Run();
36
37    const char expected[] =
38        "defines =\n"
39        "include_dirs =\n"
40        "cflags =\n"
41        "cflags_c =\n"
42        "cflags_cc =\n"
43        "cflags_objc =\n"
44        "cflags_objcc =\n"
45        "root_out_dir = .\n"
46        "target_out_dir = obj/foo\n"
47        "target_output_name = bar\n"
48        "\n"
49        "build obj/foo/bar.input1.o: cxx ../../foo/input1.cc\n"
50        "build obj/foo/bar.input2.o: cxx ../../foo/input2.cc\n"
51        "\n"
52        "build obj/foo/bar.stamp: stamp obj/foo/bar.input1.o "
53            "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n";
54    std::string out_str = out.str();
55    EXPECT_EQ(expected, out_str);
56  }
57
58  // A shared library that depends on the source set.
59  Target shlib_target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
60  shlib_target.set_output_type(Target::SHARED_LIBRARY);
61  shlib_target.public_deps().push_back(LabelTargetPair(&target));
62  shlib_target.SetToolchain(setup.toolchain());
63  ASSERT_TRUE(shlib_target.OnResolved(&err));
64
65  {
66    std::ostringstream out;
67    NinjaBinaryTargetWriter writer(&shlib_target, out);
68    writer.Run();
69
70    const char expected[] =
71        "defines =\n"
72        "include_dirs =\n"
73        "cflags =\n"
74        "cflags_c =\n"
75        "cflags_cc =\n"
76        "cflags_objc =\n"
77        "cflags_objcc =\n"
78        "root_out_dir = .\n"
79        "target_out_dir = obj/foo\n"
80        "target_output_name = libshlib\n"
81        "\n"
82        "\n"
83        // Ordering of the obj files here should come out in the order
84        // specified, with the target's first, followed by the source set's, in
85        // order.
86        "build ./libshlib.so: solink obj/foo/bar.input1.o "
87            "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n"
88        "  ldflags =\n"
89        "  libs =\n"
90        "  output_extension = .so\n";
91    std::string out_str = out.str();
92    EXPECT_EQ(expected, out_str);
93  }
94
95  // A static library that depends on the source set (should not link it).
96  Target stlib_target(setup.settings(), Label(SourceDir("//foo/"), "stlib"));
97  stlib_target.set_output_type(Target::STATIC_LIBRARY);
98  stlib_target.public_deps().push_back(LabelTargetPair(&target));
99  stlib_target.SetToolchain(setup.toolchain());
100  ASSERT_TRUE(stlib_target.OnResolved(&err));
101
102  {
103    std::ostringstream out;
104    NinjaBinaryTargetWriter writer(&stlib_target, out);
105    writer.Run();
106
107    const char expected[] =
108        "defines =\n"
109        "include_dirs =\n"
110        "cflags =\n"
111        "cflags_c =\n"
112        "cflags_cc =\n"
113        "cflags_objc =\n"
114        "cflags_objcc =\n"
115        "root_out_dir = .\n"
116        "target_out_dir = obj/foo\n"
117        "target_output_name = libstlib\n"
118        "\n"
119        "\n"
120        // There are no sources so there are no params to alink. (In practice
121        // this will probably fail in the archive tool.)
122        "build obj/foo/libstlib.a: alink\n"
123        "  ldflags =\n"
124        "  libs =\n"
125        "  output_extension = \n";
126    std::string out_str = out.str();
127    EXPECT_EQ(expected, out_str);
128  }
129
130  // Make the static library 'complete', which means it should be linked.
131  stlib_target.set_complete_static_lib(true);
132  {
133    std::ostringstream out;
134    NinjaBinaryTargetWriter writer(&stlib_target, out);
135    writer.Run();
136
137    const char expected[] =
138        "defines =\n"
139        "include_dirs =\n"
140        "cflags =\n"
141        "cflags_c =\n"
142        "cflags_cc =\n"
143        "cflags_objc =\n"
144        "cflags_objcc =\n"
145        "root_out_dir = .\n"
146        "target_out_dir = obj/foo\n"
147        "target_output_name = libstlib\n"
148        "\n"
149        "\n"
150        // Ordering of the obj files here should come out in the order
151        // specified, with the target's first, followed by the source set's, in
152        // order.
153        "build obj/foo/libstlib.a: alink obj/foo/bar.input1.o "
154            "obj/foo/bar.input2.o ../../foo/input3.o ../../foo/input4.obj\n"
155        "  ldflags =\n"
156        "  libs =\n"
157        "  output_extension = \n";
158    std::string out_str = out.str();
159    EXPECT_EQ(expected, out_str);
160  }
161}
162
163// This tests that output extension overrides apply, and input dependencies
164// are applied.
165TEST(NinjaBinaryTargetWriter, ProductExtensionAndInputDeps) {
166  TestWithScope setup;
167  Err err;
168
169  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
170  setup.settings()->set_target_os(Settings::LINUX);
171
172  // An action for our library to depend on.
173  Target action(setup.settings(), Label(SourceDir("//foo/"), "action"));
174  action.set_output_type(Target::ACTION_FOREACH);
175  action.visibility().SetPublic();
176  action.SetToolchain(setup.toolchain());
177  ASSERT_TRUE(action.OnResolved(&err));
178
179  // A shared library w/ the product_extension set to a custom value.
180  Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
181  target.set_output_type(Target::SHARED_LIBRARY);
182  target.set_output_extension(std::string("so.6"));
183  target.sources().push_back(SourceFile("//foo/input1.cc"));
184  target.sources().push_back(SourceFile("//foo/input2.cc"));
185  target.public_deps().push_back(LabelTargetPair(&action));
186  target.SetToolchain(setup.toolchain());
187  ASSERT_TRUE(target.OnResolved(&err));
188
189  std::ostringstream out;
190  NinjaBinaryTargetWriter writer(&target, out);
191  writer.Run();
192
193  const char expected[] =
194      "defines =\n"
195      "include_dirs =\n"
196      "cflags =\n"
197      "cflags_c =\n"
198      "cflags_cc =\n"
199      "cflags_objc =\n"
200      "cflags_objcc =\n"
201      "root_out_dir = .\n"
202      "target_out_dir = obj/foo\n"
203      "target_output_name = libshlib\n"
204      "\n"
205      "build obj/foo/shlib.inputdeps.stamp: stamp obj/foo/action.stamp\n"
206      "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc"
207        " || obj/foo/shlib.inputdeps.stamp\n"
208      "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc"
209        " || obj/foo/shlib.inputdeps.stamp\n"
210      "\n"
211      "build ./libshlib.so.6: solink obj/foo/libshlib.input1.o "
212      // The order-only dependency here is stricly unnecessary since the
213      // sources list this as an order-only dep. See discussion in the code
214      // that writes this.
215          "obj/foo/libshlib.input2.o || obj/foo/action.stamp\n"
216      "  ldflags =\n"
217      "  libs =\n"
218      "  output_extension = .so.6\n";
219
220  std::string out_str = out.str();
221  EXPECT_EQ(expected, out_str);
222}
223
224TEST(NinjaBinaryTargetWriter, EmptyProductExtension) {
225  TestWithScope setup;
226  Err err;
227
228  setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/"));
229  setup.settings()->set_target_os(Settings::LINUX);
230
231  // This test is the same as ProductExtension, except that
232  // we call set_output_extension("") and ensure that we still get the default.
233  Target target(setup.settings(), Label(SourceDir("//foo/"), "shlib"));
234  target.set_output_type(Target::SHARED_LIBRARY);
235  target.set_output_extension(std::string());
236  target.sources().push_back(SourceFile("//foo/input1.cc"));
237  target.sources().push_back(SourceFile("//foo/input2.cc"));
238
239  target.SetToolchain(setup.toolchain());
240  ASSERT_TRUE(target.OnResolved(&err));
241
242  std::ostringstream out;
243  NinjaBinaryTargetWriter writer(&target, out);
244  writer.Run();
245
246  const char expected[] =
247      "defines =\n"
248      "include_dirs =\n"
249      "cflags =\n"
250      "cflags_c =\n"
251      "cflags_cc =\n"
252      "cflags_objc =\n"
253      "cflags_objcc =\n"
254      "root_out_dir = .\n"
255      "target_out_dir = obj/foo\n"
256      "target_output_name = libshlib\n"
257      "\n"
258      "build obj/foo/libshlib.input1.o: cxx ../../foo/input1.cc\n"
259      "build obj/foo/libshlib.input2.o: cxx ../../foo/input2.cc\n"
260      "\n"
261      "build ./libshlib.so: solink obj/foo/libshlib.input1.o "
262          "obj/foo/libshlib.input2.o\n"
263      "  ldflags =\n"
264      "  libs =\n"
265      "  output_extension = .so\n";
266
267  std::string out_str = out.str();
268  EXPECT_EQ(expected, out_str);
269}
270