blob: 831caaab6f77025520e6acb1625c4c01799ac5d2 [file] [log] [blame]
// 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.packages.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.ExtendedEventHandler;
import com.google.devtools.build.lib.packages.CachingPackageLocator;
import com.google.devtools.build.lib.packages.ConstantRuleVisibility;
import com.google.devtools.build.lib.packages.GlobCache;
import com.google.devtools.build.lib.packages.LegacyGlobber;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PackageFactory;
import com.google.devtools.build.lib.packages.PackageLoadingListener;
import com.google.devtools.build.lib.packages.PackageValidator;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.StarlarkSemanticsOptions;
import com.google.devtools.build.lib.syntax.Module;
import com.google.devtools.build.lib.syntax.ParserInput;
import com.google.devtools.build.lib.syntax.StarlarkFile;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.testutil.TestUtils;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.common.options.OptionsParser;
import java.io.IOException;
/**
* An apparatus that creates / maintains a {@link PackageFactory}.
*/
public class PackageFactoryApparatus {
private final ExtendedEventHandler eventHandler;
private final PackageFactory factory;
public PackageFactoryApparatus(ExtendedEventHandler eventHandler) {
this(eventHandler, PackageValidator.NOOP_VALIDATOR);
}
public PackageFactoryApparatus(
ExtendedEventHandler eventHandler, PackageValidator packageValidator) {
this.eventHandler = eventHandler;
RuleClassProvider ruleClassProvider = TestRuleClassProvider.getRuleClassProvider();
factory =
new PackageFactory(
ruleClassProvider,
/*environmentExtensions=*/ ImmutableList.of(),
"test",
Package.Builder.DefaultHelper.INSTANCE,
packageValidator,
PackageLoadingListener.NOOP_LISTENER);
}
/**
* Returns the package factory maintained by this apparatus.
*/
public PackageFactory factory() {
return factory;
}
private CachingPackageLocator getPackageLocator() {
// This is used only in globbing and will cause us to always traverse
// subdirectories.
return createEmptyLocator();
}
/** Parses and evaluates {@code buildFile} and returns the resulting {@link Package} instance. */
public Package createPackage(String packageName, RootedPath buildFile) throws Exception {
return createPackage(PackageIdentifier.createInMainRepo(packageName), buildFile, eventHandler);
}
public Package createPackage(String packageName, RootedPath buildFile, String starlarkOption)
throws Exception {
return createPackage(
PackageIdentifier.createInMainRepo(packageName), buildFile, eventHandler, starlarkOption);
}
/**
* Parses and evaluates {@code buildFile} with custom {@code eventHandler} and returns the
* resulting {@link Package} instance.
*/
public Package createPackage(
PackageIdentifier packageIdentifier,
RootedPath buildFile,
ExtendedEventHandler reporter,
String starlarkOption)
throws Exception {
OptionsParser parser =
OptionsParser.builder().optionsClasses(StarlarkSemanticsOptions.class).build();
parser.parse(
starlarkOption == null
? ImmutableList.<String>of()
: ImmutableList.<String>of(starlarkOption));
StarlarkSemantics semantics =
parser.getOptions(StarlarkSemanticsOptions.class).toStarlarkSemantics();
try {
Package externalPkg =
factory
.newExternalPackageBuilder(
RootedPath.toRootedPath(
buildFile.getRoot(),
buildFile
.getRootRelativePath()
.getRelative(LabelConstants.WORKSPACE_FILE_NAME)),
"TESTING",
semantics)
.build();
Package pkg =
factory.createPackageForTesting(
packageIdentifier, externalPkg, buildFile, getPackageLocator(), reporter, semantics);
return pkg;
} catch (InterruptedException e) {
throw new IllegalStateException(e);
}
}
public Package createPackage(
PackageIdentifier packageIdentifier, RootedPath buildFile, ExtendedEventHandler reporter)
throws Exception {
return createPackage(packageIdentifier, buildFile, reporter, null);
}
/** Parses the {@code buildFile} into a {@link StarlarkFile}. */
// TODO(adonovan): inline this into all callers. It has nothing to do with PackageFactory.
public StarlarkFile parse(Path buildFile) throws IOException {
byte[] bytes = FileSystemUtils.readWithKnownFileSize(buildFile, buildFile.getFileSize());
ParserInput input = ParserInput.fromLatin1(bytes, buildFile.toString());
StarlarkFile file = StarlarkFile.parse(input);
Event.replayEventsOn(eventHandler, file.errors());
return file;
}
/** Evaluates the parsed BUILD file {@code file} into a {@link Package}. */
public Pair<Package, GlobCache> evalAndReturnGlobCache(
String packageName, RootedPath filename, StarlarkFile file)
throws InterruptedException, NoSuchPackageException {
PackageIdentifier packageId = PackageIdentifier.createInMainRepo(packageName);
GlobCache globCache =
new GlobCache(
filename.asPath().getParentDirectory(),
packageId,
ImmutableSet.of(),
getPackageLocator(),
null,
TestUtils.getPool(),
-1);
LegacyGlobber globber = PackageFactory.createLegacyGlobber(globCache);
Package externalPkg =
factory
.newExternalPackageBuilder(
RootedPath.toRootedPath(
filename.getRoot(),
filename.getRootRelativePath().getParentDirectory().getRelative("WORKSPACE")),
"TESTING",
StarlarkSemantics.DEFAULT)
.build();
Package.Builder resultBuilder =
factory.evaluateBuildFile(
externalPkg.getWorkspaceName(),
packageId,
file,
filename,
globber,
ConstantRuleVisibility.PUBLIC,
StarlarkSemantics.DEFAULT,
ImmutableMap.<String, Module>of(),
ImmutableList.<Label>of(),
/*repositoryMapping=*/ ImmutableMap.of());
Package result;
try {
result = resultBuilder.build();
} catch (NoSuchPackageException e) {
// Make sure not to lose events if we fail to construct the package.
Event.replayEventsOn(eventHandler, resultBuilder.getEvents());
throw e;
}
Event.replayEventsOn(eventHandler, result.getEvents());
return Pair.of(result, globCache);
}
public Package eval(String packageName, RootedPath filename, StarlarkFile file)
throws InterruptedException, NoSuchPackageException {
return evalAndReturnGlobCache(packageName, filename, file).first;
}
/** Evaluates the {@code filename} into a {@link Package}. */
public Package eval(String packageName, RootedPath filename)
throws InterruptedException, IOException, NoSuchPackageException {
return eval(packageName, filename, parse(filename.asPath()));
}
/**
* Creates a package locator that finds no packages.
*/
public static CachingPackageLocator createEmptyLocator() {
return new CachingPackageLocator() {
@Override
public Path getBuildFileForPackage(PackageIdentifier packageName) {
return null;
}
};
}
}