Merge "AAPT2: Support building, linking, and merging static libraries" into nyc-dev

This commit is contained in:
Adam Lesinski
2016-03-04 23:46:48 +00:00
committed by Android (Google) Code Review
67 changed files with 1396 additions and 786 deletions

View File

@@ -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

View File

@@ -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;
};
/**

View File

@@ -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>

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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"/>

View File

@@ -1 +0,0 @@
Oh howdy there

View File

@@ -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>

View File

@@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
<public name="hooha" type="string" id="0x7f020001"/>
<string name="wow">@android:string/ok</string>
<public name="layout_width" type="attr" />
<attr name="layout_width" format="boolean" />
<attr name="flags">
<flag name="complex" value="1" />
<flag name="pub" value="2" />
<flag name="weak" value="4" />
</attr>
</resources>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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))

View File

@@ -0,0 +1,2 @@
LOCAL_PATH := $(call my-dir)
include $(call all-makefiles-under,$(LOCAL_PATH))

View File

@@ -0,0 +1,27 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_PACKAGE_NAME := AaptTestAppOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_STATIC_ANDROID_LIBRARIES := \
AaptTestStaticLibOne \
AaptTestStaticLibTwo
include $(BUILD_PACKAGE)

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest package="com.android.aapt.app.one" />

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector />

View File

Before

Width:  |  Height:  |  Size: 124 B

After

Width:  |  Height:  |  Size: 124 B

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:support="http://schemas.android.com/apk/res/android.appcompat"
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>

View File

@@ -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"

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="App" parent="android:Theme.Material">
<item name="android:colorAccent">@color/accent</item>
<item name="android:text">Hey</item>
</style>
</resources>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<color name="primary">#f44336</color>
<color name="primary_dark">#b71c1c</color>
<color name="accent">#fdd835</color>
</resources>

View File

@@ -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>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Reference the two static libraries -->
<string name="AppFooBar">@string/FooBar</string>
<string name="AppFoo">@string/Foo</string>
<string name="hooha"><font bgcolor="#ffffff">Hey guys!</font> <xliff:g>My</xliff:g> name is <b>Adam</b>. How <b><i>are</i></b> you?</string>
<public name="hooha" type="string" id="0x7f020001"/>
<string name="wow">@android:string/ok</string>
<public name="layout_width" type="attr" />
<attr name="layout_width" format="boolean" />
<attr name="flags">
<flag name="complex" value="1" />
<flag name="pub" value="2" />
<flag name="weak" value="4" />
</attr>
</resources>

View File

@@ -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;
}

View File

@@ -0,0 +1,25 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := AaptTestStaticLibOne
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest package="com.android.aapt.staticlib.one" />

View File

@@ -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" />

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<attr name="StaticLibOne_attr" format="string" />
<string name="Foo">Foo</string>
<string name="Foo" product="tablet">Bar</string>
</resources>

View File

@@ -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;
}

View File

@@ -0,0 +1,27 @@
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_USE_AAPT2 := true
LOCAL_MODULE := AaptTestStaticLibTwo
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
LOCAL_SHARED_ANDROID_LIBRARIES := AaptTestStaticLibOne
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest package="com.android.aapt.staticlib.two" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
}

View File

@@ -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 {};

View File

@@ -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;

View File

@@ -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());
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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);
/**

View File

@@ -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;

View File

@@ -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.
*/

View File

