diff options
9 files changed, 126 insertions, 53 deletions
diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java index e06f067d60..84bbc0b6eb 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspace.java @@ -23,6 +23,7 @@ import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.JavaIoFileSystem; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.UnixFileSystem; +import com.google.devtools.build.workspace.maven.Resolver; import com.google.devtools.common.options.OptionsParser; import java.io.File; @@ -41,14 +42,16 @@ public class GenerateWorkspace { private final EventHandler handler; private final FileSystem fileSystem; - private final com.google.devtools.build.workspace.maven.Resolver resolver; + private final Resolver resolver; private final Path outputDir; public static void main(String[] args) { OptionsParser parser = OptionsParser.newOptionsParser(GenerateWorkspaceOptions.class); parser.parseAndExitUponError(args); GenerateWorkspaceOptions options = parser.getOptions(GenerateWorkspaceOptions.class); - if (options.mavenProjects.isEmpty() && options.bazelProjects.isEmpty()) { + if (options.mavenProjects.isEmpty() + && options.bazelProjects.isEmpty() + && options.artifacts.isEmpty()) { printUsage(parser); return; } @@ -56,6 +59,7 @@ public class GenerateWorkspace { GenerateWorkspace workspaceFileGenerator = new GenerateWorkspace(options.outputDir); workspaceFileGenerator.generateFromWorkspace(options.bazelProjects); workspaceFileGenerator.generateFromPom(options.mavenProjects); + workspaceFileGenerator.generateFromArtifacts(options.artifacts); if (!workspaceFileGenerator.hasErrors()) { workspaceFileGenerator.writeResults(); } @@ -66,11 +70,11 @@ public class GenerateWorkspace { } private static void printUsage(OptionsParser parser) { - System.out.println("Usage: generate_workspace (-b PATH|-m PATH)+ [-o PATH]\n\n" + System.out.println("Usage: generate_workspace (-b PATH|-m PATH|-a coord)+ [-o PATH]\n\n" + "Generates a WORKSPACE file from the given projects and a BUILD file with a rule that " - + "contains all of the transitive dependencies. At least one bazel_project or " - + "maven_project must be specified. If output_dir is not specified, the generated files " - + "will be written to a temporary directory.\n"); + + "contains all of the transitive dependencies. At least one bazel_project, " + + "maven_project, or artifact coordinate must be specified. If output_dir is not " + + "specified, the generated files will be written to a temporary directory.\n"); System.out.println(parser.describeOptions(Collections.<String, String>emptyMap(), OptionsParser.HelpVerbosity.LONG)); } @@ -78,7 +82,7 @@ public class GenerateWorkspace { private GenerateWorkspace(String outputDir) { this.handler = new EventHandler(); this.fileSystem = getFileSystem(); - this.resolver = new com.google.devtools.build.workspace.maven.Resolver(handler); + this.resolver = new Resolver(handler); if (outputDir.isEmpty()) { this.outputDir = fileSystem.getPath(Files.createTempDir().toString()); } else { @@ -93,7 +97,7 @@ public class GenerateWorkspace { private void generateFromWorkspace(List<String> projects) { for (String project : projects) { - Resolver workspaceResolver = new Resolver(resolver, handler); + WorkspaceResolver workspaceResolver = new WorkspaceResolver(resolver, handler); Path projectPath = fileSystem.getPath(getAbsolute(project)); Package externalPackage = workspaceResolver.parse(projectPath.getRelative("WORKSPACE")); workspaceResolver.resolveTransitiveDependencies(externalPackage); @@ -106,6 +110,12 @@ public class GenerateWorkspace { } } + private void generateFromArtifacts(List<String> artifacts) { + for (String artifactCoord : artifacts) { + resolver.resolveArtifact(artifactCoord); + } + } + private String getAbsolute(String path) { return Paths.get(System.getProperty("user.dir")).resolve(path).toString(); } diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspaceOptions.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspaceOptions.java index 935df95345..7e9011bd0d 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspaceOptions.java +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/GenerateWorkspaceOptions.java @@ -36,6 +36,7 @@ public class GenerateWorkspaceOptions extends OptionsBase { abbrev = 'b', help = "Directory contains a Bazel project.", allowMultiple = true, + category = "input", defaultValue = "" ) public List<String> bazelProjects; @@ -45,15 +46,27 @@ public class GenerateWorkspaceOptions extends OptionsBase { abbrev = 'm', help = "Directory containing a Maven project.", allowMultiple = true, + category = "input", defaultValue = "" ) public List<String> mavenProjects; @Option( + name = "artifact", + abbrev = 'a', + help = "Maven artifact coordinates (e.g. groupId:artifactId:version).", + allowMultiple = true, + category = "input", + defaultValue = "" + ) + public List<String> artifacts; + + @Option( name = "output_dir", abbrev = 'o', help = "Output directory to store the WORKSPACE and BUILD files. If unspecified, a temporary" + " directory is used.", + category = "output", defaultValue = "" ) public String outputDir; diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/WorkspaceResolver.java index 2d95c4bd58..1adadb83c5 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/Resolver.java +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/WorkspaceResolver.java @@ -37,6 +37,8 @@ import com.google.devtools.build.lib.syntax.ParserInputSource; import com.google.devtools.build.lib.syntax.Type; 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 org.apache.maven.model.building.ModelSource; @@ -48,14 +50,14 @@ import java.util.List; /** * Finds the transitive dependencies of a WORKSPACE file. */ -public class Resolver { +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; - Resolver(com.google.devtools.build.workspace.maven.Resolver resolver, EventHandler handler) { + WorkspaceResolver(Resolver resolver, EventHandler handler) { this.resolver = resolver; this.handler = handler; ConfiguredRuleClassProvider.Builder ruleClassBuilder = @@ -110,8 +112,8 @@ public class Resolver { AttributeMap attributeMap = AggregatingAttributeMapper.of(workspaceRule); Rule rule; try { - rule = new Rule(attributeMap.get("artifact", Type.STRING)); - } catch (Rule.InvalidRuleException e) { + 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; } diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/BUILD b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/BUILD index 94517fd18e..be51073317 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/BUILD +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/BUILD @@ -17,6 +17,7 @@ java_library( ":rule", "//src/main/java/com/google/devtools/build/lib:events", "//src/main/java/com/google/devtools/build/lib:maven-connector", + "//third_party:aether", "//third_party:guava", "//third_party:jsr305", "//third_party:maven_model", 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 395d619e60..d00d714506 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 @@ -26,6 +26,7 @@ import org.apache.maven.model.building.ModelSource; import org.apache.maven.model.building.UrlModelSource; import org.apache.maven.model.resolution.ModelResolver; import org.apache.maven.model.resolution.UnresolvableModelException; +import org.eclipse.aether.artifact.Artifact; import java.io.IOException; import java.net.HttpURLConnection; @@ -42,26 +43,30 @@ import java.util.Set; public class DefaultModelResolver implements ModelResolver { private final Set<Repository> repositories; - private final Map<String, ModelSource> artifactToUrl; + private final Map<String, ModelSource> ruleNameToModelSource; public DefaultModelResolver() { repositories = Sets.newHashSet(); repositories.add(MavenConnector.getMavenCentral()); - artifactToUrl = Maps.newHashMap(); + ruleNameToModelSource = Maps.newHashMap(); } private DefaultModelResolver( - Set<Repository> repositories, Map<String, ModelSource> artifactToRepository) { + Set<Repository> repositories, Map<String, ModelSource> ruleNameToModelSource) { this.repositories = repositories; - this.artifactToUrl = artifactToRepository; + this.ruleNameToModelSource = ruleNameToModelSource; } + public ModelSource resolveModel(Artifact artifact) throws UnresolvableModelException { + return resolveModel(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()); + } + @Override public ModelSource resolveModel(String groupId, String artifactId, String version) throws UnresolvableModelException { - String artifact = Rule.name(groupId, artifactId); - if (artifactToUrl.containsKey(artifact)) { - return artifactToUrl.get(artifact); + String ruleName = Rule.name(groupId, artifactId); + if (ruleNameToModelSource.containsKey(ruleName)) { + return ruleNameToModelSource.get(ruleName); } for (Repository repository : repositories) { UrlModelSource modelSource = getModelSource( @@ -72,13 +77,13 @@ public class DefaultModelResolver implements ModelResolver { } // TODO(kchodorow): use Java 8 features to make this a one-liner. - List<String> urls = Lists.newArrayList(); + List<String> attemptedUrls = Lists.newArrayList(); for (Repository repository : repositories) { - urls.add(repository.getUrl()); + attemptedUrls.add(repository.getUrl()); } throw new UnresolvableModelException("Could not find any repositories that knew how to " + "resolve " + groupId + ":" + artifactId + ":" + version + " (checked " - + Joiner.on(", ").join(urls) + ")", groupId, artifactId, version); + + Joiner.on(", ").join(attemptedUrls) + ")", groupId, artifactId, version); } // TODO(kchodorow): make this work with local repositories. @@ -94,7 +99,7 @@ public class DefaultModelResolver implements ModelResolver { + "-" + version + ".pom"); if (pomFileExists(urlUrl)) { UrlModelSource urlModelSource = new UrlModelSource(urlUrl); - artifactToUrl.put(Rule.name(groupId, artifactId), urlModelSource); + ruleNameToModelSource.put(Rule.name(groupId, artifactId), urlModelSource); return urlModelSource; } } catch (MalformedURLException e) { @@ -121,22 +126,26 @@ public class DefaultModelResolver implements ModelResolver { return false; } + // For compatibility with older versions of ModelResolver which don't have this method, + // don't add @Override. public ModelSource resolveModel(Parent parent) throws UnresolvableModelException { return resolveModel(parent.getGroupId(), parent.getArtifactId(), parent.getVersion()); } - @Override + // For compatibility with older versions of ModelResolver which don't have this method, + // don't add @Override. public void addRepository(Repository repository) { repositories.add(repository); } + @Override public void addRepository(Repository repository, boolean replace) { addRepository(repository); } @Override public ModelResolver newCopy() { - return new DefaultModelResolver(repositories, artifactToUrl); + return new DefaultModelResolver(repositories, ruleNameToModelSource); } /** @@ -152,8 +161,8 @@ public class DefaultModelResolver implements ModelResolver { public boolean putModelSource(String groupId, String artifactId, ModelSource modelSource) { String key = Rule.name(groupId, artifactId); - if (!artifactToUrl.containsKey(key)) { - artifactToUrl.put(key, modelSource); + if (!ruleNameToModelSource.containsKey(key)) { + ruleNameToModelSource.put(key, modelSource); return true; } return false; diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Resolver.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Resolver.java index bbc9e52993..d379deca5e 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Resolver.java +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Resolver.java @@ -21,6 +21,7 @@ import com.google.common.io.CharStreams; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; +import org.apache.maven.model.Dependency; import org.apache.maven.model.Model; import org.apache.maven.model.Parent; import org.apache.maven.model.Repository; @@ -40,6 +41,8 @@ import org.apache.maven.model.management.DefaultPluginManagementInjector; import org.apache.maven.model.plugin.DefaultPluginConfigurationExpander; import org.apache.maven.model.profile.DefaultProfileSelector; import org.apache.maven.model.resolution.UnresolvableModelException; +import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.artifact.DefaultArtifact; import java.io.File; import java.io.IOException; @@ -59,6 +62,31 @@ import javax.annotation.Nullable; * Resolves Maven dependencies. */ public class Resolver { + + /** + * Exception thrown if an artifact coordinate could not be parsed. + */ + public static class InvalidArtifactCoordinateException extends Exception { + InvalidArtifactCoordinateException(String message) { + super(message); + } + } + + public static Artifact getArtifact(String atrifactCoords) + throws InvalidArtifactCoordinateException { + try { + return new DefaultArtifact(atrifactCoords); + } catch (IllegalArgumentException e) { + throw new InvalidArtifactCoordinateException(e.getMessage()); + } + } + + public static Artifact getArtifact(Dependency dependency) + throws InvalidArtifactCoordinateException { + return getArtifact(dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + + dependency.getVersion()); + } + private static final String COMPILE_SCOPE = "compile"; private final EventHandler handler; @@ -110,6 +138,7 @@ public class Resolver { } outputStream.println(" ],"); outputStream.println(")"); + outputStream.println(); } } @@ -148,6 +177,27 @@ public class Resolver { } /** + * Resolves an artifact as a root of a dependency graph. + */ + public void resolveArtifact(String artifactCoord) { + Artifact artifact; + ModelSource modelSource; + try { + artifact = getArtifact(artifactCoord); + modelSource = modelResolver.resolveModel(artifact); + } catch (UnresolvableModelException | InvalidArtifactCoordinateException e) { + handler.handle(Event.error(e.getMessage())); + return; + } + + addHeader(artifactCoord); + Rule rule = new Rule(artifact); + addRootDependency(rule); + deps.put(rule.name(), rule); // add the artifact rule to the workspace + resolveEffectiveModel(modelSource, Sets.<String>newHashSet(), rule); + } + + /** * Resolves all dependencies from a given "model source," which could be either a URL or a local * file. * @return the model. @@ -171,7 +221,7 @@ public class Resolver { modelResolver.addRepository(repo); } - for (org.apache.maven.model.Dependency dependency : model.getDependencies()) { + for (Dependency dependency : model.getDependencies()) { if (!dependency.getScope().equals(COMPILE_SCOPE)) { continue; } @@ -182,7 +232,7 @@ public class Resolver { continue; } try { - Rule artifactRule = new Rule(dependency); + Rule artifactRule = new Rule(getArtifact(dependency), dependency.getExclusions()); HashSet<String> localDepExclusions = new HashSet<>(exclusions); localDepExclusions.addAll(artifactRule.getExclusions()); @@ -205,7 +255,7 @@ public class Resolver { } else { rootDependencies.add(artifactRule); } - } catch (UnresolvableModelException | Rule.InvalidRuleException e) { + } catch (UnresolvableModelException | InvalidArtifactCoordinateException e) { handler.handle(Event.error("Could not resolve dependency " + dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" + dependency.getVersion() + ": " + e.getMessage())); diff --git a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Rule.java b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Rule.java index 8f14523a41..13698f364c 100644 --- a/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Rule.java +++ b/src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven/Rule.java @@ -20,11 +20,10 @@ import com.google.devtools.build.lib.bazel.repository.MavenConnector; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; -import org.apache.maven.model.Dependency; import org.apache.maven.model.Exclusion; import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -32,6 +31,7 @@ import java.util.Set; * A struct representing the fields of maven_jar to be written to the WORKSPACE file. */ public final class Rule implements Comparable<Rule> { + private final Artifact artifact; private final Set<String> parents; private final Set<String> exclusions; @@ -39,24 +39,20 @@ public final class Rule implements Comparable<Rule> { private String repository; private String sha1; - public Rule(String artifactStr) throws InvalidRuleException { - try { - this.artifact = new DefaultArtifact(artifactStr); - } catch (IllegalArgumentException e) { - throw new InvalidRuleException(e.getMessage()); - } + public Rule(Artifact artifact) { + this.artifact = artifact; this.parents = Sets.newHashSet(); this.dependencies = Sets.newTreeSet(); this.exclusions = Sets.newHashSet(); this.repository = MavenConnector.MAVEN_CENTRAL_URL; } - public Rule(Dependency dependency) throws InvalidRuleException { - this(dependency.getGroupId() + ":" + dependency.getArtifactId() + ":" - + dependency.getVersion()); + public Rule(Artifact artifact, List<Exclusion> exclusions) { + this(artifact); - for (Exclusion exclusion : dependency.getExclusions()) { - exclusions.add(String.format("%s:%s", exclusion.getGroupId(), exclusion.getArtifactId())); + for (Exclusion exclusion : exclusions) { + String coord = String.format("%s:%s", exclusion.getGroupId(), exclusion.getArtifactId()); + this.exclusions.add(coord); } } @@ -190,13 +186,4 @@ public final class Rule implements Comparable<Rule> { public int compareTo(Rule o) { return name().compareTo(o.name()); } - - /** - * Exception thrown if the rule could not be created. - */ - public static class InvalidRuleException extends Exception { - InvalidRuleException(String message) { - super(message); - } - } } diff --git a/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/BUILD b/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/BUILD index 919502fef7..edce307ff2 100644 --- a/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/BUILD +++ b/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/BUILD @@ -3,6 +3,7 @@ java_test( srcs = ["RuleTest.java"], deps = [ "//src/main/java/com/google/devtools/build/lib:events", + "//src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven", "//src/tools/generate_workspace/src/main/java/com/google/devtools/build/workspace/maven:rule", "//third_party:junit4", "//third_party:truth", diff --git a/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/RuleTest.java b/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/RuleTest.java index 18cd687862..2782aba7da 100644 --- a/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/RuleTest.java +++ b/src/tools/generate_workspace/src/test/java/com/google/devtools/build/workspace/maven/RuleTest.java @@ -38,7 +38,7 @@ public class RuleTest { @Test public void testUrl() throws Exception { - Rule rule = new Rule("foo:bar:1.2.3"); + Rule rule = new Rule(Resolver.getArtifact("foo:bar:1.2.3")); assertThat(rule.getUrl()) .isEqualTo("https://repo1.maven.org/maven2/foo/bar/1.2.3/bar-1.2.3.pom"); rule.setRepository("http://myrepo.com/foo/bar/1.2.3/bar-1.2.3.pom", handler); |