diff --git a/target-platform-configuration/src/main/java/org/eclipse/tycho/target/MirrorTargetPlatformMojo.java b/target-platform-configuration/src/main/java/org/eclipse/tycho/target/MirrorTargetPlatformMojo.java new file mode 100644 index 0000000000..bc7c9c61e3 --- /dev/null +++ b/target-platform-configuration/src/main/java/org/eclipse/tycho/target/MirrorTargetPlatformMojo.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2023 Christoph Läubrich and others. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.tycho.target; + +import java.io.File; +import java.util.List; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Component; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.plugins.annotations.ResolutionScope; +import org.apache.maven.project.MavenProject; +import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; +import org.eclipse.tycho.ReactorProject; +import org.eclipse.tycho.TargetPlatform; +import org.eclipse.tycho.TargetPlatformService; +import org.eclipse.tycho.core.osgitools.DefaultReactorProject; +import org.eclipse.tycho.p2.repository.ListCompositeMetadataRepository; +import org.eclipse.tycho.p2.repository.PublishingRepository; +import org.eclipse.tycho.p2.tools.FacadeException; +import org.eclipse.tycho.p2.tools.mirroring.facade.MirrorApplicationService; +import org.eclipse.tycho.p2maven.ListCompositeArtifactRepository; +import org.eclipse.tycho.repository.registry.facade.ReactorRepositoryManager; + +/** + * Supports mirroring the computed target platform of the current project, this behaves similar to + * what PDE offers with its export deployable feature / plug-in and assembles an update site that + * contains everything this particular project depends on. + */ +@Mojo(name = "mirror-target-platform", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, defaultPhase = LifecyclePhase.PREPARE_PACKAGE) +public class MirrorTargetPlatformMojo extends AbstractMojo { + + @Parameter(property = "project", readonly = true) + private MavenProject project; + + @Parameter(defaultValue = "${project.build.directory}/target-platform-repository") + private File destination; + + @Parameter(defaultValue = "${project.id}") + private String name; + + @Component + private TargetPlatformService platformService; + + @Component + private MirrorApplicationService mirrorService; + + @Component + private ReactorRepositoryManager repositoryManager; + + @Component + private IProvisioningAgent agent; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + ReactorProject reactorProject = DefaultReactorProject.adapt(project); + TargetPlatform targetPlatform = platformService.getTargetPlatform(reactorProject).orElse(null); + if (targetPlatform == null) { + getLog().info("Project has no target platform, skip execution."); + return; + } + IArtifactRepository sourceArtifactRepository = targetPlatform.getArtifactRepository(); + IMetadataRepository sourceMetadataRepository = targetPlatform.getMetadataRepository(); + PublishingRepository publishingRepository = repositoryManager.getPublishingRepository(reactorProject); + getLog().info("Mirroring target platform, this can take a while ..."); + try { + IArtifactRepository artifactRepository = new ListCompositeArtifactRepository( + List.of(sourceArtifactRepository, publishingRepository.getArtifactRepository()), agent); + IMetadataRepository metadataRepository = new ListCompositeMetadataRepository( + List.of(sourceMetadataRepository, publishingRepository.getMetadataRepository()), agent); + mirrorService.mirrorDirect(artifactRepository, metadataRepository, destination, name); + } catch (FacadeException e) { + throw new MojoFailureException(e.getMessage(), e.getCause()); + } + } + +} diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/mirroring/facade/MirrorApplicationService.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/mirroring/facade/MirrorApplicationService.java index 2362e74a4b..2cd396d248 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/mirroring/facade/MirrorApplicationService.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/tools/mirroring/facade/MirrorApplicationService.java @@ -17,6 +17,8 @@ import java.util.Collection; import java.util.Map; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.tycho.BuildDirectory; import org.eclipse.tycho.DependencySeed; import org.eclipse.tycho.p2.tools.BuildContext; @@ -107,6 +109,22 @@ void mirrorStandalone(RepositoryReferences sources, DestinationRepositoryDescrip Collection seedUnits, MirrorOptions mirrorOptions, BuildDirectory tempDirectory) throws FacadeException; + /** + * Mirrors the given sources to the destination, if the destination exits it will be delete + * beforehand. + * + * @param sourceArtifactRepository + * the source artifact repository + * @param sourceMetadataRepository + * the source metadata repository + * @param repositoryDestination + * the destination + * @param repositoryName + * the name of the new repository + */ + void mirrorDirect(IArtifactRepository sourceArtifactRepository, IMetadataRepository sourceMetadataRepository, + File repositoryDestination, String repositoryName) throws FacadeException; + /** * Modifies the artifact repository to add mapping rules to download Maven released artifacts * from one of the specified maven repositories (when it's found). diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PreliminaryTargetPlatformImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PreliminaryTargetPlatformImpl.java index 089b5f6627..258121013d 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PreliminaryTargetPlatformImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/PreliminaryTargetPlatformImpl.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.Set; +import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; @@ -61,7 +62,8 @@ public PreliminaryTargetPlatformImpl(Map externalIUs, ExecutionEnvironmentResolutionHints executionEnvironment, TargetPlatformFilterEvaluator filter, LocalMetadataRepository localMetadataRepository, IRawArtifactFileProvider externalArtifacts, LocalArtifactRepository localArtifactRepository, - boolean includeLocalRepo, MavenLogger logger, Set shadowed) { + boolean includeLocalRepo, MavenLogger logger, Set shadowed, + IProvisioningAgent remoteAgent) { super(collectAllInstallableUnits(reactorProjectIUs, externalIUs, executionEnvironment), executionEnvironment, externalArtifacts, localArtifactRepository, reactorProjectIUs, new HashMap<>(), shadowed); this.externalIUs = externalIUs; @@ -69,7 +71,8 @@ public PreliminaryTargetPlatformImpl(Map collectAllInstallableUnits( diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java index 960c32992d..6f069e6b6a 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetPlatformFactoryImpl.java @@ -294,7 +294,7 @@ public P2TargetPlatform createTargetPlatform(TargetPlatformConfigurationStub tpC externalArtifactFileProvider, // localArtifactRepository, // includeLocalMavenRepo, // - logger, shadowed); + logger, shadowed, remoteAgent); eeResolutionHandler.readFullSpecification(targetPlatform.getInstallableUnits()); return targetPlatform; diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java b/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java index 68ea32319b..79b9c726bd 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2tools/MirrorApplicationServiceImpl.java @@ -21,21 +21,33 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeSet; +import org.apache.commons.io.FileUtils; import org.codehaus.plexus.component.annotations.Component; import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository; import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepositoryFactory; +import org.eclipse.equinox.internal.p2.metadata.repository.SimpleMetadataRepositoryFactory; +import org.eclipse.equinox.internal.p2.repository.Transport; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.internal.repository.mirroring.IArtifactMirrorLog; +import org.eclipse.equinox.p2.internal.repository.mirroring.Mirroring; +import org.eclipse.equinox.p2.internal.repository.tools.Activator; +import org.eclipse.equinox.p2.internal.repository.tools.Messages; import org.eclipse.equinox.p2.internal.repository.tools.RecreateRepositoryApplication; import org.eclipse.equinox.p2.internal.repository.tools.RepositoryDescriptor; import org.eclipse.equinox.p2.internal.repository.tools.SlicingOptions; @@ -44,9 +56,11 @@ import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.p2.repository.IRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; import org.eclipse.tycho.ArtifactType; @@ -436,4 +450,52 @@ public void addMavenMappingRules(File repository, URI[] mavenRepositories) throw true, false, false); xzCompress(desc); } + + @Override + public void mirrorDirect(IArtifactRepository sourceArtifactRepository, IMetadataRepository sourceMetadataRepository, + File repositoryDestination, String repositoryName) throws FacadeException { + if (repositoryDestination.exists()) { + FileUtils.deleteQuietly(repositoryDestination); + } + //See https://github.com/eclipse-equinox/p2/pull/418 + Objects.requireNonNull(sourceArtifactRepository.getProvisioningAgent(), + "Source repository needs to have an agent"); + SimpleMetadataRepositoryFactory metadataRepositoryFactory = new SimpleMetadataRepositoryFactory(); + metadataRepositoryFactory.setAgent(agent); + SimpleArtifactRepositoryFactory artifactRepositoryFactory = new SimpleArtifactRepositoryFactory(); + artifactRepositoryFactory.setAgent(agent); + IArtifactRepository destinationArtifactRepository = artifactRepositoryFactory + .create(repositoryDestination.toURI(), repositoryName, null, Map.of()); + IMetadataRepository destinationMetadataRepository = metadataRepositoryFactory + .create(repositoryDestination.toURI(), repositoryName, null, Map.of()); + MultiStatus multiStatus = new MultiStatus(Activator.ID, IStatus.OK, Messages.message_mirroringStatus, null); + Set toMirror = new TreeSet<>(Comparator.comparing(IArtifactKey::getId).thenComparing( + Comparator.comparing(IArtifactKey::getVersion).thenComparing(IArtifactKey::getClassifier))); + multiStatus.add(destinationMetadataRepository.executeBatch(monitor -> { + IQueryResult result = sourceMetadataRepository.query(QueryUtil.ALL_UNITS, monitor); + Set units = result.toUnmodifiableSet(); + destinationMetadataRepository.addInstallableUnits(units); + for (IInstallableUnit unit : units) { + toMirror.addAll(unit.getArtifacts()); + } + }, null)); + multiStatus.add(destinationArtifactRepository.executeBatch(monitor -> { + Mirroring mirroring = new Mirroring(sourceArtifactRepository, destinationArtifactRepository, true); + mirroring.setCompare(false); + mirroring.setValidate(false); + mirroring.setTransport(agent.getService(Transport.class)); + IArtifactKey[] keys = toMirror.toArray(IArtifactKey[]::new); + mirroring.setArtifactKeys(keys); + multiStatus.addAll(mirroring.run(false, false)); + }, null)); + if (!multiStatus.isOK()) { + String logMessage = StatusTool.toLogMessage(multiStatus); + if (multiStatus.getSeverity() == IStatus.INFO) { + logger.info(logMessage, StatusTool.findException(multiStatus)); + } else if (multiStatus.getSeverity() == IStatus.WARNING) { + logger.warn(logMessage, StatusTool.findException(multiStatus)); + } + throw new FacadeException(logMessage, new CoreException(multiStatus)); + } + } }