diff options
67 files changed, 1386 insertions, 776 deletions
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index f9d35aba86d2..57a769221a8f 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 @@ LOCAL_C_INCLUDES += $(protoIncludes) include $(BUILD_HOST_EXECUTABLE) -endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK +ifeq ($(ONE_SHOT_MAKEFILE),) +include $(call all-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp index 5f9719e61a9a..2452a1d29410 100644 --- a/tools/aapt2/compile/Compile.cpp +++ b/tools/aapt2/compile/Compile.cpp @@ -422,10 +422,6 @@ static bool compileFile(IAaptContext* context, const CompileOptions& options, } class CompileContext : public IAaptContext { -private: - StdErrDiagnostics mDiagnostics; - bool mVerbose = false; - public: void setVerbose(bool val) { mVerbose = val; @@ -444,18 +440,24 @@ public: return nullptr; } - StringPiece16 getCompilationPackage() override { - return {}; + const std::u16string& getCompilationPackage() override { + static std::u16string empty; + return empty; } uint8_t getPackageId() override { return 0x0; } - ISymbolTable* getExternalSymbols() override { + SymbolTable* getExternalSymbols() override { abort(); return nullptr; } + +private: + StdErrDiagnostics mDiagnostics; + bool mVerbose = false; + }; /** diff --git a/tools/aapt2/data/AndroidManifest.xml b/tools/aapt2/data/AndroidManifest.xml deleted file mode 100644 index d3b2fbe515ae..000000000000 --- 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 37012decdf05..000000000000 --- 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 08b468ee6229..000000000000 --- 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 741be9a0e75a..000000000000 --- 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 187ed2d75da3..000000000000 --- 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 44fc22bebb39..000000000000 --- 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 4ce633394063..000000000000 --- 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 9b387390ea82..000000000000 --- 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 959b349bfaff..000000000000 --- 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/values-v4/styles.xml b/tools/aapt2/data/res/values-v4/styles.xml deleted file mode 100644 index 979a82a06df9..000000000000 --- 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 89db5fb3ec45..000000000000 --- 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/test.xml b/tools/aapt2/data/res/values/test.xml deleted file mode 100644 index d7ab1c8ddde9..000000000000 --- 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 Binary files differdeleted file mode 100644 index 6a416df0a96f..000000000000 --- a/tools/aapt2/data/resources.arsc +++ /dev/null diff --git a/tools/aapt2/data/resources_base.arsc b/tools/aapt2/data/resources_base.arsc Binary files differdeleted file mode 100644 index f9d06107b0e0..000000000000 --- a/tools/aapt2/data/resources_base.arsc +++ /dev/null diff --git a/tools/aapt2/data/resources_hdpi.arsc b/tools/aapt2/data/resources_hdpi.arsc Binary files differdeleted file mode 100644 index 97232a3317ba..000000000000 --- a/tools/aapt2/data/resources_hdpi.arsc +++ /dev/null diff --git a/tools/aapt2/dump/Dump.cpp b/tools/aapt2/dump/Dump.cpp index ad7de0ab2fbd..56b9f9a3e081 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 dumpCompiledTable(const pb::ResourceTable& pbTable, const Source& source, void tryDumpFile(IAaptContext* context, const std::string& filePath) { std::string err; + std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::create(filePath, &err); + if (zip) { + io::IFile* file = zip->findFile("resources.arsc.flat"); + if (file) { + std::unique_ptr<io::IData> data = file->openAsData(); + if (!data) { + context->getDiagnostics()->error(DiagMessage(filePath) + << "failed to open resources.arsc.flat"); + return; + } + + pb::ResourceTable pbTable; + if (!pbTable.ParseFromArray(data->data(), data->size())) { + context->getDiagnostics()->error(DiagMessage(filePath) + << "invalid resources.arsc.flat"); + return; + } + + std::unique_ptr<ResourceTable> table = deserializeTableFromPb( + pbTable, Source(filePath), context->getDiagnostics()); + if (table) { + DebugPrintTableOptions debugPrintTableOptions; + debugPrintTableOptions.showSources = true; + Debug::printTable(table.get(), debugPrintTableOptions); + } + } + return; + } + Maybe<android::FileMap> file = file::mmapPath(filePath, &err); if (!file) { context->getDiagnostics()->error(DiagMessage(filePath) << err); @@ -90,15 +120,16 @@ public: return nullptr; } - StringPiece16 getCompilationPackage() override { - return {}; + const std::u16string& getCompilationPackage() override { + static std::u16string empty; + return empty; } uint8_t getPackageId() override { return 0; } - ISymbolTable* getExternalSymbols() override { + SymbolTable* getExternalSymbols() override { abort(); return nullptr; } diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp index 8219462f8a73..3eac6331aaa2 100644 --- a/tools/aapt2/flatten/XmlFlattener.cpp +++ b/tools/aapt2/flatten/XmlFlattener.cpp @@ -144,9 +144,9 @@ struct XmlFlattenerVisitor : public xml::Visitor { } static bool cmpXmlAttributeById(const xml::Attribute* a, const xml::Attribute* b) { - if (a->compiledAttribute) { - if (b->compiledAttribute) { - return a->compiledAttribute.value().id < b->compiledAttribute.value().id; + if (a->compiledAttribute && a->compiledAttribute.value().id) { + if (b->compiledAttribute && b->compiledAttribute.value().id) { + return a->compiledAttribute.value().id.value() < b->compiledAttribute.value().id.value(); } return true; } else if (!b->compiledAttribute) { @@ -167,8 +167,8 @@ struct XmlFlattenerVisitor : public xml::Visitor { // Filter the attributes. for (xml::Attribute& attr : node->attributes) { - if (mOptions.maxSdkLevel && attr.compiledAttribute) { - size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id); + if (mOptions.maxSdkLevel && attr.compiledAttribute && attr.compiledAttribute.value().id) { + size_t sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id.value()); if (sdkLevel > mOptions.maxSdkLevel.value()) { continue; } @@ -191,8 +191,8 @@ struct XmlFlattenerVisitor : public xml::Visitor { uint16_t attributeIndex = 1; for (const xml::Attribute* xmlAttr : mFilteredAttrs) { // Assign the indices for specific attributes. - if (xmlAttr->compiledAttribute && - xmlAttr->compiledAttribute.value().id == kIdAttr) { + if (xmlAttr->compiledAttribute && xmlAttr->compiledAttribute.value().id && + xmlAttr->compiledAttribute.value().id.value() == kIdAttr) { flatElem->idIndex = util::hostToDevice16(attributeIndex); } else if (xmlAttr->namespaceUri.empty()) { if (xmlAttr->name == u"class") { @@ -208,7 +208,7 @@ struct XmlFlattenerVisitor : public xml::Visitor { flatAttr->rawValue.index = util::hostToDevice32(-1); - if (!xmlAttr->compiledAttribute) { + if (!xmlAttr->compiledAttribute || !xmlAttr->compiledAttribute.value().id) { // The attribute has no associated ResourceID, so the string order doesn't matter. addString(xmlAttr->name, kLowPriority, &flatAttr->name); } else { @@ -221,17 +221,17 @@ struct XmlFlattenerVisitor : public xml::Visitor { // Lookup the StringPool for this package and make the reference there. const xml::AaptAttribute& aaptAttr = xmlAttr->compiledAttribute.value(); - StringPool::Ref nameRef = mPackagePools[aaptAttr.id.packageId()].makeRef( - xmlAttr->name, StringPool::Context{ aaptAttr.id.id }); + StringPool::Ref nameRef = mPackagePools[aaptAttr.id.value().packageId()].makeRef( + xmlAttr->name, StringPool::Context{ aaptAttr.id.value().id }); // Add it to the list of strings to flatten. addString(nameRef, &flatAttr->name); + } - if (mOptions.keepRawValues) { - // Keep raw values (this is for static libraries). - // TODO(with a smarter inflater for binary XML, we can do without this). - addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue); - } + if (mOptions.keepRawValues || !xmlAttr->compiledValue) { + // Keep raw values if the value is not compiled or + // if we're building a static library (need symbols). + addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue); } if (xmlAttr->compiledValue) { @@ -240,7 +240,6 @@ struct XmlFlattenerVisitor : public xml::Visitor { } else { // Flatten as a regular string type. flatAttr->typedValue.dataType = android::Res_value::TYPE_STRING; - addString(xmlAttr->value, kLowPriority, &flatAttr->rawValue); addString(xmlAttr->value, kLowPriority, (ResStringPool_ref*) &flatAttr->typedValue.data); } diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp index 864887927328..fef5ca38fd65 100644 --- a/tools/aapt2/flatten/XmlFlattener_test.cpp +++ b/tools/aapt2/flatten/XmlFlattener_test.cpp @@ -32,7 +32,7 @@ public: mContext = test::ContextBuilder() .setCompilationPackage(u"com.app.test") .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) - .setSymbolTable(test::StaticSymbolTableBuilder() + .addSymbolSource(test::StaticSymbolSourceBuilder() .addSymbol(u"@android:attr/id", ResourceId(0x010100d0), test::AttributeBuilder().build()) .addSymbol(u"@com.app.test:id/id", ResourceId(0x7f020000)) diff --git a/tools/aapt2/integration-tests/Android.mk b/tools/aapt2/integration-tests/Android.mk new file mode 100644 index 000000000000..6361f9b8ae7d --- /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 000000000000..859cc8cbf4e5 --- /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 000000000000..b6d8f2d1b748 --- /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 Binary files differnew file mode 100644 index 000000000000..a9ae9768e5d6 --- /dev/null +++ b/tools/aapt2/integration-tests/AppOne/core diff --git a/tools/aapt2/data/res/drawable/icon.png b/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png Binary files differindex 4bff9b900643..4bff9b900643 100644 --- a/tools/aapt2/data/res/drawable/icon.png +++ b/tools/aapt2/integration-tests/AppOne/res/drawable/icon.png 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 000000000000..6132a75d85d0 --- /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 Binary files differindex 33daa117ea9d..33daa117ea9d 100644 --- a/tools/aapt2/data/res/drawable/test.9.png +++ b/tools/aapt2/integration-tests/AppOne/res/drawable/test.9.png 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 000000000000..9f5a4a85cbcf --- /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/data/res/layout/main.xml b/tools/aapt2/integration-tests/AppOne/res/layout/main.xml index 8a5e9e8ee58d..ab1a251a7d56 100644 --- a/tools/aapt2/data/res/layout/main.xml +++ b/tools/aapt2/integration-tests/AppOne/res/layout/main.xml @@ -1,4 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:support="http://schemas.android.com/apk/res/android.appcompat" android:id="@+id/view" diff --git a/tools/aapt2/data/res/raw/test.txt b/tools/aapt2/integration-tests/AppOne/res/raw/test.txt index b14df6442ea5..b14df6442ea5 100644 --- a/tools/aapt2/data/res/raw/test.txt +++ b/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 000000000000..d8c11e210eda --- /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 000000000000..4df5077051d2 --- /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/data/res/values/styles.xml b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml index 2bbdad1bcbf1..f05845cfab28 100644 --- a/tools/aapt2/data/res/values/styles.xml +++ b/tools/aapt2/integration-tests/AppOne/res/values/styles.xml @@ -1,4 +1,19 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + <resources xmlns:lib="http://schemas.android.com/apk/res/android.appcompat"> <style name="App"> <item name="android:background">@color/primary</item> 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 000000000000..f4b7471aefae --- /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 000000000000..472b35a781fe --- /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 000000000000..d59dc6090dec --- /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 000000000000..705047e71300 --- /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 000000000000..683c91cd9cf5 --- /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 000000000000..2b24544d3d83 --- /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 000000000000..cf48f67056cf --- /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 000000000000..8b6eb41b08cd --- /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 000000000000..28f069932452 --- /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 000000000000..ba9830708eb0 --- /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 000000000000..97bb2a53d9f6 --- /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 000000000000..7110dcdd017a --- /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 329dac9163ce..b3e7a02102e4 100644 --- a/tools/aapt2/io/ZipArchive.cpp +++ b/tools/aapt2/io/ZipArchive.cpp @@ -92,9 +92,8 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::create(const StringPiece& return {}; } - ZipString suffix(".flat"); void* cookie = nullptr; - result = StartIteration(collection->mHandle, &cookie, nullptr, &suffix); + result = StartIteration(collection->mHandle, &cookie, nullptr, nullptr); if (result != 0) { if (outError) *outError = ErrorCodeString(result); return {}; diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp index 6e340a2a2742..9e94d4276f5e 100644 --- a/tools/aapt2/java/JavaClassGenerator.cpp +++ b/tools/aapt2/java/JavaClassGenerator.cpp @@ -223,8 +223,10 @@ bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef continue; } - ResourceId id(package->id.value(), type->id.value(), entry->id.value()); - assert(id.isValid()); + ResourceId id; + if (package->id && type->id && entry->id) { + id = ResourceId(package->id.value(), type->id.value(), entry->id.value()); + } std::u16string unmangledPackage; std::u16string unmangledName = entry->name; diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp index d83f6def890c..b28415dc4a76 100644 --- a/tools/aapt2/link/Link.cpp +++ b/tools/aapt2/link/Link.cpp @@ -62,7 +62,9 @@ struct LinkOptions { std::set<std::u16string> extraJavaPackages; Maybe<std::string> generateProguardRulesPath; bool noAutoVersion = false; + bool noVersionVectors = false; bool staticLib = false; + bool noStaticLibPackages = false; bool generateNonFinalIds = false; bool outputToDirectory = false; bool autoAddOverlay = false; @@ -74,37 +76,58 @@ struct LinkOptions { TableSplitterOptions tableSplitterOptions; }; -struct LinkContext : public IAaptContext { - StdErrDiagnostics mDiagnostics; - std::unique_ptr<NameMangler> mNameMangler; - std::u16string mCompilationPackage; - uint8_t mPackageId; - std::unique_ptr<ISymbolTable> mSymbols; - bool mVerbose = false; +class LinkContext : public IAaptContext { +public: + LinkContext() : mNameMangler({}) { + } IDiagnostics* getDiagnostics() override { return &mDiagnostics; } NameMangler* getNameMangler() override { - return mNameMangler.get(); + return &mNameMangler; } - StringPiece16 getCompilationPackage() override { + void setNameManglerPolicy(const NameManglerPolicy& policy) { + mNameMangler = NameMangler(policy); + } + + const std::u16string& getCompilationPackage() override { return mCompilationPackage; } + void setCompilationPackage(const StringPiece16& packageName) { + mCompilationPackage = packageName.toString(); + } + uint8_t getPackageId() override { return mPackageId; } - ISymbolTable* getExternalSymbols() override { - return mSymbols.get(); + void setPackageId(uint8_t id) { + mPackageId = id; + } + + SymbolTable* getExternalSymbols() override { + return &mSymbols; } bool verbose() override { return mVerbose; } + + void setVerbose(bool val) { + mVerbose = val; + } + +private: + StdErrDiagnostics mDiagnostics; + NameMangler mNameMangler; + std::u16string mCompilationPackage; + uint8_t mPackageId = 0x0; + SymbolTable mSymbols; + bool mVerbose = false; }; static bool copyFileToArchive(io::IFile* file, const std::string& outPath, @@ -117,11 +140,19 @@ static bool copyFileToArchive(io::IFile* file, const std::string& outPath, return false; } - CompiledFileInputStream inputStream(data->data(), data->size()); - if (!inputStream.CompiledFile()) { - context->getDiagnostics()->error(DiagMessage(file->getSource()) - << "invalid compiled file header"); - return false; + const uint8_t* buffer = reinterpret_cast<const uint8_t*>(data->data()); + size_t bufferSize = data->size(); + + // If the file ends with .flat, we must strip off the CompiledFileHeader from it. + if (util::stringEndsWith<char>(file->getSource().path, ".flat")) { + CompiledFileInputStream inputStream(data->data(), data->size()); + if (!inputStream.CompiledFile()) { + context->getDiagnostics()->error(DiagMessage(file->getSource()) + << "invalid compiled file header"); + return false; + } + buffer = reinterpret_cast<const uint8_t*>(inputStream.data()); + bufferSize = inputStream.size(); } if (context->verbose()) { @@ -129,8 +160,7 @@ static bool copyFileToArchive(io::IFile* file, const std::string& outPath, } if (writer->startEntry(outPath, compressionFlags)) { - if (writer->writeEntry(reinterpret_cast<const uint8_t*>(inputStream.data()), - inputStream.size())) { + if (writer->writeEntry(buffer, bufferSize)) { if (writer->finishEntry()) { return true; } @@ -156,7 +186,7 @@ static bool flattenXml(xml::XmlResource* xmlRes, const StringPiece& path, Maybe< DiagMessage msg; msg << "writing " << path << " to archive"; if (maxSdkLevel) { - msg << " maxSdkLevel=" << maxSdkLevel.value(); + msg << " maxSdkLevel=" << maxSdkLevel.value() << " keepRawValues=" << keepRawValues; } context->getDiagnostics()->note(msg); } @@ -346,7 +376,7 @@ std::unique_ptr<xml::XmlResource> ResourceFileFlattener::linkAndVersionXmlFile( } ResourceFile versionedFileDesc = xmlRes->file; - versionedFileDesc.config.sdkVersion = sdkLevel; + versionedFileDesc.config.sdkVersion = (uint16_t) sdkLevel; if (mContext->verbose()) { mContext->getDiagnostics()->note(DiagMessage(versionedFileDesc.source) @@ -474,39 +504,61 @@ bool ResourceFileFlattener::flatten(ResourceTable* table, IArchiveWriter* archiv class LinkCommand { public: LinkCommand(LinkContext* context, const LinkOptions& options) : - mOptions(options), mContext(context), mFinalTable(), mFileCollection(nullptr) { - std::unique_ptr<io::FileCollection> fileCollection = - util::make_unique<io::FileCollection>(); - - // Get a pointer to the FileCollection for convenience, but it will be owned by the vector. - mFileCollection = fileCollection.get(); - - // Move it to the collection. - mCollections.push_back(std::move(fileCollection)); + mOptions(options), mContext(context), mFinalTable(), + mFileCollection(util::make_unique<io::FileCollection>()) { } /** * Creates a SymbolTable that loads symbols from the various APKs and caches the * results for faster lookup. */ - std::unique_ptr<ISymbolTable> createSymbolTableFromIncludePaths() { - AssetManagerSymbolTableBuilder builder; + bool loadSymbolsFromIncludePaths() { + std::unique_ptr<AssetManagerSymbolSource> assetSource = + util::make_unique<AssetManagerSymbolSource>(); for (const std::string& path : mOptions.includePaths) { if (mContext->verbose()) { mContext->getDiagnostics()->note(DiagMessage(path) << "loading include path"); } - std::unique_ptr<android::AssetManager> assetManager = - util::make_unique<android::AssetManager>(); - int32_t cookie = 0; - if (!assetManager->addAssetPath(android::String8(path.data(), path.size()), &cookie)) { + // First try to load the file as a static lib. + std::string errorStr; + std::unique_ptr<ResourceTable> staticInclude = loadStaticLibrary(path, &errorStr); + if (staticInclude) { + if (!mOptions.staticLib) { + // Can't include static libraries when not building a static library. + mContext->getDiagnostics()->error( + DiagMessage(path) << "can't include static library when building app"); + return false; + } + + // If we are using --no-static-lib-packages, we need to rename the package of this + // table to our compilation package. + if (mOptions.noStaticLibPackages) { + if (ResourceTablePackage* pkg = staticInclude->findPackageById(0x7f)) { + pkg->name = mContext->getCompilationPackage(); + } + } + + mContext->getExternalSymbols()->appendSource( + util::make_unique<ResourceTableSymbolSource>(staticInclude.get())); + + mStaticTableIncludes.push_back(std::move(staticInclude)); + + } else if (!errorStr.empty()) { + // We had an error with reading, so fail. + mContext->getDiagnostics()->error(DiagMessage(path) << errorStr); + return false; + } + + if (!assetSource->addAssetPath(path)) { mContext->getDiagnostics()->error( DiagMessage(path) << "failed to load include path"); - return {}; + return false; } - builder.add(std::move(assetManager)); } - return builder.build(); + + mContext->getExternalSymbols()->appendSource(std::move(assetSource)); + return true; } Maybe<AppInfo> extractAppInfoFromManifest(xml::XmlResource* xmlRes) { @@ -571,6 +623,35 @@ public: return !error; } + /** + * Returns true if no IDs have been set, false otherwise. + */ + bool verifyNoIdsSet() { + for (const auto& package : mFinalTable.packages) { + for (const auto& type : package->types) { + if (type->id) { + mContext->getDiagnostics()->error(DiagMessage() << "type " << type->type + << " has ID " << std::hex + << (int) type->id.value() + << std::dec << " assigned"); + return false; + } + + for (const auto& entry : type->entries) { + if (entry->id) { + ResourceNameRef resName(package->name, type->type, entry->name); + mContext->getDiagnostics()->error(DiagMessage() << "entry " << resName + << " has ID " << std::hex + << (int) entry->id.value() + << std::dec << " assigned"); + return false; + } + } + } + } + return true; + } + std::unique_ptr<IArchiveWriter> makeArchiveWriter() { if (mOptions.outputToDirectory) { return createDirectoryArchiveWriter(mContext->getDiagnostics(), mOptions.outputPath); @@ -599,6 +680,32 @@ public: return false; } + bool flattenTableToPb(ResourceTable* table, IArchiveWriter* writer) { + // Create the file/zip entry. + if (!writer->startEntry("resources.arsc.flat", 0)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed to open"); + return false; + } + + std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table); + + // Wrap our IArchiveWriter with an adaptor that implements the ZeroCopyOutputStream + // interface. + { + google::protobuf::io::CopyingOutputStreamAdaptor adaptor(writer); + + if (!pbTable->SerializeToZeroCopyStream(&adaptor)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed to write"); + return false; + } + } + + if (!writer->finishEntry()) { + mContext->getDiagnostics()->error(DiagMessage() << "failed to finish entry"); + return false; + } + return true; + } bool writeJavaFile(ResourceTable* table, const StringPiece16& packageNameToGenerate, const StringPiece16& outPackage, JavaClassGeneratorOptions javaOptions) { @@ -674,18 +781,91 @@ public: return true; } - bool mergeStaticLibrary(const std::string& input) { - // TODO(adamlesinski): Load resources from a static library APK and merge the table into - // TableMerger. - mContext->getDiagnostics()->warn(DiagMessage() - << "linking static libraries not supported yet: " - << input); + std::unique_ptr<ResourceTable> loadStaticLibrary(const std::string& input, + std::string* outError) { + std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create( + input, outError); + if (!collection) { + return {}; + } + return loadTablePbFromCollection(collection.get()); + } + + std::unique_ptr<ResourceTable> loadTablePbFromCollection(io::IFileCollection* collection) { + io::IFile* file = collection->findFile("resources.arsc.flat"); + if (!file) { + return {}; + } + + std::unique_ptr<io::IData> data = file->openAsData(); + return loadTableFromPb(file->getSource(), data->data(), data->size(), + mContext->getDiagnostics()); + } + + bool mergeStaticLibrary(const std::string& input, bool override) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note(DiagMessage() << "merging static library " << input); + } + + std::string errorStr; + std::unique_ptr<io::ZipFileCollection> collection = + io::ZipFileCollection::create(input, &errorStr); + if (!collection) { + mContext->getDiagnostics()->error(DiagMessage(input) << errorStr); + return false; + } + + std::unique_ptr<ResourceTable> table = loadTablePbFromCollection(collection.get()); + if (!table) { + mContext->getDiagnostics()->error(DiagMessage(input) << "invalid static library"); + return false; + } + + ResourceTablePackage* pkg = table->findPackageById(0x7f); + if (!pkg) { + mContext->getDiagnostics()->error(DiagMessage(input) + << "static library has no package"); + return false; + } + + bool result; + if (mOptions.noStaticLibPackages) { + // Merge all resources as if they were in the compilation package. This is the old + // behaviour of aapt. + + // Add the package to the set of --extra-packages so we emit an R.java for each + // library package. + if (!pkg->name.empty()) { + mOptions.extraJavaPackages.insert(pkg->name); + } + + pkg->name = u""; + if (override) { + result = mTableMerger->mergeOverlay(Source(input), table.get(), collection.get()); + } else { + result = mTableMerger->merge(Source(input), table.get(), collection.get()); + } + + } else { + // This is the proper way to merge libraries, where the package name is preserved + // and resource names are mangled. + result = mTableMerger->mergeAndMangle(Source(input), pkg->name, table.get(), + collection.get()); + } + + if (!result) { + return false; + } + + // Make sure to move the collection into the set of IFileCollections. + mCollections.push_back(std::move(collection)); return true; } bool mergeResourceTable(io::IFile* file, bool override) { if (mContext->verbose()) { - mContext->getDiagnostics()->note(DiagMessage() << "linking " << file->getSource()); + mContext->getDiagnostics()->note(DiagMessage() << "merging resource table " + << file->getSource()); } std::unique_ptr<io::IData> data = file->openAsData(); @@ -711,13 +891,14 @@ public: return result; } - bool mergeCompiledFile(io::IFile* file, std::unique_ptr<ResourceFile> fileDesc, bool overlay) { + bool mergeCompiledFile(io::IFile* file, ResourceFile* fileDesc, bool override) { if (mContext->verbose()) { - mContext->getDiagnostics()->note(DiagMessage() << "adding " << file->getSource()); + mContext->getDiagnostics()->note(DiagMessage() << "merging compiled file " + << file->getSource()); } bool result = false; - if (overlay) { + if (override) { result = mTableMerger->mergeFileOverlay(*fileDesc, file); } else { result = mTableMerger->mergeFile(*fileDesc, file); @@ -730,7 +911,7 @@ public: // Add the exports of this file to the table. for (SourcedResourceName& exportedSymbol : fileDesc->exportedSymbols) { if (exportedSymbol.name.package.empty()) { - exportedSymbol.name.package = mContext->getCompilationPackage().toString(); + exportedSymbol.name.package = mContext->getCompilationPackage(); } ResourceNameRef resName = exportedSymbol.name; @@ -743,11 +924,9 @@ public: std::unique_ptr<Id> id = util::make_unique<Id>(); id->setSource(fileDesc->source.withLine(exportedSymbol.line)); - bool result = mFinalTable.addResourceAllowMangled(resName, - ConfigDescription::defaultConfig(), - std::string(), - std::move(id), - mContext->getDiagnostics()); + bool result = mFinalTable.addResourceAllowMangled( + resName, ConfigDescription::defaultConfig(), std::string(), std::move(id), + mContext->getDiagnostics()); if (!result) { return false; } @@ -756,12 +935,21 @@ public: } /** - * Creates an io::IFileCollection from the ZIP archive and processes the files within. + * Takes a path to load as a ZIP file and merges the files within into the master ResourceTable. + * If override is true, conflicting resources are allowed to override each other, in order of + * last seen. + * + * An io::IFileCollection is created from the ZIP file and added to the set of + * io::IFileCollections that are open. */ bool mergeArchive(const std::string& input, bool override) { + if (mContext->verbose()) { + mContext->getDiagnostics()->note(DiagMessage() << "merging archive " << input); + } + std::string errorStr; - std::unique_ptr<io::ZipFileCollection> collection = io::ZipFileCollection::create( - input, &errorStr); + std::unique_ptr<io::ZipFileCollection> collection = + io::ZipFileCollection::create(input, &errorStr); if (!collection) { mContext->getDiagnostics()->error(DiagMessage(input) << errorStr); return false; @@ -769,7 +957,7 @@ public: bool error = false; for (auto iter = collection->iterator(); iter->hasNext(); ) { - if (!processFile(iter->next(), override)) { + if (!mergeFile(iter->next(), override)) { error = true; } } @@ -779,22 +967,45 @@ public: return !error; } - bool processFile(const std::string& path, bool override) { + /** + * Takes a path to load and merge into the master ResourceTable. If override is true, + * conflicting resources are allowed to override each other, in order of last seen. + * + * If the file path ends with .flata, .jar, .jack, or .zip the file is treated as ZIP archive + * and the files within are merged individually. + * + * Otherwise the files is processed on its own. + */ + bool mergePath(const std::string& path, bool override) { if (util::stringEndsWith<char>(path, ".flata") || util::stringEndsWith<char>(path, ".jar") || util::stringEndsWith<char>(path, ".jack") || util::stringEndsWith<char>(path, ".zip")) { return mergeArchive(path, override); + } else if (util::stringEndsWith<char>(path, ".apk")) { + return mergeStaticLibrary(path, override); } io::IFile* file = mFileCollection->insertFile(path); - return processFile(file, override); + return mergeFile(file, override); } - bool processFile(io::IFile* file, bool override) { + /** + * Takes a file to load and merge into the master ResourceTable. If override is true, + * conflicting resources are allowed to override each other, in order of last seen. + * + * If the file ends with .arsc.flat, then it is loaded as a ResourceTable and merged into the + * master ResourceTable. If the file ends with .flat, then it is treated like a compiled file + * and the header data is read and merged into the final ResourceTable. + * + * All other file types are ignored. This is because these files could be coming from a zip, + * where we could have other files like classes.dex. + */ + bool mergeFile(io::IFile* file, bool override) { const Source& src = file->getSource(); if (util::stringEndsWith<char>(src.path, ".arsc.flat")) { return mergeResourceTable(file, override); + } else if (util::stringEndsWith<char>(src.path, ".flat")){ // Try opening the file and looking for an Export header. std::unique_ptr<io::IData> data = file->openAsData(); @@ -806,9 +1017,8 @@ public: std::unique_ptr<ResourceFile> resourceFile = loadFileExportHeader( src, data->data(), data->size(), mContext->getDiagnostics()); if (resourceFile) { - return mergeCompiledFile(file, std::move(resourceFile), override); + return mergeCompiledFile(file, resourceFile.get(), override); } - return false; } @@ -826,32 +1036,30 @@ public: } if (Maybe<AppInfo> maybeAppInfo = extractAppInfoFromManifest(manifestXml.get())) { - mContext->mCompilationPackage = maybeAppInfo.value().package; + mContext->setCompilationPackage(maybeAppInfo.value().package); } else { mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath) << "no package specified in <manifest> tag"); return 1; } - if (!util::isJavaPackageName(mContext->mCompilationPackage)) { + if (!util::isJavaPackageName(mContext->getCompilationPackage())) { mContext->getDiagnostics()->error(DiagMessage(mOptions.manifestPath) << "invalid package name '" - << mContext->mCompilationPackage + << mContext->getCompilationPackage() << "'"); return 1; } - mContext->mNameMangler = util::make_unique<NameMangler>( - NameManglerPolicy{ mContext->mCompilationPackage }); + mContext->setNameManglerPolicy(NameManglerPolicy{ mContext->getCompilationPackage() }); - if (mContext->mCompilationPackage == u"android") { - mContext->mPackageId = 0x01; + if (mContext->getCompilationPackage() == u"android") { + mContext->setPackageId(0x01); } else { - mContext->mPackageId = 0x7f; + mContext->setPackageId(0x7f); } - mContext->mSymbols = createSymbolTableFromIncludePaths(); - if (!mContext->mSymbols) { + if (!loadSymbolsFromIncludePaths()) { return 1; } @@ -861,20 +1069,21 @@ public: if (mContext->verbose()) { mContext->getDiagnostics()->note( - DiagMessage() << "linking package '" << mContext->mCompilationPackage << "' " - << "with package ID " << std::hex << (int) mContext->mPackageId); + DiagMessage() << "linking package '" << mContext->getCompilationPackage() + << "' with package ID " << std::hex + << (int) mContext->getPackageId()); } for (const std::string& input : inputFiles) { - if (!processFile(input, false)) { + if (!mergePath(input, false)) { mContext->getDiagnostics()->error(DiagMessage() << "failed parsing input"); return 1; } } for (const std::string& input : mOptions.overlayFiles) { - if (!processFile(input, true)) { + if (!mergePath(input, true)) { mContext->getDiagnostics()->error(DiagMessage() << "failed parsing overlays"); return 1; } @@ -893,20 +1102,28 @@ public: } } - { + if (!mOptions.staticLib) { + // Assign IDs if we are building a regular app. IdAssigner idAssigner; if (!idAssigner.consume(mContext, &mFinalTable)) { mContext->getDiagnostics()->error(DiagMessage() << "failed assigning IDs"); return 1; } + } else { + // Static libs are merged with other apps, and ID collisions are bad, so verify that + // no IDs have been set. + if (!verifyNoIdsSet()) { + return 1; + } } - mContext->mNameMangler = util::make_unique<NameMangler>(NameManglerPolicy{ - mContext->mCompilationPackage, mTableMerger->getMergedPackages() }); - mContext->mSymbols = JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(&mFinalTable)) - .addSymbolTable(std::move(mContext->mSymbols)) - .build(); + // Add the names to mangle based on our source merge earlier. + mContext->setNameManglerPolicy(NameManglerPolicy{ + mContext->getCompilationPackage(), mTableMerger->getMergedPackages() }); + + // Add our table to the symbol table. + mContext->getExternalSymbols()->prependSource( + util::make_unique<ResourceTableSymbolSource>(&mFinalTable)); { ReferenceLinker linker; @@ -915,20 +1132,32 @@ public: return 1; } - ProductFilter productFilter(mOptions.products); - if (!productFilter.consume(mContext, &mFinalTable)) { - mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); - return 1; - } + if (mOptions.staticLib) { + if (!mOptions.products.empty()) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't select products when building static library"); + } - // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file - // level. - TableSplitter tableSplitter({}, mOptions.tableSplitterOptions); - if (!tableSplitter.verifySplitConstraints(mContext)) { - return 1; - } + if (mOptions.tableSplitterOptions.configFilter != nullptr || + mOptions.tableSplitterOptions.preferredDensity) { + mContext->getDiagnostics()->warn( + DiagMessage() << "can't strip resources when building static library"); + } + } else { + ProductFilter productFilter(mOptions.products); + if (!productFilter.consume(mContext, &mFinalTable)) { + mContext->getDiagnostics()->error(DiagMessage() << "failed stripping products"); + return 1; + } - tableSplitter.splitTable(&mFinalTable); + // TODO(adamlesinski): Actually pass in split constraints and handle splits at the file + // level. + TableSplitter tableSplitter({}, mOptions.tableSplitterOptions); + if (!tableSplitter.verifySplitConstraints(mContext)) { + return 1; + } + tableSplitter.splitTable(&mFinalTable); + } } proguard::KeepSet proguardKeepSet; @@ -949,7 +1178,7 @@ public: // AndroidManifest.xml has no resource name, but the CallSite is built from the name // (aka, which package the AndroidManifest.xml is coming from). // So we give it a package name so it can see local resources. - manifestXml->file.name.package = mContext->getCompilationPackage().toString(); + manifestXml->file.name.package = mContext->getCompilationPackage(); XmlReferenceLinker manifestLinker; if (manifestLinker.consume(mContext, manifestXml.get())) { @@ -993,7 +1222,7 @@ public: return 1; } - if (!mOptions.noAutoVersion) { + if (!mOptions.staticLib && !mOptions.noAutoVersion) { AutoVersioner versioner; if (!versioner.consume(mContext, &mFinalTable)) { mContext->getDiagnostics()->error(DiagMessage() << "failed versioning styles"); @@ -1001,9 +1230,18 @@ public: } } - if (!flattenTable(&mFinalTable, archiveWriter.get())) { - mContext->getDiagnostics()->error(DiagMessage() << "failed to write resources.arsc"); - return 1; + if (mOptions.staticLib) { + if (!flattenTableToPb(&mFinalTable, archiveWriter.get())) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc.flat"); + return 1; + } + } else { + if (!flattenTable(&mFinalTable, archiveWriter.get())) { + mContext->getDiagnostics()->error(DiagMessage() + << "failed to write resources.arsc"); + return 1; + } } if (mOptions.generateJavaClassPath) { @@ -1065,14 +1303,17 @@ private: LinkContext* mContext; ResourceTable mFinalTable; - ResourceTable mLocalFileTable; std::unique_ptr<TableMerger> mTableMerger; // A pointer to the FileCollection representing the filesystem (not archives). - io::FileCollection* mFileCollection; + std::unique_ptr<io::FileCollection> mFileCollection; // A vector of IFileCollections. This is mainly here to keep ownership of the collections. std::vector<std::unique_ptr<io::IFileCollection>> mCollections; + + // A vector of ResourceTables. This is here to retain ownership, so that the SymbolTable + // can use these. + std::vector<std::unique_ptr<ResourceTable>> mStaticTableIncludes; }; int link(const std::vector<StringPiece>& args) { @@ -1089,6 +1330,7 @@ int link(const std::vector<StringPiece>& args) { Maybe<std::string> productList; bool legacyXFlag = false; bool requireLocalization = false; + bool verbose = false; Flags flags = Flags() .requiredFlag("-o", "Output path", &options.outputPath) .requiredFlag("--manifest", "Path to the Android manifest to build", @@ -1104,6 +1346,10 @@ int link(const std::vector<StringPiece>& args) { .optionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning", &options.noAutoVersion) + .optionalSwitch("--no-version-vectors", + "Disables automatic versioning of vector drawables. Use this only\n" + "when building with vector drawable support library", + &options.noVersionVectors) .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01", &legacyXFlag) .optionalSwitch("-z", "Require localization of strings marked 'suggested'", @@ -1127,6 +1373,9 @@ int link(const std::vector<StringPiece>& args) { .optionalFlag("--version-name", "Version name to inject into the AndroidManifest.xml " "if none is present", &versionName) .optionalSwitch("--static-lib", "Generate a static Android library", &options.staticLib) + .optionalSwitch("--no-static-lib-packages", + "Merge all library resources under the app's package", + &options.noStaticLibPackages) .optionalSwitch("--non-final-ids", "Generates R.java without the final modifier.\n" "This is implied when --static-lib is specified.", &options.generateNonFinalIds) @@ -1148,12 +1397,16 @@ int link(const std::vector<StringPiece>& args) { &renameInstrumentationTargetPackage) .optionalFlagList("-0", "File extensions not to compress", &options.extensionsToNotCompress) - .optionalSwitch("-v", "Enables verbose logging", &context.mVerbose); + .optionalSwitch("-v", "Enables verbose logging", &verbose); if (!flags.parse("aapt2 link", args, &std::cerr)) { return 1; } + if (verbose) { + context.setVerbose(verbose); + } + if (privateSymbolsPackage) { options.privateSymbols = util::utf8ToUtf16(privateSymbolsPackage.value()); } diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp index f40fbfb2e81a..18c47dff33ec 100644 --- a/tools/aapt2/link/ManifestFixer_test.cpp +++ b/tools/aapt2/link/ManifestFixer_test.cpp @@ -30,7 +30,7 @@ struct ManifestFixerTest : public ::testing::Test { .setCompilationPackage(u"android") .setPackageId(0x01) .setNameManglerPolicy(NameManglerPolicy{ u"android" }) - .setSymbolTable(test::StaticSymbolTableBuilder() + .addSymbolSource(test::StaticSymbolSourceBuilder() .addSymbol(u"@android:attr/package", ResourceId(0x01010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_STRING) diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp index ef3fe4f58d41..66eb0df048db 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 @@ namespace { * NOTE: All of the entries in the ResourceTable must be assigned IDs. */ class ReferenceLinkerVisitor : public ValueVisitor { -private: - IAaptContext* mContext; - ISymbolTable* mSymbols; - xml::IPackageDeclStack* mPackageDecls; - StringPool* mStringPool; - CallSite* mCallSite; - bool mError = false; - - /** - * Transform a RawString value into a more specific, appropriate value, based on the - * Attribute. If a non RawString value is passed in, this is an identity transform. - */ - std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value, - const Attribute* attr) { - if (RawString* rawString = valueCast<RawString>(value.get())) { - std::unique_ptr<Item> transformed = - ResourceUtils::parseItemForAttribute(*rawString->value, attr); - - // If we could not parse as any specific type, try a basic STRING. - if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) { - util::StringBuilder stringBuilder; - stringBuilder.append(*rawString->value); - if (stringBuilder) { - transformed = util::make_unique<String>( - mStringPool->makeRef(stringBuilder.str())); - } - } - - if (transformed) { - return transformed; - } - }; - return value; - } - public: using ValueVisitor::visit; - ReferenceLinkerVisitor(IAaptContext* context, ISymbolTable* symbols, StringPool* stringPool, + ReferenceLinkerVisitor(IAaptContext* context, SymbolTable* symbols, StringPool* stringPool, xml::IPackageDeclStack* decl,CallSite* callSite) : mContext(context), mSymbols(symbols), mPackageDecls(decl), mStringPool(stringPool), mCallSite(callSite) { @@ -114,10 +78,11 @@ public: &transformedReference); // Find the attribute in the symbol table and check if it is visible from this callsite. - const ISymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility( + const SymbolTable::Symbol* symbol = ReferenceLinker::resolveAttributeCheckVisibility( transformedReference, mContext->getNameMangler(), mSymbols, mCallSite, &errStr); if (symbol) { // Assign our style key the correct ID. + // The ID may not exist. entry.key.id = symbol->id; // Try to convert the value to a more specific, typed value based on the @@ -156,6 +121,41 @@ public: bool hasError() { return mError; } + +private: + IAaptContext* mContext; + SymbolTable* mSymbols; + xml::IPackageDeclStack* mPackageDecls; + StringPool* mStringPool; + CallSite* mCallSite; + bool mError = false; + + /** + * Transform a RawString value into a more specific, appropriate value, based on the + * Attribute. If a non RawString value is passed in, this is an identity transform. + */ + std::unique_ptr<Item> parseValueWithAttribute(std::unique_ptr<Item> value, + const Attribute* attr) { + if (RawString* rawString = valueCast<RawString>(value.get())) { + std::unique_ptr<Item> transformed = + ResourceUtils::parseItemForAttribute(*rawString->value, attr); + + // If we could not parse as any specific type, try a basic STRING. + if (!transformed && (attr->typeMask & android::ResTable_map::TYPE_STRING)) { + util::StringBuilder stringBuilder; + stringBuilder.append(*rawString->value); + if (stringBuilder) { + transformed = util::make_unique<String>( + mStringPool->makeRef(stringBuilder.str())); + } + } + + if (transformed) { + return transformed; + } + }; + return value; + } }; } // namespace @@ -164,13 +164,13 @@ public: * The symbol is visible if it is public, or if the reference to it is requesting private access * or if the callsite comes from the same package. */ -bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref, +bool ReferenceLinker::isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref, const CallSite& callSite) { if (!symbol.isPublic && !ref.privateReference) { if (ref.name) { return callSite.resource.package == ref.name.value().package; - } else if (ref.id) { - return ref.id.value().packageId() == symbol.id.packageId(); + } else if (ref.id && symbol.id) { + return ref.id.value().packageId() == symbol.id.value().packageId(); } else { return false; } @@ -178,9 +178,9 @@ bool ReferenceLinker::isSymbolVisible(const ISymbolTable::Symbol& symbol, const return true; } -const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference, - NameMangler* mangler, - ISymbolTable* symbols) { +const SymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& reference, + NameMangler* mangler, + SymbolTable* symbols) { if (reference.name) { Maybe<ResourceName> mangled = mangler->mangleName(reference.name.value()); return symbols->findByName(mangled ? mangled.value() : reference.name.value()); @@ -191,10 +191,10 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveSymbol(const Reference& refe } } -const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility( - const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols, +const SymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility( + const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols, CallSite* callSite, std::string* outError) { - const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); + const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); if (!symbol) { if (outError) *outError = "not found"; return nullptr; @@ -207,12 +207,12 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveSymbolCheckVisibility( return symbol; } -const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility( - const Reference& reference, NameMangler* nameMangler, ISymbolTable* symbols, +const SymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility( + const Reference& reference, NameMangler* nameMangler, SymbolTable* symbols, CallSite* callSite, std::string* outError) { - const ISymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler, - symbols, callSite, - outError); + const SymbolTable::Symbol* symbol = resolveSymbolCheckVisibility(reference, nameMangler, + symbols, callSite, + outError); if (!symbol) { return nullptr; } @@ -226,10 +226,10 @@ const ISymbolTable::Symbol* ReferenceLinker::resolveAttributeCheckVisibility( Maybe<xml::AaptAttribute> ReferenceLinker::compileXmlAttribute(const Reference& reference, NameMangler* nameMangler, - ISymbolTable* symbols, + SymbolTable* symbols, CallSite* callSite, std::string* outError) { - const ISymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); + const SymbolTable::Symbol* symbol = resolveSymbol(reference, nameMangler, symbols); if (!symbol) { return {}; } @@ -256,7 +256,7 @@ void ReferenceLinker::writeResourceName(DiagMessage* outMsg, const Reference& or } bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context, - ISymbolTable* symbols, xml::IPackageDeclStack* decls, + SymbolTable* symbols, xml::IPackageDeclStack* decls, CallSite* callSite) { assert(reference); assert(reference->name || reference->id); @@ -266,9 +266,12 @@ bool ReferenceLinker::linkReference(Reference* reference, IAaptContext* context, &transformedReference); std::string errStr; - const ISymbolTable::Symbol* s = resolveSymbolCheckVisibility( + const SymbolTable::Symbol* s = resolveSymbolCheckVisibility( transformedReference, context->getNameMangler(), symbols, callSite, &errStr); if (s) { + // The ID may not exist. This is fine because of the possibility of building against + // libraries without assigned IDs. + // Ex: Linking against own resources when building a static library. reference->id = s->id; return true; } diff --git a/tools/aapt2/link/ReferenceLinker.h b/tools/aapt2/link/ReferenceLinker.h index a0eb00c85b70..7993aaf39e47 100644 --- a/tools/aapt2/link/ReferenceLinker.h +++ b/tools/aapt2/link/ReferenceLinker.h @@ -38,36 +38,36 @@ struct ReferenceLinker : public IResourceTableConsumer { /** * Returns true if the symbol is visible by the reference and from the callsite. */ - static bool isSymbolVisible(const ISymbolTable::Symbol& symbol, const Reference& ref, + static bool isSymbolVisible(const SymbolTable::Symbol& symbol, const Reference& ref, const CallSite& callSite); /** * Performs name mangling and looks up the resource in the symbol table. Returns nullptr * if the symbol was not found. */ - static const ISymbolTable::Symbol* resolveSymbol(const Reference& reference, - NameMangler* mangler, ISymbolTable* symbols); + static const SymbolTable::Symbol* resolveSymbol(const Reference& reference, + NameMangler* mangler, SymbolTable* symbols); /** * Performs name mangling and looks up the resource in the symbol table. If the symbol is * not visible by the reference at the callsite, nullptr is returned. outError holds * the error message. */ - static const ISymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference, - NameMangler* nameMangler, - ISymbolTable* symbols, - CallSite* callSite, - std::string* outError); + static const SymbolTable::Symbol* resolveSymbolCheckVisibility(const Reference& reference, + NameMangler* nameMangler, + SymbolTable* symbols, + CallSite* callSite, + std::string* outError); /** * Same as resolveSymbolCheckVisibility(), but also makes sure the symbol is an attribute. * That is, the return value will have a non-null value for ISymbolTable::Symbol::attribute. */ - static const ISymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference, - NameMangler* nameMangler, - ISymbolTable* symbols, - CallSite* callSite, - std::string* outError); + static const SymbolTable::Symbol* resolveAttributeCheckVisibility(const Reference& reference, + NameMangler* nameMangler, + SymbolTable* symbols, + CallSite* callSite, + std::string* outError); /** * Resolves the attribute reference and returns an xml::AaptAttribute if successful. @@ -75,7 +75,7 @@ struct ReferenceLinker : public IResourceTableConsumer { */ static Maybe<xml::AaptAttribute> compileXmlAttribute(const Reference& reference, NameMangler* nameMangler, - ISymbolTable* symbols, + SymbolTable* symbols, CallSite* callSite, std::string* outError); @@ -92,7 +92,7 @@ struct ReferenceLinker : public IResourceTableConsumer { * to the reference at the callsite, the reference is updated with an ID. * Returns false on failure, and an error message is logged to the IDiagnostics in the context. */ - static bool linkReference(Reference* reference, IAaptContext* context, ISymbolTable* symbols, + static bool linkReference(Reference* reference, IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls, CallSite* callSite); /** diff --git a/tools/aapt2/link/ReferenceLinker_test.cpp b/tools/aapt2/link/ReferenceLinker_test.cpp index 8d324fe753a1..76b23098a35c 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 @@ TEST(ReferenceLinkerTest, LinkSimpleReferences) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) - .setSymbolTable(JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) - .addSymbolTable(test::StaticSymbolTableBuilder() - .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034)) - .build()) - .build()) + .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addPublicSymbol(u"@android:string/ok", ResourceId(0x01040034)) + .build()) .build(); ReferenceLinker linker; @@ -91,19 +86,20 @@ TEST(ReferenceLinkerTest, LinkStyleAttributes) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) - .setSymbolTable(test::StaticSymbolTableBuilder() - .addPublicSymbol(u"@android:style/Theme.Material", ResourceId(0x01060000)) - .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001), - test::AttributeBuilder() - .setTypeMask(android::ResTable_map::TYPE_COLOR) - .build()) - .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002), - test::AttributeBuilder() - .setTypeMask(android::ResTable_map::TYPE_FLAGS) - .addItem(u"one", 0x01) - .addItem(u"two", 0x02) - .build()) - .build()) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addPublicSymbol(u"@android:style/Theme.Material", + ResourceId(0x01060000)) + .addPublicSymbol(u"@android:attr/foo", ResourceId(0x01010001), + test::AttributeBuilder() + .setTypeMask(ResTable_map::TYPE_COLOR) + .build()) + .addPublicSymbol(u"@android:attr/bar", ResourceId(0x01010002), + test::AttributeBuilder() + .setTypeMask(ResTable_map::TYPE_FLAGS) + .addItem(u"one", 0x01) + .addItem(u"two", 0x02) + .build()) + .build()) .build(); ReferenceLinker linker; @@ -131,11 +127,13 @@ TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) - .setSymbolTable(test::StaticSymbolTableBuilder() - .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo", - ResourceId(0x7f010000), test::AttributeBuilder() - .setTypeMask(android::ResTable_map::TYPE_COLOR).build()) - .build()) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addPublicSymbol(u"@com.app.test:attr/com.android.support$foo", + ResourceId(0x7f010000), + test::AttributeBuilder() + .setTypeMask(ResTable_map::TYPE_COLOR) + .build()) + .build()) .build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() @@ -167,12 +165,10 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) - .setSymbolTable(JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) - .addSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@android:string/hidden", ResourceId(0x01040034)) - .build()) - .build()) + .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addSymbol(u"@android:string/hidden", ResourceId(0x01040034)) + .build()) .build(); ReferenceLinker linker; @@ -190,13 +186,12 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test", { u"com.app.lib" } }) - .setSymbolTable(JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) - .addSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@com.app.test:string/com.app.lib$hidden", - ResourceId(0x7f040034)) - .build()) - .build()) + .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addSymbol(u"@com.app.test:string/com.app.lib$hidden", + ResourceId(0x7f040034)) + .build()) + .build(); ReferenceLinker linker; @@ -215,15 +210,14 @@ TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) { .setCompilationPackage(u"com.app.test") .setPackageId(0x7f) .setNameManglerPolicy(NameManglerPolicy{ u"com.app.test" }) - .setSymbolTable(JoinedSymbolTableBuilder() - .addSymbolTable(util::make_unique<SymbolTableWrapper>(table.get())) - .addSymbolTable(test::StaticSymbolTableBuilder() - .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001), - test::AttributeBuilder() - .setTypeMask(android::ResTable_map::TYPE_COLOR) - .build()) - .build()) - .build()) + .addSymbolSource(util::make_unique<ResourceTableSymbolSource>(table.get())) + .addSymbolSource(test::StaticSymbolSourceBuilder() + .addSymbol(u"@android:attr/hidden", ResourceId(0x01010001), + test::AttributeBuilder() + .setTypeMask( + android::ResTable_map::TYPE_COLOR) + .build()) + .build()) .build(); ReferenceLinker linker; diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 5f1174588438..7471e15db41a 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -34,10 +34,21 @@ TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable, assert(mMasterPackage && "package name or ID already taken"); } +bool TableMerger::merge(const Source& src, ResourceTable* table, + io::IFileCollection* collection) { + return mergeImpl(src, table, collection, false /* overlay */, true /* allow new */); +} + +bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table, + io::IFileCollection* collection) { + return mergeImpl(src, table, collection, true /* overlay */, mOptions.autoAddOverlay); +} + /** * This will merge packages with the same package name (or no package name). */ bool TableMerger::mergeImpl(const Source& src, ResourceTable* table, + io::IFileCollection* collection, bool overlay, bool allowNew) { const uint8_t desiredPackageId = mContext->getPackageId(); @@ -51,26 +62,36 @@ bool TableMerger::mergeImpl(const Source& src, ResourceTable* table, } if (package->name.empty() || mContext->getCompilationPackage() == package->name) { + FileMergeCallback callback; + if (collection) { + callback = [&](const ResourceNameRef& name, const ConfigDescription& config, + FileReference* newFile, FileReference* oldFile) -> bool { + // The old file's path points inside the APK, so we can use it as is. + io::IFile* f = collection->findFile(util::utf16ToUtf8(*oldFile->path)); + if (!f) { + mContext->getDiagnostics()->error(DiagMessage(src) << "file '" + << *oldFile->path + << "' not found"); + return false; + } + + newFile->file = f; + return true; + }; + } + // Merge here. Once the entries are merged and mangled, any references to // them are still valid. This is because un-mangled references are // mangled, then looked up at resolution time. // Also, when linking, we convert references with no package name to use // the compilation package name. error |= !doMerge(src, table, package.get(), - false /* mangle */, overlay, allowNew, {}); + false /* mangle */, overlay, allowNew, callback); } } return !error; } -bool TableMerger::merge(const Source& src, ResourceTable* table) { - return mergeImpl(src, table, false /* overlay */, true /* allow new */); -} - -bool TableMerger::mergeOverlay(const Source& src, ResourceTable* table) { - return mergeImpl(src, table, true /* overlay */, mOptions.autoAddOverlay); -} - /** * This will merge and mangle resources from a static library. */ diff --git a/tools/aapt2/link/TableMerger.h b/tools/aapt2/link/TableMerger.h index b3c22dd736f4..80c2a5e69b66 100644 --- a/tools/aapt2/link/TableMerger.h +++ b/tools/aapt2/link/TableMerger.h @@ -65,13 +65,17 @@ public: /** * Merges resources from the same or empty package. This is for local sources. + * An io::IFileCollection is optional and used to find the referenced Files and process them. */ - bool merge(const Source& src, ResourceTable* table); + bool merge(const Source& src, ResourceTable* table, + io::IFileCollection* collection = nullptr); /** * Merges resources from an overlay ResourceTable. + * An io::IFileCollection is optional and used to find the referenced Files and process them. */ - bool mergeOverlay(const Source& src, ResourceTable* table); + bool mergeOverlay(const Source& src, ResourceTable* table, + io::IFileCollection* collection = nullptr); /** * Merges resources from the given package, mangling the name. This is for static libraries. @@ -104,7 +108,7 @@ private: bool mergeFileImpl(const ResourceFile& fileDesc, io::IFile* file, bool overlay); - bool mergeImpl(const Source& src, ResourceTable* srcTable, + bool mergeImpl(const Source& src, ResourceTable* srcTable, io::IFileCollection* collection, bool overlay, bool allowNew); bool doMerge(const Source& src, ResourceTable* srcTable, ResourceTablePackage* srcPackage, diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp index a26d7637ab3a..568bc74895c7 100644 --- a/tools/aapt2/link/XmlReferenceLinker.cpp +++ b/tools/aapt2/link/XmlReferenceLinker.cpp @@ -34,17 +34,10 @@ namespace { * as needed. */ class ReferenceVisitor : public ValueVisitor { -private: - IAaptContext* mContext; - ISymbolTable* mSymbols; - xml::IPackageDeclStack* mDecls; - CallSite* mCallSite; - bool mError; - public: using ValueVisitor::visit; - ReferenceVisitor(IAaptContext* context, ISymbolTable* symbols, xml::IPackageDeclStack* decls, + ReferenceVisitor(IAaptContext* context, SymbolTable* symbols, xml::IPackageDeclStack* decls, CallSite* callSite) : mContext(context), mSymbols(symbols), mDecls(decls), mCallSite(callSite), mError(false) { @@ -59,25 +52,23 @@ public: bool hasError() const { return mError; } + +private: + IAaptContext* mContext; + SymbolTable* mSymbols; + xml::IPackageDeclStack* mDecls; + CallSite* mCallSite; + bool mError; }; /** * Visits each xml Element and compiles the attributes within. */ class XmlVisitor : public xml::PackageAwareVisitor { -private: - IAaptContext* mContext; - ISymbolTable* mSymbols; - Source mSource; - std::set<int>* mSdkLevelsFound; - CallSite* mCallSite; - ReferenceVisitor mReferenceVisitor; - bool mError = false; - public: using xml::PackageAwareVisitor::visit; - XmlVisitor(IAaptContext* context, ISymbolTable* symbols, const Source& source, + XmlVisitor(IAaptContext* context, SymbolTable* symbols, const Source& source, std::set<int>* sdkLevelsFound, CallSite* callSite) : mContext(context), mSymbols(symbols), mSource(source), mSdkLevelsFound(sdkLevelsFound), mCallSite(callSite), mReferenceVisitor(context, symbols, this, callSite) { @@ -105,10 +96,13 @@ public: // Convert the string value into a compiled Value if this is a valid attribute. if (attr.compiledAttribute) { - // Record all SDK levels from which the attributes were defined. - const int sdkLevel = findAttributeSdkLevel(attr.compiledAttribute.value().id); - if (sdkLevel > 1) { - mSdkLevelsFound->insert(sdkLevel); + if (attr.compiledAttribute.value().id) { + // Record all SDK levels from which the attributes were defined. + const size_t sdkLevel = findAttributeSdkLevel( + attr.compiledAttribute.value().id.value()); + if (sdkLevel > 1) { + mSdkLevelsFound->insert(sdkLevel); + } } const Attribute* attribute = &attr.compiledAttribute.value().attribute; @@ -124,6 +118,7 @@ public: << *attribute); mError = true; } + } else { mContext->getDiagnostics()->error(DiagMessage(source) << "attribute '" << package << ":" @@ -150,6 +145,15 @@ public: bool hasError() { return mError || mReferenceVisitor.hasError(); } + +private: + IAaptContext* mContext; + SymbolTable* mSymbols; + Source mSource; + std::set<int>* mSdkLevelsFound; + CallSite* mCallSite; + ReferenceVisitor mReferenceVisitor; + bool mError = false; }; } // namespace diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp index 3bfaf91854bb..af9098b9e483 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 @@ public: .setCompilationPackage(u"com.app.test") .setNameManglerPolicy( NameManglerPolicy{ u"com.app.test", { u"com.android.support" } }) - .setSymbolTable(test::StaticSymbolTableBuilder() + .addSymbolSource(test::StaticSymbolSourceBuilder() .addPublicSymbol(u"@android:attr/layout_width", ResourceId(0x01010000), test::AttributeBuilder() .setTypeMask(android::ResTable_map::TYPE_ENUM | @@ -92,14 +89,16 @@ TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) { u"layout_width"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010000)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010000)); ASSERT_NE(xmlAttr->compiledValue, nullptr); ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr); xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/android", u"background"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010001)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010001)); ASSERT_NE(xmlAttr->compiledValue, nullptr); Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); @@ -163,7 +162,8 @@ TEST_F(XmlReferenceLinkerTest, LinkMangledAttributes) { u"http://schemas.android.com/apk/res/com.android.support", u"colorAccent"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010001)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010001)); ASSERT_NE(valueCast<BinaryPrimitive>(xmlAttr->compiledValue.get()), nullptr); } @@ -182,7 +182,8 @@ TEST_F(XmlReferenceLinkerTest, LinkAutoResReference) { u"colorAccent"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010000)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010000)); Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->name); @@ -209,7 +210,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { u"attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x01010002)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x01010002)); Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); @@ -223,7 +225,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithShadowedPackageAlias) { xmlAttr = viewEl->findAttribute(u"http://schemas.android.com/apk/res/com.app.test", u"attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002)); ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); @@ -246,7 +249,8 @@ TEST_F(XmlReferenceLinkerTest, LinkViewWithLocalPackageAndAliasOfTheSameName) { u"http://schemas.android.com/apk/res/com.app.test", u"attr"); ASSERT_NE(xmlAttr, nullptr); AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute); - EXPECT_EQ(xmlAttr->compiledAttribute.value().id, ResourceId(0x7f010002)); + AAPT_ASSERT_TRUE(xmlAttr->compiledAttribute.value().id); + EXPECT_EQ(xmlAttr->compiledAttribute.value().id.value(), ResourceId(0x7f010002)); Reference* ref = valueCast<Reference>(xmlAttr->compiledValue.get()); ASSERT_NE(ref, nullptr); AAPT_ASSERT_TRUE(ref->id); diff --git a/tools/aapt2/process/IResourceTableConsumer.h b/tools/aapt2/process/IResourceTableConsumer.h index 3a880449b759..9affb836340c 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 b6030a2874a3..a8f9bfe4fb32 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 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n ResourceTable::SearchResult sr = result.value(); - // If no ID exists, we treat the symbol as missing. SymbolTables are used to - // find symbols to link. - if (!sr.package->id || !sr.type->id || !sr.entry->id) { - return {}; - } - - std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>(); - symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value()); + std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(); symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic); + if (sr.package->id && sr.type->id && sr.entry->id) { + symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value()); + } + if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) { const ConfigDescription kDefaultConfig; ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig); @@ -63,18 +108,16 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n } } } + return symbol; +} - 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(); +bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) { + int32_t cookie = 0; + return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie); } -static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table, - ResourceId id) { +static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table, + ResourceId id) { // Try as a bag. const android::ResTable::bag_entry* entry; ssize_t count = table.lockBag(id.id, &entry); @@ -84,7 +127,7 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi } // We found a resource. - std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>(); + std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(); s->id = id; // Check to see if it is an attribute. @@ -138,43 +181,36 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi return s; } -const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName( +std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName( const ResourceName& name) { - if (const std::shared_ptr<Symbol>& s = mCache.get(name)) { - return s.get(); + const android::ResTable& table = mAssets.getResources(false); + StringPiece16 typeStr = toString(name.type); + uint32_t typeSpecFlags = 0; + ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(), + typeStr.data(), typeStr.size(), + name.package.data(), name.package.size(), + &typeSpecFlags); + if (!resId.isValid()) { + return {}; } - for (const auto& asset : mAssets) { - const android::ResTable& table = asset->getResources(false); - StringPiece16 typeStr = toString(name.type); - uint32_t typeSpecFlags = 0; - ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(), - typeStr.data(), typeStr.size(), - name.package.data(), name.package.size(), - &typeSpecFlags); - if (!resId.isValid()) { - continue; - } - - std::shared_ptr<Symbol> s; - if (name.type == ResourceType::kAttr) { - s = lookupAttributeInTable(table, resId); - } else { - s = std::make_shared<Symbol>(); - s->id = resId; - } + 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; + } - if (s) { - s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; - mCache.put(name, s); - return s.get(); - } + if (s) { + s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; + return s; } - return nullptr; + return {}; } static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) { - android::ResTable::resource_name resName; + android::ResTable::resource_name resName = {}; if (!table.getResourceName(id.id, true, &resName)) { return {}; } @@ -211,55 +247,27 @@ static Maybe<ResourceName> getResourceName(const android::ResTable& table, Resou return name; } -const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById( - ResourceId id) { - if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) { - return s.get(); +std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) { + const android::ResTable& table = mAssets.getResources(false); + Maybe<ResourceName> maybeName = getResourceName(table, id); + if (!maybeName) { + return {}; } - for (const auto& asset : mAssets) { - const android::ResTable& table = asset->getResources(false); - - 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; - } + uint32_t typeSpecFlags = 0; + table.getResourceFlags(id.id, &typeSpecFlags); - if (s) { - s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; - mIdCache.put(id, s); - return s.get(); - } - } - return nullptr; -} - -const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName( - const ResourceName& name) { - for (auto& symbolTable : mSymbolTables) { - if (const Symbol* s = symbolTable->findByName(name)) { - return s; - } + 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 {}; -} -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 22096ed82f4e..8ea1c757fc68 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 @@ inline android::hash_t hash_type(const ResourceId& id) { return android::hash_type(id.id); } -/** - * Presents a ResourceTable as an ISymbolTable, caching results. - * Instances of this class must outlive the encompassed ResourceTable. - * Since symbols are cached, the ResourceTable should not change during the - * lifetime of this SymbolTableWrapper. - * - * If a resource in the ResourceTable does not have a ResourceID assigned to it, - * it is ignored. - * - * Lookups by ID are ignored. - */ -class SymbolTableWrapper : public ISymbolTable { -private: - ResourceTable* mTable; - - // We use shared_ptr because unique_ptr is not supported and - // we need automatic deletion. - android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache; +class ISymbolSource; +class SymbolTable { public: - SymbolTableWrapper(ResourceTable* table) : mTable(table), mCache(200) { + struct Symbol { + Maybe<ResourceId> id; + std::unique_ptr<Attribute> attribute; + bool isPublic; + }; + + SymbolTable() : mCache(200), mIdCache(200) { } - const Symbol* findByName(const ResourceName& name) override; + void appendSource(std::unique_ptr<ISymbolSource> source); + void prependSource(std::unique_ptr<ISymbolSource> source); - // Unsupported, all queries to ResourceTable should be done by name. - const Symbol* findById(ResourceId id) override { - return {}; - } -}; + /** + * 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); -class AssetManagerSymbolTableBuilder { private: - struct AssetManagerSymbolTable : public ISymbolTable { - std::vector<std::unique_ptr<android::AssetManager>> mAssets; + 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; + // 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) { - } + DISALLOW_COPY_AND_ASSIGN(SymbolTable); +}; - const Symbol* findByName(const ResourceName& name) override; - const Symbol* findById(ResourceId id) override; - }; +/** + * An interface that a symbol source implements in order to surface symbol information + * to the symbol table. + */ +class ISymbolSource { +public: + virtual ~ISymbolSource() = default; - std::unique_ptr<AssetManagerSymbolTable> mSymbolTable = - util::make_unique<AssetManagerSymbolTable>(); + 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: - AssetManagerSymbolTableBuilder& add(std::unique_ptr<android::AssetManager> assetManager) { - mSymbolTable->mAssets.push_back(std::move(assetManager)); - return *this; + explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) { } - std::unique_ptr<ISymbolTable> build() { - return std::move(mSymbolTable); + std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override; + + std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override { + return {}; } -}; -class JoinedSymbolTableBuilder { private: - struct JoinedSymbolTable : public ISymbolTable { - std::vector<std::unique_ptr<ISymbolTable>> mSymbolTables; - - const Symbol* findByName(const ResourceName& name) override; - const Symbol* findById(ResourceId id) override; - }; + ResourceTable* mTable; - std::unique_ptr<JoinedSymbolTable> mSymbolTable = util::make_unique<JoinedSymbolTable>(); + DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource); +}; +class AssetManagerSymbolSource : public ISymbolSource { public: - JoinedSymbolTableBuilder& addSymbolTable(std::unique_ptr<ISymbolTable> table) { - mSymbolTable->mSymbolTables.push_back(std::move(table)); - return *this; - } + AssetManagerSymbolSource() = default; - std::unique_ptr<ISymbolTable> build() { - return std::move(mSymbolTable); - } + bool addAssetPath(const StringPiece& path); + + std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override; + std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override; + +private: + android::AssetManager mAssets; + + DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource); }; } // namespace aapt diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp index 1dc3b4fe4e4a..34f31be3d0db 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(SymbolTableWrapperTest, FindSymbolsWithIds) { test::AttributeBuilder().build()) .build(); - SymbolTableWrapper symbolTable(table.get()); - EXPECT_NE(symbolTable.findByName(test::parseNameOrDie(u"@android:id/foo")), nullptr); - EXPECT_EQ(symbolTable.findByName(test::parseNameOrDie(u"@android:id/bar")), nullptr); + ResourceTableSymbolSource symbolSource(table.get()); + EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/foo"))); + EXPECT_NE(nullptr, symbolSource.findByName(test::parseNameOrDie(u"@android:id/bar"))); - const ISymbolTable::Symbol* s = symbolTable.findByName( + std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName( test::parseNameOrDie(u"@android:attr/foo")); - ASSERT_NE(s, nullptr); - EXPECT_NE(s->attribute, nullptr); + ASSERT_NE(nullptr, s); + EXPECT_NE(nullptr, s->attribute); } -TEST(SymbolTableWrapperTest, FindPrivateAttrSymbol) { +TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) { std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .addValue(u"@android:^attr-private/foo", ResourceId(0x01010000), test::AttributeBuilder().build()) .build(); - SymbolTableWrapper symbolTable(table.get()); - const ISymbolTable::Symbol* s = symbolTable.findByName( + ResourceTableSymbolSource symbolSource(table.get()); + std::unique_ptr<SymbolTable::Symbol> s = symbolSource.findByName( test::parseNameOrDie(u"@android:attr/foo")); - ASSERT_NE(s, nullptr); - EXPECT_NE(s->attribute, nullptr); + ASSERT_NE(nullptr, s); + EXPECT_NE(nullptr, s->attribute); } } // namespace aapt diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp index 9856a00d7f67..86883f882d33 100644 --- a/tools/aapt2/proto/TableProtoDeserializer.cpp +++ b/tools/aapt2/proto/TableProtoDeserializer.cpp @@ -483,8 +483,13 @@ const pb::CompiledFile* CompiledFileInputStream::CompiledFile() { } const size_t padding = 4 - (pbSize & 0x03); - mData += sizeof(uint64_t) + pbSize + padding; - mSize -= sizeof(uint64_t) + pbSize + padding; + const size_t offset = sizeof(uint64_t) + pbSize + padding; + if (offset > mSize) { + return nullptr; + } + + mData += offset; + mSize -= offset; mPbFile = std::move(pbFile); } return mPbFile.get(); diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp index b3d87d805c6d..5d1b72b0ebbd 100644 --- a/tools/aapt2/proto/TableProtoSerializer.cpp +++ b/tools/aapt2/proto/TableProtoSerializer.cpp @@ -178,10 +178,13 @@ private: void serializeReferenceToPb(const Reference& ref, pb::Reference* pbRef) { if (ref.id) { pbRef->set_id(ref.id.value().id); - } else if (ref.name) { + } + + if (ref.name) { StringPool::Ref symbolRef = mSymbolPool->makeRef(ref.name.value().toString()); pbRef->set_symbol_idx(static_cast<uint32_t>(symbolRef.getIndex())); } + pbRef->set_private_(ref.privateReference); pbRef->set_type(serializeReferenceTypeToPb(ref.referenceType)); } diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp index 70a33f795f87..dd995d858d77 100644 --- a/tools/aapt2/proto/TableProtoSerializer_test.cpp +++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp @@ -62,6 +62,17 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->getDiagnostics())); + // Make a reference with both resource name and resource ID. + // The reference should point to a resource outside of this table to test that both + // name and id get serialized. + Reference expectedRef; + expectedRef.name = test::parseNameOrDie(u"@android:layout/main"); + expectedRef.id = ResourceId(0x01020000); + ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"), + ConfigDescription::defaultConfig(), std::string(), + util::make_unique<Reference>(expectedRef), + context->getDiagnostics())); + std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get()); ASSERT_NE(nullptr, pbTable); @@ -90,6 +101,13 @@ TEST(TableProtoSerializer, SerializeSinglePackage) { newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); ASSERT_NE(nullptr, prim); EXPECT_EQ(321u, prim->value.data); + + Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc"); + ASSERT_NE(nullptr, actualRef); + AAPT_ASSERT_TRUE(actualRef->name); + AAPT_ASSERT_TRUE(actualRef->id); + EXPECT_EQ(expectedRef.name.value(), actualRef->name.value()); + EXPECT_EQ(expectedRef.id.value(), actualRef->id.value()); } TEST(TableProtoSerializer, SerializeFileHeader) { @@ -130,4 +148,27 @@ TEST(TableProtoSerializer, SerializeFileHeader) { EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name); } +TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { + ResourceFile f; + std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); + + const std::string expectedData = "1234"; + + std::string outputStr; + { + google::protobuf::io::StringOutputStream outStream(&outputStr); + CompiledFileOutputStream outFileStream(&outStream, pbFile.get()); + + ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size())); + ASSERT_TRUE(outFileStream.Finish()); + } + + outputStr[0] = 0xff; + + CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); + EXPECT_EQ(nullptr, inFileStream.CompiledFile()); + EXPECT_EQ(nullptr, inFileStream.data()); + EXPECT_EQ(0u, inFileStream.size()); +} + } // namespace aapt diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h index 834caf8b9a49..8c56ebc67b32 100644 --- a/tools/aapt2/test/Builders.h +++ b/tools/aapt2/test/Builders.h @@ -246,7 +246,7 @@ inline std::unique_ptr<xml::XmlResource> buildXmlDom(const StringPiece& str) { inline std::unique_ptr<xml::XmlResource> buildXmlDomForPackageName(IAaptContext* context, const StringPiece& str) { std::unique_ptr<xml::XmlResource> doc = buildXmlDom(str); - doc->file.name.package = context->getCompilationPackage().toString(); + doc->file.name.package = context->getCompilationPackage(); return doc; } diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h index e540cd7ace90..96752d33dd9a 100644 --- a/tools/aapt2/test/Context.h +++ b/tools/aapt2/test/Context.h @@ -31,33 +31,16 @@ namespace aapt { namespace test { class Context : public IAaptContext { -private: - friend class ContextBuilder; - - Context() = default; - - Maybe<std::u16string> mCompilationPackage; - Maybe<uint8_t> mPackageId; - std::unique_ptr<IDiagnostics> mDiagnostics = util::make_unique<StdErrDiagnostics>(); - std::unique_ptr<ISymbolTable> mSymbols; - std::unique_ptr<NameMangler> mNameMangler; - public: - ISymbolTable* getExternalSymbols() override { - assert(mSymbols && "test symbols not set"); - return mSymbols.get(); - } - - void setSymbolTable(std::unique_ptr<ISymbolTable> symbols) { - mSymbols = std::move(symbols); + SymbolTable* getExternalSymbols() override { + return &mSymbols; } IDiagnostics* getDiagnostics() override { - assert(mDiagnostics && "test diagnostics not set"); - return mDiagnostics.get(); + return &mDiagnostics; } - StringPiece16 getCompilationPackage() override { + const std::u16string& getCompilationPackage() override { assert(mCompilationPackage && "package name not set"); return mCompilationPackage.value(); } @@ -68,13 +51,24 @@ public: } NameMangler* getNameMangler() override { - assert(mNameMangler && "test name mangler not set"); - return mNameMangler.get(); + return &mNameMangler; } bool verbose() override { return false; } + +private: + friend class ContextBuilder; + + Context() : mNameMangler({}) { + } + + Maybe<std::u16string> mCompilationPackage; + Maybe<uint8_t> mPackageId; + StdErrDiagnostics mDiagnostics; + SymbolTable mSymbols; + NameMangler mNameMangler; }; class ContextBuilder { @@ -92,18 +86,13 @@ public: return *this; } - ContextBuilder& setSymbolTable(std::unique_ptr<ISymbolTable> symbols) { - mContext->mSymbols = std::move(symbols); + ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) { + mContext->mNameMangler = NameMangler(policy); 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); + ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) { + mContext->getExternalSymbols()->appendSource(std::move(src)); return *this; } @@ -112,57 +101,72 @@ public: } }; -class StaticSymbolTableBuilder { +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; + } + + 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: - struct SymbolTable : public ISymbolTable { - std::list<std::unique_ptr<Symbol>> mSymbols; - std::map<ResourceName, Symbol*> mNameMap; - std::map<ResourceId, Symbol*> mIdMap; + class StaticSymbolSource : public ISymbolSource { + public: + StaticSymbolSource() = default; - const Symbol* findByName(const ResourceName& name) override { + 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::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>(); + std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols; + std::map<ResourceName, SymbolTable::Symbol*> mNameMap; + std::map<ResourceId, SymbolTable::Symbol*> mIdMap; -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; - } + 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; + } - 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; - } + DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource); + }; - 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 000000000000..d4845cfc19b7 --- /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 033b0a4d031c..b374d20039a5 100644 --- a/tools/aapt2/xml/XmlDom.h +++ b/tools/aapt2/xml/XmlDom.h @@ -68,7 +68,7 @@ struct Namespace : public BaseNode<Namespace> { }; struct AaptAttribute { - ResourceId id; + Maybe<ResourceId> id; aapt::Attribute attribute; }; |