AAPT2: Fixup namespace implementation
A few pieces were missing in the namespace mangling implementation. Namespace aware libraries now work, along with R class generation. Bug: 64706588 Test: make AaptTestNamespace_App Change-Id: I12f78d6aa909e782c0faf7ceaa36058f2e6c864a
@@ -69,8 +69,7 @@ class NameMangler {
|
||||
* The mangled name should contain symbols that are illegal to define in XML,
|
||||
* so that there will never be name mangling collisions.
|
||||
*/
|
||||
static std::string MangleEntry(const std::string& package,
|
||||
const std::string& name) {
|
||||
static std::string MangleEntry(const std::string& package, const std::string& name) {
|
||||
return package + "$" + name;
|
||||
}
|
||||
|
||||
@@ -86,8 +85,8 @@ class NameMangler {
|
||||
}
|
||||
|
||||
out_package->assign(out_name->data(), pivot);
|
||||
out_name->assign(out_name->data() + pivot + 1,
|
||||
out_name->size() - (pivot + 1));
|
||||
std::string new_name = out_name->substr(pivot + 1);
|
||||
*out_name = std::move(new_name);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -605,7 +605,7 @@ std::unique_ptr<Item> ResourceParser::ParseXml(xml::XmlPullParser* parser,
|
||||
if (processed_item) {
|
||||
// Fix up the reference.
|
||||
if (Reference* ref = ValueCast<Reference>(processed_item.get())) {
|
||||
TransformReferenceFromNamespace(parser, "", ref);
|
||||
ResolvePackage(parser, ref);
|
||||
}
|
||||
return processed_item;
|
||||
}
|
||||
@@ -1074,15 +1074,13 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Maybe<Reference> maybe_key =
|
||||
ResourceUtils::ParseXmlAttributeName(maybe_name.value());
|
||||
Maybe<Reference> maybe_key = ResourceUtils::ParseXmlAttributeName(maybe_name.value());
|
||||
if (!maybe_key) {
|
||||
diag_->Error(DiagMessage(source) << "invalid attribute name '"
|
||||
<< maybe_name.value() << "'");
|
||||
diag_->Error(DiagMessage(source) << "invalid attribute name '" << maybe_name.value() << "'");
|
||||
return false;
|
||||
}
|
||||
|
||||
TransformReferenceFromNamespace(parser, "", &maybe_key.value());
|
||||
ResolvePackage(parser, &maybe_key.value());
|
||||
maybe_key.value().SetSource(source);
|
||||
|
||||
std::unique_ptr<Item> value = ParseXml(parser, 0, kAllowRawString);
|
||||
@@ -1091,8 +1089,7 @@ bool ResourceParser::ParseStyleItem(xml::XmlPullParser* parser, Style* style) {
|
||||
return false;
|
||||
}
|
||||
|
||||
style->entries.push_back(
|
||||
Style::Entry{std::move(maybe_key.value()), std::move(value)});
|
||||
style->entries.push_back(Style::Entry{std::move(maybe_key.value()), std::move(value)});
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1104,21 +1101,18 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
|
||||
|
||||
Maybe<StringPiece> maybe_parent = xml::FindAttribute(parser, "parent");
|
||||
if (maybe_parent) {
|
||||
// If the parent is empty, we don't have a parent, but we also don't infer
|
||||
// either.
|
||||
// If the parent is empty, we don't have a parent, but we also don't infer either.
|
||||
if (!maybe_parent.value().empty()) {
|
||||
std::string err_str;
|
||||
style->parent = ResourceUtils::ParseStyleParentReference(
|
||||
maybe_parent.value(), &err_str);
|
||||
style->parent = ResourceUtils::ParseStyleParentReference(maybe_parent.value(), &err_str);
|
||||
if (!style->parent) {
|
||||
diag_->Error(DiagMessage(out_resource->source) << err_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Transform the namespace prefix to the actual package name, and mark the
|
||||
// reference as
|
||||
// Transform the namespace prefix to the actual package name, and mark the reference as
|
||||
// private if appropriate.
|
||||
TransformReferenceFromNamespace(parser, "", &style->parent.value());
|
||||
ResolvePackage(parser, &style->parent.value());
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -1127,8 +1121,7 @@ bool ResourceParser::ParseStyle(const ResourceType type, xml::XmlPullParser* par
|
||||
size_t pos = style_name.find_last_of(u'.');
|
||||
if (pos != std::string::npos) {
|
||||
style->parent_inferred = true;
|
||||
style->parent = Reference(
|
||||
ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
|
||||
style->parent = Reference(ResourceName({}, ResourceType::kStyle, style_name.substr(0, pos)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1373,7 +1366,7 @@ bool ResourceParser::ParseDeclareStyleable(xml::XmlPullParser* parser,
|
||||
}
|
||||
|
||||
Reference& child_ref = maybe_ref.value();
|
||||
xml::TransformReferenceFromNamespace(parser, "", &child_ref);
|
||||
xml::ResolvePackage(parser, &child_ref);
|
||||
|
||||
// Create the ParsedResource that will add the attribute to the table.
|
||||
ParsedResource child_resource;
|
||||
|
||||
@@ -457,7 +457,8 @@ 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 << ")");
|
||||
}
|
||||
|
||||
XmlReferenceLinker xml_linker;
|
||||
@@ -505,6 +506,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
std::map<std::pair<ConfigDescription, StringPiece>, FileOperation> config_sorted_files;
|
||||
|
||||
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();
|
||||
@@ -701,7 +704,7 @@ class LinkCommand {
|
||||
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.
|
||||
@@ -819,11 +822,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 ||
|
||||
@@ -965,10 +966,94 @@ class LinkCommand {
|
||||
context_->GetDiagnostics()->Error(DiagMessage()
|
||||
<< "failed writing to '" << out_path
|
||||
<< "': " << android::base::SystemErrorCodeToString(errno));
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
bool WriteManifestJavaFile(xml::XmlResource* manifest_xml) {
|
||||
if (!options_.generate_java_class_path) {
|
||||
return true;
|
||||
@@ -1097,15 +1182,17 @@ 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());
|
||||
@@ -1673,8 +1760,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();
|
||||
@@ -1727,72 +1813,7 @@ class LinkCommand {
|
||||
}
|
||||
|
||||
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 (!GenerateJavaClasses()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,10 +69,10 @@ class Visitor : public xml::PackageAwareVisitor {
|
||||
// Use an empty string for the compilation package because we don't want to default to
|
||||
// the local package if the user specified name="style" or something. This should just
|
||||
// be the default namespace.
|
||||
Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package, {});
|
||||
Maybe<xml::ExtractedPackage> maybe_pkg = TransformPackageAlias(name.package);
|
||||
if (!maybe_pkg) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src) << "invalid namespace prefix '"
|
||||
<< name.package << "'");
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "invalid namespace prefix '" << name.package << "'");
|
||||
error_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
2
tools/aapt2/integration-tests/NamespaceTest/Android.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
29
tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
Normal file
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Copyright (C) 2017 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_AAPT_NAMESPACES := true
|
||||
LOCAL_PACKAGE_NAME := AaptTestNamespace_App
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_STATIC_ANDROID_LIBRARIES := \
|
||||
AaptTestNamespace_LibOne \
|
||||
AaptTestNamespace_LibTwo
|
||||
LOCAL_AAPT_FLAGS := -v
|
||||
include $(BUILD_PACKAGE)
|
||||
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.aapt.namespace.app">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
|
||||
|
||||
<application android:theme="@style/AppTheme" android:label="@string/app_name">
|
||||
<activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:libone="http://schemas.android.com/apk/res/com.android.aapt.namespace.libone"
|
||||
xmlns:libtwo="http://schemas.android.com/apk/res/com.android.aapt.namespace.libtwo"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.android.aapt.namespace.libtwo.TextView
|
||||
android:id="@+id/textview"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="@bool/always_true"
|
||||
android:text="@libone:string/textview_text"
|
||||
libtwo:textview_attr="?libone:theme_attr" />
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<string name="app_name">Namespace App</string>
|
||||
<bool name="always_true">true</bool>
|
||||
|
||||
<style name="AppTheme" parent="com.android.aapt.namespace.libone:style/Theme">
|
||||
<item name="android:colorPrimary">#3F51B5</item>
|
||||
<item name="android:colorPrimaryDark">#303F9F</item>
|
||||
<item name="android:colorAccent">#FF4081</item>
|
||||
<item name="com.android.aapt.namespace.libone:theme_attr">
|
||||
@com.android.aapt.namespace.libtwo:string/public_string
|
||||
</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.aapt.namespace.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
com.android.aapt.namespace.libtwo.TextView tv = findViewById(R.id.textview);
|
||||
|
||||
|
||||
|
||||
Toast.makeText(this, tv.getTextViewAttr(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#
|
||||
# Copyright (C) 2017 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_AAPT_NAMESPACES := true
|
||||
LOCAL_MODULE := AaptTestNamespace_LibOne
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
|
||||
# We need this to retain the R.java generated for this library.
|
||||
LOCAL_JAR_EXCLUDE_FILES := none
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.aapt.namespace.libone">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="string" name="textview_text" />
|
||||
<string name="textview_text">LibOne\'s textview_text string!</string>
|
||||
|
||||
<public type="attr" name="theme_attr" />
|
||||
<attr name="theme_attr" format="string" />
|
||||
|
||||
<public type="style" name="Theme" />
|
||||
<style name="Theme" parent="android:Theme.Material.Light.DarkActionBar">
|
||||
<item name="theme_attr">[Please override with your own value]</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,29 @@
|
||||
#
|
||||
# Copyright (C) 2017 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_AAPT_NAMESPACES := true
|
||||
LOCAL_MODULE := AaptTestNamespace_LibTwo
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
|
||||
# We need this to retain the R.java generated for this library.
|
||||
LOCAL_JAR_EXCLUDE_FILES := none
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.aapt.namespace.libtwo">
|
||||
|
||||
<uses-sdk android:minSdkVersion="21" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<public type="string" name="public_string" />
|
||||
<string name="public_string">LibTwo\'s public string!</string>
|
||||
|
||||
<public type="attr" name="textview_attr" />
|
||||
<attr name="textview_attr" format="string" />
|
||||
|
||||
<declare-styleable name="TextView">
|
||||
<attr name="textview_attr" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.aapt.namespace.libtwo;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
public class TextView extends android.widget.TextView {
|
||||
|
||||
private String mTextViewAttr;
|
||||
|
||||
public TextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
|
||||
final TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TextView,
|
||||
0, 0);
|
||||
try {
|
||||
mTextViewAttr = ta.getString(R.styleable.TextView_textview_attr);
|
||||
} finally {
|
||||
ta.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public String getTextViewAttr() {
|
||||
return mTextViewAttr;
|
||||
}
|
||||
}
|
||||
2
tools/aapt2/integration-tests/StaticLibTest/Android.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
@@ -18,12 +18,12 @@ LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_PACKAGE_NAME := AaptTestAppOne
|
||||
LOCAL_PACKAGE_NAME := AaptTestStaticLib_App
|
||||
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
|
||||
AaptTestStaticLib_LibOne \
|
||||
AaptTestStaticLib_LibTwo
|
||||
LOCAL_AAPT_FLAGS := --no-version-vectors --no-version-transitions
|
||||
include $(BUILD_PACKAGE)
|
||||
|
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 397 B |
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 191 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 196 B |
|
Before Width: | Height: | Size: 181 B After Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 124 B After Width: | Height: | Size: 124 B |
|
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 105 B |
|
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 103 B |
|
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 103 B |
|
Before Width: | Height: | Size: 105 B After Width: | Height: | Size: 105 B |
@@ -15,7 +15,6 @@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -15,7 +15,6 @@
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
|
||||
android:id="@+id/view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
@@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
|
||||
<resources>
|
||||
<style name="App">
|
||||
<item name="android:background">@color/primary</item>
|
||||
<item name="android:colorPrimary">@color/primary</item>
|
||||
@@ -18,11 +18,11 @@ LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_MODULE := AaptTestStaticLibOne
|
||||
LOCAL_MODULE := AaptTestStaticLib_LibOne
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
|
||||
# We need this to compile the Java sources of AaptTestStaticLibTwo using javac.
|
||||
# We need this to compile the Java sources of AaptTestStaticLib_LibTwo using javac.
|
||||
LOCAL_JAR_EXCLUDE_FILES := none
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
@@ -18,10 +18,10 @@ LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_USE_AAPT2 := true
|
||||
LOCAL_MODULE := AaptTestStaticLibTwo
|
||||
LOCAL_MODULE := AaptTestStaticLib_LibTwo
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
|
||||
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLib_LibOne
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
@@ -480,7 +480,7 @@ Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& packa
|
||||
if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
|
||||
// The entry name was mangled, and we successfully unmangled it.
|
||||
// Check that we want to emit this symbol.
|
||||
if (package_name != unmangled_package) {
|
||||
if (package_name_to_generate != unmangled_package) {
|
||||
// Skip the entry if it doesn't belong to the package we're writing.
|
||||
return {};
|
||||
}
|
||||
@@ -579,8 +579,7 @@ bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
|
||||
continue;
|
||||
}
|
||||
|
||||
// Stay consistent with AAPT and generate an empty type class if the R class
|
||||
// is public.
|
||||
// Stay consistent with AAPT and generate an empty type class if the R class is public.
|
||||
const bool force_creation_if_empty =
|
||||
(options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <unordered_set>
|
||||
|
||||
#include "android-base/macros.h"
|
||||
#include "androidfw/StringPiece.h"
|
||||
|
||||
#include "Resource.h"
|
||||
#include "SdkConstants.h"
|
||||
@@ -33,18 +34,15 @@ class ResourceTable;
|
||||
class ResourceEntry;
|
||||
struct ConfigDescription;
|
||||
|
||||
/**
|
||||
* Defines the location in which a value exists. This determines visibility of
|
||||
* other package's private symbols.
|
||||
*/
|
||||
// Defines the context in which a resource value is defined. Most resources are defined with the
|
||||
// implicit package name of their compilation context. Understanding the package name of a resource
|
||||
// allows to determine visibility of other symbols which may or may not have their packages defined.
|
||||
struct CallSite {
|
||||
ResourceNameRef resource;
|
||||
std::string package;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines whether a versioned resource should be created. If a versioned
|
||||
* resource already exists, it takes precedence.
|
||||
*/
|
||||
// Determines whether a versioned resource should be created. If a versioned resource already
|
||||
// exists, it takes precedence.
|
||||
bool ShouldGenerateVersionedResource(const ResourceEntry* entry, const ConfigDescription& config,
|
||||
const ApiVersion sdk_version_to_generate);
|
||||
|
||||
@@ -62,39 +60,26 @@ class AutoVersioner : public IResourceTableConsumer {
|
||||
DISALLOW_COPY_AND_ASSIGN(AutoVersioner);
|
||||
};
|
||||
|
||||
/**
|
||||
* If any attribute resource values are defined as public, this consumer will
|
||||
* move all private
|
||||
* attribute resource values to a private ^private-attr type, avoiding backwards
|
||||
* compatibility
|
||||
* issues with new apps running on old platforms.
|
||||
*
|
||||
* The Android platform ignores resource attributes it doesn't recognize, so an
|
||||
* app developer can
|
||||
* use new attributes in their layout XML files without worrying about
|
||||
* versioning. This assumption
|
||||
* actually breaks on older platforms. OEMs may add private attributes that are
|
||||
* used internally.
|
||||
* AAPT originally assigned all private attributes IDs immediately proceeding
|
||||
* the public attributes'
|
||||
* IDs.
|
||||
*
|
||||
* This means that on a newer Android platform, an ID previously assigned to a
|
||||
* private attribute
|
||||
* may end up assigned to a public attribute.
|
||||
*
|
||||
* App developers assume using the newer attribute is safe on older platforms
|
||||
* because it will
|
||||
* be ignored. Instead, the platform thinks the new attribute is an older,
|
||||
* private attribute and
|
||||
* will interpret it as such. This leads to unintended styling and exceptions
|
||||
* thrown due to
|
||||
* unexpected types.
|
||||
*
|
||||
* By moving the private attributes to a completely different type, this ID
|
||||
* conflict will never
|
||||
* occur.
|
||||
*/
|
||||
// If any attribute resource values are defined as public, this consumer will move all private
|
||||
// attribute resource values to a private ^private-attr type, avoiding backwards compatibility
|
||||
// issues with new apps running on old platforms.
|
||||
//
|
||||
// The Android platform ignores resource attributes it doesn't recognize, so an app developer can
|
||||
// use new attributes in their layout XML files without worrying about versioning. This assumption
|
||||
// actually breaks on older platforms. OEMs may add private attributes that are used internally.
|
||||
// AAPT originally assigned all private attributes IDs immediately proceeding the public attributes'
|
||||
// IDs.
|
||||
//
|
||||
// This means that on a newer Android platform, an ID previously assigned to a private attribute
|
||||
// may end up assigned to a public attribute.
|
||||
//
|
||||
// App developers assume using the newer attribute is safe on older platforms because it will
|
||||
// be ignored. Instead, the platform thinks the new attribute is an older, private attribute and
|
||||
// will interpret it as such. This leads to unintended styling and exceptions thrown due to
|
||||
// unexpected types.
|
||||
//
|
||||
// By moving the private attributes to a completely different type, this ID conflict will never
|
||||
// occur.
|
||||
class PrivateAttributeMover : public IResourceTableConsumer {
|
||||
public:
|
||||
PrivateAttributeMover() = default;
|
||||
@@ -126,14 +111,10 @@ class ProductFilter : public IResourceTableConsumer {
|
||||
std::unordered_set<std::string> products_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes namespace nodes and URI information from the XmlResource.
|
||||
*
|
||||
* Once an XmlResource is processed by this consumer, it is no longer able to
|
||||
* have its attributes
|
||||
* parsed. As such, this XmlResource must have already been processed by
|
||||
* XmlReferenceLinker.
|
||||
*/
|
||||
// Removes namespace nodes and URI information from the XmlResource.
|
||||
//
|
||||
// Once an XmlResource is processed by this consumer, it is no longer able to have its attributes
|
||||
// parsed. As such, this XmlResource must have already been processed by XmlReferenceLinker.
|
||||
class XmlNamespaceRemover : public IXmlResourceConsumer {
|
||||
public:
|
||||
explicit XmlNamespaceRemover(bool keep_uris = false) : keep_uris_(keep_uris){};
|
||||
@@ -146,11 +127,8 @@ class XmlNamespaceRemover : public IXmlResourceConsumer {
|
||||
bool keep_uris_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves attributes in the XmlResource and compiles string values to resource
|
||||
* values.
|
||||
* Once an XmlResource is processed by this linker, it is ready to be flattened.
|
||||
*/
|
||||
// Resolves attributes in the XmlResource and compiles string values to resource values.
|
||||
// Once an XmlResource is processed by this linker, it is ready to be flattened.
|
||||
class XmlReferenceLinker : public IXmlResourceConsumer {
|
||||
public:
|
||||
XmlReferenceLinker() = default;
|
||||
|
||||
@@ -30,23 +30,18 @@
|
||||
#include "util/Util.h"
|
||||
#include "xml/XmlUtil.h"
|
||||
|
||||
using android::StringPiece;
|
||||
using ::android::StringPiece;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* The ReferenceLinkerVisitor will follow all references and make sure they
|
||||
* point
|
||||
* to resources that actually exist, either in the local resource table, or as
|
||||
* external
|
||||
* symbols. Once the target resource has been found, the ID of the resource will
|
||||
* be assigned
|
||||
* to the reference object.
|
||||
*
|
||||
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
|
||||
*/
|
||||
// The ReferenceLinkerVisitor will follow all references and make sure they point
|
||||
// to resources that actually exist, either in the local resource table, or as external
|
||||
// symbols. Once the target resource has been found, the ID of the resource will be assigned
|
||||
// to the reference object.
|
||||
//
|
||||
// NOTE: All of the entries in the ResourceTable must be assigned IDs.
|
||||
class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
public:
|
||||
using ValueVisitor::Visit;
|
||||
@@ -65,14 +60,9 @@ class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We visit the Style specially because during this phase, values of
|
||||
* attributes are
|
||||
* all RawString values. Now that we are expected to resolve all symbols, we
|
||||
* can
|
||||
* lookup the attributes to find out which types are allowed for the
|
||||
* attributes' values.
|
||||
*/
|
||||
// We visit the Style specially because during this phase, values of attributes are
|
||||
// all RawString values. Now that we are expected to resolve all symbols, we can
|
||||
// lookup the attributes to find out which types are allowed for the attributes' values.
|
||||
void Visit(Style* style) override {
|
||||
if (style->parent) {
|
||||
Visit(&style->parent.value());
|
||||
@@ -81,28 +71,21 @@ class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
for (Style::Entry& entry : style->entries) {
|
||||
std::string err_str;
|
||||
|
||||
// Transform the attribute reference so that it is using the fully
|
||||
// qualified package
|
||||
// name. This will also mark the reference as being able to see private
|
||||
// resources if
|
||||
// there was a '*' in the reference or if the package came from the
|
||||
// private namespace.
|
||||
// Transform the attribute reference so that it is using the fully qualified package
|
||||
// name. This will also mark the reference as being able to see private resources if
|
||||
// there was a '*' in the reference or if the package came from the private namespace.
|
||||
Reference transformed_reference = entry.key;
|
||||
TransformReferenceFromNamespace(package_decls_,
|
||||
context_->GetCompilationPackage(),
|
||||
&transformed_reference);
|
||||
ResolvePackage(package_decls_, &transformed_reference);
|
||||
|
||||
// Find the attribute in the symbol table and check if it is visible from
|
||||
// this callsite.
|
||||
// Find the attribute in the symbol table and check if it is visible from this callsite.
|
||||
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveAttributeCheckVisibility(
|
||||
transformed_reference, callsite_, symbols_, &err_str);
|
||||
if (symbol) {
|
||||
// Assign our style key the correct ID.
|
||||
// The ID may not exist.
|
||||
// Assign our style key the correct ID. The ID may not exist.
|
||||
entry.key.id = symbol->id;
|
||||
|
||||
// Try to convert the value to a more specific, typed value based on the
|
||||
// attribute it is set to.
|
||||
// Try to convert the value to a more specific, typed value based on the attribute it is
|
||||
// set to.
|
||||
entry.value = ParseValueWithAttribute(std::move(entry.value), symbol->attribute.get());
|
||||
|
||||
// Link/resolve the final value (mostly if it's a reference).
|
||||
@@ -115,8 +98,8 @@ class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
// The actual type of this item is incompatible with the attribute.
|
||||
DiagMessage msg(entry.key.GetSource());
|
||||
|
||||
// Call the matches method again, this time with a DiagMessage so we
|
||||
// fill in the actual error message.
|
||||
// Call the matches method again, this time with a DiagMessage so we fill in the actual
|
||||
// error message.
|
||||
symbol->attribute->Matches(*entry.value, &msg);
|
||||
context_->GetDiagnostics()->Error(msg);
|
||||
error_ = true;
|
||||
@@ -125,7 +108,7 @@ class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
} else {
|
||||
DiagMessage msg(entry.key.GetSource());
|
||||
msg << "style attribute '";
|
||||
ReferenceLinker::WriteResourceName(&msg, entry.key, transformed_reference);
|
||||
ReferenceLinker::WriteResourceName(entry.key, callsite_, package_decls_, &msg);
|
||||
msg << "' " << err_str;
|
||||
context_->GetDiagnostics()->Error(msg);
|
||||
error_ = true;
|
||||
@@ -133,17 +116,15 @@ class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
bool HasError() { return error_; }
|
||||
bool HasError() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ReferenceLinkerVisitor);
|
||||
|
||||
/**
|
||||
* Transform a RawString value into a more specific, appropriate value, based
|
||||
* on the
|
||||
* Attribute. If a non RawString value is passed in, this is an identity
|
||||
* transform.
|
||||
*/
|
||||
// Transform a RawString value into a more specific, appropriate value, based on the
|
||||
// Attribute. If a non RawString value is passed in, this is an identity transform.
|
||||
std::unique_ptr<Item> ParseValueWithAttribute(std::unique_ptr<Item> value,
|
||||
const Attribute* attr) {
|
||||
if (RawString* raw_string = ValueCast<RawString>(value.get())) {
|
||||
@@ -178,11 +159,9 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
|
||||
public:
|
||||
EmptyDeclStack() = default;
|
||||
|
||||
Maybe<xml::ExtractedPackage> TransformPackageAlias(
|
||||
const StringPiece& alias,
|
||||
const StringPiece& local_package) const override {
|
||||
Maybe<xml::ExtractedPackage> TransformPackageAlias(const StringPiece& alias) const override {
|
||||
if (alias.empty()) {
|
||||
return xml::ExtractedPackage{local_package.to_string(), true /* private */};
|
||||
return xml::ExtractedPackage{{}, true /*private*/};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -191,32 +170,44 @@ class EmptyDeclStack : public xml::IPackageDeclStack {
|
||||
DISALLOW_COPY_AND_ASSIGN(EmptyDeclStack);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/**
|
||||
* The symbol is visible if it is public, or if the reference to it is
|
||||
* requesting private access
|
||||
* or if the callsite comes from the same package.
|
||||
*/
|
||||
bool ReferenceLinker::IsSymbolVisible(const SymbolTable::Symbol& symbol,
|
||||
const Reference& ref,
|
||||
const CallSite& callsite) {
|
||||
if (!symbol.is_public && !ref.private_reference) {
|
||||
if (ref.name) {
|
||||
return callsite.resource.package == ref.name.value().package;
|
||||
} else if (ref.id && symbol.id) {
|
||||
return ref.id.value().package_id() == symbol.id.value().package_id();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
// The symbol is visible if it is public, or if the reference to it is requesting private access
|
||||
// or if the callsite comes from the same package.
|
||||
bool IsSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
|
||||
const CallSite& callsite) {
|
||||
if (symbol.is_public || ref.private_reference) {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
|
||||
if (ref.name) {
|
||||
const ResourceName& name = ref.name.value();
|
||||
if (name.package.empty()) {
|
||||
// If the symbol was found, and the package is empty, that means it was found in the local
|
||||
// scope, which is always visible (private local).
|
||||
return true;
|
||||
}
|
||||
|
||||
// The symbol is visible if the reference is local to the same package it is defined in.
|
||||
return callsite.package == name.package;
|
||||
}
|
||||
|
||||
if (ref.id && symbol.id) {
|
||||
return ref.id.value().package_id() == symbol.id.value().package_id();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const SymbolTable::Symbol* ReferenceLinker::ResolveSymbol(const Reference& reference,
|
||||
const CallSite& callsite,
|
||||
SymbolTable* symbols) {
|
||||
if (reference.name) {
|
||||
return symbols->FindByName(reference.name.value());
|
||||
const ResourceName& name = reference.name.value();
|
||||
if (name.package.empty()) {
|
||||
// Use the callsite's package name if no package name was defined.
|
||||
return symbols->FindByName(ResourceName(callsite.package, name.type, name.entry));
|
||||
}
|
||||
return symbols->FindByName(name);
|
||||
} else if (reference.id) {
|
||||
return symbols->FindById(reference.id.value());
|
||||
} else {
|
||||
@@ -228,7 +219,7 @@ const SymbolTable::Symbol* ReferenceLinker::ResolveSymbolCheckVisibility(const R
|
||||
const CallSite& callsite,
|
||||
SymbolTable* symbols,
|
||||
std::string* out_error) {
|
||||
const SymbolTable::Symbol* symbol = ResolveSymbol(reference, symbols);
|
||||
const SymbolTable::Symbol* symbol = ResolveSymbol(reference, callsite, symbols);
|
||||
if (!symbol) {
|
||||
if (out_error) *out_error = "not found";
|
||||
return nullptr;
|
||||
@@ -274,24 +265,62 @@ Maybe<xml::AaptAttribute> ReferenceLinker::CompileXmlAttribute(const Reference&
|
||||
return xml::AaptAttribute(*symbol->attribute, symbol->id);
|
||||
}
|
||||
|
||||
void ReferenceLinker::WriteResourceName(DiagMessage* out_msg,
|
||||
const Reference& orig,
|
||||
const Reference& transformed) {
|
||||
void ReferenceLinker::WriteResourceName(const Reference& ref, const CallSite& callsite,
|
||||
const xml::IPackageDeclStack* decls, DiagMessage* out_msg) {
|
||||
CHECK(out_msg != nullptr);
|
||||
if (!ref.name) {
|
||||
*out_msg << ref.id.value();
|
||||
return;
|
||||
}
|
||||
|
||||
if (orig.name) {
|
||||
*out_msg << orig.name.value();
|
||||
if (transformed.name.value() != orig.name.value()) {
|
||||
*out_msg << " (aka " << transformed.name.value() << ")";
|
||||
}
|
||||
} else {
|
||||
*out_msg << orig.id.value();
|
||||
*out_msg << ref.name.value();
|
||||
|
||||
Reference fully_qualified = ref;
|
||||
xml::ResolvePackage(decls, &fully_qualified);
|
||||
|
||||
ResourceName& full_name = fully_qualified.name.value();
|
||||
if (full_name.package.empty()) {
|
||||
full_name.package = callsite.package;
|
||||
}
|
||||
|
||||
if (full_name != ref.name.value()) {
|
||||
*out_msg << " (aka " << full_name << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void ReferenceLinker::WriteAttributeName(const Reference& ref, const CallSite& callsite,
|
||||
const xml::IPackageDeclStack* decls,
|
||||
DiagMessage* out_msg) {
|
||||
CHECK(out_msg != nullptr);
|
||||
if (!ref.name) {
|
||||
*out_msg << ref.id.value();
|
||||
return;
|
||||
}
|
||||
|
||||
const ResourceName& ref_name = ref.name.value();
|
||||
CHECK_EQ(ref_name.type, ResourceType::kAttr);
|
||||
|
||||
if (!ref_name.package.empty()) {
|
||||
*out_msg << ref_name.package << ":";
|
||||
}
|
||||
*out_msg << ref_name.entry;
|
||||
|
||||
Reference fully_qualified = ref;
|
||||
xml::ResolvePackage(decls, &fully_qualified);
|
||||
|
||||
ResourceName& full_name = fully_qualified.name.value();
|
||||
if (full_name.package.empty()) {
|
||||
full_name.package = callsite.package;
|
||||
}
|
||||
|
||||
if (full_name != ref.name.value()) {
|
||||
*out_msg << " (aka " << full_name.package << ":" << full_name.entry << ")";
|
||||
}
|
||||
}
|
||||
|
||||
bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* reference,
|
||||
IAaptContext* context, SymbolTable* symbols,
|
||||
xml::IPackageDeclStack* decls) {
|
||||
const xml::IPackageDeclStack* decls) {
|
||||
CHECK(reference != nullptr);
|
||||
if (!reference->name && !reference->id) {
|
||||
// This is @null.
|
||||
@@ -299,7 +328,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
|
||||
}
|
||||
|
||||
Reference transformed_reference = *reference;
|
||||
TransformReferenceFromNamespace(decls, context->GetCompilationPackage(), &transformed_reference);
|
||||
xml::ResolvePackage(decls, &transformed_reference);
|
||||
|
||||
std::string err_str;
|
||||
const SymbolTable::Symbol* s =
|
||||
@@ -314,7 +343,7 @@ bool ReferenceLinker::LinkReference(const CallSite& callsite, Reference* referen
|
||||
|
||||
DiagMessage error_msg(reference->GetSource());
|
||||
error_msg << "resource ";
|
||||
WriteResourceName(&error_msg, *reference, transformed_reference);
|
||||
WriteResourceName(*reference, callsite, decls, &error_msg);
|
||||
error_msg << " " << err_str;
|
||||
context->GetDiagnostics()->Error(error_msg);
|
||||
return false;
|
||||
@@ -324,21 +353,24 @@ bool ReferenceLinker::Consume(IAaptContext* context, ResourceTable* table) {
|
||||
EmptyDeclStack decl_stack;
|
||||
bool error = false;
|
||||
for (auto& package : table->packages) {
|
||||
// Since we're linking, each package must have a name.
|
||||
CHECK(!package->name.empty()) << "all packages being linked must have a name";
|
||||
|
||||
for (auto& type : package->types) {
|
||||
for (auto& entry : type->entries) {
|
||||
// Symbol state information may be lost if there is no value for the
|
||||
// resource.
|
||||
if (entry->symbol_status.state != SymbolState::kUndefined &&
|
||||
entry->values.empty()) {
|
||||
context->GetDiagnostics()->Error(
|
||||
DiagMessage(entry->symbol_status.source)
|
||||
<< "no definition for declared symbol '"
|
||||
<< ResourceNameRef(package->name, type->type, entry->name)
|
||||
<< "'");
|
||||
// First, unmangle the name if necessary.
|
||||
ResourceName name(package->name, type->type, entry->name);
|
||||
NameMangler::Unmangle(&name.entry, &name.package);
|
||||
|
||||
// Symbol state information may be lost if there is no value for the resource.
|
||||
if (entry->symbol_status.state != SymbolState::kUndefined && entry->values.empty()) {
|
||||
context->GetDiagnostics()->Error(DiagMessage(entry->symbol_status.source)
|
||||
<< "no definition for declared symbol '" << name << "'");
|
||||
error = true;
|
||||
}
|
||||
|
||||
CallSite callsite = {ResourceNameRef(package->name, type->type, entry->name)};
|
||||
// The context of this resource is the package in which it is defined.
|
||||
const CallSite callsite{name.package};
|
||||
ReferenceLinkerVisitor visitor(callsite, context, context->GetExternalSymbols(),
|
||||
&table->string_pool, &decl_stack);
|
||||
|
||||
|
||||
@@ -29,83 +29,58 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
/**
|
||||
* Resolves all references to resources in the ResourceTable and assigns them
|
||||
* IDs.
|
||||
* The ResourceTable must already have IDs assigned to each resource.
|
||||
* Once the ResourceTable is processed by this linker, it is ready to be
|
||||
* flattened.
|
||||
*/
|
||||
// Resolves all references to resources in the ResourceTable and assigns them IDs.
|
||||
// The ResourceTable must already have IDs assigned to each resource.
|
||||
// Once the ResourceTable is processed by this linker, it is ready to be flattened.
|
||||
class ReferenceLinker : public IResourceTableConsumer {
|
||||
public:
|
||||
ReferenceLinker() = default;
|
||||
|
||||
/**
|
||||
* Returns true if the symbol is visible by the reference and from the
|
||||
* callsite.
|
||||
*/
|
||||
static bool IsSymbolVisible(const SymbolTable::Symbol& symbol,
|
||||
const Reference& ref, const CallSite& callsite);
|
||||
// Performs name mangling and looks up the resource in the symbol table. Uses the callsite's
|
||||
// package if the reference has no package name defined (implicit).
|
||||
// Returns nullptr if the symbol was not found.
|
||||
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference,
|
||||
const CallSite& callsite, SymbolTable* symbols);
|
||||
|
||||
/**
|
||||
* Performs name mangling and looks up the resource in the symbol table.
|
||||
* Returns nullptr if the symbol was not found.
|
||||
*/
|
||||
static const SymbolTable::Symbol* ResolveSymbol(const Reference& reference, SymbolTable* symbols);
|
||||
|
||||
/**
|
||||
* Performs name mangling and looks up the resource in the symbol table. If
|
||||
* the symbol is not visible by the reference at the callsite, nullptr is
|
||||
* returned. out_error holds the error message.
|
||||
*/
|
||||
// Performs name mangling and looks up the resource in the symbol table. If the symbol is not
|
||||
// visible by the reference at the callsite, nullptr is returned.
|
||||
// `out_error` holds the error message.
|
||||
static const SymbolTable::Symbol* ResolveSymbolCheckVisibility(const Reference& reference,
|
||||
const CallSite& callsite,
|
||||
SymbolTable* symbols,
|
||||
std::string* out_error);
|
||||
|
||||
/**
|
||||
* Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is
|
||||
* an attribute.
|
||||
* That is, the return value will have a non-null value for
|
||||
* ISymbolTable::Symbol::attribute.
|
||||
*/
|
||||
// Same as ResolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute.
|
||||
// That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute.
|
||||
static const SymbolTable::Symbol* ResolveAttributeCheckVisibility(const Reference& reference,
|
||||
const CallSite& callsite,
|
||||
SymbolTable* symbols,
|
||||
std::string* out_error);
|
||||
|
||||
/**
|
||||
* Resolves the attribute reference and returns an xml::AaptAttribute if
|
||||
* successful.
|
||||
* If resolution fails, outError holds the error message.
|
||||
*/
|
||||
// Resolves the attribute reference and returns an xml::AaptAttribute if successful.
|
||||
// If resolution fails, outError holds the error message.
|
||||
static Maybe<xml::AaptAttribute> CompileXmlAttribute(const Reference& reference,
|
||||
const CallSite& callsite,
|
||||
SymbolTable* symbols,
|
||||
std::string* out_error);
|
||||
|
||||
/**
|
||||
* Writes the resource name to the DiagMessage, using the
|
||||
* "orig_name (aka <transformed_name>)" syntax.
|
||||
*/
|
||||
static void WriteResourceName(DiagMessage* out_msg, const Reference& orig,
|
||||
const Reference& transformed);
|
||||
// Writes the resource name to the DiagMessage, using the
|
||||
// "orig_name (aka <transformed_name>)" syntax.
|
||||
static void WriteResourceName(const Reference& orig, const CallSite& callsite,
|
||||
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
|
||||
|
||||
/**
|
||||
* Transforms the package name of the reference to the fully qualified package
|
||||
* name using
|
||||
* the xml::IPackageDeclStack, then mangles and looks up the symbol. If the
|
||||
* symbol is visible
|
||||
* to the reference at the callsite, the reference is updated with an ID.
|
||||
* Returns false on failure, and an error message is logged to the
|
||||
* IDiagnostics in the context.
|
||||
*/
|
||||
// Same as WriteResourceName but omits the 'attr' part.
|
||||
static void WriteAttributeName(const Reference& ref, const CallSite& callsite,
|
||||
const xml::IPackageDeclStack* decls, DiagMessage* out_msg);
|
||||
|
||||
// Transforms the package name of the reference to the fully qualified package name using
|
||||
// the xml::IPackageDeclStack, then mangles and looks up the symbol. If the symbol is visible
|
||||
// to the reference at the callsite, the reference is updated with an ID.
|
||||
// Returns false on failure, and an error message is logged to the IDiagnostics in the context.
|
||||
static bool LinkReference(const CallSite& callsite, Reference* reference, IAaptContext* context,
|
||||
SymbolTable* symbols, xml::IPackageDeclStack* decls);
|
||||
SymbolTable* symbols, const xml::IPackageDeclStack* decls);
|
||||
|
||||
/**
|
||||
* Links all references in the ResourceTable.
|
||||
*/
|
||||
// Links all references in the ResourceTable.
|
||||
bool Consume(IAaptContext* context, ResourceTable* table) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
|
||||
#include "test/Test.h"
|
||||
|
||||
using android::ResTable_map;
|
||||
using ::android::ResTable_map;
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsNull;
|
||||
using ::testing::NotNull;
|
||||
|
||||
namespace aapt {
|
||||
@@ -263,7 +265,7 @@ TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic)
|
||||
.Build());
|
||||
|
||||
std::string error;
|
||||
const CallSite call_site{ResourceNameRef("com.app.test", ResourceType::kString, "foo")};
|
||||
const CallSite call_site{"com.app.test"};
|
||||
const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
|
||||
*test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
|
||||
ASSERT_THAT(symbol, NotNull());
|
||||
@@ -281,7 +283,7 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
|
||||
.Build());
|
||||
|
||||
std::string error;
|
||||
const CallSite call_site{ResourceNameRef("com.app.ext", ResourceType::kLayout, "foo")};
|
||||
const CallSite call_site{"com.app.ext"};
|
||||
|
||||
EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
|
||||
*test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
|
||||
@@ -293,4 +295,27 @@ TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute)
|
||||
EXPECT_TRUE(error.empty());
|
||||
}
|
||||
|
||||
TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
|
||||
NameMangler mangler(NameManglerPolicy{"com.app.test"});
|
||||
SymbolTable table(&mangler);
|
||||
table.AppendSource(test::StaticSymbolSourceBuilder()
|
||||
.AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
|
||||
.AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
|
||||
.Build());
|
||||
|
||||
const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
|
||||
CallSite{"com.app.test"}, &table);
|
||||
ASSERT_THAT(s, NotNull());
|
||||
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
|
||||
|
||||
s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
|
||||
&table);
|
||||
ASSERT_THAT(s, NotNull());
|
||||
EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
|
||||
|
||||
EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
|
||||
CallSite{"com.app.bad"}, &table),
|
||||
IsNull());
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "ValueVisitor.h"
|
||||
#include "util/Util.h"
|
||||
|
||||
using android::StringPiece;
|
||||
using ::android::StringPiece;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -32,27 +32,23 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* out_table,
|
||||
const TableMergerOptions& options)
|
||||
: context_(context), master_table_(out_table), options_(options) {
|
||||
// Create the desired package that all tables will be merged into.
|
||||
master_package_ = master_table_->CreatePackage(
|
||||
context_->GetCompilationPackage(), context_->GetPackageId());
|
||||
master_package_ =
|
||||
master_table_->CreatePackage(context_->GetCompilationPackage(), context_->GetPackageId());
|
||||
CHECK(master_package_ != nullptr) << "package name or ID already taken";
|
||||
}
|
||||
|
||||
bool TableMerger::Merge(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection) {
|
||||
return MergeImpl(src, table, collection, false /* overlay */, true /* allow new */);
|
||||
bool TableMerger::Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection) {
|
||||
return MergeImpl(src, table, collection, false /*overlay*/, true /*allow_new*/);
|
||||
}
|
||||
|
||||
bool TableMerger::MergeOverlay(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection) {
|
||||
return MergeImpl(src, table, collection, true /* overlay */, options_.auto_add_overlay);
|
||||
return MergeImpl(src, table, collection, true /*overlay*/, options_.auto_add_overlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will merge packages with the same package name (or no package name).
|
||||
*/
|
||||
// This will merge packages with the same package name (or no package name).
|
||||
bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection, bool overlay,
|
||||
bool allow_new) {
|
||||
io::IFileCollection* collection, bool overlay, bool allow_new) {
|
||||
bool error = false;
|
||||
for (auto& package : table->packages) {
|
||||
// Only merge an empty package or the package we're building.
|
||||
@@ -62,9 +58,8 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
|
||||
if (package->name.empty() || context_->GetCompilationPackage() == package->name) {
|
||||
FileMergeCallback callback;
|
||||
if (collection) {
|
||||
callback = [&](const ResourceNameRef& name,
|
||||
const ConfigDescription& config, FileReference* new_file,
|
||||
FileReference* old_file) -> bool {
|
||||
callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* new_file, FileReference* old_file) -> bool {
|
||||
// The old file's path points inside the APK, so we can use it as is.
|
||||
io::IFile* f = collection->FindFile(*old_file->path);
|
||||
if (!f) {
|
||||
@@ -78,45 +73,38 @@ bool TableMerger::MergeImpl(const Source& src, ResourceTable* table,
|
||||
};
|
||||
}
|
||||
|
||||
// Merge here. Once the entries are merged and mangled, any references to
|
||||
// them are still valid. This is because un-mangled references are
|
||||
// mangled, then looked up at resolution time.
|
||||
// Also, when linking, we convert references with no package name to use
|
||||
// the compilation package name.
|
||||
error |= !DoMerge(src, table, package.get(), false /* mangle */, overlay,
|
||||
allow_new, callback);
|
||||
// Merge here. Once the entries are merged and mangled, any references to them are still
|
||||
// valid. This is because un-mangled references are mangled, then looked up at resolution
|
||||
// time. Also, when linking, we convert references with no package name to use the compilation
|
||||
// package name.
|
||||
error |=
|
||||
!DoMerge(src, table, package.get(), false /* mangle */, overlay, allow_new, callback);
|
||||
}
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will merge and mangle resources from a static library.
|
||||
*/
|
||||
bool TableMerger::MergeAndMangle(const Source& src,
|
||||
const StringPiece& package_name,
|
||||
ResourceTable* table,
|
||||
io::IFileCollection* collection) {
|
||||
// This will merge and mangle resources from a static library.
|
||||
bool TableMerger::MergeAndMangle(const Source& src, const StringPiece& package_name,
|
||||
ResourceTable* table, io::IFileCollection* collection) {
|
||||
bool error = false;
|
||||
for (auto& package : table->packages) {
|
||||
// Warn of packages with an unrelated ID.
|
||||
if (package_name != package->name) {
|
||||
context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package "
|
||||
<< package->name);
|
||||
context_->GetDiagnostics()->Warn(DiagMessage(src) << "ignoring package " << package->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool mangle = package_name != context_->GetCompilationPackage();
|
||||
merged_packages_.insert(package->name);
|
||||
|
||||
auto callback = [&](
|
||||
const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* new_file, FileReference* old_file) -> bool {
|
||||
auto callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* new_file, FileReference* old_file) -> bool {
|
||||
// The old file's path points inside the APK, so we can use it as is.
|
||||
io::IFile* f = collection->FindFile(*old_file->path);
|
||||
if (!f) {
|
||||
context_->GetDiagnostics()->Error(
|
||||
DiagMessage(src) << "file '" << *old_file->path << "' not found");
|
||||
context_->GetDiagnostics()->Error(DiagMessage(src)
|
||||
<< "file '" << *old_file->path << "' not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -124,21 +112,18 @@ bool TableMerger::MergeAndMangle(const Source& src,
|
||||
return true;
|
||||
};
|
||||
|
||||
error |= !DoMerge(src, table, package.get(), mangle, false /* overlay */,
|
||||
true /* allow new */, callback);
|
||||
error |= !DoMerge(src, table, package.get(), mangle, false /*overlay*/, true /*allow_new*/,
|
||||
callback);
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
static bool MergeType(IAaptContext* context, const Source& src,
|
||||
ResourceTableType* dst_type,
|
||||
static bool MergeType(IAaptContext* context, const Source& src, ResourceTableType* dst_type,
|
||||
ResourceTableType* src_type) {
|
||||
if (dst_type->symbol_status.state < src_type->symbol_status.state) {
|
||||
// The incoming type's visibility is stronger, so we should override
|
||||
// the visibility.
|
||||
// The incoming type's visibility is stronger, so we should override the visibility.
|
||||
if (src_type->symbol_status.state == SymbolState::kPublic) {
|
||||
// Only copy the ID if the source is public, or else the ID is
|
||||
// meaningless.
|
||||
// Only copy the ID if the source is public, or else the ID is meaningless.
|
||||
dst_type->id = src_type->id;
|
||||
}
|
||||
dst_type->symbol_status = std::move(src_type->symbol_status);
|
||||
@@ -155,14 +140,12 @@ static bool MergeType(IAaptContext* context, const Source& src,
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool MergeEntry(IAaptContext* context, const Source& src,
|
||||
ResourceEntry* dst_entry, ResourceEntry* src_entry) {
|
||||
static bool MergeEntry(IAaptContext* context, const Source& src, ResourceEntry* dst_entry,
|
||||
ResourceEntry* src_entry) {
|
||||
if (dst_entry->symbol_status.state < src_entry->symbol_status.state) {
|
||||
// The incoming type's visibility is stronger, so we should override
|
||||
// the visibility.
|
||||
// The incoming type's visibility is stronger, so we should override the visibility.
|
||||
if (src_entry->symbol_status.state == SymbolState::kPublic) {
|
||||
// Only copy the ID if the source is public, or else the ID is
|
||||
// meaningless.
|
||||
// Only copy the ID if the source is public, or else the ID is meaningless.
|
||||
dst_entry->id = src_entry->id;
|
||||
}
|
||||
dst_entry->symbol_status = std::move(src_entry->symbol_status);
|
||||
@@ -171,9 +154,8 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
|
||||
dst_entry->id && src_entry->id &&
|
||||
dst_entry->id.value() != src_entry->id.value()) {
|
||||
// Both entries are public and have different IDs.
|
||||
context->GetDiagnostics()->Error(
|
||||
DiagMessage(src) << "cannot merge entry '" << src_entry->name
|
||||
<< "': conflicting public IDs");
|
||||
context->GetDiagnostics()->Error(DiagMessage(src) << "cannot merge entry '" << src_entry->name
|
||||
<< "': conflicting public IDs");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -181,12 +163,10 @@ static bool MergeEntry(IAaptContext* context, const Source& src,
|
||||
|
||||
// Modified CollisionResolver which will merge Styleables and Styles. Used with overlays.
|
||||
//
|
||||
// Styleables are not actual resources, but they are treated as such during the
|
||||
// compilation phase.
|
||||
// Styleables are not actual resources, but they are treated as such during the compilation phase.
|
||||
//
|
||||
// Styleables and Styles don't simply overlay each other, their definitions merge
|
||||
// and accumulate. If both values are Styleables/Styles, we just merge them into the
|
||||
// existing value.
|
||||
// Styleables and Styles don't simply overlay each other, their definitions merge and accumulate.
|
||||
// If both values are Styleables/Styles, we just merge them into the existing value.
|
||||
static ResourceTable::CollisionResult ResolveMergeCollision(Value* existing, Value* incoming,
|
||||
StringPool* pool) {
|
||||
if (Styleable* existing_styleable = ValueCast<Styleable>(existing)) {
|
||||
|
||||
@@ -33,81 +33,49 @@
|
||||
namespace aapt {
|
||||
|
||||
struct TableMergerOptions {
|
||||
/**
|
||||
* If true, resources in overlays can be added without previously having
|
||||
* existed.
|
||||
*/
|
||||
// If true, resources in overlays can be added without previously having existed.
|
||||
bool auto_add_overlay = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* TableMerger takes resource tables and merges all packages within the tables
|
||||
* that have the same
|
||||
* package ID.
|
||||
*
|
||||
* If a package has a different name, all the entries in that table have their
|
||||
* names mangled
|
||||
* to include the package name. This way there are no collisions. In order to do
|
||||
* this correctly,
|
||||
* the TableMerger needs to also mangle any FileReference paths. Once these are
|
||||
* mangled,
|
||||
* the original source path of the file, along with the new destination path is
|
||||
* recorded in the
|
||||
* queue returned from getFileMergeQueue().
|
||||
*
|
||||
* Once the merging is complete, a separate process can go collect the files
|
||||
* from the various
|
||||
* source APKs and either copy or process their XML and put them in the correct
|
||||
* location in
|
||||
* the final APK.
|
||||
*/
|
||||
// TableMerger takes resource tables and merges all packages within the tables that have the same
|
||||
// package ID.
|
||||
//
|
||||
// If a package has a different name, all the entries in that table have their names mangled
|
||||
// to include the package name. This way there are no collisions. In order to do this correctly,
|
||||
// the TableMerger needs to also mangle any FileReference paths. Once these are mangled, the
|
||||
// `IFile` pointer in `FileReference` will point to the original file.
|
||||
//
|
||||
// Once the merging is complete, a separate phase can go collect the files from the various
|
||||
// source APKs and either copy or process their XML and put them in the correct location in the
|
||||
// final APK.
|
||||
class TableMerger {
|
||||
public:
|
||||
/**
|
||||
* Note: The out_table ResourceTable must live longer than this TableMerger.
|
||||
* References are made to this ResourceTable for efficiency reasons.
|
||||
*/
|
||||
TableMerger(IAaptContext* context, ResourceTable* out_table,
|
||||
const TableMergerOptions& options);
|
||||
// Note: The out_table ResourceTable must live longer than this TableMerger.
|
||||
// References are made to this ResourceTable for efficiency reasons.
|
||||
TableMerger(IAaptContext* context, ResourceTable* out_table, const TableMergerOptions& options);
|
||||
|
||||
const std::set<std::string>& merged_packages() const {
|
||||
inline const std::set<std::string>& merged_packages() const {
|
||||
return merged_packages_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges resources from the same or empty package. This is for local sources.
|
||||
* An io::IFileCollection is optional and used to find the referenced Files
|
||||
* and process them.
|
||||
*/
|
||||
bool Merge(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection = nullptr);
|
||||
// Merges resources from the same or empty package. This is for local sources.
|
||||
// An io::IFileCollection is optional and used to find the referenced Files and process them.
|
||||
bool Merge(const Source& src, ResourceTable* table, io::IFileCollection* collection = nullptr);
|
||||
|
||||
/**
|
||||
* Merges resources from an overlay ResourceTable.
|
||||
* An io::IFileCollection is optional and used to find the referenced Files
|
||||
* and process them.
|
||||
*/
|
||||
// Merges resources from an overlay ResourceTable.
|
||||
// An io::IFileCollection is optional and used to find the referenced Files and process them.
|
||||
bool MergeOverlay(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection = nullptr);
|
||||
|
||||
/**
|
||||
* Merges resources from the given package, mangling the name. This is for
|
||||
* static libraries.
|
||||
* An io::IFileCollection is needed in order to find the referenced Files and
|
||||
* process them.
|
||||
*/
|
||||
// Merges resources from the given package, mangling the name. This is for static libraries.
|
||||
// An io::IFileCollection is needed in order to find the referenced Files and process them.
|
||||
bool MergeAndMangle(const Source& src, const android::StringPiece& package, ResourceTable* table,
|
||||
io::IFileCollection* collection);
|
||||
|
||||
/**
|
||||
* Merges a compiled file that belongs to this same or empty package. This is
|
||||
* for local sources.
|
||||
*/
|
||||
// Merges a compiled file that belongs to this same or empty package. This is for local sources.
|
||||
bool MergeFile(const ResourceFile& fileDesc, io::IFile* file);
|
||||
|
||||
/**
|
||||
* Merges a compiled file from an overlay, overriding an existing definition.
|
||||
*/
|
||||
// Merges a compiled file from an overlay, overriding an existing definition.
|
||||
bool MergeFileOverlay(const ResourceFile& fileDesc, io::IFile* file);
|
||||
|
||||
private:
|
||||
|
||||
@@ -31,13 +31,9 @@ namespace aapt {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Visits all references (including parents of styles, references in styles,
|
||||
* arrays, etc) and
|
||||
* links their symbolic name to their Resource ID, performing mangling and
|
||||
* package aliasing
|
||||
* as needed.
|
||||
*/
|
||||
// Visits all references (including parents of styles, references in styles, arrays, etc) and
|
||||
// links their symbolic name to their Resource ID, performing mangling and package aliasing
|
||||
// as needed.
|
||||
class ReferenceVisitor : public ValueVisitor {
|
||||
public:
|
||||
using ValueVisitor::Visit;
|
||||
@@ -52,7 +48,9 @@ class ReferenceVisitor : public ValueVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
bool HasError() const { return error_; }
|
||||
bool HasError() const {
|
||||
return error_;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ReferenceVisitor);
|
||||
@@ -64,9 +62,7 @@ class ReferenceVisitor : public ValueVisitor {
|
||||
bool error_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visits each xml Element and compiles the attributes within.
|
||||
*/
|
||||
// Visits each xml Element and compiles the attributes within.
|
||||
class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
public:
|
||||
using xml::PackageAwareVisitor::Visit;
|
||||
@@ -92,18 +88,12 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
// they were assigned to the default Attribute.
|
||||
|
||||
const Attribute* attribute = &kDefaultAttribute;
|
||||
std::string attribute_package;
|
||||
|
||||
if (Maybe<xml::ExtractedPackage> maybe_package =
|
||||
xml::ExtractPackageFromNamespace(attr.namespace_uri)) {
|
||||
// There is a valid package name for this attribute. We will look this up.
|
||||
attribute_package = maybe_package.value().package;
|
||||
if (attribute_package.empty()) {
|
||||
// Empty package means the 'current' or 'local' package.
|
||||
attribute_package = context_->GetCompilationPackage();
|
||||
}
|
||||
|
||||
Reference attr_ref(ResourceNameRef(attribute_package, ResourceType::kAttr, attr.name));
|
||||
Reference attr_ref(
|
||||
ResourceNameRef(maybe_package.value().package, ResourceType::kAttr, attr.name));
|
||||
attr_ref.private_reference = maybe_package.value().private_namespace;
|
||||
|
||||
std::string err_str;
|
||||
@@ -111,9 +101,11 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
ReferenceLinker::CompileXmlAttribute(attr_ref, callsite_, symbols_, &err_str);
|
||||
|
||||
if (!attr.compiled_attribute) {
|
||||
context_->GetDiagnostics()->Error(DiagMessage(source) << "attribute '"
|
||||
<< attribute_package << ":"
|
||||
<< attr.name << "' " << err_str);
|
||||
DiagMessage error_msg(source);
|
||||
error_msg << "attribute ";
|
||||
ReferenceLinker::WriteAttributeName(attr_ref, callsite_, this, &error_msg);
|
||||
error_msg << " " << err_str;
|
||||
context_->GetDiagnostics()->Error(error_msg);
|
||||
error_ = true;
|
||||
continue;
|
||||
}
|
||||
@@ -129,12 +121,8 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
} else if ((attribute->type_mask & android::ResTable_map::TYPE_STRING) == 0) {
|
||||
// We won't be able to encode this as a string.
|
||||
DiagMessage msg(source);
|
||||
msg << "'" << attr.value << "' "
|
||||
<< "is incompatible with attribute ";
|
||||
if (!attribute_package.empty()) {
|
||||
msg << attribute_package << ":";
|
||||
}
|
||||
msg << attr.name << " " << *attribute;
|
||||
msg << "'" << attr.value << "' is incompatible with attribute " << attr.name << " "
|
||||
<< *attribute;
|
||||
context_->GetDiagnostics()->Error(msg);
|
||||
error_ = true;
|
||||
}
|
||||
@@ -163,7 +151,17 @@ class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
} // namespace
|
||||
|
||||
bool XmlReferenceLinker::Consume(IAaptContext* context, xml::XmlResource* resource) {
|
||||
const CallSite callsite = {resource->file.name};
|
||||
CallSite callsite{resource->file.name.package};
|
||||
|
||||
std::string out_name = resource->file.name.entry;
|
||||
NameMangler::Unmangle(&out_name, &callsite.package);
|
||||
|
||||
if (callsite.package.empty()) {
|
||||
// Assume an empty package means that the XML file is local. This is true of AndroidManifest.xml
|
||||
// for example.
|
||||
callsite.package = context->GetCompilationPackage();
|
||||
}
|
||||
|
||||
XmlVisitor visitor(resource->file.source, callsite, context, context->GetExternalSymbols());
|
||||
if (resource->root) {
|
||||
resource->root->Accept(&visitor);
|
||||
|
||||
@@ -274,6 +274,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
switch (code) {
|
||||
case ResXMLParser::START_NAMESPACE: {
|
||||
NamespaceDecl decl;
|
||||
decl.line_number = tree.getLineNumber();
|
||||
|
||||
size_t len;
|
||||
const char16_t* str16 = tree.getNamespacePrefix(&len);
|
||||
if (str16) {
|
||||
@@ -288,6 +290,7 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
if (pending_element == nullptr) {
|
||||
pending_element = util::make_unique<Element>();
|
||||
}
|
||||
pending_element->namespace_decls.push_back(std::move(decl));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -297,8 +300,8 @@ std::unique_ptr<XmlResource> Inflate(const void* data, size_t data_len, IDiagnos
|
||||
el = std::move(pending_element);
|
||||
} else {
|
||||
el = util::make_unique<Element>();
|
||||
;
|
||||
}
|
||||
el->line_number = tree.getLineNumber();
|
||||
|
||||
size_t len;
|
||||
const char16_t* str16 = tree.getElementNamespace(&len);
|
||||
@@ -479,10 +482,9 @@ void PackageAwareVisitor::AfterVisitElement(Element* el) {
|
||||
package_decls_.pop_back();
|
||||
}
|
||||
|
||||
Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
|
||||
const StringPiece& alias, const StringPiece& local_package) const {
|
||||
Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(const StringPiece& alias) const {
|
||||
if (alias.empty()) {
|
||||
return ExtractedPackage{local_package.to_string(), false /* private */};
|
||||
return ExtractedPackage{{}, false /*private*/};
|
||||
}
|
||||
|
||||
const auto rend = package_decls_.rend();
|
||||
@@ -493,7 +495,7 @@ Maybe<ExtractedPackage> PackageAwareVisitor::TransformPackageAlias(
|
||||
const PackageDecl& decl = *iter2;
|
||||
if (alias == decl.prefix) {
|
||||
if (decl.package.package.empty()) {
|
||||
return ExtractedPackage{local_package.to_string(), decl.package.private_namespace};
|
||||
return ExtractedPackage{{}, decl.package.private_namespace};
|
||||
}
|
||||
return decl.package;
|
||||
}
|
||||
|
||||
@@ -185,8 +185,7 @@ class PackageAwareVisitor : public Visitor, public IPackageDeclStack {
|
||||
public:
|
||||
using Visitor::Visit;
|
||||
|
||||
Maybe<ExtractedPackage> TransformPackageAlias(
|
||||
const android::StringPiece& alias, const android::StringPiece& local_package) const override;
|
||||
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
|
||||
|
||||
protected:
|
||||
PackageAwareVisitor() = default;
|
||||
|
||||
@@ -86,19 +86,14 @@ class TestVisitor : public PackageAwareVisitor {
|
||||
|
||||
void Visit(Element* el) override {
|
||||
if (el->name == "View1") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
} else if (el->name == "View2") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
} else if (el->name == "View3") {
|
||||
EXPECT_THAT(TransformPackageAlias("one", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two", "local"),
|
||||
Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("three", "local"),
|
||||
EXPECT_THAT(TransformPackageAlias("one"), Eq(make_value(ExtractedPackage{"com.one", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("two"), Eq(make_value(ExtractedPackage{"com.two", false})));
|
||||
EXPECT_THAT(TransformPackageAlias("three"),
|
||||
Eq(make_value(ExtractedPackage{"com.three", false})));
|
||||
}
|
||||
}
|
||||
@@ -112,7 +107,6 @@ TEST(XmlDomTest, PackageAwareXmlVisitor) {
|
||||
</View2>
|
||||
</View1>)");
|
||||
|
||||
Debug::DumpXml(doc.get());
|
||||
TestVisitor visitor;
|
||||
doc->root->Accept(&visitor);
|
||||
}
|
||||
|
||||
@@ -141,17 +141,16 @@ const std::string& XmlPullParser::namespace_uri() const {
|
||||
return event_queue_.front().data2;
|
||||
}
|
||||
|
||||
Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(
|
||||
const StringPiece& alias, const StringPiece& local_package) const {
|
||||
Maybe<ExtractedPackage> XmlPullParser::TransformPackageAlias(const StringPiece& alias) const {
|
||||
if (alias.empty()) {
|
||||
return ExtractedPackage{local_package.to_string(), false /* private */};
|
||||
return ExtractedPackage{{}, false /*private*/};
|
||||
}
|
||||
|
||||
const auto end_iter = package_aliases_.rend();
|
||||
for (auto iter = package_aliases_.rbegin(); iter != end_iter; ++iter) {
|
||||
if (alias == iter->prefix) {
|
||||
if (iter->package.package.empty()) {
|
||||
return ExtractedPackage{local_package.to_string(), iter->package.private_namespace};
|
||||
return ExtractedPackage{{}, iter->package.private_namespace};
|
||||
}
|
||||
return iter->package;
|
||||
}
|
||||
|
||||
@@ -119,8 +119,7 @@ class XmlPullParser : public IPackageDeclStack {
|
||||
* If xmlns:app="http://schemas.android.com/apk/res-auto", then
|
||||
* 'package' will be set to 'defaultPackage'.
|
||||
*/
|
||||
Maybe<ExtractedPackage> TransformPackageAlias(
|
||||
const android::StringPiece& alias, const android::StringPiece& local_package) const override;
|
||||
Maybe<ExtractedPackage> TransformPackageAlias(const android::StringPiece& alias) const override;
|
||||
|
||||
//
|
||||
// Remaining methods are for retrieving information about attributes
|
||||
|
||||
@@ -62,19 +62,15 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(
|
||||
return {};
|
||||
}
|
||||
|
||||
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
|
||||
const StringPiece& local_package,
|
||||
Reference* in_ref) {
|
||||
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref) {
|
||||
if (in_ref->name) {
|
||||
if (Maybe<ExtractedPackage> transformed_package =
|
||||
decl_stack->TransformPackageAlias(in_ref->name.value().package,
|
||||
local_package)) {
|
||||
decl_stack->TransformPackageAlias(in_ref->name.value().package)) {
|
||||
ExtractedPackage& extracted_package = transformed_package.value();
|
||||
in_ref->name.value().package = std::move(extracted_package.package);
|
||||
|
||||
// If the reference was already private (with a * prefix) and the
|
||||
// namespace is public,
|
||||
// we keep the reference private.
|
||||
// namespace is public, we keep the reference private.
|
||||
in_ref->private_reference |= extracted_package.private_namespace;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ constexpr const char* kSchemaAapt = "http://schemas.android.com/aapt";
|
||||
// Result of extracting a package name from a namespace URI declaration.
|
||||
struct ExtractedPackage {
|
||||
// The name of the package. This can be the empty string, which means that the package
|
||||
// should be assumed to be the package being compiled.
|
||||
// should be assumed to be the same as the CallSite it was defined in.
|
||||
std::string package;
|
||||
|
||||
// True if the package's private namespace was declared. This means that private resources
|
||||
@@ -51,8 +51,8 @@ struct ExtractedPackage {
|
||||
// http://schemas.android.com/apk/res/<package> or
|
||||
// http://schemas.android.com/apk/prv/res/<package>
|
||||
//
|
||||
// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto,
|
||||
// returns an empty package name.
|
||||
// Special case: if namespaceUri is http://schemas.android.com/apk/res-auto, returns an empty
|
||||
// package name.
|
||||
Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace_uri);
|
||||
|
||||
// Returns an XML Android namespace for the given package of the form:
|
||||
@@ -63,21 +63,20 @@ Maybe<ExtractedPackage> ExtractPackageFromNamespace(const std::string& namespace
|
||||
std::string BuildPackageNamespace(const android::StringPiece& package,
|
||||
bool private_reference = false);
|
||||
|
||||
// Interface representing a stack of XML namespace declarations. When looking up the package
|
||||
// for a namespace prefix, the stack is checked from top to bottom.
|
||||
// Interface representing a stack of XML namespace declarations. When looking up the package for a
|
||||
// namespace prefix, the stack is checked from top to bottom.
|
||||
struct IPackageDeclStack {
|
||||
virtual ~IPackageDeclStack() = default;
|
||||
|
||||
// Returns an ExtractedPackage struct if the alias given corresponds with a package declaration.
|
||||
virtual Maybe<ExtractedPackage> TransformPackageAlias(
|
||||
const android::StringPiece& alias, const android::StringPiece& local_package) const = 0;
|
||||
const android::StringPiece& alias) const = 0;
|
||||
};
|
||||
|
||||
// Helper function for transforming the original Reference inRef to a fully qualified reference
|
||||
// via the IPackageDeclStack. This will also mark the Reference as private if the namespace of the
|
||||
// package declaration was private.
|
||||
void TransformReferenceFromNamespace(IPackageDeclStack* decl_stack,
|
||||
const android::StringPiece& local_package, Reference* in_ref);
|
||||
void ResolvePackage(const IPackageDeclStack* decl_stack, Reference* in_ref);
|
||||
|
||||
} // namespace xml
|
||||
} // namespace aapt
|
||||
|
||||