diff --git a/tests/FeatureSplit/feature1/Android.bp b/tests/FeatureSplit/feature1/Android.bp index 1a93e842cec50..706a4f5443931 100644 --- a/tests/FeatureSplit/feature1/Android.bp +++ b/tests/FeatureSplit/feature1/Android.bp @@ -18,7 +18,7 @@ android_test { name: "FeatureSplit1", srcs: ["**/*.java"], sdk_version: "current", - libs: ["FeatureSplitBase"], + libs: ["FeatureSplitBase", "FeatureSplit2"], aaptflags: [ "--package-id", "0x80", diff --git a/tests/FeatureSplit/feature1/AndroidManifest.xml b/tests/FeatureSplit/feature1/AndroidManifest.xml index b87361faac627..086c2c33422db 100644 --- a/tests/FeatureSplit/feature1/AndroidManifest.xml +++ b/tests/FeatureSplit/feature1/AndroidManifest.xml @@ -19,6 +19,7 @@ featureSplit="feature1"> + diff --git a/tests/FeatureSplit/feature1/res/layout/included.xml b/tests/FeatureSplit/feature1/res/layout/included.xml index c64bdb7ff85f0..f0c56f872438b 100644 --- a/tests/FeatureSplit/feature1/res/layout/included.xml +++ b/tests/FeatureSplit/feature1/res/layout/included.xml @@ -2,4 +2,5 @@ + android:layout_height="wrap_content" + android:text="@string/feature2_string" /> diff --git a/tests/FeatureSplit/feature1/res/values/values.xml b/tests/FeatureSplit/feature1/res/values/values.xml index 7d58865b546ea..6a840df2c79c8 100644 --- a/tests/FeatureSplit/feature1/res/values/values.xml +++ b/tests/FeatureSplit/feature1/res/values/values.xml @@ -20,7 +20,8 @@ 200 #00ff00 - @string/app_title + @string/app_title + @string/feature2_string diff --git a/tests/FeatureSplit/feature2/res/values/values.xml b/tests/FeatureSplit/feature2/res/values/values.xml index af5ed1b79b26d..70e772c0d0197 100644 --- a/tests/FeatureSplit/feature2/res/values/values.xml +++ b/tests/FeatureSplit/feature2/res/values/values.xml @@ -15,10 +15,11 @@ --> + feature 2 string referenced from feature 1 300 #0000ff - @string/app_title + @string/app_title diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 75123537116f3..d3ca357b03059 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -17,6 +17,7 @@ #ifndef AAPT_APP_INFO_H #define AAPT_APP_INFO_H +#include #include #include "util/Maybe.h" @@ -42,6 +43,9 @@ struct AppInfo { // The app's split name, if it is a split. Maybe split_name; + + // The split names that this split depends on. + std::set split_name_dependencies; }; } // namespace aapt diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 9b81369fa9f04..c7ac438dacfbf 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -629,6 +629,12 @@ class CompileContext : public IAaptContext { return 0; } + const std::set& GetSplitNameDependencies() override { + UNIMPLEMENTED(FATAL) << "No Split Name Dependencies be needed in compile phase"; + static std::set empty; + return empty; + } + private: DISALLOW_COPY_AND_ASSIGN(CompileContext); diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp index 0cf86ccdd59fc..22bcd8589ce9a 100644 --- a/tools/aapt2/cmd/Convert.cpp +++ b/tools/aapt2/cmd/Convert.cpp @@ -243,6 +243,12 @@ class Context : public IAaptContext { return 0u; } + const std::set& GetSplitNameDependencies() override { + UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary"; + static std::set empty; + return empty; + } + bool verbose_ = false; std::string package_; diff --git a/tools/aapt2/cmd/Diff.cpp b/tools/aapt2/cmd/Diff.cpp index 262f4fc4e394d..d56994e3ae24a 100644 --- a/tools/aapt2/cmd/Diff.cpp +++ b/tools/aapt2/cmd/Diff.cpp @@ -65,6 +65,12 @@ class DiffContext : public IAaptContext { return 0; } + const std::set& GetSplitNameDependencies() override { + UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary"; + static std::set empty; + return empty; + } + private: std::string empty_; StdErrDiagnostics diagnostics_; diff --git a/tools/aapt2/cmd/Dump.cpp b/tools/aapt2/cmd/Dump.cpp index a23a6a46cf0fb..429aff1ff594b 100644 --- a/tools/aapt2/cmd/Dump.cpp +++ b/tools/aapt2/cmd/Dump.cpp @@ -118,6 +118,12 @@ class DumpContext : public IAaptContext { return 0; } + const std::set& GetSplitNameDependencies() override { + UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary"; + static std::set empty; + return empty; + } + private: StdErrDiagnostics diagnostics_; bool verbose_ = false; diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index b8be21cd47425..ed2d0c1b33bf7 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -140,6 +140,14 @@ class LinkContext : public IAaptContext { min_sdk_version_ = minSdk; } + const std::set& GetSplitNameDependencies() override { + return split_name_dependencies_; + } + + void SetSplitNameDependencies(const std::set& split_name_dependencies) { + split_name_dependencies_ = split_name_dependencies; + } + private: DISALLOW_COPY_AND_ASSIGN(LinkContext); @@ -151,6 +159,7 @@ class LinkContext : public IAaptContext { SymbolTable symbols_; bool verbose_ = false; int min_sdk_version_ = 0; + std::set split_name_dependencies_; }; // A custom delegate that generates compatible pre-O IDs for use with feature splits. @@ -963,6 +972,17 @@ class Linker { app_info.min_sdk_version = ResourceUtils::ParseSdkVersion(min_sdk->value); } } + + for (const xml::Element* child_el : manifest_el->GetChildElements()) { + if (child_el->namespace_uri.empty() && child_el->name == "uses-split") { + if (const xml::Attribute* split_name = + child_el->FindAttribute(xml::kSchemaAndroid, "name")) { + if (!split_name->value.empty()) { + app_info.split_name_dependencies.insert(split_name->value); + } + } + } + } return app_info; } @@ -1770,6 +1790,7 @@ class Linker { context_->SetMinSdkVersion(app_info_.min_sdk_version.value_or_default(0)); context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); + context_->SetSplitNameDependencies(app_info_.split_name_dependencies); // Override the package ID when it is "android". if (context_->GetCompilationPackage() == "android") { diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index bf8f043f6b68b..062dd8eac975b 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "AppInfo.h" #include "Link.h" #include "LoadedApk.h" @@ -253,4 +254,67 @@ TEST_F(LinkTest, OverrideStylesInsteadOfOverlaying) { EXPECT_EQ(actual_style->entries[0].key.id, 0x010100d4); // android:background } +TEST_F(LinkTest, AppInfoWithUsesSplit) { + StdErrDiagnostics diag; + const std::string base_files_dir = GetTestPath("base"); + ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), + R"( + bar + )", + base_files_dir, &diag)); + const std::string base_apk = GetTestPath("base.apk"); + std::vector link_args = { + "--manifest", GetDefaultManifest("com.aapt2.app"), + "-o", base_apk, + }; + ASSERT_TRUE(Link(link_args, base_files_dir, &diag)); + + const std::string feature_manifest = GetTestPath("feature_manifest.xml"); + WriteFile(feature_manifest, android::base::StringPrintf(R"( + + )")); + const std::string feature_files_dir = GetTestPath("feature"); + ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), + R"( + foo + )", + feature_files_dir, &diag)); + const std::string feature_apk = GetTestPath("feature.apk"); + const std::string feature_package_id = "0x80"; + link_args = { + "--manifest", feature_manifest, + "-I", base_apk, + "--package-id", feature_package_id, + "-o", feature_apk, + }; + ASSERT_TRUE(Link(link_args, feature_files_dir, &diag)); + + const std::string feature2_manifest = GetTestPath("feature2_manifest.xml"); + WriteFile(feature2_manifest, android::base::StringPrintf(R"( + + + )")); + const std::string feature2_files_dir = GetTestPath("feature2"); + ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), + R"( + + @string/bar + @string/foo + + )", + feature2_files_dir, &diag)); + const std::string feature2_apk = GetTestPath("feature2.apk"); + const std::string feature2_package_id = "0x81"; + link_args = { + "--manifest", feature2_manifest, + "-I", base_apk, + "-I", feature_apk, + "--package-id", feature2_package_id, + "-o", feature2_apk, + }; + ASSERT_TRUE(Link(link_args, feature2_files_dir, &diag)); +} + } // namespace aapt diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp index 2e6af18c1948b..5e06818d7a131 100644 --- a/tools/aapt2/cmd/Optimize.cpp +++ b/tools/aapt2/cmd/Optimize.cpp @@ -108,6 +108,12 @@ class OptimizeContext : public IAaptContext { return sdk_version_; } + const std::set& GetSplitNameDependencies() override { + UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary"; + static std::set empty; + return empty; + } + private: DISALLOW_COPY_AND_ASSIGN(OptimizeContext); diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index 28f09aa483652..8e49fabe6a5cc 100644 --- a/tools/aapt2/link/ReferenceLinker.cpp +++ b/tools/aapt2/link/ReferenceLinker.cpp @@ -17,6 +17,7 @@ #include "link/ReferenceLinker.h" #include "android-base/logging.h" +#include "android-base/stringprintf.h" #include "androidfw/ResourceTypes.h" #include "Diagnostics.h" @@ -33,6 +34,7 @@ using ::aapt::ResourceUtils::StringBuilder; using ::android::StringPiece; +using ::android::base::StringPrintf; namespace aapt { @@ -81,7 +83,7 @@ class ReferenceLinkerVisitor : public DescendingValueVisitor { // Find the attribute in the symbol table and check if it is visible from this callsite. const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility( - transformed_reference, callsite_, symbols_, &err_str); + transformed_reference, callsite_, context_, symbols_, &err_str); if (symbol) { // Assign our style key the correct ID. The ID may not exist. entry.key.id = symbol->id; @@ -203,12 +205,35 @@ bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref, const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols) { if (reference.name) { const ResourceName& name = reference.name.value(); if (name.package.empty()) { // Use the callsite's package name if no package name was defined. - return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry)); + const SymbolTable::Symbol* symbol = symbols->FindByName( + ResourceName(callsite.package, name.type, name.entry)); + if (symbol) { + return symbol; + } + + // If the callsite package is the same as the current compilation package, + // check the feature split dependencies as well. Feature split resources + // can be referenced without a namespace, just like the base package. + // TODO: modify the package name of included splits instead of having the + // symbol table look up the resource in in every package. b/136105066 + if (callsite.package == context->GetCompilationPackage()) { + const auto& split_name_dependencies = context->GetSplitNameDependencies(); + for (const std::string& split_name : split_name_dependencies) { + std::string split_package = + StringPrintf("%s.%s", callsite.package.c_str(), split_name.c_str()); + symbol = symbols->FindByName(ResourceName(split_package, name.type, name.entry)); + if (symbol) { + return symbol; + } + } + } + return nullptr; } return symbols->FindByName(name); } else if (reference.id) { @@ -220,9 +245,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& refer const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols, std::string* out_error) { - const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols); + const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, context, symbols); if (!symbol) { if (out_error) *out_error = "not found"; return nullptr; @@ -236,10 +262,10 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R } const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility( - const Reference& reference, const CallSite& callsite, SymbolTable* symbols, - std::string* out_error) { + const Reference& reference, const CallSite& callsite, IAaptContext* context, + SymbolTable* symbols, std::string* out_error) { const SymbolTable::Symbol* symbol = - ResolveSymbolCheckVisibility(reference, callsite, symbols, out_error); + ResolveSymbolCheckVisibility(reference, callsite, context, symbols, out_error); if (!symbol) { return nullptr; } @@ -253,10 +279,11 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveAttributeCheckVisibility( Maybe ReferenceLinker::CompileXmlAttribute(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols, std::string* out_error) { const SymbolTable::Symbol* symbol = - ResolveAttributeCheckVisibility(reference, callsite, symbols, out_error); + ResolveAttributeCheckVisibility(reference, callsite, context, symbols, out_error); if (!symbol) { return {}; } @@ -335,7 +362,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen std::string err_str; const SymbolTable::Symbol* s = - ResolveSymbolCheckVisibility(transformed_reference, callsite, symbols, &err_str); + ResolveSymbolCheckVisibility(transformed_reference, callsite, context, symbols, &err_str); if (s) { // The ID may not exist. This is fine because of the possibility of building // against libraries without assigned IDs. diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h index b0b49457e5dd7..1256709edbf4e 100644 --- a/tools/aapt2/link/ReferenceLinker.h +++ b/tools/aapt2/link/ReferenceLinker.h @@ -39,13 +39,16 @@ class ReferenceLinker : public IResourceTableConsumer { // package if the reference has no package name defined (implicit). // Returns nullptr if the symbol was not found. static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, - const CallSite& callsite, SymbolTable* symbols); + const CallSite& callsite, + IAaptContext* context, + SymbolTable* symbols); // Performs name mangling and looks up the resource in the symbol table. If the symbol is not // visible by the reference at the callsite, nullptr is returned. // `out_error` holds the error message. static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols, std::string* out_error); @@ -53,6 +56,7 @@ class ReferenceLinker : public IResourceTableConsumer { // That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute. static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols, std::string* out_error); @@ -60,6 +64,7 @@ class ReferenceLinker : public IResourceTableConsumer { // If resolution fails, outError holds the error message. static Maybe CompileXmlAttribute(const Reference& reference, const CallSite& callsite, + IAaptContext* context, SymbolTable* symbols, std::string* out_error); diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp index be38b967c9866..a31ce9496d0c9 100644 --- a/tools/aapt2/link/ReferenceLinker_test.cpp +++ b/tools/aapt2/link/ReferenceLinker_test.cpp @@ -266,8 +266,13 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic) std::string error; const CallSite call_site{"com.app.test"}; + std::unique_ptr context = + test::ContextBuilder() + .SetCompilationPackage("com.app.test") + .SetPackageId(0x7f) + .Build(); const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility( - *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error); + *test::BuildReference("com.app.test:string/foo"), call_site, context.get(), &table, &error); ASSERT_THAT(symbol, NotNull()); EXPECT_TRUE(error.empty()); } @@ -281,17 +286,23 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute) .AddPublicSymbol("com.app.test:attr/public_foo", ResourceId(0x7f010001), test::AttributeBuilder().Build()) .Build()); + std::unique_ptr context = + test::ContextBuilder() + .SetCompilationPackage("com.app.ext") + .SetPackageId(0x7f) + .Build(); std::string error; const CallSite call_site{"com.app.ext"}; EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute( - *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error)); + *test::BuildReference("com.app.test:attr/foo"), call_site, context.get(), &table, &error)); EXPECT_FALSE(error.empty()); error = ""; ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute( - *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error)); + *test::BuildReference("com.app.test:attr/public_foo"), call_site, context.get(), &table, + &error)); EXPECT_TRUE(error.empty()); } @@ -302,20 +313,62 @@ TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) { .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000)) .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001)) .Build()); + std::unique_ptr context = + test::ContextBuilder() + .SetCompilationPackage("com.app.test") + .SetPackageId(0x7f) + .Build(); const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), - CallSite{"com.app.test"}, &table); + CallSite{"com.app.test"}, + context.get(), &table); ASSERT_THAT(s, NotNull()); EXPECT_THAT(s->id, Eq(make_value(0x7f010000))); s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"}, - &table); + context.get(), &table); ASSERT_THAT(s, NotNull()); EXPECT_THAT(s->id, Eq(make_value(0x7f010001))); EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), - CallSite{"com.app.bad"}, &table), + CallSite{"com.app.bad"}, context.get(), &table), IsNull()); } +TEST(ReferenceLinkerTest, ReferenceSymbolFromOtherSplit) { + NameMangler mangler(NameManglerPolicy{"com.app.test"}); + SymbolTable table(&mangler); + table.AppendSource(test::StaticSymbolSourceBuilder() + .AddSymbol("com.app.test.feature:string/bar", ResourceId(0x80010000)) + .Build()); + std::set split_name_dependencies; + split_name_dependencies.insert("feature"); + std::unique_ptr context = + test::ContextBuilder() + .SetCompilationPackage("com.app.test") + .SetPackageId(0x81) + .SetSplitNameDependencies(split_name_dependencies) + .Build(); + + const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"), + CallSite{"com.app.test"}, + context.get(), &table); + ASSERT_THAT(s, NotNull()); + EXPECT_THAT(s->id, Eq(make_value(0x80010000))); + + s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"}, + context.get(), &table); + EXPECT_THAT(s, IsNull()); + + context = + test::ContextBuilder() + .SetCompilationPackage("com.app.test") + .SetPackageId(0x81) + .Build(); + s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/bar"),CallSite{"com.app.test"}, + context.get(), &table); + + EXPECT_THAT(s, IsNull()); +} + } // namespace aapt diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index d68f7dd44c9f9..f3be483ab7288 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -99,7 +99,7 @@ class XmlVisitor : public xml::PackageAwareVisitor { std::string err_str; attr.compiled_attribute = - ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str); + ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, context_, symbols_, &err_str); if (!attr.compiled_attribute) { DiagMessage error_msg(source); diff --git a/tools/aapt2/optimize/MultiApkGenerator.cpp b/tools/aapt2/optimize/MultiApkGenerator.cpp index 8c9c434095697..c686a10a3fa91 100644 --- a/tools/aapt2/optimize/MultiApkGenerator.cpp +++ b/tools/aapt2/optimize/MultiApkGenerator.cpp @@ -101,6 +101,10 @@ class ContextWrapper : public IAaptContext { util::make_unique(Source{source}, context_->GetDiagnostics()); } + const std::set& GetSplitNameDependencies() override { + return context_->GetSplitNameDependencies(); + } + private: IAaptContext* context_; std::unique_ptr source_diag_; diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h index 30dad8025900a..9c4b323db4334 100644 --- a/tools/aapt2/process/IResourceTableConsumer.h +++ b/tools/aapt2/process/IResourceTableConsumer.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "Diagnostics.h" @@ -50,6 +51,7 @@ struct IAaptContext { virtual NameMangler* GetNameMangler() = 0; virtual bool IsVerbose() = 0; virtual int GetMinSdkVersion() = 0; + virtual const std::set& GetSplitNameDependencies() = 0; }; struct IResourceTableConsumer { diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index 0564db063b9a4..7e10a59df8770 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -81,6 +81,10 @@ class Context : public IAaptContext { return min_sdk_version_; } + const std::set& GetSplitNameDependencies() override { + return split_name_dependencies_; + } + private: DISALLOW_COPY_AND_ASSIGN(Context); @@ -93,6 +97,7 @@ class Context : public IAaptContext { NameMangler name_mangler_; SymbolTable symbols_; int min_sdk_version_; + std::set split_name_dependencies_; }; class ContextBuilder { @@ -127,6 +132,11 @@ class ContextBuilder { return *this; } + ContextBuilder& SetSplitNameDependencies(const std::set& split_name_dependencies) { + context_->split_name_dependencies_ = split_name_dependencies; + return *this; + } + std::unique_ptr Build() { return std::move(context_); } private: