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