diff options
author | 2017-03-06 20:05:57 -0800 | |
---|---|---|
committer | 2017-03-16 15:45:16 -0700 | |
commit | b0c47ef8779e445865e63355ab66265461e1f2a3 (patch) | |
tree | b0823faa314d767d90f9f0d2120c56ca57653ed0 | |
parent | 1665d0f028e3a225cb117d3e227bef5c5dace2d4 (diff) |
AAPT2: Finish support for feature splits
- Prefix the config split name generated from a feature split with the
name of the feature split.
- Add the 'configForSplit' attribute to the <manifest> tag of a config
split and give it the same name as the feature split it was generated
from.
- Look for the featureSplit attribute in <manifest> and automatically
convert it to 'split' and inject 'android:isFeatureSplit="true"'.
Feature splits should be written like so:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.foo.example"
featureSplit="feature_b">
<uses-split android:name="feature_a" />
...
</manifest>
Bug: 34703094
Test: manual
Change-Id: I01b5c4a9aa03a2d25ef1e87bc7874b57c9deede9
-rw-r--r-- | tools/aapt2/AppInfo.h | 24 | ||||
-rw-r--r-- | tools/aapt2/link/Link.cpp | 138 | ||||
-rw-r--r-- | tools/aapt2/link/ManifestFixer.cpp | 87 |
3 files changed, 131 insertions, 118 deletions
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h index 1e488f70bd4d..9db21aadb242 100644 --- a/tools/aapt2/AppInfo.h +++ b/tools/aapt2/AppInfo.h @@ -23,30 +23,22 @@ namespace aapt { -/** - * Holds basic information about the app being built. Most of this information - * will come from the app's AndroidManifest. - */ +// Information relevant to building an app, parsed from the app's AndroidManifest.xml. struct AppInfo { - /** - * App's package name. - */ + // The app's package name. std::string package; - /** - * The App's minimum SDK version. - */ + // The app's minimum SDK version, if it is defined. Maybe<std::string> min_sdk_version; - /** - * The Version code of the app. - */ + // The app's version code, if it is defined. Maybe<uint32_t> version_code; - /** - * The revision code of the app. - */ + // The app's revision code, if it is defined. Maybe<uint32_t> revision_code; + + // The app's split name, if it is a split. + Maybe<std::string> split_name; }; } // namespace aapt diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index 10421115b629..1b4d5bb4f6e1 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -751,70 +751,67 @@ class LinkCommand { return true; } - Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, - IDiagnostics* diag) { + Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) { // Make sure the first element is <manifest> with package attribute. - if (xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get())) { - AppInfo app_info; + xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get()); + if (manifest_el == nullptr) { + return {}; + } + + AppInfo app_info; + + if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") { + diag->Error(DiagMessage(xml_res->file.source) << "root tag must be <manifest>"); + return {}; + } - if (!manifest_el->namespace_uri.empty() || - manifest_el->name != "manifest") { - diag->Error(DiagMessage(xml_res->file.source) - << "root tag must be <manifest>"); + xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); + if (!package_attr) { + diag->Error(DiagMessage(xml_res->file.source) + << "<manifest> must have a 'package' attribute"); + return {}; + } + app_info.package = package_attr->value; + + if (xml::Attribute* version_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { + Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:versionCode '" << version_code_attr->value << "'"); return {}; } + app_info.version_code = maybe_code.value(); + } - xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package"); - if (!package_attr) { - diag->Error(DiagMessage(xml_res->file.source) - << "<manifest> must have a 'package' attribute"); + if (xml::Attribute* revision_code_attr = + manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { + Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value); + if (!maybe_code) { + diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number)) + << "invalid android:revisionCode '" << revision_code_attr->value << "'"); return {}; } + app_info.revision_code = maybe_code.value(); + } - app_info.package = package_attr->value; - - if (xml::Attribute* version_code_attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) { - Maybe<uint32_t> maybe_code = - ResourceUtils::ParseInt(version_code_attr->value); - if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine( - manifest_el->line_number)) - << "invalid android:versionCode '" - << version_code_attr->value << "'"); - return {}; - } - app_info.version_code = maybe_code.value(); - } - - if (xml::Attribute* revision_code_attr = - manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) { - Maybe<uint32_t> maybe_code = - ResourceUtils::ParseInt(revision_code_attr->value); - if (!maybe_code) { - diag->Error(DiagMessage(xml_res->file.source.WithLine( - manifest_el->line_number)) - << "invalid android:revisionCode '" - << revision_code_attr->value << "'"); - return {}; - } - app_info.revision_code = maybe_code.value(); + if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) { + if (!split_name_attr->value.empty()) { + app_info.split_name = split_name_attr->value; } + } - if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { - if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute( - xml::kSchemaAndroid, "minSdkVersion")) { - app_info.min_sdk_version = min_sdk->value; - } + if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) { + if (xml::Attribute* min_sdk = + uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) { + app_info.min_sdk_version = min_sdk->value; } - return app_info; } - return {}; + return app_info; } /** - * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it - * linked. + * 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. */ @@ -1367,45 +1364,44 @@ class LinkCommand { return true; } - std::unique_ptr<xml::XmlResource> GenerateSplitManifest( - const AppInfo& app_info, const SplitConstraints& constraints) { - std::unique_ptr<xml::XmlResource> doc = - util::make_unique<xml::XmlResource>(); + std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info, + const SplitConstraints& constraints) { + std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>(); - std::unique_ptr<xml::Namespace> namespace_android = - util::make_unique<xml::Namespace>(); + std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>(); namespace_android->namespace_uri = xml::kSchemaAndroid; namespace_android->namespace_prefix = "android"; - std::unique_ptr<xml::Element> manifest_el = - util::make_unique<xml::Element>(); + std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>(); manifest_el->name = "manifest"; - manifest_el->attributes.push_back( - xml::Attribute{"", "package", app_info.package}); + manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package}); if (app_info.version_code) { - manifest_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "versionCode", - std::to_string(app_info.version_code.value())}); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "versionCode", std::to_string(app_info.version_code.value())}); } if (app_info.revision_code) { - manifest_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "revisionCode", - std::to_string(app_info.revision_code.value())}); + manifest_el->attributes.push_back(xml::Attribute{ + xml::kSchemaAndroid, "revisionCode", std::to_string(app_info.revision_code.value())}); } std::stringstream split_name; + if (app_info.split_name) { + split_name << app_info.split_name.value() << "."; + } split_name << "config." << util::Joiner(constraints.configs, "_"); - manifest_el->attributes.push_back( - xml::Attribute{"", "split", split_name.str()}); + manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()}); + + if (app_info.split_name) { + manifest_el->attributes.push_back( + xml::Attribute{"", "configForSplit", app_info.split_name.value()}); + } - std::unique_ptr<xml::Element> application_el = - util::make_unique<xml::Element>(); + std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>(); application_el->name = "application"; - application_el->attributes.push_back( - xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"}); + application_el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"}); manifest_el->AppendChild(std::move(application_el)); namespace_android->AppendChild(std::move(manifest_el)); diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 313fe453dff5..0c19c7ad7f32 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -29,10 +29,7 @@ using android::StringPiece; namespace aapt { -/** - * This is how PackageManager builds class names from AndroidManifest.xml - * entries. - */ +// This is how PackageManager builds class names from AndroidManifest.xml entries. static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr, SourcePathDiagnostics* diag) { // We allow unqualified class names (ie: .HelloActivity) @@ -90,6 +87,36 @@ static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std }; } +static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) { + constexpr const char* kFeatureSplit = "featureSplit"; + constexpr const char* kIsFeatureSplit = "isFeatureSplit"; + + xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit); + if (attr != nullptr) { + // Rewrite the featureSplit attribute to be "split". This is what the + // platform recognizes. + attr->name = "split"; + + // Now inject the android:isFeatureSplit="true" attribute. + xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit); + if (attr != nullptr) { + if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) { + // The isFeatureSplit attribute is false, which conflicts with the use + // of "featureSplit". + diag->Error(DiagMessage(el->line_number) + << "attribute 'featureSplit' used in <manifest> but 'android:isFeatureSplit' " + "is not 'true'"); + return false; + } + + // The attribute is already there and set to true, nothing to do. + } else { + el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"}); + } + } + return true; +} + static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { xml::Attribute* attr = el->FindAttribute({}, "package"); if (!attr) { @@ -97,31 +124,34 @@ static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { << "<manifest> tag is missing 'package' attribute"); return false; } else if (ResourceUtils::IsReference(attr->value)) { - diag->Error( - DiagMessage(el->line_number) - << "attribute 'package' in <manifest> tag must not be a reference"); + diag->Error(DiagMessage(el->line_number) + << "attribute 'package' in <manifest> tag must not be a reference"); return false; } else if (!util::IsJavaPackageName(attr->value)) { diag->Error(DiagMessage(el->line_number) - << "attribute 'package' in <manifest> tag is not a valid Java " - "package name: '" + << "attribute 'package' in <manifest> tag is not a valid Java package name: '" << attr->value << "'"); return false; } + + attr = el->FindAttribute({}, "split"); + if (attr) { + if (!util::IsJavaPackageName(attr->value)) { + diag->Error(DiagMessage(el->line_number) << "attribute 'split' in <manifest> tag is not a " + "valid split name"); + return false; + } + } return true; } -/** - * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type - * checking on it is manual. - */ +// The coreApp attribute in <manifest> is not a regular AAPT attribute, so type +// checking on it is manual. static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) { if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) { - std::unique_ptr<BinaryPrimitive> result = - ResourceUtils::TryParseBool(attr->value); + std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseBool(attr->value); if (!result) { - diag->Error(DiagMessage(el->line_number) - << "attribute coreApp must be a boolean"); + diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean"); return false; } attr->compiled_value = std::move(result); @@ -172,8 +202,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, } if (options_.rename_instrumentation_target_package) { - if (!util::IsJavaPackageName( - options_.rename_instrumentation_target_package.value())) { + if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) { diag->Error(DiagMessage() << "invalid instrumentation target package override '" << options_.rename_instrumentation_target_package.value() @@ -203,6 +232,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, // Manifest actions. xml::XmlNodeAction& manifest_action = (*executor)["manifest"]; + manifest_action.Action(AutoGenerateIsFeatureSplit); manifest_action.Action(VerifyManifest); manifest_action.Action(FixCoreAppAttribute); manifest_action.Action([&](xml::Element* el) -> bool { @@ -276,6 +306,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, manifest_action["compatible-screens"]["screen"]; manifest_action["supports-gl-texture"]; manifest_action["meta-data"] = meta_data_action; + manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); // Application actions. xml::XmlNodeAction& application_action = manifest_action["application"]; @@ -311,15 +342,13 @@ class FullyQualifiedClassNameVisitor : public xml::Visitor { public: using xml::Visitor::Visit; - explicit FullyQualifiedClassNameVisitor(const StringPiece& package) - : package_(package) {} + explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {} void Visit(xml::Element* el) override { for (xml::Attribute& attr : el->attributes) { if (attr.namespace_uri == xml::kSchemaAndroid && class_attributes_.find(attr.name) != class_attributes_.end()) { - if (Maybe<std::string> new_value = - util::GetFullyQualifiedClassName(package_, attr.value)) { + if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) { attr.value = std::move(new_value.value()); } } @@ -334,8 +363,7 @@ class FullyQualifiedClassNameVisitor : public xml::Visitor { std::unordered_set<StringPiece> class_attributes_ = {"name"}; }; -static bool RenameManifestPackage(const StringPiece& package_override, - xml::Element* manifest_el) { +static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) { xml::Attribute* attr = manifest_el->FindAttribute({}, "package"); // We've already verified that the manifest element is present, with a package @@ -358,8 +386,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { return false; } - if ((options_.min_sdk_version_default || - options_.target_sdk_version_default) && + if ((options_.min_sdk_version_default || options_.target_sdk_version_default) && root->FindChild({}, "uses-sdk") == nullptr) { // Auto insert a <uses-sdk> element. This must be inserted before the // <application> tag. The device runtime PackageParser will make SDK version @@ -374,8 +401,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { return false; } - if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, - context->GetDiagnostics(), doc)) { + if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) { return false; } @@ -383,8 +409,7 @@ bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { // Rename manifest package outside of the XmlActionExecutor. // We need to extract the old package name and FullyQualify all class // names. - if (!RenameManifestPackage(options_.rename_manifest_package.value(), - root)) { + if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) { return false; } } |