Home | History | Annotate | Download | only in gn
      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