use of com.google.common.annotations.VisibleForTesting in project buck by facebook.
the class AndroidBinary method addDexingSteps.
/**
* Create dex artifacts for all of the individual directories of compiled .class files (or
* the obfuscated jar files if proguard is used). If split dex is used, multiple dex artifacts
* will be produced.
* @param classpathEntriesToDex Full set of classpath entries that must make
* their way into the final APK structure (but not necessarily into the
* primary dex).
* @param secondaryDexDirectories The contract for updating this builder must match that
* of {@link PreDexMerge#getSecondaryDexDirectories()}.
* @param steps List of steps to add to.
* @param primaryDexPath Output path for the primary dex file.
*/
@VisibleForTesting
void addDexingSteps(Set<Path> classpathEntriesToDex, Supplier<ImmutableMap<String, HashCode>> classNamesToHashesSupplier, ImmutableSet.Builder<Path> secondaryDexDirectories, ImmutableList.Builder<Step> steps, Path primaryDexPath, Optional<SourcePath> dexReorderToolFile, Optional<SourcePath> dexReorderDataDumpFile, ImmutableMultimap<APKModule, Path> additionalDexStoreToJarPathMap, SourcePathResolver resolver) {
final Supplier<Set<Path>> primaryInputsToDex;
final Optional<Path> secondaryDexDir;
final Optional<Supplier<Multimap<Path, Path>>> secondaryOutputToInputs;
Path secondaryDexParentDir = getBinPath("__%s_secondary_dex__/");
Path additionalDexParentDir = getBinPath("__%s_additional_dex__/");
Path additionalDexAssetsDir = additionalDexParentDir.resolve("assets");
final Optional<ImmutableSet<Path>> additionalDexDirs;
if (shouldSplitDex()) {
Optional<Path> proguardFullConfigFile = Optional.empty();
Optional<Path> proguardMappingFile = Optional.empty();
if (packageType.isBuildWithObfuscation()) {
Path proguardConfigDir = getProguardTextFilesPath();
proguardFullConfigFile = Optional.of(proguardConfigDir.resolve("configuration.txt"));
proguardMappingFile = Optional.of(proguardConfigDir.resolve("mapping.txt"));
}
// DexLibLoader expects that metadata.txt and secondary jar files are under this dir
// in assets.
// Intermediate directory holding the primary split-zip jar.
Path splitZipDir = getBinPath("__%s_split_zip__");
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), splitZipDir));
Path primaryJarPath = splitZipDir.resolve("primary.jar");
Path secondaryJarMetaDirParent = splitZipDir.resolve("secondary_meta");
Path secondaryJarMetaDir = secondaryJarMetaDirParent.resolve(SECONDARY_DEX_SUBDIR);
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), secondaryJarMetaDir));
Path secondaryJarMeta = secondaryJarMetaDir.resolve("metadata.txt");
// Intermediate directory holding _ONLY_ the secondary split-zip jar files. This is
// important because SmartDexingCommand will try to dx every entry in this directory. It
// does this because it's impossible to know what outputs split-zip will generate until it
// runs.
final Path secondaryZipDir = getBinPath("__%s_secondary_zip__");
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), secondaryZipDir));
// Intermediate directory holding the directories holding _ONLY_ the additional split-zip
// jar files that are intended for that dex store.
final Path additionalDexStoresZipDir = getBinPath("__%s_dex_stores_zip__");
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), additionalDexStoresZipDir));
for (APKModule dexStore : additionalDexStoreToJarPathMap.keySet()) {
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), additionalDexStoresZipDir.resolve(dexStore.getName())));
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), secondaryJarMetaDirParent.resolve("assets").resolve(dexStore.getName())));
}
// Run the split-zip command which is responsible for dividing the large set of input
// classpaths into a more compact set of jar files such that no one jar file when dexed will
// yield a dex artifact too large for dexopt or the dx method limit to handle.
Path zipSplitReportDir = getBinPath("__%s_split_zip_report__");
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), zipSplitReportDir));
SplitZipStep splitZipCommand = new SplitZipStep(getProjectFilesystem(), classpathEntriesToDex, secondaryJarMeta, primaryJarPath, secondaryZipDir, "secondary-%d.jar", secondaryJarMetaDirParent, additionalDexStoresZipDir, proguardFullConfigFile, proguardMappingFile, skipProguard, dexSplitMode, dexSplitMode.getPrimaryDexScenarioFile().map(resolver::getAbsolutePath), dexSplitMode.getPrimaryDexClassesFile().map(resolver::getAbsolutePath), dexSplitMode.getSecondaryDexHeadClassesFile().map(resolver::getAbsolutePath), dexSplitMode.getSecondaryDexTailClassesFile().map(resolver::getAbsolutePath), additionalDexStoreToJarPathMap, enhancementResult.getAPKModuleGraph(), zipSplitReportDir);
steps.add(splitZipCommand);
// smart dexing command. Smart dex will handle "cleaning" this directory properly.
if (reorderClassesIntraDex) {
secondaryDexDir = Optional.of(secondaryDexParentDir.resolve(SMART_DEX_SECONDARY_DEX_SUBDIR));
Path intraDexReorderSecondaryDexDir = secondaryDexParentDir.resolve(SECONDARY_DEX_SUBDIR);
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), secondaryDexDir.get()));
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), intraDexReorderSecondaryDexDir));
} else {
secondaryDexDir = Optional.of(secondaryDexParentDir.resolve(SECONDARY_DEX_SUBDIR));
steps.add(new MkdirStep(getProjectFilesystem(), secondaryDexDir.get()));
}
if (additionalDexStoreToJarPathMap.isEmpty()) {
additionalDexDirs = Optional.empty();
} else {
ImmutableSet.Builder<Path> builder = ImmutableSet.builder();
for (APKModule dexStore : additionalDexStoreToJarPathMap.keySet()) {
Path dexStorePath = additionalDexAssetsDir.resolve(dexStore.getName());
builder.add(dexStorePath);
steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), dexStorePath));
}
additionalDexDirs = Optional.of(builder.build());
}
if (dexSplitMode.getDexStore() == DexStore.RAW) {
secondaryDexDirectories.add(secondaryDexDir.get());
} else {
secondaryDexDirectories.add(secondaryJarMetaDirParent);
secondaryDexDirectories.add(secondaryDexParentDir);
}
if (additionalDexDirs.isPresent()) {
secondaryDexDirectories.add(additionalDexParentDir);
}
// Adjust smart-dex inputs for the split-zip case.
primaryInputsToDex = Suppliers.ofInstance(ImmutableSet.of(primaryJarPath));
Supplier<Multimap<Path, Path>> secondaryOutputToInputsMap = splitZipCommand.getOutputToInputsMapSupplier(secondaryDexDir.get(), additionalDexAssetsDir);
secondaryOutputToInputs = Optional.of(secondaryOutputToInputsMap);
} else {
// Simple case where our inputs are the natural classpath directories and we don't have
// to worry about secondary jar/dex files.
primaryInputsToDex = Suppliers.ofInstance(classpathEntriesToDex);
secondaryDexDir = Optional.empty();
secondaryOutputToInputs = Optional.empty();
}
HashInputJarsToDexStep hashInputJarsToDexStep = new HashInputJarsToDexStep(getProjectFilesystem(), primaryInputsToDex, secondaryOutputToInputs, classNamesToHashesSupplier);
steps.add(hashInputJarsToDexStep);
// Stores checksum information from each invocation to intelligently decide when dx needs
// to be re-run.
Path successDir = getBinPath("__%s_smart_dex__/.success");
steps.add(new MkdirStep(getProjectFilesystem(), successDir));
// Add the smart dexing tool that is capable of avoiding the external dx invocation(s) if
// it can be shown that the inputs have not changed. It also parallelizes dx invocations
// where applicable.
//
// Note that by not specifying the number of threads this command will use it will select an
// optimal default regardless of the value of --num-threads. This decision was made with the
// assumption that --num-threads specifies the threading of build rule execution and does not
// directly apply to the internal threading/parallelization details of various build commands
// being executed. For example, aapt is internally threaded by default when preprocessing
// images.
EnumSet<DxStep.Option> dxOptions = PackageType.RELEASE.equals(packageType) ? EnumSet.of(DxStep.Option.NO_LOCALS) : EnumSet.of(DxStep.Option.NO_OPTIMIZE);
Path selectedPrimaryDexPath = primaryDexPath;
if (reorderClassesIntraDex) {
String primaryDexFileName = primaryDexPath.getFileName().toString();
String smartDexPrimaryDexFileName = "smart-dex-" + primaryDexFileName;
Path smartDexPrimaryDexPath = Paths.get(primaryDexPath.toString().replace(primaryDexFileName, smartDexPrimaryDexFileName));
selectedPrimaryDexPath = smartDexPrimaryDexPath;
}
SmartDexingStep smartDexingCommand = new SmartDexingStep(getProjectFilesystem(), selectedPrimaryDexPath, primaryInputsToDex, secondaryDexDir, secondaryOutputToInputs, hashInputJarsToDexStep, successDir, dxOptions, dxExecutorService, xzCompressionLevel, dxMaxHeapSize);
steps.add(smartDexingCommand);
if (reorderClassesIntraDex) {
IntraDexReorderStep intraDexReorderStep = new IntraDexReorderStep(getProjectFilesystem(), resolver.getAbsolutePath(dexReorderToolFile.get()), resolver.getAbsolutePath(dexReorderDataDumpFile.get()), getBuildTarget(), selectedPrimaryDexPath, primaryDexPath, secondaryOutputToInputs, SMART_DEX_SECONDARY_DEX_SUBDIR, SECONDARY_DEX_SUBDIR);
steps.add(intraDexReorderStep);
}
}
use of com.google.common.annotations.VisibleForTesting in project buck by facebook.
the class AndroidBinary method getProguardOutputFromInputClasspath.
@VisibleForTesting
static Path getProguardOutputFromInputClasspath(Path proguardConfigDir, Path classpathEntry) {
// Hehe, this is so ridiculously fragile.
Preconditions.checkArgument(!classpathEntry.isAbsolute(), "Classpath entries should be relative rather than absolute paths: %s", classpathEntry);
String obfuscatedName = Files.getNameWithoutExtension(classpathEntry.toString()) + "-obfuscated.jar";
Path dirName = classpathEntry.getParent();
return proguardConfigDir.resolve(dirName).resolve(obfuscatedName);
}
use of com.google.common.annotations.VisibleForTesting in project buck by facebook.
the class ExopackageInstaller method parsePathAndPackageInfo.
@VisibleForTesting
static Optional<PackageInfo> parsePathAndPackageInfo(String packageName, String rawOutput) {
Iterable<String> lines = Splitter.on(LINE_ENDING).omitEmptyStrings().split(rawOutput);
String pmPathPrefix = "package:";
String pmPath = null;
for (String line : lines) {
// Ignore silly linker warnings about non-PIC code on emulators
if (!line.startsWith("WARNING: linker: ")) {
pmPath = line;
break;
}
}
if (pmPath == null || !pmPath.startsWith(pmPathPrefix)) {
LOG.warn("unable to locate package path for [" + packageName + "]");
return Optional.empty();
}
final String packagePrefix = " Package [" + packageName + "] (";
final String otherPrefix = " Package [";
boolean sawPackageLine = false;
final Splitter splitter = Splitter.on('=').limit(2);
String codePath = null;
String resourcePath = null;
String nativeLibPath = null;
String versionCode = null;
for (String line : lines) {
// Just ignore everything until we see the line that says we are in the right package.
if (line.startsWith(packagePrefix)) {
sawPackageLine = true;
continue;
}
// This should never happen, but if we do see a different package, stop parsing.
if (line.startsWith(otherPrefix)) {
break;
}
// Ignore lines before our package.
if (!sawPackageLine) {
continue;
}
// Parse key-value pairs.
List<String> parts = splitter.splitToList(line.trim());
if (parts.size() != 2) {
continue;
}
switch(parts.get(0)) {
case "codePath":
codePath = parts.get(1);
break;
case "resourcePath":
resourcePath = parts.get(1);
break;
case "nativeLibraryPath":
nativeLibPath = parts.get(1);
break;
// Might need to update if people report failures.
case "legacyNativeLibraryDir":
nativeLibPath = parts.get(1);
break;
case "versionCode":
// Extra split to get rid of the SDK thing.
versionCode = parts.get(1).split(" ", 2)[0];
break;
default:
break;
}
}
if (!sawPackageLine) {
return Optional.empty();
}
Preconditions.checkNotNull(codePath, "Could not find codePath");
Preconditions.checkNotNull(resourcePath, "Could not find resourcePath");
Preconditions.checkNotNull(nativeLibPath, "Could not find nativeLibraryPath");
Preconditions.checkNotNull(versionCode, "Could not find versionCode");
if (!codePath.equals(resourcePath)) {
throw new IllegalStateException("Code and resource path do not match");
}
// Lollipop doesn't give the full path to the apk anymore. Not sure why it's "base.apk".
if (!codePath.endsWith(".apk")) {
codePath += "/base.apk";
}
return Optional.of(new PackageInfo(codePath, nativeLibPath, versionCode));
}
use of com.google.common.annotations.VisibleForTesting in project buck by facebook.
the class AndroidResourceDescription method collectInputFiles.
@VisibleForTesting
ImmutableSortedMap<Path, SourcePath> collectInputFiles(final ProjectFilesystem filesystem, Path inputDir) {
final ImmutableSortedMap.Builder<Path, SourcePath> paths = ImmutableSortedMap.naturalOrder();
// We ignore the same files that mini-aapt and aapt ignore.
FileVisitor<Path> fileVisitor = new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attr) throws IOException {
String dirName = dir.getFileName().toString();
// Special case: directory starting with '_' as per aapt.
if (dirName.charAt(0) == '_' || !isPossibleResourceName(dirName)) {
return FileVisitResult.SKIP_SUBTREE;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attr) throws IOException {
String filename = file.getFileName().toString();
if (isPossibleResourceName(filename)) {
paths.put(MorePaths.relativize(inputDir, file), new PathSourcePath(filesystem, file));
}
return FileVisitResult.CONTINUE;
}
};
try {
filesystem.walkRelativeFileTree(inputDir, fileVisitor);
} catch (IOException e) {
throw new HumanReadableException(e, "Error while searching for android resources in directory %s.", inputDir);
}
return paths.build();
}
use of com.google.common.annotations.VisibleForTesting in project buck by facebook.
the class AndroidBinaryGraphEnhancer method createPreDexRulesForLibraries.
@VisibleForTesting
ImmutableMultimap<APKModule, DexProducedFromJavaLibrary> createPreDexRulesForLibraries(Iterable<BuildRule> additionalJavaLibrariesToDex, AndroidPackageableCollection packageableCollection) {
Iterable<BuildTarget> additionalJavaLibraryTargets = FluentIterable.from(additionalJavaLibrariesToDex).transform(BuildRule::getBuildTarget);
ImmutableMultimap.Builder<APKModule, DexProducedFromJavaLibrary> preDexDeps = ImmutableMultimap.builder();
for (BuildTarget buildTarget : Iterables.concat(packageableCollection.getJavaLibrariesToDex(), additionalJavaLibraryTargets)) {
Preconditions.checkState(!buildTargetsToExcludeFromDex.contains(buildTarget), "JavaLibrary should have been excluded from target to dex: %s", buildTarget);
BuildRule libraryRule = ruleResolver.getRule(buildTarget);
Preconditions.checkState(libraryRule instanceof JavaLibrary);
JavaLibrary javaLibrary = (JavaLibrary) libraryRule;
// resources, but export_deps is true), then there will not be anything to dx.
if (javaLibrary.getSourcePathToOutput() == null) {
continue;
}
// See whether the corresponding IntermediateDexRule has already been added to the
// ruleResolver.
BuildTarget originalTarget = javaLibrary.getBuildTarget();
BuildTarget preDexTarget = BuildTarget.builder(originalTarget).addFlavors(DEX_FLAVOR).build();
Optional<BuildRule> preDexRule = ruleResolver.getRuleOptional(preDexTarget);
if (preDexRule.isPresent()) {
preDexDeps.put(apkModuleGraph.findModuleForTarget(buildTarget), (DexProducedFromJavaLibrary) preDexRule.get());
continue;
}
// Create the IntermediateDexRule and add it to both the ruleResolver and preDexDeps.
BuildRuleParams paramsForPreDex = buildRuleParams.withBuildTarget(preDexTarget).copyReplacingDeclaredAndExtraDeps(Suppliers.ofInstance(ImmutableSortedSet.of(ruleResolver.getRule(javaLibrary.getBuildTarget()))), Suppliers.ofInstance(ImmutableSortedSet.of()));
DexProducedFromJavaLibrary preDex = new DexProducedFromJavaLibrary(paramsForPreDex, javaLibrary);
ruleResolver.addToIndex(preDex);
preDexDeps.put(apkModuleGraph.findModuleForTarget(buildTarget), preDex);
}
return preDexDeps.build();
}
Aggregations