Add a maven_server rule
This will also be used for authentication, but that has not been implemented
yet.
--
MOS_MIGRATED_REVID=103194964
diff --git a/src/main/java/BUILD b/src/main/java/BUILD
index 5b2be77..9c7214a 100644
--- a/src/main/java/BUILD
+++ b/src/main/java/BUILD
@@ -427,6 +427,7 @@
"//third_party:apache_commons_logging",
"//third_party:apache_httpclient",
"//third_party:apache_httpcore",
+ "//third_party:maven",
"//third_party:maven_model",
"//third_party:plexus_interpolation",
"//third_party:plexus_utils",
@@ -470,7 +471,9 @@
"//third_party:joda_time",
"//third_party:jsr305",
"//third_party:jsr330_inject",
+ "//third_party:maven",
"//third_party:maven_model",
+ "//third_party:plexus_component_annotations",
"//third_party:protobuf",
"//third_party:slf4j",
],
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
index 7e90a11..1c6347f 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/BazelRepositoryModule.java
@@ -37,6 +37,7 @@
import com.google.devtools.build.lib.bazel.repository.JarFunction;
import com.google.devtools.build.lib.bazel.repository.LocalRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.MavenJarFunction;
+import com.google.devtools.build.lib.bazel.repository.MavenServerFunction;
import com.google.devtools.build.lib.bazel.repository.NewGitRepositoryFunction;
import com.google.devtools.build.lib.bazel.repository.NewHttpArchiveFunction;
import com.google.devtools.build.lib.bazel.repository.NewLocalRepositoryFunction;
@@ -220,6 +221,7 @@
builder.put(ZipFunction.NAME, new ZipFunction());
builder.put(TarGzFunction.NAME, new TarGzFunction());
builder.put(FileFunction.NAME, new FileFunction());
+ builder.put(MavenServerFunction.NAME, new MavenServerFunction(directories));
return builder.build();
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
index f149235..497daeb 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenJarFunction.java
@@ -18,18 +18,19 @@
import com.google.common.base.Ascii;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
+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.Rule;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.skyframe.FileValue;
import com.google.devtools.build.lib.skyframe.RepositoryValue;
+import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
import com.google.devtools.build.skyframe.SkyFunctionName;
@@ -46,7 +47,6 @@
import org.eclipse.aether.resolution.ArtifactResult;
import java.io.IOException;
-import java.util.List;
import javax.annotation.Nullable;
@@ -55,6 +55,8 @@
*/
public class MavenJarFunction extends HttpArchiveFunction {
+ private static final String DEFAULT_SERVER = "default";
+
@Override
public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException {
RepositoryName repositoryName = (RepositoryName) skyKey.argument();
@@ -62,16 +64,43 @@
if (rule == null) {
return null;
}
+
+ String url;
AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
- MavenDownloader downloader = createMavenDownloader(mapper);
+ boolean hasRepository = mapper.has("repository", Type.STRING)
+ && !mapper.get("repository", Type.STRING).isEmpty();
+ boolean hasServer = mapper.has("server", Type.STRING)
+ && !mapper.get("server", Type.STRING).isEmpty();
+ if (hasRepository && hasServer) {
+ throw new RepositoryFunctionException(new EvalException(
+ Location.fromFile(getWorkspace().getRelative("WORKSPACE")), rule + " specifies both "
+ + "'repository' and 'server', which are mutually exclusive options"),
+ Transience.PERSISTENT);
+ } else if (hasRepository) {
+ url = mapper.get("repository", Type.STRING);
+ } else {
+ String serverName = DEFAULT_SERVER;
+ if (mapper.has("server", Type.STRING) && !mapper.get("server", Type.STRING).isEmpty()) {
+ serverName = mapper.get("server", Type.STRING);
+ }
+
+ MavenServerValue mavenServerValue = (MavenServerValue) env.getValue(
+ MavenServerValue.key(serverName));
+ if (mavenServerValue == null) {
+ return null;
+ }
+ url = mavenServerValue.getUrl();
+ }
+
+ MavenDownloader downloader = createMavenDownloader(mapper, url);
return createOutputTree(downloader, env);
}
@VisibleForTesting
- MavenDownloader createMavenDownloader(AttributeMap mapper) {
+ MavenDownloader createMavenDownloader(AttributeMap mapper, String url) {
String name = mapper.getName();
Path outputDirectory = getExternalRepositoryDirectory().getRelative(name);
- return new MavenDownloader(name, mapper, outputDirectory);
+ return new MavenDownloader(name, mapper, outputDirectory, url);
}
SkyValue createOutputTree(MavenDownloader downloader, Environment env)
@@ -129,10 +158,10 @@
private final Path outputDirectory;
@Nullable
private final String sha1;
- // TODO(kchodorow): change this to a single repository on 9/15.
- private final List<RemoteRepository> repositories;
+ private final String url;
- public MavenDownloader(String name, AttributeMap mapper, Path outputDirectory) {
+ public MavenDownloader(
+ String name, AttributeMap mapper, Path outputDirectory, String url) {
this.name = name;
this.outputDirectory = outputDirectory;
@@ -144,22 +173,7 @@
+ mapper.get("version", Type.STRING);
}
this.sha1 = (mapper.has("sha1", Type.STRING)) ? mapper.get("sha1", Type.STRING) : null;
-
- if (mapper.has("repository", Type.STRING)
- && !mapper.get("repository", Type.STRING).isEmpty()) {
- this.repositories = ImmutableList.of(new RemoteRepository.Builder(
- "user-defined repository", "default", mapper.get("repository", Type.STRING)).build());
- } else if (mapper.has("repositories", Type.STRING_LIST)
- && !mapper.get("repositories", Type.STRING_LIST).isEmpty()) {
- // TODO(kchodorow): remove after 9/15, uses deprecated list of repositories attribute.
- this.repositories = Lists.newArrayList();
- for (String repositoryUrl : mapper.get("repositories", Type.STRING_LIST)) {
- this.repositories.add(new RemoteRepository.Builder(
- "user-defined repository " + repositories.size(), "default", repositoryUrl).build());
- }
- } else {
- this.repositories = ImmutableList.of(MavenConnector.getMavenCentralRemote());
- }
+ this.url = url;
}
/**
@@ -184,6 +198,7 @@
RepositorySystem system = connector.newRepositorySystem();
RepositorySystemSession session = connector.newRepositorySystemSession(system);
+ RemoteRepository repository = new RemoteRepository.Builder(name, "default", url).build();
ArtifactRequest artifactRequest = new ArtifactRequest();
Artifact artifact;
try {
@@ -192,7 +207,7 @@
throw new IOException(e.getMessage());
}
artifactRequest.setArtifact(artifact);
- artifactRequest.setRepositories(repositories);
+ artifactRequest.setRepositories(ImmutableList.of(repository));
try {
ArtifactResult artifactResult = system.resolveArtifact(session, artifactRequest);
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerFunction.java
new file mode 100644
index 0000000..4485021
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerFunction.java
@@ -0,0 +1,125 @@
+// 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.bazel.repository;
+
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.bazel.rules.workspace.MavenServerRule;
+import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
+import com.google.devtools.build.lib.packages.ExternalPackage;
+import com.google.devtools.build.lib.packages.Rule;
+import com.google.devtools.build.lib.packages.Type;
+import com.google.devtools.build.lib.skyframe.FileValue;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.lib.vfs.RootedPath;
+import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
+import com.google.devtools.build.skyframe.SkyFunctionName;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+
+import org.apache.maven.settings.Server;
+import org.apache.maven.settings.Settings;
+import org.apache.maven.settings.building.DefaultSettingsBuilder;
+import org.apache.maven.settings.building.DefaultSettingsBuilderFactory;
+import org.apache.maven.settings.building.DefaultSettingsBuildingRequest;
+import org.apache.maven.settings.building.SettingsBuildingException;
+import org.apache.maven.settings.building.SettingsBuildingResult;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.annotation.Nullable;
+
+/**
+ * Implementation of maven_repository.
+ */
+public class MavenServerFunction extends RepositoryFunction {
+ public static final SkyFunctionName NAME = SkyFunctionName.create("MAVEN_SERVER_FUNCTION");
+
+ public MavenServerFunction(BlazeDirectories directories) {
+ setDirectories(directories);
+ }
+
+ @Nullable
+ @Override
+ public SkyValue compute(SkyKey skyKey, Environment env) throws RepositoryFunctionException {
+ String repository = skyKey.argument().toString();
+ ExternalPackage externalPackage = RepositoryFunction.getExternalPackage(env);
+ Rule repositoryRule = externalPackage.getRule(repository);
+
+ boolean foundRepoRule = repositoryRule != null
+ && repositoryRule.getRuleClass().equals(MavenServerRule.NAME);
+ if (!foundRepoRule) {
+ if (repository.equals(MavenServerValue.DEFAULT_ID)) {
+ // The default repository is being used and the WORKSPACE is not overriding the default.
+ return new MavenServerValue();
+ }
+ throw new RepositoryFunctionException(
+ new IOException("Could not find maven repository " + repository), Transience.TRANSIENT);
+ }
+
+ AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(repositoryRule);
+ String serverName = repositoryRule.getName();
+ String url = mapper.get("url", Type.STRING);
+ if (!mapper.has("settings_file", Type.STRING)
+ || mapper.get("settings_file", Type.STRING).isEmpty()) {
+ return new MavenServerValue(serverName, url, new Server());
+ }
+ PathFragment settingsFilePath = new PathFragment(mapper.get("settings_file", Type.STRING));
+ RootedPath settingsPath = RootedPath.toRootedPath(
+ getWorkspace().getRelative(settingsFilePath), PathFragment.EMPTY_FRAGMENT);
+ FileValue settingsFile = (FileValue) env.getValue(FileValue.key(settingsPath));
+ if (settingsFile == null) {
+ return null;
+ }
+
+ if (!settingsFile.exists()) {
+ throw new RepositoryFunctionException(
+ new IOException("Could not find settings file " + settingsPath), Transience.TRANSIENT);
+ }
+
+ DefaultSettingsBuildingRequest request = new DefaultSettingsBuildingRequest();
+ request.setUserSettingsFile(new File(settingsFile.realRootedPath().asPath().toString()));
+ DefaultSettingsBuilder builder = (new DefaultSettingsBuilderFactory()).newInstance();
+ SettingsBuildingResult result;
+ try {
+ result = builder.build(request);
+ } catch (SettingsBuildingException e) {
+ throw new RepositoryFunctionException(
+ new IOException("Error parsing settings file " + settingsFile + ": " + e.getMessage()),
+ Transience.TRANSIENT);
+ }
+ if (!result.getProblems().isEmpty()) {
+ throw new RepositoryFunctionException(
+ new IOException("Errors interpreting settings file: "
+ + Arrays.toString(result.getProblems().toArray())), Transience.PERSISTENT);
+ }
+ Settings settings = result.getEffectiveSettings();
+ Server server = settings.getServer(mapper.getName());
+ server = server == null ? new Server() : server;
+ return new MavenServerValue(serverName, url, server);
+ }
+
+ @Override
+ public SkyFunctionName getSkyFunctionName() {
+ return NAME;
+ }
+
+ @Override
+ public Class<? extends RuleDefinition> getRuleDefinition() {
+ return MavenServerRule.class;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerValue.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerValue.java
new file mode 100644
index 0000000..78f2e26
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/MavenServerValue.java
@@ -0,0 +1,78 @@
+// 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.bazel.repository;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.devtools.build.skyframe.SkyValue;
+import org.apache.maven.settings.Server;
+
+/**
+ * A Maven repository's identifier.
+ */
+public class MavenServerValue implements SkyValue {
+ public static final String DEFAULT_ID = "default";
+
+ private final String id;
+ private final String url;
+ private final Server server;
+
+ public static SkyKey key(String serverName) {
+ Preconditions.checkNotNull(serverName);
+ return new SkyKey(MavenServerFunction.NAME, serverName);
+ }
+
+ public MavenServerValue() {
+ id = DEFAULT_ID;
+ url = MavenConnector.getMavenCentralRemote().getUrl();
+ server = new Server();
+ }
+
+ public MavenServerValue(String id, String url, Server server) {
+ Preconditions.checkNotNull(id);
+ Preconditions.checkNotNull(url);
+ Preconditions.checkNotNull(server);
+ this.id = id;
+ this.url = url;
+ this.server = server;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object == null || !(object instanceof MavenServerValue)) {
+ return false;
+ }
+
+ MavenServerValue other = (MavenServerValue) object;
+ return id.equals(other.id) && url.equals(other.url);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(id, url);
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public Server getServer() {
+ return server;
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryDelegatorFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryDelegatorFunction.java
index d4042fe5..47fc19d 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryDelegatorFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryDelegatorFunction.java
@@ -18,6 +18,7 @@
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.bazel.repository.RepositoryFunction.RepositoryFunctionException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
+import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.packages.NoSuchPackageException;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.skyframe.FileValue;
@@ -84,7 +85,9 @@
RepositoryFunction handler = handlers.get(rule.getRuleClass());
if (handler == null) {
- throw new IllegalStateException("Could not find handler for " + rule);
+ throw new RepositoryFunctionException(new EvalException(
+ Location.fromFile(directories.getWorkspace().getRelative("WORKSPACE")),
+ "Could not find handler for " + rule), Transience.PERSISTENT);
}
SkyKey key = new SkyKey(handler.getSkyFunctionName(), repositoryName);
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
index 4bf10f7..1fe07a9 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/RepositoryFunction.java
@@ -17,6 +17,7 @@
import com.google.common.base.Preconditions;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
@@ -243,16 +244,8 @@
}
}
-
- /**
- * Uses a remote repository name to fetch the corresponding Rule describing how to get it.
- * This should be called from {@link SkyFunction#compute} functions, which should return null if
- * this returns null. If {@code ruleClassName} is set, the rule found must have a matching rule
- * class name.
- */
@Nullable
- public static Rule getRule(
- RepositoryName repositoryName, @Nullable String ruleClassName, Environment env)
+ public static ExternalPackage getExternalPackage(Environment env)
throws RepositoryFunctionException {
SkyKey packageKey = PackageValue.key(ExternalPackage.PACKAGE_IDENTIFIER);
PackageValue packageValue;
@@ -268,7 +261,35 @@
if (packageValue == null) {
return null;
}
- ExternalPackage externalPackage = (ExternalPackage) packageValue.getPackage();
+ return (ExternalPackage) packageValue.getPackage();
+ }
+
+ @Nullable
+ public static Rule getRule(
+ String ruleName, @Nullable String ruleClassName, Environment env)
+ throws RepositoryFunctionException {
+ try {
+ return getRule(RepositoryName.create("@" + ruleName), ruleClassName, env);
+ } catch (LabelSyntaxException e) {
+ throw new RepositoryFunctionException(
+ new IOException("Invalid rule name " + ruleName), Transience.PERSISTENT);
+ }
+ }
+
+ /**
+ * Uses a remote repository name to fetch the corresponding Rule describing how to get it.
+ * This should be called from {@link SkyFunction#compute} functions, which should return null if
+ * this returns null. If {@code ruleClassName} is set, the rule found must have a matching rule
+ * class name.
+ */
+ @Nullable
+ public static Rule getRule(
+ RepositoryName repositoryName, @Nullable String ruleClassName, Environment env)
+ throws RepositoryFunctionException {
+ ExternalPackage externalPackage = getExternalPackage(env);
+ if (externalPackage == null) {
+ return null;
+ }
Rule rule = externalPackage.getRepositoryInfo(repositoryName);
if (rule == null) {
throw new RepositoryFunctionException(
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
index 605a15d..c8f829e 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -66,6 +66,7 @@
import com.google.devtools.build.lib.bazel.rules.workspace.HttpJarRule;
import com.google.devtools.build.lib.bazel.rules.workspace.LocalRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.MavenJarRule;
+import com.google.devtools.build.lib.bazel.rules.workspace.MavenServerRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewGitRepositoryRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewHttpArchiveRule;
import com.google.devtools.build.lib.bazel.rules.workspace.NewLocalRepositoryRule;
@@ -354,6 +355,7 @@
builder.addRuleDefinition(new HttpFileRule());
builder.addRuleDefinition(new LocalRepositoryRule());
builder.addRuleDefinition(new MavenJarRule());
+ builder.addRuleDefinition(new MavenServerRule());
builder.addRuleDefinition(new NewHttpArchiveRule());
builder.addRuleDefinition(new NewGitRepositoryRule());
builder.addRuleDefinition(new NewLocalRepositoryRule());
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenJarRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenJarRule.java
index a9a0a8e..f8f5b91 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenJarRule.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenJarRule.java
@@ -48,13 +48,17 @@
A URL for a Maven repository to fetch the jar from.
${SYNOPSIS}
- <p>Defaults to Maven Central ("central.maven.org").</p>
-
- <p><b>To be implemented: add a maven_repository rule that allows a default repository
- to be specified once.</b></p>
+ <p>Either this or <code>server</code> can be specified. Defaults to Maven Central
+ ("central.maven.org").</p>
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
.add(attr("repository", Type.STRING))
- .add(attr("repositories", Type.STRING_LIST).undocumented("deprecated"))
+ /* <!-- #BLAZE_RULE(maven_jar).attribute(server) -->
+ A maven_server to use for this artifact.
+ ${SYNOPSIS}
+
+ <p>Either this or <code>repository</code> can be specified.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("server", Type.STRING))
/* <!-- #BLAZE_RULE(maven_jar).ATTRIBUTE(sha1) -->
A SHA-1 hash of the desired jar.
${SYNOPSIS}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenServerRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenServerRule.java
new file mode 100644
index 0000000..602d705
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/workspace/MavenServerRule.java
@@ -0,0 +1,74 @@
+// 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.bazel.rules.workspace;
+
+import static com.google.devtools.build.lib.packages.Attribute.attr;
+
+import com.google.devtools.build.lib.analysis.RuleDefinition;
+import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
+import com.google.devtools.build.lib.packages.RuleClass;
+import com.google.devtools.build.lib.packages.RuleClass.Builder;
+import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
+import com.google.devtools.build.lib.packages.Type;
+
+/**
+ * Rule definition for the maven_jar rule.
+ */
+public class MavenServerRule implements RuleDefinition {
+
+ public static final String NAME = "maven_server";
+
+ @Override
+ public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
+ return builder
+ /* <!-- #BLAZE_RULE(maven_server).ATTRIBUTE(url) -->
+ A URL for accessing the server.
+ ${SYNOPSIS}
+
+ <p>For example, Maven Central (which is the default and does not need to be defined) would
+ be specified as <code>url = "http://central.maven.org/maven2/"</code>.</p>
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("url", Type.STRING))
+ /* <!-- #BLAZE_RULE(maven_server).ATTRIBUTE(settings_file) -->
+ A path to a settings.xml file.
+ ${SYNOPSIS}
+ <!-- #END_BLAZE_RULE.ATTRIBUTE --> */
+ .add(attr("settings_file", Type.STRING))
+ .setWorkspaceOnly()
+ .build();
+ }
+
+ @Override
+ public Metadata getMetadata() {
+ return RuleDefinition.Metadata.builder()
+ .name(MavenServerRule.NAME)
+ .type(RuleClassType.WORKSPACE)
+ .ancestors(WorkspaceBaseRule.class)
+ .factoryClass(WorkspaceConfiguredTargetFactory.class)
+ .build();
+ }
+}
+/*<!-- #BLAZE_RULE (NAME = maven_server, TYPE = OTHER, FAMILY = Workspace)[GENERIC_RULE] -->
+
+${ATTRIBUTE_SIGNATURE}
+
+<p>How to access a Maven repository.</p>
+
+${ATTRIBUTE_DEFINITION}
+
+<p>This is a combination of a <repository> definition from a pom.xml file and a
+<server< definition from a settings.xml file.</p>
+
+<!-- #END_BLAZE_RULE -->*/
diff --git a/src/test/shell/bazel/generate_workspace_test.sh b/src/test/shell/bazel/generate_workspace_test.sh
index ddfc41a..07dda35 100755
--- a/src/test/shell/bazel/generate_workspace_test.sh
+++ b/src/test/shell/bazel/generate_workspace_test.sh
@@ -18,25 +18,24 @@
#
# Load test environment
-source $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-setup.sh \
+src_dir=$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)
+source $src_dir/test-setup.sh \
|| { echo "test-setup.sh not found!" >&2; exit 1; }
+source $src_dir/remote_helpers.sh \
+ || { echo "remote_helpers.sh not found!" >&2; exit 1; }
export JAVA_RUNFILES=$TEST_SRCDIR
function set_up() {
# Set up custom repository directory.
m2=$TEST_TMPDIR/my-m2
+ rm -rf $m2
mkdir -p $m2
- cd $m2
- m2_port=$(pick_random_unused_tcp_port) || exit 1
- python -m SimpleHTTPServer $m2_port &
- m2_pid=$!
- wait_for_server_startup
- cd -
+ startup_server $m2
}
function tear_down() {
- kill $m2_pid
+ shutdown_server
rm -rf $m2
}
@@ -74,18 +73,6 @@
${bazel_javabase}/bin/jar cf $pkg_dir/$artifactId-$version.jar $TEST_TMPDIR/$groupId.class
}
-# Waits for the SimpleHTTPServer to actually start up before the test is run.
-# Otherwise the entire test can run before the server starts listening for
-# connections, which causes flakes.
-function wait_for_server_startup() {
- touch some-file
- while ! curl localhost:$m2_port/some-file; do
- echo "waiting for server, exit code: $?"
- done
- echo "done waiting for server, exit code: $?"
- rm some-file
-}
-
function get_workspace_file() {
cat $TEST_log | tail -n 2 | head -n 1
}
@@ -109,7 +96,7 @@
<repository>
<id>my-repo1</id>
<name>a custom repo</name>
- <url>http://localhost:$m2_port/</url>
+ <url>http://localhost:$fileserver_port/</url>
</repository>
</repositories>
@@ -130,7 +117,7 @@
cat $(cat $TEST_log | tail -n 1) > build
assert_contains "artifact = \"blorp:glorp:1.2.3\"," ws
- assert_contains "repository = \"http://localhost:$m2_port/\"," ws
+ assert_contains "repository = \"http://localhost:$fileserver_port/\"," ws
assert_contains "\"@blorp/glorp//jar\"," build
}
diff --git a/src/test/shell/bazel/maven_test.sh b/src/test/shell/bazel/maven_test.sh
index 3bdb599..b7b7def 100755
--- a/src/test/shell/bazel/maven_test.sh
+++ b/src/test/shell/bazel/maven_test.sh
@@ -18,13 +18,14 @@
#
# Load test environment
-src=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
+src=$(cd "$(dirname ${BASH_SOURCE[0]})" && pwd)
source $src/test-setup.sh \
|| { echo "test-setup.sh not found!" >&2; exit 1; }
source $src/remote_helpers.sh \
|| { echo "remote_helpers.sh not found!" >&2; exit 1; }
function set_up() {
+ startup_server $PWD
mkdir -p zoo
cat > zoo/BUILD <<EOF
java_binary(
@@ -46,6 +47,10 @@
EOF
}
+function tear_down() {
+ shutdown_server
+}
+
function test_maven_jar() {
serve_jar
@@ -68,21 +73,18 @@
# Same as test_maven_jar, except omit sha1 implying "we don't care".
function test_maven_jar_no_sha1() {
- serve_jar
+ serve_artifact com.example.carnivore carnivore 1.23
cat > WORKSPACE <<EOF
maven_jar(
name = 'endangered',
artifact = "com.example.carnivore:carnivore:1.23",
- repository = 'http://localhost:$nc_port/',
+ repository = 'http://localhost:$fileserver_port/',
)
bind(name = 'mongoose', actual = '@endangered//jar')
EOF
- bazel fetch //zoo:ball-pit || fail "Fetch failed"
bazel run //zoo:ball-pit >& $TEST_log || fail "Expected run to succeed"
- kill_nc
- assert_contains "GET /com/example/carnivore/carnivore/1.23/carnivore-1.23.jar" $nc_log
expect_log "Tra-la!"
}
@@ -104,7 +106,8 @@
bind(name = 'mongoose', actual = '@endangered//jar')
EOF
- bazel fetch //zoo:ball-pit >& $TEST_log && echo "Expected fetch to fail"
+ bazel clean --expunge
+ bazel build //zoo:ball-pit >& $TEST_log && echo "Expected build to fail"
kill_nc
expect_log "Failed to fetch Maven dependency: Could not find artifact"
}
@@ -126,3 +129,93 @@
kill_nc
expect_log "has SHA-1 of $sha1, does not match expected SHA-1 ($sha256)"
}
+
+function test_default_repository() {
+ serve_artifact thing amabop 1.9
+ cat > WORKSPACE <<EOF
+maven_server(
+ name = "default",
+ url = "http://localhost:$fileserver_port/",
+)
+
+maven_jar(
+ name = "thing-a-ma-bop",
+ artifact = "thing:amabop:1.9",
+)
+EOF
+
+ bazel build @thing-a-ma-bop//jar &> $TEST_log || fail "Building thing failed"
+ expect_log "Target @thing-a-ma-bop//jar:jar up-to-date"
+}
+
+function test_settings() {
+ serve_artifact thing amabop 1.9
+ cat > WORKSPACE <<EOF
+maven_server(
+ name = "x",
+ url = "http://localhost:$fileserver_port/",
+ settings_file = "settings.xml",
+)
+maven_jar(
+ name = "thing-a-ma-bop",
+ artifact = "thing:amabop:1.9",
+ server = "x",
+)
+EOF
+
+ cat > settings.xml <<EOF
+<settings>
+ <servers>
+ <server>
+ <id>default</id>
+ </server>
+ </servers>
+</settings>
+EOF
+
+ bazel build @thing-a-ma-bop//jar &> $TEST_log \
+ || fail "Building thing failed"
+ expect_log "Target @thing-a-ma-bop//jar:jar up-to-date"
+
+ # Create an invalid settings.xml (by using a tag that isn't allowed in
+ # settings).
+ cat > settings.xml <<EOF
+<settings>
+ <repositories>
+ <repository>
+ <id>default</id>
+ </repository>
+ </repositories>
+</settings>
+EOF
+ bazel clean --expunge
+ bazel build @thing-a-ma-bop//jar &> $TEST_log \
+ && fail "Building thing succeeded"
+ expect_log "Unrecognised tag: 'repositories'"
+}
+
+function test_maven_server_dep() {
+ cat > WORKSPACE <<EOF
+maven_server(
+ name = "x",
+ url = "http://localhost:12345/",
+)
+EOF
+
+ cat > BUILD <<EOF
+sh_binary(
+ name = "y",
+ srcs = ["y.sh"],
+ deps = ["@x//:bar"],
+)
+EOF
+
+ touch y.sh
+ chmod +x y.sh
+
+ bazel build //:y &> $TEST_log && fail "Building thing failed"
+ expect_log "no such package '@x//'"
+}
+
+
+run_suite "maven tests"
diff --git a/src/test/shell/bazel/remote_helpers.sh b/src/test/shell/bazel/remote_helpers.sh
index 653a3f6..e1a7a55 100755
--- a/src/test/shell/bazel/remote_helpers.sh
+++ b/src/test/shell/bazel/remote_helpers.sh
@@ -49,11 +49,14 @@
# Creates a jar carnivore.Mongoose and serves it using serve_file.
function serve_jar() {
- pkg_dir=$TEST_TMPDIR/carnivore
- if [ -e "$pkg_dir" ]; then
- rm -fr $pkg_dir
- fi
+ make_test_jar
+ serve_file $test_jar
+ cd ${WORKSPACE_DIR}
+}
+function make_test_jar() {
+ pkg_dir=$TEST_TMPDIR/carnivore
+ rm -fr $pkg_dir
mkdir $pkg_dir
cat > $pkg_dir/Mongoose.java <<EOF
package carnivore;
@@ -67,12 +70,10 @@
test_jar=$TEST_TMPDIR/libcarnivore.jar
cd ${TEST_TMPDIR}
${bazel_javabase}/bin/jar cf $test_jar carnivore/Mongoose.class
-
sha256=$(sha256sum $test_jar | cut -f 1 -d ' ')
# OS X doesn't have sha1sum, so use openssl.
sha1=$(openssl sha1 $test_jar | cut -f 2 -d ' ')
- serve_file $test_jar
- cd ${WORKSPACE_DIR}
+ cd -
}
# Serves a redirection from localhost:$redirect_port to $1. Sets the following variables:
@@ -93,6 +94,45 @@
redirect_pid=$!
}
+# Waits for the SimpleHTTPServer to actually start up before the test is run.
+# Otherwise the entire test can run before the server starts listening for
+# connections, which causes flakes.
+function wait_for_server_startup() {
+ touch some-file
+ while ! curl localhost:$fileserver_port/some-file; do
+ echo "waiting for server, exit code: $?"
+ sleep 1
+ done
+ echo "done waiting for server, exit code: $?"
+ rm some-file
+}
+
+
+function serve_artifact() {
+ local group_id=$1
+ local artifact_id=$2
+ local version=$3
+ make_test_jar
+ maven_path=$fileserver_root/$(echo $group_id | sed 's/\./\//g')/$artifact_id/$version
+ mkdir -p $maven_path
+ openssl sha1 $test_jar > $maven_path/$artifact_id-$version.jar.sha1
+ mv $test_jar $maven_path/$artifact_id-$version.jar
+}
+
+function startup_server() {
+ fileserver_root=$1
+ cd $fileserver_root
+ fileserver_port=$(pick_random_unused_tcp_port) || exit 1
+ python -m SimpleHTTPServer $fileserver_port &
+ fileserver_pid=$!
+ wait_for_server_startup
+ cd -
+}
+
+function shutdown_server() {
+ kill $fileserver_pid
+}
+
function kill_nc() {
# Try to kill nc, otherwise the test will time out if Bazel has a bug and
# didn't make a request to it.
diff --git a/src/test/shell/bazel/test-setup.sh b/src/test/shell/bazel/test-setup.sh
index 237106d..0ff08f9 100755
--- a/src/test/shell/bazel/test-setup.sh
+++ b/src/test/shell/bazel/test-setup.sh
@@ -432,4 +432,3 @@
setup_bazelrc
setup_clean_workspace
-bazel fetch //tools/jdk/... >& $TEST_log
diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/DefaultModelResolver.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/DefaultModelResolver.java
index c903c02..6934f91 100644
--- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/DefaultModelResolver.java
+++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/DefaultModelResolver.java
@@ -121,7 +121,6 @@
return false;
}
- @Override
public ModelSource resolveModel(Parent parent) throws UnresolvableModelException {
return resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion());
}
@@ -131,7 +130,6 @@
repositories.add(repository);
}
- @Override
public void addRepository(Repository repository, boolean replace) {
addRepository(repository);
}
diff --git a/third_party/BUILD b/third_party/BUILD
index fbf5f56..838dd1d 100644
--- a/third_party/BUILD
+++ b/third_party/BUILD
@@ -258,6 +258,11 @@
)
java_import(
+ name = "maven",
+ jars = glob(["maven/*.jar"]),
+)
+
+java_import(
name = "maven_model",
jars = [
"maven_model/maven-aether-provider-3.2.3.jar",
diff --git a/third_party/README.md b/third_party/README.md
index bf4ccdb..4e529af 100644
--- a/third_party/README.md
+++ b/third_party/README.md
@@ -202,7 +202,13 @@
* License: MIT license
-[maven_model](http://maven.apache.org/ref/3.2.5/maven-model/)
+[maven](http://mvnrepository.com/artifact/org.apache.maven)
+-------------
+* Version: 3.3.3
+* License: Apache License 2.0
+
+
+[maven_model](http://maven.apache.org/ref/3.2.3/maven-model/)
-------------
* Version: 3.2.3
* License: Apache License 2.0