|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "tools/gn/ninja_create_bundle_target_writer.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <sstream> | 
|  |  | 
|  | #include "tools/gn/target.h" | 
|  | #include "tools/gn/test_with_scope.h" | 
|  | #include "util/test/test.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void SetupBundleDataDir(BundleData* bundle_data, const std::string& root_dir) { | 
|  | std::string bundle_root_dir = root_dir + "/bar.bundle"; | 
|  | bundle_data->root_dir() = SourceDir(bundle_root_dir); | 
|  | bundle_data->contents_dir() = SourceDir(bundle_root_dir + "/Contents"); | 
|  | bundle_data->resources_dir() = | 
|  | SourceDir(bundle_data->contents_dir().value() + "/Resources"); | 
|  | bundle_data->executable_dir() = | 
|  | SourceDir(bundle_data->contents_dir().value() + "/MacOS"); | 
|  | bundle_data->plugins_dir() = | 
|  | SourceDir(bundle_data->contents_dir().value() + "/Plug Ins"); | 
|  | } | 
|  |  | 
|  | std::unique_ptr<Target> NewAction(const TestWithScope& setup) { | 
|  | Err err; | 
|  | auto action = std::make_unique<Target>(setup.settings(), | 
|  | Label(SourceDir("//foo/"), "bar")); | 
|  | action->set_output_type(Target::ACTION); | 
|  | action->visibility().SetPublic(); | 
|  | action->action_values().set_script(SourceFile("//foo/script.py")); | 
|  |  | 
|  | action->action_values().outputs() = | 
|  | SubstitutionList::MakeForTest("//out/Debug/foo.out"); | 
|  |  | 
|  | action->SetToolchain(setup.toolchain()); | 
|  | return action; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // Tests multiple files with an output pattern. | 
|  | TEST(NinjaCreateBundleTargetWriter, Run) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data")); | 
|  | bundle_data.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input1.txt")); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input2.txt")); | 
|  | bundle_data.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data.SetToolchain(setup.toolchain()); | 
|  | bundle_data.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data.OnResolved(&err)); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp " | 
|  | "obj/foo/data.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " | 
|  | "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " | 
|  | "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "bar.bundle/Contents/Resources/input1.txt " | 
|  | "bar.bundle/Contents/Resources/input2.txt" | 
|  | " || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests creating a bundle in a sub-directory of $root_out_dir. | 
|  | TEST(NinjaCreateBundleTargetWriter, InSubDirectory) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data")); | 
|  | bundle_data.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input1.txt")); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input2.txt")); | 
|  | bundle_data.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data.SetToolchain(setup.toolchain()); | 
|  | bundle_data.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data.OnResolved(&err)); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug/gen"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp " | 
|  | "obj/foo/data.stamp\n" | 
|  | "build gen/bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " | 
|  | "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build gen/bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " | 
|  | "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "gen/bar.bundle/Contents/Resources/input1.txt " | 
|  | "gen/bar.bundle/Contents/Resources/input2.txt || " | 
|  | "obj/baz/bar.inputdeps.stamp\n" | 
|  | "build gen/bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests empty asset catalog with partial_info_plist property defined. | 
|  | TEST(NinjaCreateBundleTargetWriter, JustPartialInfoPlist) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.bundle_data().product_type().assign("com.apple.product-type"); | 
|  | create_bundle.bundle_data().set_partial_info_plist( | 
|  | SourceFile("//out/Debug/baz/bar/bar_partial_info.plist")); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build baz/bar/bar_partial_info.plist: stamp || obj/foo/bar.stamp\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "baz/bar/bar_partial_info.plist || obj/foo/bar.stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests multiple files from asset catalog. | 
|  | TEST(NinjaCreateBundleTargetWriter, AssetCatalog) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data")); | 
|  | bundle_data.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/Contents.json")); | 
|  | bundle_data.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json")); | 
|  | bundle_data.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png")); | 
|  | bundle_data.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png")); | 
|  | bundle_data.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png")); | 
|  | bundle_data.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data.SetToolchain(setup.toolchain()); | 
|  | bundle_data.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data.OnResolved(&err)); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.bundle_data().product_type().assign("com.apple.product-type"); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/bar.stamp " | 
|  | "obj/foo/data.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/Assets.car: compile_xcassets " | 
|  | "../../foo/Foo.xcassets | obj/foo/data.stamp || " | 
|  | "obj/baz/bar.inputdeps.stamp\n" | 
|  | "  product_type = com.apple.product-type\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "bar.bundle/Contents/Resources/Assets.car || " | 
|  | "obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests that the phony target for the top-level bundle directory is generated | 
|  | // correctly. | 
|  | TEST(NinjaCreateBundleTargetWriter, PhonyTarget) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.stamp: stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests complex target with multiple bundle_data sources, including | 
|  | // some asset catalog. | 
|  | TEST(NinjaCreateBundleTargetWriter, Complex) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target bundle_data0(setup.settings(), | 
|  | Label(SourceDir("//qux/"), "info_plist")); | 
|  | bundle_data0.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data0.sources().push_back(SourceFile("//qux/qux-Info.plist")); | 
|  | bundle_data0.action_values().outputs() = | 
|  | SubstitutionList::MakeForTest("{{bundle_contents_dir}}/Info.plist"); | 
|  | bundle_data0.SetToolchain(setup.toolchain()); | 
|  | bundle_data0.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data0.OnResolved(&err)); | 
|  |  | 
|  | Target bundle_data1(setup.settings(), Label(SourceDir("//foo/"), "data")); | 
|  | bundle_data1.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data1.sources().push_back(SourceFile("//foo/input1.txt")); | 
|  | bundle_data1.sources().push_back(SourceFile("//foo/input2.txt")); | 
|  | bundle_data1.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data1.SetToolchain(setup.toolchain()); | 
|  | bundle_data1.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data1.OnResolved(&err)); | 
|  |  | 
|  | Target bundle_data2(setup.settings(), Label(SourceDir("//foo/"), "assets")); | 
|  | bundle_data2.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data2.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/Contents.json")); | 
|  | bundle_data2.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/Contents.json")); | 
|  | bundle_data2.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29.png")); | 
|  | bundle_data2.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@2x.png")); | 
|  | bundle_data2.sources().push_back( | 
|  | SourceFile("//foo/Foo.xcassets/foo.imageset/FooIcon-29@3x.png")); | 
|  | bundle_data2.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data2.SetToolchain(setup.toolchain()); | 
|  | bundle_data2.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data2.OnResolved(&err)); | 
|  |  | 
|  | Target bundle_data3(setup.settings(), Label(SourceDir("//quz/"), "assets")); | 
|  | bundle_data3.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data3.sources().push_back( | 
|  | SourceFile("//quz/Quz.xcassets/Contents.json")); | 
|  | bundle_data3.sources().push_back( | 
|  | SourceFile("//quz/Quz.xcassets/quz.imageset/Contents.json")); | 
|  | bundle_data3.sources().push_back( | 
|  | SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29.png")); | 
|  | bundle_data3.sources().push_back( | 
|  | SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@2x.png")); | 
|  | bundle_data3.sources().push_back( | 
|  | SourceFile("//quz/Quz.xcassets/quz.imageset/QuzIcon-29@3x.png")); | 
|  | bundle_data3.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data3.SetToolchain(setup.toolchain()); | 
|  | bundle_data3.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data3.OnResolved(&err)); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data0)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data1)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data2)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data3)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.bundle_data().product_type().assign("com.apple.product-type"); | 
|  | create_bundle.bundle_data().set_partial_info_plist( | 
|  | SourceFile("//out/Debug/baz/bar/bar_partial_info.plist")); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.inputdeps.stamp: stamp obj/foo/assets.stamp " | 
|  | "obj/foo/bar.stamp obj/foo/data.stamp obj/qux/info_plist.stamp " | 
|  | "obj/quz/assets.stamp\n" | 
|  | "build bar.bundle/Contents/Info.plist: copy_bundle_data " | 
|  | "../../qux/qux-Info.plist || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " | 
|  | "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " | 
|  | "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build obj/baz/bar.xcassets.inputdeps.stamp: stamp " | 
|  | "obj/foo/assets.stamp " | 
|  | "obj/quz/assets.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/Assets.car | " | 
|  | "baz/bar/bar_partial_info.plist: compile_xcassets " | 
|  | "../../foo/Foo.xcassets " | 
|  | "../../quz/Quz.xcassets | obj/baz/bar.xcassets.inputdeps.stamp || " | 
|  | "obj/baz/bar.inputdeps.stamp\n" | 
|  | "  product_type = com.apple.product-type\n" | 
|  | "  partial_info_plist = baz/bar/bar_partial_info.plist\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "bar.bundle/Contents/Info.plist " | 
|  | "bar.bundle/Contents/Resources/input1.txt " | 
|  | "bar.bundle/Contents/Resources/input2.txt " | 
|  | "bar.bundle/Contents/Resources/Assets.car " | 
|  | "baz/bar/bar_partial_info.plist || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } | 
|  |  | 
|  | // Tests code signing steps. | 
|  | TEST(NinjaCreateBundleTargetWriter, CodeSigning) { | 
|  | Err err; | 
|  | TestWithScope setup; | 
|  |  | 
|  | std::unique_ptr<Target> action = NewAction(setup); | 
|  | ASSERT_TRUE(action->OnResolved(&err)) << err.message(); | 
|  |  | 
|  | Target executable(setup.settings(), Label(SourceDir("//baz/"), "quz")); | 
|  | executable.set_output_type(Target::EXECUTABLE); | 
|  | executable.sources().push_back(SourceFile("//baz/quz.c")); | 
|  | executable.SetToolchain(setup.toolchain()); | 
|  | executable.visibility().SetPublic(); | 
|  | ASSERT_TRUE(executable.OnResolved(&err)); | 
|  |  | 
|  | Target bundle_data(setup.settings(), Label(SourceDir("//foo/"), "data")); | 
|  | bundle_data.set_output_type(Target::BUNDLE_DATA); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input1.txt")); | 
|  | bundle_data.sources().push_back(SourceFile("//foo/input2.txt")); | 
|  | bundle_data.action_values().outputs() = SubstitutionList::MakeForTest( | 
|  | "{{bundle_resources_dir}}/{{source_file_part}}"); | 
|  | bundle_data.SetToolchain(setup.toolchain()); | 
|  | bundle_data.visibility().SetPublic(); | 
|  | ASSERT_TRUE(bundle_data.OnResolved(&err)); | 
|  |  | 
|  | Target create_bundle( | 
|  | setup.settings(), | 
|  | Label(SourceDir("//baz/"), "bar", setup.toolchain()->label().dir(), | 
|  | setup.toolchain()->label().name())); | 
|  | SetupBundleDataDir(&create_bundle.bundle_data(), "//out/Debug"); | 
|  | create_bundle.set_output_type(Target::CREATE_BUNDLE); | 
|  | create_bundle.bundle_data().set_code_signing_script( | 
|  | SourceFile("//build/codesign.py")); | 
|  | create_bundle.bundle_data().code_signing_sources().push_back( | 
|  | SourceFile("//out/Debug/quz")); | 
|  | create_bundle.bundle_data().code_signing_outputs() = | 
|  | SubstitutionList::MakeForTest( | 
|  | "//out/Debug/bar.bundle/Contents/quz", | 
|  | "//out/Debug/bar.bundle/_CodeSignature/CodeResources"); | 
|  | create_bundle.bundle_data().code_signing_args() = | 
|  | SubstitutionList::MakeForTest("-b=quz", "bar.bundle"); | 
|  | create_bundle.public_deps().push_back(LabelTargetPair(&executable)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(&bundle_data)); | 
|  | create_bundle.private_deps().push_back(LabelTargetPair(action.get())); | 
|  | create_bundle.SetToolchain(setup.toolchain()); | 
|  | ASSERT_TRUE(create_bundle.OnResolved(&err)); | 
|  |  | 
|  | std::ostringstream out; | 
|  | NinjaCreateBundleTargetWriter writer(&create_bundle, out); | 
|  | writer.Run(); | 
|  |  | 
|  | const char expected[] = | 
|  | "build obj/baz/bar.inputdeps.stamp: stamp ./quz obj/foo/bar.stamp " | 
|  | "obj/foo/data.stamp\n" | 
|  | "rule __baz_bar___toolchain_default__code_signing_rule\n" | 
|  | "  command =  ../../build/codesign.py -b=quz bar.bundle\n" | 
|  | "  description = CODE SIGNING //baz:bar(//toolchain:default)\n" | 
|  | "  restat = 1\n" | 
|  | "\n" | 
|  | "build bar.bundle/Contents/Resources/input1.txt: copy_bundle_data " | 
|  | "../../foo/input1.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle/Contents/Resources/input2.txt: copy_bundle_data " | 
|  | "../../foo/input2.txt || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build obj/baz/bar.codesigning.inputdeps.stamp: stamp " | 
|  | "../../build/codesign.py " | 
|  | "quz " | 
|  | "bar.bundle/Contents/Resources/input1.txt " | 
|  | "bar.bundle/Contents/Resources/input2.txt || " | 
|  | "obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle/Contents/quz bar.bundle/_CodeSignature/CodeResources: " | 
|  | "__baz_bar___toolchain_default__code_signing_rule " | 
|  | "| obj/baz/bar.codesigning.inputdeps.stamp\n" | 
|  | "build obj/baz/bar.stamp: stamp " | 
|  | "bar.bundle/Contents/quz " | 
|  | "bar.bundle/_CodeSignature/CodeResources || obj/baz/bar.inputdeps.stamp\n" | 
|  | "build bar.bundle: phony obj/baz/bar.stamp\n"; | 
|  | std::string out_str = out.str(); | 
|  | EXPECT_EQ(expected, out_str); | 
|  | } |