// 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.workspace;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.bazel.BazelMain;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.RepositoryName;
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.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.WorkspaceFactory;
import com.google.devtools.build.lib.runtime.BlazeModule;
import com.google.devtools.build.lib.runtime.BlazeRuntime;
import com.google.devtools.build.lib.runtime.ServerBuilder;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.syntax.ParserInputSource;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.workspace.maven.DefaultModelResolver;
import com.google.devtools.build.workspace.maven.Resolver;
import com.google.devtools.build.workspace.maven.Resolver.InvalidArtifactCoordinateException;
import com.google.devtools.build.workspace.maven.Rule;
import java.io.IOException;
import java.util.List;
import org.apache.maven.model.building.ModelSource;
import org.apache.maven.model.resolution.UnresolvableModelException;
/**
* Finds the transitive dependencies of a WORKSPACE file.
*/
public class WorkspaceResolver {
private final RuleClassProvider ruleClassProvider;
private final ImmutableList<EnvironmentExtension> environmentExtensions;
private final EventHandler handler;
private final com.google.devtools.build.workspace.maven.Resolver resolver;
WorkspaceResolver(Resolver resolver, EventHandler handler) {
this.resolver = resolver;
this.handler = handler;
ServerBuilder serverBuilder = new ServerBuilder();
ConfiguredRuleClassProvider.Builder ruleClassBuilder =
new ConfiguredRuleClassProvider.Builder();
List<BlazeModule> blazeModules = BlazeRuntime.createModules(BazelMain.BAZEL_MODULES);
for (BlazeModule blazeModule : blazeModules) {
try {
blazeModule.serverInit(null, serverBuilder);
} catch (AbruptExitException e) {
throw new RuntimeException(e);
}
blazeModule.initializeRuleClasses(ruleClassBuilder);
}
this.ruleClassProvider = ruleClassBuilder.build();
this.environmentExtensions = serverBuilder.getEnvironmentExtensions();
}
/** Converts the WORKSPACE file content into an ExternalPackage. */
public Package parse(Path workspacePath) throws InterruptedException {
Package.Builder builder = Package.newExternalPackageBuilder(
Package.Builder.DefaultHelper.INSTANCE,
workspacePath,
ruleClassProvider.getRunfilesPrefix());
try (Mutability mutability = Mutability.create("External Package %s", workspacePath)) {
new WorkspaceFactory(builder, ruleClassProvider, environmentExtensions, mutability)
.parse(ParserInputSource.create(workspacePath));
} catch (IOException | BuildFileContainsErrorsException | InterruptedException e) {
handler.handle(Event.error(Location.fromFile(workspacePath), e.getMessage()));
}
return builder.build();
}
/**
* Calculates transitive dependencies of the given //external package.
*/
public void resolveTransitiveDependencies(Package externalPackage) {
Location location = Location.fromFile(externalPackage.getFilename());
for (Target target : externalPackage.getTargets().values()) {
// Targets are //external:foo.
if (target.getTargetKind().startsWith("maven_jar ")) {
RepositoryName repositoryName;
try {
repositoryName = RepositoryName.create("@" + target.getName());
} catch (LabelSyntaxException e) {
handler.handle(Event.error(location, "Invalid repository name for " + target + ": "
+ e.getMessage()));
return;
}
com.google.devtools.build.lib.packages.Rule workspaceRule =
externalPackage.getRule(repositoryName.strippedName());
DefaultModelResolver modelResolver = resolver.getModelResolver();
AttributeMap attributeMap = AggregatingAttributeMapper.of(workspaceRule);
Rule rule;
try {
rule = new Rule(Resolver.getArtifact(attributeMap.get("artifact", Type.STRING)));
} catch (InvalidArtifactCoordinateException e) {
handler.handle(Event.error(location, "Couldn't get attribute: " + e.getMessage()));
return;
}
if (attributeMap.isAttributeValueExplicitlySpecified("repository")) {
modelResolver.addUserRepository(attributeMap.get("repository", Type.STRING));
rule.setRepository(attributeMap.get("repository", Type.STRING), handler);
}
if (attributeMap.isAttributeValueExplicitlySpecified("sha1")) {
rule.setSha1(attributeMap.get("sha1", Type.STRING));
} else {
rule.setSha1(resolver.downloadSha1(rule));
}
ModelSource modelSource;
try {
modelSource = modelResolver.resolveModel(
rule.groupId(), rule.artifactId(), rule.version());
} catch (UnresolvableModelException e) {
handler.handle(Event.error(
"Could not resolve model for " + target + ": " + e.getMessage()));
continue;
}
resolver.addArtifact(rule, modelSource.getLocation());
resolver.resolveEffectiveModel(modelSource, Sets.<String>newHashSet(), rule);
} else if (!target.getTargetKind().startsWith("bind")
&& !target.getTargetKind().startsWith("source ")) {
handler.handle(Event.warn(location, "Cannot fetch transitive dependencies for " + target
+ " yet, skipping"));
}
}
}
}