summaryrefslogtreecommitdiff
path: root/tools/aapt2/cmd/Link.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/aapt2/cmd/Link.cpp')
-rw-r--r--tools/aapt2/cmd/Link.cpp955
1 files changed, 534 insertions, 421 deletions
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 5fc35b811213..d782de55f66a 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -15,8 +15,8 @@
*/
#include <sys/stat.h>
+#include <cinttypes>
-#include <fstream>
#include <queue>
#include <unordered_map>
#include <vector>
@@ -25,22 +25,28 @@
#include "android-base/file.h"
#include "android-base/stringprintf.h"
#include "androidfw/StringPiece.h"
-#include "google/protobuf/io/coded_stream.h"
#include "AppInfo.h"
#include "Debug.h"
#include "Flags.h"
+#include "LoadedApk.h"
#include "Locale.h"
#include "NameMangler.h"
#include "ResourceUtils.h"
+#include "ResourceValues.h"
+#include "ValueVisitor.h"
#include "cmd/Util.h"
#include "compile/IdAssigner.h"
+#include "compile/XmlIdCollector.h"
#include "filter/ConfigFilter.h"
-#include "flatten/Archive.h"
-#include "flatten/TableFlattener.h"
-#include "flatten/XmlFlattener.h"
-#include "io/BigBufferInputStream.h"
-#include "io/FileInputStream.h"
+#include "format/Archive.h"
+#include "format/Container.h"
+#include "format/binary/TableFlattener.h"
+#include "format/binary/XmlFlattener.h"
+#include "format/proto/ProtoDeserialize.h"
+#include "format/proto/ProtoSerialize.h"
+#include "io/BigBufferStream.h"
+#include "io/FileStream.h"
#include "io/FileSystem.h"
#include "io/Util.h"
#include "io/ZipArchive.h"
@@ -56,9 +62,7 @@
#include "optimize/VersionCollapser.h"
#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
-#include "proto/ProtoSerialize.h"
#include "split/TableSplitter.h"
-#include "unflatten/BinaryResourceParser.h"
#include "util/Files.h"
#include "xml/XmlDom.h"
@@ -68,6 +72,11 @@ using ::android::base::StringPrintf;
namespace aapt {
+enum class OutputFormat {
+ kApk,
+ kProto,
+};
+
struct LinkOptions {
std::string output_path;
std::string manifest_path;
@@ -76,6 +85,7 @@ struct LinkOptions {
std::vector<std::string> assets_dirs;
bool output_to_directory = false;
bool auto_add_overlay = false;
+ OutputFormat output_format = OutputFormat::kApk;
// Java/Proguard options.
Maybe<std::string> generate_java_class_path;
@@ -84,6 +94,7 @@ struct LinkOptions {
Maybe<std::string> generate_text_symbols_path;
Maybe<std::string> generate_proguard_rules_path;
Maybe<std::string> generate_main_dex_proguard_rules_path;
+ bool generate_conditional_proguard_rules = false;
bool generate_non_final_ids = false;
std::vector<std::string> javadoc_annotations;
Maybe<std::string> private_symbols;
@@ -202,6 +213,8 @@ class LinkContext : public IAaptContext {
// This delegate will attempt to masquerade any '@id/' references with ID 0xPPTTEEEE,
// where PP > 7f, as 0x7fPPEEEE. Any potential overlapping is verified and an error occurs if such
// an overlap exists.
+//
+// See b/37498913.
class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
public:
FeatureSplitSymbolTableDelegate(IAaptContext* context) : context_(context) {
@@ -250,41 +263,39 @@ class FeatureSplitSymbolTableDelegate : public DefaultSymbolTableDelegate {
IAaptContext* context_;
};
-static bool FlattenXml(IAaptContext* context, xml::XmlResource* xml_res, const StringPiece& path,
- bool keep_raw_values, bool utf16, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- XmlFlattenerOptions options = {};
- options.keep_raw_values = keep_raw_values;
- options.use_utf16 = utf16;
- XmlFlattener flattener(&buffer, options);
- if (!flattener.Consume(context, xml_res)) {
- return false;
- }
-
+static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml_res,
+ const StringPiece& path, bool keep_raw_values, bool utf16,
+ OutputFormat format, IArchiveWriter* writer) {
if (context->IsVerbose()) {
context->GetDiagnostics()->Note(DiagMessage(path) << "writing to archive (keep_raw_values="
<< (keep_raw_values ? "true" : "false")
<< ")");
}
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(),
- ArchiveEntry::kCompress, writer);
-}
+ switch (format) {
+ case OutputFormat::kApk: {
+ BigBuffer buffer(1024);
+ XmlFlattenerOptions options = {};
+ options.keep_raw_values = keep_raw_values;
+ options.use_utf16 = utf16;
+ XmlFlattener flattener(&buffer, options);
+ if (!flattener.Consume(context, &xml_res)) {
+ return false;
+ }
-static std::unique_ptr<ResourceTable> LoadTableFromPb(const Source& source, const void* data,
- size_t len, IDiagnostics* diag) {
- pb::ResourceTable pb_table;
- if (!pb_table.ParseFromArray(data, len)) {
- diag->Error(DiagMessage(source) << "invalid compiled table");
- return {};
- }
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context, &input_stream, path.to_string(),
+ ArchiveEntry::kCompress, writer);
+ } break;
- std::unique_ptr<ResourceTable> table = DeserializeTableFromPb(pb_table, source, diag);
- if (!table) {
- return {};
+ case OutputFormat::kProto: {
+ pb::XmlNode pb_node;
+ SerializeXmlResourceToPb(xml_res, &pb_node);
+ return io::CopyProtoToArchive(context, &pb_node, path.to_string(), ArchiveEntry::kCompress,
+ writer);
+ } break;
}
- return table;
+ return false;
}
// Inflates an XML file from the source path.
@@ -305,6 +316,7 @@ struct ResourceFileFlattenerOptions {
bool keep_raw_values = false;
bool do_not_compress_anything = false;
bool update_proguard_spec = false;
+ OutputFormat output_format = OutputFormat::kApk;
std::unordered_set<std::string> extensions_to_not_compress;
};
@@ -458,15 +470,20 @@ std::vector<std::unique_ptr<xml::XmlResource>> ResourceFileFlattener::LinkAndVer
const Source& src = doc->file.source;
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "linking " << src.path);
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "linking " << src.path << " (" << doc->file.name << ")");
}
+ // First, strip out any tools namespace attributes. AAPT stripped them out early, which means
+ // that existing projects have out-of-date references which pass compilation.
+ xml::StripAndroidStudioAttributes(doc->root.get());
+
XmlReferenceLinker xml_linker;
if (!xml_linker.Consume(context_, doc)) {
return {};
}
- if (options_.update_proguard_spec && !proguard::CollectProguardRules(src, doc, keep_set_)) {
+ if (options_.update_proguard_spec && !proguard::CollectProguardRules(doc, keep_set_)) {
return {};
}
@@ -505,7 +522,11 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
bool error = false;
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
+ proguard::CollectResourceReferences(context_, table, keep_set_);
+
for (auto& pkg : table->packages) {
+ CHECK(!pkg->name.empty()) << "Packages must have names when being linked";
+
for (auto& type : pkg->types) {
// Sort by config and name, so that we get better locality in the zip file.
config_sorted_files.clear();
@@ -535,9 +556,9 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
file_op.config = config_value->config;
file_op.file_to_copy = file;
- const StringPiece src_path = file->GetSource().path;
if (type->type != ResourceType::kRaw &&
- (util::EndsWith(src_path, ".xml.flat") || util::EndsWith(src_path, ".xml"))) {
+ (file_ref->type == ResourceFile::Type::kBinaryXml ||
+ file_ref->type == ResourceFile::Type::kProtoXml)) {
std::unique_ptr<io::IData> data = file->OpenAsData();
if (!data) {
context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
@@ -545,11 +566,29 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
return false;
}
- file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(),
- context_->GetDiagnostics(), file->GetSource());
+ if (file_ref->type == ResourceFile::Type::kProtoXml) {
+ pb::XmlNode pb_xml_node;
+ if (!pb_xml_node.ParseFromArray(data->data(), static_cast<int>(data->size()))) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to parse proto XML");
+ return false;
+ }
- if (!file_op.xml_to_flatten) {
- return false;
+ std::string error;
+ file_op.xml_to_flatten = DeserializeXmlResourceFromPb(pb_xml_node, &error);
+ if (file_op.xml_to_flatten == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to deserialize proto XML: " << error);
+ return false;
+ }
+ } else {
+ std::string error_str;
+ file_op.xml_to_flatten = xml::Inflate(data->data(), data->size(), &error_str);
+ if (file_op.xml_to_flatten == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(file->GetSource())
+ << "failed to parse binary XML: " << error_str);
+ return false;
+ }
}
file_op.xml_to_flatten->file.config = config_value->config;
@@ -599,8 +638,9 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
return false;
}
}
- error |= !FlattenXml(context_, doc.get(), dst_path, options_.keep_raw_values,
- false /*utf16*/, archive_writer);
+
+ error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
+ false /*utf16*/, options_.output_format, archive_writer);
}
} else {
error |= !io::CopyFileToArchive(context_, file_op.file_to_copy, file_op.dst_path,
@@ -615,24 +655,26 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
static bool WriteStableIdMapToPath(IDiagnostics* diag,
const std::unordered_map<ResourceName, ResourceId>& id_map,
const std::string& id_map_path) {
- std::ofstream fout(id_map_path, std::ofstream::binary);
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << strerror(errno));
+ io::FileOutputStream fout(id_map_path);
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed to open: " << fout.GetError());
return false;
}
+ text::Printer printer(&fout);
for (const auto& entry : id_map) {
const ResourceName& name = entry.first;
const ResourceId& id = entry.second;
- fout << name << " = " << id << "\n";
+ printer.Print(name.to_string());
+ printer.Print(" = ");
+ printer.Println(id.to_string());
}
+ fout.Flush();
- if (!fout) {
- diag->Error(DiagMessage(id_map_path) << "failed writing to file: "
- << android::base::SystemErrorCodeToString(errno));
+ if (fout.HadError()) {
+ diag->Error(DiagMessage(id_map_path) << "failed writing to file: " << fout.GetError());
return false;
}
-
return true;
}
@@ -684,6 +726,30 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path,
return true;
}
+static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) {
+ using namespace android;
+
+ // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so
+ // we're looking for the first attribute resource in the system package.
+ const ResTable& table = assets.getResources(true);
+ Res_value val;
+ ssize_t idx = table.getResource(0x01010000, &val, true);
+ if (idx != NO_ERROR) {
+ // Try as a bag.
+ const ResTable::bag_entry* entry;
+ ssize_t cnt = table.lockBag(0x01010000, &entry);
+ if (cnt >= 0) {
+ idx = entry->stringBlock;
+ }
+ table.unlockBag(entry);
+ }
+
+ if (idx < 0) {
+ return 0;
+ }
+ return table.getTableCookie(idx);
+}
+
class LinkCommand {
public:
LinkCommand(LinkContext* context, const LinkOptions& options)
@@ -693,22 +759,87 @@ class LinkCommand {
file_collection_(util::make_unique<io::FileCollection>()) {
}
- /**
- * Creates a SymbolTable that loads symbols from the various APKs and caches
- * the results for faster lookup.
- */
+ void ExtractCompileSdkVersions(android::AssetManager* assets) {
+ using namespace android;
+
+ int32_t cookie = FindFrameworkAssetManagerCookie(*assets);
+ if (cookie == 0) {
+ // No Framework assets loaded. Not a failure.
+ return;
+ }
+
+ std::unique_ptr<Asset> manifest(
+ assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER));
+ if (manifest == nullptr) {
+ // No errors.
+ return;
+ }
+
+ std::string error;
+ std::unique_ptr<xml::XmlResource> manifest_xml =
+ xml::Inflate(manifest->getBuffer(true /*wordAligned*/), manifest->getLength(), &error);
+ if (manifest_xml == nullptr) {
+ // No errors.
+ return;
+ }
+
+ xml::Attribute* attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionCode");
+ if (attr != nullptr) {
+ Maybe<std::string>& compile_sdk_version = options_.manifest_fixer_options.compile_sdk_version;
+ if (BinaryPrimitive* prim = ValueCast<BinaryPrimitive>(attr->compiled_value.get())) {
+ switch (prim->value.dataType) {
+ case Res_value::TYPE_INT_DEC:
+ compile_sdk_version = StringPrintf("%" PRId32, static_cast<int32_t>(prim->value.data));
+ break;
+ case Res_value::TYPE_INT_HEX:
+ compile_sdk_version = StringPrintf("%" PRIx32, prim->value.data);
+ break;
+ default:
+ break;
+ }
+ } else if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+ compile_sdk_version = *str->value;
+ } else {
+ compile_sdk_version = attr->value;
+ }
+ }
+
+ attr = manifest_xml->root->FindAttribute(xml::kSchemaAndroid, "versionName");
+ if (attr != nullptr) {
+ Maybe<std::string>& compile_sdk_version_codename =
+ options_.manifest_fixer_options.compile_sdk_version_codename;
+ if (String* str = ValueCast<String>(attr->compiled_value.get())) {
+ compile_sdk_version_codename = *str->value;
+ } else {
+ compile_sdk_version_codename = attr->value;
+ }
+ }
+ }
+
+ // Creates a SymbolTable that loads symbols from the various APKs.
+ // Pre-condition: context_->GetCompilationPackage() needs to be set.
bool LoadSymbolsFromIncludePaths() {
- std::unique_ptr<AssetManagerSymbolSource> asset_source =
- util::make_unique<AssetManagerSymbolSource>();
+ auto asset_source = util::make_unique<AssetManagerSymbolSource>();
for (const std::string& path : options_.include_paths) {
if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage(path) << "loading include path");
+ context_->GetDiagnostics()->Note(DiagMessage() << "including " << path);
}
- // First try to load the file as a static lib.
- std::string error_str;
- std::unique_ptr<ResourceTable> include_static = LoadStaticLibrary(path, &error_str);
- if (include_static) {
+ std::string error;
+ auto zip_collection = io::ZipFileCollection::Create(path, &error);
+ if (zip_collection == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open APK: " << error);
+ return false;
+ }
+
+ if (zip_collection->FindFile(kProtoResourceTablePath) != nullptr) {
+ // Load this as a static library include.
+ std::unique_ptr<LoadedApk> static_apk = LoadedApk::LoadProtoApkFromFileCollection(
+ Source(path), std::move(zip_collection), context_->GetDiagnostics());
+ if (static_apk == nullptr) {
+ return false;
+ }
+
if (context_->GetPackageType() != PackageType::kStaticLib) {
// Can't include static libraries when not building a static library (they have no IDs
// assigned).
@@ -717,13 +848,15 @@ class LinkCommand {
return false;
}
- // If we are using --no-static-lib-packages, we need to rename the
- // package of this table to our compilation package.
+ ResourceTable* table = static_apk->GetResourceTable();
+
+ // If we are using --no-static-lib-packages, we need to rename the package of this table to
+ // our compilation package.
if (options_.no_static_lib_packages) {
// Since package names can differ, and multiple packages can exist in a ResourceTable,
// we place the requirement that all static libraries are built with the package
// ID 0x7f. So if one is not found, this is an error.
- if (ResourceTablePackage* pkg = include_static->FindPackageById(kAppPackageId)) {
+ if (ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId)) {
pkg->name = context_->GetCompilationPackage();
} else {
context_->GetDiagnostics()->Error(DiagMessage(path)
@@ -733,19 +866,14 @@ class LinkCommand {
}
context_->GetExternalSymbols()->AppendSource(
- util::make_unique<ResourceTableSymbolSource>(include_static.get()));
-
- static_table_includes_.push_back(std::move(include_static));
-
- } else if (!error_str.empty()) {
- // We had an error with reading, so fail.
- context_->GetDiagnostics()->Error(DiagMessage(path) << error_str);
- return false;
- }
-
- if (!asset_source->AddAssetPath(path)) {
- context_->GetDiagnostics()->Error(DiagMessage(path) << "failed to load include path");
- return false;
+ util::make_unique<ResourceTableSymbolSource>(table));
+ static_library_includes_.push_back(std::move(static_apk));
+ } else {
+ if (!asset_source->AddAssetPath(path)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to load include path " << path);
+ return false;
+ }
}
}
@@ -757,6 +885,17 @@ class LinkCommand {
} else if (entry.first == kAppPackageId) {
// Capture the included base feature package.
included_feature_base_ = entry.second;
+ } else if (entry.first == kFrameworkPackageId) {
+ // Try to embed which version of the framework we're compiling against.
+ // First check if we should use compileSdkVersion at all. Otherwise compilation may fail
+ // when linking our synthesized 'android:compileSdkVersion' attribute.
+ std::unique_ptr<SymbolTable::Symbol> symbol = asset_source->FindByName(
+ ResourceName("android", ResourceType::kAttr, "compileSdkVersion"));
+ if (symbol != nullptr && symbol->is_public) {
+ // The symbol is present and public, extract the android:versionName and
+ // android:versionCode from the framework AndroidManifest.xml.
+ ExtractCompileSdkVersions(asset_source->GetAssetManager());
+ }
}
}
@@ -823,11 +962,9 @@ class LinkCommand {
return app_info;
}
- /**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
- * Postcondition: ResourceTable has only one package left. All others are
- * stripped, or there is an error and false is returned.
- */
+ // Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
+ // Postcondition: ResourceTable has only one package left. All others are
+ // stripped, or there is an error and false is returned.
bool VerifyNoExternalPackages() {
auto is_ext_package_func = [&](const std::unique_ptr<ResourceTablePackage>& pkg) -> bool {
return context_->GetCompilationPackage() != pkg->name || !pkg->id ||
@@ -904,72 +1041,159 @@ class LinkCommand {
}
}
- bool FlattenTable(ResourceTable* table, IArchiveWriter* writer) {
- BigBuffer buffer(1024);
- TableFlattener flattener(options_.table_flattener_options, &buffer);
- if (!flattener.Consume(context_, table)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table");
- return false;
- }
+ bool FlattenTable(ResourceTable* table, OutputFormat format, IArchiveWriter* writer) {
+ switch (format) {
+ case OutputFormat::kApk: {
+ BigBuffer buffer(1024);
+ TableFlattener flattener(options_.table_flattener_options, &buffer);
+ if (!flattener.Consume(context_, table)) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to flatten resource table");
+ return false;
+ }
- io::BigBufferInputStream input_stream(&buffer);
- return io::CopyInputStreamToArchive(context_, &input_stream, "resources.arsc",
- ArchiveEntry::kAlign, writer);
- }
+ io::BigBufferInputStream input_stream(&buffer);
+ return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
+ ArchiveEntry::kAlign, writer);
+ } break;
- bool FlattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
- std::unique_ptr<pb::ResourceTable> pb_table = SerializeTableToPb(table);
- return io::CopyProtoToArchive(context_, pb_table.get(), "resources.arsc.flat", 0, writer);
+ case OutputFormat::kProto: {
+ pb::ResourceTable pb_table;
+ SerializeTableToPb(*table, &pb_table);
+ return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath,
+ ArchiveEntry::kCompress, writer);
+ } break;
+ }
+ return false;
}
bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
- const Maybe<std::string> out_text_symbols_path = {}) {
- if (!options_.generate_java_class_path) {
+ const Maybe<std::string>& out_text_symbols_path = {}) {
+ if (!options_.generate_java_class_path && !out_text_symbols_path) {
return true;
}
- std::string out_path = options_.generate_java_class_path.value();
- file::AppendPath(&out_path, file::PackageToPath(out_package));
- if (!file::mkdirs(out_path)) {
- context_->GetDiagnostics()->Error(DiagMessage() << "failed to create directory '" << out_path
- << "'");
- return false;
- }
+ std::string out_path;
+ std::unique_ptr<io::FileOutputStream> fout;
+ if (options_.generate_java_class_path) {
+ out_path = options_.generate_java_class_path.value();
+ file::AppendPath(&out_path, file::PackageToPath(out_package));
+ if (!file::mkdirs(out_path)) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed to create directory '" << out_path << "'");
+ return false;
+ }
- file::AppendPath(&out_path, "R.java");
+ file::AppendPath(&out_path, "R.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
- return false;
+ fout = util::make_unique<io::FileOutputStream>(out_path);
+ if (fout->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout->GetError());
+ return false;
+ }
}
- std::unique_ptr<std::ofstream> fout_text;
+ std::unique_ptr<io::FileOutputStream> fout_text;
if (out_text_symbols_path) {
- fout_text =
- util::make_unique<std::ofstream>(out_text_symbols_path.value(), std::ofstream::binary);
- if (!*fout_text) {
- context_->GetDiagnostics()->Error(
- DiagMessage() << "failed writing to '" << out_text_symbols_path.value()
- << "': " << android::base::SystemErrorCodeToString(errno));
+ fout_text = util::make_unique<io::FileOutputStream>(out_text_symbols_path.value());
+ if (fout_text->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage()
+ << "failed writing to '" << out_text_symbols_path.value()
+ << "': " << fout_text->GetError());
return false;
}
}
JavaClassGenerator generator(context_, table, java_options);
- if (!generator.Generate(package_name_to_generate, out_package, &fout, fout_text.get())) {
- context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.getError());
+ if (!generator.Generate(package_name_to_generate, out_package, fout.get(), fout_text.get())) {
+ context_->GetDiagnostics()->Error(DiagMessage(out_path) << generator.GetError());
return false;
}
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ return true;
+ }
+
+ bool GenerateJavaClasses() {
+ // The set of packages whose R class to call in the main classes onResourcesLoaded callback.
+ std::vector<std::string> packages_to_callback;
+
+ JavaClassGeneratorOptions template_options;
+ template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ template_options.javadoc_annotations = options_.javadoc_annotations;
+
+ if (context_->GetPackageType() == PackageType::kStaticLib || options_.generate_non_final_ids) {
+ template_options.use_final = false;
+ }
+
+ if (context_->GetPackageType() == PackageType::kSharedLib) {
+ template_options.use_final = false;
+ template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
+ }
+
+ const StringPiece actual_package = context_->GetCompilationPackage();
+ StringPiece output_package = context_->GetCompilationPackage();
+ if (options_.custom_java_package) {
+ // Override the output java package to the custom one.
+ output_package = options_.custom_java_package.value();
+ }
+
+ // Generate the private symbols if required.
+ if (options_.private_symbols) {
+ packages_to_callback.push_back(options_.private_symbols.value());
+
+ // If we defined a private symbols package, we only emit Public symbols
+ // to the original package, and private and public symbols to the private package.
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
+ if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
+ options)) {
+ return false;
+ }
+ }
+
+ // Generate copies of the original package R class but with different package names.
+ // This is to support non-namespaced builds.
+ for (const std::string& extra_package : options_.extra_java_packages) {
+ packages_to_callback.push_back(extra_package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
+ return false;
+ }
+ }
+
+ // Generate R classes for each package that was merged (static library).
+ // Use the actual package's resources only.
+ for (const std::string& package : table_merger_->merged_packages()) {
+ packages_to_callback.push_back(package);
+
+ JavaClassGeneratorOptions options = template_options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+ if (!WriteJavaFile(&final_table_, package, package, options)) {
+ return false;
+ }
+ }
+
+ // Generate the main public R class.
+ JavaClassGeneratorOptions options = template_options;
+
+ // Only generate public symbols if we have a private package.
+ if (options_.private_symbols) {
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
}
+
+ if (options.rewrite_callback_options) {
+ options.rewrite_callback_options.value().packages_to_callback =
+ std::move(packages_to_callback);
+ }
+
+ if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
+ options_.generate_text_symbols_path)) {
+ return false;
+ }
+
return true;
}
@@ -1012,18 +1236,19 @@ class LinkCommand {
file::AppendPath(&out_path, "Manifest.java");
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- if (!ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout)) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ ClassDefinition::WriteJavaFile(manifest_class.get(), package_utf8, true, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
@@ -1035,64 +1260,36 @@ class LinkCommand {
}
const std::string& out_path = out.value();
- std::ofstream fout(out_path, std::ofstream::binary);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed to open '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ io::FileOutputStream fout(out_path);
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed to open '" << out_path
+ << "': " << fout.GetError());
return false;
}
- proguard::WriteKeepSet(&fout, keep_set);
- if (!fout) {
- context_->GetDiagnostics()->Error(DiagMessage()
- << "failed writing to '" << out_path
- << "': " << android::base::SystemErrorCodeToString(errno));
+ proguard::WriteKeepSet(keep_set, &fout);
+ fout.Flush();
+
+ if (fout.HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage() << "failed writing to '" << out_path
+ << "': " << fout.GetError());
return false;
}
return true;
}
- std::unique_ptr<ResourceTable> LoadStaticLibrary(const std::string& input,
- std::string* out_error) {
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::Create(input, out_error);
- if (!collection) {
- return {};
- }
- return LoadTablePbFromCollection(collection.get());
- }
-
- std::unique_ptr<ResourceTable> LoadTablePbFromCollection(io::IFileCollection* collection) {
- io::IFile* file = collection->FindFile("resources.arsc.flat");
- if (!file) {
- return {};
- }
-
- std::unique_ptr<io::IData> data = file->OpenAsData();
- return LoadTableFromPb(file->GetSource(), data->data(), data->size(),
- context_->GetDiagnostics());
- }
-
bool MergeStaticLibrary(const std::string& input, bool override) {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage() << "merging static library " << input);
}
- std::string error_str;
- std::unique_ptr<io::ZipFileCollection> collection =
- io::ZipFileCollection::Create(input, &error_str);
- if (!collection) {
- context_->GetDiagnostics()->Error(DiagMessage(input) << error_str);
- return false;
- }
-
- std::unique_ptr<ResourceTable> table = LoadTablePbFromCollection(collection.get());
- if (!table) {
+ std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(input, context_->GetDiagnostics());
+ if (apk == nullptr) {
context_->GetDiagnostics()->Error(DiagMessage(input) << "invalid static library");
return false;
}
+ ResourceTable* table = apk->GetResourceTable();
ResourceTablePackage* pkg = table->FindPackageById(kAppPackageId);
if (!pkg) {
context_->GetDiagnostics()->Error(DiagMessage(input) << "static library has no package");
@@ -1101,27 +1298,24 @@ class LinkCommand {
bool result;
if (options_.no_static_lib_packages) {
- // Merge all resources as if they were in the compilation package. This is
- // the old behavior of aapt.
+ // Merge all resources as if they were in the compilation package. This is the old behavior
+ // of aapt.
- // Add the package to the set of --extra-packages so we emit an R.java for
- // each library package.
+ // Add the package to the set of --extra-packages so we emit an R.java for each library
+ // package.
if (!pkg->name.empty()) {
options_.extra_java_packages.insert(pkg->name);
}
+ // Clear the package name, so as to make the resources look like they are coming from the
+ // local package.
pkg->name = "";
- if (override) {
- result = table_merger_->MergeOverlay(Source(input), table.get(), collection.get());
- } else {
- result = table_merger_->Merge(Source(input), table.get(), collection.get());
- }
+ result = table_merger_->Merge(Source(input), table, override);
} else {
// This is the proper way to merge libraries, where the package name is
// preserved and resource names are mangled.
- result =
- table_merger_->MergeAndMangle(Source(input), pkg->name, table.get(), collection.get());
+ result = table_merger_->MergeAndMangle(Source(input), pkg->name, table);
}
if (!result) {
@@ -1129,71 +1323,26 @@ class LinkCommand {
}
// Make sure to move the collection into the set of IFileCollections.
- collections_.push_back(std::move(collection));
+ merged_apks_.push_back(std::move(apk));
return true;
}
- bool MergeResourceTable(io::IFile* file, bool override) {
- if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "merging resource table "
- << file->GetSource());
- }
-
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context_->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << "failed to open file");
- return false;
- }
-
- std::unique_ptr<ResourceTable> table =
- LoadTableFromPb(file->GetSource(), data->data(), data->size(), context_->GetDiagnostics());
- if (!table) {
- return false;
- }
-
- bool result = false;
- if (override) {
- result = table_merger_->MergeOverlay(file->GetSource(), table.get());
- } else {
- result = table_merger_->Merge(file->GetSource(), table.get());
- }
- return result;
- }
-
- bool MergeCompiledFile(io::IFile* file, ResourceFile* file_desc, bool override) {
- if (context_->IsVerbose()) {
- context_->GetDiagnostics()->Note(DiagMessage() << "merging '" << file_desc->name
- << "' from compiled file "
- << file->GetSource());
- }
-
- bool result = false;
- if (override) {
- result = table_merger_->MergeFileOverlay(*file_desc, file);
- } else {
- result = table_merger_->MergeFile(*file_desc, file);
- }
-
- if (!result) {
- return false;
- }
-
+ bool MergeExportedSymbols(const Source& source,
+ const std::vector<SourcedResourceName>& exported_symbols) {
// Add the exports of this file to the table.
- for (SourcedResourceName& exported_symbol : file_desc->exported_symbols) {
- if (exported_symbol.name.package.empty()) {
- exported_symbol.name.package = context_->GetCompilationPackage();
+ for (const SourcedResourceName& exported_symbol : exported_symbols) {
+ ResourceName res_name = exported_symbol.name;
+ if (res_name.package.empty()) {
+ res_name.package = context_->GetCompilationPackage();
}
- ResourceNameRef res_name = exported_symbol.name;
-
- Maybe<ResourceName> mangled_name =
- context_->GetNameMangler()->MangleName(exported_symbol.name);
+ Maybe<ResourceName> mangled_name = context_->GetNameMangler()->MangleName(res_name);
if (mangled_name) {
res_name = mangled_name.value();
}
std::unique_ptr<Id> id = util::make_unique<Id>();
- id->SetSource(file_desc->source.WithLine(exported_symbol.line));
+ id->SetSource(source.WithLine(exported_symbol.line));
bool result = final_table_.AddResourceAllowMangled(
res_name, ConfigDescription::DefaultConfig(), std::string(), std::move(id),
context_->GetDiagnostics());
@@ -1204,15 +1353,24 @@ class LinkCommand {
return true;
}
- /**
- * Takes a path to load as a ZIP file and merges the files within into the
- * master ResourceTable.
- * If override is true, conflicting resources are allowed to override each
- * other, in order of last seen.
- *
- * An io::IFileCollection is created from the ZIP file and added to the set of
- * io::IFileCollections that are open.
- */
+ bool MergeCompiledFile(const ResourceFile& compiled_file, io::IFile* file, bool override) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Note(DiagMessage()
+ << "merging '" << compiled_file.name
+ << "' from compiled file " << compiled_file.source);
+ }
+
+ if (!table_merger_->MergeFile(compiled_file, override, file)) {
+ return false;
+ }
+ return MergeExportedSymbols(compiled_file.source, compiled_file.exported_symbols);
+ }
+
+ // Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
+ // If override is true, conflicting resources are allowed to override each other, in order of last
+ // seen.
+ // An io::IFileCollection is created from the ZIP file and added to the set of
+ // io::IFileCollections that are open.
bool MergeArchive(const std::string& input, bool override) {
if (context_->IsVerbose()) {
context_->GetDiagnostics()->Note(DiagMessage() << "merging archive " << input);
@@ -1238,18 +1396,11 @@ class LinkCommand {
return !error;
}
- /**
- * Takes a path to load and merge into the master ResourceTable. If override
- * is true,
- * conflicting resources are allowed to override each other, in order of last
- * seen.
- *
- * If the file path ends with .flata, .jar, .jack, or .zip the file is treated
- * as ZIP archive
- * and the files within are merged individually.
- *
- * Otherwise the files is processed on its own.
- */
+ // Takes a path to load and merge into the master ResourceTable. If override is true,
+ // conflicting resources are allowed to override each other, in order of last seen.
+ // If the file path ends with .flata, .jar, .jack, or .zip the file is treated
+ // as ZIP archive and the files within are merged individually.
+ // Otherwise the file is processed on its own.
bool MergePath(const std::string& path, bool override) {
if (util::EndsWith(path, ".flata") || util::EndsWith(path, ".jar") ||
util::EndsWith(path, ".jack") || util::EndsWith(path, ".zip")) {
@@ -1262,81 +1413,87 @@ class LinkCommand {
return MergeFile(file, override);
}
- /**
- * Takes a file to load and merge into the master ResourceTable. If override
- * is true,
- * conflicting resources are allowed to override each other, in order of last
- * seen.
- *
- * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and
- * merged into the
- * master ResourceTable. If the file ends with .flat, then it is treated like
- * a compiled file
- * and the header data is read and merged into the final ResourceTable.
- *
- * All other file types are ignored. This is because these files could be
- * coming from a zip,
- * where we could have other files like classes.dex.
- */
+ // Takes an AAPT Container file (.apc/.flat) to load and merge into the master ResourceTable.
+ // If override is true, conflicting resources are allowed to override each other, in order of last
+ // seen.
+ // All other file types are ignored. This is because these files could be coming from a zip,
+ // where we could have other files like classes.dex.
bool MergeFile(io::IFile* file, bool override) {
const Source& src = file->GetSource();
- if (util::EndsWith(src.path, ".arsc.flat")) {
- return MergeResourceTable(file, override);
-
- } else if (util::EndsWith(src.path, ".flat")) {
- // Try opening the file and looking for an Export header.
- std::unique_ptr<io::IData> data = file->OpenAsData();
- if (!data) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open");
- return false;
- }
- CompiledFileInputStream input_stream(data->data(), data->size());
- uint32_t num_files = 0;
- if (!input_stream.ReadLittleEndian32(&num_files)) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed read num files");
- return false;
+ if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
+ // Since AAPT compiles these file types and appends .flat to them, seeing
+ // their raw extensions is a sign that they weren't compiled.
+ const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type
+ << " file passed as argument. Must be "
+ "compiled first into .flat file.");
+ return false;
+ } else if (!util::EndsWith(src.path, ".apc") && !util::EndsWith(src.path, ".flat")) {
+ if (context_->IsVerbose()) {
+ context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring unrecognized file");
+ return true;
}
+ }
- for (uint32_t i = 0; i < num_files; i++) {
- pb::internal::CompiledFile compiled_file;
- if (!input_stream.ReadCompiledFile(&compiled_file)) {
+ std::unique_ptr<io::InputStream> input_stream = file->OpenInputStream();
+ if (input_stream == nullptr) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to open file");
+ return false;
+ }
+
+ if (input_stream->HadError()) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to open file: " << input_stream->GetError());
+ return false;
+ }
+
+ ContainerReaderEntry* entry;
+ ContainerReader reader(input_stream.get());
+ while ((entry = reader.Next()) != nullptr) {
+ if (entry->Type() == ContainerEntryType::kResTable) {
+ pb::ResourceTable pb_table;
+ if (!entry->GetResTable(&pb_table)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read resource table: "
+ << entry->GetError());
+ return false;
+ }
+
+ ResourceTable table;
+ std::string error;
+ if (!DeserializeTableFromPb(pb_table, nullptr /*files*/, &table, &error)) {
context_->GetDiagnostics()->Error(DiagMessage(src)
- << "failed to read compiled file header");
+ << "failed to deserialize resource table: " << error);
return false;
}
- uint64_t offset, len;
- if (!input_stream.ReadDataMetaData(&offset, &len)) {
- context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to read data meta data");
+ if (!table_merger_->Merge(src, &table, override)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to merge resource table");
+ return false;
+ }
+ } else if (entry->Type() == ContainerEntryType::kResFile) {
+ pb::internal::CompiledFile pb_compiled_file;
+ off64_t offset;
+ size_t len;
+ if (!entry->GetResFileOffsets(&pb_compiled_file, &offset, &len)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src) << "failed to get resource file: "
+ << entry->GetError());
return false;
}
- std::unique_ptr<ResourceFile> resource_file = DeserializeCompiledFileFromPb(
- compiled_file, file->GetSource(), context_->GetDiagnostics());
- if (!resource_file) {
+ ResourceFile resource_file;
+ std::string error;
+ if (!DeserializeCompiledFileFromPb(pb_compiled_file, &resource_file, &error)) {
+ context_->GetDiagnostics()->Error(DiagMessage(src)
+ << "failed to read compiled header: " << error);
return false;
}
- if (!MergeCompiledFile(file->CreateFileSegment(offset, len), resource_file.get(),
- override)) {
+ if (!MergeCompiledFile(resource_file, file->CreateFileSegment(offset, len), override)) {
return false;
}
}
- return true;
- } else if (util::EndsWith(src.path, ".xml") || util::EndsWith(src.path, ".png")) {
- // Since AAPT compiles these file types and appends .flat to them, seeing
- // their raw extensions is a sign that they weren't compiled.
- const StringPiece file_type = util::EndsWith(src.path, ".xml") ? "XML" : "PNG";
- context_->GetDiagnostics()->Error(DiagMessage(src) << "uncompiled " << file_type
- << " file passed as argument. Must be "
- "compiled first into .flat file.");
- return false;
}
-
- // Ignore non .flat files. This could be classes.dex or something else that
- // happens
- // to be in an archive.
return true;
}
@@ -1380,15 +1537,13 @@ class LinkCommand {
return true;
}
- /**
- * Writes the AndroidManifest, ResourceTable, and all XML files referenced by
- * the ResourceTable to the IArchiveWriter.
- */
+ // Writes the AndroidManifest, ResourceTable, and all XML files referenced by the ResourceTable
+ // to the IArchiveWriter.
bool WriteApk(IArchiveWriter* writer, proguard::KeepSet* keep_set, xml::XmlResource* manifest,
ResourceTable* table) {
const bool keep_raw_values = context_->GetPackageType() == PackageType::kStaticLib;
- bool result = FlattenXml(context_, manifest, "AndroidManifest.xml", keep_raw_values,
- true /*utf16*/, writer);
+ bool result = FlattenXml(context_, *manifest, "AndroidManifest.xml", keep_raw_values,
+ true /*utf16*/, options_.output_format, writer);
if (!result) {
return false;
}
@@ -1403,6 +1558,7 @@ class LinkCommand {
file_flattener_options.no_xml_namespaces = options_.no_xml_namespaces;
file_flattener_options.update_proguard_spec =
static_cast<bool>(options_.generate_proguard_rules_path);
+ file_flattener_options.output_format = options_.output_format;
ResourceFileFlattener file_flattener(file_flattener_options, context_, keep_set);
@@ -1439,12 +1595,7 @@ class LinkCommand {
}
}
- bool success;
- if (context_->GetPackageType() == PackageType::kStaticLib) {
- success = FlattenTableToPb(table, writer);
- } else {
- success = FlattenTable(table, writer);
- }
+ bool success = FlattenTable(table, options_.output_format, writer);
if (package_to_rewrite != nullptr) {
// Change the name back.
@@ -1475,6 +1626,12 @@ class LinkCommand {
context_->SetCompilationPackage(app_info.package);
}
+ // Now that the compilation package is set, load the dependencies. This will also extract
+ // the Android framework's versionCode and versionName, if they exist.
+ if (!LoadSymbolsFromIncludePaths()) {
+ return 1;
+ }
+
ManifestFixer manifest_fixer(options_.manifest_fixer_options);
if (!manifest_fixer.Consume(context_, manifest_xml.get())) {
return 1;
@@ -1503,10 +1660,6 @@ class LinkCommand {
}
}
- if (!LoadSymbolsFromIncludePaths()) {
- return 1;
- }
-
TableMergerOptions table_merger_options;
table_merger_options.auto_add_overlay = options_.auto_add_overlay;
table_merger_ = util::make_unique<TableMerger>(context_, &final_table_, table_merger_options);
@@ -1518,6 +1671,19 @@ class LinkCommand {
context_->GetPackageId()));
}
+ // Extract symbols from AndroidManifest.xml, since this isn't merged like the other XML files
+ // in res/**/*.
+ {
+ XmlIdCollector collector;
+ if (!collector.Consume(context_, manifest_xml.get())) {
+ return false;
+ }
+
+ if (!MergeExportedSymbols(manifest_xml->file.source, manifest_xml->file.exported_symbols)) {
+ return false;
+ }
+ }
+
for (const std::string& input : input_files) {
if (!MergePath(input, false)) {
context_->GetDiagnostics()->Error(DiagMessage() << "failed parsing input");
@@ -1646,7 +1812,8 @@ class LinkCommand {
}
}
- proguard::KeepSet proguard_keep_set;
+ proguard::KeepSet proguard_keep_set =
+ proguard::KeepSet(options_.generate_conditional_proguard_rules);
proguard::KeepSet proguard_main_dex_keep_set;
if (context_->GetPackageType() == PackageType::kStaticLib) {
@@ -1714,8 +1881,7 @@ class LinkCommand {
bool error = false;
{
- // AndroidManifest.xml has no resource name, but the CallSite is built
- // from the name
+ // AndroidManifest.xml has no resource name, but the CallSite is built from the name
// (aka, which package the AndroidManifest.xml is coming from).
// So we give it a package name so it can see local resources.
manifest_xml->file.name.package = context_->GetCompilationPackage();
@@ -1723,14 +1889,12 @@ class LinkCommand {
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(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::CollectProguardRulesForManifest(manifest_xml.get(),
&proguard_main_dex_keep_set, true)) {
error = true;
}
@@ -1767,73 +1931,8 @@ class LinkCommand {
return 1;
}
- if (options_.generate_java_class_path) {
- // The set of packages whose R class to call in the main classes
- // onResourcesLoaded callback.
- std::vector<std::string> packages_to_callback;
-
- JavaClassGeneratorOptions template_options;
- template_options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- template_options.javadoc_annotations = options_.javadoc_annotations;
-
- if (context_->GetPackageType() == PackageType::kStaticLib ||
- options_.generate_non_final_ids) {
- template_options.use_final = false;
- }
-
- if (context_->GetPackageType() == PackageType::kSharedLib) {
- template_options.use_final = false;
- template_options.rewrite_callback_options = OnResourcesLoadedCallbackOptions{};
- }
-
- const StringPiece actual_package = context_->GetCompilationPackage();
- StringPiece output_package = context_->GetCompilationPackage();
- if (options_.custom_java_package) {
- // Override the output java package to the custom one.
- output_package = options_.custom_java_package.value();
- }
-
- // Generate the private symbols if required.
- if (options_.private_symbols) {
- packages_to_callback.push_back(options_.private_symbols.value());
-
- // If we defined a private symbols package, we only emit Public symbols
- // to the original package, and private and public symbols to the
- // private package.
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate;
- if (!WriteJavaFile(&final_table_, actual_package, options_.private_symbols.value(),
- options)) {
- return 1;
- }
- }
-
- // Generate all the symbols for all extra packages.
- for (const std::string& extra_package : options_.extra_java_packages) {
- packages_to_callback.push_back(extra_package);
-
- JavaClassGeneratorOptions options = template_options;
- options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
- if (!WriteJavaFile(&final_table_, actual_package, extra_package, options)) {
- return 1;
- }
- }
-
- // Generate the main public R class.
- JavaClassGeneratorOptions options = template_options;
-
- // Only generate public symbols if we have a private package.
- if (options_.private_symbols) {
- options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
- }
-
- if (options.rewrite_callback_options) {
- options.rewrite_callback_options.value().packages_to_callback =
- std::move(packages_to_callback);
- }
-
- if (!WriteJavaFile(&final_table_, actual_package, output_package, options,
- options_.generate_text_symbols_path)) {
+ if (options_.generate_java_class_path || options_.generate_text_symbols_path) {
+ if (!GenerateJavaClasses()) {
return 1;
}
}
@@ -1861,13 +1960,15 @@ class LinkCommand {
// A pointer to the FileCollection representing the filesystem (not archives).
std::unique_ptr<io::FileCollection> file_collection_;
- // A vector of IFileCollections. This is mainly here to keep ownership of the
+ // A vector of IFileCollections. This is mainly here to retain ownership of the
// collections.
std::vector<std::unique_ptr<io::IFileCollection>> collections_;
- // A vector of ResourceTables. This is here to retain ownership, so that the
- // SymbolTable can use these.
- std::vector<std::unique_ptr<ResourceTable>> static_table_includes_;
+ // The set of merged APKs. This is mainly here to retain ownership of the APKs.
+ std::vector<std::unique_ptr<LoadedApk>> merged_apks_;
+
+ // The set of included APKs (not merged). This is mainly here to retain ownership of the APKs.
+ std::vector<std::unique_ptr<LoadedApk>> static_library_includes_;
// The set of shared libraries being used, mapping their assigned package ID to package name.
std::map<size_t, std::string> shared_libs_;
@@ -1890,6 +1991,7 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
bool verbose = false;
bool shared_lib = false;
bool static_lib = false;
+ bool proto_format = false;
Maybe<std::string> stable_id_file_path;
std::vector<std::string> split_args;
Flags flags =
@@ -1916,6 +2018,9 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
.OptionalFlag("--proguard-main-dex",
"Output file for generated Proguard rules for the main dex.",
&options.generate_main_dex_proguard_rules_path)
+ .OptionalSwitch("--proguard-conditional-keep-rules",
+ "Generate conditional Proguard keep rules.",
+ &options.generate_conditional_proguard_rules)
.OptionalSwitch("--no-auto-version",
"Disables automatic style and layout SDK versioning.",
&options.no_auto_version)
@@ -1970,6 +2075,10 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
.OptionalSwitch("--shared-lib", "Generates a shared Android runtime library.",
&shared_lib)
.OptionalSwitch("--static-lib", "Generate a static Android library.", &static_lib)
+ .OptionalSwitch("--proto-format",
+ "Generates compiled resources in Protobuf format.\n"
+ "Suitable as input to the bundle tool for generating an App Bundle.",
+ &proto_format)
.OptionalSwitch("--no-static-lib-packages",
"Merge all library resources under the app's package.",
&options.no_static_lib_packages)
@@ -2056,21 +2165,25 @@ int Link(const std::vector<StringPiece>& args, IDiagnostics* diagnostics) {
context.SetVerbose(verbose);
}
- if (shared_lib && static_lib) {
- context.GetDiagnostics()->Error(DiagMessage()
- << "only one of --shared-lib and --static-lib can be defined");
+ if (int{shared_lib} + int{static_lib} + int{proto_format} > 1) {
+ context.GetDiagnostics()->Error(
+ DiagMessage()
+ << "only one of --shared-lib, --static-lib, or --proto_format can be defined");
return 1;
}
+ // The default build type.
+ context.SetPackageType(PackageType::kApp);
+ context.SetPackageId(kAppPackageId);
+
if (shared_lib) {
context.SetPackageType(PackageType::kSharedLib);
context.SetPackageId(0x00);
} else if (static_lib) {
context.SetPackageType(PackageType::kStaticLib);
- context.SetPackageId(kAppPackageId);
- } else {
- context.SetPackageType(PackageType::kApp);
- context.SetPackageId(kAppPackageId);
+ options.output_format = OutputFormat::kProto;
+ } else if (proto_format) {
+ options.output_format = OutputFormat::kProto;
}
if (package_id) {