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/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