Search in sources :

Example 61 with VisibleForTesting

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);
    }
}
Also used : Path(java.nio.file.Path) SourcePath(com.facebook.buck.rules.SourcePath) ExplicitBuildTargetSourcePath(com.facebook.buck.rules.ExplicitBuildTargetSourcePath) EnumSet(java.util.EnumSet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) ImmutableSortedSet(com.google.common.collect.ImmutableSortedSet) MkdirStep(com.facebook.buck.step.fs.MkdirStep) Multimap(com.google.common.collect.Multimap) ImmutableMultimap(com.google.common.collect.ImmutableMultimap) ImmutableSet(com.google.common.collect.ImmutableSet) MakeCleanDirectoryStep(com.facebook.buck.step.fs.MakeCleanDirectoryStep) Supplier(com.google.common.base.Supplier) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 62 with VisibleForTesting

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);
}
Also used : Path(java.nio.file.Path) SourcePath(com.facebook.buck.rules.SourcePath) ExplicitBuildTargetSourcePath(com.facebook.buck.rules.ExplicitBuildTargetSourcePath) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 63 with VisibleForTesting

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));
}
Also used : Splitter(com.google.common.base.Splitter) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 64 with VisibleForTesting

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();
}
Also used : SourcePath(com.facebook.buck.rules.SourcePath) Path(java.nio.file.Path) PathSourcePath(com.facebook.buck.rules.PathSourcePath) SourcePath(com.facebook.buck.rules.SourcePath) PathSourcePath(com.facebook.buck.rules.PathSourcePath) SimpleFileVisitor(java.nio.file.SimpleFileVisitor) HumanReadableException(com.facebook.buck.util.HumanReadableException) ImmutableSortedMap(com.google.common.collect.ImmutableSortedMap) PathSourcePath(com.facebook.buck.rules.PathSourcePath) IOException(java.io.IOException) BasicFileAttributes(java.nio.file.attribute.BasicFileAttributes) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Example 65 with VisibleForTesting

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();
}
Also used : JavaLibrary(com.facebook.buck.jvm.java.JavaLibrary) DefaultJavaLibrary(com.facebook.buck.jvm.java.DefaultJavaLibrary) BuildRuleParams(com.facebook.buck.rules.BuildRuleParams) BuildTarget(com.facebook.buck.model.BuildTarget) BuildRule(com.facebook.buck.rules.BuildRule) ImmutableMultimap(com.google.common.collect.ImmutableMultimap) VisibleForTesting(com.google.common.annotations.VisibleForTesting)

Aggregations

VisibleForTesting (com.google.common.annotations.VisibleForTesting)808 IOException (java.io.IOException)135 ArrayList (java.util.ArrayList)67 Map (java.util.Map)52 Path (java.nio.file.Path)47 File (java.io.File)41 HashMap (java.util.HashMap)37 Path (org.apache.hadoop.fs.Path)33 List (java.util.List)29 ImmutableList (com.google.common.collect.ImmutableList)28 Matcher (java.util.regex.Matcher)26 HashSet (java.util.HashSet)23 ImmutableMap (com.google.common.collect.ImmutableMap)21 FileStatus (org.apache.hadoop.fs.FileStatus)21 SourcePath (com.facebook.buck.rules.SourcePath)20 FileHandle (org.apache.hadoop.nfs.nfs3.FileHandle)19 DFSClient (org.apache.hadoop.hdfs.DFSClient)18 Nfs3FileAttributes (org.apache.hadoop.nfs.nfs3.Nfs3FileAttributes)18 ImmutableSet (com.google.common.collect.ImmutableSet)17 LinkedHashMap (java.util.LinkedHashMap)17