Main.cpp revision c3dc0b57b8d0b3875f868788e110aa67fb032b4a
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <algorithm> 18#include <cstdio> 19 20#include "aapt/AaptUtil.h" 21 22#include "Grouper.h" 23#include "Rule.h" 24#include "RuleGenerator.h" 25#include "SplitDescription.h" 26 27#include <androidfw/AssetManager.h> 28#include <androidfw/ResourceTypes.h> 29#include <utils/KeyedVector.h> 30#include <utils/Vector.h> 31 32using namespace android; 33 34namespace split { 35 36static void usage() { 37 fprintf(stderr, 38 "split-select --help\n" 39 "split-select --target <config> --split <path/to/apk> [--split <path/to/apk> [...]]\n" 40 "split-select --generate --split <path/to/apk> [--split <path/to/apk> [...]]\n" 41 "\n" 42 " --help Displays more information about this program.\n" 43 " --target <config> Performs the Split APK selection on the given configuration.\n" 44 " --generate Generates the logic for selecting the Split APK, in JSON format.\n" 45 " --split <path/to/apk> Includes a Split APK in the selection process.\n" 46 "\n" 47 " Where <config> is an extended AAPT resource qualifier of the form\n" 48 " 'resource-qualifiers:extended-qualifiers', where 'resource-qualifiers' is an AAPT resource\n" 49 " qualifier (ex: en-rUS-sw600dp-xhdpi), and 'extended-qualifiers' is an ordered list of one\n" 50 " qualifier (or none) from each category:\n" 51 " Architecture: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips\n"); 52} 53 54static void help() { 55 usage(); 56 fprintf(stderr, "\n" 57 " Generates the logic for selecting a Split APK given some target Android device configuration.\n" 58 " Using the flag --generate will emit a JSON encoded tree of rules that must be satisfied in order\n" 59 " to install the given Split APK. Using the flag --target along with the device configuration\n" 60 " will emit the set of Split APKs to install, following the same logic that would have been emitted\n" 61 " via JSON.\n"); 62} 63 64class SplitSelector { 65public: 66 SplitSelector(); 67 SplitSelector(const Vector<SplitDescription>& splits); 68 69 Vector<SplitDescription> getBestSplits(const SplitDescription& target) const; 70 71 template <typename RuleGenerator> 72 KeyedVector<SplitDescription, sp<Rule> > getRules() const; 73 74private: 75 Vector<SortedVector<SplitDescription> > mGroups; 76}; 77 78SplitSelector::SplitSelector() { 79} 80 81SplitSelector::SplitSelector(const Vector<SplitDescription>& splits) 82 : mGroups(groupByMutualExclusivity(splits)) { 83} 84 85static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, 86 const SplitDescription& target, Vector<SplitDescription>& splitsOut) { 87 SplitDescription bestSplit; 88 bool isSet = false; 89 const size_t splitCount = splits.size(); 90 for (size_t j = 0; j < splitCount; j++) { 91 const SplitDescription& thisSplit = splits[j]; 92 if (!thisSplit.match(target)) { 93 continue; 94 } 95 96 if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { 97 isSet = true; 98 bestSplit = thisSplit; 99 } 100 } 101 102 if (isSet) { 103 splitsOut.add(bestSplit); 104 } 105} 106 107Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { 108 Vector<SplitDescription> bestSplits; 109 const size_t groupCount = mGroups.size(); 110 for (size_t i = 0; i < groupCount; i++) { 111 selectBestFromGroup(mGroups[i], target, bestSplits); 112 } 113 return bestSplits; 114} 115 116template <typename RuleGenerator> 117KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { 118 KeyedVector<SplitDescription, sp<Rule> > rules; 119 120 const size_t groupCount = mGroups.size(); 121 for (size_t i = 0; i < groupCount; i++) { 122 const SortedVector<SplitDescription>& splits = mGroups[i]; 123 const size_t splitCount = splits.size(); 124 for (size_t j = 0; j < splitCount; j++) { 125 sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); 126 if (rule != NULL) { 127 rules.add(splits[j], rule); 128 } 129 } 130 } 131 return rules; 132} 133 134Vector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) { 135 const SplitSelector selector(splits); 136 return selector.getBestSplits(target); 137} 138 139void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { 140 Vector<SplitDescription> allSplits; 141 const size_t apkSplitCount = splits.size(); 142 for (size_t i = 0; i < apkSplitCount; i++) { 143 allSplits.appendVector(splits[i]); 144 } 145 const SplitSelector selector(allSplits); 146 KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules<RuleGenerator>()); 147 148 fprintf(stdout, "[\n"); 149 for (size_t i = 0; i < apkSplitCount; i++) { 150 sp<Rule> masterRule = new Rule(); 151 masterRule->op = Rule::OR_SUBRULES; 152 const Vector<SplitDescription>& splitDescriptions = splits[i]; 153 const size_t splitDescriptionCount = splitDescriptions.size(); 154 for (size_t j = 0; j < splitDescriptionCount; j++) { 155 masterRule->subrules.add(rules.valueFor(splitDescriptions[j])); 156 } 157 masterRule = Rule::simplify(masterRule); 158 fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }%s\n", 159 splits.keyAt(i).string(), 160 masterRule->toJson(2).string(), 161 i < apkSplitCount - 1 ? "," : ""); 162 } 163 fprintf(stdout, "]\n"); 164} 165 166static void removeRuntimeQualifiers(ConfigDescription* outConfig) { 167 outConfig->imsi = 0; 168 outConfig->orientation = ResTable_config::ORIENTATION_ANY; 169 outConfig->screenWidth = ResTable_config::SCREENWIDTH_ANY; 170 outConfig->screenHeight = ResTable_config::SCREENHEIGHT_ANY; 171 outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY; 172} 173 174static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) { 175 AssetManager assetManager; 176 Vector<SplitDescription> splits; 177 int32_t cookie = 0; 178 if (!assetManager.addAssetPath(path, &cookie)) { 179 return splits; 180 } 181 182 const ResTable& res = assetManager.getResources(false); 183 if (res.getError() == NO_ERROR) { 184 Vector<ResTable_config> configs; 185 res.getConfigurations(&configs); 186 const size_t configCount = configs.size(); 187 for (size_t i = 0; i < configCount; i++) { 188 splits.add(); 189 splits.editTop().config = configs[i]; 190 } 191 } 192 193 AssetDir* dir = assetManager.openNonAssetDir(cookie, "lib"); 194 if (dir != NULL) { 195 const size_t fileCount = dir->getFileCount(); 196 for (size_t i = 0; i < fileCount; i++) { 197 splits.add(); 198 Vector<String8> parts = AaptUtil::splitAndLowerCase(dir->getFileName(i), '-'); 199 if (parseAbi(parts, 0, &splits.editTop()) < 0) { 200 fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).string()); 201 splits.pop(); 202 } 203 } 204 delete dir; 205 } 206 return splits; 207} 208 209static int main(int argc, char** argv) { 210 // Skip over the first argument. 211 argc--; 212 argv++; 213 214 bool generateFlag = false; 215 String8 targetConfigStr; 216 Vector<String8> splitApkPaths; 217 while (argc > 0) { 218 const String8 arg(*argv); 219 if (arg == "--target") { 220 argc--; 221 argv++; 222 if (argc < 1) { 223 fprintf(stderr, "Missing parameter for --split.\n"); 224 usage(); 225 return 1; 226 } 227 targetConfigStr.setTo(*argv); 228 } else if (arg == "--split") { 229 argc--; 230 argv++; 231 if (argc < 1) { 232 fprintf(stderr, "Missing parameter for --split.\n"); 233 usage(); 234 return 1; 235 } 236 splitApkPaths.add(String8(*argv)); 237 } else if (arg == "--generate") { 238 generateFlag = true; 239 } else if (arg == "--help") { 240 help(); 241 return 0; 242 } else { 243 fprintf(stderr, "Unknown argument '%s'\n", arg.string()); 244 usage(); 245 return 1; 246 } 247 argc--; 248 argv++; 249 } 250 251 if (!generateFlag && targetConfigStr == "") { 252 usage(); 253 return 1; 254 } 255 256 if (splitApkPaths.size() == 0) { 257 usage(); 258 return 1; 259 } 260 261 SplitDescription targetSplit; 262 if (!generateFlag) { 263 if (!SplitDescription::parse(targetConfigStr, &targetSplit)) { 264 fprintf(stderr, "Invalid --target config: '%s'\n", 265 targetConfigStr.string()); 266 usage(); 267 return 1; 268 } 269 270 // We don't want to match on things that will change at run-time 271 // (orientation, w/h, etc.). 272 removeRuntimeQualifiers(&targetSplit.config); 273 } 274 275 KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap; 276 KeyedVector<SplitDescription, String8> splitApkPathMap; 277 Vector<SplitDescription> splitConfigs; 278 const size_t splitCount = splitApkPaths.size(); 279 for (size_t i = 0; i < splitCount; i++) { 280 Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]); 281 if (splits.isEmpty()) { 282 fprintf(stderr, "Invalid --split path: '%s'. No splits found.\n", 283 splitApkPaths[i].string()); 284 usage(); 285 return 1; 286 } 287 apkPathSplitMap.replaceValueFor(splitApkPaths[i], splits); 288 const size_t apkSplitDescriptionCount = splits.size(); 289 for (size_t j = 0; j < apkSplitDescriptionCount; j++) { 290 splitApkPathMap.replaceValueFor(splits[j], splitApkPaths[i]); 291 } 292 splitConfigs.appendVector(splits); 293 } 294 295 if (!generateFlag) { 296 Vector<SplitDescription> matchingConfigs = select(targetSplit, splitConfigs); 297 const size_t matchingConfigCount = matchingConfigs.size(); 298 SortedVector<String8> matchingSplitPaths; 299 for (size_t i = 0; i < matchingConfigCount; i++) { 300 matchingSplitPaths.add(splitApkPathMap.valueFor(matchingConfigs[i])); 301 } 302 303 const size_t matchingSplitApkPathCount = matchingSplitPaths.size(); 304 for (size_t i = 0; i < matchingSplitApkPathCount; i++) { 305 fprintf(stderr, "%s\n", matchingSplitPaths[i].string()); 306 } 307 } else { 308 generate(apkPathSplitMap); 309 } 310 return 0; 311} 312 313} // namespace split 314 315int main(int argc, char** argv) { 316 return split::main(argc, argv); 317} 318