AAPT2: Variety of small fixes to get the build working
- Add option to rename package in AndroidManifest.xml
- Support default versionName and versionCode
- Accept True and False as valid booleans
Change-Id: I400e350b9dcd0fd1c197d1929144299c7823617d
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 8a87d96..2a4c020 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -51,16 +51,19 @@
std::vector<std::string> includePaths;
std::vector<std::string> overlayFiles;
Maybe<std::string> generateJavaClassPath;
- std::set<std::string> extraJavaPackages;
+ Maybe<std::u16string> customJavaPackage;
+ std::set<std::u16string> extraJavaPackages;
Maybe<std::string> generateProguardRulesPath;
bool noAutoVersion = false;
bool staticLib = false;
bool verbose = false;
bool outputToDirectory = false;
bool autoAddOverlay = false;
+ bool doNotCompressAnything = false;
+ std::vector<std::string> extensionsToNotCompress;
Maybe<std::u16string> privateSymbols;
- Maybe<std::u16string> minSdkVersionDefault;
- Maybe<std::u16string> targetSdkVersionDefault;
+ ManifestFixerOptions manifestFixerOptions;
+
};
struct LinkContext : public IAaptContext {
@@ -186,7 +189,20 @@
return resFile;
}
- bool copyFileToArchive(io::IFile* file, const std::string& outPath, uint32_t flags,
+ uint32_t getCompressionFlags(const StringPiece& str) {
+ if (mOptions.doNotCompressAnything) {
+ return 0;
+ }
+
+ for (const std::string& extension : mOptions.extensionsToNotCompress) {
+ if (util::stringEndsWith<char>(str, extension)) {
+ return 0;
+ }
+ }
+ return ArchiveEntry::kCompress;
+ }
+
+ bool copyFileToArchive(io::IFile* file, const std::string& outPath,
IArchiveWriter* writer) {
std::unique_ptr<io::IData> data = file->openAsData();
if (!data) {
@@ -202,7 +218,7 @@
return false;
}
- if (writer->startEntry(outPath, flags)) {
+ if (writer->startEntry(outPath, getCompressionFlags(outPath))) {
if (writer->writeEntry(reinterpret_cast<const uint8_t*>(data->data()) + offset,
data->size() - static_cast<size_t>(offset))) {
if (writer->finishEntry()) {
@@ -319,7 +335,6 @@
return false;
}
-
if (writer->startEntry(path, ArchiveEntry::kCompress)) {
if (writer->writeEntry(buffer)) {
if (writer->finishEntry()) {
@@ -520,7 +535,7 @@
const Source& src = file->getSource();
if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
return mergeResourceTable(file, override);
- } else {
+ } else if (util::stringEndsWith<char>(src.path, ".flat")){
// Try opening the file and looking for an Export header.
std::unique_ptr<io::IData> data = file->openAsData();
if (!data) {
@@ -533,7 +548,11 @@
if (resourceFile) {
return mergeCompiledFile(file, std::move(resourceFile), override);
}
+ } else {
+ // Ignore non .flat files. This could be classes.dex or something else that happens
+ // to be in an archive.
}
+
return false;
}
@@ -646,10 +665,7 @@
bool error = false;
{
- ManifestFixerOptions manifestFixerOptions;
- manifestFixerOptions.minSdkVersionDefault = mOptions.minSdkVersionDefault;
- manifestFixerOptions.targetSdkVersionDefault = mOptions.targetSdkVersionDefault;
- ManifestFixer manifestFixer(manifestFixerOptions);
+ ManifestFixer manifestFixer(mOptions.manifestFixerOptions);
if (!manifestFixer.consume(&mContext, manifestXml.get())) {
error = true;
}
@@ -786,7 +802,7 @@
mContext.getDiagnostics()->note(DiagMessage() << "copying " << path);
}
- if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath, 0,
+ if (!copyFileToArchive(fileToMerge.file, fileToMerge.dstPath,
archiveWriter.get())) {
error = true;
}
@@ -813,12 +829,18 @@
if (mOptions.generateJavaClassPath) {
JavaClassGeneratorOptions options;
+ options.types = JavaClassGeneratorOptions::SymbolTypes::kAll;
+
if (mOptions.staticLib) {
options.useFinal = false;
}
- StringPiece16 actualPackage = mContext.getCompilationPackage();
+ const StringPiece16 actualPackage = mContext.getCompilationPackage();
StringPiece16 outputPackage = mContext.getCompilationPackage();
+ if (mOptions.customJavaPackage) {
+ // Override the output java package to the custom one.
+ outputPackage = mOptions.customJavaPackage.value();
+ }
if (mOptions.privateSymbols) {
// If we defined a private symbols package, we only emit Public symbols
@@ -826,7 +848,7 @@
options.types = JavaClassGeneratorOptions::SymbolTypes::kPublic;
if (!writeJavaFile(&mFinalTable, mContext.getCompilationPackage(),
- mContext.getCompilationPackage(), options)) {
+ outputPackage, options)) {
return 1;
}
@@ -838,9 +860,8 @@
return 1;
}
- for (const std::string& extraPackage : mOptions.extraJavaPackages) {
- if (!writeJavaFile(&mFinalTable, actualPackage, util::utf8ToUtf16(extraPackage),
- options)) {
+ for (const std::u16string& extraPackage : mOptions.extraJavaPackages) {
+ if (!writeJavaFile(&mFinalTable, actualPackage, extraPackage, options)) {
return 1;
}
}
@@ -877,13 +898,16 @@
LinkOptions options;
Maybe<std::string> privateSymbolsPackage;
Maybe<std::string> minSdkVersion, targetSdkVersion;
+ Maybe<std::string> renameManifestPackage, renameInstrumentationTargetPackage;
+ Maybe<std::string> versionCode, versionName;
+ Maybe<std::string> customJavaPackage;
std::vector<std::string> extraJavaPackages;
Flags flags = Flags()
.requiredFlag("-o", "Output path", &options.outputPath)
.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. "
+ .optionalFlagList("-R", "Compilation unit to link, using `overlay` semantics.\n"
"The last conflicting resource given takes precedence.",
&options.overlayFiles)
.optionalFlag("--java", "Directory in which to generate R.java",
@@ -900,15 +924,29 @@
"AndroidManifest.xml", &minSdkVersion)
.optionalFlag("--target-sdk-version", "Default target SDK version to use for "
"AndroidManifest.xml", &targetSdkVersion)
+ .optionalFlag("--version-code", "Version code (integer) to inject into the "
+ "AndroidManifest.xml if none is present", &versionCode)
+ .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
+ "if none is present", &versionName)
.optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
.optionalFlag("--private-symbols", "Package name to use when generating R.java for "
"private symbols.\n"
"If not specified, public and private symbols will use the application's "
"package name", &privateSymbolsPackage)
+ .optionalFlag("--custom-package", "Custom Java package under which to generate R.java",
+ &customJavaPackage)
.optionalFlagList("--extra-packages", "Generate the same R.java but with different "
"package names", &extraJavaPackages)
.optionalSwitch("--auto-add-overlay", "Allows the addition of new resources in "
"overlays without <add-resource> tags", &options.autoAddOverlay)
+ .optionalFlag("--rename-manifest-package", "Renames the package in AndroidManifest.xml",
+ &renameManifestPackage)
+ .optionalFlag("--rename-instrumentation-target-package",
+ "Changes the name of the target package for instrumentation. Most useful "
+ "when used\nin conjunction with --rename-manifest-package",
+ &renameInstrumentationTargetPackage)
+ .optionalFlagList("-0", "File extensions not to compress",
+ &options.extensionsToNotCompress)
.optionalSwitch("-v", "Enables verbose logging", &options.verbose);
if (!flags.parse("aapt2 link", args, &std::cerr)) {
@@ -920,18 +958,42 @@
}
if (minSdkVersion) {
- options.minSdkVersionDefault = util::utf8ToUtf16(minSdkVersion.value());
+ options.manifestFixerOptions.minSdkVersionDefault =
+ util::utf8ToUtf16(minSdkVersion.value());
}
if (targetSdkVersion) {
- options.targetSdkVersionDefault = util::utf8ToUtf16(targetSdkVersion.value());
+ options.manifestFixerOptions.targetSdkVersionDefault =
+ util::utf8ToUtf16(targetSdkVersion.value());
+ }
+
+ if (renameManifestPackage) {
+ options.manifestFixerOptions.renameManifestPackage =
+ util::utf8ToUtf16(renameManifestPackage.value());
+ }
+
+ if (renameInstrumentationTargetPackage) {
+ options.manifestFixerOptions.renameInstrumentationTargetPackage =
+ util::utf8ToUtf16(renameInstrumentationTargetPackage.value());
+ }
+
+ if (versionCode) {
+ options.manifestFixerOptions.versionCodeDefault = util::utf8ToUtf16(versionCode.value());
+ }
+
+ if (versionName) {
+ options.manifestFixerOptions.versionNameDefault = util::utf8ToUtf16(versionName.value());
+ }
+
+ if (customJavaPackage) {
+ options.customJavaPackage = util::utf8ToUtf16(customJavaPackage.value());
}
// Populate the set of extra packages for which to generate R.java.
for (std::string& extraPackage : extraJavaPackages) {
// A given package can actually be a colon separated list of packages.
for (StringPiece package : util::split(extraPackage, ':')) {
- options.extraJavaPackages.insert(package.toString());
+ options.extraJavaPackages.insert(util::utf8ToUtf16(package));
}
}
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 2034c57..9baf1d8 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -22,25 +22,43 @@
namespace aapt {
static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
- bool error = false;
-
xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
if (!attr) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "missing 'package' attribute");
- error = true;
} else if (ResourceUtils::isReference(attr->value)) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "value for attribute 'package' must not be a "
"reference");
- error = true;
} else if (!util::isJavaPackageName(attr->value)) {
context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
<< "invalid package name '" << attr->value << "'");
- error = true;
+ } else {
+ return true;
+ }
+ return false;
+}
+
+static bool includeVersionName(IAaptContext* context, const Source& source,
+ const StringPiece16& versionName, xml::Element* manifestEl) {
+ if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
+ return true;
}
- return !error;
+ manifestEl->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"versionName", versionName.toString() });
+ return true;
+}
+
+static bool includeVersionCode(IAaptContext* context, const Source& source,
+ const StringPiece16& versionCode, xml::Element* manifestEl) {
+ if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+ return true;
+ }
+
+ manifestEl->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
+ return true;
}
static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
@@ -62,6 +80,76 @@
return true;
}
+class FullyQualifiedClassNameVisitor : public xml::Visitor {
+public:
+ using xml::Visitor::visit;
+
+ FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
+ }
+
+ void visit(xml::Element* el) override {
+ for (xml::Attribute& attr : el->attributes) {
+ if (Maybe<std::u16string> newValue =
+ util::getFullyQualifiedClassName(mPackage, attr.value)) {
+ attr.value = std::move(newValue.value());
+ }
+ }
+
+ // Super implementation to iterate over the children.
+ xml::Visitor::visit(el);
+ }
+
+private:
+ StringPiece16 mPackage;
+};
+
+static bool renameManifestPackage(IAaptContext* context, const Source& source,
+ const StringPiece16& packageOverride, xml::Element* manifestEl) {
+ if (!util::isJavaPackageName(packageOverride)) {
+ context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
+ << packageOverride << "'");
+ return false;
+ }
+
+ xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
+
+ // We've already verified that the manifest element is present, with a package name specified.
+ assert(attr);
+
+ std::u16string originalPackage = std::move(attr->value);
+ attr->value = packageOverride.toString();
+
+ FullyQualifiedClassNameVisitor visitor(originalPackage);
+ manifestEl->accept(&visitor);
+ return true;
+}
+
+static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
+ const StringPiece16& packageOverride,
+ xml::Element* manifestEl) {
+ if (!util::isJavaPackageName(packageOverride)) {
+ context->getDiagnostics()->error(DiagMessage()
+ << "invalid instrumentation target package override '"
+ << packageOverride << "'");
+ return false;
+ }
+
+ xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+ if (!instrumentationEl) {
+ // No error if there is no work to be done.
+ return true;
+ }
+
+ xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+ if (!attr) {
+ // No error if there is no work to be done.
+ return true;
+ }
+
+ attr->value = packageOverride.toString();
+ return true;
+}
+
bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
xml::Element* root = xml::findRootElement(doc->root.get());
if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -74,6 +162,36 @@
return false;
}
+ if (mOptions.versionCodeDefault) {
+ if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
+ root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.versionNameDefault) {
+ if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
+ root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.renameManifestPackage) {
+ // Rename manifest package.
+ if (!renameManifestPackage(context, doc->file.source,
+ mOptions.renameManifestPackage.value(), root)) {
+ return false;
+ }
+ }
+
+ if (mOptions.renameInstrumentationTargetPackage) {
+ if (!renameInstrumentationTargetPackage(context, doc->file.source,
+ mOptions.renameInstrumentationTargetPackage.value(),
+ root)) {
+ return false;
+ }
+ }
+
bool foundUsesSdk = false;
for (xml::Element* el : root->getChildElements()) {
if (!el->namespaceUri.empty()) {
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index a77e6d5..b8d9c83 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -28,6 +28,10 @@
struct ManifestFixerOptions {
Maybe<std::u16string> minSdkVersionDefault;
Maybe<std::u16string> targetSdkVersionDefault;
+ Maybe<std::u16string> renameManifestPackage;
+ Maybe<std::u16string> renameInstrumentationTargetPackage;
+ Maybe<std::u16string> versionNameDefault;
+ Maybe<std::u16string> versionCodeDefault;
};
/**
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f6bf895..f40fbfb 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -82,8 +82,6 @@
EXPECT_EQ(nullptr, verify("<manifest package=\"@string/str\" />"));
}
-
-
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
ManifestFixerOptions options = { std::u16string(u"8"), std::u16string(u"22") };
@@ -97,7 +95,7 @@
xml::Element* el;
xml::Attribute* attr;
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -115,7 +113,7 @@
</manifest>)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -133,7 +131,7 @@
</manifest>)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -149,7 +147,7 @@
package="android" />)EOF", options);
ASSERT_NE(nullptr, doc);
- el = xml::findRootElement(doc->root.get());
+ el = xml::findRootElement(doc.get());
ASSERT_NE(nullptr, el);
el = el->findChild({}, u"uses-sdk");
ASSERT_NE(nullptr, el);
@@ -161,4 +159,98 @@
EXPECT_EQ(u"22", attr->value);
}
+TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
+ ManifestFixerOptions options;
+ options.renameManifestPackage = std::u16string(u"com.android");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <application name=".MainApplication" text="hello">
+ <activity name=".activity.Start" />
+ <receiver name="com.google.android.Receiver" />
+ </application>
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Attribute* attr = nullptr;
+
+ attr = manifestEl->findAttribute({}, u"package");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+
+ xml::Element* applicationEl = manifestEl->findChild({}, u"application");
+ ASSERT_NE(nullptr, applicationEl);
+
+ attr = applicationEl->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
+
+ attr = applicationEl->findAttribute({}, u"text");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"hello"), attr->value);
+
+ xml::Element* el;
+ el = applicationEl->findChild({}, u"activity");
+ ASSERT_NE(nullptr, el);
+
+ attr = el->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
+
+ el = applicationEl->findChild({}, u"receiver");
+ ASSERT_NE(nullptr, el);
+
+ attr = el->findAttribute({}, u"name");
+ ASSERT_NE(nullptr, el);
+ EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
+ ManifestFixerOptions options;
+ options.renameInstrumentationTargetPackage = std::u16string(u"com.android");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android">
+ <instrumentation android:targetPackage="android" />
+ </manifest>)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
+ ASSERT_NE(nullptr, instrumentationEl);
+
+ xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"com.android"), attr->value);
+}
+
+TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
+ ManifestFixerOptions options;
+ options.versionNameDefault = std::u16string(u"Beta");
+ options.versionCodeDefault = std::u16string(u"0x10000000");
+
+ std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android" />)EOF", options);
+ ASSERT_NE(nullptr, doc);
+
+ xml::Element* manifestEl = xml::findRootElement(doc.get());
+ ASSERT_NE(nullptr, manifestEl);
+
+ xml::Attribute* attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"Beta"), attr->value);
+
+ attr = manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ(std::u16string(u"0x10000000"), attr->value);
+}
+
} // namespace aapt