Merge commit 'cd8442d4' into manualmerge
Conflicts: tools/aapt/ResourceIdCache.cpp Change-Id: I5341d47ed0fe891d6fefb174f644e636ce966eb8
This commit is contained in:
@@ -1140,9 +1140,10 @@ bail:
|
|||||||
ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
|
ssize_t AaptAssets::slurpFullTree(Bundle* bundle, const String8& srcDir,
|
||||||
const AaptGroupEntry& kind,
|
const AaptGroupEntry& kind,
|
||||||
const String8& resType,
|
const String8& resType,
|
||||||
sp<FilePathStore>& fullResPaths)
|
sp<FilePathStore>& fullResPaths,
|
||||||
|
const bool overwrite)
|
||||||
{
|
{
|
||||||
ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths);
|
ssize_t res = AaptDir::slurpFullTree(bundle, srcDir, kind, resType, fullResPaths, overwrite);
|
||||||
if (res > 0) {
|
if (res > 0) {
|
||||||
mGroupEntries.add(kind);
|
mGroupEntries.add(kind);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -591,7 +591,8 @@ private:
|
|||||||
const String8& srcDir,
|
const String8& srcDir,
|
||||||
const AaptGroupEntry& kind,
|
const AaptGroupEntry& kind,
|
||||||
const String8& resType,
|
const String8& resType,
|
||||||
sp<FilePathStore>& fullResPaths);
|
sp<FilePathStore>& fullResPaths,
|
||||||
|
const bool overwrite=false);
|
||||||
|
|
||||||
ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
|
ssize_t slurpResourceTree(Bundle* bundle, const String8& srcDir);
|
||||||
ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
|
ssize_t slurpResourceZip(Bundle* bundle, const char* filename);
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ struct ConfigDescription : public android::ResTable_config {
|
|||||||
memset(this, 0, sizeof(*this));
|
memset(this, 0, sizeof(*this));
|
||||||
size = sizeof(android::ResTable_config);
|
size = sizeof(android::ResTable_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigDescription(const android::ResTable_config&o) {
|
ConfigDescription(const android::ResTable_config&o) {
|
||||||
*static_cast<android::ResTable_config*>(this) = o;
|
*static_cast<android::ResTable_config*>(this) = o;
|
||||||
size = sizeof(android::ResTable_config);
|
size = sizeof(android::ResTable_config);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigDescription(const ConfigDescription&o) {
|
ConfigDescription(const ConfigDescription&o) {
|
||||||
*static_cast<android::ResTable_config*>(this) = o;
|
*static_cast<android::ResTable_config*>(this) = o;
|
||||||
}
|
}
|
||||||
@@ -41,6 +43,7 @@ struct ConfigDescription : public android::ResTable_config {
|
|||||||
size = sizeof(android::ResTable_config);
|
size = sizeof(android::ResTable_config);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigDescription& operator=(const ConfigDescription& o) {
|
ConfigDescription& operator=(const ConfigDescription& o) {
|
||||||
*static_cast<android::ResTable_config*>(this) = o;
|
*static_cast<android::ResTable_config*>(this) = o;
|
||||||
return *this;
|
return *this;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "ResourceIdCache.h"
|
#include "ResourceIdCache.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
|
||||||
static size_t mHits = 0;
|
static size_t mHits = 0;
|
||||||
static size_t mMisses = 0;
|
static size_t mMisses = 0;
|
||||||
static size_t mCollisions = 0;
|
static size_t mCollisions = 0;
|
||||||
|
|||||||
83
tools/split-select/Abi.cpp
Normal file
83
tools/split-select/Abi.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Abi.h"
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
namespace abi {
|
||||||
|
|
||||||
|
static const std::vector<Variant> sNoneVariants = {};
|
||||||
|
static const std::vector<Variant> sArmVariants =
|
||||||
|
{Variant::armeabi, Variant::armeabi_v7a, Variant::arm64_v8a};
|
||||||
|
static const std::vector<Variant> sIntelVariants = {Variant::x86, Variant::x86_64};
|
||||||
|
static const std::vector<Variant> sMipsVariants = {Variant::mips, Variant::mips64};
|
||||||
|
|
||||||
|
Family getFamily(Variant variant) {
|
||||||
|
switch (variant) {
|
||||||
|
case Variant::none:
|
||||||
|
return Family::none;
|
||||||
|
case Variant::armeabi:
|
||||||
|
case Variant::armeabi_v7a:
|
||||||
|
case Variant::arm64_v8a:
|
||||||
|
return Family::arm;
|
||||||
|
case Variant::x86:
|
||||||
|
case Variant::x86_64:
|
||||||
|
return Family::intel;
|
||||||
|
case Variant::mips:
|
||||||
|
case Variant::mips64:
|
||||||
|
return Family::mips;
|
||||||
|
}
|
||||||
|
return Family::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Variant>& getVariants(Family family) {
|
||||||
|
switch (family) {
|
||||||
|
case Family::none:
|
||||||
|
return sNoneVariants;
|
||||||
|
case Family::arm:
|
||||||
|
return sArmVariants;
|
||||||
|
case Family::intel:
|
||||||
|
return sIntelVariants;
|
||||||
|
case Family::mips:
|
||||||
|
return sMipsVariants;
|
||||||
|
}
|
||||||
|
return sNoneVariants;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* toString(Variant variant) {
|
||||||
|
switch (variant) {
|
||||||
|
case Variant::none:
|
||||||
|
return "";
|
||||||
|
case Variant::armeabi:
|
||||||
|
return "armeabi";
|
||||||
|
case Variant::armeabi_v7a:
|
||||||
|
return "armeabi-v7a";
|
||||||
|
case Variant::arm64_v8a:
|
||||||
|
return "arm64-v8a";
|
||||||
|
case Variant::x86:
|
||||||
|
return "x86";
|
||||||
|
case Variant::x86_64:
|
||||||
|
return "x86_64";
|
||||||
|
case Variant::mips:
|
||||||
|
return "mips";
|
||||||
|
case Variant::mips64:
|
||||||
|
return "mips64";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace abi
|
||||||
|
} // namespace split
|
||||||
50
tools/split-select/Abi.h
Normal file
50
tools/split-select/Abi.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 H_ANDROID_SPLIT_ABI
|
||||||
|
#define H_ANDROID_SPLIT_ABI
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
namespace abi {
|
||||||
|
|
||||||
|
enum class Variant {
|
||||||
|
none = 0,
|
||||||
|
armeabi,
|
||||||
|
armeabi_v7a,
|
||||||
|
arm64_v8a,
|
||||||
|
x86,
|
||||||
|
x86_64,
|
||||||
|
mips,
|
||||||
|
mips64,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Family {
|
||||||
|
none,
|
||||||
|
arm,
|
||||||
|
intel,
|
||||||
|
mips,
|
||||||
|
};
|
||||||
|
|
||||||
|
Family getFamily(Variant variant);
|
||||||
|
const std::vector<Variant>& getVariants(Family family);
|
||||||
|
const char* toString(Variant variant);
|
||||||
|
|
||||||
|
} // namespace abi
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
#endif // H_ANDROID_SPLIT_ABI
|
||||||
119
tools/split-select/Android.mk
Normal file
119
tools/split-select/Android.mk
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2014 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
# This tool is prebuilt if we're doing an app-only build.
|
||||||
|
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
|
||||||
|
|
||||||
|
# TODO(adamlesinski): Enable OS X builds when I figure out how
|
||||||
|
# to build with clang and libc++
|
||||||
|
ifneq ($(HOST_OS),darwin)
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# Setup some common variables for the different build
|
||||||
|
# targets here.
|
||||||
|
# ==========================================================
|
||||||
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
|
||||||
|
main := Main.cpp
|
||||||
|
sources := \
|
||||||
|
Abi.cpp \
|
||||||
|
Grouper.cpp \
|
||||||
|
Rule.cpp \
|
||||||
|
RuleGenerator.cpp \
|
||||||
|
SplitDescription.cpp
|
||||||
|
|
||||||
|
testSources := \
|
||||||
|
Grouper_test.cpp \
|
||||||
|
Rule_test.cpp \
|
||||||
|
RuleGenerator_test.cpp
|
||||||
|
|
||||||
|
cIncludes := \
|
||||||
|
external/zlib \
|
||||||
|
frameworks/base/tools
|
||||||
|
|
||||||
|
hostLdLibs :=
|
||||||
|
hostStaticLibs := \
|
||||||
|
libaapt \
|
||||||
|
libandroidfw \
|
||||||
|
libpng \
|
||||||
|
liblog \
|
||||||
|
libutils \
|
||||||
|
libcutils \
|
||||||
|
libexpat \
|
||||||
|
libziparchive-host
|
||||||
|
|
||||||
|
cFlags := -std=c++11 -Wall -Werror
|
||||||
|
|
||||||
|
ifeq ($(HOST_OS),linux)
|
||||||
|
hostLdLibs += -lrt -ldl -lpthread
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Statically link libz for MinGW (Win SDK under Linux),
|
||||||
|
# and dynamically link for all others.
|
||||||
|
ifneq ($(strip $(USE_MINGW)),)
|
||||||
|
hostStaticLibs += libz
|
||||||
|
else
|
||||||
|
hostLdLibs += -lz
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# Build the host static library: libsplit-select
|
||||||
|
# ==========================================================
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libsplit-select
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(sources)
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES += $(cIncludes)
|
||||||
|
LOCAL_CFLAGS += $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
|
||||||
|
|
||||||
|
include $(BUILD_HOST_STATIC_LIBRARY)
|
||||||
|
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# Build the host tests: libsplit-select_tests
|
||||||
|
# ==========================================================
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := libsplit-select_tests
|
||||||
|
LOCAL_MODULE_TAGS := tests
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(testSources)
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES += $(cIncludes)
|
||||||
|
LOCAL_STATIC_LIBRARIES += libsplit-select $(hostStaticLibs)
|
||||||
|
LOCAL_LDLIBS += $(hostLdLibs)
|
||||||
|
LOCAL_CFLAGS += $(cFlags)
|
||||||
|
|
||||||
|
include $(BUILD_HOST_NATIVE_TEST)
|
||||||
|
|
||||||
|
# ==========================================================
|
||||||
|
# Build the host executable: split-select
|
||||||
|
# ==========================================================
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
LOCAL_MODULE := split-select
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := $(main)
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES += $(cIncludes)
|
||||||
|
LOCAL_STATIC_LIBRARIES += libsplit-select $(hostStaticLibs)
|
||||||
|
LOCAL_LDLIBS += $(hostLdLibs)
|
||||||
|
LOCAL_CFLAGS += $(cFlags)
|
||||||
|
|
||||||
|
include $(BUILD_HOST_EXECUTABLE)
|
||||||
|
|
||||||
|
endif # Not OS X
|
||||||
|
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
|
||||||
83
tools/split-select/Grouper.cpp
Normal file
83
tools/split-select/Grouper.cpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Grouper.h"
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <utils/KeyedVector.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
template <typename Key, typename Value>
|
||||||
|
static void addToVector(KeyedVector<Key, SortedVector<Value> >& group,
|
||||||
|
const Key& key, const Value& value) {
|
||||||
|
ssize_t idx = group.indexOfKey(key);
|
||||||
|
if (idx < 0) {
|
||||||
|
idx = group.add(key, SortedVector<Value>());
|
||||||
|
}
|
||||||
|
group.editValueAt(idx).add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<SortedVector<SplitDescription> >
|
||||||
|
groupByMutualExclusivity(const Vector<SplitDescription>& splits) {
|
||||||
|
Vector<SortedVector<SplitDescription> > groups;
|
||||||
|
|
||||||
|
// Find mutually exclusive splits and group them.
|
||||||
|
KeyedVector<SplitDescription, SortedVector<SplitDescription> > densityGroups;
|
||||||
|
KeyedVector<SplitDescription, SortedVector<SplitDescription> > abiGroups;
|
||||||
|
KeyedVector<SplitDescription, SortedVector<SplitDescription> > localeGroups;
|
||||||
|
for (const SplitDescription& split : splits) {
|
||||||
|
if (split.config.density != 0) {
|
||||||
|
SplitDescription key(split);
|
||||||
|
key.config.density = 0;
|
||||||
|
key.config.sdkVersion = 0; // Ignore density so we can support anydpi.
|
||||||
|
addToVector(densityGroups, key, split);
|
||||||
|
} else if (split.abi != abi::Variant::none) {
|
||||||
|
SplitDescription key(split);
|
||||||
|
key.abi = abi::Variant::none;
|
||||||
|
addToVector(abiGroups, key, split);
|
||||||
|
} else if (split.config.locale != 0) {
|
||||||
|
SplitDescription key(split);
|
||||||
|
key.config.clearLocale();
|
||||||
|
addToVector(localeGroups, key, split);
|
||||||
|
} else {
|
||||||
|
groups.add();
|
||||||
|
groups.editTop().add(split);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t densityCount = densityGroups.size();
|
||||||
|
for (size_t i = 0; i < densityCount; i++) {
|
||||||
|
groups.add(densityGroups[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t abiCount = abiGroups.size();
|
||||||
|
for (size_t i = 0; i < abiCount; i++) {
|
||||||
|
groups.add(abiGroups[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t localeCount = localeGroups.size();
|
||||||
|
for (size_t i = 0; i < localeCount; i++) {
|
||||||
|
groups.add(localeGroups[i]);
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
32
tools/split-select/Grouper.h
Normal file
32
tools/split-select/Grouper.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 H_ANDROID_SPLIT_GROUPER
|
||||||
|
#define H_ANDROID_SPLIT_GROUPER
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <utils/SortedVector.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
android::Vector<android::SortedVector<SplitDescription> >
|
||||||
|
groupByMutualExclusivity(const android::Vector<SplitDescription>& splits);
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
#endif // H_ANDROID_SPLIT_GROUPER
|
||||||
151
tools/split-select/Grouper_test.cpp
Normal file
151
tools/split-select/Grouper_test.cpp
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Grouper.h"
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
class GrouperTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() {
|
||||||
|
Vector<SplitDescription> splits;
|
||||||
|
addSplit(splits, "en-rUS-sw600dp-hdpi");
|
||||||
|
addSplit(splits, "fr-rFR-sw600dp-hdpi");
|
||||||
|
addSplit(splits, "fr-rFR-sw600dp-xhdpi");
|
||||||
|
addSplit(splits, ":armeabi");
|
||||||
|
addSplit(splits, "en-rUS-sw300dp-xhdpi");
|
||||||
|
addSplit(splits, "large");
|
||||||
|
addSplit(splits, "pl-rPL");
|
||||||
|
addSplit(splits, "xlarge");
|
||||||
|
addSplit(splits, "en-rUS-sw600dp-xhdpi");
|
||||||
|
addSplit(splits, "en-rUS-sw300dp-hdpi");
|
||||||
|
addSplit(splits, "xxhdpi");
|
||||||
|
addSplit(splits, "hdpi");
|
||||||
|
addSplit(splits, "de-rDE");
|
||||||
|
addSplit(splits, "xhdpi");
|
||||||
|
addSplit(splits, ":x86");
|
||||||
|
addSplit(splits, "anydpi");
|
||||||
|
addSplit(splits, "v7");
|
||||||
|
addSplit(splits, "v8");
|
||||||
|
addSplit(splits, "sw600dp");
|
||||||
|
addSplit(splits, "sw300dp");
|
||||||
|
mGroups = groupByMutualExclusivity(splits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSplit(Vector<SplitDescription>& splits, const char* str);
|
||||||
|
void expectHasGroupWithSplits(std::initializer_list<const char*> l);
|
||||||
|
|
||||||
|
Vector<SortedVector<SplitDescription> > mGroups;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(GrouperTest, shouldHaveCorrectNumberOfGroups) {
|
||||||
|
EXPECT_EQ(12u, mGroups.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GrouperTest, shouldGroupDensities) {
|
||||||
|
expectHasGroupWithSplits({"en-rUS-sw300dp-hdpi", "en-rUS-sw300dp-xhdpi"});
|
||||||
|
expectHasGroupWithSplits({"en-rUS-sw600dp-hdpi", "en-rUS-sw600dp-xhdpi"});
|
||||||
|
expectHasGroupWithSplits({"fr-rFR-sw600dp-hdpi", "fr-rFR-sw600dp-xhdpi"});
|
||||||
|
expectHasGroupWithSplits({"hdpi", "xhdpi", "xxhdpi", "anydpi"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GrouperTest, shouldGroupAbi) {
|
||||||
|
expectHasGroupWithSplits({":armeabi", ":x86"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GrouperTest, shouldGroupLocale) {
|
||||||
|
expectHasGroupWithSplits({"pl-rPL", "de-rDE"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(GrouperTest, shouldGroupEachSplitIntoItsOwnGroup) {
|
||||||
|
expectHasGroupWithSplits({"large"});
|
||||||
|
expectHasGroupWithSplits({"xlarge"});
|
||||||
|
expectHasGroupWithSplits({"v7"});
|
||||||
|
expectHasGroupWithSplits({"v8"});
|
||||||
|
expectHasGroupWithSplits({"sw600dp"});
|
||||||
|
expectHasGroupWithSplits({"sw300dp"});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helper methods
|
||||||
|
//
|
||||||
|
|
||||||
|
void GrouperTest::expectHasGroupWithSplits(std::initializer_list<const char*> l) {
|
||||||
|
Vector<SplitDescription> splits;
|
||||||
|
for (const char* str : l) {
|
||||||
|
splits.add();
|
||||||
|
if (!SplitDescription::parse(String8(str), &splits.editTop())) {
|
||||||
|
ADD_FAILURE() << "Failed to parse SplitDescription " << str;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const size_t splitCount = splits.size();
|
||||||
|
|
||||||
|
const size_t groupCount = mGroups.size();
|
||||||
|
for (size_t i = 0; i < groupCount; i++) {
|
||||||
|
const SortedVector<SplitDescription>& group = mGroups[i];
|
||||||
|
if (group.size() != splitCount) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t found = 0;
|
||||||
|
for (size_t j = 0; j < splitCount; j++) {
|
||||||
|
if (group.indexOf(splits[j]) >= 0) {
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found == splitCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String8 errorMessage("Failed to find expected group [");
|
||||||
|
for (size_t i = 0; i < splitCount; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
errorMessage.append(", ");
|
||||||
|
}
|
||||||
|
errorMessage.append(splits[i].toString());
|
||||||
|
}
|
||||||
|
errorMessage.append("].\nActual:\n");
|
||||||
|
|
||||||
|
for (size_t i = 0; i < groupCount; i++) {
|
||||||
|
errorMessage.appendFormat("Group %d:\n", int(i + 1));
|
||||||
|
const SortedVector<SplitDescription>& group = mGroups[i];
|
||||||
|
for (size_t j = 0; j < group.size(); j++) {
|
||||||
|
errorMessage.append(" ");
|
||||||
|
errorMessage.append(group[j].toString());
|
||||||
|
errorMessage.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ADD_FAILURE() << errorMessage.string();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GrouperTest::addSplit(Vector<SplitDescription>& splits, const char* str) {
|
||||||
|
splits.add();
|
||||||
|
EXPECT_TRUE(SplitDescription::parse(String8(str), &splits.editTop()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
314
tools/split-select/Main.cpp
Normal file
314
tools/split-select/Main.cpp
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "aapt/AaptUtil.h"
|
||||||
|
|
||||||
|
#include "Grouper.h"
|
||||||
|
#include "Rule.h"
|
||||||
|
#include "RuleGenerator.h"
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <androidfw/AssetManager.h>
|
||||||
|
#include <androidfw/ResourceTypes.h>
|
||||||
|
#include <utils/KeyedVector.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
static void usage() {
|
||||||
|
fprintf(stderr,
|
||||||
|
"split-select --help\n"
|
||||||
|
"split-select --target <config> --split <path/to/apk> [--split <path/to/apk> [...]]\n"
|
||||||
|
"split-select --generate --split <path/to/apk> [--split <path/to/apk> [...]]\n"
|
||||||
|
"\n"
|
||||||
|
" --help Displays more information about this program.\n"
|
||||||
|
" --target <config> Performs the Split APK selection on the given configuration.\n"
|
||||||
|
" --generate Generates the logic for selecting the Split APK, in JSON format.\n"
|
||||||
|
" --split <path/to/apk> Includes a Split APK in the selection process.\n"
|
||||||
|
"\n"
|
||||||
|
" Where <config> is an extended AAPT resource qualifier of the form\n"
|
||||||
|
" 'resource-qualifiers:extended-qualifiers', where 'resource-qualifiers' is an AAPT resource\n"
|
||||||
|
" qualifier (ex: en-rUS-sw600dp-xhdpi), and 'extended-qualifiers' is an ordered list of one\n"
|
||||||
|
" qualifier (or none) from each category:\n"
|
||||||
|
" Architecture: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help() {
|
||||||
|
usage();
|
||||||
|
fprintf(stderr, "\n"
|
||||||
|
" Generates the logic for selecting a Split APK given some target Android device configuration.\n"
|
||||||
|
" Using the flag --generate will emit a JSON encoded tree of rules that must be satisfied in order\n"
|
||||||
|
" to install the given Split APK. Using the flag --target along with the device configuration\n"
|
||||||
|
" will emit the set of Split APKs to install, following the same logic that would have been emitted\n"
|
||||||
|
" via JSON.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
class SplitSelector {
|
||||||
|
public:
|
||||||
|
SplitSelector() = default;
|
||||||
|
SplitSelector(const Vector<SplitDescription>& splits);
|
||||||
|
|
||||||
|
Vector<SplitDescription> getBestSplits(const SplitDescription& target) const;
|
||||||
|
|
||||||
|
template <typename RuleGenerator>
|
||||||
|
KeyedVector<SplitDescription, sp<Rule> > getRules() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Vector<SortedVector<SplitDescription> > mGroups;
|
||||||
|
};
|
||||||
|
|
||||||
|
SplitSelector::SplitSelector(const Vector<SplitDescription>& splits)
|
||||||
|
: mGroups(groupByMutualExclusivity(splits)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void selectBestFromGroup(const SortedVector<SplitDescription>& splits,
|
||||||
|
const SplitDescription& target, Vector<SplitDescription>& splitsOut) {
|
||||||
|
SplitDescription bestSplit;
|
||||||
|
bool isSet = false;
|
||||||
|
const size_t splitCount = splits.size();
|
||||||
|
for (size_t j = 0; j < splitCount; j++) {
|
||||||
|
const SplitDescription& thisSplit = splits[j];
|
||||||
|
if (!thisSplit.match(target)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSet || thisSplit.isBetterThan(bestSplit, target)) {
|
||||||
|
isSet = true;
|
||||||
|
bestSplit = thisSplit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSet) {
|
||||||
|
splitsOut.add(bestSplit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const {
|
||||||
|
Vector<SplitDescription> bestSplits;
|
||||||
|
const size_t groupCount = mGroups.size();
|
||||||
|
for (size_t i = 0; i < groupCount; i++) {
|
||||||
|
selectBestFromGroup(mGroups[i], target, bestSplits);
|
||||||
|
}
|
||||||
|
return bestSplits;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename RuleGenerator>
|
||||||
|
KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const {
|
||||||
|
KeyedVector<SplitDescription, sp<Rule> > rules;
|
||||||
|
|
||||||
|
const size_t groupCount = mGroups.size();
|
||||||
|
for (size_t i = 0; i < groupCount; i++) {
|
||||||
|
const SortedVector<SplitDescription>& splits = mGroups[i];
|
||||||
|
const size_t splitCount = splits.size();
|
||||||
|
for (size_t j = 0; j < splitCount; j++) {
|
||||||
|
sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j));
|
||||||
|
if (rule != NULL) {
|
||||||
|
rules.add(splits[j], rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) {
|
||||||
|
const SplitSelector selector(splits);
|
||||||
|
return selector.getBestSplits(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) {
|
||||||
|
Vector<SplitDescription> allSplits;
|
||||||
|
const size_t apkSplitCount = splits.size();
|
||||||
|
for (size_t i = 0; i < apkSplitCount; i++) {
|
||||||
|
allSplits.appendVector(splits[i]);
|
||||||
|
}
|
||||||
|
const SplitSelector selector(allSplits);
|
||||||
|
KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules<RuleGenerator>());
|
||||||
|
|
||||||
|
fprintf(stdout, "[\n");
|
||||||
|
for (size_t i = 0; i < apkSplitCount; i++) {
|
||||||
|
sp<Rule> masterRule = new Rule();
|
||||||
|
masterRule->op = Rule::OR_SUBRULES;
|
||||||
|
const Vector<SplitDescription>& splitDescriptions = splits[i];
|
||||||
|
const size_t splitDescriptionCount = splitDescriptions.size();
|
||||||
|
for (size_t j = 0; j < splitDescriptionCount; j++) {
|
||||||
|
masterRule->subrules.add(rules.valueFor(splitDescriptions[j]));
|
||||||
|
}
|
||||||
|
masterRule = Rule::simplify(masterRule);
|
||||||
|
fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }%s\n",
|
||||||
|
splits.keyAt(i).string(),
|
||||||
|
masterRule->toJson(2).string(),
|
||||||
|
i < apkSplitCount - 1 ? "," : "");
|
||||||
|
}
|
||||||
|
fprintf(stdout, "]\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void removeRuntimeQualifiers(ConfigDescription* outConfig) {
|
||||||
|
outConfig->imsi = 0;
|
||||||
|
outConfig->orientation = ResTable_config::ORIENTATION_ANY;
|
||||||
|
outConfig->screenWidth = ResTable_config::SCREENWIDTH_ANY;
|
||||||
|
outConfig->screenHeight = ResTable_config::SCREENHEIGHT_ANY;
|
||||||
|
outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) {
|
||||||
|
AssetManager assetManager;
|
||||||
|
Vector<SplitDescription> splits;
|
||||||
|
int32_t cookie = 0;
|
||||||
|
if (!assetManager.addAssetPath(path, &cookie)) {
|
||||||
|
return splits;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ResTable& res = assetManager.getResources(false);
|
||||||
|
if (res.getError() == NO_ERROR) {
|
||||||
|
Vector<ResTable_config> configs;
|
||||||
|
res.getConfigurations(&configs);
|
||||||
|
const size_t configCount = configs.size();
|
||||||
|
for (size_t i = 0; i < configCount; i++) {
|
||||||
|
splits.add();
|
||||||
|
splits.editTop().config = configs[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDir* dir = assetManager.openNonAssetDir(cookie, "lib");
|
||||||
|
if (dir != NULL) {
|
||||||
|
const size_t fileCount = dir->getFileCount();
|
||||||
|
for (size_t i = 0; i < fileCount; i++) {
|
||||||
|
splits.add();
|
||||||
|
Vector<String8> parts = AaptUtil::splitAndLowerCase(dir->getFileName(i), '-');
|
||||||
|
if (parseAbi(parts, 0, &splits.editTop()) < 0) {
|
||||||
|
fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).string());
|
||||||
|
splits.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete dir;
|
||||||
|
}
|
||||||
|
return splits;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int main(int argc, char** argv) {
|
||||||
|
// Skip over the first argument.
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
bool generateFlag = false;
|
||||||
|
String8 targetConfigStr;
|
||||||
|
Vector<String8> splitApkPaths;
|
||||||
|
while (argc > 0) {
|
||||||
|
const String8 arg(*argv);
|
||||||
|
if (arg == "--target") {
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
if (argc < 1) {
|
||||||
|
fprintf(stderr, "Missing parameter for --split.\n");
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
targetConfigStr.setTo(*argv);
|
||||||
|
} else if (arg == "--split") {
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
if (argc < 1) {
|
||||||
|
fprintf(stderr, "Missing parameter for --split.\n");
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
splitApkPaths.add(String8(*argv));
|
||||||
|
} else if (arg == "--generate") {
|
||||||
|
generateFlag = true;
|
||||||
|
} else if (arg == "--help") {
|
||||||
|
help();
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Unknown argument '%s'\n", arg.string());
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generateFlag && targetConfigStr == "") {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (splitApkPaths.size() == 0) {
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitDescription targetSplit;
|
||||||
|
if (!generateFlag) {
|
||||||
|
if (!SplitDescription::parse(targetConfigStr, &targetSplit)) {
|
||||||
|
fprintf(stderr, "Invalid --target config: '%s'\n",
|
||||||
|
targetConfigStr.string());
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't want to match on things that will change at run-time
|
||||||
|
// (orientation, w/h, etc.).
|
||||||
|
removeRuntimeQualifiers(&targetSplit.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap;
|
||||||
|
KeyedVector<SplitDescription, String8> splitApkPathMap;
|
||||||
|
Vector<SplitDescription> splitConfigs;
|
||||||
|
const size_t splitCount = splitApkPaths.size();
|
||||||
|
for (size_t i = 0; i < splitCount; i++) {
|
||||||
|
Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]);
|
||||||
|
if (splits.isEmpty()) {
|
||||||
|
fprintf(stderr, "Invalid --split path: '%s'. No splits found.\n",
|
||||||
|
splitApkPaths[i].string());
|
||||||
|
usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
apkPathSplitMap.replaceValueFor(splitApkPaths[i], splits);
|
||||||
|
const size_t apkSplitDescriptionCount = splits.size();
|
||||||
|
for (size_t j = 0; j < apkSplitDescriptionCount; j++) {
|
||||||
|
splitApkPathMap.replaceValueFor(splits[j], splitApkPaths[i]);
|
||||||
|
}
|
||||||
|
splitConfigs.appendVector(splits);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generateFlag) {
|
||||||
|
Vector<SplitDescription> matchingConfigs = select(targetSplit, splitConfigs);
|
||||||
|
const size_t matchingConfigCount = matchingConfigs.size();
|
||||||
|
SortedVector<String8> matchingSplitPaths;
|
||||||
|
for (size_t i = 0; i < matchingConfigCount; i++) {
|
||||||
|
matchingSplitPaths.add(splitApkPathMap.valueFor(matchingConfigs[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t matchingSplitApkPathCount = matchingSplitPaths.size();
|
||||||
|
for (size_t i = 0; i < matchingSplitApkPathCount; i++) {
|
||||||
|
fprintf(stderr, "%s\n", matchingSplitPaths[i].string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generate(apkPathSplitMap);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return split::main(argc, argv);
|
||||||
|
}
|
||||||
196
tools/split-select/Rule.cpp
Normal file
196
tools/split-select/Rule.cpp
Normal file
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Rule.h"
|
||||||
|
|
||||||
|
#include <utils/String8.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
inline static void indentStr(String8& str, int indent) {
|
||||||
|
while (indent > 0) {
|
||||||
|
str.append(" ");
|
||||||
|
indent--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String8 Rule::toJson(int indent) const {
|
||||||
|
String8 str;
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("{\n");
|
||||||
|
indent++;
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"op\": \"");
|
||||||
|
switch (op) {
|
||||||
|
case ALWAYS_TRUE:
|
||||||
|
str.append("ALWAYS_TRUE");
|
||||||
|
break;
|
||||||
|
case GREATER_THAN:
|
||||||
|
str.append("GREATER_THAN");
|
||||||
|
break;
|
||||||
|
case LESS_THAN:
|
||||||
|
str.append("LESS_THAN");
|
||||||
|
break;
|
||||||
|
case EQUALS:
|
||||||
|
str.append("EQUALS");
|
||||||
|
break;
|
||||||
|
case AND_SUBRULES:
|
||||||
|
str.append("AND_SUBRULES");
|
||||||
|
break;
|
||||||
|
case OR_SUBRULES:
|
||||||
|
str.append("OR_SUBRULES");
|
||||||
|
break;
|
||||||
|
case CONTAINS_ANY:
|
||||||
|
str.append("CONTAINS_ANY");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str.appendFormat("%d", op);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str.append("\"");
|
||||||
|
|
||||||
|
if (negate) {
|
||||||
|
str.append(",\n");
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"negate\": true");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool includeKey = true;
|
||||||
|
switch (op) {
|
||||||
|
case AND_SUBRULES:
|
||||||
|
case OR_SUBRULES:
|
||||||
|
includeKey = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeKey) {
|
||||||
|
str.append(",\n");
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"property\": \"");
|
||||||
|
switch (key) {
|
||||||
|
case NONE:
|
||||||
|
str.append("NONE");
|
||||||
|
break;
|
||||||
|
case SDK_VERSION:
|
||||||
|
str.append("SDK_VERSION");
|
||||||
|
break;
|
||||||
|
case SCREEN_DENSITY:
|
||||||
|
str.append("SCREEN_DENSITY");
|
||||||
|
break;
|
||||||
|
case NATIVE_PLATFORM:
|
||||||
|
str.append("NATIVE_PLATFORM");
|
||||||
|
break;
|
||||||
|
case LANGUAGE:
|
||||||
|
str.append("LANGUAGE");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
str.appendFormat("%d", key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
str.append("\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == AND_SUBRULES || op == OR_SUBRULES) {
|
||||||
|
str.append(",\n");
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"subrules\": [\n");
|
||||||
|
const size_t subruleCount = subrules.size();
|
||||||
|
for (size_t i = 0; i < subruleCount; i++) {
|
||||||
|
str.append(subrules[i]->toJson(indent + 1));
|
||||||
|
if (i != subruleCount - 1) {
|
||||||
|
str.append(",");
|
||||||
|
}
|
||||||
|
str.append("\n");
|
||||||
|
}
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("]");
|
||||||
|
} else {
|
||||||
|
switch (key) {
|
||||||
|
case SDK_VERSION:
|
||||||
|
case SCREEN_DENSITY: {
|
||||||
|
str.append(",\n");
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"args\": [");
|
||||||
|
const size_t argCount = longArgs.size();
|
||||||
|
for (size_t i = 0; i < argCount; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
str.append(", ");
|
||||||
|
}
|
||||||
|
str.appendFormat("%d", longArgs[i]);
|
||||||
|
}
|
||||||
|
str.append("]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LANGUAGE:
|
||||||
|
case NATIVE_PLATFORM: {
|
||||||
|
str.append(",\n");
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("\"args\": [");
|
||||||
|
const size_t argCount = stringArgs.size();
|
||||||
|
for (size_t i = 0; i < argCount; i++) {
|
||||||
|
if (i != 0) {
|
||||||
|
str.append(", ");
|
||||||
|
}
|
||||||
|
str.append(stringArgs[i]);
|
||||||
|
}
|
||||||
|
str.append("]");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str.append("\n");
|
||||||
|
indent--;
|
||||||
|
indentStr(str, indent);
|
||||||
|
str.append("}");
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> Rule::simplify(sp<Rule> rule) {
|
||||||
|
if (rule->op != AND_SUBRULES && rule->op != OR_SUBRULES) {
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<sp<Rule> > newSubrules;
|
||||||
|
newSubrules.setCapacity(rule->subrules.size());
|
||||||
|
const size_t subruleCount = rule->subrules.size();
|
||||||
|
for (size_t i = 0; i < subruleCount; i++) {
|
||||||
|
sp<Rule> simplifiedRule = simplify(rule->subrules.editItemAt(i));
|
||||||
|
if (simplifiedRule != NULL) {
|
||||||
|
if (simplifiedRule->op == rule->op) {
|
||||||
|
newSubrules.appendVector(simplifiedRule->subrules);
|
||||||
|
} else {
|
||||||
|
newSubrules.add(simplifiedRule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t newSubruleCount = newSubrules.size();
|
||||||
|
if (newSubruleCount == 0) {
|
||||||
|
return NULL;
|
||||||
|
} else if (subruleCount == 1) {
|
||||||
|
return newSubrules.editTop();
|
||||||
|
}
|
||||||
|
rule->subrules = newSubrules;
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
78
tools/split-select/Rule.h
Normal file
78
tools/split-select/Rule.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 H_ANDROID_SPLIT_RULE
|
||||||
|
#define H_ANDROID_SPLIT_RULE
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <utils/RefBase.h>
|
||||||
|
#include <utils/StrongPointer.h>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
struct Rule : public virtual android::RefBase {
|
||||||
|
inline Rule();
|
||||||
|
|
||||||
|
enum Operator {
|
||||||
|
LESS_THAN = 1,
|
||||||
|
GREATER_THAN,
|
||||||
|
EQUALS,
|
||||||
|
CONTAINS_ANY,
|
||||||
|
CONTAINS_ALL,
|
||||||
|
IS_TRUE,
|
||||||
|
IS_FALSE,
|
||||||
|
AND_SUBRULES,
|
||||||
|
OR_SUBRULES,
|
||||||
|
ALWAYS_TRUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
Operator op;
|
||||||
|
|
||||||
|
enum Key {
|
||||||
|
NONE = 0,
|
||||||
|
SDK_VERSION,
|
||||||
|
SCREEN_DENSITY,
|
||||||
|
LANGUAGE,
|
||||||
|
NATIVE_PLATFORM,
|
||||||
|
TOUCH_SCREEN,
|
||||||
|
SCREEN_SIZE,
|
||||||
|
SCREEN_LAYOUT,
|
||||||
|
};
|
||||||
|
|
||||||
|
Key key;
|
||||||
|
bool negate;
|
||||||
|
|
||||||
|
android::Vector<android::String8> stringArgs;
|
||||||
|
android::Vector<int> longArgs;
|
||||||
|
android::Vector<double> doubleArgs;
|
||||||
|
android::Vector<android::sp<Rule> > subrules;
|
||||||
|
|
||||||
|
android::String8 toJson(int indent=0) const;
|
||||||
|
|
||||||
|
static android::sp<Rule> simplify(android::sp<Rule> rule);
|
||||||
|
};
|
||||||
|
|
||||||
|
Rule::Rule()
|
||||||
|
: op(ALWAYS_TRUE)
|
||||||
|
, key(NONE)
|
||||||
|
, negate(false) {}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
#endif // H_ANDROID_SPLIT_RULE
|
||||||
153
tools/split-select/RuleGenerator.cpp
Normal file
153
tools/split-select/RuleGenerator.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RuleGenerator.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <vector>
|
||||||
|
#include <androidfw/ResourceTypes.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
// Calculate the point at which the density selection changes between l and h.
|
||||||
|
static inline int findMid(int l, int h) {
|
||||||
|
double root = sqrt((h*h) + (8*l*h));
|
||||||
|
return (double(-h) + root) / 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> RuleGenerator::generateDensity(const Vector<int>& allDensities, size_t index) {
|
||||||
|
sp<Rule> densityRule = new Rule();
|
||||||
|
densityRule->op = Rule::AND_SUBRULES;
|
||||||
|
|
||||||
|
const bool anyDensity = allDensities[index] == ResTable_config::DENSITY_ANY;
|
||||||
|
sp<Rule> any = new Rule();
|
||||||
|
any->op = Rule::EQUALS;
|
||||||
|
any->key = Rule::SCREEN_DENSITY;
|
||||||
|
any->longArgs.add((int)ResTable_config::DENSITY_ANY);
|
||||||
|
any->negate = !anyDensity;
|
||||||
|
densityRule->subrules.add(any);
|
||||||
|
|
||||||
|
if (!anyDensity) {
|
||||||
|
if (index > 0) {
|
||||||
|
sp<Rule> gt = new Rule();
|
||||||
|
gt->op = Rule::GREATER_THAN;
|
||||||
|
gt->key = Rule::SCREEN_DENSITY;
|
||||||
|
gt->longArgs.add(findMid(allDensities[index - 1], allDensities[index]) - 1);
|
||||||
|
densityRule->subrules.add(gt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index + 1 < allDensities.size() && allDensities[index + 1] != ResTable_config::DENSITY_ANY) {
|
||||||
|
sp<Rule> lt = new Rule();
|
||||||
|
lt->op = Rule::LESS_THAN;
|
||||||
|
lt->key = Rule::SCREEN_DENSITY;
|
||||||
|
lt->longArgs.add(findMid(allDensities[index], allDensities[index + 1]));
|
||||||
|
densityRule->subrules.add(lt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return densityRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> RuleGenerator::generateAbi(const Vector<abi::Variant>& splitAbis, size_t index) {
|
||||||
|
const abi::Variant thisAbi = splitAbis[index];
|
||||||
|
const std::vector<abi::Variant>& familyVariants = abi::getVariants(abi::getFamily(thisAbi));
|
||||||
|
|
||||||
|
std::vector<abi::Variant>::const_iterator start =
|
||||||
|
std::find(familyVariants.begin(), familyVariants.end(), thisAbi);
|
||||||
|
|
||||||
|
std::vector<abi::Variant>::const_iterator end = familyVariants.end();
|
||||||
|
if (index + 1 < splitAbis.size()) {
|
||||||
|
end = std::find(start, familyVariants.end(), splitAbis[index + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> abiRule = new Rule();
|
||||||
|
abiRule->op = Rule::CONTAINS_ANY;
|
||||||
|
abiRule->key = Rule::NATIVE_PLATFORM;
|
||||||
|
while (start != end) {
|
||||||
|
abiRule->stringArgs.add(String8(abi::toString(*start)));
|
||||||
|
++start;
|
||||||
|
}
|
||||||
|
return abiRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> RuleGenerator::generate(const SortedVector<SplitDescription>& group, size_t index) {
|
||||||
|
sp<Rule> rootRule = new Rule();
|
||||||
|
rootRule->op = Rule::AND_SUBRULES;
|
||||||
|
|
||||||
|
if (group[index].config.locale != 0) {
|
||||||
|
sp<Rule> locale = new Rule();
|
||||||
|
locale->op = Rule::EQUALS;
|
||||||
|
locale->key = Rule::LANGUAGE;
|
||||||
|
char str[RESTABLE_MAX_LOCALE_LEN];
|
||||||
|
group[index].config.getBcp47Locale(str);
|
||||||
|
locale->stringArgs.add(String8(str));
|
||||||
|
rootRule->subrules.add(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group[index].config.sdkVersion != 0) {
|
||||||
|
sp<Rule> sdk = new Rule();
|
||||||
|
sdk->op = Rule::GREATER_THAN;
|
||||||
|
sdk->key = Rule::SDK_VERSION;
|
||||||
|
sdk->longArgs.add(group[index].config.sdkVersion - 1);
|
||||||
|
rootRule->subrules.add(sdk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group[index].config.density != 0) {
|
||||||
|
size_t densityIndex = 0;
|
||||||
|
Vector<int> allDensities;
|
||||||
|
allDensities.add(group[index].config.density);
|
||||||
|
|
||||||
|
const size_t groupSize = group.size();
|
||||||
|
for (size_t i = 0; i < groupSize; i++) {
|
||||||
|
if (group[i].config.density != group[index].config.density) {
|
||||||
|
// This group differs by density.
|
||||||
|
allDensities.clear();
|
||||||
|
for (size_t j = 0; j < groupSize; j++) {
|
||||||
|
allDensities.add(group[j].config.density);
|
||||||
|
}
|
||||||
|
densityIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootRule->subrules.add(generateDensity(allDensities, densityIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group[index].abi != abi::Variant::none) {
|
||||||
|
size_t abiIndex = 0;
|
||||||
|
Vector<abi::Variant> allVariants;
|
||||||
|
allVariants.add(group[index].abi);
|
||||||
|
|
||||||
|
const size_t groupSize = group.size();
|
||||||
|
for (size_t i = 0; i < groupSize; i++) {
|
||||||
|
if (group[i].abi != group[index].abi) {
|
||||||
|
// This group differs by ABI.
|
||||||
|
allVariants.clear();
|
||||||
|
for (size_t j = 0; j < groupSize; j++) {
|
||||||
|
allVariants.add(group[j].abi);
|
||||||
|
}
|
||||||
|
abiIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rootRule->subrules.add(generateAbi(allVariants, abiIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rootRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
39
tools/split-select/RuleGenerator.h
Normal file
39
tools/split-select/RuleGenerator.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 H_ANDROID_SPLIT_RULE_GENERATOR
|
||||||
|
#define H_ANDROID_SPLIT_RULE_GENERATOR
|
||||||
|
|
||||||
|
#include "Abi.h"
|
||||||
|
#include "Rule.h"
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <utils/SortedVector.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
struct RuleGenerator {
|
||||||
|
// Generate rules for a Split given the group of mutually exclusive splits it belongs to
|
||||||
|
static android::sp<Rule> generate(const android::SortedVector<SplitDescription>& group, size_t index);
|
||||||
|
|
||||||
|
static android::sp<Rule> generateAbi(const android::Vector<abi::Variant>& allVariants, size_t index);
|
||||||
|
static android::sp<Rule> generateDensity(const android::Vector<int>& allDensities, size_t index);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
#endif // H_ANDROID_SPLIT_RULE_GENERATOR
|
||||||
155
tools/split-select/RuleGenerator_test.cpp
Normal file
155
tools/split-select/RuleGenerator_test.cpp
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "RuleGenerator.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
static void expectDensityRule(const Vector<int>& densities, int density, int greaterThan, int lessThan);
|
||||||
|
static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant,
|
||||||
|
std::initializer_list<const char*> matches);
|
||||||
|
|
||||||
|
TEST(RuleGeneratorTest, testAbiRules) {
|
||||||
|
Vector<abi::Variant> abis;
|
||||||
|
abis.add(abi::Variant::armeabi);
|
||||||
|
abis.add(abi::Variant::armeabi_v7a);
|
||||||
|
abis.add(abi::Variant::x86);
|
||||||
|
std::sort(abis.begin(), abis.end());
|
||||||
|
|
||||||
|
expectAbiRule(abis, abi::Variant::armeabi, {"armeabi"});
|
||||||
|
expectAbiRule(abis, abi::Variant::armeabi_v7a, {"armeabi-v7a", "arm64-v8a"});
|
||||||
|
expectAbiRule(abis, abi::Variant::x86, {"x86", "x86_64"});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RuleGeneratorTest, testDensityRules) {
|
||||||
|
Vector<int> densities;
|
||||||
|
densities.add(ConfigDescription::DENSITY_HIGH);
|
||||||
|
densities.add(ConfigDescription::DENSITY_XHIGH);
|
||||||
|
densities.add(ConfigDescription::DENSITY_XXHIGH);
|
||||||
|
densities.add(ConfigDescription::DENSITY_ANY);
|
||||||
|
|
||||||
|
ASSERT_LT(263, ConfigDescription::DENSITY_XHIGH);
|
||||||
|
ASSERT_GT(262, ConfigDescription::DENSITY_HIGH);
|
||||||
|
ASSERT_LT(363, ConfigDescription::DENSITY_XXHIGH);
|
||||||
|
ASSERT_GT(362, ConfigDescription::DENSITY_XHIGH);
|
||||||
|
|
||||||
|
expectDensityRule(densities, ConfigDescription::DENSITY_HIGH, 0, 263);
|
||||||
|
expectDensityRule(densities, ConfigDescription::DENSITY_XHIGH, 262, 363);
|
||||||
|
expectDensityRule(densities, ConfigDescription::DENSITY_XXHIGH, 362, 0);
|
||||||
|
expectDensityRule(densities, ConfigDescription::DENSITY_ANY, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Helper methods.
|
||||||
|
//
|
||||||
|
|
||||||
|
static void expectDensityRule(const Vector<int>& densities, int density, int greaterThan, int lessThan) {
|
||||||
|
const int* iter = std::find(densities.begin(), densities.end(), density);
|
||||||
|
if (densities.end() == iter) {
|
||||||
|
ADD_FAILURE() << density << "dpi was not in the density list.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> rule = RuleGenerator::generateDensity(densities, iter - densities.begin());
|
||||||
|
if (rule->op != Rule::AND_SUBRULES) {
|
||||||
|
ADD_FAILURE() << "Op in rule for " << density << "dpi is not Rule::AND_SUBRULES.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
|
||||||
|
bool isAnyDpi = density == ConfigDescription::DENSITY_ANY;
|
||||||
|
|
||||||
|
sp<Rule> anyDpiRule = rule->subrules[index++];
|
||||||
|
EXPECT_EQ(Rule::EQUALS, anyDpiRule->op)
|
||||||
|
<< "for " << density << "dpi ANY DPI rule";
|
||||||
|
EXPECT_EQ(Rule::SCREEN_DENSITY, anyDpiRule->key)
|
||||||
|
<< "for " << density << "dpi ANY DPI rule";
|
||||||
|
EXPECT_EQ(isAnyDpi == false, anyDpiRule->negate)
|
||||||
|
<< "for " << density << "dpi ANY DPI rule";
|
||||||
|
if (anyDpiRule->longArgs.size() == 1) {
|
||||||
|
EXPECT_EQ(ConfigDescription::DENSITY_ANY, anyDpiRule->longArgs[0])
|
||||||
|
<< "for " << density << "dpi ANY DPI rule";
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(1u, anyDpiRule->longArgs.size())
|
||||||
|
<< "for " << density << "dpi ANY DPI rule";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (greaterThan != 0) {
|
||||||
|
sp<Rule> greaterThanRule = rule->subrules[index++];
|
||||||
|
EXPECT_EQ(Rule::GREATER_THAN, greaterThanRule->op)
|
||||||
|
<< "for " << density << "dpi GREATER_THAN rule";
|
||||||
|
EXPECT_EQ(Rule::SCREEN_DENSITY, greaterThanRule->key)
|
||||||
|
<< "for " << density << "dpi GREATER_THAN rule";
|
||||||
|
if (greaterThanRule->longArgs.size() == 1) {
|
||||||
|
EXPECT_EQ(greaterThan, greaterThanRule->longArgs[0])
|
||||||
|
<< "for " << density << "dpi GREATER_THAN rule";
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(1u, greaterThanRule->longArgs.size())
|
||||||
|
<< "for " << density << "dpi GREATER_THAN rule";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lessThan != 0) {
|
||||||
|
sp<Rule> lessThanRule = rule->subrules[index++];
|
||||||
|
EXPECT_EQ(Rule::LESS_THAN, lessThanRule->op)
|
||||||
|
<< "for " << density << "dpi LESS_THAN rule";
|
||||||
|
EXPECT_EQ(Rule::SCREEN_DENSITY, lessThanRule->key)
|
||||||
|
<< "for " << density << "dpi LESS_THAN rule";
|
||||||
|
if (lessThanRule->longArgs.size() == 1) {
|
||||||
|
EXPECT_EQ(lessThan, lessThanRule->longArgs[0])
|
||||||
|
<< "for " << density << "dpi LESS_THAN rule";
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(1u, lessThanRule->longArgs.size())
|
||||||
|
<< "for " << density << "dpi LESS_THAN rule";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expectAbiRule(const Vector<abi::Variant>& abis, abi::Variant variant,
|
||||||
|
std::initializer_list<const char*> matches) {
|
||||||
|
const abi::Variant* iter = std::find(abis.begin(), abis.end(), variant);
|
||||||
|
if (abis.end() == iter) {
|
||||||
|
ADD_FAILURE() << abi::toString(variant) << " was not in the abi list.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sp<Rule> rule = RuleGenerator::generateAbi(abis, iter - abis.begin());
|
||||||
|
|
||||||
|
EXPECT_EQ(Rule::CONTAINS_ANY, rule->op)
|
||||||
|
<< "for " << abi::toString(variant) << " rule";
|
||||||
|
EXPECT_EQ(Rule::NATIVE_PLATFORM, rule->key)
|
||||||
|
<< " for " << abi::toString(variant) << " rule";
|
||||||
|
EXPECT_EQ(matches.size(), rule->stringArgs.size())
|
||||||
|
<< " for " << abi::toString(variant) << " rule";
|
||||||
|
|
||||||
|
for (const char* match : matches) {
|
||||||
|
if (rule->stringArgs.end() ==
|
||||||
|
std::find(rule->stringArgs.begin(), rule->stringArgs.end(), String8(match))) {
|
||||||
|
ADD_FAILURE() << "Rule for abi " << abi::toString(variant)
|
||||||
|
<< " does not contain match for expected abi " << match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
147
tools/split-select/Rule_test.cpp
Normal file
147
tools/split-select/Rule_test.cpp
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Rule.h"
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <utils/String8.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
TEST(RuleTest, generatesValidJson) {
|
||||||
|
sp<Rule> rule = new Rule();
|
||||||
|
rule->op = Rule::AND_SUBRULES;
|
||||||
|
|
||||||
|
sp<Rule> subrule = new Rule();
|
||||||
|
subrule->op = Rule::EQUALS;
|
||||||
|
subrule->key = Rule::SDK_VERSION;
|
||||||
|
subrule->longArgs.add(7);
|
||||||
|
rule->subrules.add(subrule);
|
||||||
|
|
||||||
|
subrule = new Rule();
|
||||||
|
subrule->op = Rule::OR_SUBRULES;
|
||||||
|
rule->subrules.add(subrule);
|
||||||
|
|
||||||
|
sp<Rule> subsubrule = new Rule();
|
||||||
|
subsubrule->op = Rule::GREATER_THAN;
|
||||||
|
subsubrule->key = Rule::SCREEN_DENSITY;
|
||||||
|
subsubrule->longArgs.add(10);
|
||||||
|
subrule->subrules.add(subsubrule);
|
||||||
|
|
||||||
|
subsubrule = new Rule();
|
||||||
|
subsubrule->op = Rule::LESS_THAN;
|
||||||
|
subsubrule->key = Rule::SCREEN_DENSITY;
|
||||||
|
subsubrule->longArgs.add(5);
|
||||||
|
subrule->subrules.add(subsubrule);
|
||||||
|
|
||||||
|
std::string expected(
|
||||||
|
"{"
|
||||||
|
" \"op\": \"AND_SUBRULES\","
|
||||||
|
" \"subrules\": ["
|
||||||
|
" {"
|
||||||
|
" \"op\": \"EQUALS\","
|
||||||
|
" \"property\": \"SDK_VERSION\","
|
||||||
|
" \"args\": [7]"
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"op\": \"OR_SUBRULES\","
|
||||||
|
" \"subrules\": ["
|
||||||
|
" {"
|
||||||
|
" \"op\": \"GREATER_THAN\","
|
||||||
|
" \"property\": \"SCREEN_DENSITY\","
|
||||||
|
" \"args\": [10]"
|
||||||
|
" },"
|
||||||
|
" {"
|
||||||
|
" \"op\": \"LESS_THAN\","
|
||||||
|
" \"property\": \"SCREEN_DENSITY\","
|
||||||
|
" \"args\": [5]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
" }"
|
||||||
|
" ]"
|
||||||
|
"}");
|
||||||
|
// Trim
|
||||||
|
expected.erase(std::remove_if(expected.begin(), expected.end(), ::isspace), expected.end());
|
||||||
|
|
||||||
|
std::string result(rule->toJson().string());
|
||||||
|
|
||||||
|
// Trim
|
||||||
|
result.erase(std::remove_if(result.begin(), result.end(), ::isspace), result.end());
|
||||||
|
|
||||||
|
ASSERT_EQ(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RuleTest, simplifiesSingleSubruleRules) {
|
||||||
|
sp<Rule> rule = new Rule();
|
||||||
|
rule->op = Rule::AND_SUBRULES;
|
||||||
|
|
||||||
|
sp<Rule> subrule = new Rule();
|
||||||
|
subrule->op = Rule::EQUALS;
|
||||||
|
subrule->key = Rule::SDK_VERSION;
|
||||||
|
subrule->longArgs.add(7);
|
||||||
|
rule->subrules.add(subrule);
|
||||||
|
|
||||||
|
sp<Rule> simplified = Rule::simplify(rule);
|
||||||
|
EXPECT_EQ(Rule::EQUALS, simplified->op);
|
||||||
|
EXPECT_EQ(Rule::SDK_VERSION, simplified->key);
|
||||||
|
ASSERT_EQ(1u, simplified->longArgs.size());
|
||||||
|
EXPECT_EQ(7, simplified->longArgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(RuleTest, simplifiesNestedSameOpSubrules) {
|
||||||
|
sp<Rule> rule = new Rule();
|
||||||
|
rule->op = Rule::AND_SUBRULES;
|
||||||
|
|
||||||
|
sp<Rule> subrule = new Rule();
|
||||||
|
subrule->op = Rule::AND_SUBRULES;
|
||||||
|
rule->subrules.add(subrule);
|
||||||
|
|
||||||
|
sp<Rule> subsubrule = new Rule();
|
||||||
|
subsubrule->op = Rule::EQUALS;
|
||||||
|
subsubrule->key = Rule::SDK_VERSION;
|
||||||
|
subsubrule->longArgs.add(7);
|
||||||
|
subrule->subrules.add(subsubrule);
|
||||||
|
|
||||||
|
subrule = new Rule();
|
||||||
|
subrule->op = Rule::EQUALS;
|
||||||
|
subrule->key = Rule::SDK_VERSION;
|
||||||
|
subrule->longArgs.add(8);
|
||||||
|
rule->subrules.add(subrule);
|
||||||
|
|
||||||
|
sp<Rule> simplified = Rule::simplify(rule);
|
||||||
|
EXPECT_EQ(Rule::AND_SUBRULES, simplified->op);
|
||||||
|
ASSERT_EQ(2u, simplified->subrules.size());
|
||||||
|
|
||||||
|
sp<Rule> simplifiedSubrule = simplified->subrules[0];
|
||||||
|
EXPECT_EQ(Rule::EQUALS, simplifiedSubrule->op);
|
||||||
|
EXPECT_EQ(Rule::SDK_VERSION, simplifiedSubrule->key);
|
||||||
|
ASSERT_EQ(1u, simplifiedSubrule->longArgs.size());
|
||||||
|
EXPECT_EQ(7, simplifiedSubrule->longArgs[0]);
|
||||||
|
|
||||||
|
simplifiedSubrule = simplified->subrules[1];
|
||||||
|
EXPECT_EQ(Rule::EQUALS, simplifiedSubrule->op);
|
||||||
|
EXPECT_EQ(Rule::SDK_VERSION, simplifiedSubrule->key);
|
||||||
|
ASSERT_EQ(1u, simplifiedSubrule->longArgs.size());
|
||||||
|
EXPECT_EQ(8, simplifiedSubrule->longArgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
175
tools/split-select/SplitDescription.cpp
Normal file
175
tools/split-select/SplitDescription.cpp
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "SplitDescription.h"
|
||||||
|
|
||||||
|
#include "aapt/AaptConfig.h"
|
||||||
|
#include "aapt/AaptUtil.h"
|
||||||
|
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
using namespace android;
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
SplitDescription::SplitDescription()
|
||||||
|
: abi(abi::Variant::none) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int SplitDescription::compare(const SplitDescription& rhs) const {
|
||||||
|
int cmp;
|
||||||
|
cmp = (int)abi - (int)rhs.abi;
|
||||||
|
if (cmp != 0) return cmp;
|
||||||
|
return config.compareLogical(rhs.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitDescription::isBetterThan(const SplitDescription& o, const SplitDescription& target) const {
|
||||||
|
if (abi != abi::Variant::none || o.abi != abi::Variant::none) {
|
||||||
|
abi::Family family = abi::getFamily(abi);
|
||||||
|
abi::Family oFamily = abi::getFamily(o.abi);
|
||||||
|
if (family != oFamily) {
|
||||||
|
return family != abi::Family::none;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int(target.abi) - int(abi) < int(target.abi) - int(o.abi)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.isBetterThan(o.config, &target.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitDescription::match(const SplitDescription& o) const {
|
||||||
|
if (abi != abi::Variant::none) {
|
||||||
|
abi::Family family = abi::getFamily(abi);
|
||||||
|
abi::Family oFamily = abi::getFamily(o.abi);
|
||||||
|
if (family != oFamily) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int(abi) > int(o.abi)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return config.match(o.config);
|
||||||
|
}
|
||||||
|
|
||||||
|
String8 SplitDescription::toString() const {
|
||||||
|
String8 extension;
|
||||||
|
if (abi != abi::Variant::none) {
|
||||||
|
if (extension.isEmpty()) {
|
||||||
|
extension.append(":");
|
||||||
|
} else {
|
||||||
|
extension.append("-");
|
||||||
|
}
|
||||||
|
extension.append(abi::toString(abi));
|
||||||
|
}
|
||||||
|
String8 str(config.toString());
|
||||||
|
str.append(extension);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t parseAbi(const Vector<String8>& parts, const ssize_t index,
|
||||||
|
SplitDescription* outSplit) {
|
||||||
|
const ssize_t N = parts.size();
|
||||||
|
abi::Variant abi = abi::Variant::none;
|
||||||
|
ssize_t endIndex = index;
|
||||||
|
if (parts[endIndex] == "arm64") {
|
||||||
|
endIndex++;
|
||||||
|
if (endIndex < N) {
|
||||||
|
if (parts[endIndex] == "v8a") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::arm64_v8a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (parts[endIndex] == "armeabi") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::armeabi;
|
||||||
|
if (endIndex < N) {
|
||||||
|
if (parts[endIndex] == "v7a") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::armeabi_v7a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (parts[endIndex] == "x86") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::x86;
|
||||||
|
} else if (parts[endIndex] == "x86_64") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::x86_64;
|
||||||
|
} else if (parts[endIndex] == "mips") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::mips;
|
||||||
|
} else if (parts[endIndex] == "mips64") {
|
||||||
|
endIndex++;
|
||||||
|
abi = abi::Variant::mips64;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abi == abi::Variant::none && endIndex != index) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outSplit != NULL) {
|
||||||
|
outSplit->abi = abi;
|
||||||
|
}
|
||||||
|
return endIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitDescription::parse(const String8& str, SplitDescription* outSplit) {
|
||||||
|
ssize_t index = str.find(":");
|
||||||
|
|
||||||
|
String8 configStr;
|
||||||
|
String8 extensionStr;
|
||||||
|
if (index >= 0) {
|
||||||
|
configStr.setTo(str.string(), index);
|
||||||
|
extensionStr.setTo(str.string() + index + 1);
|
||||||
|
} else {
|
||||||
|
configStr.setTo(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
SplitDescription split;
|
||||||
|
if (!AaptConfig::parse(configStr, &split.config)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<String8> parts = AaptUtil::splitAndLowerCase(extensionStr, '-');
|
||||||
|
const ssize_t N = parts.size();
|
||||||
|
index = 0;
|
||||||
|
|
||||||
|
if (extensionStr.length() == 0) {
|
||||||
|
goto success;
|
||||||
|
}
|
||||||
|
|
||||||
|
index = parseAbi(parts, index, &split);
|
||||||
|
if (index < 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (index == N) {
|
||||||
|
goto success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unrecognized
|
||||||
|
return false;
|
||||||
|
|
||||||
|
success:
|
||||||
|
if (outSplit != NULL) {
|
||||||
|
*outSplit = split;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
65
tools/split-select/SplitDescription.h
Normal file
65
tools/split-select/SplitDescription.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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 H_ANDROID_SPLIT_SPLIT_DESCRIPTION
|
||||||
|
#define H_ANDROID_SPLIT_SPLIT_DESCRIPTION
|
||||||
|
|
||||||
|
#include "aapt/ConfigDescription.h"
|
||||||
|
#include "Abi.h"
|
||||||
|
|
||||||
|
#include <utils/String8.h>
|
||||||
|
#include <utils/Vector.h>
|
||||||
|
|
||||||
|
namespace split {
|
||||||
|
|
||||||
|
struct SplitDescription {
|
||||||
|
SplitDescription();
|
||||||
|
SplitDescription(const SplitDescription&) = default;
|
||||||
|
|
||||||
|
ConfigDescription config;
|
||||||
|
abi::Variant abi;
|
||||||
|
|
||||||
|
int compare(const SplitDescription& rhs) const;
|
||||||
|
inline bool operator<(const SplitDescription& rhs) const;
|
||||||
|
inline bool operator==(const SplitDescription& rhs) const;
|
||||||
|
inline bool operator!=(const SplitDescription& rhs) const;
|
||||||
|
|
||||||
|
bool match(const SplitDescription& o) const;
|
||||||
|
bool isBetterThan(const SplitDescription& o, const SplitDescription& target) const;
|
||||||
|
|
||||||
|
android::String8 toString() const;
|
||||||
|
|
||||||
|
static bool parse(const android::String8& str, SplitDescription* outSplit);
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t parseAbi(const android::Vector<android::String8>& parts, const ssize_t index,
|
||||||
|
SplitDescription* outSplit);
|
||||||
|
|
||||||
|
bool SplitDescription::operator<(const SplitDescription& rhs) const {
|
||||||
|
return compare(rhs) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitDescription::operator==(const SplitDescription& rhs) const {
|
||||||
|
return compare(rhs) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SplitDescription::operator!=(const SplitDescription& rhs) const {
|
||||||
|
return compare(rhs) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace split
|
||||||
|
|
||||||
|
#endif // H_ANDROID_SPLIT_SPLIT_DESCRIPTION
|
||||||
Reference in New Issue
Block a user