// Copyright 2014 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.packages; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.LabelValidator; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.events.NullEventHandler; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.GlobCache.BadGlobException; import com.google.devtools.build.lib.packages.License.DistributionType; import com.google.devtools.build.lib.syntax.AssignmentStatement; import com.google.devtools.build.lib.syntax.BaseFunction; import com.google.devtools.build.lib.syntax.BuildFileAST; import com.google.devtools.build.lib.syntax.BuiltinFunction; import com.google.devtools.build.lib.syntax.ClassObject; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.Extension; import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException; import com.google.devtools.build.lib.syntax.EvalException; import com.google.devtools.build.lib.syntax.EvalUtils; import com.google.devtools.build.lib.syntax.Expression; import com.google.devtools.build.lib.syntax.FuncallExpression; import com.google.devtools.build.lib.syntax.FunctionSignature; import com.google.devtools.build.lib.syntax.GlobList; import com.google.devtools.build.lib.syntax.Identifier; import com.google.devtools.build.lib.syntax.Mutability; import com.google.devtools.build.lib.syntax.ParserInputSource; import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SkylarkSignature; import com.google.devtools.build.lib.syntax.SkylarkSignature.Param; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor; import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor.HackHackEitherList; import com.google.devtools.build.lib.syntax.Statement; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.syntax.Type.ConversionException; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.UnixGlob; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import javax.annotation.Nullable; /** * The package factory is responsible for constructing Package instances * from a BUILD file's abstract syntax tree (AST). * *

