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