/* * Copyright 2011 the original author or authors. * * 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 org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import org.gradle.api.artifacts.ModuleIdentifier; import org.gradle.api.artifacts.ModuleVersionSelector; import org.gradle.api.internal.artifacts.DefaultModuleVersionSelector; import org.gradle.api.internal.artifacts.ImmutableModuleIdentifierFactory; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.PomReader.PomDependencyData; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.data.PomDependencyMgt; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelector; import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.VersionSelectorScheme; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.ModuleExclusions; import org.gradle.api.internal.artifacts.ivyservice.resolveengine.excludes.PatternMatchers; import org.gradle.internal.component.external.descriptor.Artifact; import org.gradle.internal.component.external.descriptor.Configuration; import org.gradle.internal.component.external.descriptor.DefaultExclude; import org.gradle.internal.component.external.descriptor.MavenScope; import org.gradle.internal.component.external.descriptor.ModuleDescriptorState; import org.gradle.internal.component.external.descriptor.MutableModuleDescriptorState; import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier; import org.gradle.internal.component.external.model.IvyDependencyMetadata; import org.gradle.internal.component.external.model.MavenDependencyMetadata; import org.gradle.internal.component.model.DefaultIvyArtifactName; import org.gradle.internal.component.model.DependencyMetadata; import org.gradle.internal.component.model.Exclude; import org.gradle.internal.component.model.IvyArtifactName; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * This a straight copy of org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder, with minor changes: 1) Do not create artifact for empty classifier. (Previously did so for all non-null * classifiers) */ public class GradlePomModuleDescriptorBuilder { public static final Map<String, Configuration> MAVEN2_CONFIGURATIONS = ImmutableMap.<String, Configuration>builder() .put("default", new Configuration("default", true, true, ImmutableSet.of("runtime", "master"))) .put("master", new Configuration("master", true, true, ImmutableSet.<String>of())) .put("compile", new Configuration("compile", true, true, ImmutableSet.<String>of())) .put("provided", new Configuration("provided", true, true, ImmutableSet.<String>of())) .put("runtime", new Configuration("runtime", true, true, ImmutableSet.of("compile"))) .put("test", new Configuration("test", true, false, ImmutableSet.of("runtime"))) .put("system", new Configuration("system", true, true, ImmutableSet.<String>of())) .put("sources", new Configuration("sources", true, true, ImmutableSet.<String>of())) .put("javadoc", new Configuration("javadoc", true, true, ImmutableSet.<String>of())) .put("optional", new Configuration("optional", true, true, ImmutableSet.<String>of())).build(); private static final Map<String, MavenScope> SCOPES = ImmutableMap.<String, MavenScope>builder() .put("compile", MavenScope.Compile) .put("runtime", MavenScope.Runtime) .put("provided", MavenScope.Provided) .put("test", MavenScope.Test) .put("system", MavenScope.System) .build(); private static final Pattern TIMESTAMP_PATTERN = Pattern.compile("(.+)-\\d{8}\\.\\d{6}-\\d+"); private static final String[] WILDCARD = new String[]{"*"}; private final VersionSelectorScheme defaultVersionSelectorScheme; private final VersionSelectorScheme mavenVersionSelectorScheme; private MutableModuleDescriptorState descriptor; private List<DependencyMetadata> dependencies = Lists.newArrayList(); private final PomReader pomReader; private final ImmutableModuleIdentifierFactory moduleIdentifierFactory; private final ModuleExclusions moduleExclusions; public GradlePomModuleDescriptorBuilder(PomReader pomReader, VersionSelectorScheme gradleVersionSelectorScheme, VersionSelectorScheme mavenVersionSelectorScheme, ImmutableModuleIdentifierFactory moduleIdentifierFactory, ModuleExclusions moduleExclusions) { this.defaultVersionSelectorScheme = gradleVersionSelectorScheme; this.mavenVersionSelectorScheme = mavenVersionSelectorScheme; this.pomReader = pomReader; this.moduleIdentifierFactory = moduleIdentifierFactory; this.moduleExclusions = moduleExclusions; } public List<DependencyMetadata> getDependencies() { return dependencies; } public ModuleDescriptorState getModuleDescriptor() { return descriptor; } public void setModuleRevId(String group, String module, String version) { String effectiveVersion = version; if (version != null) { Matcher matcher = TIMESTAMP_PATTERN.matcher(version); if (matcher.matches()) { effectiveVersion = matcher.group(1) + "-SNAPSHOT"; } } String status = effectiveVersion != null && effectiveVersion.endsWith("SNAPSHOT") ? "integration" : "release"; descriptor = new MutableModuleDescriptorState(DefaultModuleComponentIdentifier.newId(group, module, effectiveVersion), status, false); } public void setDescription(String description) { descriptor.setDescription(description); } public void addDependency(PomDependencyData dep) { String scopeString = dep.getScope(); if (scopeString == null || scopeString.length() == 0) { scopeString = getDefaultScope(dep); } MavenScope scope; if (SCOPES.containsKey(scopeString)) { scope = SCOPES.get(scopeString); } else { // unknown scope, defaulting to 'compile' scope = MavenScope.Compile; } String version = determineVersion(dep); String mappedVersion = convertVersionFromMavenSyntax(version); ModuleVersionSelector selector = DefaultModuleVersionSelector.newSelector(dep.getGroupId(), dep.getArtifactId(), mappedVersion); // Some POMs depend on themselves, don't add this dependency: Ivy doesn't allow this! // Example: http://repo2.maven.org/maven2/net/jini/jsk-platform/2.1/jsk-platform-2.1.pom if (selector.getGroup().equals(descriptor.getComponentIdentifier().getGroup()) && selector.getName().equals(descriptor.getComponentIdentifier().getModule())) { return; } boolean optional = dep.isOptional(); List<Artifact> artifacts = Lists.newArrayList(); boolean hasClassifier = dep.getClassifier() != null && dep.getClassifier().length() > 0; boolean hasNonJarType = dep.getType() != null && !"jar".equals(dep.getType()); if (hasClassifier || hasNonJarType) { String type = "jar"; if (dep.getType() != null) { type = dep.getType(); } String ext = determineExtension(type); String classifier = hasClassifier ? dep.getClassifier() : getClassifierForType(type); // here we have to assume a type and ext for the artifact, so this is a limitation // compared to how m2 behave with classifiers String optionalizedScope = optional ? "optional" : scope.toString().toLowerCase(); IvyArtifactName artifactName = new DefaultIvyArtifactName(selector.getName(), type, ext, classifier); artifacts.add(new Artifact(artifactName, Collections.singleton(optionalizedScope))); } // experimentation shows the following, excluded modules are // inherited from parent POMs if either of the following is true: // the <exclusions> element is missing or the <exclusions> element // is present, but empty. List<Exclude> excludes = Lists.newArrayList(); List<ModuleIdentifier> excluded = dep.getExcludedModules(); if (excluded.isEmpty()) { excluded = getDependencyMgtExclusions(dep); } for (ModuleIdentifier excludedModule : excluded) { DefaultExclude rule = new DefaultExclude( moduleIdentifierFactory.module(excludedModule.getGroup(), excludedModule.getName()), WILDCARD, PatternMatchers.EXACT); excludes.add(rule); } dependencies.add(new MavenDependencyMetadata(scope, optional, selector, artifacts, excludes)); } private String convertVersionFromMavenSyntax(String version) { VersionSelector versionSelector = mavenVersionSelectorScheme.parseSelector(version); return defaultVersionSelectorScheme.renderSelector(versionSelector); } /** * Determines extension of dependency. * * @param type Type * @return Extension */ private String determineExtension(String type) { return JarDependencyType.isJarExtension(type) ? "jar" : type; } /** * Handles special types of dependencies. If one of the following types matches, a specific type of classifier is set. * * - test-jar (see <a href="http://maven.apache.org/guides/mini/guide-attached-tests.html">Maven documentation</a>) * - ejb-client (see <a href="http://maven.apache.org/plugins/maven-ejb-plugin/examples/ejb-client-dependency.html">Maven documentation</a>) * * @param type Type */ private String getClassifierForType(String type) { if(JarDependencyType.TEST_JAR.getName().equals(type)) { return "tests"; } else if(JarDependencyType.EJB_CLIENT.getName().equals(type)) { return "client"; } return null; } private enum JarDependencyType { TEST_JAR("test-jar"), EJB_CLIENT("ejb-client"), EJB("ejb"), BUNDLE("bundle"), MAVEN_PLUGIN("maven-plugin"), ECLIPSE_PLUGIN("eclipse-plugin"); private static final Map<String, JarDependencyType> TYPES; static { TYPES = new HashMap<String, JarDependencyType>(); for(JarDependencyType type : values()) { TYPES.put(type.name, type); } } private final String name; private JarDependencyType(String name) { this.name = name; } public String getName() { return name; } public static boolean isJarExtension(String type) { return TYPES.containsKey(type); } } /** * Determines the version of a dependency. Uses the specified version if declared for the as coordinate. If the version is not declared, try to resolve it from the dependency management section. * In case the version cannot be resolved with any of these methods, throw an exception of type {@see org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.UnresolvedDependencyVersionException}. * * @param dependency Dependency * @return Resolved dependency version */ private String determineVersion(PomDependencyData dependency) { String version = dependency.getVersion(); version = (version == null || version.length() == 0) ? getDefaultVersion(dependency) : version; if (version == null) { throw new UnresolvedDependencyVersionException(dependency.getId()); } return version; } public void addDependencyForRelocation(ModuleVersionSelector selector) { // Some POMs depend on themselves through their parent POM, don't add this dependency // since Ivy doesn't allow this! // Example: http://repo2.maven.org/maven2/com/atomikos/atomikos-util/3.6.4/atomikos-util-3.6.4.pom if (selector.getGroup().equals(descriptor.getComponentIdentifier().getGroup()) && selector.getName().equals(descriptor.getComponentIdentifier().getModule())) { return; } // TODO - this is a constant ListMultimap<String, String> confMappings = ArrayListMultimap.create(); // Map dependency on all public configurations for (Configuration m2Conf : GradlePomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS.values()) { if (m2Conf.isVisible()) { confMappings.put(m2Conf.getName(), m2Conf.getName()); } } dependencies.add(new IvyDependencyMetadata(selector, confMappings)); } private String getDefaultVersion(PomDependencyData dep) { PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep); if (pomDependencyMgt != null) { return pomDependencyMgt.getVersion(); } return null; } private String getDefaultScope(PomDependencyData dep) { PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep); String result = null; if (pomDependencyMgt != null) { result = pomDependencyMgt.getScope(); } if ((result == null) || !SCOPES.containsKey(result)) { result = "compile"; } return result; } private List<ModuleIdentifier> getDependencyMgtExclusions(PomDependencyData dep) { PomDependencyMgt pomDependencyMgt = findDependencyDefault(dep); if (pomDependencyMgt != null) { return pomDependencyMgt.getExcludedModules(); } return Collections.emptyList(); } private PomDependencyMgt findDependencyDefault(PomDependencyData dependency) { return pomReader.findDependencyDefaults(dependency.getId()); } }