com.facebook.buck.android.HashInputJarsToDexStep.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.android.HashInputJarsToDexStep.java

Source

/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.
 */

package com.facebook.buck.android;

import com.facebook.buck.java.classes.ClasspathTraversal;
import com.facebook.buck.java.classes.DefaultClasspathTraverser;
import com.facebook.buck.java.classes.FileLike;
import com.facebook.buck.rules.Sha1HashCode;
import com.facebook.buck.step.AbstractExecutionStep;
import com.facebook.buck.step.ExecutionContext;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
import com.google.common.hash.HashCode;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
 * Step responsible for hashing dex inputs to be passed to {@link SmartDexingStep}. It goes through
 * each dex input, looks at the classes that went into that jar, and uses the class hashes collected
 * in {@link AndroidDexTransitiveDependencies} to calculate the final hash of the input.
 */
public class HashInputJarsToDexStep extends AbstractExecutionStep
        implements SmartDexingStep.DexInputHashesProvider {

    private final Supplier<Set<Path>> primaryInputsToDex;
    private final Optional<Supplier<Multimap<Path, Path>>> secondaryOutputToInputs;
    private final Supplier<Map<String, HashCode>> classNamesToHashesSupplier;

    private final ImmutableMap.Builder<Path, Sha1HashCode> dexInputsToHashes;
    private boolean stepFinished;

    public HashInputJarsToDexStep(Supplier<Set<Path>> primaryInputsToDex,
            Optional<Supplier<Multimap<Path, Path>>> secondaryOutputToInputs,
            Supplier<Map<String, HashCode>> classNamesToHashesSupplier) {
        super("collect_smart_dex_inputs_hash");
        this.primaryInputsToDex = Preconditions.checkNotNull(primaryInputsToDex);
        this.secondaryOutputToInputs = Preconditions.checkNotNull(secondaryOutputToInputs);
        this.classNamesToHashesSupplier = Preconditions.checkNotNull(classNamesToHashesSupplier);

        this.dexInputsToHashes = ImmutableMap.builder();
        this.stepFinished = false;
    }

    @Override
    public int execute(final ExecutionContext context) {
        ImmutableList.Builder<Path> allInputs = ImmutableList.builder();
        allInputs.addAll(primaryInputsToDex.get());
        if (secondaryOutputToInputs.isPresent()) {
            allInputs.addAll(secondaryOutputToInputs.get().get().values());
        }

        final Map<String, HashCode> classNamesToHashes = classNamesToHashesSupplier.get();

        for (Path path : allInputs.build()) {
            try {
                final Hasher hasher = Hashing.sha1().newHasher();
                new DefaultClasspathTraverser().traverse(
                        new ClasspathTraversal(Collections.singleton(path), context.getProjectFilesystem()) {
                            @Override
                            public void visit(FileLike fileLike) throws IOException {
                                String className = fileLike.getRelativePath().replaceAll("\\.class$", "");
                                if (classNamesToHashes.containsKey(className)) {
                                    HashCode classHash = classNamesToHashes.get(className);
                                    hasher.putBytes(classHash.asBytes());
                                }
                            }
                        });
                dexInputsToHashes.put(path, new Sha1HashCode(hasher.hash().toString()));
            } catch (IOException e) {
                context.logError(e, "Error hashing smart dex input: %s", path);
                return 1;
            }
        }
        stepFinished = true;
        return 0;
    }

    @Override
    public ImmutableMap<Path, Sha1HashCode> getDexInputHashes() {
        Preconditions.checkState(stepFinished,
                "Either the step did not complete successfully or getDexInputHashes() was called before "
                        + "it could finish its execution.");
        return dexInputsToHashes.build();
    }
}