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

Java tutorial

Introduction

Here is the source code for com.facebook.buck.cli.AbstractCommand.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.config.CellConfig;
import com.facebook.buck.event.BuckEventListener;
import com.facebook.buck.event.ConsoleEvent;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.log.LogConfigSetup;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.parser.BuildTargetParser;
import com.facebook.buck.parser.BuildTargetPatternParser;
import com.facebook.buck.parser.BuildTargetPatternTargetNodeParser;
import com.facebook.buck.parser.TargetNodeSpec;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.RelativeCellName;
import com.facebook.buck.rules.TargetGraphAndBuildTargets;
import com.facebook.buck.step.ExecutionContext;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.concurrent.ConcurrencyLimit;
import com.facebook.buck.versions.VersionBuckConfig;
import com.facebook.buck.versions.VersionException;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;

import org.kohsuke.args4j.Option;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ForkJoinPool;

import javax.annotation.Nullable;

public abstract class AbstractCommand implements Command {

    private static final String HELP_LONG_ARG = "--help";
    private static final String NO_CACHE_LONG_ARG = "--no-cache";
    private static final String OUTPUT_TEST_EVENTS_TO_FILE_LONG_ARG = "--output-test-events-to-file";
    private static final String PROFILE_PARSER_LONG_ARG = "--profile-buck-parser";
    private static final String NUM_THREADS_LONG_ARG = "--num-threads";
    private static final String LOAD_LIMIT_LONG_ARG = "--load-limit";

    /**
     * This value should never be read. {@link VerbosityParser} should be used instead.
     * args4j requires that all options that could be passed in are listed as fields, so we include
     * this field so that {@code --verbose} is universally available to all commands.
     */
    @Option(name = VerbosityParser.VERBOSE_LONG_ARG, aliases = {
            VerbosityParser.VERBOSE_SHORT_ARG }, usage = "Specify a number between 0 and 8. '-v 1' is default, '-v 8' is most verbose.")
    @SuppressWarnings("PMD.UnusedPrivateField")
    private int verbosityLevel = -1;

    @Option(name = NUM_THREADS_LONG_ARG, aliases = "-j", usage = "Default is 1.25 * num processors.")
    @Nullable
    private Integer numThreads = null;

    @Nullable
    @Option(name = LOAD_LIMIT_LONG_ARG, aliases = "-L", usage = "[Float] Do not start new jobs when system load is above this level."
            + " See uptime(1).")
    private Double loadLimit = null;

    @Option(name = "--config", aliases = { "-c" }, usage = "")
    private Map<String, String> configOverrides = Maps.newLinkedHashMap();

    @Override
    public CellConfig getConfigOverrides() {
        CellConfig.Builder builder = CellConfig.builder();

        // Parse command-line config overrides.
        for (Map.Entry<String, String> entry : configOverrides.entrySet()) {
            List<String> key = Splitter.on("//").limit(2).splitToList(entry.getKey());
            RelativeCellName cellName = RelativeCellName.ROOT_CELL_NAME;
            String configKey = key.get(0);
            if (key.size() == 2) {
                // Here we explicitly take the whole string as the cell name. We don't support transitive
                // path overrides for cells.
                cellName = RelativeCellName.of(ImmutableSet.of(key.get(0)));
                configKey = key.get(1);
            }
            int separatorIndex = configKey.lastIndexOf('.');
            if (separatorIndex < 0 || separatorIndex == configKey.length() - 1) {
                throw new HumanReadableException(
                        "Invalid config override \"%s=%s\" Expected <section>.<field>=<value>.", configKey,
                        entry.getValue());
            }
            String value = entry.getValue();
            // If the value is empty, un-set the config
            if (value == null) {
                value = "";
            }

            // Overrides for locations of transitive children of cells are weird as the order of overrides
            // can affect the result (for example `-c a/b/c.k=v -c a/b//repositories.c=foo` causes an
            // interesting problem as the a/b/c cell gets created as a side-effect of the first override,
            // but the second override wants to change its identity).
            // It's generally a better idea to use the .buckconfig.local mechanism when overriding
            // repositories anyway, so here we simply disallow them.
            String section = configKey.substring(0, separatorIndex);
            if (section.equals("repositories")) {
                throw new HumanReadableException("Overriding repository locations from the command line "
                        + "is not supported. Please place a .buckconfig.local in the appropriate location and "
                        + "use that instead.");
            }
            String field = configKey.substring(separatorIndex + 1);
            builder.put(cellName, section, field, value);
        }
        if (numThreads != null) {
            builder.put(CellConfig.ALL_CELLS_OVERRIDE, "build", "threads", String.valueOf(numThreads));
        }
        if (noCache) {
            builder.put(CellConfig.ALL_CELLS_OVERRIDE, "cache", "mode", "");
        }

        return builder.build();
    }

    @Override
    public LogConfigSetup getLogConfig() {
        return LogConfigSetup.DEFAULT_SETUP;
    }

    @Option(name = NO_CACHE_LONG_ARG, usage = "Whether to ignore the [cache] declared in .buckconfig.")
    private boolean noCache = false;

    @Nullable
    @Option(name = OUTPUT_TEST_EVENTS_TO_FILE_LONG_ARG, aliases = {
            "--output-events-to-file" }, usage = "Serialize test-related event-bus events to the given file "
                    + "as line-oriented JSON objects.")
    private String eventsOutputPath = null;

    @Option(name = PROFILE_PARSER_LONG_ARG, usage = "Enable profiling of buck.py internals (not the target being compiled) in the debug"
            + "log and trace.")
    private boolean enableParserProfiling = false;

    @Option(name = HELP_LONG_ARG, usage = "Prints the available options and exits.")
    private boolean help = false;

