/subprojects/maven/src/main/java/org/gradle/api/publish/maven/internal/publication/DefaultMavenPublication.java

http://github.com/gradle/gradle · Java · 977 lines · 813 code · 116 blank · 48 comment · 106 complexity · 46ca784add1aeb6e92854b7bd9b2d187 MD5 · raw file

  1. /*
  2. * Copyright 2012 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.gradle.api.publish.maven.internal.publication;
  17. import com.google.common.annotations.VisibleForTesting;
  18. import com.google.common.base.Objects;
  19. import com.google.common.base.Strings;
  20. import com.google.common.collect.Lists;
  21. import com.google.common.collect.Sets;
  22. import org.gradle.api.Action;
  23. import org.gradle.api.DomainObjectCollection;
  24. import org.gradle.api.DomainObjectSet;
  25. import org.gradle.api.InvalidUserDataException;
  26. import org.gradle.api.Task;
  27. import org.gradle.api.artifacts.DependencyArtifact;
  28. import org.gradle.api.artifacts.DependencyConstraint;
  29. import org.gradle.api.artifacts.ExcludeRule;
  30. import org.gradle.api.artifacts.ModuleDependency;
  31. import org.gradle.api.artifacts.ModuleVersionIdentifier;
  32. import org.gradle.api.artifacts.ProjectDependency;
  33. import org.gradle.api.artifacts.PublishArtifact;
  34. import org.gradle.api.artifacts.PublishException;
  35. import org.gradle.api.attributes.AttributeContainer;
  36. import org.gradle.api.capabilities.Capability;
  37. import org.gradle.api.component.AdhocComponentWithVariants;
  38. import org.gradle.api.component.SoftwareComponent;
  39. import org.gradle.api.internal.CollectionCallbackActionDecorator;
  40. import org.gradle.api.internal.CompositeDomainObjectSet;
  41. import org.gradle.api.internal.artifacts.DefaultExcludeRule;
  42. import org.gradle.api.internal.artifacts.DefaultModuleIdentifier;
  43. import org.gradle.api.internal.artifacts.DefaultModuleVersionIdentifier;
  44. import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependencyConstraint;
  45. import org.gradle.api.internal.artifacts.dsl.dependencies.PlatformSupport;
  46. import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.parser.MavenVersionUtils;
  47. import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.DefaultVersionSelectorScheme;
  48. import org.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy.MavenVersionSelectorScheme;
  49. import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyPublicationResolver;
  50. import org.gradle.api.internal.attributes.ImmutableAttributes;
  51. import org.gradle.api.internal.attributes.ImmutableAttributesFactory;
  52. import org.gradle.api.internal.component.MavenPublishingAwareContext;
  53. import org.gradle.api.internal.component.SoftwareComponentInternal;
  54. import org.gradle.api.internal.component.UsageContext;
  55. import org.gradle.api.internal.file.FileCollectionFactory;
  56. import org.gradle.api.internal.project.ProjectInternal;
  57. import org.gradle.api.logging.Logger;
  58. import org.gradle.api.logging.Logging;
  59. import org.gradle.api.model.ObjectFactory;
  60. import org.gradle.api.publish.VersionMappingStrategy;
  61. import org.gradle.api.publish.internal.CompositePublicationArtifactSet;
  62. import org.gradle.api.publish.internal.DefaultPublicationArtifactSet;
  63. import org.gradle.api.publish.internal.PublicationArtifactInternal;
  64. import org.gradle.api.publish.internal.PublicationArtifactSet;
  65. import org.gradle.api.publish.internal.validation.PublicationWarningsCollector;
  66. import org.gradle.api.publish.internal.versionmapping.VersionMappingStrategyInternal;
  67. import org.gradle.api.publish.maven.MavenArtifact;
  68. import org.gradle.api.publish.maven.MavenArtifactSet;
  69. import org.gradle.api.publish.maven.MavenDependency;
  70. import org.gradle.api.publish.maven.MavenPom;
  71. import org.gradle.api.publish.maven.internal.artifact.AbstractMavenArtifact;
  72. import org.gradle.api.publish.maven.internal.artifact.DefaultMavenArtifactSet;
  73. import org.gradle.api.publish.maven.internal.artifact.DerivedMavenArtifact;
  74. import org.gradle.api.publish.maven.internal.artifact.SingleOutputTaskMavenArtifact;
  75. import org.gradle.api.publish.maven.internal.dependencies.DefaultMavenDependency;
  76. import org.gradle.api.publish.maven.internal.dependencies.DefaultMavenProjectDependency;
  77. import org.gradle.api.publish.maven.internal.dependencies.MavenDependencyInternal;
  78. import org.gradle.api.publish.maven.internal.publisher.MavenNormalizedPublication;
  79. import org.gradle.api.publish.maven.internal.publisher.MutableMavenProjectIdentity;
  80. import org.gradle.api.tasks.TaskDependency;
  81. import org.gradle.api.tasks.TaskProvider;
  82. import org.gradle.internal.Cast;
  83. import org.gradle.internal.Describables;
  84. import org.gradle.internal.DisplayName;
  85. import org.gradle.internal.reflect.Instantiator;
  86. import org.gradle.internal.typeconversion.NotationParser;
  87. import org.gradle.util.internal.CollectionUtils;
  88. import org.gradle.util.internal.GUtil;
  89. import javax.annotation.Nullable;
  90. import javax.inject.Inject;
  91. import java.io.File;
  92. import java.util.Collections;
  93. import java.util.Comparator;
  94. import java.util.HashSet;
  95. import java.util.LinkedHashSet;
  96. import java.util.List;
  97. import java.util.Map;
  98. import java.util.Set;
  99. import java.util.function.Function;
  100. import static java.util.stream.Collectors.toMap;
  101. public class DefaultMavenPublication implements MavenPublicationInternal {
  102. private final static Logger LOG = Logging.getLogger(DefaultMavenPublication.class);
  103. private static final String API_VARIANT = "api";
  104. private static final String API_ELEMENTS_VARIANT = "apiElements";
  105. /*
  106. * Maven supports wildcards in exclusion rules according to:
  107. * http://www.smartjava.org/content/maven-and-wildcard-exclusions
  108. * https://issues.apache.org/jira/browse/MNG-3832
  109. * This should be used for non-transitive dependencies
  110. */
  111. private static final Set<ExcludeRule> EXCLUDE_ALL_RULE = Collections.singleton(new DefaultExcludeRule("*", "*"));
  112. private static final Comparator<String> VARIANT_ORDERING = (left, right) -> {
  113. // API first
  114. if (API_VARIANT.equals(left) || API_ELEMENTS_VARIANT.equals(left)) {
  115. return -1;
  116. }
  117. if (API_VARIANT.equals(right) || API_ELEMENTS_VARIANT.equals(right)) {
  118. return 1;
  119. }
  120. return left.compareTo(right);
  121. };
  122. @VisibleForTesting
  123. public static final String INCOMPATIBLE_FEATURE = " contains dependencies that will produce a pom file that cannot be consumed by a Maven client.";
  124. @VisibleForTesting
  125. public static final String UNSUPPORTED_FEATURE = " contains dependencies that cannot be represented in a published pom file.";
  126. @VisibleForTesting
  127. public static final String PUBLICATION_WARNING_FOOTER = "These issues indicate information that is lost in the published 'pom' metadata file, which may be an issue if the published library is consumed by an old Gradle version or Apache Maven.\nThe 'module' metadata file, which is used by Gradle 6+ is not affected.";
  128. private final String name;
  129. private final MavenPomInternal pom;
  130. private final MutableMavenProjectIdentity projectIdentity;
  131. private final DefaultMavenArtifactSet mainArtifacts;
  132. private final PublicationArtifactSet<MavenArtifact> metadataArtifacts;
  133. private final PublicationArtifactSet<MavenArtifact> derivedArtifacts;
  134. private final PublicationArtifactSet<MavenArtifact> publishableArtifacts;
  135. private final Set<MavenDependencyInternal> runtimeDependencies = new LinkedHashSet<>();
  136. private final Set<MavenDependencyInternal> apiDependencies = new LinkedHashSet<>();
  137. private final Set<MavenDependencyInternal> optionalDependencies = new LinkedHashSet<>();
  138. private final Set<MavenDependency> runtimeDependencyConstraints = new LinkedHashSet<>();
  139. private final Set<MavenDependency> apiDependencyConstraints = new LinkedHashSet<>();
  140. private final Set<MavenDependency> importDependencyConstraints = new LinkedHashSet<>();
  141. private final ProjectDependencyPublicationResolver projectDependencyResolver;
  142. private final ImmutableAttributesFactory immutableAttributesFactory;
  143. private final VersionMappingStrategyInternal versionMappingStrategy;
  144. private final PlatformSupport platformSupport;
  145. private final Set<String> silencedVariants = new HashSet<>();
  146. private MavenArtifact pomArtifact;
  147. private SingleOutputTaskMavenArtifact moduleMetadataArtifact;
  148. private TaskProvider<? extends Task> moduleDescriptorGenerator;
  149. private SoftwareComponentInternal component;
  150. private boolean isPublishWithOriginalFileName;
  151. private boolean alias;
  152. private boolean populated;
  153. private boolean artifactsOverridden;
  154. private boolean versionMappingInUse = false;
  155. private boolean silenceAllPublicationWarnings;
  156. private boolean withBuildIdentifier = false;
  157. @Inject
  158. public DefaultMavenPublication(
  159. String name, MutableMavenProjectIdentity projectIdentity, NotationParser<Object, MavenArtifact> mavenArtifactParser, Instantiator instantiator,
  160. ObjectFactory objectFactory, ProjectDependencyPublicationResolver projectDependencyResolver, FileCollectionFactory fileCollectionFactory,
  161. ImmutableAttributesFactory immutableAttributesFactory,
  162. CollectionCallbackActionDecorator collectionCallbackActionDecorator, VersionMappingStrategyInternal versionMappingStrategy,
  163. PlatformSupport platformSupport) {
  164. this.name = name;
  165. this.projectDependencyResolver = projectDependencyResolver;
  166. this.projectIdentity = projectIdentity;
  167. this.immutableAttributesFactory = immutableAttributesFactory;
  168. this.versionMappingStrategy = versionMappingStrategy;
  169. this.platformSupport = platformSupport;
  170. this.mainArtifacts = instantiator.newInstance(DefaultMavenArtifactSet.class, name, mavenArtifactParser, fileCollectionFactory, collectionCallbackActionDecorator);
  171. this.metadataArtifacts = new DefaultPublicationArtifactSet<>(MavenArtifact.class, "metadata artifacts for " + name, fileCollectionFactory, collectionCallbackActionDecorator);
  172. derivedArtifacts = new DefaultPublicationArtifactSet<>(MavenArtifact.class, "derived artifacts for " + name, fileCollectionFactory, collectionCallbackActionDecorator);
  173. publishableArtifacts = new CompositePublicationArtifactSet<>(MavenArtifact.class, Cast.uncheckedCast(new PublicationArtifactSet<?>[]{mainArtifacts, metadataArtifacts, derivedArtifacts}));
  174. pom = instantiator.newInstance(DefaultMavenPom.class, this, instantiator, objectFactory);
  175. }
  176. @Override
  177. public String getName() {
  178. return name;
  179. }
  180. @Override
  181. public void withoutBuildIdentifier() {
  182. withBuildIdentifier = false;
  183. }
  184. @Override
  185. public void withBuildIdentifier() {
  186. withBuildIdentifier = true;
  187. }
  188. @Override
  189. public boolean isPublishBuildId() {
  190. return withBuildIdentifier;
  191. }
  192. @Override
  193. public DisplayName getDisplayName() {
  194. return Describables.withTypeAndName("Maven publication", name);
  195. }
  196. @Override
  197. public boolean isLegacy() {
  198. return false;
  199. }
  200. @Nullable
  201. @Override
  202. public SoftwareComponentInternal getComponent() {
  203. return component;
  204. }
  205. @Override
  206. public MavenPomInternal getPom() {
  207. return pom;
  208. }
  209. @Override
  210. public void setPomGenerator(TaskProvider<? extends Task> pomGenerator) {
  211. if (pomArtifact != null) {
  212. metadataArtifacts.remove(pomArtifact);
  213. }
  214. pomArtifact = new SingleOutputTaskMavenArtifact(pomGenerator, "pom", null);
  215. metadataArtifacts.add(pomArtifact);
  216. }
  217. @Override
  218. public void setModuleDescriptorGenerator(TaskProvider<? extends Task> descriptorGenerator) {
  219. moduleDescriptorGenerator = descriptorGenerator;
  220. if (moduleMetadataArtifact != null) {
  221. metadataArtifacts.remove(moduleMetadataArtifact);
  222. }
  223. moduleMetadataArtifact = null;
  224. updateModuleDescriptorArtifact();
  225. }
  226. private void updateModuleDescriptorArtifact() {
  227. if (!canPublishModuleMetadata()) {
  228. return;
  229. }
  230. if (moduleDescriptorGenerator == null) {
  231. return;
  232. }
  233. moduleMetadataArtifact = new SingleOutputTaskMavenArtifact(moduleDescriptorGenerator, "module", null);
  234. metadataArtifacts.add(moduleMetadataArtifact);
  235. moduleDescriptorGenerator = null;
  236. }
  237. @Override
  238. public void pom(Action<? super MavenPom> configure) {
  239. configure.execute(pom);
  240. }
  241. @Override
  242. public boolean isAlias() {
  243. return alias;
  244. }
  245. @Override
  246. public void setAlias(boolean alias) {
  247. this.alias = alias;
  248. }
  249. @Override
  250. public void from(SoftwareComponent component) {
  251. if (this.component != null) {
  252. throw new InvalidUserDataException(String.format("Maven publication '%s' cannot include multiple components", name));
  253. }
  254. this.component = (SoftwareComponentInternal) component;
  255. artifactsOverridden = false;
  256. updateModuleDescriptorArtifact();
  257. }
  258. private void populateFromComponent() {
  259. if (populated) {
  260. return;
  261. }
  262. populated = true;
  263. if (component == null) {
  264. return;
  265. }
  266. PublicationWarningsCollector publicationWarningsCollector = new PublicationWarningsCollector(LOG, UNSUPPORTED_FEATURE, INCOMPATIBLE_FEATURE, PUBLICATION_WARNING_FOOTER, "suppressPomMetadataWarningsFor");
  267. Set<ArtifactKey> seenArtifacts = Sets.newHashSet();
  268. Set<PublishedDependency> seenDependencies = Sets.newHashSet();
  269. Set<DependencyConstraint> seenConstraints = Sets.newHashSet();
  270. for (UsageContext usageContext : getSortedUsageContexts()) {
  271. // TODO Need a smarter way to map usage to artifact classifier
  272. for (PublishArtifact publishArtifact : usageContext.getArtifacts()) {
  273. ArtifactKey key = new ArtifactKey(publishArtifact.getFile(), publishArtifact.getClassifier(), publishArtifact.getExtension());
  274. if (!artifactsOverridden && seenArtifacts.add(key)) {
  275. artifact(publishArtifact);
  276. }
  277. }
  278. Set<ExcludeRule> globalExcludes = usageContext.getGlobalExcludes();
  279. publicationWarningsCollector.newContext(usageContext.getName());
  280. Set<MavenDependencyInternal> dependencies = dependenciesFor(usageContext);
  281. for (ModuleDependency dependency : usageContext.getDependencies()) {
  282. if (seenDependencies.add(PublishedDependency.of(dependency))) {
  283. if (isDependencyWithDefaultArtifact(dependency) && dependencyMatchesProject(dependency)) {
  284. // We skip all self referencing dependency declarations, unless they have custom artifact information
  285. continue;
  286. }
  287. if (platformSupport.isTargetingPlatform(dependency)) {
  288. if (dependency instanceof ProjectDependency) {
  289. addImportDependencyConstraint((ProjectDependency) dependency);
  290. } else {
  291. if (!versionMappingInUse && isVersionMavenIncompatible(dependency.getVersion())) {
  292. publicationWarningsCollector.addIncompatible(String.format("%s:%s:%s declared with a Maven incompatible version notation", dependency.getGroup(), dependency.getName(), dependency.getVersion()));
  293. }
  294. addImportDependencyConstraint(dependency);
  295. }
  296. } else {
  297. if (!dependency.getAttributes().isEmpty()) {
  298. publicationWarningsCollector.addUnsupported(String.format("%s:%s:%s declared with Gradle attributes", dependency.getGroup(), dependency.getName(), dependency.getVersion()));
  299. }
  300. if (dependency instanceof ProjectDependency) {
  301. addProjectDependency((ProjectDependency) dependency, globalExcludes, dependencies);
  302. } else {
  303. if (!versionMappingInUse && isVersionMavenIncompatible(dependency.getVersion())) {
  304. publicationWarningsCollector.addIncompatible(String.format("%s:%s:%s declared with a Maven incompatible version notation", dependency.getGroup(), dependency.getName(), dependency.getVersion()));
  305. }
  306. addModuleDependency(dependency, globalExcludes, dependencies);
  307. }
  308. }
  309. }
  310. }
  311. Set<MavenDependency> dependencyConstraints = dependencyConstraintsFor(usageContext);
  312. for (DependencyConstraint dependency : usageContext.getDependencyConstraints()) {
  313. if (seenConstraints.add(dependency)) {
  314. if (dependency instanceof DefaultProjectDependencyConstraint) {
  315. addDependencyConstraint((DefaultProjectDependencyConstraint) dependency, dependencyConstraints);
  316. } else if (dependency.getVersion() != null) {
  317. if (!versionMappingInUse && isVersionMavenIncompatible(dependency.getVersion())) {
  318. publicationWarningsCollector.addIncompatible(String.format("constraint %s:%s:%s declared with a Maven incompatible version notation", dependency.getGroup(), dependency.getName(), dependency.getVersion()));
  319. }
  320. addDependencyConstraint(dependency, dependencyConstraints);
  321. }
  322. }
  323. }
  324. if (!usageContext.getCapabilities().isEmpty()) {
  325. for (Capability capability : usageContext.getCapabilities()) {
  326. if (isNotDefaultCapability(capability)) {
  327. publicationWarningsCollector.addVariantUnsupported(String.format("Declares capability %s:%s:%s which cannot be mapped to Maven", capability.getGroup(), capability.getName(), capability.getVersion()));
  328. }
  329. }
  330. }
  331. }
  332. if (!silenceAllPublicationWarnings) {
  333. publicationWarningsCollector.complete(getDisplayName() + " pom metadata", silencedVariants);
  334. }
  335. }
  336. private boolean isNotDefaultCapability(Capability capability) {
  337. ModuleVersionIdentifier coordinates = getCoordinates();
  338. return !capability.getGroup().equals(coordinates.getGroup())
  339. || !capability.getName().equals(coordinates.getName())
  340. || !capability.getVersion().equals(coordinates.getVersion());
  341. }
  342. private boolean isDependencyWithDefaultArtifact(ModuleDependency dependency) {
  343. if (dependency.getArtifacts().isEmpty()) {
  344. return true;
  345. }
  346. return dependency.getArtifacts().stream().allMatch(artifact -> Strings.nullToEmpty(artifact.getClassifier()).isEmpty());
  347. }
  348. private boolean dependencyMatchesProject(ModuleDependency dependency) {
  349. return getCoordinates().getModule().equals(DefaultModuleIdentifier.newId(dependency.getGroup(), dependency.getName()));
  350. }
  351. private boolean isVersionMavenIncompatible(String version) {
  352. if (version == null) {
  353. return false;
  354. }
  355. if (DefaultVersionSelectorScheme.isSubVersion(version)) {
  356. return true;
  357. }
  358. if (DefaultVersionSelectorScheme.isLatestVersion(version)) {
  359. return !MavenVersionSelectorScheme.isSubstituableLatest(version);
  360. }
  361. return false;
  362. }
  363. private void addImportDependencyConstraint(ProjectDependency dependency) {
  364. ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier.class, dependency);
  365. importDependencyConstraints.add(new DefaultMavenDependency(identifier.getGroup(), identifier.getName(), identifier.getVersion(), "pom"));
  366. }
  367. private void addImportDependencyConstraint(ModuleDependency dependency) {
  368. importDependencyConstraints.add(new DefaultMavenDependency(dependency.getGroup(), dependency.getName(), dependency.getVersion(), "pom"));
  369. }
  370. private List<UsageContext> getSortedUsageContexts() {
  371. List<UsageContext> usageContexts = Lists.newArrayList(this.component.getUsages());
  372. if (component instanceof AdhocComponentWithVariants) {
  373. Collections.sort(Cast.uncheckedCast(usageContexts), Comparator.comparing(MavenPublishingAwareContext::getScopeMapping));
  374. } else {
  375. Collections.sort(usageContexts, (u1, u2) -> VARIANT_ORDERING.compare(u1.getName(), u2.getName()));
  376. }
  377. return usageContexts;
  378. }
  379. private Set<MavenDependencyInternal> dependenciesFor(UsageContext usage) {
  380. if (usage instanceof MavenPublishingAwareContext) {
  381. MavenPublishingAwareContext.ScopeMapping mapping = ((MavenPublishingAwareContext) usage).getScopeMapping();
  382. switch (mapping) {
  383. case compile:
  384. return apiDependencies;
  385. case runtime:
  386. return runtimeDependencies;
  387. case compile_optional:
  388. case runtime_optional:
  389. // currently single list of optionals
  390. return optionalDependencies;
  391. }
  392. }
  393. // legacy mode for internal APIs
  394. String name = usage.getName();
  395. if (API_VARIANT.equals(name) || API_ELEMENTS_VARIANT.equals(name)) {
  396. return apiDependencies;
  397. }
  398. return runtimeDependencies;
  399. }
  400. private Set<MavenDependency> dependencyConstraintsFor(UsageContext usage) {
  401. if (usage instanceof MavenPublishingAwareContext) {
  402. MavenPublishingAwareContext.ScopeMapping mapping = ((MavenPublishingAwareContext) usage).getScopeMapping();
  403. switch (mapping) {
  404. case compile:
  405. case compile_optional:
  406. return apiDependencyConstraints;
  407. case runtime:
  408. case runtime_optional:
  409. return runtimeDependencyConstraints;
  410. }
  411. }
  412. // legacy mode
  413. String name = usage.getName();
  414. if (API_VARIANT.equals(name) || API_ELEMENTS_VARIANT.equals(name)) {
  415. return apiDependencyConstraints;
  416. }
  417. return runtimeDependencyConstraints;
  418. }
  419. private void addProjectDependency(ProjectDependency dependency, Set<ExcludeRule> globalExcludes, Set<MavenDependencyInternal> dependencies) {
  420. ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier.class, dependency);
  421. DefaultMavenDependency moduleDependency = new DefaultMavenDependency(identifier.getGroup(), identifier.getName(), identifier.getVersion(), Collections.emptyList(), getExcludeRules(globalExcludes, dependency));
  422. dependencies.add(new DefaultMavenProjectDependency(moduleDependency, dependency.getDependencyProject().getPath()));
  423. }
  424. private void addModuleDependency(ModuleDependency dependency, Set<ExcludeRule> globalExcludes, Set<MavenDependencyInternal> dependencies) {
  425. dependencies.add(new DefaultMavenDependency(dependency.getGroup(), dependency.getName(), dependency.getVersion(), dependency.getArtifacts(), getExcludeRules(globalExcludes, dependency)));
  426. }
  427. private void addDependencyConstraint(DependencyConstraint dependency, Set<MavenDependency> dependencies) {
  428. dependencies.add(new DefaultMavenDependency(dependency.getGroup(), dependency.getName(), dependency.getVersion()));
  429. }
  430. private void addDependencyConstraint(DefaultProjectDependencyConstraint dependency, Set<MavenDependency> dependencies) {
  431. ProjectDependency projectDependency = dependency.getProjectDependency();
  432. ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier.class, projectDependency);
  433. DefaultMavenDependency moduleDependency = new DefaultMavenDependency(identifier.getGroup(), identifier.getName(), identifier.getVersion());
  434. dependencies.add(new DefaultMavenProjectDependency(moduleDependency, projectDependency.getDependencyProject().getPath()));
  435. }
  436. private static Set<ExcludeRule> getExcludeRules(Set<ExcludeRule> globalExcludes, ModuleDependency dependency) {
  437. return dependency.isTransitive() ? Sets.union(globalExcludes, dependency.getExcludeRules()) : EXCLUDE_ALL_RULE;
  438. }
  439. @Override
  440. public MavenArtifact artifact(Object source) {
  441. return mainArtifacts.artifact(source);
  442. }
  443. @Override
  444. public MavenArtifact artifact(Object source, Action<? super MavenArtifact> config) {
  445. return mainArtifacts.artifact(source, config);
  446. }
  447. @Override
  448. public MavenArtifactSet getArtifacts() {
  449. populateFromComponent();
  450. return mainArtifacts;
  451. }
  452. @Override
  453. public void setArtifacts(Iterable<?> sources) {
  454. artifactsOverridden = true;
  455. mainArtifacts.clear();
  456. for (Object source : sources) {
  457. artifact(source);
  458. }
  459. }
  460. @Override
  461. public String getGroupId() {
  462. return projectIdentity.getGroupId().get();
  463. }
  464. @Override
  465. public void setGroupId(String groupId) {
  466. projectIdentity.getGroupId().set(groupId);
  467. }
  468. @Override
  469. public String getArtifactId() {
  470. return projectIdentity.getArtifactId().get();
  471. }
  472. @Override
  473. public void setArtifactId(String artifactId) {
  474. projectIdentity.getArtifactId().set(artifactId);
  475. }
  476. @Override
  477. public String getVersion() {
  478. return projectIdentity.getVersion().get();
  479. }
  480. @Override
  481. public void setVersion(String version) {
  482. projectIdentity.getVersion().set(version);
  483. }
  484. @Override
  485. public void versionMapping(Action<? super VersionMappingStrategy> configureAction) {
  486. this.versionMappingInUse = true;
  487. configureAction.execute(versionMappingStrategy);
  488. }
  489. @Override
  490. public void suppressPomMetadataWarningsFor(String variantName) {
  491. this.silencedVariants.add(variantName);
  492. }
  493. @Override
  494. public void suppressAllPomMetadataWarnings() {
  495. this.silenceAllPublicationWarnings = true;
  496. }
  497. @Override
  498. @Nullable
  499. public VersionMappingStrategyInternal getVersionMappingStrategy() {
  500. return versionMappingStrategy;
  501. }
  502. @Override
  503. public boolean writeGradleMetadataMarker() {
  504. return canPublishModuleMetadata() && moduleMetadataArtifact != null && moduleMetadataArtifact.isEnabled();
  505. }
  506. @Override
  507. public PublicationArtifactSet<MavenArtifact> getPublishableArtifacts() {
  508. populateFromComponent();
  509. return publishableArtifacts;
  510. }
  511. @Override
  512. public void allPublishableArtifacts(Action<? super MavenArtifact> action) {
  513. publishableArtifacts.all(action);
  514. }
  515. @Override
  516. public void whenPublishableArtifactRemoved(Action<? super MavenArtifact> action) {
  517. publishableArtifacts.whenObjectRemoved(action);
  518. }
  519. @Override
  520. public MavenArtifact addDerivedArtifact(MavenArtifact originalArtifact, DerivedArtifact file) {
  521. MavenArtifact artifact = new DerivedMavenArtifact((AbstractMavenArtifact) originalArtifact, file);
  522. derivedArtifacts.add(artifact);
  523. return artifact;
  524. }
  525. @Override
  526. public void removeDerivedArtifact(MavenArtifact artifact) {
  527. derivedArtifacts.remove(artifact);
  528. }
  529. @Override
  530. public MutableMavenProjectIdentity getMavenProjectIdentity() {
  531. return projectIdentity;
  532. }
  533. @Override
  534. public Set<MavenDependency> getApiDependencyConstraints() {
  535. populateFromComponent();
  536. return apiDependencyConstraints;
  537. }
  538. @Override
  539. public Set<MavenDependency> getRuntimeDependencyConstraints() {
  540. populateFromComponent();
  541. return runtimeDependencyConstraints;
  542. }
  543. @Override
  544. public Set<MavenDependency> getImportDependencyConstraints() {
  545. populateFromComponent();
  546. return importDependencyConstraints;
  547. }
  548. @Override
  549. public Set<MavenDependencyInternal> getRuntimeDependencies() {
  550. populateFromComponent();
  551. return runtimeDependencies;
  552. }
  553. @Override
  554. public Set<MavenDependencyInternal> getApiDependencies() {
  555. populateFromComponent();
  556. return apiDependencies;
  557. }
  558. @Override
  559. public Set<MavenDependencyInternal> getOptionalDependencies() {
  560. populateFromComponent();
  561. return optionalDependencies;
  562. }
  563. @Override
  564. public MavenNormalizedPublication asNormalisedPublication() {
  565. populateFromComponent();
  566. // Preserve identity of artifacts
  567. Map<MavenArtifact, MavenArtifact> normalizedArtifacts = normalizedMavenArtifacts();
  568. return new MavenNormalizedPublication(
  569. name,
  570. projectIdentity,
  571. pom.getPackaging(),
  572. normalizedArtifactFor(getPomArtifact(), normalizedArtifacts),
  573. normalizedArtifactFor(determineMainArtifact(), normalizedArtifacts),
  574. new LinkedHashSet<>(normalizedArtifacts.values())
  575. );
  576. }
  577. private MavenArtifact normalizedArtifactFor(MavenArtifact artifact, Map<MavenArtifact, MavenArtifact> normalizedArtifacts) {
  578. if (artifact == null) {
  579. return null;
  580. }
  581. MavenArtifact normalized = normalizedArtifacts.get(artifact);
  582. if (normalized != null) {
  583. return normalized;
  584. }
  585. return normalizedArtifactFor(artifact);
  586. }
  587. private Map<MavenArtifact, MavenArtifact> normalizedMavenArtifacts() {
  588. return artifactsToBePublished()
  589. .stream()
  590. .collect(toMap(
  591. Function.identity(),
  592. this::normalizedArtifactFor
  593. ));
  594. }
  595. private MavenArtifact normalizedArtifactFor(MavenArtifact artifact) {
  596. // TODO: introduce something like a NormalizedMavenArtifact to capture the required MavenArtifact
  597. // information and only that instead of having MavenArtifact references in
  598. // MavenNormalizedPublication
  599. return new SerializableMavenArtifact(artifact);
  600. }
  601. private DomainObjectSet<MavenArtifact> artifactsToBePublished() {
  602. return CompositeDomainObjectSet.create(
  603. MavenArtifact.class,
  604. Cast.uncheckedCast(
  605. new DomainObjectCollection<?>[]{mainArtifacts, metadataArtifacts, derivedArtifacts}
  606. )
  607. ).matching(element -> {
  608. if (!((PublicationArtifactInternal) element).shouldBePublished()) {
  609. return false;
  610. }
  611. if (moduleMetadataArtifact == element) {
  612. // We temporarily want to allow skipping the publication of Gradle module metadata
  613. return moduleMetadataArtifact.isEnabled();
  614. }
  615. return true;
  616. });
  617. }
  618. private MavenArtifact getPomArtifact() {
  619. if (pomArtifact == null) {
  620. throw new IllegalStateException("pomArtifact not set for publication");
  621. }
  622. return pomArtifact;
  623. }
  624. @Override
  625. public String determinePackagingFromArtifacts() {
  626. Set<MavenArtifact> unclassifiedArtifacts = getUnclassifiedArtifactsWithExtension();
  627. if (unclassifiedArtifacts.size() == 1) {
  628. return unclassifiedArtifacts.iterator().next().getExtension();
  629. }
  630. return "pom";
  631. }
  632. private MavenArtifact determineMainArtifact() {
  633. Set<MavenArtifact> unclassifiedArtifacts = getUnclassifiedArtifactsWithExtension();
  634. if (unclassifiedArtifacts.isEmpty()) {
  635. return null;
  636. }
  637. if (unclassifiedArtifacts.size() == 1) {
  638. // Pom packaging doesn't matter when we have a single unclassified artifact
  639. return unclassifiedArtifacts.iterator().next();
  640. }
  641. for (MavenArtifact unclassifiedArtifact : unclassifiedArtifacts) {
  642. // With multiple unclassified artifacts, choose the one with extension matching pom packaging
  643. String packaging = pom.getPackaging();
  644. if (unclassifiedArtifact.getExtension().equals(packaging)) {
  645. return unclassifiedArtifact;
  646. }
  647. }
  648. return null;
  649. }
  650. private Set<MavenArtifact> getUnclassifiedArtifactsWithExtension() {
  651. populateFromComponent();
  652. return CollectionUtils.filter(mainArtifacts, mavenArtifact -> hasNoClassifier(mavenArtifact) && hasExtension(mavenArtifact));
  653. }
  654. private boolean hasNoClassifier(MavenArtifact element) {
  655. return element.getClassifier() == null || element.getClassifier().length() == 0;
  656. }
  657. private boolean hasExtension(MavenArtifact element) {
  658. return element.getExtension() != null && element.getExtension().length() > 0;
  659. }
  660. @Override
  661. public ModuleVersionIdentifier getCoordinates() {
  662. return DefaultModuleVersionIdentifier.newId(getGroupId(), getArtifactId(), getVersion());
  663. }
  664. @Nullable
  665. @Override
  666. public <T> T getCoordinates(Class<T> type) {
  667. if (type.isAssignableFrom(ModuleVersionIdentifier.class)) {
  668. return type.cast(getCoordinates());
  669. }
  670. return null;
  671. }
  672. @Override
  673. public void publishWithOriginalFileName() {
  674. this.isPublishWithOriginalFileName = true;
  675. }
  676. private boolean canPublishModuleMetadata() {
  677. // Cannot yet publish module metadata without component
  678. return getComponent() != null;
  679. }
  680. @Override
  681. public PublishedFile getPublishedFile(final PublishArtifact source) {
  682. checkThatArtifactIsPublishedUnmodified(source);
  683. final String publishedUrl = getPublishedUrl(source);
  684. final String publishedName = isPublishWithOriginalFileName ? source.getFile().getName() : publishedUrl;
  685. return new PublishedFile() {
  686. @Override
  687. public String getName() {
  688. return publishedName;
  689. }
  690. @Override
  691. public String getUri() {
  692. return publishedUrl;
  693. }
  694. };
  695. }
  696. @Nullable
  697. @Override
  698. public ImmutableAttributes getAttributes() {
  699. String version = getMavenProjectIdentity().getVersion().get();
  700. String status = MavenVersionUtils.inferStatusFromVersionNumber(version);
  701. return immutableAttributesFactory.of(ProjectInternal.STATUS_ATTRIBUTE, status);
  702. }
  703. /*
  704. * When the artifacts declared in a component are modified for publishing (name/classifier/extension), then the
  705. * Maven publication no longer represents the underlying java component. Instead of
  706. * publishing incorrect metadata, we fail any attempt to publish the module metadata.
  707. *
  708. * In the long term, we will likely prevent any modification of artifacts added from a component. Instead, we will
  709. * make it easier to modify the component(s) produced by a project, allowing the
  710. * published metadata to accurately reflect the local component metadata.
  711. */
  712. private void checkThatArtifactIsPublishedUnmodified(PublishArtifact source) {
  713. populateFromComponent();
  714. for (MavenArtifact mavenArtifact : mainArtifacts) {
  715. if (source.getFile().equals(mavenArtifact.getFile())
  716. && source.getExtension().equals(mavenArtifact.getExtension())
  717. && Strings.nullToEmpty(source.getClassifier()).equals(Strings.nullToEmpty(mavenArtifact.getClassifier()))) {
  718. return;
  719. }
  720. }
  721. throw new PublishException("Cannot publish module metadata where component artifacts are modified.");
  722. }
  723. private String getPublishedUrl(PublishArtifact source) {
  724. return getArtifactFileName(source.getClassifier(), source.getExtension());
  725. }
  726. private String getArtifactFileName(String classifier, String extension) {
  727. StringBuilder artifactPath = new StringBuilder();
  728. ModuleVersionIdentifier coordinates = getCoordinates();
  729. artifactPath.append(coordinates.getName());
  730. artifactPath.append('-');
  731. artifactPath.append(coordinates.getVersion());
  732. if (GUtil.isTrue(classifier)) {
  733. artifactPath.append('-');
  734. artifactPath.append(classifier);
  735. }
  736. if (GUtil.isTrue(extension)) {
  737. artifactPath.append('.');
  738. artifactPath.append(extension);
  739. }
  740. return artifactPath.toString();
  741. }
  742. private static class ArtifactKey {
  743. final File file;
  744. final String classifier;
  745. final String extension;
  746. public ArtifactKey(File file, String classifier, String extension) {
  747. this.file = file;
  748. this.classifier = classifier;
  749. this.extension = extension;
  750. }
  751. @Override
  752. public boolean equals(Object obj) {
  753. ArtifactKey other = (ArtifactKey) obj;
  754. return file.equals(other.file) && Objects.equal(classifier, other.classifier) && Objects.equal(extension, other.extension);
  755. }
  756. @Override
  757. public int hashCode() {
  758. return file.hashCode() ^ Objects.hashCode(classifier, extension);
  759. }
  760. }
  761. /**
  762. * This is used to de-duplicate dependencies based on relevant contents.
  763. * In particular, versions are ignored.
  764. */
  765. private static class PublishedDependency {
  766. private final String group;
  767. private final String name;
  768. private final String targetConfiguration;
  769. private final AttributeContainer attributes;
  770. private final Set<DependencyArtifact> artifacts;
  771. private final Set<ExcludeRule> excludeRules;
  772. private final List<Capability> requestedCapabilities;
  773. private PublishedDependency(String group, String name, String targetConfiguration, AttributeContainer attributes, Set<DependencyArtifact> artifacts, Set<ExcludeRule> excludeRules, List<Capability> requestedCapabilities) {
  774. this.group = group;
  775. this.name = name;
  776. this.targetConfiguration = targetConfiguration;
  777. this.attributes = attributes;
  778. this.artifacts = artifacts;
  779. this.excludeRules = excludeRules;
  780. this.requestedCapabilities = requestedCapabilities;
  781. }
  782. static PublishedDependency of(ModuleDependency dep) {
  783. return new PublishedDependency(
  784. dep.getGroup(),
  785. dep.getName(),
  786. dep.getTargetConfiguration(),
  787. dep.getAttributes(),
  788. dep.getArtifacts(),
  789. dep.getExcludeRules(),
  790. dep.getRequestedCapabilities()
  791. );
  792. }
  793. @Override
  794. public boolean equals(Object o) {
  795. if (this == o) {
  796. return true;
  797. }
  798. if (o == null || getClass() != o.getClass()) {
  799. return false;
  800. }
  801. PublishedDependency that = (PublishedDependency) o;
  802. return Objects.equal(group, that.group) &&
  803. Objects.equal(name, that.name) &&
  804. Objects.equal(targetConfiguration, that.targetConfiguration) &&
  805. Objects.equal(attributes, that.attributes) &&
  806. Objects.equal(artifacts, that.artifacts) &&
  807. Objects.equal(excludeRules, that.excludeRules) &&
  808. Objects.equal(requestedCapabilities, that.requestedCapabilities);
  809. }
  810. @Override
  811. public int hashCode() {
  812. return Objects.hashCode(group, name, targetConfiguration, attributes, artifacts, excludeRules, requestedCapabilities);
  813. }
  814. }
  815. private static class SerializableMavenArtifact implements MavenArtifact, PublicationArtifactInternal {
  816. private final File file;
  817. private final String extension;
  818. private final String classifier;
  819. private final boolean shouldBePublished;
  820. public SerializableMavenArtifact(MavenArtifact artifact) {
  821. PublicationArtifactInternal artifactInternal = (PublicationArtifactInternal) artifact;
  822. this.file = artifact.getFile();
  823. this.extension = artifact.getExtension();
  824. this.classifier = artifact.getClassifier();
  825. this.shouldBePublished = artifactInternal.shouldBePublished();
  826. }
  827. @Override
  828. public String getExtension() {
  829. return extension;
  830. }
  831. @Override
  832. public void setExtension(String extension) {
  833. throw new IllegalStateException();
  834. }
  835. @Nullable
  836. @Override
  837. public String getClassifier() {
  838. return classifier;
  839. }
  840. @Override
  841. public void setClassifier(@Nullable String classifier) {
  842. throw new IllegalStateException();
  843. }
  844. @Override
  845. public File getFile() {
  846. return file;
  847. }
  848. @Override
  849. public void builtBy(Object... tasks) {
  850. throw new IllegalStateException();
  851. }
  852. @Override
  853. public TaskDependency getBuildDependencies() {
  854. throw new IllegalStateException();
  855. }
  856. @Override
  857. public boolean shouldBePublished() {
  858. return shouldBePublished;
  859. }
  860. }
  861. }