Java tutorial
/* * Copyright 2013-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.json.BuildFileParseException; import com.facebook.buck.json.DefaultProjectBuildFileParserFactory; import com.facebook.buck.json.ProjectBuildFileParser; import com.facebook.buck.json.ProjectBuildFileParserFactory; import com.facebook.buck.util.Escaper; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.util.MoreStrings; import com.facebook.buck.util.ProjectFilesystem; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Predicate; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.io.IOException; import java.io.PrintStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.EnumSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.SortedSet; import javax.annotation.Nullable; /** * Evaluates a build file and prints out an equivalent build file with all includes/macros * expanded. When complex macros are in play, this helps clarify what the resulting build rule * definitions are. */ public class AuditRulesCommand extends AbstractCommandRunner<AuditRulesOptions> { /** Indent to use in generated build file. */ private static final String INDENT = " "; /** * Properties from the JSON produced by {@code buck.py} that start with this prefix do not * correspond to build rule arguments specified by the user. Instead, they contain internal-only * metadata, so they should not be printed when the build rule is reproduced. */ private static final String INTERNAL_PROPERTY_NAME_PREFIX = "buck."; /** * The name of the property in the JSON produced by {@code buck.py} that identifies the type of * the build rule being defined. * <p> * TODO(mbolin): Change this property name to "buck.type" so that all internal properties start * with "buck.". */ private static final String TYPE_PROPERTY_NAME = "type"; /** Properties that should be listed last in the declaration of a build rule. */ private static final ImmutableSet<String> LAST_PROPERTIES = ImmutableSet.of("deps", "visibility"); protected AuditRulesCommand(CommandRunnerParams params) { super(params); } @Override AuditRulesOptions createOptions(BuckConfig buckConfig) { return new AuditRulesOptions(buckConfig); } @Override String getUsageIntro() { return "List build rule definitions resulting from expanding macros."; } /** Prints the expanded build rules from the specified build files to the console. */ @Override int runCommandWithOptionsInternal(AuditRulesOptions options) throws IOException { ProjectFilesystem projectFilesystem = getProjectFilesystem(); ProjectBuildFileParserFactory factory = new DefaultProjectBuildFileParserFactory(projectFilesystem, options.getBuckConfig().getPythonInterpreter(), // TODO(simons): When we land dynamic loading, this MUST change. getBuildRuleTypes().getAllDescriptions()); try (ProjectBuildFileParser parser = factory.createParser(options.getBuckConfig().getDefaultIncludes(), EnumSet.noneOf(ProjectBuildFileParser.Option.class), console)) { PrintStream out = console.getStdOut(); for (String pathToBuildFile : options.getArguments()) { // Print a comment with the path to the build file. out.printf("# %s\n\n", pathToBuildFile); // Resolve the path specified by the user. Path path = Paths.get(pathToBuildFile); if (!path.isAbsolute()) { Path root = projectFilesystem.getProjectRoot().toPath(); path = root.resolve(path); } // Parse the rules from the build file. List<Map<String, Object>> rawRules; try { rawRules = parser.getAllRules(path); } catch (BuildFileParseException e) { throw new HumanReadableException(e); } // Format and print the rules from the raw data, filtered by type. final ImmutableSet<String> types = options.getTypes(); Predicate<String> includeType = new Predicate<String>() { @Override public boolean apply(String type) { return types.isEmpty() || types.contains(type); } }; printRulesToStdout(rawRules, includeType); } } catch (BuildFileParseException e) { throw new HumanReadableException("Unable to create parser"); } return 0; } private void printRulesToStdout(List<Map<String, Object>> rawRules, Predicate<String> includeType) { PrintStream out = console.getStdOut(); for (Map<String, Object> rawRule : rawRules) { String type = (String) rawRule.get(TYPE_PROPERTY_NAME); if (!includeType.apply(type)) { continue; } out.printf("%s(\n", type); // The properties in the order they should be displayed for this rule. LinkedHashSet<String> properties = Sets.newLinkedHashSet(); // Always display the "name" property first. properties.add("name"); // Add the properties specific to the rule. SortedSet<String> customProperties = Sets.newTreeSet(); for (String key : rawRule.keySet()) { // Ignore keys that start with "buck.". if (!(key.equals(TYPE_PROPERTY_NAME) || key.startsWith(INTERNAL_PROPERTY_NAME_PREFIX) || LAST_PROPERTIES.contains(key))) { customProperties.add(key); } } properties.addAll(customProperties); // Add common properties that should be displayed last. properties.addAll(LAST_PROPERTIES); // Write out the properties and their corresponding values. for (String property : properties) { String displayValue = createDisplayString(rawRule.get(property)); out.printf("%s%s = %s,\n", INDENT, property, displayValue); } // Close the rule definition. out.printf(")\n\n"); } } /** * @param value in a Map returned by {@link ProjectBuildFileParser#getAllRules(Path)}. * @return a string that represents the Python equivalent of the value. */ @VisibleForTesting static String createDisplayString(@Nullable Object value) { if (value == null) { return "None"; } else if (value instanceof Boolean) { return MoreStrings.capitalize(value.toString()); } else if (value instanceof String) { return Escaper.escapeAsPythonString(value.toString()); } else if (value instanceof Number) { return value.toString(); } else if (value instanceof List) { StringBuilder out = new StringBuilder("[\n"); String indent = Strings.repeat(INDENT, 2); for (Object item : (List<?>) value) { out.append(indent).append(createDisplayString(item)).append(",\n"); } out.append(INDENT).append("]"); return out.toString(); } else { throw new IllegalStateException(); } } }