Merge "AAPT2: Support building, linking, and merging static libraries" into nyc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
8b030cce34
@@ -13,15 +13,12 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# This tool is prebuilt if we're doing an app-only build.
|
||||
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# ==========================================================
|
||||
# Setup some common variables for the different build
|
||||
# targets here.
|
||||
# ==========================================================
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
main := Main.cpp
|
||||
sources := \
|
||||
@@ -192,4 +189,6 @@ LOCAL_C_INCLUDES += $(protoIncludes)
|
||||
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
|
||||
ifeq ($(ONE_SHOT_MAKEFILE),)
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
endif
|
||||
|
||||
@@ -422,10 +422,6 @@ static bool compileFile(IAaptContext* context, const CompileOptions& options,
|
||||
}
|
||||
|
||||
class CompileContext : public IAaptContext {
|
||||
private:
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
bool mVerbose = false;
|
||||
|
||||
public:
|
||||
void setVerbose(bool val) {
|
||||
mVerbose = val;
|
||||
@@ -444,18 +440,24 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StringPiece16 getCompilationPackage() override {
|
||||
return {};
|
||||
const std::u16string& getCompilationPackage() override {
|
||||
static std::u16string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
uint8_t getPackageId() override {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
ISymbolTable* getExternalSymbols() override {
|
||||
SymbolTable* getExternalSymbols() override {
|
||||
abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
bool mVerbose = false;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.app">
|
||||
<application
|
||||
android:name=".ActivityMain">
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -1,61 +0,0 @@
|
||||
##
|
||||
# Environment dependent variables
|
||||
##
|
||||
|
||||
AAPT := aapt2
|
||||
ZIPALIGN := zipalign -f 4
|
||||
FRAMEWORK := ../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
|
||||
|
||||
##
|
||||
# Project depenedent variables
|
||||
##
|
||||
|
||||
LOCAL_PACKAGE := com.android.app
|
||||
LOCAL_RESOURCE_DIR := res
|
||||
LOCAL_LIBS := lib/out/package.apk
|
||||
LOCAL_OUT := out
|
||||
LOCAL_GEN := out/gen
|
||||
LOCAL_PROGUARD := out/proguard.rule
|
||||
|
||||
##
|
||||
# AAPT2 custom rules.
|
||||
##
|
||||
|
||||
PRIVATE_R_FILE := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
|
||||
$(info PRIVATE_R_FILE = $(PRIVATE_R_FILE))
|
||||
|
||||
# Eg: framework.apk, etc.
|
||||
PRIVATE_INCLUDES := $(FRAMEWORK)
|
||||
$(info PRIVATE_INCLUDES = $(PRIVATE_INCLUDES))
|
||||
|
||||
# Eg: res/drawable/icon.png, res/values/styles.xml
|
||||
PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
|
||||
$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
|
||||
|
||||
PRIVATE_RESOURCE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
|
||||
PRIVATE_RESOURCE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(PRIVATE_RESOURCE_OBJECTS:.xml=.arsc.flat))
|
||||
$(info PRIVATE_RESOURCE_OBJECTS = $(PRIVATE_RESOURCE_OBJECTS))
|
||||
|
||||
PRIVATE_FILE_OBJECTS := $(subst /,_,$(patsubst $(LOCAL_RESOURCE_DIR)/%,%,$(filter-out $(LOCAL_RESOURCE_DIR)/values%,$(PRIVATE_RESOURCES))))
|
||||
PRIVATE_FILE_OBJECTS := $(addprefix $(LOCAL_OUT)/,$(addsuffix .flat,$(PRIVATE_FILE_OBJECTS)))
|
||||
$(info PRIVATE_FILE_OBJECTS = $(PRIVATE_FILE_OBJECTS))
|
||||
|
||||
.SECONDEXPANSION:
|
||||
|
||||
$(LOCAL_OUT)/%.arsc.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%).xml
|
||||
$(AAPT) compile -o $(LOCAL_OUT) $<
|
||||
|
||||
$(LOCAL_OUT)/%.flat: $(LOCAL_RESOURCE_DIR)/$$(subst _,/,%)
|
||||
$(AAPT) compile -o $(LOCAL_OUT) $<
|
||||
|
||||
$(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: AndroidManifest.xml
|
||||
$(PRIVATE_R_FILE) $(LOCAL_PROGUARD) $(LOCAL_OUT)/package.apk: $(PRIVATE_FILE_OBJECTS) $(PRIVATE_RESOURCE_OBJECTS)
|
||||
$(AAPT) link -o $(LOCAL_OUT)/package.apk --manifest AndroidManifest.xml --java $(LOCAL_GEN) --proguard $(LOCAL_PROGUARD) -I $(PRIVATE_INCLUDES) $(filter-out AndroidManifest.xml,$^) -v
|
||||
|
||||
# Create the out directory if needed.
|
||||
dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
|
||||
|
||||
.PHONY: all
|
||||
all: $(LOCAL_OUT)/package.apk $(LOCAL_PROGUARD) $(PRIVATE_R_FILE)
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="android.appcompat">
|
||||
|
||||
<uses-feature android:name="bloooop" />
|
||||
</manifest>
|
||||
@@ -1,81 +0,0 @@
|
||||
##
|
||||
# Environment dependent variables
|
||||
##
|
||||
|
||||
AAPT := aapt2
|
||||
ZIPALIGN := zipalign -f 4
|
||||
FRAMEWORK := ../../../../../../out/target/common/obj/APPS/framework-res_intermediates/package-export.apk
|
||||
|
||||
##
|
||||
# Project depenedent variables
|
||||
##
|
||||
|
||||
LOCAL_PACKAGE := android.appcompat
|
||||
LOCAL_RESOURCE_DIR := res
|
||||
LOCAL_OUT := out
|
||||
LOCAL_GEN := out/gen
|
||||
|
||||
##
|
||||
# AAPT2 custom rules.
|
||||
##
|
||||
|
||||
PRIVATE_APK_UNALIGNED := $(LOCAL_OUT)/package-unaligned.apk
|
||||
PRIVATE_APK_ALIGNED := $(LOCAL_OUT)/package.apk
|
||||
|
||||
# Eg: framework.apk, etc.
|
||||
PRIVATE_LIBS := $(FRAMEWORK)
|
||||
$(info PRIVATE_LIBS = $(PRIVATE_LIBS))
|
||||
|
||||
# Eg: gen/com/android/app/R.java
|
||||
PRIVATE_R_JAVA := $(LOCAL_GEN)/$(subst .,/,$(LOCAL_PACKAGE))/R.java
|
||||
$(info PRIVATE_R_JAVA = $(PRIVATE_R_JAVA))
|
||||
|
||||
# Eg: res/drawable/icon.png, res/values/styles.xml
|
||||
PRIVATE_RESOURCES := $(shell find $(LOCAL_RESOURCE_DIR) -mindepth 1 -maxdepth 2 -type f)
|
||||
$(info PRIVATE_RESOURCES = $(PRIVATE_RESOURCES))
|
||||
|
||||
# Eg: drawable, values, layouts
|
||||
PRIVATE_RESOURCE_TYPES := \
|
||||
$(patsubst $(LOCAL_RESOURCE_DIR)/%/,%,$(sort $(dir $(PRIVATE_RESOURCES))))
|
||||
$(info PRIVATE_RESOURCE_TYPES = $(PRIVATE_RESOURCE_TYPES))
|
||||
|
||||
# Eg: out/values-v4.apk, out/drawable-xhdpi.apk
|
||||
PRIVATE_INTERMEDIATE_TABLES := $(patsubst %,$(LOCAL_OUT)/%.apk,$(PRIVATE_RESOURCE_TYPES))
|
||||
$(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES))
|
||||
|
||||
# Generates rules for collect phase.
|
||||
# $1: Resource type (values-v4)
|
||||
# returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml
|
||||
define make-collect-rule
|
||||
$(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES))
|
||||
$(AAPT) compile -o $$@ $$^
|
||||
endef
|
||||
|
||||
# Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml
|
||||
$(foreach d,$(PRIVATE_RESOURCE_TYPES),$(eval $(call make-collect-rule,$d)))
|
||||
|
||||
# Link: out/package-unaligned.apk <- out/values-v4.apk out/drawable-v4.apk
|
||||
$(PRIVATE_APK_UNALIGNED): $(PRIVATE_INTERMEDIATE_TABLES) $(PRIVATE_LIBS) AndroidManifest.xml
|
||||
$(AAPT) link --manifest AndroidManifest.xml $(addprefix -I ,$(PRIVATE_LIBS)) --java $(LOCAL_GEN) -o $@ $(PRIVATE_INTERMEDIATE_TABLES) --static-lib
|
||||
|
||||
# R.java: gen/com/android/app/R.java <- out/resources.arsc
|
||||
# No action since R.java is generated when out/resources.arsc is.
|
||||
$(PRIVATE_R_JAVA): $(PRIVATE_APK_UNALIGNED)
|
||||
|
||||
# Assemble: zip out/resources.arsc AndroidManifest.xml and res/**/*
|
||||
$(PRIVATE_APK_ALIGNED): $(PRIVATE_APK_UNALIGNED)
|
||||
$(ZIPALIGN) $< $@
|
||||
|
||||
# Create the out directory if needed.
|
||||
dummy := $(shell test -d $(LOCAL_OUT) || mkdir -p $(LOCAL_OUT))
|
||||
|
||||
.PHONY: java
|
||||
java: $(PRIVATE_R_JAVA)
|
||||
|
||||
.PHONY: assemble
|
||||
assemble: $(PRIVATE_APK_ALIGNED)
|
||||
|
||||
.PHONY: all
|
||||
all: assemble java
|
||||
|
||||
.DEFAULT_GOAL := all
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
@@ -1 +0,0 @@
|
||||
Oh howdy there
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Platform.AppCompat" parent="@android:style/Theme">
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<bool name="allow">true</bool>
|
||||
</resources>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector />
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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">
|
||||
</LinearLayout>
|
||||
@@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="App" parent="android:Theme.Material">
|
||||
<item name="android:colorAccent">@color/accent</item>
|
||||
<item name="android:text">Hey</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary">#f44336</color>
|
||||
<color name="primary_dark">#b71c1c</color>
|
||||
<color name="accent">#fdd835</color>
|
||||
</resources>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
|
||||
<public name="hooha" type="string" id="0x7f020001"/>
|
||||
<string name="wow">@android:string/ok</string>
|
||||
<public name="layout_width" type="attr" />
|
||||
<attr name="layout_width" format="boolean" />
|
||||
<attr name="flags">
|
||||
<flag name="complex" value="1" />
|
||||
<flag name="pub" value="2" />
|
||||
<flag name="weak" value="4" />
|
||||
</attr>
|
||||
</resources>
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,6 +17,7 @@
|
||||
#include "Debug.h"
|
||||
#include "Diagnostics.h"
|
||||
#include "Flags.h"
|
||||
#include "io/ZipArchive.h"
|
||||
#include "process/IResourceTableConsumer.h"
|
||||
#include "proto/ProtoSerialize.h"
|
||||
#include "util/Files.h"
|
||||
@@ -56,6 +57,35 @@ void dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source,
|
||||
|
||||
void tryDumpFile(IAaptContext* context, const std::string& filePath) {
|
||||
std::string err;
|
||||
std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err);
|
||||
if (zip) {
|
||||
io::IFile* file = zip->findFile("resources.arsc.flat");
|
||||
if (file) {
|
||||
std::unique_ptr<io::IData> data = file->openAsData();
|
||||
if (!data) {
|
||||
context->getDiagnostics()->error(DiagMessage(filePath)
|
||||
<< "failed to open resources.arsc.flat");
|
||||
return;
|
||||
}
|
||||
|
||||
pb::ResourceTable pbTable;
|
||||
if (!pbTable.ParseFromArray(data->data(), data->size())) {
|
||||
context->getDiagnostics()->error(DiagMessage(filePath)
|
||||
<< "invalid resources.arsc.flat");
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = deserializeTableFromPb(
|
||||
pbTable, Source(filePath), context->getDiagnostics());
|
||||
if (table) {
|
||||
DebugPrintTableOptions debugPrintTableOptions;
|
||||
debugPrintTableOptions.showSources = true;
|
||||
Debug::printTable(table.get(), debugPrintTableOptions);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Maybe<android::FileMap> file = file::mmapPath(filePath, &err);
|
||||
if (!file) {
|
||||
context->getDiagnostics()->error(DiagMessage(filePath) << err);
|
||||
@@ -90,15 +120,16 @@ public:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
StringPiece16 getCompilationPackage() override {
|
||||
return {};
|
||||
const std::u16string& getCompilationPackage() override {
|
||||
static std::u16string empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
uint8_t getPackageId() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ISymbolTable* getExternalSymbols() override {
|
||||
SymbolTable* getExternalSymbols() override {
|
||||
abort();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -144,9 +144,9 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
}
|
||||
|
||||
static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) {
|
||||
if (a->compiledAttribute) {
|
||||
if (b->compiledAttribute) {
|
||||
return a->compiledAttribute.value().id < b->compiledAttribute.value().id;
|
||||
if (a->compiledAttribute && a->compiledAttribute.value().id) {
|
||||
if (b->compiledAttribute && b->compiledAttribute.value().id) {
|
||||
return a->compiledAttribute.value().id.value() < b->compiledAttribute.value().id.value();
|
||||
}
|
||||
return true;
|
||||
} else if (!b->compiledAttribute) {
|
||||
@@ -167,8 +167,8 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
|
||||
// Filter the attributes.
|
||||
for (xml::Attribute& attr : node->attributes) {
|
||||
if (mOptions.maxSdkLevel && attr.compiledAttribute) {
|
||||
size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
|
||||
if (mOptions.maxSdkLevel && attr.compiledAttribute && attr.compiledAttribute.value().id) {
|
||||
size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id.value());
|
||||
if (sdkLevel > mOptions.maxSdkLevel.value()) {
|
||||
continue;
|
||||
}
|
||||
@@ -191,8 +191,8 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
uint16_t attributeIndex = 1;
|
||||
for (const xml::Attribute* xmlAttr : mFilteredAttrs) {
|
||||
// Assign the indices for specific attributes.
|
||||
if (xmlAttr->compiledAttribute &&
|
||||
xmlAttr->compiledAttribute.value().id == kIdAttr) {
|
||||
if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id &&
|
||||
xmlAttr->compiledAttribute.value().id.value() == kIdAttr) {
|
||||
flatElem->idIndex = util::hostToDevice16(attributeIndex);
|
||||
} else if (xmlAttr->namespaceUri.empty()) {
|
||||
if (xmlAttr->name == u"class") {
|
||||
@@ -208,7 +208,7 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
|
||||
flatAttr->rawValue.index = util::hostToDevice32(-1);
|
||||
|
||||
if (!xmlAttr->compiledAttribute) {
|
||||
if (!xmlAttr->compiledAttribute || !xmlAttr->compiledAttribute.value().id) {
|
||||
// The attribute has no associated ResourceID, so the string order doesn't matter.
|
||||
addString(xmlAttr->name, kLowPriority, &flatAttr->name);
|
||||
} else {
|
||||
@@ -221,17 +221,17 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
// Lookup the StringPool for this package and make the reference there.
|
||||
const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value();
|
||||
|
||||
StringPool::Ref nameRef = mPackagePools[aaptAttr.id.packageId()].makeRef(
|
||||
xmlAttr->name, StringPool::Context{ aaptAttr.id.id });
|
||||
StringPool::Ref nameRef = mPackagePools[aaptAttr.id.value().packageId()].makeRef(
|
||||
xmlAttr->name, StringPool::Context{ aaptAttr.id.value().id });
|
||||
|
||||
// Add it to the list of strings to flatten.
|
||||
addString(nameRef, &flatAttr->name);
|
||||
}
|
||||
|
||||
if (mOptions.keepRawValues) {
|
||||
// Keep raw values (this is for static libraries).
|
||||
// TODO(with a smarter inflater for binary XML, we can do without this).
|
||||
addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
|
||||
}
|
||||
if (mOptions.keepRawValues || !xmlAttr->compiledValue) {
|
||||
// Keep raw values if the value is not compiled or
|
||||
// if we're building a static library (need symbols).
|
||||
addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
|
||||
}
|
||||
|
||||
if (xmlAttr->compiledValue) {
|
||||
@@ -240,7 +240,6 @@ struct XmlFlattenerVisitor : public xml::Visitor {
|
||||
} else {
|
||||
// Flatten as a regular string type.
|
||||
flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING;
|
||||
addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue);
|
||||
addString(xmlAttr->value, kLowPriority,
|
||||
(ResStringPool_ref*) &flatAttr->typedValue.data);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
mContext = test::ContextBuilder()
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addSymbol(u"@android:attr/id", ResourceId(0x010100d0),
|
||||
test::AttributeBuilder().build())
|
||||
.addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000))
|
||||
|
||||
2
tools/aapt2/integration-tests/Android.mk
Normal file
2
tools/aapt2/integration-tests/Android.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(call all-makefiles-under,$(LOCAL_PATH))
|
||||
27
tools/aapt2/integration-tests/AppOne/Android.mk
Normal file
27
tools/aapt2/integration-tests/AppOne/Android.mk
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (C) 2016 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_PACKAGE_NAME := AaptTestAppOne
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_STATIC_ANDROID_LIBRARIES := \
|
||||
AaptTestStaticLibOne \
|
||||
AaptTestStaticLibTwo
|
||||
include $(BUILD_PACKAGE)
|
||||
17
tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
Normal file
17
tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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 package="com.android.aapt.app.one" />
|
||||
BIN
tools/aapt2/integration-tests/AppOne/core
Normal file
BIN
tools/aapt2/integration-tests/AppOne/core
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
17
tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
Normal file
17
tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<vector />
|
||||
|
Before Width: | Height: | Size: 124 B After Width: | Height: | Size: 124 B |
22
tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
Normal file
22
tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<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">
|
||||
</LinearLayout>
|
||||
@@ -1,4 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
|
||||
android:id="@+id/view"
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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>
|
||||
<style name="App" parent="android:Theme.Material">
|
||||
<item name="android:colorAccent">@color/accent</item>
|
||||
<item name="android:text">Hey</item>
|
||||
</style>
|
||||
</resources>
|
||||
21
tools/aapt2/integration-tests/AppOne/res/values/colors.xml
Normal file
21
tools/aapt2/integration-tests/AppOne/res/values/colors.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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>
|
||||
<color name="primary">#f44336</color>
|
||||
<color name="primary_dark">#b71c1c</color>
|
||||
<color name="accent">#fdd835</color>
|
||||
</resources>
|
||||
@@ -1,4 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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 xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
|
||||
<style name="App">
|
||||
<item name="android:background">@color/primary</item>
|
||||
32
tools/aapt2/integration-tests/AppOne/res/values/test.xml
Normal file
32
tools/aapt2/integration-tests/AppOne/res/values/test.xml
Normal file
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<!-- Reference the two static libraries -->
|
||||
<string name="AppFooBar">@string/FooBar</string>
|
||||
<string name="AppFoo">@string/Foo</string>
|
||||
|
||||
<string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
|
||||
<public name="hooha" type="string" id="0x7f020001"/>
|
||||
<string name="wow">@android:string/ok</string>
|
||||
<public name="layout_width" type="attr" />
|
||||
<attr name="layout_width" format="boolean" />
|
||||
<attr name="flags">
|
||||
<flag name="complex" value="1" />
|
||||
<flag name="pub" value="2" />
|
||||
<flag name="weak" value="4" />
|
||||
</attr>
|
||||
</resources>
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.app.one;
|
||||
|
||||
public class AppOne {
|
||||
// IDs from StaticLibOne
|
||||
public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
|
||||
public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
|
||||
|
||||
// IDs from StaticLibTwo
|
||||
public static int FooBarId = com.android.aapt.staticlib.two.R.string.FooBar;
|
||||
}
|
||||
|
||||
25
tools/aapt2/integration-tests/StaticLibOne/Android.mk
Normal file
25
tools/aapt2/integration-tests/StaticLibOne/Android.mk
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Copyright (C) 2016 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_MODULE := AaptTestStaticLibOne
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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 package="com.android.aapt.staticlib.one" />
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<View xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:text="@string/Foo" />
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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>
|
||||
<attr name="StaticLibOne_attr" format="string" />
|
||||
|
||||
<string name="Foo">Foo</string>
|
||||
<string name="Foo" product="tablet">Bar</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.staticlib.one;
|
||||
|
||||
public class StaticLibOne {
|
||||
// IDs from StaticLibOne
|
||||
public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
|
||||
public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
|
||||
}
|
||||
27
tools/aapt2/integration-tests/StaticLibTwo/Android.mk
Normal file
27
tools/aapt2/integration-tests/StaticLibTwo/Android.mk
Normal file
@@ -0,0 +1,27 @@
|
||||
#
|
||||
# Copyright (C) 2016 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_MODULE := AaptTestStaticLibTwo
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under,src)
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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 package="com.android.aapt.staticlib.two" />
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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.
|
||||
-->
|
||||
|
||||
<View xmlns:custom="http://schemas.android.com/apk/res-auto"
|
||||
custom:StaticLibOne_attr="@string/FooBar" />
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 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="FooBar">@string/Foo</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.staticlib.two;
|
||||
|
||||
public class StaticLibTwo {
|
||||
// IDs from StaticLibOne
|
||||
public static int FooId = com.android.aapt.staticlib.one.R.string.Foo;
|
||||
public static int LayoutId = com.android.aapt.staticlib.one.R.layout.layout;
|
||||
|
||||
// IDs from StaticLibTwo
|
||||
public static int FooBarId = com.android.aapt.staticlib.two.R.string.FooBar;
|
||||
}
|
||||
@@ -92,9 +92,8 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece&
|
||||
return {};
|
||||
}
|
||||
|
||||
ZipString suffix(".flat");
|
||||
void* cookie = nullptr;
|
||||
result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix);
|
||||
result = StartIteration(collection->mHandle, &cookie, nullptr, nullptr);
|
||||
if (result != 0) {
|
||||
if (outError) *outError = ErrorCodeString(result);
|
||||
return {};
|
||||
|
||||
@@ -223,8 +223,10 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceId id(package->id.value(), type->id.value(), entry->id.value());
|
||||
assert(id.isValid());
|
||||
ResourceId id;
|
||||
if (package->id && type->id && entry->id) {
|
||||
id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
|
||||
}
|
||||
|
||||
std::u16string unmangledPackage;
|
||||
std::u16string unmangledName = entry->name;
|
||||
|
||||
@@ -62,7 +62,9 @@ struct LinkOptions {
|
||||
std::set<std::u16string> extraJavaPackages;
|
||||
Maybe<std::string> generateProguardRulesPath;
|
||||
bool noAutoVersion = false;
|
||||
bool noVersionVectors = false;
|
||||
bool staticLib = false;
|
||||
bool noStaticLibPackages = false;
|
||||
bool generateNonFinalIds = false;
|
||||
bool outputToDirectory = false;
|
||||
bool autoAddOverlay = false;
|
||||
@@ -74,37 +76,58 @@ struct LinkOptions {
|
||||
TableSplitterOptions tableSplitterOptions;
|
||||
};
|
||||
|
||||
struct LinkContext : public IAaptContext {
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
std::unique_ptr<NameMangler> mNameMangler;
|
||||
std::u16string mCompilationPackage;
|
||||
uint8_t mPackageId;
|
||||
std::unique_ptr<ISymbolTable> mSymbols;
|
||||
bool mVerbose = false;
|
||||
class LinkContext : public IAaptContext {
|
||||
public:
|
||||
LinkContext() : mNameMangler({}) {
|
||||
}
|
||||
|
||||
IDiagnostics* getDiagnostics() override {
|
||||
return &mDiagnostics;
|
||||
}
|
||||
|
||||
NameMangler* getNameMangler() override {
|
||||
return mNameMangler.get();
|
||||
return &mNameMangler;
|
||||
}
|
||||
|
||||
StringPiece16 getCompilationPackage() override {
|
||||
void setNameManglerPolicy(const NameManglerPolicy& policy) {
|
||||
mNameMangler = NameMangler(policy);
|
||||
}
|
||||
|
||||
const std::u16string& getCompilationPackage() override {
|
||||
return mCompilationPackage;
|
||||
}
|
||||
|
||||
void setCompilationPackage(const StringPiece16& packageName) {
|
||||
mCompilationPackage = packageName.toString();
|
||||
}
|
||||
|
||||
uint8_t getPackageId() override {
|
||||
return mPackageId;
|
||||
}
|
||||
|
||||
ISymbolTable* getExternalSymbols() override {
|
||||
return mSymbols.get();
|
||||
void setPackageId(uint8_t id) {
|
||||
mPackageId = id;
|
||||
}
|
||||
|
||||
SymbolTable* getExternalSymbols() override {
|
||||
return &mSymbols;
|
||||
}
|
||||
|
||||
bool verbose() override {
|
||||
return mVerbose;
|
||||
}
|
||||
|
||||
void setVerbose(bool val) {
|
||||
mVerbose = val;
|
||||
}
|
||||
|
||||
private:
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
NameMangler mNameMangler;
|
||||
std::u16string mCompilationPackage;
|
||||
uint8_t mPackageId = 0x0;
|
||||
SymbolTable mSymbols;
|
||||
bool mVerbose = false;
|
||||
};
|
||||
|
||||
static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
|
||||
@@ -117,11 +140,19 @@ static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
|
||||
return false;
|
||||
}
|
||||
|
||||
CompiledFileInputStream inputStream(data->data(), data->size());
|
||||
if (!inputStream.CompiledFile()) {
|
||||
context->getDiagnostics()->error(DiagMessage(file->getSource())
|
||||
<< "invalid compiled file header");
|
||||
return false;
|
||||
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data());
|
||||
size_t bufferSize = data->size();
|
||||
|
||||
// If the file ends with .flat, we must strip off the CompiledFileHeader from it.
|
||||
if (util::stringEndsWith<char>(file->getSource().path, ".flat")) {
|
||||
CompiledFileInputStream inputStream(data->data(), data->size());
|
||||
if (!inputStream.CompiledFile()) {
|
||||
context->getDiagnostics()->error(DiagMessage(file->getSource())
|
||||
<< "invalid compiled file header");
|
||||
return false;
|
||||
}
|
||||
buffer = reinterpret_cast<const uint8_t*>(inputStream.data());
|
||||
bufferSize = inputStream.size();
|
||||
}
|
||||
|
||||
if (context->verbose()) {
|
||||
@@ -129,8 +160,7 @@ static bool copyFileToArchive(io::IFile* file, const std::string& outPath,
|
||||
}
|
||||
|
||||
if (writer->startEntry(outPath, compressionFlags)) {
|
||||
if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()),
|
||||
inputStream.size())) {
|
||||
if (writer->writeEntry(buffer, bufferSize)) {
|
||||
if (writer->finishEntry()) {
|
||||
return true;
|
||||
}
|
||||
@@ -156,7 +186,7 @@ static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe<
|
||||
DiagMessage msg;
|
||||
msg << "writing " << path << " to archive";
|
||||
if (maxSdkLevel) {
|
||||
msg << " maxSdkLevel=" << maxSdkLevel.value();
|
||||
msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues;
|
||||
}
|
||||
context->getDiagnostics()->note(msg);
|
||||
}
|
||||
@@ -346,7 +376,7 @@ std::unique_ptr<xml::XmlResource> ResourceFileFlattener::linkAndVersionXmlFile(
|
||||
}
|
||||
|
||||
ResourceFile versionedFileDesc = xmlRes->file;
|
||||
versionedFileDesc.config.sdkVersion = sdkLevel;
|
||||
versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel;
|
||||
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source)
|
||||
@@ -474,39 +504,61 @@ bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiv
|
||||
class LinkCommand {
|
||||
public:
|
||||
LinkCommand(LinkContext* context, const LinkOptions& options) :
|
||||
mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) {
|
||||
std::unique_ptr<io::FileCollection> fileCollection =
|
||||
util::make_unique<io::FileCollection>();
|
||||
|
||||
// Get a pointer to the FileCollection for convenience, but it will be owned by the vector.
|
||||
mFileCollection = fileCollection.get();
|
||||
|
||||
// Move it to the collection.
|
||||
mCollections.push_back(std::move(fileCollection));
|
||||
mOptions(options), mContext(context), mFinalTable(),
|
||||
mFileCollection(util::make_unique<io::FileCollection>()) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SymbolTable that loads symbols from the various APKs and caches the
|
||||
* results for faster lookup.
|
||||
*/
|
||||
std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() {
|
||||
AssetManagerSymbolTableBuilder builder;
|
||||
bool loadSymbolsFromIncludePaths() {
|
||||
std::unique_ptr<AssetManagerSymbolSource> assetSource =
|
||||
util::make_unique<AssetManagerSymbolSource>();
|
||||
for (const std::string& path : mOptions.includePaths) {
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path");
|
||||
}
|
||||
|
||||
std::unique_ptr<android::AssetManager> assetManager =
|
||||
util::make_unique<android::AssetManager>();
|
||||
int32_t cookie = 0;
|
||||
if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) {
|
||||
// First try to load the file as a static lib.
|
||||
std::string errorStr;
|
||||
std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr);
|
||||
if (staticInclude) {
|
||||
if (!mOptions.staticLib) {
|
||||
// Can't include static libraries when not building a static library.
|
||||
mContext->getDiagnostics()->error(
|
||||
DiagMessage(path) << "can't include static library when building app");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are using --no-static-lib-packages, we need to rename the package of this
|
||||
// table to our compilation package.
|
||||
if (mOptions.noStaticLibPackages) {
|
||||
if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) {
|
||||
pkg->name = mContext->getCompilationPackage();
|
||||
}
|
||||
}
|
||||
|
||||
mContext->getExternalSymbols()->appendSource(
|
||||
util::make_unique<ResourceTableSymbolSource>(staticInclude.get()));
|
||||
|
||||
mStaticTableIncludes.push_back(std::move(staticInclude));
|
||||
|
||||
} else if (!errorStr.empty()) {
|
||||
// We had an error with reading, so fail.
|
||||
mContext->getDiagnostics()->error(DiagMessage(path) << errorStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!assetSource->addAssetPath(path)) {
|
||||
mContext->getDiagnostics()->error(
|
||||
DiagMessage(path) << "failed to load include path");
|
||||
return {};
|
||||
return false;
|
||||
}
|
||||
builder.add(std::move(assetManager));
|
||||
}
|
||||
return builder.build();
|
||||
|
||||
mContext->getExternalSymbols()->appendSource(std::move(assetSource));
|
||||
return true;
|
||||
}
|
||||
|
||||
Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) {
|
||||
@@ -571,6 +623,35 @@ public:
|
||||
return !error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if no IDs have been set, false otherwise.
|
||||
*/
|
||||
bool verifyNoIdsSet() {
|
||||
for (const auto& package : mFinalTable.packages) {
|
||||
for (const auto& type : package->types) {
|
||||
if (type->id) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type
|
||||
<< " has ID " << std::hex
|
||||
<< (int) type->id.value()
|
||||
<< std::dec << " assigned");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& entry : type->entries) {
|
||||
if (entry->id) {
|
||||
ResourceNameRef resName(package->name, type->type, entry->name);
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName
|
||||
<< " has ID " << std::hex
|
||||
<< (int) entry->id.value()
|
||||
<< std::dec << " assigned");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<IArchiveWriter> makeArchiveWriter() {
|
||||
if (mOptions.outputToDirectory) {
|
||||
return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath);
|
||||
@@ -599,6 +680,32 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) {
|
||||
// Create the file/zip entry.
|
||||
if (!writer->startEntry("resources.arsc.flat", 0)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed to open");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table);
|
||||
|
||||
// Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream
|
||||
// interface.
|
||||
{
|
||||
google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer);
|
||||
|
||||
if (!pbTable->SerializeToZeroCopyStream(&adaptor)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed to write");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!writer->finishEntry()) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate,
|
||||
const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) {
|
||||
@@ -674,18 +781,91 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mergeStaticLibrary(const std::string& input) {
|
||||
// TODO(adamlesinski): Load resources from a static library APK and merge the table into
|
||||
// TableMerger.
|
||||
mContext->getDiagnostics()->warn(DiagMessage()
|
||||
<< "linking static libraries not supported yet: "
|
||||
<< input);
|
||||
std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input,
|
||||
std::string* outError) {
|
||||
std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
|
||||
input, outError);
|
||||
if (!collection) {
|
||||
return {};
|
||||
}
|
||||
return loadTablePbFromCollection(collection.get());
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) {
|
||||
io::IFile* file = collection->findFile("resources.arsc.flat");
|
||||
if (!file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->openAsData();
|
||||
return loadTableFromPb(file->getSource(), data->data(), data->size(),
|
||||
mContext->getDiagnostics());
|
||||
}
|
||||
|
||||
bool mergeStaticLibrary(const std::string& input, bool override) {
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input);
|
||||
}
|
||||
|
||||
std::string errorStr;
|
||||
std::unique_ptr<io::ZipFileCollection> collection =
|
||||
io::ZipFileCollection::create(input, &errorStr);
|
||||
if (!collection) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get());
|
||||
if (!table) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library");
|
||||
return false;
|
||||
}
|
||||
|
||||
ResourceTablePackage* pkg = table->findPackageById(0x7f);
|
||||
if (!pkg) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(input)
|
||||
<< "static library has no package");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result;
|
||||
if (mOptions.noStaticLibPackages) {
|
||||
// Merge all resources as if they were in the compilation package. This is the old
|
||||
// behaviour of aapt.
|
||||
|
||||
// Add the package to the set of --extra-packages so we emit an R.java for each
|
||||
// library package.
|
||||
if (!pkg->name.empty()) {
|
||||
mOptions.extraJavaPackages.insert(pkg->name);
|
||||
}
|
||||
|
||||
pkg->name = u"";
|
||||
if (override) {
|
||||
result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get());
|
||||
} else {
|
||||
result = mTableMerger->merge(Source(input), table.get(), collection.get());
|
||||
}
|
||||
|
||||
} else {
|
||||
// This is the proper way to merge libraries, where the package name is preserved
|
||||
// and resource names are mangled.
|
||||
result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(),
|
||||
collection.get());
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure to move the collection into the set of IFileCollections.
|
||||
mCollections.push_back(std::move(collection));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mergeResourceTable(io::IFile* file, bool override) {
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource());
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "merging resource table "
|
||||
<< file->getSource());
|
||||
}
|
||||
|
||||
std::unique_ptr<io::IData> data = file->openAsData();
|
||||
@@ -711,13 +891,14 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) {
|
||||
bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) {
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource());
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file "
|
||||
<< file->getSource());
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (overlay) {
|
||||
if (override) {
|
||||
result = mTableMerger->mergeFileOverlay(*fileDesc, file);
|
||||
} else {
|
||||
result = mTableMerger->mergeFile(*fileDesc, file);
|
||||
@@ -730,7 +911,7 @@ public:
|
||||
// Add the exports of this file to the table.
|
||||
for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) {
|
||||
if (exportedSymbol.name.package.empty()) {
|
||||
exportedSymbol.name.package = mContext->getCompilationPackage().toString();
|
||||
exportedSymbol.name.package = mContext->getCompilationPackage();
|
||||
}
|
||||
|
||||
ResourceNameRef resName = exportedSymbol.name;
|
||||
@@ -743,11 +924,9 @@ public:
|
||||
|
||||
std::unique_ptr<Id> id = util::make_unique<Id>();
|
||||
id->setSource(fileDesc->source.withLine(exportedSymbol.line));
|
||||
bool result = mFinalTable.addResourceAllowMangled(resName,
|
||||
ConfigDescription::defaultConfig(),
|
||||
std::string(),
|
||||
std::move(id),
|
||||
mContext->getDiagnostics());
|
||||
bool result = mFinalTable.addResourceAllowMangled(
|
||||
resName, ConfigDescription::defaultConfig(), std::string(), std::move(id),
|
||||
mContext->getDiagnostics());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@@ -756,12 +935,21 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an io::IFileCollection from the ZIP archive and processes the files within.
|
||||
* Takes a path to load as a ZIP file and merges the files within into the master ResourceTable.
|
||||
* If override is true, conflicting resources are allowed to override each other, in order of
|
||||
* last seen.
|
||||
*
|
||||
* An io::IFileCollection is created from the ZIP file and added to the set of
|
||||
* io::IFileCollections that are open.
|
||||
*/
|
||||
bool mergeArchive(const std::string& input, bool override) {
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input);
|
||||
}
|
||||
|
||||
std::string errorStr;
|
||||
std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create(
|
||||
input, &errorStr);
|
||||
std::unique_ptr<io::ZipFileCollection> collection =
|
||||
io::ZipFileCollection::create(input, &errorStr);
|
||||
if (!collection) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(input) << errorStr);
|
||||
return false;
|
||||
@@ -769,7 +957,7 @@ public:
|
||||
|
||||
bool error = false;
|
||||
for (auto iter = collection->iterator(); iter->hasNext(); ) {
|
||||
if (!processFile(iter->next(), override)) {
|
||||
if (!mergeFile(iter->next(), override)) {
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
@@ -779,22 +967,45 @@ public:
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool processFile(const std::string& path, bool override) {
|
||||
/**
|
||||
* Takes a path to load and merge into the master ResourceTable. If override is true,
|
||||
* conflicting resources are allowed to override each other, in order of last seen.
|
||||
*
|
||||
* If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive
|
||||
* and the files within are merged individually.
|
||||
*
|
||||
* Otherwise the files is processed on its own.
|
||||
*/
|
||||
bool mergePath(const std::string& path, bool override) {
|
||||
if (util::stringEndsWith<char>(path, ".flata") ||
|
||||
util::stringEndsWith<char>(path, ".jar") ||
|
||||
util::stringEndsWith<char>(path, ".jack") ||
|
||||
util::stringEndsWith<char>(path, ".zip")) {
|
||||
return mergeArchive(path, override);
|
||||
} else if (util::stringEndsWith<char>(path, ".apk")) {
|
||||
return mergeStaticLibrary(path, override);
|
||||
}
|
||||
|
||||
io::IFile* file = mFileCollection->insertFile(path);
|
||||
return processFile(file, override);
|
||||
return mergeFile(file, override);
|
||||
}
|
||||
|
||||
bool processFile(io::IFile* file, bool override) {
|
||||
/**
|
||||
* Takes a file to load and merge into the master ResourceTable. If override is true,
|
||||
* conflicting resources are allowed to override each other, in order of last seen.
|
||||
*
|
||||
* If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the
|
||||
* master ResourceTable. If the file ends with .flat, then it is treated like a compiled file
|
||||
* and the header data is read and merged into the final ResourceTable.
|
||||
*
|
||||
* All other file types are ignored. This is because these files could be coming from a zip,
|
||||
* where we could have other files like classes.dex.
|
||||
*/
|
||||
bool mergeFile(io::IFile* file, bool override) {
|
||||
const Source& src = file->getSource();
|
||||
if (util::stringEndsWith<char>(src.path, ".arsc.flat")) {
|
||||
return mergeResourceTable(file, override);
|
||||
|
||||
} else if (util::stringEndsWith<char>(src.path, ".flat")){
|
||||
// Try opening the file and looking for an Export header.
|
||||
std::unique_ptr<io::IData> data = file->openAsData();
|
||||
@@ -806,9 +1017,8 @@ public:
|
||||
std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader(
|
||||
src, data->data(), data->size(), mContext->getDiagnostics());
|
||||
if (resourceFile) {
|
||||
return mergeCompiledFile(file, std::move(resourceFile), override);
|
||||
return mergeCompiledFile(file, resourceFile.get(), override);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -826,32 +1036,30 @@ public:
|
||||
}
|
||||
|
||||
if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) {
|
||||
mContext->mCompilationPackage = maybeAppInfo.value().package;
|
||||
mContext->setCompilationPackage(maybeAppInfo.value().package);
|
||||
} else {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
|
||||
<< "no package specified in <manifest> tag");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!util::isJavaPackageName(mContext->mCompilationPackage)) {
|
||||
if (!util::isJavaPackageName(mContext->getCompilationPackage())) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath)
|
||||
<< "invalid package name '"
|
||||
<< mContext->mCompilationPackage
|
||||
<< mContext->getCompilationPackage()
|
||||
<< "'");
|
||||
return 1;
|
||||
}
|
||||
|
||||
mContext->mNameMangler = util::make_unique<NameMangler>(
|
||||
NameManglerPolicy{ mContext->mCompilationPackage });
|
||||
mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() });
|
||||
|
||||
if (mContext->mCompilationPackage == u"android") {
|
||||
mContext->mPackageId = 0x01;
|
||||
if (mContext->getCompilationPackage() == u"android") {
|
||||
mContext->setPackageId(0x01);
|
||||
} else {
|
||||
mContext->mPackageId = 0x7f;
|
||||
mContext->setPackageId(0x7f);
|
||||
}
|
||||
|
||||
mContext->mSymbols = createSymbolTableFromIncludePaths();
|
||||
if (!mContext->mSymbols) {
|
||||
if (!loadSymbolsFromIncludePaths()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -861,20 +1069,21 @@ public:
|
||||
|
||||
if (mContext->verbose()) {
|
||||
mContext->getDiagnostics()->note(
|
||||
DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' "
|
||||
<< "with package ID " << std::hex << (int) mContext->mPackageId);
|
||||
DiagMessage() << "linking package '" << mContext->getCompilationPackage()
|
||||
<< "' with package ID " << std::hex
|
||||
<< (int) mContext->getPackageId());
|
||||
}
|
||||
|
||||
|
||||
for (const std::string& input : inputFiles) {
|
||||
if (!processFile(input, false)) {
|
||||
if (!mergePath(input, false)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::string& input : mOptions.overlayFiles) {
|
||||
if (!processFile(input, true)) {
|
||||
if (!mergePath(input, true)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays");
|
||||
return 1;
|
||||
}
|
||||
@@ -893,20 +1102,28 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (!mOptions.staticLib) {
|
||||
// Assign IDs if we are building a regular app.
|
||||
IdAssigner idAssigner;
|
||||
if (!idAssigner.consume(mContext, &mFinalTable)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
// Static libs are merged with other apps, and ID collisions are bad, so verify that
|
||||
// no IDs have been set.
|
||||
if (!verifyNoIdsSet()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{
|
||||
mContext->mCompilationPackage, mTableMerger->getMergedPackages() });
|
||||
mContext->mSymbols = JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable))
|
||||
.addSymbolTable(std::move(mContext->mSymbols))
|
||||
.build();
|
||||
// Add the names to mangle based on our source merge earlier.
|
||||
mContext->setNameManglerPolicy(NameManglerPolicy{
|
||||
mContext->getCompilationPackage(), mTableMerger->getMergedPackages() });
|
||||
|
||||
// Add our table to the symbol table.
|
||||
mContext->getExternalSymbols()->prependSource(
|
||||
util::make_unique<ResourceTableSymbolSource>(&mFinalTable));
|
||||
|
||||
{
|
||||
ReferenceLinker linker;
|
||||
@@ -915,20 +1132,32 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
ProductFilter productFilter(mOptions.products);
|
||||
if (!productFilter.consume(mContext, &mFinalTable)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
|
||||
return 1;
|
||||
}
|
||||
if (mOptions.staticLib) {
|
||||
if (!mOptions.products.empty()) {
|
||||
mContext->getDiagnostics()->warn(
|
||||
DiagMessage() << "can't select products when building static library");
|
||||
}
|
||||
|
||||
// TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
|
||||
// level.
|
||||
TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
|
||||
if (!tableSplitter.verifySplitConstraints(mContext)) {
|
||||
return 1;
|
||||
}
|
||||
if (mOptions.tableSplitterOptions.configFilter != nullptr ||
|
||||
mOptions.tableSplitterOptions.preferredDensity) {
|
||||
mContext->getDiagnostics()->warn(
|
||||
DiagMessage() << "can't strip resources when building static library");
|
||||
}
|
||||
} else {
|
||||
ProductFilter productFilter(mOptions.products);
|
||||
if (!productFilter.consume(mContext, &mFinalTable)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products");
|
||||
return 1;
|
||||
}
|
||||
|
||||
tableSplitter.splitTable(&mFinalTable);
|
||||
// TODO(adamlesinski): Actually pass in split constraints and handle splits at the file
|
||||
// level.
|
||||
TableSplitter tableSplitter({}, mOptions.tableSplitterOptions);
|
||||
if (!tableSplitter.verifySplitConstraints(mContext)) {
|
||||
return 1;
|
||||
}
|
||||
tableSplitter.splitTable(&mFinalTable);
|
||||
}
|
||||
}
|
||||
|
||||
proguard::KeepSet proguardKeepSet;
|
||||
@@ -949,7 +1178,7 @@ public:
|
||||
// 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.
|
||||
manifestXml->file.name.package = mContext->getCompilationPackage().toString();
|
||||
manifestXml->file.name.package = mContext->getCompilationPackage();
|
||||
|
||||
XmlReferenceLinker manifestLinker;
|
||||
if (manifestLinker.consume(mContext, manifestXml.get())) {
|
||||
@@ -993,7 +1222,7 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!mOptions.noAutoVersion) {
|
||||
if (!mOptions.staticLib && !mOptions.noAutoVersion) {
|
||||
AutoVersioner versioner;
|
||||
if (!versioner.consume(mContext, &mFinalTable)) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles");
|
||||
@@ -1001,9 +1230,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!flattenTable(&mFinalTable, archiveWriter.get())) {
|
||||
mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc");
|
||||
return 1;
|
||||
if (mOptions.staticLib) {
|
||||
if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) {
|
||||
mContext->getDiagnostics()->error(DiagMessage()
|
||||
<< "failed to write resources.arsc.flat");
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (!flattenTable(&mFinalTable, archiveWriter.get())) {
|
||||
mContext->getDiagnostics()->error(DiagMessage()
|
||||
<< "failed to write resources.arsc");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (mOptions.generateJavaClassPath) {
|
||||
@@ -1065,14 +1303,17 @@ private:
|
||||
LinkContext* mContext;
|
||||
ResourceTable mFinalTable;
|
||||
|
||||
ResourceTable mLocalFileTable;
|
||||
std::unique_ptr<TableMerger> mTableMerger;
|
||||
|
||||
// A pointer to the FileCollection representing the filesystem (not archives).
|
||||
io::FileCollection* mFileCollection;
|
||||
std::unique_ptr<io::FileCollection> mFileCollection;
|
||||
|
||||
// A vector of IFileCollections. This is mainly here to keep ownership of the collections.
|
||||
std::vector<std::unique_ptr<io::IFileCollection>> mCollections;
|
||||
|
||||
// A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable
|
||||
// can use these.
|
||||
std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes;
|
||||
};
|
||||
|
||||
int link(const std::vector<StringPiece>& args) {
|
||||
@@ -1089,6 +1330,7 @@ int link(const std::vector<StringPiece>& args) {
|
||||
Maybe<std::string> productList;
|
||||
bool legacyXFlag = false;
|
||||
bool requireLocalization = false;
|
||||
bool verbose = false;
|
||||
Flags flags = Flags()
|
||||
.requiredFlag("-o", "Output path", &options.outputPath)
|
||||
.requiredFlag("--manifest", "Path to the Android manifest to build",
|
||||
@@ -1104,6 +1346,10 @@ int link(const std::vector<StringPiece>& args) {
|
||||
.optionalSwitch("--no-auto-version",
|
||||
"Disables automatic style and layout SDK versioning",
|
||||
&options.noAutoVersion)
|
||||
.optionalSwitch("--no-version-vectors",
|
||||
"Disables automatic versioning of vector drawables. Use this only\n"
|
||||
"when building with vector drawable support library",
|
||||
&options.noVersionVectors)
|
||||
.optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
|
||||
&legacyXFlag)
|
||||
.optionalSwitch("-z", "Require localization of strings marked 'suggested'",
|
||||
@@ -1127,6 +1373,9 @@ int link(const std::vector<StringPiece>& args) {
|
||||
.optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml "
|
||||
"if none is present", &versionName)
|
||||
.optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib)
|
||||
.optionalSwitch("--no-static-lib-packages",
|
||||
"Merge all library resources under the app's package",
|
||||
&options.noStaticLibPackages)
|
||||
.optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n"
|
||||
"This is implied when --static-lib is specified.",
|
||||
&options.generateNonFinalIds)
|
||||
@@ -1148,12 +1397,16 @@ int link(const std::vector<StringPiece>& args) {
|
||||
&renameInstrumentationTargetPackage)
|
||||
.optionalFlagList("-0", "File extensions not to compress",
|
||||
&options.extensionsToNotCompress)
|
||||
.optionalSwitch("-v", "Enables verbose logging", &context.mVerbose);
|
||||
.optionalSwitch("-v", "Enables verbose logging", &verbose);
|
||||
|
||||
if (!flags.parse("aapt2 link", args, &std::cerr)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (verbose) {
|
||||
context.setVerbose(verbose);
|
||||
}
|
||||
|
||||
if (privateSymbolsPackage) {
|
||||
options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value());
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ struct ManifestFixerTest : public ::testing::Test {
|
||||
.setCompilationPackage(u"android")
|
||||
.setPackageId(0x01)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"android" })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addSymbol(u"@android:attr/package", ResourceId(0x01010000),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_STRING)
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ReferenceLinker.h"
|
||||
|
||||
#include "Diagnostics.h"
|
||||
#include "ReferenceLinker.h"
|
||||
#include "ResourceTable.h"
|
||||
#include "ResourceUtils.h"
|
||||
#include "ResourceValues.h"
|
||||
@@ -43,45 +42,10 @@ namespace {
|
||||
* NOTE: All of the entries in the ResourceTable must be assigned IDs.
|
||||
*/
|
||||
class ReferenceLinkerVisitor : public ValueVisitor {
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
xml::IPackageDeclStack* mPackageDecls;
|
||||
StringPool* mStringPool;
|
||||
CallSite* mCallSite;
|
||||
bool mError = false;
|
||||
|
||||
/**
|
||||
* 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* rawString = valueCast<RawString>(value.get())) {
|
||||
std::unique_ptr<Item> transformed =
|
||||
ResourceUtils::parseItemForAttribute(*rawString->value, attr);
|
||||
|
||||
// If we could not parse as any specific type, try a basic STRING.
|
||||
if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
|
||||
util::StringBuilder stringBuilder;
|
||||
stringBuilder.append(*rawString->value);
|
||||
if (stringBuilder) {
|
||||
transformed = util::make_unique<String>(
|
||||
mStringPool->makeRef(stringBuilder.str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
return transformed;
|
||||
}
|
||||
};
|
||||
return value;
|
||||
}
|
||||
|
||||
public:
|
||||
using ValueVisitor::visit;
|
||||
|
||||
ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool,
|
||||
ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool,
|
||||
xml::IPackageDeclStack* decl,CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool),
|
||||
mCallSite(callSite) {
|
||||
@@ -114,10 +78,11 @@ public:
|
||||
&transformedReference);
|
||||
|
||||
// Find the attribute in the symbol table and check if it is visible from this callsite.
|
||||
const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr);
|
||||
if (symbol) {
|
||||
// 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
|
||||
@@ -156,6 +121,41 @@ public:
|
||||
bool hasError() {
|
||||
return mError;
|
||||
}
|
||||
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
SymbolTable* mSymbols;
|
||||
xml::IPackageDeclStack* mPackageDecls;
|
||||
StringPool* mStringPool;
|
||||
CallSite* mCallSite;
|
||||
bool mError = false;
|
||||
|
||||
/**
|
||||
* 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* rawString = valueCast<RawString>(value.get())) {
|
||||
std::unique_ptr<Item> transformed =
|
||||
ResourceUtils::parseItemForAttribute(*rawString->value, attr);
|
||||
|
||||
// If we could not parse as any specific type, try a basic STRING.
|
||||
if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) {
|
||||
util::StringBuilder stringBuilder;
|
||||
stringBuilder.append(*rawString->value);
|
||||
if (stringBuilder) {
|
||||
transformed = util::make_unique<String>(
|
||||
mStringPool->makeRef(stringBuilder.str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (transformed) {
|
||||
return transformed;
|
||||
}
|
||||
};
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@@ -164,13 +164,13 @@ public:
|
||||
* 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 ISymbolTable::Symbol& symbol, const Reference& ref,
|
||||
bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref,
|
||||
const CallSite& callSite) {
|
||||
if (!symbol.isPublic && !ref.privateReference) {
|
||||
if (ref.name) {
|
||||
return callSite.resource.package == ref.name.value().package;
|
||||
} else if (ref.id) {
|
||||
return ref.id.value().packageId() == symbol.id.packageId();
|
||||
} else if (ref.id && symbol.id) {
|
||||
return ref.id.value().packageId() == symbol.id.value().packageId();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -178,9 +178,9 @@ bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const
|
||||
return true;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler,
|
||||
ISymbolTable* symbols) {
|
||||
const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler,
|
||||
SymbolTable* symbols) {
|
||||
if (reference.name) {
|
||||
Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value());
|
||||
return symbols->findByName(mangled ? mangled.value() : reference.name.value());
|
||||
@@ -191,10 +191,10 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& refe
|
||||
}
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
|
||||
const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
|
||||
CallSite* callSite, std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
if (!symbol) {
|
||||
if (outError) *outError = "not found";
|
||||
return nullptr;
|
||||
@@ -207,12 +207,12 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility(
|
||||
return symbol;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols,
|
||||
const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols,
|
||||
CallSite* callSite, std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
|
||||
symbols, callSite,
|
||||
outError);
|
||||
const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler,
|
||||
symbols, callSite,
|
||||
outError);
|
||||
if (!symbol) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -226,10 +226,10 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility(
|
||||
|
||||
Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
SymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError) {
|
||||
const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols);
|
||||
if (!symbol) {
|
||||
return {};
|
||||
}
|
||||
@@ -256,7 +256,7 @@ void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& or
|
||||
}
|
||||
|
||||
bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
|
||||
ISymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
SymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
CallSite* callSite) {
|
||||
assert(reference);
|
||||
assert(reference->name || reference->id);
|
||||
@@ -266,9 +266,12 @@ bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context,
|
||||
&transformedReference);
|
||||
|
||||
std::string errStr;
|
||||
const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility(
|
||||
const SymbolTable::Symbol* s = resolveSymbolCheckVisibility(
|
||||
transformedReference, context->getNameMangler(), symbols, callSite, &errStr);
|
||||
if (s) {
|
||||
// The ID may not exist. This is fine because of the possibility of building against
|
||||
// libraries without assigned IDs.
|
||||
// Ex: Linking against own resources when building a static library.
|
||||
reference->id = s->id;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,36 +38,36 @@ struct ReferenceLinker : public IResourceTableConsumer {
|
||||
/**
|
||||
* Returns true if the symbol is visible by the reference and from the callsite.
|
||||
*/
|
||||
static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref,
|
||||
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. Returns nullptr
|
||||
* if the symbol was not found.
|
||||
*/
|
||||
static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler, ISymbolTable* symbols);
|
||||
static const SymbolTable::Symbol* resolveSymbol(const Reference& reference,
|
||||
NameMangler* mangler, 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. outError holds
|
||||
* the error message.
|
||||
*/
|
||||
static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
static const SymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
SymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
/**
|
||||
* 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 ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
static const SymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
SymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
/**
|
||||
* Resolves the attribute reference and returns an xml::AaptAttribute if successful.
|
||||
@@ -75,7 +75,7 @@ struct ReferenceLinker : public IResourceTableConsumer {
|
||||
*/
|
||||
static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
|
||||
NameMangler* nameMangler,
|
||||
ISymbolTable* symbols,
|
||||
SymbolTable* symbols,
|
||||
CallSite* callSite,
|
||||
std::string* outError);
|
||||
|
||||
@@ -92,7 +92,7 @@ struct ReferenceLinker : public IResourceTableConsumer {
|
||||
* 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(Reference* reference, IAaptContext* context, ISymbolTable* symbols,
|
||||
static bool linkReference(Reference* reference, IAaptContext* context, SymbolTable* symbols,
|
||||
xml::IPackageDeclStack* decls, CallSite* callSite);
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
*/
|
||||
|
||||
#include "link/ReferenceLinker.h"
|
||||
#include "process/SymbolTable.h"
|
||||
#include "test/Test.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
using android::ResTable_map;
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -41,12 +38,10 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build())
|
||||
.addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
@@ -91,19 +86,20 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000))
|
||||
.addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_FLAGS)
|
||||
.addItem(u"one", 0x01)
|
||||
.addItem(u"two", 0x02)
|
||||
.build())
|
||||
.build())
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addPublicSymbol(u"@android:style/Theme.Material",
|
||||
ResourceId(0x01060000))
|
||||
.addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(ResTable_map::TYPE_FLAGS)
|
||||
.addItem(u"one", 0x01)
|
||||
.addItem(u"two", 0x02)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
@@ -131,11 +127,13 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
|
||||
ResourceId(0x7f010000), test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR).build())
|
||||
.build())
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addPublicSymbol(u"@com.app.test:attr/com.android.support$foo",
|
||||
ResourceId(0x7f010000),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
@@ -167,12 +165,10 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build())
|
||||
.addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addSymbol(u"@android:string/hidden", ResourceId(0x01040034))
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
@@ -190,13 +186,12 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@com.app.test:string/com.app.lib$hidden",
|
||||
ResourceId(0x7f040034))
|
||||
.build())
|
||||
.build())
|
||||
.addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addSymbol(u"@com.app.test:string/com.app.lib$hidden",
|
||||
ResourceId(0x7f040034))
|
||||
.build())
|
||||
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
@@ -215,15 +210,14 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setPackageId(0x7f)
|
||||
.setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" })
|
||||
.setSymbolTable(JoinedSymbolTableBuilder()
|
||||
.addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get()))
|
||||
.addSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.build())
|
||||
.build())
|
||||
.addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get()))
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addSymbol(u"@android:attr/hidden", ResourceId(0x01010001),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(
|
||||
android::ResTable_map::TYPE_COLOR)
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
|
||||
ReferenceLinker linker;
|
||||
|
||||
@@ -34,10 +34,21 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable,
|
||||
assert(mMasterPackage && "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::mergeOverlay(const Source& src, ResourceTable* table,
|
||||
io::IFileCollection* collection) {
|
||||
return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 allowNew) {
|
||||
const uint8_t desiredPackageId = mContext->getPackageId();
|
||||
|
||||
@@ -51,26 +62,36 @@ bool TableMerger::mergeImpl(const Source& src, ResourceTable* table,
|
||||
}
|
||||
|
||||
if (package->name.empty() || mContext->getCompilationPackage() == package->name) {
|
||||
FileMergeCallback callback;
|
||||
if (collection) {
|
||||
callback = [&](const ResourceNameRef& name, const ConfigDescription& config,
|
||||
FileReference* newFile, FileReference* oldFile) -> bool {
|
||||
// The old file's path points inside the APK, so we can use it as is.
|
||||
io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path));
|
||||
if (!f) {
|
||||
mContext->getDiagnostics()->error(DiagMessage(src) << "file '"
|
||||
<< *oldFile->path
|
||||
<< "' not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
newFile->file = f;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// 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, allowNew, {});
|
||||
false /* mangle */, overlay, allowNew, callback);
|
||||
}
|
||||
}
|
||||
return !error;
|
||||
}
|
||||
|
||||
bool TableMerger::merge(const Source& src, ResourceTable* table) {
|
||||
return mergeImpl(src, table, false /* overlay */, true /* allow new */);
|
||||
}
|
||||
|
||||
bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) {
|
||||
return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay);
|
||||
}
|
||||
|
||||
/**
|
||||
* This will merge and mangle resources from a static library.
|
||||
*/
|
||||
|
||||
@@ -65,13 +65,17 @@ public:
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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.
|
||||
*/
|
||||
bool mergeOverlay(const Source& src, ResourceTable* table);
|
||||
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.
|
||||
@@ -104,7 +108,7 @@ private:
|
||||
|
||||
bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay);
|
||||
|
||||
bool mergeImpl(const Source& src, ResourceTable* srcTable,
|
||||
bool mergeImpl(const Source& src, ResourceTable* srcTable, io::IFileCollection* collection,
|
||||
bool overlay, bool allowNew);
|
||||
|
||||
bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage,
|
||||
|
||||
@@ -34,17 +34,10 @@ namespace {
|
||||
* as needed.
|
||||
*/
|
||||
class ReferenceVisitor : public ValueVisitor {
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
xml::IPackageDeclStack* mDecls;
|
||||
CallSite* mCallSite;
|
||||
bool mError;
|
||||
|
||||
public:
|
||||
using ValueVisitor::visit;
|
||||
|
||||
ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls,
|
||||
CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite),
|
||||
mError(false) {
|
||||
@@ -59,25 +52,23 @@ public:
|
||||
bool hasError() const {
|
||||
return mError;
|
||||
}
|
||||
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
SymbolTable* mSymbols;
|
||||
xml::IPackageDeclStack* mDecls;
|
||||
CallSite* mCallSite;
|
||||
bool mError;
|
||||
};
|
||||
|
||||
/**
|
||||
* Visits each xml Element and compiles the attributes within.
|
||||
*/
|
||||
class XmlVisitor : public xml::PackageAwareVisitor {
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
ISymbolTable* mSymbols;
|
||||
Source mSource;
|
||||
std::set<int>* mSdkLevelsFound;
|
||||
CallSite* mCallSite;
|
||||
ReferenceVisitor mReferenceVisitor;
|
||||
bool mError = false;
|
||||
|
||||
public:
|
||||
using xml::PackageAwareVisitor::visit;
|
||||
|
||||
XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source,
|
||||
XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source,
|
||||
std::set<int>* sdkLevelsFound, CallSite* callSite) :
|
||||
mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound),
|
||||
mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) {
|
||||
@@ -105,10 +96,13 @@ public:
|
||||
|
||||
// Convert the string value into a compiled Value if this is a valid attribute.
|
||||
if (attr.compiledAttribute) {
|
||||
// Record all SDK levels from which the attributes were defined.
|
||||
const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id);
|
||||
if (sdkLevel > 1) {
|
||||
mSdkLevelsFound->insert(sdkLevel);
|
||||
if (attr.compiledAttribute.value().id) {
|
||||
// Record all SDK levels from which the attributes were defined.
|
||||
const size_t sdkLevel = findAttributeSdkLevel(
|
||||
attr.compiledAttribute.value().id.value());
|
||||
if (sdkLevel > 1) {
|
||||
mSdkLevelsFound->insert(sdkLevel);
|
||||
}
|
||||
}
|
||||
|
||||
const Attribute* attribute = &attr.compiledAttribute.value().attribute;
|
||||
@@ -124,6 +118,7 @@ public:
|
||||
<< *attribute);
|
||||
mError = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
mContext->getDiagnostics()->error(DiagMessage(source)
|
||||
<< "attribute '" << package << ":"
|
||||
@@ -150,6 +145,15 @@ public:
|
||||
bool hasError() {
|
||||
return mError || mReferenceVisitor.hasError();
|
||||
}
|
||||
|
||||
private:
|
||||
IAaptContext* mContext;
|
||||
SymbolTable* mSymbols;
|
||||
Source mSource;
|
||||
std::set<int>* mSdkLevelsFound;
|
||||
CallSite* mCallSite;
|
||||
ReferenceVisitor mReferenceVisitor;
|
||||
bool mError = false;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -14,12 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <test/Context.h>
|
||||
#include "link/Linkers.h"
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "test/Test.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
@@ -30,7 +27,7 @@ public:
|
||||
.setCompilationPackage(u"com.app.test")
|
||||
.setNameManglerPolicy(
|
||||
NameManglerPolicy{ u"com.app.test", { u"com.android.support" } })
|
||||
.setSymbolTable(test::StaticSymbolTableBuilder()
|
||||
.addSymbolSource(test::StaticSymbolSourceBuilder()
|
||||
.addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000),
|
||||
test::AttributeBuilder()
|
||||
.setTypeMask(android::ResTable_map::TYPE_ENUM |
|
||||
@@ -92,14 +89,16 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
|
||||
u"layout_width");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010000));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000));
|
||||
ASSERT_NE(xmlAttr->compiledValue, nullptr);
|
||||
ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
|
||||
|
||||
xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010001));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001));
|
||||
ASSERT_NE(xmlAttr->compiledValue, nullptr);
|
||||
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
|
||||
ASSERT_NE(ref, nullptr);
|
||||
@@ -163,7 +162,8 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) {
|
||||
u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010001));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001));
|
||||
ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr);
|
||||
}
|
||||
|
||||
@@ -182,7 +182,8 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) {
|
||||
u"colorAccent");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010000));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000));
|
||||
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
|
||||
ASSERT_NE(ref, nullptr);
|
||||
AAPT_ASSERT_TRUE(ref->name);
|
||||
@@ -209,7 +210,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
u"attr");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010002));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002));
|
||||
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
|
||||
ASSERT_NE(ref, nullptr);
|
||||
AAPT_ASSERT_TRUE(ref->id);
|
||||
@@ -223,7 +225,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) {
|
||||
xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
|
||||
ref = valueCast<Reference>(xmlAttr->compiledValue.get());
|
||||
ASSERT_NE(ref, nullptr);
|
||||
AAPT_ASSERT_TRUE(ref->id);
|
||||
@@ -246,7 +249,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) {
|
||||
u"http://schemas.android.com/apk/res/com.app.test", u"attr");
|
||||
ASSERT_NE(xmlAttr, nullptr);
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002));
|
||||
AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id);
|
||||
EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002));
|
||||
Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get());
|
||||
ASSERT_NE(ref, nullptr);
|
||||
AAPT_ASSERT_TRUE(ref->id);
|
||||
|
||||
@@ -30,14 +30,14 @@
|
||||
namespace aapt {
|
||||
|
||||
class ResourceTable;
|
||||
struct ISymbolTable;
|
||||
class SymbolTable;
|
||||
|
||||
struct IAaptContext {
|
||||
virtual ~IAaptContext() = default;
|
||||
|
||||
virtual ISymbolTable* getExternalSymbols() = 0;
|
||||
virtual SymbolTable* getExternalSymbols() = 0;
|
||||
virtual IDiagnostics* getDiagnostics() = 0;
|
||||
virtual StringPiece16 getCompilationPackage() = 0;
|
||||
virtual const std::u16string& getCompilationPackage() = 0;
|
||||
virtual uint8_t getPackageId() = 0;
|
||||
virtual NameMangler* getNameMangler() = 0;
|
||||
virtual bool verbose() = 0;
|
||||
|
||||
@@ -25,11 +25,59 @@
|
||||
|
||||
namespace aapt {
|
||||
|
||||
const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& name) {
|
||||
void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
|
||||
mSources.push_back(std::move(source));
|
||||
|
||||
// We do not clear the cache, because sources earlier in the list take precedent.
|
||||
}
|
||||
|
||||
void SymbolTable::prependSource(std::unique_ptr<ISymbolSource> source) {
|
||||
mSources.insert(mSources.begin(), std::move(source));
|
||||
|
||||
// We must clear the cache in case we did a lookup before adding this resource.
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
|
||||
if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
|
||||
return s.get();
|
||||
}
|
||||
|
||||
// We did not find it in the cache, so look through the sources.
|
||||
for (auto& symbolSource : mSources) {
|
||||
std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
|
||||
if (symbol) {
|
||||
// Take ownership of the symbol into a shared_ptr. We do this because LruCache
|
||||
// doesn't support unique_ptr.
|
||||
std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
|
||||
mCache.put(name, sharedSymbol);
|
||||
return sharedSymbol.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const SymbolTable::Symbol* SymbolTable::findById(ResourceId id) {
|
||||
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
|
||||
return s.get();
|
||||
}
|
||||
|
||||
// We did not find it in the cache, so look through the sources.
|
||||
for (auto& symbolSource : mSources) {
|
||||
std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
|
||||
if (symbol) {
|
||||
// Take ownership of the symbol into a shared_ptr. We do this because LruCache
|
||||
// doesn't support unique_ptr.
|
||||
std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
|
||||
mIdCache.put(id, sharedSymbol);
|
||||
return sharedSymbol.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
|
||||
const ResourceName& name) {
|
||||
Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
|
||||
if (!result) {
|
||||
if (name.type == ResourceType::kAttr) {
|
||||
@@ -41,16 +89,13 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
|
||||
|
||||
ResourceTable::SearchResult sr = result.value();
|
||||
|
||||
// If no ID exists, we treat the symbol as missing. SymbolTables are used to
|
||||
// find symbols to link.
|
||||
if (!sr.package->id || !sr.type->id || !sr.entry->id) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
|
||||
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
|
||||
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
|
||||
symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
|
||||
|
||||
if (sr.package->id && sr.type->id && sr.entry->id) {
|
||||
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
|
||||
}
|
||||
|
||||
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
|
||||
const ConfigDescription kDefaultConfig;
|
||||
ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
|
||||
@@ -63,18 +108,16 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (name.type == ResourceType::kAttrPrivate) {
|
||||
// Masquerade this entry as kAttr.
|
||||
mCache.put(ResourceName(name.package, ResourceType::kAttr, name.entry), symbol);
|
||||
} else {
|
||||
mCache.put(name, symbol);
|
||||
}
|
||||
return symbol.get();
|
||||
return symbol;
|
||||
}
|
||||
|
||||
static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
|
||||
ResourceId id) {
|
||||
bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) {
|
||||
int32_t cookie = 0;
|
||||
return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie);
|
||||
}
|
||||
|
||||
static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
|
||||
ResourceId id) {
|
||||
// Try as a bag.
|
||||
const android::ResTable::bag_entry* entry;
|
||||
ssize_t count = table.lockBag(id.id, &entry);
|
||||
@@ -84,7 +127,7 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi
|
||||
}
|
||||
|
||||
// We found a resource.
|
||||
std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
|
||||
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = id;
|
||||
|
||||
// Check to see if it is an attribute.
|
||||
@@ -138,43 +181,36 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi
|
||||
return s;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName(
|
||||
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
|
||||
const ResourceName& name) {
|
||||
if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
|
||||
return s.get();
|
||||
const android::ResTable& table = mAssets.getResources(false);
|
||||
StringPiece16 typeStr = toString(name.type);
|
||||
uint32_t typeSpecFlags = 0;
|
||||
ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
|
||||
typeStr.data(), typeStr.size(),
|
||||
name.package.data(), name.package.size(),
|
||||
&typeSpecFlags);
|
||||
if (!resId.isValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& asset : mAssets) {
|
||||
const android::ResTable& table = asset->getResources(false);
|
||||
StringPiece16 typeStr = toString(name.type);
|
||||
uint32_t typeSpecFlags = 0;
|
||||
ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
|
||||
typeStr.data(), typeStr.size(),
|
||||
name.package.data(), name.package.size(),
|
||||
&typeSpecFlags);
|
||||
if (!resId.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::shared_ptr<Symbol> s;
|
||||
if (name.type == ResourceType::kAttr) {
|
||||
s = lookupAttributeInTable(table, resId);
|
||||
} else {
|
||||
s = std::make_shared<Symbol>();
|
||||
s->id = resId;
|
||||
}
|
||||
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
mCache.put(name, s);
|
||||
return s.get();
|
||||
}
|
||||
std::unique_ptr<SymbolTable::Symbol> s;
|
||||
if (name.type == ResourceType::kAttr) {
|
||||
s = lookupAttributeInTable(table, resId);
|
||||
} else {
|
||||
s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = resId;
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
return s;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
|
||||
android::ResTable::resource_name resName;
|
||||
android::ResTable::resource_name resName = {};
|
||||
if (!table.getResourceName(id.id, true, &resName)) {
|
||||
return {};
|
||||
}
|
||||
@@ -211,55 +247,27 @@ static Maybe<ResourceName> getResourceName(const android::ResTable& table, Resou
|
||||
return name;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
|
||||
ResourceId id) {
|
||||
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
|
||||
return s.get();
|
||||
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) {
|
||||
const android::ResTable& table = mAssets.getResources(false);
|
||||
Maybe<ResourceName> maybeName = getResourceName(table, id);
|
||||
if (!maybeName) {
|
||||
return {};
|
||||
}
|
||||
|
||||
for (const auto& asset : mAssets) {
|
||||
const android::ResTable& table = asset->getResources(false);
|
||||
uint32_t typeSpecFlags = 0;
|
||||
table.getResourceFlags(id.id, &typeSpecFlags);
|
||||
|
||||
Maybe<ResourceName> maybeName = getResourceName(table, id);
|
||||
if (!maybeName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t typeSpecFlags = 0;
|
||||
table.getResourceFlags(id.id, &typeSpecFlags);
|
||||
|
||||
std::shared_ptr<Symbol> s;
|
||||
if (maybeName.value().type == ResourceType::kAttr) {
|
||||
s = lookupAttributeInTable(table, id);
|
||||
} else {
|
||||
s = std::make_shared<Symbol>();
|
||||
s->id = id;
|
||||
}
|
||||
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
mIdCache.put(id, s);
|
||||
return s.get();
|
||||
}
|
||||
std::unique_ptr<SymbolTable::Symbol> s;
|
||||
if (maybeName.value().type == ResourceType::kAttr) {
|
||||
s = lookupAttributeInTable(table, id);
|
||||
} else {
|
||||
s = util::make_unique<SymbolTable::Symbol>();
|
||||
s->id = id;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName(
|
||||
const ResourceName& name) {
|
||||
for (auto& symbolTable : mSymbolTables) {
|
||||
if (const Symbol* s = symbolTable->findByName(name)) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findById(ResourceId id) {
|
||||
for (auto& symbolTable : mSymbolTables) {
|
||||
if (const Symbol* s = symbolTable->findById(id)) {
|
||||
return s;
|
||||
}
|
||||
if (s) {
|
||||
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
||||
return s;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -25,37 +25,20 @@
|
||||
#include <utils/JenkinsHash.h>
|
||||
#include <utils/LruCache.h>
|
||||
|
||||
#include <android-base/macros.h>
|
||||
#include <androidfw/AssetManager.h>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace aapt {
|
||||
|
||||
struct ISymbolTable {
|
||||
virtual ~ISymbolTable() = default;
|
||||
|
||||
struct Symbol {
|
||||
ResourceId id;
|
||||
std::unique_ptr<Attribute> attribute;
|
||||
bool isPublic;
|
||||
};
|
||||
|
||||
/**
|
||||
* Never hold on to the result between calls to findByName or findById. The results
|
||||
* are typically stored in a cache which may evict entries.
|
||||
*/
|
||||
virtual const Symbol* findByName(const ResourceName& name) = 0;
|
||||
virtual const Symbol* findById(ResourceId id) = 0;
|
||||
};
|
||||
|
||||
inline android::hash_t hash_type(const ResourceName& name) {
|
||||
std::hash<std::u16string> strHash;
|
||||
android::hash_t hash = 0;
|
||||
hash = android::JenkinsHashMix(hash, strHash(name.package));
|
||||
hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.package));
|
||||
hash = android::JenkinsHashMix(hash, (uint32_t) name.type);
|
||||
hash = android::JenkinsHashMix(hash, strHash(name.entry));
|
||||
hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.entry));
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -63,88 +46,87 @@ inline android::hash_t hash_type(const ResourceId& id) {
|
||||
return android::hash_type(id.id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Presents a ResourceTable as an ISymbolTable, caching results.
|
||||
* Instances of this class must outlive the encompassed ResourceTable.
|
||||
* Since symbols are cached, the ResourceTable should not change during the
|
||||
* lifetime of this SymbolTableWrapper.
|
||||
*
|
||||
* If a resource in the ResourceTable does not have a ResourceID assigned to it,
|
||||
* it is ignored.
|
||||
*
|
||||
* Lookups by ID are ignored.
|
||||
*/
|
||||
class SymbolTableWrapper : public ISymbolTable {
|
||||
class ISymbolSource;
|
||||
|
||||
class SymbolTable {
|
||||
public:
|
||||
struct Symbol {
|
||||
Maybe<ResourceId> id;
|
||||
std::unique_ptr<Attribute> attribute;
|
||||
bool isPublic;
|
||||
};
|
||||
|
||||
SymbolTable() : mCache(200), mIdCache(200) {
|
||||
}
|
||||
|
||||
void appendSource(std::unique_ptr<ISymbolSource> source);
|
||||
void prependSource(std::unique_ptr<ISymbolSource> source);
|
||||
|
||||
/**
|
||||
* Never hold on to the result between calls to findByName or findById. The results
|
||||
* are typically stored in a cache which may evict entries.
|
||||
*/
|
||||
const Symbol* findByName(const ResourceName& name);
|
||||
const Symbol* findById(ResourceId id);
|
||||
|
||||
private:
|
||||
ResourceTable* mTable;
|
||||
std::vector<std::unique_ptr<ISymbolSource>> mSources;
|
||||
|
||||
// We use shared_ptr because unique_ptr is not supported and
|
||||
// we need automatic deletion.
|
||||
android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
|
||||
android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SymbolTable);
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface that a symbol source implements in order to surface symbol information
|
||||
* to the symbol table.
|
||||
*/
|
||||
class ISymbolSource {
|
||||
public:
|
||||
SymbolTableWrapper(ResourceTable* table) : mTable(table), mCache(200) {
|
||||
virtual ~ISymbolSource() = default;
|
||||
|
||||
virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
|
||||
virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exposes the resources in a ResourceTable as symbols for SymbolTable.
|
||||
* Instances of this class must outlive the encompassed ResourceTable.
|
||||
* Lookups by ID are ignored.
|
||||
*/
|
||||
class ResourceTableSymbolSource : public ISymbolSource {
|
||||
public:
|
||||
explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
|
||||
}
|
||||
|
||||
const Symbol* findByName(const ResourceName& name) override;
|
||||
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
|
||||
|
||||
// Unsupported, all queries to ResourceTable should be done by name.
|
||||
const Symbol* findById(ResourceId id) override {
|
||||
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
ResourceTable* mTable;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
|
||||
};
|
||||
|
||||
class AssetManagerSymbolTableBuilder {
|
||||
private:
|
||||
struct AssetManagerSymbolTable : public ISymbolTable {
|
||||
std::vector<std::unique_ptr<android::AssetManager>> mAssets;
|
||||
|
||||
// We use shared_ptr because unique_ptr is not supported and
|
||||
// we need automatic deletion.
|
||||
android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
|
||||
android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
|
||||
|
||||
AssetManagerSymbolTable() : mCache(200), mIdCache(200) {
|
||||
}
|
||||
|
||||
const Symbol* findByName(const ResourceName& name) override;
|
||||
const Symbol* findById(ResourceId id) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<AssetManagerSymbolTable> mSymbolTable =
|
||||
util::make_unique<AssetManagerSymbolTable>();
|
||||
|
||||
class AssetManagerSymbolSource : public ISymbolSource {
|
||||
public:
|
||||
AssetManagerSymbolTableBuilder& add(std::unique_ptr<android::AssetManager> assetManager) {
|
||||
mSymbolTable->mAssets.push_back(std::move(assetManager));
|
||||
return *this;
|
||||
}
|
||||
AssetManagerSymbolSource() = default;
|
||||
|
||||
std::unique_ptr<ISymbolTable> build() {
|
||||
return std::move(mSymbolTable);
|
||||
}
|
||||
};
|
||||
bool addAssetPath(const StringPiece& path);
|
||||
|
||||
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
|
||||
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
|
||||
|
||||
class JoinedSymbolTableBuilder {
|
||||
private:
|
||||
struct JoinedSymbolTable : public ISymbolTable {
|
||||
std::vector<std::unique_ptr<ISymbolTable>> mSymbolTables;
|
||||
android::AssetManager mAssets;
|
||||
|
||||
const Symbol* findByName(const ResourceName& name) override;
|
||||
const Symbol* findById(ResourceId id) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<JoinedSymbolTable> mSymbolTable = util::make_unique<JoinedSymbolTable>();
|
||||
|
||||
public:
|
||||
JoinedSymbolTableBuilder& addSymbolTable(std::unique_ptr<ISymbolTable> table) {
|
||||
mSymbolTable->mSymbolTables.push_back(std::move(table));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISymbolTable> build() {
|
||||
return std::move(mSymbolTable);
|
||||
}
|
||||
DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
|
||||
};
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -15,14 +15,11 @@
|
||||
*/
|
||||
|
||||
#include "process/SymbolTable.h"
|
||||
#include "test/Builders.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "test/Test.h"
|
||||
|
||||
namespace aapt {
|
||||
|
||||
TEST(SymbolTableWrapperTest, FindSymbolsWithIds) {
|
||||
TEST(ResourceTableSymbolSourceTest, FindSymbols) {
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
.addSimple(u"@android:id/foo", ResourceId(0x01020000))
|
||||
.addSimple(u"@android:id/bar")
|
||||
@@ -30,27 +27,27 @@ TEST(SymbolTableWrapperTest, FindSymbolsWithIds) {
|
||||
test::AttributeBuilder().build())
|
||||
.build();
|
||||
|
||||
SymbolTableWrapper symbolTable(table.get());
|
||||
EXPECT_NE(symbolTable.findByName(test::parseNameOrDie(u"@android:id/foo")), nullptr);
|
||||
EXPECT_EQ(symbolTable.findByName(test::parseNameOrDie(u"@android:id/bar")), nullptr);
|
||||
ResourceTableSymbolSource symbolSource(table.get());
|
||||
EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/foo")));
|
||||
EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/bar")));
|
||||
|
||||
const ISymbolTable::Symbol* s = symbolTable.findByName(
|
||||
std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
|
||||
test::parseNameOrDie(u"@android:attr/foo"));
|
||||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_NE(s->attribute, nullptr);
|
||||
ASSERT_NE(nullptr, s);
|
||||
EXPECT_NE(nullptr, s->attribute);
|
||||
}
|
||||
|
||||
TEST(SymbolTableWrapperTest, FindPrivateAttrSymbol) {
|
||||
TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
|
||||
std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
|
||||
.addValue(u"@android:^attr-private/foo", ResourceId(0x01010000),
|
||||
test::AttributeBuilder().build())
|
||||
.build();
|
||||
|
||||
SymbolTableWrapper symbolTable(table.get());
|
||||
const ISymbolTable::Symbol* s = symbolTable.findByName(
|
||||
ResourceTableSymbolSource symbolSource(table.get());
|
||||
std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName(
|
||||
test::parseNameOrDie(u"@android:attr/foo"));
|
||||
ASSERT_NE(s, nullptr);
|
||||
EXPECT_NE(s->attribute, nullptr);
|
||||
ASSERT_NE(nullptr, s);
|
||||
EXPECT_NE(nullptr, s->attribute);
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -483,8 +483,13 @@ const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
|
||||
}
|
||||
|
||||
const size_t padding = 4 - (pbSize & 0x03);
|
||||
mData += sizeof(uint64_t) + pbSize + padding;
|
||||
mSize -= sizeof(uint64_t) + pbSize + padding;
|
||||
const size_t offset = sizeof(uint64_t) + pbSize + padding;
|
||||
if (offset > mSize) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mData += offset;
|
||||
mSize -= offset;
|
||||
mPbFile = std::move(pbFile);
|
||||
}
|
||||
return mPbFile.get();
|
||||
|
||||
@@ -178,10 +178,13 @@ private:
|
||||
void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) {
|
||||
if (ref.id) {
|
||||
pbRef->set_id(ref.id.value().id);
|
||||
} else if (ref.name) {
|
||||
}
|
||||
|
||||
if (ref.name) {
|
||||
StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString());
|
||||
pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex()));
|
||||
}
|
||||
|
||||
pbRef->set_private_(ref.privateReference);
|
||||
pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType));
|
||||
}
|
||||
|
||||
@@ -62,6 +62,17 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u),
|
||||
context->getDiagnostics()));
|
||||
|
||||
// Make a reference with both resource name and resource ID.
|
||||
// The reference should point to a resource outside of this table to test that both
|
||||
// name and id get serialized.
|
||||
Reference expectedRef;
|
||||
expectedRef.name = test::parseNameOrDie(u"@android:layout/main");
|
||||
expectedRef.id = ResourceId(0x01020000);
|
||||
ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"),
|
||||
ConfigDescription::defaultConfig(), std::string(),
|
||||
util::make_unique<Reference>(expectedRef),
|
||||
context->getDiagnostics()));
|
||||
|
||||
std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get());
|
||||
ASSERT_NE(nullptr, pbTable);
|
||||
|
||||
@@ -90,6 +101,13 @@ TEST(TableProtoSerializer, SerializeSinglePackage) {
|
||||
newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet");
|
||||
ASSERT_NE(nullptr, prim);
|
||||
EXPECT_EQ(321u, prim->value.data);
|
||||
|
||||
Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc");
|
||||
ASSERT_NE(nullptr, actualRef);
|
||||
AAPT_ASSERT_TRUE(actualRef->name);
|
||||
AAPT_ASSERT_TRUE(actualRef->id);
|
||||
EXPECT_EQ(expectedRef.name.value(), actualRef->name.value());
|
||||
EXPECT_EQ(expectedRef.id.value(), actualRef->id.value());
|
||||
}
|
||||
|
||||
TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
@@ -130,4 +148,27 @@ TEST(TableProtoSerializer, SerializeFileHeader) {
|
||||
EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name);
|
||||
}
|
||||
|
||||
TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) {
|
||||
ResourceFile f;
|
||||
std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f);
|
||||
|
||||
const std::string expectedData = "1234";
|
||||
|
||||
std::string outputStr;
|
||||
{
|
||||
google::protobuf::io::StringOutputStream outStream(&outputStr);
|
||||
CompiledFileOutputStream outFileStream(&outStream, pbFile.get());
|
||||
|
||||
ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size()));
|
||||
ASSERT_TRUE(outFileStream.Finish());
|
||||
}
|
||||
|
||||
outputStr[0] = 0xff;
|
||||
|
||||
CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size());
|
||||
EXPECT_EQ(nullptr, inFileStream.CompiledFile());
|
||||
EXPECT_EQ(nullptr, inFileStream.data());
|
||||
EXPECT_EQ(0u, inFileStream.size());
|
||||
}
|
||||
|
||||
} // namespace aapt
|
||||
|
||||
@@ -246,7 +246,7 @@ inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) {
|
||||
inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context,
|
||||
const StringPiece& str) {
|
||||
std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str);
|
||||
doc->file.name.package = context->getCompilationPackage().toString();
|
||||
doc->file.name.package = context->getCompilationPackage();
|
||||
return doc;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,33 +31,16 @@ namespace aapt {
|
||||
namespace test {
|
||||
|
||||
class Context : public IAaptContext {
|
||||
private:
|
||||
friend class ContextBuilder;
|
||||
|
||||
Context() = default;
|
||||
|
||||
Maybe<std::u16string> mCompilationPackage;
|
||||
Maybe<uint8_t> mPackageId;
|
||||
std::unique_ptr<IDiagnostics> mDiagnostics = util::make_unique<StdErrDiagnostics>();
|
||||
std::unique_ptr<ISymbolTable> mSymbols;
|
||||
std::unique_ptr<NameMangler> mNameMangler;
|
||||
|
||||
public:
|
||||
ISymbolTable* getExternalSymbols() override {
|
||||
assert(mSymbols && "test symbols not set");
|
||||
return mSymbols.get();
|
||||
}
|
||||
|
||||
void setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
|
||||
mSymbols = std::move(symbols);
|
||||
SymbolTable* getExternalSymbols() override {
|
||||
return &mSymbols;
|
||||
}
|
||||
|
||||
IDiagnostics* getDiagnostics() override {
|
||||
assert(mDiagnostics && "test diagnostics not set");
|
||||
return mDiagnostics.get();
|
||||
return &mDiagnostics;
|
||||
}
|
||||
|
||||
StringPiece16 getCompilationPackage() override {
|
||||
const std::u16string& getCompilationPackage() override {
|
||||
assert(mCompilationPackage && "package name not set");
|
||||
return mCompilationPackage.value();
|
||||
}
|
||||
@@ -68,13 +51,24 @@ public:
|
||||
}
|
||||
|
||||
NameMangler* getNameMangler() override {
|
||||
assert(mNameMangler && "test name mangler not set");
|
||||
return mNameMangler.get();
|
||||
return &mNameMangler;
|
||||
}
|
||||
|
||||
bool verbose() override {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ContextBuilder;
|
||||
|
||||
Context() : mNameMangler({}) {
|
||||
}
|
||||
|
||||
Maybe<std::u16string> mCompilationPackage;
|
||||
Maybe<uint8_t> mPackageId;
|
||||
StdErrDiagnostics mDiagnostics;
|
||||
SymbolTable mSymbols;
|
||||
NameMangler mNameMangler;
|
||||
};
|
||||
|
||||
class ContextBuilder {
|
||||
@@ -92,18 +86,13 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContextBuilder& setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
|
||||
mContext->mSymbols = std::move(symbols);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContextBuilder& setDiagnostics(std::unique_ptr<IDiagnostics> diag) {
|
||||
mContext->mDiagnostics = std::move(diag);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) {
|
||||
mContext->mNameMangler = util::make_unique<NameMangler>(policy);
|
||||
mContext->mNameMangler = NameMangler(policy);
|
||||
return *this;
|
||||
}
|
||||
|
||||
ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
|
||||
mContext->getExternalSymbols()->appendSource(std::move(src));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -112,57 +101,72 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class StaticSymbolTableBuilder {
|
||||
private:
|
||||
struct SymbolTable : public ISymbolTable {
|
||||
std::list<std::unique_ptr<Symbol>> mSymbols;
|
||||
std::map<ResourceName, Symbol*> mNameMap;
|
||||
std::map<ResourceId, Symbol*> mIdMap;
|
||||
class StaticSymbolSourceBuilder {
|
||||
public:
|
||||
StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
|
||||
id, std::move(attr), true);
|
||||
mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
mSymbolSource->mIdMap[id] = symbol.get();
|
||||
mSymbolSource->mSymbols.push_back(std::move(symbol));
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Symbol* findByName(const ResourceName& name) override {
|
||||
StaticSymbolSourceBuilder& addSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
|
||||
id, std::move(attr), false);
|
||||
mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
mSymbolSource->mIdMap[id] = symbol.get();
|
||||
mSymbolSource->mSymbols.push_back(std::move(symbol));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISymbolSource> build() {
|
||||
return std::move(mSymbolSource);
|
||||
}
|
||||
|
||||
private:
|
||||
class StaticSymbolSource : public ISymbolSource {
|
||||
public:
|
||||
StaticSymbolSource() = default;
|
||||
|
||||
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
|
||||
auto iter = mNameMap.find(name);
|
||||
if (iter != mNameMap.end()) {
|
||||
return iter->second;
|
||||
return cloneSymbol(iter->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Symbol* findById(ResourceId id) override {
|
||||
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
|
||||
auto iter = mIdMap.find(id);
|
||||
if (iter != mIdMap.end()) {
|
||||
return iter->second;
|
||||
return cloneSymbol(iter->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
|
||||
std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
|
||||
std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
|
||||
|
||||
private:
|
||||
std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
|
||||
std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
|
||||
clone->id = sym->id;
|
||||
if (sym->attribute) {
|
||||
clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
|
||||
}
|
||||
clone->isPublic = sym->isPublic;
|
||||
return clone;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
|
||||
};
|
||||
|
||||
std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
|
||||
|
||||
public:
|
||||
StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
|
||||
id, std::move(attr));
|
||||
symbol->isPublic = true;
|
||||
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
mSymbolTable->mIdMap[id] = symbol.get();
|
||||
mSymbolTable->mSymbols.push_back(std::move(symbol));
|
||||
return *this;
|
||||
}
|
||||
|
||||
StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
|
||||
std::unique_ptr<Attribute> attr = {}) {
|
||||
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
|
||||
id, std::move(attr));
|
||||
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
|
||||
mSymbolTable->mIdMap[id] = symbol.get();
|
||||
mSymbolTable->mSymbols.push_back(std::move(symbol));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISymbolTable> build() {
|
||||
return std::move(mSymbolTable);
|
||||
}
|
||||
std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
32
tools/aapt2/test/Test.h
Normal file
32
tools/aapt2/test/Test.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef AAPT_TEST_TEST_H
|
||||
#define AAPT_TEST_TEST_H
|
||||
|
||||
#include "test/Builders.h"
|
||||
#include "test/Common.h"
|
||||
#include "test/Context.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace aapt {
|
||||
namespace test {
|
||||
|
||||
} // namespace test
|
||||
} // namespace aapt
|
||||
|
||||
#endif // AAPT_TEST_TEST_H
|
||||
@@ -68,7 +68,7 @@ struct Namespace : public BaseNode<Namespace> {
|
||||
};
|
||||
|
||||
struct AaptAttribute {
|
||||
ResourceId id;
|
||||
Maybe<ResourceId> id;
|
||||
aapt::Attribute attribute;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user