A PackageFactory is a heavy-weight object; create them sparingly. * Typically only one is needed per client application. */ public final class PackageFactory { /** * An argument to the {@code package()} function. */ public abstract static class PackageArgument { private final String name; private final Type type; protected PackageArgument(String name, Type type) { this.name = name; this.type = type; } public String getName() { return name; } private void convertAndProcess( Package.LegacyBuilder pkgBuilder, Location location, Object value) throws EvalException, ConversionException { T typedValue = type.convert(value, "'package' argument", pkgBuilder.getBuildFileLabel()); process(pkgBuilder, location, typedValue); } /** * Process an argument. * * @param pkgBuilder the package builder to be mutated * @param location the location of the {@code package} function for error reporting * @param value the value of the argument. Typically passed to {@link Type#convert} */ protected abstract void process( Package.LegacyBuilder pkgBuilder, Location location, T value) throws EvalException; } /** Interface for evaluating globs during package loading. */ public static interface Globber { /** An opaque token for fetching the result of a glob computation. */ abstract static class Token {} /** * Asynchronously starts the given glob computation and returns a token for fetching the * result. */ Token runAsync(List includes, List excludes, boolean excludeDirs) throws BadGlobException; /** Fetches the result of a previously started glob computation. */ List fetch(Token token) throws IOException, InterruptedException; /** Should be called when the globber is about to be discarded due to an interrupt. */ void onInterrupt(); /** Should be called when the globber is no longer needed. */ void onCompletion(); /** Returns all the glob computations requested before {@link #onCompletion} was called. */ Set> getGlobPatterns(); } /** * An extension to the global namespace of the BUILD language. */ // TODO(bazel-team): this is largely unrelated to syntax.Environment.Extension, // and should probably be renamed PackageFactory.RuntimeExtension, since really, // we're extending the Runtime with more classes. public interface EnvironmentExtension { /** * Update the global environment with the identifiers this extension contributes. */ void update(Environment environment, Label buildFileLabel); /** * Returns the extra functions needed to be added to the Skylark native module. */ ImmutableList nativeModuleFunctions(); Iterable> getPackageArguments(); } private static class DefaultVisibility extends PackageArgument> { private DefaultVisibility() { super("default_visibility", BuildType.LABEL_LIST); } @Override protected void process(Package.LegacyBuilder pkgBuilder, Location location, List

{@code
   *   environment_group(
   *       name = "sample_group",
   *       environments = [":env1", ":env2", ...],
   *       defaults = [":env1", ...]
   *   )
   * }
* *

Where ":env1", "env2", ... are all environment rules declared in the same package. All * parameters are mandatory. */ @SkylarkSignature(name = "environment_group", returnType = Runtime.NoneType.class, doc = "Defines a cc_library, by wrapping around the usual library " + "and also defining a headers target.", mandatoryNamedOnly = { @Param(name = "name", type = String.class, doc = "The name of the rule."), // Both parameter below are lists of label designators @Param(name = "environments", type = HackHackEitherList.class, generic1 = Object.class, doc = "A list of Labels for the environments to be grouped, from the same package."), @Param(name = "defaults", type = HackHackEitherList.class, generic1 = Object.class, doc = "A list of Labels.")}, // TODO(bazel-team): document what that is documented = false, useLocation = true) private static final BuiltinFunction.Factory newEnvironmentGroupFunction = new BuiltinFunction.Factory("environment_group") { public BuiltinFunction create(final PackageContext context) { return new BuiltinFunction("environment_group", this) { public Runtime.NoneType invoke(String name, Object environmentsO, Object defaultsO, Location loc) throws EvalException, ConversionException { List

This method returns a builder to allow the caller to do additional work, if necessary. * *

This method assumes "packageId" is a valid package name according to the * {@link LabelValidator#validatePackageName} heuristic. * *

See {@link #evaluateBuildFile} for information on AST retention. * *

Executes {@code globber.onCompletion()} on completion and executes * {@code globber.onInterrupt()} on an {@link InterruptedException}. */ // Used outside of bazel! public Package.LegacyBuilder createPackageFromPreprocessingResult( Package externalPkg, PackageIdentifier packageId, Path buildFile, Preprocessor.Result preprocessingResult, Iterable preprocessingEvents, List preludeStatements, Map imports, ImmutableList

PLEASE NOTE: references to PackageContext objects are held by many * BaseFunction closures, but should become unreachable once the Environment is * discarded at the end of evaluation. Please be aware of your memory * footprint when making changes here! */ public static class PackageContext { final Package.LegacyBuilder pkgBuilder; final Globber globber; final EventHandler eventHandler; @VisibleForTesting public PackageContext(Package.LegacyBuilder pkgBuilder, Globber globber, EventHandler eventHandler) { this.pkgBuilder = pkgBuilder; this.eventHandler = eventHandler; this.globber = globber; } /** * Returns the Label of this Package. */ public Label getLabel() { return pkgBuilder.getBuildFileLabel(); } /** * Returns the MakeEnvironment Builder of this Package. */ public MakeEnvironment.Builder getMakeEnvironment() { return pkgBuilder.getMakeEnvironment(); } } private final ClassObject nativeModule; /** @return the Skylark struct to bind to "native" */ public ClassObject getNativeModule() { return nativeModule; } /** * Returns a native module with the functions created using the {@link RuleClassProvider} * of this {@link PackageFactory}. */ private ClassObject newNativeModule() { ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); for (String nativeFunction : Runtime.getFunctionNames(SkylarkNativeModule.class)) { builder.put(nativeFunction, Runtime.getFunction(SkylarkNativeModule.class, nativeFunction)); } for (String ruleClass : ruleFactory.getRuleClassNames()) { builder.put(ruleClass, newRuleFunction(ruleFactory, ruleClass)); } builder.put("package", newPackageFunction(packageArguments)); for (EnvironmentExtension extension : environmentExtensions) { for (BaseFunction function : extension.nativeModuleFunctions()) { builder.put(function.getName(), function); } } return new ClassObject.SkylarkClassObject(builder.build(), "no native function or rule '%s'"); } private void buildPkgEnv(Environment pkgEnv, PackageContext context, RuleFactory ruleFactory) { // TODO(bazel-team): remove the naked functions that are redundant with the nativeModule, // or if not possible, at least make them straight copies from the native module variant. // or better, use a common Environment.Frame for these common bindings // (that shares a backing ImmutableMap for the bindings?) pkgEnv .setup("native", nativeModule) .setup("distribs", newDistribsFunction.apply(context)) .setup("glob", newGlobFunction.apply(context, /*async=*/false)) .setup("mocksubinclude", newMockSubincludeFunction.apply(context)) .setup("licenses", newLicensesFunction.apply(context)) .setup("exports_files", newExportsFilesFunction.apply()) .setup("package_group", newPackageGroupFunction.apply()) .setup("package", newPackageFunction(packageArguments)) .setup("environment_group", newEnvironmentGroupFunction.apply(context)); for (String ruleClass : ruleFactory.getRuleClassNames()) { BaseFunction ruleFunction = newRuleFunction(ruleFactory, ruleClass); pkgEnv.setup(ruleClass, ruleFunction); } for (EnvironmentExtension extension : environmentExtensions) { extension.update(pkgEnv, context.pkgBuilder.getBuildFileLabel()); } } /** * Constructs a Package instance, evaluates the BUILD-file AST inside the * build environment, and populates the package with Rule instances as it * goes. As with most programming languages, evaluation stops when an * exception is encountered: no further rules after the point of failure will * be constructed. We assume that rules constructed before the point of * failure are valid; this assumption is not entirely correct, since a * "vardef" after a rule declaration can affect the behavior of that rule. * *

Rule attribute checking is performed during evaluation. Each attribute * must conform to the type specified for that (rule class, attribute * name) pair. Errors reported at this stage include: missing value for * mandatory attribute, value of wrong type. Such error cause Rule * construction to be aborted, so the resulting package will have missing * members. * * @see PackageFactory#PackageFactory */ @VisibleForTesting // used by PackageFactoryApparatus public Package.LegacyBuilder evaluateBuildFile( Package externalPkg, PackageIdentifier packageId, BuildFileAST buildFileAST, Path buildFilePath, Globber globber, Iterable pastEvents, RuleVisibility defaultVisibility, boolean containsError, MakeEnvironment.Builder pkgMakeEnv, Map imports, ImmutableList