diff options
-rw-r--r-- | tools/aapt2/ResourceTable.cpp | 33 | ||||
-rw-r--r-- | tools/aapt2/ResourceTable.h | 18 | ||||
-rw-r--r-- | tools/aapt2/link/Link.cpp | 272 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger.cpp | 11 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger.h | 4 | ||||
-rw-r--r-- | tools/aapt2/link/TableMerger_test.cpp | 4 |
6 files changed, 213 insertions, 129 deletions
diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index deafe206910d..73d85852b2b0 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -190,22 +190,32 @@ static constexpr const char16_t* kValidNameMangledChars = u"._-$"; bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config, std::unique_ptr<Value> value, IDiagnostics* diag) { - return addResourceImpl(name, {}, config, std::move(value), kValidNameChars, diag); + return addResourceImpl(name, {}, config, std::move(value), kValidNameChars, + resolveValueCollision, diag); } bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId, const ConfigDescription& config, std::unique_ptr<Value> value, IDiagnostics* diag) { - return addResourceImpl(name, resId, config, std::move(value), kValidNameChars, diag); + return addResourceImpl(name, resId, config, std::move(value), kValidNameChars, + resolveValueCollision, diag); } bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config, const Source& source, const StringPiece16& path, IDiagnostics* diag) { + return addFileReference(name, config, source, path, resolveValueCollision, diag); +} + +bool ResourceTable::addFileReference(const ResourceNameRef& name, const ConfigDescription& config, + const Source& source, const StringPiece16& path, + std::function<int(Value*,Value*)> conflictResolver, + IDiagnostics* diag) { std::unique_ptr<FileReference> fileRef = util::make_unique<FileReference>( stringPool.makeRef(path)); fileRef->setSource(source); - return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars, diag); + return addResourceImpl(name, ResourceId{}, config, std::move(fileRef), kValidNameChars, + conflictResolver, diag); } bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, @@ -213,7 +223,7 @@ bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, std::unique_ptr<Value> value, IDiagnostics* diag) { return addResourceImpl(name, ResourceId{}, config, std::move(value), kValidNameMangledChars, - diag); + resolveValueCollision, diag); } bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, @@ -221,12 +231,17 @@ bool ResourceTable::addResourceAllowMangled(const ResourceNameRef& name, const ConfigDescription& config, std::unique_ptr<Value> value, IDiagnostics* diag) { - return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars, diag); + return addResourceImpl(name, id, config, std::move(value), kValidNameMangledChars, + resolveValueCollision, diag); } -bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId resId, - const ConfigDescription& config, std::unique_ptr<Value> value, - const char16_t* validChars, IDiagnostics* diag) { +bool ResourceTable::addResourceImpl(const ResourceNameRef& name, + const ResourceId resId, + const ConfigDescription& config, + std::unique_ptr<Value> value, + const char16_t* validChars, + std::function<int(Value*,Value*)> conflictResolver, + IDiagnostics* diag) { assert(value && "value can't be nullptr"); assert(diag && "diagnostics can't be nullptr"); @@ -289,7 +304,7 @@ bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceI // This resource did not exist before, add it. entry->values.insert(iter, ResourceConfigValue{ config, std::move(value) }); } else { - int collisionResult = resolveValueCollision(iter->value.get(), value.get()); + int collisionResult = conflictResolver(iter->value.get(), value.get()); if (collisionResult > 0) { // Take the incoming value. iter->value = std::move(value); diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 980504be377b..6b7b07ea3a93 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -163,7 +163,12 @@ public: IDiagnostics* diag); bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config, - const Source& source, const StringPiece16& path, IDiagnostics* diag); + const Source& source, const StringPiece16& path, + IDiagnostics* diag); + + bool addFileReference(const ResourceNameRef& name, const ConfigDescription& config, + const Source& source, const StringPiece16& path, + std::function<int(Value*,Value*)> conflictResolver, IDiagnostics* diag); /** * Same as addResource, but doesn't verify the validity of the name. This is used @@ -221,9 +226,14 @@ public: private: ResourceTablePackage* findOrCreatePackage(const StringPiece16& name); - bool addResourceImpl(const ResourceNameRef& name, ResourceId resId, - const ConfigDescription& config, std::unique_ptr<Value> value, - const char16_t* validChars, IDiagnostics* diag); + bool addResourceImpl(const ResourceNameRef& name, + ResourceId resId, + const ConfigDescription& config, + std::unique_ptr<Value> value, + const char16_t* validChars, + std::function<int(Value*,Value*)> conflictResolver, + IDiagnostics* diag); + bool setSymbolStateImpl(const ResourceNameRef& name, ResourceId resId, const Symbol& symbol, const char16_t* validChars, IDiagnostics* diag); }; diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 9ce3734f1914..93f2dc6f04cd 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -48,6 +48,7 @@ struct LinkOptions { std::string outputPath; std::string manifestPath; std::vector<std::string> includePaths; + std::vector<std::string> overlayFiles; Maybe<std::string> generateJavaClassPath; std::vector<std::string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; @@ -88,9 +89,11 @@ struct LinkContext : public IAaptContext { } }; -struct LinkCommand { - LinkOptions mOptions; - LinkContext mContext; +class LinkCommand { +public: + LinkCommand(const LinkOptions& options) : + mOptions(options), mContext(), mFinalTable() { + } std::string buildResourceFileName(const ResourceFile& resFile) { std::stringstream out; @@ -117,8 +120,7 @@ struct LinkCommand { AssetManagerSymbolTableBuilder builder; for (const std::string& path : mOptions.includePaths) { if (mOptions.verbose) { - mContext.getDiagnostics()->note( - DiagMessage(Source{ path }) << "loading include path"); + mContext.getDiagnostics()->note(DiagMessage(path) << "loading include path"); } std::unique_ptr<android::AssetManager> assetManager = @@ -126,7 +128,7 @@ struct LinkCommand { int32_t cookie = 0; if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) { mContext.getDiagnostics()->error( - DiagMessage(Source{ path }) << "failed to load include path"); + DiagMessage(path) << "failed to load include path"); return {}; } builder.add(std::move(assetManager)); @@ -141,12 +143,12 @@ struct LinkCommand { std::string errorStr; Maybe<android::FileMap> map = file::mmapPath(input, &errorStr); if (!map) { - mContext.getDiagnostics()->error(DiagMessage(Source{ input }) << errorStr); + mContext.getDiagnostics()->error(DiagMessage(input) << errorStr); return {}; } std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); - BinaryResourceParser parser(&mContext, table.get(), Source{ input }, + BinaryResourceParser parser(&mContext, table.get(), Source(input), map.value().getDataPtr(), map.value().getDataLength()); if (!parser.parse()) { return {}; @@ -160,11 +162,11 @@ struct LinkCommand { std::unique_ptr<XmlResource> loadXml(const std::string& path) { std::ifstream fin(path, std::ifstream::binary); if (!fin) { - mContext.getDiagnostics()->error(DiagMessage(Source{ path }) << strerror(errno)); + mContext.getDiagnostics()->error(DiagMessage(path) << strerror(errno)); return {}; } - return xml::inflate(&fin, mContext.getDiagnostics(), Source{ path }); + return xml::inflate(&fin, mContext.getDiagnostics(), Source(path)); } /** @@ -255,9 +257,9 @@ struct LinkCommand { return {}; } - bool verifyNoExternalPackages(ResourceTable* table) { + bool verifyNoExternalPackages() { bool error = false; - for (const auto& package : table->packages) { + for (const auto& package : mFinalTable.packages) { if (mContext.getCompilationPackage() != package->name || !package->id || package->id.value() != mContext.getPackageId()) { // We have a package that is not related to the one we're building! @@ -401,6 +403,103 @@ struct LinkCommand { return true; } + bool mergeStaticLibrary(const std::string& input) { + // TODO(adamlesinski): Load resources from a static library APK and merge the table into + // TableMerger. + mContext.getDiagnostics()->warn(DiagMessage() + << "linking static libraries not supported yet: " + << input); + return true; + } + + bool mergeResourceTable(const std::string& input, bool override) { + if (mOptions.verbose) { + mContext.getDiagnostics()->note(DiagMessage() << "linking " << input); + } + + std::unique_ptr<ResourceTable> table = loadTable(input); + if (!table) { + return false; + } + + if (!mTableMerger->merge(Source(input), table.get(), override)) { + return false; + } + return true; + } + + bool mergeCompiledFile(const std::string& input, ResourceFile&& file, bool override) { + if (file.name.package.empty()) { + file.name.package = mContext.getCompilationPackage().toString(); + } + + ResourceNameRef resName = file.name; + + Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(file.name); + if (mangledName) { + resName = mangledName.value(); + } + + std::function<int(Value*,Value*)> resolver; + if (override) { + resolver = [](Value* a, Value* b) -> int { + int result = ResourceTable::resolveValueCollision(a, b); + if (result == 0) { + // Always accept the new value if it would normally conflict (override). + result = 1; + } + return result; + }; + } else { + // Otherwise use the default resolution. + resolver = ResourceTable::resolveValueCollision; + } + + // Add this file to the table. + if (!mFinalTable.addFileReference(resName, file.config, file.source, + util::utf8ToUtf16(buildResourceFileName(file)), + resolver, mContext.getDiagnostics())) { + return false; + } + + // Add the exports of this file to the table. + for (SourcedResourceName& exportedSymbol : file.exportedSymbols) { + if (exportedSymbol.name.package.empty()) { + exportedSymbol.name.package = mContext.getCompilationPackage().toString(); + } + + ResourceNameRef resName = exportedSymbol.name; + + Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName( + exportedSymbol.name); + if (mangledName) { + resName = mangledName.value(); + } + + std::unique_ptr<Id> id = util::make_unique<Id>(); + id->setSource(file.source.withLine(exportedSymbol.line)); + bool result = mFinalTable.addResourceAllowMangled(resName, {}, std::move(id), + mContext.getDiagnostics()); + if (!result) { + return false; + } + } + + mFilesToProcess[resName.toResourceName()] = FileToProcess{ Source(input), std::move(file) }; + return true; + } + + bool processFile(const std::string& input, bool override) { + if (util::stringEndsWith<char>(input, ".apk")) { + return mergeStaticLibrary(input); + } else if (util::stringEndsWith<char>(input, ".arsc.flat")) { + return mergeResourceTable(input, override); + } else if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) { + return mergeCompiledFile(input, std::move(maybeF.value()), override); + } + return false; + } + int run(const std::vector<std::string>& inputFiles) { // Load the AndroidManifest.xml std::unique_ptr<XmlResource> manifestXml = loadXml(mOptions.manifestPath); @@ -438,82 +537,25 @@ struct LinkCommand { return 1; } + mTableMerger = util::make_unique<TableMerger>(&mContext, &mFinalTable); + if (mOptions.verbose) { mContext.getDiagnostics()->note( DiagMessage() << "linking package '" << mContext.mCompilationPackage << "' " << "with package ID " << std::hex << (int) mContext.mPackageId); } - ResourceTable mergedTable; - TableMerger tableMerger(&mContext, &mergedTable); - - struct FilesToProcess { - Source source; - ResourceFile file; - }; - bool error = false; - std::queue<FilesToProcess> filesToProcess; - for (const std::string& input : inputFiles) { - if (util::stringEndsWith<char>(input, ".apk")) { - // TODO(adamlesinski): Load resources from a static library APK - // Merge the table into TableMerger. - - } else if (util::stringEndsWith<char>(input, ".arsc.flat")) { - if (mOptions.verbose) { - mContext.getDiagnostics()->note(DiagMessage() << "linking " << input); - } - - std::unique_ptr<ResourceTable> table = loadTable(input); - if (!table) { - return 1; - } - - if (!tableMerger.merge(Source(input), table.get())) { - return 1; - } - - } else { - // Extract the exported IDs here so we can build the resource table. - if (Maybe<ResourceFile> maybeF = loadFileExportHeader(input)) { - ResourceFile& f = maybeF.value(); - - if (f.name.package.empty()) { - f.name.package = mContext.getCompilationPackage().toString(); - } - - Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName(f.name); - // Add this file to the table. - if (!mergedTable.addFileReference(mangledName ? mangledName.value() : f.name, - f.config, f.source, - util::utf8ToUtf16(buildResourceFileName(f)), - mContext.getDiagnostics())) { - error = true; - } - - // Add the exports of this file to the table. - for (SourcedResourceName& exportedSymbol : f.exportedSymbols) { - if (exportedSymbol.name.package.empty()) { - exportedSymbol.name.package = mContext.getCompilationPackage() - .toString(); - } - - Maybe<ResourceName> mangledName = mContext.getNameMangler()->mangleName( - exportedSymbol.name); - std::unique_ptr<Id> id = util::make_unique<Id>(); - id->setSource(f.source.withLine(exportedSymbol.line)); - if (!mergedTable.addResourceAllowMangled( - mangledName ? mangledName.value() : exportedSymbol.name, - {}, std::move(id), mContext.getDiagnostics())) { - error = true; - } - } + for (const std::string& input : inputFiles) { + if (!processFile(input, false)) { + error = true; + } + } - filesToProcess.push(FilesToProcess{ Source(input), std::move(f) }); - } else { - return 1; - } + for (const std::string& input : mOptions.overlayFiles) { + if (!processFile(input, true)) { + error = true; } } @@ -522,13 +564,13 @@ struct LinkCommand { return 1; } - if (!verifyNoExternalPackages(&mergedTable)) { + if (!verifyNoExternalPackages()) { return 1; } if (!mOptions.staticLib) { PrivateAttributeMover mover; - if (!mover.consume(&mContext, &mergedTable)) { + if (!mover.consume(&mContext, &mFinalTable)) { mContext.getDiagnostics()->error( DiagMessage() << "failed moving private attributes"); return 1; @@ -537,22 +579,22 @@ struct LinkCommand { { IdAssigner idAssigner; - if (!idAssigner.consume(&mContext, &mergedTable)) { + if (!idAssigner.consume(&mContext, &mFinalTable)) { mContext.getDiagnostics()->error(DiagMessage() << "failed assigning IDs"); return 1; } } - mContext.mNameMangler = util::make_unique<NameMangler>( - NameManglerPolicy{ mContext.mCompilationPackage, tableMerger.getMergedPackages() }); + mContext.mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{ + mContext.mCompilationPackage, mTableMerger->getMergedPackages() }); mContext.mSymbols = JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mergedTable)) + .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable)) .addSymbolTable(std::move(mContext.mSymbols)) .build(); { ReferenceLinker linker; - if (!linker.consume(&mContext, &mergedTable)) { + if (!linker.consume(&mContext, &mFinalTable)) { mContext.getDiagnostics()->error(DiagMessage() << "failed linking references"); return 1; } @@ -598,20 +640,20 @@ struct LinkCommand { } } - for (; !filesToProcess.empty(); filesToProcess.pop()) { - FilesToProcess& f = filesToProcess.front(); - if (f.file.name.type != ResourceType::kRaw && - util::stringEndsWith<char>(f.source.path, ".xml.flat")) { + for (auto& pair : mFilesToProcess) { + FileToProcess& file = pair.second; + if (file.file.name.type != ResourceType::kRaw && + util::stringEndsWith<char>(file.source.path, ".xml.flat")) { if (mOptions.verbose) { - mContext.getDiagnostics()->note(DiagMessage() << "linking " << f.source.path); + mContext.getDiagnostics()->note(DiagMessage() << "linking " << file.source.path); } - std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(f.source.path); + std::unique_ptr<XmlResource> xmlRes = loadBinaryXmlSkipFileExport(file.source.path); if (!xmlRes) { return 1; } - xmlRes->file = std::move(f.file); + xmlRes->file = std::move(file.file); XmlReferenceLinker xmlLinker; if (xmlLinker.consume(&mContext, xmlRes.get())) { @@ -631,7 +673,7 @@ struct LinkCommand { } if (!mOptions.noAutoVersion) { - Maybe<ResourceTable::SearchResult> result = mergedTable.findResource( + Maybe<ResourceTable::SearchResult> result = mFinalTable.findResource( xmlRes->file.name); for (int sdkLevel : xmlLinker.getSdkLevels()) { if (sdkLevel > xmlRes->file.config.sdkVersion && @@ -639,7 +681,7 @@ struct LinkCommand { xmlRes->file.config, sdkLevel)) { xmlRes->file.config.sdkVersion = sdkLevel; - if (!mergedTable.addFileReference(xmlRes->file.name, + if (!mFinalTable.addFileReference(xmlRes->file.name, xmlRes->file.config, xmlRes->file.source, util::utf8ToUtf16( @@ -662,10 +704,11 @@ struct LinkCommand { } } else { if (mOptions.verbose) { - mContext.getDiagnostics()->note(DiagMessage() << "copying " << f.source.path); + mContext.getDiagnostics()->note(DiagMessage() << "copying " + << file.source.path); } - if (!copyFileToArchive(f.source.path, buildResourceFileName(f.file), 0, + if (!copyFileToArchive(file.source.path, buildResourceFileName(file.file), 0, archiveWriter.get())) { error = true; } @@ -679,13 +722,13 @@ struct LinkCommand { if (!mOptions.noAutoVersion) { AutoVersioner versioner; - if (!versioner.consume(&mContext, &mergedTable)) { + if (!versioner.consume(&mContext, &mFinalTable)) { mContext.getDiagnostics()->error(DiagMessage() << "failed versioning styles"); return 1; } } - if (!flattenTable(&mergedTable, archiveWriter.get())) { + if (!flattenTable(&mFinalTable, archiveWriter.get())) { mContext.getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc"); return 1; } @@ -704,7 +747,7 @@ struct LinkCommand { // to the original package, and private and public symbols to the private package. options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic; - if (!writeJavaFile(&mergedTable, mContext.getCompilationPackage(), + if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(), mContext.getCompilationPackage(), options)) { return 1; } @@ -713,12 +756,12 @@ struct LinkCommand { outputPackage = mOptions.privateSymbols.value(); } - if (!writeJavaFile(&mergedTable, actualPackage, outputPackage, options)) { + if (!writeJavaFile(&mFinalTable, actualPackage, outputPackage, options)) { return 1; } for (std::string& extraPackage : mOptions.extraJavaPackages) { - if (!writeJavaFile(&mergedTable, actualPackage, util::utf8ToUtf16(extraPackage), + if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage), options)) { return 1; } @@ -732,10 +775,10 @@ struct LinkCommand { } if (mOptions.verbose) { - Debug::printTable(&mergedTable); - for (; !tableMerger.getFileMergeQueue()->empty(); - tableMerger.getFileMergeQueue()->pop()) { - const FileToMerge& f = tableMerger.getFileMergeQueue()->front(); + Debug::printTable(&mFinalTable); + for (; !mTableMerger->getFileMergeQueue()->empty(); + mTableMerger->getFileMergeQueue()->pop()) { + const FileToMerge& f = mTableMerger->getFileMergeQueue()->front(); mContext.getDiagnostics()->note( DiagMessage() << f.srcPath << " -> " << f.dstPath << " from (0x" << std::hex << (uintptr_t) f.srcTable << std::dec); @@ -744,6 +787,18 @@ struct LinkCommand { return 0; } + +private: + LinkOptions mOptions; + LinkContext mContext; + ResourceTable mFinalTable; + std::unique_ptr<TableMerger> mTableMerger; + + struct FileToProcess { + Source source; + ResourceFile file; + }; + std::map<ResourceName, FileToProcess> mFilesToProcess; }; int link(const std::vector<StringPiece>& args) { @@ -755,6 +810,9 @@ int link(const std::vector<StringPiece>& args) { .requiredFlag("--manifest", "Path to the Android manifest to build", &options.manifestPath) .optionalFlagList("-I", "Adds an Android APK to link against", &options.includePaths) + .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics. " + "The last conflicting resource given takes precedence.", + &options.overlayFiles) .optionalFlag("--java", "Directory in which to generate R.java", &options.generateJavaClassPath) .optionalFlag("--proguard", "Output file for generated Proguard rules", @@ -794,7 +852,7 @@ int link(const std::vector<StringPiece>& args) { options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value()); } - LinkCommand cmd = { options }; + LinkCommand cmd(options); return cmd.run(flags.getArgs()); } diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 1eea410cf2a6..a06a1bfe21a5 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -37,7 +37,7 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) : /** * This will merge packages with the same package name (or no package name). */ -bool TableMerger::merge(const Source& src, ResourceTable* table) { +bool TableMerger::merge(const Source& src, ResourceTable* table, bool overrideExisting) { const uint8_t desiredPackageId = mContext->getPackageId(); bool error = false; @@ -55,7 +55,7 @@ bool TableMerger::merge(const Source& src, ResourceTable* table) { // mangled, then looked up at resolution time. // Also, when linking, we convert references with no package name to use // the compilation package name. - if (!doMerge(src, table, package.get(), false)) { + if (!doMerge(src, table, package.get(), false, overrideExisting)) { error = true; } } @@ -79,7 +79,7 @@ bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& package bool mangle = packageName != mContext->getCompilationPackage(); mMergedPackages.insert(package->name); - if (!doMerge(src, table, package.get(), mangle)) { + if (!doMerge(src, table, package.get(), mangle, false)) { error = true; } } @@ -87,7 +87,8 @@ bool TableMerger::mergeAndMangle(const Source& src, const StringPiece16& package } bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable, - ResourceTablePackage* srcPackage, const bool manglePackage) { + ResourceTablePackage* srcPackage, const bool manglePackage, + const bool overrideExisting) { bool error = false; for (auto& srcType : srcPackage->types) { @@ -149,7 +150,7 @@ bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable, if (iter != dstEntry->values.end() && iter->config == srcValue.config) { const int collisionResult = ResourceTable::resolveValueCollision( iter->value.get(), srcValue.value.get()); - if (collisionResult == 0) { + if (collisionResult == 0 && !overrideExisting) { // Error! ResourceNameRef resourceName(srcPackage->name, srcType->type, diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h index c903f1bf218e..a2c9dbf31fe1 100644 --- a/tools/aapt2/link/TableMerger.h +++ b/tools/aapt2/link/TableMerger.h @@ -63,7 +63,7 @@ public: /** * Merges resources from the same or empty package. This is for local sources. */ - bool merge(const Source& src, ResourceTable* table); + bool merge(const Source& src, ResourceTable* table, bool overrideExisting); /** * Merges resources from the given package, mangling the name. This is for static libraries. @@ -79,7 +79,7 @@ private: std::queue<FileToMerge> mFilesToMerge; bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage, - const bool manglePackage); + const bool manglePackage, const bool overrideExisting); std::unique_ptr<Value> cloneAndMangle(ResourceTable* table, const std::u16string& package, Value* value); diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp index 0af4314a762e..b7ffba7d2157 100644 --- a/tools/aapt2/link/TableMerger_test.cpp +++ b/tools/aapt2/link/TableMerger_test.cpp @@ -59,7 +59,7 @@ TEST_F(TableMergerTest, SimpleMerge) { ResourceTable finalTable; TableMerger merger(mContext.get(), &finalTable); - ASSERT_TRUE(merger.merge({}, tableA.get())); + ASSERT_TRUE(merger.merge({}, tableA.get(), false)); ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get())); EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0); @@ -89,7 +89,7 @@ TEST_F(TableMergerTest, MergeFileReferences) { ResourceTable finalTable; TableMerger merger(mContext.get(), &finalTable); - ASSERT_TRUE(merger.merge({}, tableA.get())); + ASSERT_TRUE(merger.merge({}, tableA.get(), false)); ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get())); FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file"); |