AAPT2: Add -A (assets) support
Looks like the build system doesn't support assets/ for
resources, so we will re-introduce them in aapt2, even though
we're just copying them around and they would be better
suited for inclusion in the APK when classes.dex gets inserted.
Bug: 35461578
Test: CTS test android.content.res.cts.AssetManager#testAssetOperations should pass
Change-Id: I18361d7367d476806bcf7154ee76df3f0e83b565
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/AppOne/Android.mk
index a6f32d4..38bd5b5 100644
--- a/tools/aapt2/integration-tests/AppOne/Android.mk
+++ b/tools/aapt2/integration-tests/AppOne/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PACKAGE_NAME := AaptTestAppOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets $(LOCAL_PATH)/assets2
LOCAL_STATIC_ANDROID_LIBRARIES := \
AaptTestStaticLibOne \
AaptTestStaticLibTwo
diff --git a/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt b/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
new file mode 100644
index 0000000..1251949
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets/subdir/subsubdir/test.txt
@@ -0,0 +1 @@
+subdir/subsubdir/test.txt comes from assets
diff --git a/tools/aapt2/integration-tests/AppOne/assets/test.txt b/tools/aapt2/integration-tests/AppOne/assets/test.txt
new file mode 100644
index 0000000..88266de
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets/test.txt
@@ -0,0 +1 @@
+test.txt came from assets
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/new.txt b/tools/aapt2/integration-tests/AppOne/assets2/new.txt
new file mode 100644
index 0000000..f4963a9
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets2/new.txt
@@ -0,0 +1 @@
+new.txt came from assets2
diff --git a/tools/aapt2/integration-tests/AppOne/assets2/test.txt b/tools/aapt2/integration-tests/AppOne/assets2/test.txt
new file mode 100644
index 0000000..5d8b36c
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/assets2/test.txt
@@ -0,0 +1 @@
+test.txt came from assets2
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index c8f0217..1042111 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -77,6 +77,7 @@
std::string manifest_path;
std::vector<std::string> include_paths;
std::vector<std::string> overlay_files;
+ std::vector<std::string> assets_dirs;
bool output_to_directory = false;
bool auto_add_overlay = false;
@@ -1412,6 +1413,46 @@
return doc;
}
+ bool CopyAssetsDirsToApk(IArchiveWriter* writer) {
+ std::map<std::string, std::unique_ptr<io::RegularFile>> merged_assets;
+ for (const std::string& assets_dir : options_.assets_dirs) {
+ Maybe<std::vector<std::string>> files =
+ file::FindFiles(assets_dir, context_->GetDiagnostics(), nullptr);
+ if (!files) {
+ return false;
+ }
+
+ for (const std::string& file : files.value()) {
+ std::string full_key = "assets/" + file;
+ std::string full_path = assets_dir;
+ file::AppendPath(&full_path, file);
+
+ auto iter = merged_assets.find(full_key);
+ if (iter == merged_assets.end()) {
+ merged_assets.emplace(std::move(full_key),
+ util::make_unique<io::RegularFile>(Source(std::move(full_path))));
+ } else if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Warn(DiagMessage(iter->second->GetSource())
+ << "asset file overrides '" << full_path << "'");
+ }
+ }
+ }
+
+ for (auto& entry : merged_assets) {
+ uint32_t compression_flags = ArchiveEntry::kCompress;
+ std::string extension = file::GetExtension(entry.first).to_string();
+ if (options_.extensions_to_not_compress.count(extension) > 0) {
+ compression_flags = 0u;
+ }
+
+ if (!CopyFileToArchive(entry.second.get(), entry.first, compression_flags, writer,
+ context_)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
/**
* Writes the AndroidManifest, ResourceTable, and all XML files referenced by
* the ResourceTable to the IArchiveWriter.
@@ -1724,11 +1765,9 @@
}
// Start writing the base APK.
- std::unique_ptr<IArchiveWriter> archive_writer =
- MakeArchiveWriter(options_.output_path);
+ std::unique_ptr<IArchiveWriter> archive_writer = MakeArchiveWriter(options_.output_path);
if (!archive_writer) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed to create archive");
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to create archive");
return 1;
}
@@ -1743,16 +1782,15 @@
XmlReferenceLinker manifest_linker;
if (manifest_linker.Consume(context_, manifest_xml.get())) {
if (options_.generate_proguard_rules_path &&
- !proguard::CollectProguardRulesForManifest(
- Source(options_.manifest_path), manifest_xml.get(),
- &proguard_keep_set)) {
+ !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
+ manifest_xml.get(), &proguard_keep_set)) {
error = true;
}
if (options_.generate_main_dex_proguard_rules_path &&
- !proguard::CollectProguardRulesForManifest(
- Source(options_.manifest_path), manifest_xml.get(),
- &proguard_main_dex_keep_set, true)) {
+ !proguard::CollectProguardRulesForManifest(Source(options_.manifest_path),
+ manifest_xml.get(),
+ &proguard_main_dex_keep_set, true)) {
error = true;
}
@@ -1776,13 +1814,15 @@
}
if (error) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed processing manifest");
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed processing manifest");
return 1;
}
- if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(),
- &final_table_)) {
+ if (!WriteApk(archive_writer.get(), &proguard_keep_set, manifest_xml.get(), &final_table_)) {
+ return 1;
+ }
+
+ if (!CopyAssetsDirsToApk(archive_writer.get())) {
return 1;
}
@@ -1863,12 +1903,6 @@
proguard_main_dex_keep_set)) {
return 1;
}
-
- if (context_->IsVerbose()) {
- DebugPrintTableOptions debug_print_table_options;
- debug_print_table_options.show_sources = true;
- Debug::PrintTable(&final_table_, debug_print_table_options);
- }
return 0;
}
@@ -1916,6 +1950,9 @@
.RequiredFlag("--manifest", "Path to the Android manifest to build",
&options.manifest_path)
.OptionalFlagList("-I", "Adds an Android APK to link against", &options.include_paths)
+ .OptionalFlagList("-A",
+ "An assets directory to include in the APK. These are unprocessed.",
+ &options.assets_dirs)
.OptionalFlagList("-R",
"Compilation unit to link, using `overlay` semantics.\n"
"The last conflicting resource given takes precedence.",
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index aa840e2..d10351b 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -264,5 +264,57 @@
return true;
}
+Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
+ const FileFilter* filter) {
+ const std::string root_dir = path.to_string();
+ std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
+ if (!d) {
+ diag->Error(DiagMessage() << android::base::SystemErrorCodeToString(errno));
+ return {};
+ }
+
+ std::vector<std::string> files;
+ std::vector<std::string> subdirs;
+ while (struct dirent* entry = readdir(d.get())) {
+ if (util::StartsWith(entry->d_name, ".")) {
+ continue;
+ }
+
+ std::string file_name = entry->d_name;
+ std::string full_path = root_dir;
+ AppendPath(&full_path, file_name);
+ const FileType file_type = GetFileType(full_path);
+
+ if (filter != nullptr) {
+ if (!(*filter)(file_name, file_type)) {
+ continue;
+ }
+ }
+
+ if (file_type == file::FileType::kDirectory) {
+ subdirs.push_back(std::move(file_name));
+ } else {
+ files.push_back(std::move(file_name));
+ }
+ }
+
+ // Now process subdirs.
+ for (const std::string& subdir : subdirs) {
+ std::string full_subdir = root_dir;
+ AppendPath(&full_subdir, subdir);
+ Maybe<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
+ if (!subfiles) {
+ return {};
+ }
+
+ for (const std::string& subfile : subfiles.value()) {
+ std::string new_file = subdir;
+ AppendPath(&new_file, subfile);
+ files.push_back(new_file);
+ }
+ }
+ return files;
+}
+
} // namespace file
} // namespace aapt
diff --git a/tools/aapt2/util/Files.h b/tools/aapt2/util/Files.h
index 95c492f..b3b1e48 100644
--- a/tools/aapt2/util/Files.h
+++ b/tools/aapt2/util/Files.h
@@ -132,6 +132,11 @@
std::vector<std::string> pattern_tokens_;
};
+// Returns a list of files relative to the directory identified by `path`.
+// An optional FileFilter filters out any files that don't pass.
+Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDiagnostics* diag,
+ const FileFilter* filter = nullptr);
+
} // namespace file
} // namespace aapt