// Copyright 2015 The Bazel Authors. All rights reserved. // // 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.google.devtools.build.lib.rules.genquery; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.devtools.build.lib.actions.ActionExecutionContext; import com.google.devtools.build.lib.actions.ActionOwner; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.cmdline.ResolvedTargets; import com.google.devtools.build.lib.cmdline.TargetParsingException; import com.google.devtools.build.lib.cmdline.TargetPattern; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.packages.NoSuchTargetException; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.pkgcache.FilteringPolicies; import com.google.devtools.build.lib.pkgcache.FilteringPolicy; import com.google.devtools.build.lib.pkgcache.PackageProvider; import com.google.devtools.build.lib.pkgcache.TargetPatternEvaluator; import com.google.devtools.build.lib.query2.BlazeQueryEnvironment; import com.google.devtools.build.lib.query2.QueryEnvironmentFactory; import com.google.devtools.build.lib.query2.engine.DigraphQueryEvalResult; import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction; import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting; import com.google.devtools.build.lib.query2.engine.QueryException; import com.google.devtools.build.lib.query2.engine.QueryUtil.AggregateAllCallback; import com.google.devtools.build.lib.query2.engine.SkyframeRestartQueryException; import com.google.devtools.build.lib.query2.output.OutputFormatter; import com.google.devtools.build.lib.query2.output.QueryOptions; import com.google.devtools.build.lib.query2.output.QueryOptions.OrderOutput; import com.google.devtools.build.lib.query2.output.QueryOutputUtils; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.skyframe.PackageValue; import com.google.devtools.build.lib.skyframe.PrecomputedValue.Precomputed; import com.google.devtools.build.lib.skyframe.SkyFunctions; import com.google.devtools.build.lib.skyframe.TargetPatternValue; import com.google.devtools.build.lib.skyframe.TransitiveTargetValue; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.ValueOrException; import com.google.devtools.common.options.OptionsParser; import com.google.devtools.common.options.OptionsParsingException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.nio.channels.ClosedByInterruptException; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; /** * An implementation of the 'genquery' rule. */ public class GenQuery implements RuleConfiguredTargetFactory { private static final QueryEnvironmentFactory QUERY_ENVIRONMENT_FACTORY = new QueryEnvironmentFactory(); public static final Precomputed> QUERY_OUTPUT_FORMATTERS = new Precomputed<>(SkyKey.create(SkyFunctions.PRECOMPUTED, "query_output_formatters")); @Override @Nullable public ConfiguredTarget create(RuleContext ruleContext) throws InterruptedException { Artifact outputArtifact = ruleContext.createOutputArtifact(); // The query string final String query = ruleContext.attributes().get("expression", Type.STRING); OptionsParser optionsParser = OptionsParser.newOptionsParser(QueryOptions.class); optionsParser.setAllowResidue(false); try { optionsParser.parse(ruleContext.attributes().get("opts", Type.STRING_LIST)); } catch (OptionsParsingException e) { ruleContext.attributeError("opts", "error while parsing query options: " + e.getMessage()); return null; } // Parsed query options QueryOptions queryOptions = optionsParser.getOptions(QueryOptions.class); if (queryOptions.keepGoing) { ruleContext.attributeError("opts", "option --keep_going is not allowed"); return null; } if (!queryOptions.universeScope.isEmpty()) { ruleContext.attributeError("opts", "option --universe_scope is not allowed"); return null; } if (optionsParser.containsExplicitOption("order_results")) { ruleContext.attributeError("opts", "option --order_results is not allowed"); return null; } if (optionsParser.containsExplicitOption("noorder_results")) { ruleContext.attributeError("opts", "option --noorder_results is not allowed"); return null; } if (optionsParser.containsExplicitOption("order_output")) { ruleContext.attributeError("opts", "option --order_output is not allowed"); return null; } // Force results to be deterministic. queryOptions.orderOutput = OrderOutput.FULL; // force relative_locations to true so it has a deterministic output across machines. queryOptions.relativeLocations = true; final byte[] result = executeQuery(ruleContext, queryOptions, getScope(ruleContext), query); if (result == null || ruleContext.hasErrors()) { return null; } ruleContext.registerAction( new QueryResultAction(ruleContext.getActionOwner(), outputArtifact, result)); NestedSet filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, outputArtifact); return new RuleConfiguredTargetBuilder(ruleContext) .setFilesToBuild(filesToBuild) .add(RunfilesProvider.class, RunfilesProvider.simple( new Runfiles.Builder( ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) .addTransitiveArtifacts(filesToBuild).build())) .build(); } // The transitive closure of these targets is an upper estimate on the labels // the query will touch private Set getScope(RuleContext context) { List