com.facebook.buck.js.ReactNativeDeps.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.js.ReactNativeDeps.java

Source

/*
 * Copyright 2015-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.js;

import com.facebook.buck.io.MorePaths;
import com.facebook.buck.model.BuildTargets;
import com.facebook.buck.rules.AbstractBuildRule;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.BuildContext;
import com.facebook.buck.rules.BuildOutputInitializer;
import com.facebook.buck.rules.BuildRuleParams;
import com.facebook.buck.rules.BuildableContext;
import com.facebook.buck.rules.InitializableFromDisk;
import com.facebook.buck.rules.OnDiskBuildInfo;
import com.facebook.buck.rules.Sha1HashCode;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePaths;
import com.facebook.buck.shell.ShellStep;
import com.facebook.buck.step.AbstractExecutionStep;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.step.Step;
import com.facebook.buck.step.fs.MakeCleanDirectoryStep;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;

import java.io.IOException;
import java.nio.file.Path;
import java.util.Arrays;

import javax.annotation.Nullable;

public class ReactNativeDeps extends AbstractBuildRule
        implements InitializableFromDisk<ReactNativeDeps.BuildOutput> {

    private static final String METADATA_KEY_FOR_INPUTS_HASH = "js_inputs_hash";

    @AddToRuleKey
    private final ImmutableSortedSet<SourcePath> srcs;

    @AddToRuleKey
    private final SourcePath entryPath;

    @AddToRuleKey
    private final ReactNativePlatform platform;

    @AddToRuleKey
    private final Optional<String> packagerFlags;

    @AddToRuleKey
    private final SourcePath jsPackager;

    private final Path outputDir;
    private final Path inputsHashFile;

    private final BuildOutputInitializer<BuildOutput> outputInitializer;

    public ReactNativeDeps(BuildRuleParams ruleParams, SourcePathResolver resolver, SourcePath jsPackager,
            ImmutableSortedSet<SourcePath> srcs, SourcePath entryPath, ReactNativePlatform platform,
            Optional<String> packagerFlags) {
        super(ruleParams, resolver);
        this.jsPackager = jsPackager;
        this.srcs = srcs;
        this.entryPath = entryPath;
        this.platform = platform;
        this.packagerFlags = packagerFlags;
        this.outputDir = BuildTargets.getGenPath(getBuildTarget(), "%s");
        this.inputsHashFile = outputDir.resolve("inputs_hash.txt");
        this.outputInitializer = new BuildOutputInitializer<>(ruleParams.getBuildTarget(), this);
    }

    @Override
    public ImmutableList<Step> getBuildSteps(BuildContext context, final BuildableContext buildableContext) {
        ImmutableList.Builder<Step> steps = ImmutableList.builder();

        final Path output = BuildTargets.getScratchPath(getBuildTarget(), "__%s/deps.txt");
        steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), output.getParent()));

        steps.add(new ShellStep(getProjectFilesystem().getRootPath()) {
            @Override
            protected ImmutableList<String> getShellCommandInternal(ExecutionContext context) {
                ImmutableList.Builder<String> builder = ImmutableList.builder();

                builder.add(getResolver().getAbsolutePath(jsPackager).toString(), "list-dependencies",
                        platform.toString(),
                        getProjectFilesystem().resolve(getResolver().getAbsolutePath(entryPath)).toString(),
                        "--output", getProjectFilesystem().resolve(output).toString());

                if (packagerFlags.isPresent()) {
                    builder.addAll(Arrays.asList(packagerFlags.get().split(" ")));
                }

                return builder.build();
            }

            @Override
            public String getShortName() {
                return "react-native-deps";
            }
        });

        steps.add(new MakeCleanDirectoryStep(getProjectFilesystem(), outputDir));

        steps.add(new AbstractExecutionStep("hash_js_inputs") {
            @Override
            public int execute(ExecutionContext context) throws IOException {
                ImmutableList<Path> paths;
                try {
                    paths = FluentIterable.from(getProjectFilesystem().readLines(output))
                            .transform(MorePaths.TO_PATH).transform(getProjectFilesystem().getRelativizer())
                            .toSortedList(Ordering.natural());
                } catch (IOException e) {
                    context.logError(e, "Error reading output of the 'react-native-deps' step.");
                    return 1;
                }

                FluentIterable<SourcePath> unlistedSrcs = FluentIterable.from(paths)
                        .transform(SourcePaths.toSourcePath(getProjectFilesystem()))
                        .filter(Predicates.not(Predicates.in(srcs)));
                if (!unlistedSrcs.isEmpty()) {
                    context.logError(new RuntimeException(),
                            "Entry path '%s' transitively uses the following source files which were not "
                                    + "included in 'srcs':\n%s",
                            entryPath, Joiner.on('\n').join(unlistedSrcs));
                    return 1;
                }

                Hasher hasher = Hashing.sha1().newHasher();
                for (Path path : paths) {
                    try {
                        hasher.putUnencodedChars(getProjectFilesystem().computeSha1(path));
                    } catch (IOException e) {
                        context.logError(e, "Error hashing input file: %s", path);
                        return 1;
                    }
                }

                String inputsHash = hasher.hash().toString();
                buildableContext.addMetadata(METADATA_KEY_FOR_INPUTS_HASH, inputsHash);
                getProjectFilesystem().writeContentsToPath(inputsHash, inputsHashFile);
                return 0;
            }
        });

        return steps.build();
    }

    @Override
    @Nullable
    public Path getPathToOutput() {
        return outputDir;
    }

    public Sha1HashCode getInputsHash() {
        return outputInitializer.getBuildOutput().inputsHash;
    }

    @Override
    public BuildOutput initializeFromDisk(OnDiskBuildInfo onDiskBuildInfo) throws IOException {
        Optional<Sha1HashCode> hash = onDiskBuildInfo.getHash(METADATA_KEY_FOR_INPUTS_HASH);
        Preconditions.checkState(hash.isPresent());
        return new BuildOutput(hash.get());
    }

    @Override
    public BuildOutputInitializer<BuildOutput> getBuildOutputInitializer() {
        return outputInitializer;
    }

    public static class BuildOutput {
        private final Sha1HashCode inputsHash;

        private BuildOutput(Sha1HashCode hash) {
            this.inputsHash = hash;
        }
    }
}