@@ -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,

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -25,11 +25,59 @@
namespace aapt {
const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& name) {
void SymbolTable::appendSource(std::unique_ptr<ISymbolSource> source) {
mSources.push_back(std::move(source));
// We do not clear the cache, because sources earlier in the list take precedent.
}
void SymbolTable::prependSource(std::unique_ptr<ISymbolSource> source) {
mSources.insert(mSources.begin(), std::move(source));
// We must clear the cache in case we did a lookup before adding this resource.
mCache.clear();
}
const SymbolTable::Symbol* SymbolTable::findByName(const ResourceName& name) {
if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
return s.get();
}
// We did not find it in the cache, so look through the sources.
for (auto& symbolSource : mSources) {
std::unique_ptr<Symbol> symbol = symbolSource->findByName(name);
if (symbol) {
// Take ownership of the symbol into a shared_ptr. We do this because LruCache
// doesn't support unique_ptr.
std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
mCache.put(name, sharedSymbol);
return sharedSymbol.get();
}
}
return nullptr;
}
const SymbolTable::Symbol* SymbolTable::findById(ResourceId id) {
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
return s.get();
}
// We did not find it in the cache, so look through the sources.
for (auto& symbolSource : mSources) {
std::unique_ptr<Symbol> symbol = symbolSource->findById(id);
if (symbol) {
// Take ownership of the symbol into a shared_ptr. We do this because LruCache
// doesn't support unique_ptr.
std::shared_ptr<Symbol> sharedSymbol = std::shared_ptr<Symbol>(symbol.release());
mIdCache.put(id, sharedSymbol);
return sharedSymbol.get();
}
}
return nullptr;
}
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::findByName(
const ResourceName& name) {
Maybe<ResourceTable::SearchResult> result = mTable->findResource(name);
if (!result) {
if (name.type == ResourceType::kAttr) {
@@ -41,16 +89,13 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
ResourceTable::SearchResult sr = result.value();
// If no ID exists, we treat the symbol as missing. SymbolTables are used to
// find symbols to link.
if (!sr.package->id || !sr.type->id || !sr.entry->id) {
return {};
}
std::shared_ptr<Symbol> symbol = std::make_shared<Symbol>();
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
symbol->isPublic = (sr.entry->symbolStatus.state == SymbolState::kPublic);
if (sr.package->id && sr.type->id && sr.entry->id) {
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
}
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
const ConfigDescription kDefaultConfig;
ResourceConfigValue* configValue = sr.entry->findValue(kDefaultConfig);
@@ -63,18 +108,16 @@ const ISymbolTable::Symbol* SymbolTableWrapper::findByName(const ResourceName& n
}
}
}
if (name.type == ResourceType::kAttrPrivate) {
// Masquerade this entry as kAttr.
mCache.put(ResourceName(name.package, ResourceType::kAttr, name.entry), symbol);
} else {
mCache.put(name, symbol);
}
return symbol.get();
return symbol;
}
static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
ResourceId id) {
bool AssetManagerSymbolSource::addAssetPath(const StringPiece& path) {
int32_t cookie = 0;
return mAssets.addAssetPath(android::String8(path.data(), path.size()), &cookie);
}
static std::unique_ptr<SymbolTable::Symbol> lookupAttributeInTable(const android::ResTable& table,
ResourceId id) {
// Try as a bag.
const android::ResTable::bag_entry* entry;
ssize_t count = table.lockBag(id.id, &entry);
@@ -84,7 +127,7 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi
}
// We found a resource.
std::shared_ptr<ISymbolTable::Symbol> s = std::make_shared<ISymbolTable::Symbol>();
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
// Check to see if it is an attribute.
@@ -138,43 +181,36 @@ static std::shared_ptr<ISymbolTable::Symbol> lookupAttributeInTable(const androi
return s;
}
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findByName(
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findByName(
const ResourceName& name) {
if (const std::shared_ptr<Symbol>& s = mCache.get(name)) {
return s.get();
const android::ResTable& table = mAssets.getResources(false);
StringPiece16 typeStr = toString(name.type);
uint32_t typeSpecFlags = 0;
ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
typeStr.data(), typeStr.size(),
name.package.data(), name.package.size(),
&typeSpecFlags);
if (!resId.isValid()) {
return {};
}
for (const auto& asset : mAssets) {
const android::ResTable& table = asset->getResources(false);
StringPiece16 typeStr = toString(name.type);
uint32_t typeSpecFlags = 0;
ResourceId resId = table.identifierForName(name.entry.data(), name.entry.size(),
typeStr.data(), typeStr.size(),
name.package.data(), name.package.size(),
&typeSpecFlags);
if (!resId.isValid()) {
continue;
}
std::shared_ptr<Symbol> s;
if (name.type == ResourceType::kAttr) {
s = lookupAttributeInTable(table, resId);
} else {
s = std::make_shared<Symbol>();
s->id = resId;
}
if (s) {
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
mCache.put(name, s);
return s.get();
}
std::unique_ptr<SymbolTable::Symbol> s;
if (name.type == ResourceType::kAttr) {
s = lookupAttributeInTable(table, resId);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = resId;
}
return nullptr;
if (s) {
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
return s;
}
return {};
}
static Maybe<ResourceName> getResourceName(const android::ResTable& table, ResourceId id) {
android::ResTable::resource_name resName;
android::ResTable::resource_name resName = {};
if (!table.getResourceName(id.id, true, &resName)) {
return {};
}
@@ -211,55 +247,27 @@ static Maybe<ResourceName> getResourceName(const android::ResTable& table, Resou
return name;
}
const ISymbolTable::Symbol* AssetManagerSymbolTableBuilder::AssetManagerSymbolTable::findById(
ResourceId id) {
if (const std::shared_ptr<Symbol>& s = mIdCache.get(id)) {
return s.get();
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::findById(ResourceId id) {
const android::ResTable& table = mAssets.getResources(false);
Maybe<ResourceName> maybeName = getResourceName(table, id);
if (!maybeName) {
return {};
}
for (const auto& asset : mAssets) {
const android::ResTable& table = asset->getResources(false);
uint32_t typeSpecFlags = 0;
table.getResourceFlags(id.id, &typeSpecFlags);
Maybe<ResourceName> maybeName = getResourceName(table, id);
if (!maybeName) {
continue;
}
uint32_t typeSpecFlags = 0;
table.getResourceFlags(id.id, &typeSpecFlags);
std::shared_ptr<Symbol> s;
if (maybeName.value().type == ResourceType::kAttr) {
s = lookupAttributeInTable(table, id);
} else {
s = std::make_shared<Symbol>();
s->id = id;
}
if (s) {
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
mIdCache.put(id, s);
return s.get();
}
std::unique_ptr<SymbolTable::Symbol> s;
if (maybeName.value().type == ResourceType::kAttr) {
s = lookupAttributeInTable(table, id);
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
}
return nullptr;
}
const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findByName(
const ResourceName& name) {
for (auto& symbolTable : mSymbolTables) {
if (const Symbol* s = symbolTable->findByName(name)) {
return s;
}
}
return {};
}
const ISymbolTable::Symbol* JoinedSymbolTableBuilder::JoinedSymbolTable::findById(ResourceId id) {
for (auto& symbolTable : mSymbolTables) {
if (const Symbol* s = symbolTable->findById(id)) {
return s;
}
if (s) {
s->isPublic = (typeSpecFlags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
return s;
}
return {};
}

View File

@@ -25,37 +25,20 @@
#include <utils/JenkinsHash.h>
#include <utils/LruCache.h>
#include <android-base/macros.h>
#include <androidfw/AssetManager.h>
#include <algorithm>
#include <map>
#include <memory>
#include <vector>
namespace aapt {
struct ISymbolTable {
virtual ~ISymbolTable() = default;
struct Symbol {
ResourceId id;
std::unique_ptr<Attribute> attribute;
bool isPublic;
};
/**
* Never hold on to the result between calls to findByName or findById. The results
* are typically stored in a cache which may evict entries.
*/
virtual const Symbol* findByName(const ResourceName& name) = 0;
virtual const Symbol* findById(ResourceId id) = 0;
};
inline android::hash_t hash_type(const ResourceName& name) {
std::hash<std::u16string> strHash;
android::hash_t hash = 0;
hash = android::JenkinsHashMix(hash, strHash(name.package));
hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.package));
hash = android::JenkinsHashMix(hash, (uint32_t) name.type);
hash = android::JenkinsHashMix(hash, strHash(name.entry));
hash = android::JenkinsHashMix(hash, (uint32_t) strHash(name.entry));
return hash;
}
@@ -63,88 +46,87 @@ inline android::hash_t hash_type(const ResourceId& id) {
return android::hash_type(id.id);
}
/**
* Presents a ResourceTable as an ISymbolTable, caching results.
* Instances of this class must outlive the encompassed ResourceTable.
* Since symbols are cached, the ResourceTable should not change during the
* lifetime of this SymbolTableWrapper.
*
* If a resource in the ResourceTable does not have a ResourceID assigned to it,
* it is ignored.
*
* Lookups by ID are ignored.
*/
class SymbolTableWrapper : public ISymbolTable {
class ISymbolSource;
class SymbolTable {
public:
struct Symbol {
Maybe<ResourceId> id;
std::unique_ptr<Attribute> attribute;
bool isPublic;
};
SymbolTable() : mCache(200), mIdCache(200) {
}
void appendSource(std::unique_ptr<ISymbolSource> source);
void prependSource(std::unique_ptr<ISymbolSource> source);
/**
* Never hold on to the result between calls to findByName or findById. The results
* are typically stored in a cache which may evict entries.
*/
const Symbol* findByName(const ResourceName& name);
const Symbol* findById(ResourceId id);
private:
ResourceTable* mTable;
std::vector<std::unique_ptr<ISymbolSource>> mSources;
// We use shared_ptr because unique_ptr is not supported and
// we need automatic deletion.
android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
DISALLOW_COPY_AND_ASSIGN(SymbolTable);
};
/**
* An interface that a symbol source implements in order to surface symbol information
* to the symbol table.
*/
class ISymbolSource {
public:
SymbolTableWrapper(ResourceTable* table) : mTable(table), mCache(200) {
virtual ~ISymbolSource() = default;
virtual std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) = 0;
virtual std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) = 0;
};
/**
* Exposes the resources in a ResourceTable as symbols for SymbolTable.
* Instances of this class must outlive the encompassed ResourceTable.
* Lookups by ID are ignored.
*/
class ResourceTableSymbolSource : public ISymbolSource {
public:
explicit ResourceTableSymbolSource(ResourceTable* table) : mTable(table) {
}
const Symbol* findByName(const ResourceName& name) override;
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
// Unsupported, all queries to ResourceTable should be done by name.
const Symbol* findById(ResourceId id) override {
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
return {};
}
private:
ResourceTable* mTable;
DISALLOW_COPY_AND_ASSIGN(ResourceTableSymbolSource);
};
class AssetManagerSymbolTableBuilder {
private:
struct AssetManagerSymbolTable : public ISymbolTable {
std::vector<std::unique_ptr<android::AssetManager>> mAssets;
// We use shared_ptr because unique_ptr is not supported and
// we need automatic deletion.
android::LruCache<ResourceName, std::shared_ptr<Symbol>> mCache;
android::LruCache<ResourceId, std::shared_ptr<Symbol>> mIdCache;
AssetManagerSymbolTable() : mCache(200), mIdCache(200) {
}
const Symbol* findByName(const ResourceName& name) override;
const Symbol* findById(ResourceId id) override;
};
std::unique_ptr<AssetManagerSymbolTable> mSymbolTable =
util::make_unique<AssetManagerSymbolTable>();
class AssetManagerSymbolSource : public ISymbolSource {
public:
AssetManagerSymbolTableBuilder& add(std::unique_ptr<android::AssetManager> assetManager) {
mSymbolTable->mAssets.push_back(std::move(assetManager));
return *this;
}
AssetManagerSymbolSource() = default;
std::unique_ptr<ISymbolTable> build() {
return std::move(mSymbolTable);
}
};
bool addAssetPath(const StringPiece& path);
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override;
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override;
class JoinedSymbolTableBuilder {
private:
struct JoinedSymbolTable : public ISymbolTable {
std::vector<std::unique_ptr<ISymbolTable>> mSymbolTables;
android::AssetManager mAssets;
const Symbol* findByName(const ResourceName& name) override;
const Symbol* findById(ResourceId id) override;
};
std::unique_ptr<JoinedSymbolTable> mSymbolTable = util::make_unique<JoinedSymbolTable>();
public:
JoinedSymbolTableBuilder& addSymbolTable(std::unique_ptr<ISymbolTable> table) {
mSymbolTable->mSymbolTables.push_back(std::move(table));
return *this;
}
std::unique_ptr<ISymbolTable> build() {
return std::move(mSymbolTable);
}
DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource);
};
} // namespace aapt

View File

@@ -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

View File

@@ -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();

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -31,33 +31,16 @@ namespace aapt {
namespace test {
class Context : public IAaptContext {
private:
friend class ContextBuilder;
Context() = default;
Maybe<std::u16string> mCompilationPackage;
Maybe<uint8_t> mPackageId;
std::unique_ptr<IDiagnostics> mDiagnostics = util::make_unique<StdErrDiagnostics>();
std::unique_ptr<ISymbolTable> mSymbols;
std::unique_ptr<NameMangler> mNameMangler;
public:
ISymbolTable* getExternalSymbols() override {
assert(mSymbols && "test symbols not set");
return mSymbols.get();
}
void setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
mSymbols = std::move(symbols);
SymbolTable* getExternalSymbols() override {
return &mSymbols;
}
IDiagnostics* getDiagnostics() override {
assert(mDiagnostics && "test diagnostics not set");
return mDiagnostics.get();
return &mDiagnostics;
}
StringPiece16 getCompilationPackage() override {
const std::u16string& getCompilationPackage() override {
assert(mCompilationPackage && "package name not set");
return mCompilationPackage.value();
}
@@ -68,13 +51,24 @@ public:
}
NameMangler* getNameMangler() override {
assert(mNameMangler && "test name mangler not set");
return mNameMangler.get();
return &mNameMangler;
}
bool verbose() override {
return false;
}
private:
friend class ContextBuilder;
Context() : mNameMangler({}) {
}
Maybe<std::u16string> mCompilationPackage;
Maybe<uint8_t> mPackageId;
StdErrDiagnostics mDiagnostics;
SymbolTable mSymbols;
NameMangler mNameMangler;
};
class ContextBuilder {
@@ -92,18 +86,13 @@ public:
return *this;
}
ContextBuilder& setSymbolTable(std::unique_ptr<ISymbolTable> symbols) {
mContext->mSymbols = std::move(symbols);
return *this;
}
ContextBuilder& setDiagnostics(std::unique_ptr<IDiagnostics> diag) {
mContext->mDiagnostics = std::move(diag);
return *this;
}
ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) {
mContext->mNameMangler = util::make_unique<NameMangler>(policy);
mContext->mNameMangler = NameMangler(policy);
return *this;
}
ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
mContext->getExternalSymbols()->appendSource(std::move(src));
return *this;
}
@@ -112,57 +101,72 @@ public:
}
};
class StaticSymbolTableBuilder {
private:
struct SymbolTable : public ISymbolTable {
std::list<std::unique_ptr<Symbol>> mSymbols;
std::map<ResourceName, Symbol*> mNameMap;
std::map<ResourceId, Symbol*> mIdMap;
class StaticSymbolSourceBuilder {
public:
StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
std::unique_ptr<Attribute> attr = {}) {
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
id, std::move(attr), true);
mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
mSymbolSource->mIdMap[id] = symbol.get();
mSymbolSource->mSymbols.push_back(std::move(symbol));
return *this;
}
const Symbol* findByName(const ResourceName& name) override {
StaticSymbolSourceBuilder& addSymbol(const StringPiece16& name, ResourceId id,
std::unique_ptr<Attribute> attr = {}) {
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
id, std::move(attr), false);
mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
mSymbolSource->mIdMap[id] = symbol.get();
mSymbolSource->mSymbols.push_back(std::move(symbol));
return *this;
}
std::unique_ptr<ISymbolSource> build() {
return std::move(mSymbolSource);
}
private:
class StaticSymbolSource : public ISymbolSource {
public:
StaticSymbolSource() = default;
std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
auto iter = mNameMap.find(name);
if (iter != mNameMap.end()) {
return iter->second;
return cloneSymbol(iter->second);
}
return nullptr;
}
const Symbol* findById(ResourceId id) override {
std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
auto iter = mIdMap.find(id);
if (iter != mIdMap.end()) {
return iter->second;
return cloneSymbol(iter->second);
}
return nullptr;
}
std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
std::map<ResourceId, SymbolTable::Symbol*> mIdMap;
private:
std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
clone->id = sym->id;
if (sym->attribute) {
clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
}
clone->isPublic = sym->isPublic;
return clone;
}
DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
};
std::unique_ptr<SymbolTable> mSymbolTable = util::make_unique<SymbolTable>();
public:
StaticSymbolTableBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
std::unique_ptr<Attribute> attr = {}) {
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
id, std::move(attr));
symbol->isPublic = true;
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
mSymbolTable->mIdMap[id] = symbol.get();
mSymbolTable->mSymbols.push_back(std::move(symbol));
return *this;
}
StaticSymbolTableBuilder& addSymbol(const StringPiece16& name, ResourceId id,
std::unique_ptr<Attribute> attr = {}) {
std::unique_ptr<ISymbolTable::Symbol> symbol = util::make_unique<ISymbolTable::Symbol>(
id, std::move(attr));
mSymbolTable->mNameMap[parseNameOrDie(name)] = symbol.get();
mSymbolTable->mIdMap[id] = symbol.get();
mSymbolTable->mSymbols.push_back(std::move(symbol));
return *this;
}
std::unique_ptr<ISymbolTable> build() {
return std::move(mSymbolTable);
}
std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
};
} // namespace test

32
tools/aapt2/test/Test.h Normal file
View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef AAPT_TEST_TEST_H
#define AAPT_TEST_TEST_H
#include "test/Builders.h"
#include "test/Common.h"
#include "test/Context.h"
#include <gtest/gtest.h>
namespace aapt {
namespace test {
} // namespace test
} // namespace aapt
#endif // AAPT_TEST_TEST_H

View File

@@ -68,7 +68,7 @@ struct Namespace : public BaseNode<Namespace> {
};
struct AaptAttribute {
ResourceId id;
Maybe<ResourceId> id;
aapt::Attribute attribute;
};