com.facebook.buck.cli.ProjectCommand.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cli.ProjectCommand.java

Source

/*
 * Copyright 2012-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.cli;

import com.facebook.buck.apple.XcodeProjectConfigDescription;
import com.facebook.buck.apple.xcode.ProjectGenerator;
import com.facebook.buck.apple.xcode.SeparatedProjectsGenerator;
import com.facebook.buck.command.Project;
import com.facebook.buck.java.JavaLibraryDescription;
import com.facebook.buck.json.BuildFileParseException;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetException;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.parser.PartialGraph;
import com.facebook.buck.parser.RawRulePredicate;
import com.facebook.buck.parser.RawRulePredicates;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleType;
import com.facebook.buck.rules.ProjectConfigDescription;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.ProcessExecutor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;

import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;

public class ProjectCommand extends AbstractCommandRunner<ProjectCommandOptions> {

    /**
     * Include java library targets (and android library targets) that use annotation
     * processing.  The sources generated by these annotation processors is needed by
     * IntelliJ.
     */
    private static final RawRulePredicate annotationPredicate = new RawRulePredicate() {

        @Override
        public boolean isMatch(Map<String, Object> rawParseData, BuildRuleType buildRuleType,
                BuildTarget buildTarget) {
            Object rawValue = rawParseData.get(JavaLibraryDescription.ANNOTATION_PROCESSORS);
            return ((rawValue instanceof Iterable) && !Iterables.isEmpty((Iterable<?>) rawValue));
        }
    };

    public ProjectCommand(CommandRunnerParams params) {
        super(params);
    }

    @Override
    ProjectCommandOptions createOptions(BuckConfig buckConfig) {
        return new ProjectCommandOptions(buckConfig);
    }

    @Override
    int runCommandWithOptionsInternal(ProjectCommandOptions options) throws IOException {
        switch (options.getIde()) {
        case "intellij":
            return runIntellijProjectGenerator(options);
        case "xcode":
            return runXcodeProjectGenerator(options);
        default:
            throw new HumanReadableException(String.format("Unknown IDE `%s` in .buckconfig", options.getIde()));
        }
    }

    /**
     * Run intellij specific project generation actions.
     */
    int runIntellijProjectGenerator(ProjectCommandOptions options) throws IOException {
        // Create a PartialGraph that only contains targets that can be represented as IDE
        // configuration files.
        PartialGraph partialGraph;

        try {
            partialGraph = createPartialGraph(RawRulePredicates.matchBuildRuleType(ProjectConfigDescription.TYPE),
                    options);
        } catch (BuildTargetException | BuildFileParseException e) {
            throw new HumanReadableException(e);
        }

        ExecutionContext executionContext = createExecutionContext(options, partialGraph.getDependencyGraph());

        Project project = new Project(partialGraph, options.getBasePathToAliasMap(), options.getJavaPackageFinder(),
                executionContext, getProjectFilesystem(), options.getPathToDefaultAndroidManifest(),
                options.getPathToPostProcessScript(), options.getBuckConfig().getPythonInterpreter());

        File tempDir = Files.createTempDir();
        File tempFile = new File(tempDir, "project.json");
        int exitCode;
        try {
            exitCode = createIntellijProject(project, tempFile, executionContext.getProcessExecutor(),
                    !options.getArgumentsFormattedAsBuildTargets().isEmpty(), console.getStdOut(),
                    console.getStdErr());
            if (exitCode != 0) {
                return exitCode;
            }

            List<String> additionalInitialTargets = ImmutableList.of();
            if (options.shouldProcessAnnotations()) {
                try {
                    additionalInitialTargets = getAnnotationProcessingTargets(options);
                } catch (BuildTargetException | BuildFileParseException e) {
                    throw new HumanReadableException(e);
                }
            }

            // Build initial targets.
            if (options.hasInitialTargets() || !additionalInitialTargets.isEmpty()) {
                BuildCommand buildCommand = new BuildCommand(getCommandRunnerParams());
                BuildCommandOptions buildOptions = options
                        .createBuildCommandOptionsWithInitialTargets(additionalInitialTargets);

                exitCode = runBuildCommand(buildCommand, buildOptions);
                if (exitCode != 0) {
                    return exitCode;
                }
            }
        } finally {
            // Either leave project.json around for debugging or delete it on exit.
            if (console.getVerbosity().shouldPrintOutput()) {
                getStdErr().printf("project.json was written to %s", tempFile.getAbsolutePath());
            } else {
                tempFile.delete();
                tempDir.delete();
            }
        }

        return 0;
    }

    List<String> getAnnotationProcessingTargets(ProjectCommandOptions options)
            throws BuildTargetException, BuildFileParseException, IOException {
        return ImmutableList.copyOf(Iterables.transform(
                createPartialGraph(annotationPredicate, options).getTargets(), new Function<BuildTarget, String>() {
                    @Override
                    public String apply(BuildTarget target) {
                        return target.getFullyQualifiedName();
                    }
                }));
    }

    /**
     * Calls {@link Project#createIntellijProject}
     *
     * This is factored into a separate method for testing purposes.
     */
    @VisibleForTesting
    int createIntellijProject(Project project, File jsonTemplate, ProcessExecutor processExecutor,
            boolean generateMinimalProject, PrintStream stdOut, PrintStream stdErr) throws IOException {
        return project.createIntellijProject(jsonTemplate, processExecutor, generateMinimalProject, stdOut, stdErr);
    }

    /**
     * Run xcode specific project generation actions.
     */
    int runXcodeProjectGenerator(ProjectCommandOptions options) throws IOException {

        PartialGraph partialGraph;
        try {
            partialGraph = PartialGraph.createFullGraph(getProjectFilesystem(), options.getDefaultIncludes(),
                    getParser(), getBuckEventBus());
        } catch (BuildTargetException | BuildFileParseException e) {
            throw new HumanReadableException(e);
        }

        ImmutableSet<BuildTarget> passedInTargetsSet;

        try {
            List<String> argumentsAsBuildTargets = options.getArgumentsFormattedAsBuildTargets();
            passedInTargetsSet = ImmutableSet.copyOf(getBuildTargets(argumentsAsBuildTargets));
        } catch (NoSuchBuildTargetException e) {
            throw new HumanReadableException(e);
        }

        ExecutionContext executionContext = createExecutionContext(options, partialGraph.getDependencyGraph());

        if (options.getCombinedProject() != null) {
            // Generate a single project containing a target and all its dependencies and tests.
            ProjectGenerator projectGenerator = new ProjectGenerator(partialGraph, passedInTargetsSet,
                    getProjectFilesystem(), executionContext,
                    getProjectFilesystem().getPathForRelativePath(Paths.get("_gen")), "GeneratedProject",
                    ProjectGenerator.COMBINED_PROJECT_OPTIONS);
            projectGenerator.createXcodeProjects();
            console.getStdOut().println(projectGenerator.getProjectPath());
        } else {
            // Generate projects based on xcode_project_config rules, and place them in the same directory
            // as the Buck file.

            ImmutableSet<BuildTarget> targets;
            if (passedInTargetsSet.isEmpty()) {
                ImmutableSet.Builder<BuildTarget> targetsBuilder = ImmutableSet.builder();
                for (BuildRule node : partialGraph.getDependencyGraph().getNodes()) {
                    if (node.getType() == XcodeProjectConfigDescription.TYPE) {
                        targetsBuilder.add(node.getBuildTarget());
                    }
                }
                targets = targetsBuilder.build();
            } else {
                targets = passedInTargetsSet;
            }

            SeparatedProjectsGenerator projectGenerator = new SeparatedProjectsGenerator(getProjectFilesystem(),
                    partialGraph, executionContext, targets);
            ImmutableSet<Path> generatedProjectPaths = projectGenerator.generateProjects();
            for (Path path : generatedProjectPaths) {
                console.getStdOut().println(path.toString());
            }
        }

        return 0;
    }

    /**
     * Calls {@link BuildCommand#runCommandWithOptions}
     *
     * This is factored into a separate method for testing purposes.
     */
    @VisibleForTesting
    int runBuildCommand(BuildCommand buildCommand, BuildCommandOptions options) throws IOException {
        return buildCommand.runCommandWithOptions(options);
    }

    @VisibleForTesting
    PartialGraph createPartialGraph(RawRulePredicate rulePredicate, ProjectCommandOptions options)
            throws BuildFileParseException, BuildTargetException, IOException {
        List<String> argumentsAsBuildTargets = options.getArgumentsFormattedAsBuildTargets();

        if (argumentsAsBuildTargets.isEmpty()) {
            return PartialGraph.createPartialGraph(rulePredicate, getProjectFilesystem(),
                    options.getDefaultIncludes(), getParser(), getBuckEventBus());
        } else {
            // If build targets were specified, generate a partial intellij project that contains the
            // files needed to build the build targets specified.
            ImmutableList<BuildTarget> targets = getBuildTargets(argumentsAsBuildTargets);

            if (options.isWithTests()) {
                return PartialGraph.createPartialGraphFromRootsWithTests(targets, rulePredicate,
                        getProjectFilesystem(), options.getDefaultIncludes(), getParser(), getBuckEventBus());
            } else {
                return PartialGraph.createPartialGraphFromRoots(targets, rulePredicate,
                        options.getDefaultIncludes(), getParser(), getBuckEventBus());
            }
        }
    }

    @Override
    String getUsageIntro() {
        return "generates project configuration files for an IDE";
    }
}