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 <algorithm> 6 #include <sstream> 7 8 #include "testing/gtest/include/gtest/gtest.h" 9 #include "tools/gn/ninja_action_target_writer.h" 10 #include "tools/gn/substitution_list.h" 11 #include "tools/gn/target.h" 12 #include "tools/gn/test_with_scope.h" 13 14 TEST(NinjaActionTargetWriter, WriteOutputFilesForBuildLine) { 15 TestWithScope setup; 16 Err err; 17 18 setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); 19 20 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); 21 target.set_output_type(Target::ACTION_FOREACH); 22 target.action_values().outputs() = SubstitutionList::MakeForTest( 23 "//out/Debug/gen/a b{{source_name_part}}.h", 24 "//out/Debug/gen/{{source_name_part}}.cc"); 25 26 target.SetToolchain(setup.toolchain()); 27 ASSERT_TRUE(target.OnResolved(&err)); 28 29 std::ostringstream out; 30 NinjaActionTargetWriter writer(&target, out); 31 32 SourceFile source("//foo/bar.in"); 33 std::vector<OutputFile> output_files; 34 writer.WriteOutputFilesForBuildLine(source, &output_files); 35 36 EXPECT_EQ(" gen/a$ bbar.h gen/bar.cc", out.str()); 37 } 38 39 // Tests an action with no sources. 40 TEST(NinjaActionTargetWriter, ActionNoSources) { 41 TestWithScope setup; 42 Err err; 43 44 setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); 45 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); 46 target.set_output_type(Target::ACTION); 47 48 target.action_values().set_script(SourceFile("//foo/script.py")); 49 target.inputs().push_back(SourceFile("//foo/included.txt")); 50 51 target.action_values().outputs() = 52 SubstitutionList::MakeForTest("//out/Debug/foo.out"); 53 54 target.SetToolchain(setup.toolchain()); 55 ASSERT_TRUE(target.OnResolved(&err)); 56 57 setup.settings()->set_target_os(Settings::LINUX); 58 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 59 "/usr/bin/python"))); 60 61 std::ostringstream out; 62 NinjaActionTargetWriter writer(&target, out); 63 writer.Run(); 64 65 const char expected[] = 66 "rule __foo_bar___rule\n" 67 " command = /usr/bin/python ../../foo/script.py\n" 68 " description = ACTION //foo:bar()\n" 69 " restat = 1\n" 70 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 71 "../../foo/included.txt\n" 72 "\n" 73 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n" 74 "\n" 75 "build obj/foo/bar.stamp: stamp foo.out\n"; 76 EXPECT_EQ(expected, out.str()); 77 } 78 79 // Makes sure that we write sources as input dependencies for actions with 80 // both sources and inputs (ACTION_FOREACH treats the sources differently). 81 TEST(NinjaActionTargetWriter, ActionWithSources) { 82 TestWithScope setup; 83 Err err; 84 85 setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); 86 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); 87 target.set_output_type(Target::ACTION); 88 89 target.action_values().set_script(SourceFile("//foo/script.py")); 90 91 target.sources().push_back(SourceFile("//foo/source.txt")); 92 target.inputs().push_back(SourceFile("//foo/included.txt")); 93 94 target.action_values().outputs() = 95 SubstitutionList::MakeForTest("//out/Debug/foo.out"); 96 97 target.SetToolchain(setup.toolchain()); 98 ASSERT_TRUE(target.OnResolved(&err)); 99 100 // Posix. 101 { 102 setup.settings()->set_target_os(Settings::LINUX); 103 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 104 "/usr/bin/python"))); 105 106 std::ostringstream out; 107 NinjaActionTargetWriter writer(&target, out); 108 writer.Run(); 109 110 const char expected_linux[] = 111 "rule __foo_bar___rule\n" 112 " command = /usr/bin/python ../../foo/script.py\n" 113 " description = ACTION //foo:bar()\n" 114 " restat = 1\n" 115 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 116 "../../foo/included.txt ../../foo/source.txt\n" 117 "\n" 118 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n" 119 "\n" 120 "build obj/foo/bar.stamp: stamp foo.out\n"; 121 EXPECT_EQ(expected_linux, out.str()); 122 } 123 124 // Windows. 125 { 126 // Note: we use forward slashes here so that the output will be the same on 127 // Linux and Windows. 128 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 129 "C:/python/python.exe"))); 130 setup.settings()->set_target_os(Settings::WIN); 131 132 std::ostringstream out; 133 NinjaActionTargetWriter writer(&target, out); 134 writer.Run(); 135 136 const char expected_win[] = 137 "rule __foo_bar___rule\n" 138 " command = C$:/python/python.exe gyp-win-tool action-wrapper environment.x86 __foo_bar___rule.$unique_name.rsp\n" 139 " description = ACTION //foo:bar()\n" 140 " restat = 1\n" 141 " rspfile = __foo_bar___rule.$unique_name.rsp\n" 142 " rspfile_content = C$:/python/python.exe ../../foo/script.py\n" 143 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 144 "../../foo/included.txt ../../foo/source.txt\n" 145 "\n" 146 "build foo.out: __foo_bar___rule | obj/foo/bar.inputdeps.stamp\n" 147 "\n" 148 "build obj/foo/bar.stamp: stamp foo.out\n"; 149 EXPECT_EQ(expected_win, out.str()); 150 } 151 } 152 153 TEST(NinjaActionTargetWriter, ForEach) { 154 TestWithScope setup; 155 Err err; 156 157 setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); 158 159 // Some dependencies that the action can depend on. Use actions for these 160 // so they have a nice platform-independent stamp file that can appear in the 161 // output (rather than having to worry about how the current platform names 162 // binaries). 163 Target dep(setup.settings(), Label(SourceDir("//foo/"), "dep")); 164 dep.set_output_type(Target::ACTION); 165 dep.visibility().SetPublic(); 166 dep.SetToolchain(setup.toolchain()); 167 ASSERT_TRUE(dep.OnResolved(&err)); 168 169 Target datadep(setup.settings(), Label(SourceDir("//foo/"), "datadep")); 170 datadep.set_output_type(Target::ACTION); 171 datadep.visibility().SetPublic(); 172 datadep.SetToolchain(setup.toolchain()); 173 ASSERT_TRUE(datadep.OnResolved(&err)); 174 175 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); 176 target.set_output_type(Target::ACTION_FOREACH); 177 target.private_deps().push_back(LabelTargetPair(&dep)); 178 target.data_deps().push_back(LabelTargetPair(&datadep)); 179 180 target.sources().push_back(SourceFile("//foo/input1.txt")); 181 target.sources().push_back(SourceFile("//foo/input2.txt")); 182 183 target.action_values().set_script(SourceFile("//foo/script.py")); 184 185 target.action_values().args() = SubstitutionList::MakeForTest( 186 "-i", 187 "{{source}}", 188 "--out=foo bar{{source_name_part}}.o"); 189 target.action_values().outputs() = SubstitutionList::MakeForTest( 190 "//out/Debug/{{source_name_part}}.out"); 191 192 target.inputs().push_back(SourceFile("//foo/included.txt")); 193 194 target.SetToolchain(setup.toolchain()); 195 ASSERT_TRUE(target.OnResolved(&err)); 196 197 // Posix. 198 { 199 setup.settings()->set_target_os(Settings::LINUX); 200 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 201 "/usr/bin/python"))); 202 203 std::ostringstream out; 204 NinjaActionTargetWriter writer(&target, out); 205 writer.Run(); 206 207 const char expected_linux[] = 208 "rule __foo_bar___rule\n" 209 " command = /usr/bin/python ../../foo/script.py -i ${in} " 210 // Escaping is different between Windows and Posix. 211 #if defined(OS_WIN) 212 "\"--out=foo$ bar${source_name_part}.o\"\n" 213 #else 214 "--out=foo\\$ bar${source_name_part}.o\n" 215 #endif 216 " description = ACTION //foo:bar()\n" 217 " restat = 1\n" 218 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 219 "../../foo/included.txt obj/foo/dep.stamp\n" 220 "\n" 221 "build input1.out: __foo_bar___rule ../../foo/input1.txt | " 222 "obj/foo/bar.inputdeps.stamp\n" 223 " source_name_part = input1\n" 224 "build input2.out: __foo_bar___rule ../../foo/input2.txt | " 225 "obj/foo/bar.inputdeps.stamp\n" 226 " source_name_part = input2\n" 227 "\n" 228 "build obj/foo/bar.stamp: " 229 "stamp input1.out input2.out || obj/foo/datadep.stamp\n"; 230 231 std::string out_str = out.str(); 232 #if defined(OS_WIN) 233 std::replace(out_str.begin(), out_str.end(), '\\', '/'); 234 #endif 235 EXPECT_EQ(expected_linux, out_str); 236 } 237 238 // Windows. 239 { 240 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 241 "C:/python/python.exe"))); 242 setup.settings()->set_target_os(Settings::WIN); 243 244 std::ostringstream out; 245 NinjaActionTargetWriter writer(&target, out); 246 writer.Run(); 247 248 const char expected_win[] = 249 "rule __foo_bar___rule\n" 250 " command = C$:/python/python.exe gyp-win-tool action-wrapper " 251 "environment.x86 __foo_bar___rule.$unique_name.rsp\n" 252 " description = ACTION //foo:bar()\n" 253 " restat = 1\n" 254 " rspfile = __foo_bar___rule.$unique_name.rsp\n" 255 " rspfile_content = C$:/python/python.exe ../../foo/script.py -i " 256 #if defined(OS_WIN) 257 "${in} \"--out=foo$ bar${source_name_part}.o\"\n" 258 #else 259 "${in} --out=foo\\$ bar${source_name_part}.o\n" 260 #endif 261 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 262 "../../foo/included.txt obj/foo/dep.stamp\n" 263 "\n" 264 "build input1.out: __foo_bar___rule ../../foo/input1.txt | " 265 "obj/foo/bar.inputdeps.stamp\n" 266 " unique_name = 0\n" 267 " source_name_part = input1\n" 268 "build input2.out: __foo_bar___rule ../../foo/input2.txt | " 269 "obj/foo/bar.inputdeps.stamp\n" 270 " unique_name = 1\n" 271 " source_name_part = input2\n" 272 "\n" 273 "build obj/foo/bar.stamp: " 274 "stamp input1.out input2.out || obj/foo/datadep.stamp\n"; 275 EXPECT_EQ(expected_win, out.str()); 276 } 277 } 278 279 TEST(NinjaActionTargetWriter, ForEachWithDepfile) { 280 TestWithScope setup; 281 Err err; 282 283 setup.build_settings()->SetBuildDir(SourceDir("//out/Debug/")); 284 Target target(setup.settings(), Label(SourceDir("//foo/"), "bar")); 285 target.set_output_type(Target::ACTION_FOREACH); 286 287 target.sources().push_back(SourceFile("//foo/input1.txt")); 288 target.sources().push_back(SourceFile("//foo/input2.txt")); 289 290 target.action_values().set_script(SourceFile("//foo/script.py")); 291 292 target.SetToolchain(setup.toolchain()); 293 ASSERT_TRUE(target.OnResolved(&err)); 294 295 SubstitutionPattern depfile; 296 ASSERT_TRUE( 297 depfile.Parse("//out/Debug/gen/{{source_name_part}}.d", NULL, &err)); 298 target.action_values().set_depfile(depfile); 299 300 target.action_values().args() = SubstitutionList::MakeForTest( 301 "-i", 302 "{{source}}", 303 "--out=foo bar{{source_name_part}}.o"); 304 target.action_values().outputs() = SubstitutionList::MakeForTest( 305 "//out/Debug/{{source_name_part}}.out"); 306 307 target.inputs().push_back(SourceFile("//foo/included.txt")); 308 309 // Posix. 310 { 311 setup.settings()->set_target_os(Settings::LINUX); 312 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 313 "/usr/bin/python"))); 314 315 std::ostringstream out; 316 NinjaActionTargetWriter writer(&target, out); 317 writer.Run(); 318 319 const char expected_linux[] = 320 "rule __foo_bar___rule\n" 321 " command = /usr/bin/python ../../foo/script.py -i ${in} " 322 #if defined(OS_WIN) 323 "\"--out=foo$ bar${source_name_part}.o\"\n" 324 #else 325 "--out=foo\\$ bar${source_name_part}.o\n" 326 #endif 327 " description = ACTION //foo:bar()\n" 328 " restat = 1\n" 329 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 330 "../../foo/included.txt\n" 331 "\n" 332 "build input1.out: __foo_bar___rule ../../foo/input1.txt" 333 " | obj/foo/bar.inputdeps.stamp\n" 334 " source_name_part = input1\n" 335 " depfile = gen/input1.d\n" 336 "build input2.out: __foo_bar___rule ../../foo/input2.txt" 337 " | obj/foo/bar.inputdeps.stamp\n" 338 " source_name_part = input2\n" 339 " depfile = gen/input2.d\n" 340 "\n" 341 "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; 342 EXPECT_EQ(expected_linux, out.str()); 343 } 344 345 // Windows. 346 { 347 setup.build_settings()->set_python_path(base::FilePath(FILE_PATH_LITERAL( 348 "C:/python/python.exe"))); 349 setup.settings()->set_target_os(Settings::WIN); 350 351 std::ostringstream out; 352 NinjaActionTargetWriter writer(&target, out); 353 writer.Run(); 354 355 const char expected_win[] = 356 "rule __foo_bar___rule\n" 357 " command = C$:/python/python.exe gyp-win-tool action-wrapper " 358 "environment.x86 __foo_bar___rule.$unique_name.rsp\n" 359 " description = ACTION //foo:bar()\n" 360 " restat = 1\n" 361 " rspfile = __foo_bar___rule.$unique_name.rsp\n" 362 " rspfile_content = C$:/python/python.exe ../../foo/script.py -i " 363 #if defined(OS_WIN) 364 "${in} \"--out=foo$ bar${source_name_part}.o\"\n" 365 #else 366 "${in} --out=foo\\$ bar${source_name_part}.o\n" 367 #endif 368 "build obj/foo/bar.inputdeps.stamp: stamp ../../foo/script.py " 369 "../../foo/included.txt\n" 370 "\n" 371 "build input1.out: __foo_bar___rule ../../foo/input1.txt" 372 " | obj/foo/bar.inputdeps.stamp\n" 373 " unique_name = 0\n" 374 " source_name_part = input1\n" 375 " depfile = gen/input1.d\n" 376 "build input2.out: __foo_bar___rule ../../foo/input2.txt" 377 " | obj/foo/bar.inputdeps.stamp\n" 378 " unique_name = 1\n" 379 " source_name_part = input2\n" 380 " depfile = gen/input2.d\n" 381 "\n" 382 "build obj/foo/bar.stamp: stamp input1.out input2.out\n"; 383 EXPECT_EQ(expected_win, out.str()); 384 } 385 } 386