Main.cpp revision 40e8eefbedcafc51948945647d746daaee092f16
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() = default; 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(const Vector<SplitDescription>& splits) 79 : mGroups(groupByMutualExclusivity(splits)) { 80} 81 82static void selectBestFromGroup(const SortedVector<SplitDescription>& splits, 83 const SplitDescription& target, Vector<SplitDescription>& splitsOut) { 84 SplitDescription bestSplit; 85 bool isSet = false; 86 const size_t splitCount = splits.size(); 87 for (size_t j = 0; j < splitCount; j++) { 88 const SplitDescription& thisSplit = splits[j]; 89 if (!thisSplit.match(target)) { 90 continue; 91 } 92 93 if (!isSet || thisSplit.isBetterThan(bestSplit, target)) { 94 isSet = true; 95 bestSplit = thisSplit; 96 } 97 } 98 99 if (isSet) { 100 splitsOut.add(bestSplit); 101 } 102} 103 104Vector<SplitDescription> SplitSelector::getBestSplits(const SplitDescription& target) const { 105 Vector<SplitDescription> bestSplits; 106 const size_t groupCount = mGroups.size(); 107 for (size_t i = 0; i < groupCount; i++) { 108 selectBestFromGroup(mGroups[i], target, bestSplits); 109 } 110 return bestSplits; 111} 112 113template <typename RuleGenerator> 114KeyedVector<SplitDescription, sp<Rule> > SplitSelector::getRules() const { 115 KeyedVector<SplitDescription, sp<Rule> > rules; 116 117 const size_t groupCount = mGroups.size(); 118 for (size_t i = 0; i < groupCount; i++) { 119 const SortedVector<SplitDescription>& splits = mGroups[i]; 120 const size_t splitCount = splits.size(); 121 for (size_t j = 0; j < splitCount; j++) { 122 sp<Rule> rule = Rule::simplify(RuleGenerator::generate(splits, j)); 123 if (rule != NULL) { 124 rules.add(splits[j], rule); 125 } 126 } 127 } 128 return rules; 129} 130 131Vector<SplitDescription> select(const SplitDescription& target, const Vector<SplitDescription>& splits) { 132 const SplitSelector selector(splits); 133 return selector.getBestSplits(target); 134} 135 136void generate(const KeyedVector<String8, Vector<SplitDescription> >& splits) { 137 Vector<SplitDescription> allSplits; 138 const size_t apkSplitCount = splits.size(); 139 for (size_t i = 0; i < apkSplitCount; i++) { 140 allSplits.appendVector(splits[i]); 141 } 142 const SplitSelector selector(allSplits); 143 KeyedVector<SplitDescription, sp<Rule> > rules(selector.getRules<RuleGenerator>()); 144 145 fprintf(stdout, "[\n"); 146 for (size_t i = 0; i < apkSplitCount; i++) { 147 sp<Rule> masterRule = new Rule(); 148 masterRule->op = Rule::OR_SUBRULES; 149 const Vector<SplitDescription>& splitDescriptions = splits[i]; 150 const size_t splitDescriptionCount = splitDescriptions.size(); 151 for (size_t j = 0; j < splitDescriptionCount; j++) { 152 masterRule->subrules.add(rules.valueFor(splitDescriptions[j])); 153 } 154 masterRule = Rule::simplify(masterRule); 155 fprintf(stdout, " {\n \"path\": \"%s\",\n \"rules\": %s\n }%s\n", 156 splits.keyAt(i).string(), 157 masterRule->toJson(2).string(), 158 i < apkSplitCount - 1 ? "," : ""); 159 } 160 fprintf(stdout, "]\n"); 161} 162 163static void removeRuntimeQualifiers(ConfigDescription* outConfig) { 164 outConfig->imsi = 0; 165 outConfig->orientation = ResTable_config::ORIENTATION_ANY; 166 outConfig->screenWidth = ResTable_config::SCREENWIDTH_ANY; 167 outConfig->screenHeight = ResTable_config::SCREENHEIGHT_ANY; 168 outConfig->uiMode &= ResTable_config::UI_MODE_NIGHT_ANY; 169} 170 171static Vector<SplitDescription> extractSplitDescriptionsFromApk(const String8& path) { 172 AssetManager assetManager; 173 Vector<SplitDescription> splits; 174 int32_t cookie = 0; 175 if (!assetManager.addAssetPath(path, &cookie)) { 176 return splits; 177 } 178 179 const ResTable& res = assetManager.getResources(false); 180 if (res.getError() == NO_ERROR) { 181 Vector<ResTable_config> configs; 182 res.getConfigurations(&configs); 183 const size_t configCount = configs.size(); 184 for (size_t i = 0; i < configCount; i++) { 185 splits.add(); 186 splits.editTop().config = configs[i]; 187 } 188 } 189 190 AssetDir* dir = assetManager.openNonAssetDir(cookie, "lib"); 191 if (dir != NULL) { 192 const size_t fileCount = dir->getFileCount(); 193 for (size_t i = 0; i < fileCount; i++) { 194 splits.add(); 195 Vector<String8> parts = AaptUtil::splitAndLowerCase(dir->getFileName(i), '-'); 196 if (parseAbi(parts, 0, &splits.editTop()) < 0) { 197 fprintf(stderr, "Malformed library %s\n", dir->getFileName(i).string()); 198 splits.pop(); 199 } 200 } 201 delete dir; 202 } 203 return splits; 204} 205 206static int main(int argc, char** argv) { 207 // Skip over the first argument. 208 argc--; 209 argv++; 210 211 bool generateFlag = false; 212 String8 targetConfigStr; 213 Vector<String8> splitApkPaths; 214 while (argc > 0) { 215 const String8 arg(*argv); 216 if (arg == "--target") { 217 argc--; 218 argv++; 219 if (argc < 1) { 220 fprintf(stderr, "Missing parameter for --split.\n"); 221 usage(); 222 return 1; 223 } 224 targetConfigStr.setTo(*argv); 225 } else if (arg == "--split") { 226 argc--; 227 argv++; 228 if (argc < 1) { 229 fprintf(stderr, "Missing parameter for --split.\n"); 230 usage(); 231 return 1; 232 } 233 splitApkPaths.add(String8(*argv)); 234 } else if (arg == "--generate") { 235 generateFlag = true; 236 } else if (arg == "--help") { 237 help(); 238 return 0; 239 } else { 240 fprintf(stderr, "Unknown argument '%s'\n", arg.string()); 241 usage(); 242 return 1; 243 } 244 argc--; 245 argv++; 246 } 247 248 if (!generateFlag && targetConfigStr == "") { 249 usage(); 250 return 1; 251 } 252 253 if (splitApkPaths.size() == 0) { 254 usage(); 255 return 1; 256 } 257 258 SplitDescription targetSplit; 259 if (!generateFlag) { 260 if (!SplitDescription::parse(targetConfigStr, &targetSplit)) { 261 fprintf(stderr, "Invalid --target config: '%s'\n", 262 targetConfigStr.string()); 263 usage(); 264 return 1; 265 } 266 267 // We don't want to match on things that will change at run-time 268 // (orientation, w/h, etc.). 269 removeRuntimeQualifiers(&targetSplit.config); 270 } 271 272 KeyedVector<String8, Vector<SplitDescription> > apkPathSplitMap; 273 KeyedVector<SplitDescription, String8> splitApkPathMap; 274 Vector<SplitDescription> splitConfigs; 275 const size_t splitCount = splitApkPaths.size(); 276 for (size_t i = 0; i < splitCount; i++) { 277 Vector<SplitDescription> splits = extractSplitDescriptionsFromApk(splitApkPaths[i]); 278 if (splits.isEmpty()) { 279 fprintf(stderr, "Invalid --split path: '%s'. No splits found.\n", 280 splitApkPaths[i].string()); 281 usage(); 282 return 1; 283 } 284 apkPathSplitMap.replaceValueFor(splitApkPaths[i], splits); 285 const size_t apkSplitDescriptionCount = splits.size(); 286 for (size_t j = 0; j < apkSplitDescriptionCount; j++) { 287 splitApkPathMap.replaceValueFor(splits[j], splitApkPaths[i]); 288 } 289 splitConfigs.appendVector(splits); 290 } 291 292 if (!generateFlag) { 293 Vector<SplitDescription> matchingConfigs = select(targetSplit, splitConfigs); 294 const size_t matchingConfigCount = matchingConfigs.size(); 295 SortedVector<String8> matchingSplitPaths; 296 for (size_t i = 0; i < matchingConfigCount; i++) { 297 matchingSplitPaths.add(splitApkPathMap.valueFor(matchingConfigs[i])); 298 } 299 300 const size_t matchingSplitApkPathCount = matchingSplitPaths.size(); 301 for (size_t i = 0; i < matchingSplitApkPathCount; i++) { 302 fprintf(stderr, "%s\n", matchingSplitPaths[i].string()); 303 } 304 } else { 305 generate(apkPathSplitMap); 306 } 307 return 0; 308} 309 310} // namespace split 311 312int main(int argc, char** argv) { 313 return split::main(argc, argv); 314} 315