    /** @return {code true} if the {@code [cache]} in {@code .buckconfig} should be ignored. */
    public boolean isNoCache() {
        return noCache;
    }

    public boolean showHelp() {
        return help;
    }

    public Optional<Path> getEventsOutputPath() {
        if (eventsOutputPath == null) {
            return Optional.empty();
        } else {
            return Optional.of(Paths.get(eventsOutputPath));
        }
    }

    @Override
    public final int run(CommandRunnerParams params) throws IOException, InterruptedException {
        if (showHelp()) {
            new AdditionalOptionsCmdLineParser(this).printUsage(params.getConsole().getStdErr());
            return 1;
        }
        if (params.getConsole().getAnsi().isAnsiTerminal()) {
            ImmutableList<String> motd = params.getBuckConfig().getMessageOfTheDay();
            if (!motd.isEmpty()) {
                for (String line : motd) {
                    params.getBuckEventBus().post(ConsoleEvent.info(line));
                }
            }
        }
        return runWithoutHelp(params);
    }

    public abstract int runWithoutHelp(CommandRunnerParams params) throws IOException, InterruptedException;

    protected CommandLineBuildTargetNormalizer getCommandLineBuildTargetNormalizer(BuckConfig buckConfig) {
        return new CommandLineBuildTargetNormalizer(buckConfig);
    }

    public boolean getEnableParserProfiling() {
        return enableParserProfiling;
    }

    public ImmutableList<TargetNodeSpec> parseArgumentsAsTargetNodeSpecs(BuckConfig config,
            Iterable<String> targetsAsArgs) {
        ImmutableList.Builder<TargetNodeSpec> specs = ImmutableList.builder();
        CommandLineTargetNodeSpecParser parser = new CommandLineTargetNodeSpecParser(config,
                new BuildTargetPatternTargetNodeParser());
        for (String arg : targetsAsArgs) {
            specs.addAll(parser.parse(config.getCellPathResolver(), arg));
        }
        return specs.build();
    }

    /**
     *
     * @param cellNames
     * @param buildTargetNames The build targets to parse, represented as strings.
     * @return A set of {@link BuildTarget}s for the input buildTargetNames.
     */
    protected ImmutableSet<BuildTarget> getBuildTargets(CellPathResolver cellNames,
            Iterable<String> buildTargetNames) {
        ImmutableSet.Builder<BuildTarget> buildTargets = ImmutableSet.builder();

        // Parse all of the build targets specified by the user.
        for (String buildTargetName : buildTargetNames) {
            buildTargets.add(BuildTargetParser.INSTANCE.parse(buildTargetName,
                    BuildTargetPatternParser.fullyQualified(), cellNames));
        }

        return buildTargets.build();
    }

    protected ExecutionContext createExecutionContext(CommandRunnerParams params) {
        return ExecutionContext.builder().setConsole(params.getConsole())
                .setAndroidPlatformTargetSupplier(params.getAndroidPlatformTargetSupplier())
                .setBuckEventBus(params.getBuckEventBus()).setPlatform(params.getPlatform())
                .setEnvironment(params.getEnvironment()).setJavaPackageFinder(params.getJavaPackageFinder())
                .setObjectMapper(params.getObjectMapper()).setExecutors(params.getExecutors())
                .setCellPathResolver(params.getCell().getCellPathResolver()).build();
    }

    public ConcurrencyLimit getConcurrencyLimit(BuckConfig buckConfig) {
        Double loadLimit = this.loadLimit;
        if (loadLimit == null) {
            loadLimit = (double) buckConfig.getLoadLimit();
        }

        return new ConcurrencyLimit(buckConfig.getNumThreads(), loadLimit,
                buckConfig.getResourceAllocationFairness(), buckConfig.getManagedThreadCount(),
                buckConfig.getDefaultResourceAmounts(), buckConfig.getMaximumResourceAmounts());
    }

    protected ImmutableList<String> getOptions() {
        ImmutableList.Builder<String> builder = ImmutableList.builder();
        if (verbosityLevel != -1) {
            builder.add(VerbosityParser.VERBOSE_LONG_ARG);
            builder.add(String.valueOf(verbosityLevel));
        }
        if (numThreads != null) {
            builder.add(NUM_THREADS_LONG_ARG);
            builder.add(numThreads.toString());
        }
        if (loadLimit != null) {
            builder.add(LOAD_LIMIT_LONG_ARG);
            builder.add(loadLimit.toString());
        }
        if (noCache) {
            builder.add(NO_CACHE_LONG_ARG);
        }
        if (eventsOutputPath != null) {
            builder.add(OUTPUT_TEST_EVENTS_TO_FILE_LONG_ARG);
            builder.add(eventsOutputPath);
        }
        if (enableParserProfiling) {
            builder.add(PROFILE_PARSER_LONG_ARG);
        }
        return builder.build();
    }

    @Override
    public boolean isSourceControlStatsGatheringEnabled() {
        return false;
    }

    TargetGraphAndBuildTargets toVersionedTargetGraph(CommandRunnerParams params,
            TargetGraphAndBuildTargets targetGraphAndBuildTargets) throws VersionException, InterruptedException {
        return params.getVersionedTargetGraphCache()
                .getVersionedTargetGraph(params.getBuckEventBus(), targetGraphAndBuildTargets,
                        new VersionBuckConfig(params.getBuckConfig()).getVersionUniverses(),
                        new ForkJoinPool(params.getBuckConfig().getNumThreads()))
                .getTargetGraphAndBuildTargets();
    }

    @Override
    public Iterable<BuckEventListener> getEventListeners(Path logDirectoryPath, ProjectFilesystem filesystem) {
        return ImmutableList.of();
    }
}