// Copyright 2015 Google Inc. 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.Preconditions; 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.Artifact; import com.google.devtools.build.lib.actions.Executor; 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.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.events.EventHandler; 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.PackageIdentifier; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.Type; 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.AbstractBlazeQueryEnvironment; import com.google.devtools.build.lib.query2.engine.BlazeQueryEvalResult; 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.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.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.Label; import com.google.devtools.build.lib.util.Fingerprint; import com.google.devtools.build.lib.util.Pair; 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.Collections; 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 { public static final Precomputed> QUERY_OUTPUT_FORMATTERS = new Precomputed<>(new SkyKey(SkyFunctions.PRECOMPUTED, "query_output_formatters")); @Override 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; } // 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 AbstractFileWriteAction( ruleContext.getActionOwner(), Collections.emptySet(), outputArtifact, false) { @Override public DeterministicWriter newDeterministicWriter(EventHandler eventHandler, Executor executor) { return new DeterministicWriter() { @Override public void writeOutputFile(OutputStream out) throws IOException { out.write(result); } }; } @Override protected String computeKey() { Fingerprint f = new Fingerprint(); f.addBytes(result); return f.hexDigestAndReset(); } }); NestedSet filesToBuild = NestedSetBuilder.create(Order.STABLE_ORDER, outputArtifact); return new RuleConfiguredTargetBuilder(ruleContext) .setFilesToBuild(filesToBuild) .add(RunfilesProvider.class, RunfilesProvider.simple( new Runfiles.Builder(ruleContext.getWorkspaceName()) .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