Merge "AAPT2: Support building, linking, and merging static libraries" into nyc-dev
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index f9d35ab..57a7692 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -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 @@
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
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index 5f9719e..2452a1d 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -422,10 +422,6 @@
}
class CompileContext : public IAaptContext {
-private:
- StdErrDiagnostics mDiagnostics;
- bool mVerbose = false;
-
public:
void setVerbose(bool val) {
mVerbose = val;
@@ -444,18 +440,24 @@
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;
+
};
/**
diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml
deleted file mode 100644
index d3b2fbe..0000000
--- a/tools/aapt2/data/AndroidManifest.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile
deleted file mode 100644
index 37012de..0000000
--- a/tools/aapt2/data/Makefile
+++ /dev/null
@@ -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
diff --git a/tools/aapt2/data/lib/AndroidManifest.xml b/tools/aapt2/data/lib/AndroidManifest.xml
deleted file mode 100644
index 08b468e..0000000
--- a/tools/aapt2/data/lib/AndroidManifest.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/lib/Makefile b/tools/aapt2/data/lib/Makefile
deleted file mode 100644
index 741be9a..0000000
--- a/tools/aapt2/data/lib/Makefile
+++ /dev/null
@@ -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
diff --git a/tools/aapt2/data/lib/res/layout/main.xml b/tools/aapt2/data/lib/res/layout/main.xml
deleted file mode 100644
index 187ed2d..0000000
--- a/tools/aapt2/data/lib/res/layout/main.xml
+++ /dev/null
@@ -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"/>
diff --git a/tools/aapt2/data/lib/res/raw/hello.txt b/tools/aapt2/data/lib/res/raw/hello.txt
deleted file mode 100644
index 44fc22b..0000000
--- a/tools/aapt2/data/lib/res/raw/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Oh howdy there
diff --git a/tools/aapt2/data/lib/res/values/styles.xml b/tools/aapt2/data/lib/res/values/styles.xml
deleted file mode 100644
index 4ce6333..0000000
--- a/tools/aapt2/data/lib/res/values/styles.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/res/drawable/image.xml b/tools/aapt2/data/res/drawable/image.xml
deleted file mode 100644
index 9b38739..0000000
--- a/tools/aapt2/data/res/drawable/image.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector />
diff --git a/tools/aapt2/data/res/layout-v21/main.xml b/tools/aapt2/data/res/layout-v21/main.xml
deleted file mode 100644
index 959b349..0000000
--- a/tools/aapt2/data/res/layout-v21/main.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/res/layout/main.xml b/tools/aapt2/data/res/layout/main.xml
deleted file mode 100644
index 8a5e9e8..0000000
--- a/tools/aapt2/data/res/layout/main.xml
+++ /dev/null
@@ -1,21 +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">
-
- <fragment class="android.test.sample.App$Inner" />
-
- <variable name="user" type="com.android.User" />
-
- <View xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/me"
- android:layout_width="1dp"
- android:onClick="doClick"
- android:text="@{user.name}"
- android:background="#ffffff"
- android:layout_height="match_parent"
- app:flags="complex|weak"
- android:colorAccent="#ffffff"/>
-</LinearLayout>
diff --git a/tools/aapt2/data/res/values-v4/styles.xml b/tools/aapt2/data/res/values-v4/styles.xml
deleted file mode 100644
index 979a82a..0000000
--- a/tools/aapt2/data/res/values-v4/styles.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/res/values/colors.xml b/tools/aapt2/data/res/values/colors.xml
deleted file mode 100644
index 89db5fb..0000000
--- a/tools/aapt2/data/res/values/colors.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/res/values/styles.xml b/tools/aapt2/data/res/values/styles.xml
deleted file mode 100644
index 2bbdad1..0000000
--- a/tools/aapt2/data/res/values/styles.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat">
- <style name="App">
- <item name="android:background">@color/primary</item>
- <item name="android:colorPrimary">@color/primary</item>
- <item name="android:colorPrimaryDark">@color/primary_dark</item>
- <item name="android:colorAccent">@color/accent</item>
- </style>
- <attr name="custom" format="reference" />
- <style name="Pop">
- <item name="custom">@android:drawable/btn_default</item>
- <item name="android:focusable">true</item>
- </style>
- <string name="yo">@string/wow</string>
-
- <declare-styleable name="View">
- <attr name="custom" />
- <attr name="decor">
- <enum name="no-border" value="0"/>
- <enum name="border" value="1"/>
- <enum name="shadow" value="2"/>
- </attr>
- </declare-styleable>
-
-</resources>
diff --git a/tools/aapt2/data/res/values/test.xml b/tools/aapt2/data/res/values/test.xml
deleted file mode 100644
index d7ab1c8..0000000
--- a/tools/aapt2/data/res/values/test.xml
+++ /dev/null
@@ -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>
diff --git a/tools/aapt2/data/resources.arsc b/tools/aapt2/data/resources.arsc
deleted file mode 100644
index 6a416df..0000000
--- a/tools/aapt2/data/resources.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/data/resources_base.arsc b/tools/aapt2/data/resources_base.arsc
deleted file mode 100644
index f9d0610..0000000
--- a/tools/aapt2/data/resources_base.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/data/resources_hdpi.arsc b/tools/aapt2/data/resources_hdpi.arsc
deleted file mode 100644
index 97232a3..0000000
--- a/tools/aapt2/data/resources_hdpi.arsc
+++ /dev/null
Binary files differ
diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp
index ad7de0a..56b9f9a 100644
--- a/tools/aapt2/dump/Dump.cpp
+++ b/tools/aapt2/dump/Dump.cpp
@@ -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 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 @@
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;
}
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 8219462..3eac633 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -144,9 +144,9 @@
}
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 @@
// 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 @@
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 @@
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 @@
// 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 @@
} 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);
}
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index 8648879..fef5ca3 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -32,7 +32,7 @@
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))
diff --git a/tools/aapt2/integration-tests/Android.mk b/tools/aapt2/integration-tests/Android.mk
new file mode 100644
index 0000000..6361f9b
--- /dev/null
+++ b/tools/aapt2/integration-tests/Android.mk
@@ -0,0 +1,2 @@
+LOCAL_PATH := $(call my-dir)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/aapt2/integration-tests/AppOne/Android.mk b/tools/aapt2/integration-tests/AppOne/Android.mk
new file mode 100644
index 0000000..859cc8c
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/Android.mk
@@ -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)
diff --git a/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
new file mode 100644
index 0000000..b6d8f2d
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/AndroidManifest.xml
@@ -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" />
diff --git a/tools/aapt2/integration-tests/AppOne/core b/tools/aapt2/integration-tests/AppOne/core
new file mode 100644
index 0000000..a9ae976
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/core
Binary files differ
diff --git a/tools/aapt2/data/res/drawable/icon.png b/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
similarity index 100%
rename from tools/aapt2/data/res/drawable/icon.png
rename to tools/aapt2/integration-tests/AppOne/res/drawable/icon.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml b/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
new file mode 100644
index 0000000..6132a75d
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/image.xml
@@ -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 />
diff --git a/tools/aapt2/data/res/drawable/test.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
similarity index 100%
rename from tools/aapt2/data/res/drawable/test.9.png
rename to tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml b/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
new file mode 100644
index 0000000..9f5a4a8
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/layout-v21/main.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/AppOne/res/layout/main.xml b/tools/aapt2/integration-tests/AppOne/res/layout/main.xml
new file mode 100644
index 0000000..ab1a251
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/layout/main.xml
@@ -0,0 +1,36 @@
+<?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">
+
+ <fragment class="android.test.sample.App$Inner" />
+
+ <variable name="user" type="com.android.User" />
+
+ <View xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/me"
+ android:layout_width="1dp"
+ android:onClick="doClick"
+ android:text="@{user.name}"
+ android:background="#ffffff"
+ android:layout_height="match_parent"
+ app:flags="complex|weak"
+ android:colorAccent="#ffffff"/>
+</LinearLayout>
diff --git a/tools/aapt2/data/res/raw/test.txt b/tools/aapt2/integration-tests/AppOne/res/raw/test.txt
similarity index 100%
rename from tools/aapt2/data/res/raw/test.txt
rename to tools/aapt2/integration-tests/AppOne/res/raw/test.txt
diff --git a/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml b/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
new file mode 100644
index 0000000..d8c11e2
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values-v4/styles.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/colors.xml b/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
new file mode 100644
index 0000000..4df5077
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values/colors.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/styles.xml b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
new file mode 100644
index 0000000..f05845c
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml
@@ -0,0 +1,40 @@
+<?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>
+ <item name="android:colorPrimary">@color/primary</item>
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+ <item name="android:colorAccent">@color/accent</item>
+ </style>
+ <attr name="custom" format="reference" />
+ <style name="Pop">
+ <item name="custom">@android:drawable/btn_default</item>
+ <item name="android:focusable">true</item>
+ </style>
+ <string name="yo">@string/wow</string>
+
+ <declare-styleable name="View">
+ <attr name="custom" />
+ <attr name="decor">
+ <enum name="no-border" value="0"/>
+ <enum name="border" value="1"/>
+ <enum name="shadow" value="2"/>
+ </attr>
+ </declare-styleable>
+
+</resources>
diff --git a/tools/aapt2/integration-tests/AppOne/res/values/test.xml b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
new file mode 100644
index 0000000..f4b7471
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/values/test.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java b/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
new file mode 100644
index 0000000..472b35a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/src/com/android/aapt/app/one/AppOne.java
@@ -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;
+}
+
diff --git a/tools/aapt2/integration-tests/StaticLibOne/Android.mk b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
new file mode 100644
index 0000000..d59dc60
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/Android.mk
@@ -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)
diff --git a/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
new file mode 100644
index 0000000..705047e7
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/AndroidManifest.xml
@@ -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" />
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml b/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
new file mode 100644
index 0000000..683c91c
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/res/layout/layout.xml
@@ -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" />
diff --git a/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
new file mode 100644
index 0000000..2b24544
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/res/values/values.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java b/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
new file mode 100644
index 0000000..cf48f67
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibOne/src/com/android/aapt/staticlib/one/StaticLibOne.java
@@ -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;
+}
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/Android.mk b/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
new file mode 100644
index 0000000..8b6eb41
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/Android.mk
@@ -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)
+
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml b/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
new file mode 100644
index 0000000..28f0699
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/AndroidManifest.xml
@@ -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" />
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml b/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
new file mode 100644
index 0000000..ba98307
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/res/layout/layout_two.xml
@@ -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" />
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml b/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
new file mode 100644
index 0000000..97bb2a5
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/res/values/values.xml
@@ -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>
diff --git a/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java b/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
new file mode 100644
index 0000000..7110dcd
--- /dev/null
+++ b/tools/aapt2/integration-tests/StaticLibTwo/src/com/android/aapt/staticlib/two/StaticLibTwo.java
@@ -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;
+}
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 329dac9..b3e7a02 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -92,9 +92,8 @@
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 {};
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 6e340a2..9e94d42 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -223,8 +223,10 @@
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;
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index d83f6def..b28415d 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -62,7 +62,9 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
- {
+ 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 @@
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 @@
// 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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
.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 @@
.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 @@
&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());
}
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index f40fbfb..18c47df 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -30,7 +30,7 @@
.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)
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index ef3fe4f..66eb0df 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -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 @@
* 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 @@
&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 @@
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 @@
* 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 @@
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::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 @@
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 @@
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 @@
}
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 @@
&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;
}
diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h
index a0eb00c..7993aaf 100644
--- a/tools/aapt2/link/ReferenceLinker.h
+++ b/tools/aapt2/link/ReferenceLinker.h
@@ -38,36 +38,36 @@
/**
* 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 @@
*/
static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference,
NameMangler* nameMangler,
- ISymbolTable* symbols,
+ SymbolTable* symbols,
CallSite* callSite,
std::string* outError);
@@ -92,7 +92,7 @@
* 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);
/**
diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp
index 8d324fe..76b2309 100644
--- a/tools/aapt2/link/ReferenceLinker_test.cpp
+++ b/tools/aapt2/link/ReferenceLinker_test.cpp
@@ -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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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 @@
.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;
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 5f11745..7471e15 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -34,10 +34,21 @@
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 @@
}
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.
*/
diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h
index b3c22dd..80c2a5e 100644
--- a/tools/aapt2/link/TableMerger.h
+++ b/tools/aapt2/link/TableMerger.h
@@ -65,13 +65,17 @@
/**
* 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 @@
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,
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index a26d763..568bc74 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -34,17 +34,10 @@
* 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 @@
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 @@
// 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 @@
<< *attribute);
mError = true;
}
+
} else {
mContext->getDiagnostics()->error(DiagMessage(source)
<< "attribute '" << package << ":"
@@ -150,6 +145,15 @@
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
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index 3bfaf91..af9098b 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -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 @@
.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h
index 3a88044..9affb83 100644
--- a/tools/aapt2/process/IResourceTableConsumer.h
+++ b/tools/aapt2/process/IResourceTableConsumer.h
@@ -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;
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index b6030a2..a8f9bfe 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -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 @@
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 @@
}
}
}
-
- 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 @@
}
// 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 @@
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 @@
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 {};
}
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 22096ed..8ea1c75 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -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 @@
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);
-class JoinedSymbolTableBuilder {
+ std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
+ std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
+
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
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index 1dc3b4f..34f31be 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -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::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
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 9856a00..86883f8 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -483,8 +483,13 @@
}
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();
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index b3d87d8..5d1b72b 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -178,10 +178,13 @@
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));
}
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 70a33f7..dd995d8 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -62,6 +62,17 @@
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 @@
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 @@
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
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 834caf8..8c56ebc 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -246,7 +246,7 @@
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;
}
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index e540cd7..96752d3 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -31,33 +31,16 @@
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 @@
}
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 @@
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 @@
}
};
-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
diff --git a/tools/aapt2/test/Test.h b/tools/aapt2/test/Test.h
new file mode 100644
index 0000000..d4845cf
--- /dev/null
+++ b/tools/aapt2/test/Test.h
@@ -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
diff --git a/tools/aapt2/xml/XmlDom.h b/tools/aapt2/xml/XmlDom.h
index 033b0a4d..b374d20 100644
--- a/tools/aapt2/xml/XmlDom.h
+++ b/tools/aapt2/xml/XmlDom.h
@@ -68,7 +68,7 @@
};
struct AaptAttribute {
- ResourceId id;
+ Maybe<ResourceId> id;
aapt::Attribute attribute;
};