From 6bf3aeed8336519f0f8a8770457710d1d6fac0a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Tue, 19 Dec 2023 09:30:10 +0100 Subject: [PATCH] Add support for mirroring the projects target platform This adds support for converting the projects computed target platform into a deployable p2 repository. This can be used to create a mirror of a target-file, or to have a repository that can be used to install one specific bundle or feature with all its dependencies. --- RELEASE_NOTES.md | 1 + pom.xml | 2 + .../target/MirrorTargetPlatformMojo.java | 93 ++++++++++++++++ .../facade/MirrorApplicationService.java | 18 +++ .../p2resolver/InstallableUnitResolver.java | 7 +- .../PreliminaryTargetPlatformImpl.java | 7 +- .../p2resolver/TargetDefinitionResolver.java | 46 ++++++-- .../p2resolver/TargetPlatformFactoryImpl.java | 2 +- .../p2tools/MirrorApplicationServiceImpl.java | 105 +++++++++++++++++- ...etDefinitionResolverIncludeSourceTest.java | 21 +++- .../TargetDefinitionResolverTest.java | 6 +- .../targetplatform/TargetDefinition.java | 2 + .../targetplatform/TargetDefinitionFile.java | 33 +++++- 13 files changed, 319 insertions(+), 24 deletions(-) create mode 100644 target-platform-configuration/src/main/java/org/eclipse/tycho/target/MirrorTargetPlatformMojo.java diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 377fe8d7ab..ec262bbd13 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -6,6 +6,7 @@ This page describes the noteworthy improvements provided by each release of Ecli ### backports: +- new `mirror-target-platform` mojo - new director mojo - support for PDE Api Tools Annotations - api tools fixes diff --git a/pom.xml b/pom.xml index 270ee8c1e7..5bd8d9ea5a 100644 --- a/pom.xml +++ b/pom.xml @@ -390,6 +390,8 @@ ${min.jdk.version} ${min.jdk.version} + + false 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/InstallableUnitResolver.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java index 6194935b5d..c6524a233d 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/InstallableUnitResolver.java @@ -75,7 +75,8 @@ public InstallableUnitResolver(List environments, this.logger = logger; } - public void addLocation(InstallableUnitLocation iuLocationDefinition, IQueryable localUnits) { + public Collection addLocation(InstallableUnitLocation iuLocationDefinition, + IQueryable localUnits) { //update (and validate) desired global state setIncludeMode(iuLocationDefinition.getIncludeMode()); setIncludeAllEnvironments(iuLocationDefinition.includeAllEnvironments()); @@ -85,7 +86,9 @@ public void addLocation(InstallableUnitLocation iuLocationDefinition, IQueryable default -> iuLocationDefinition.includeSource(); }); //resolve root units and add them - rootUnits.add(new RootUnits(getRootIUs(iuLocationDefinition.getUnits(), localUnits), localUnits)); + Collection rootIUs = getRootIUs(iuLocationDefinition.getUnits(), localUnits); + rootUnits.add(new RootUnits(rootIUs, localUnits)); + return rootIUs; } private void setIncludeMode(IncludeMode newValue) throws TargetDefinitionResolutionException { 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/TargetDefinitionResolver.java b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolver.java index bc54520f63..9347563cc5 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolver.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolver.java @@ -23,14 +23,21 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.equinox.internal.p2.updatesite.SiteCategory; +import org.eclipse.equinox.internal.p2.updatesite.SiteXMLAction; import org.eclipse.equinox.p2.core.IProvisioningAgent; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.query.CollectionResult; import org.eclipse.equinox.p2.query.Collector; import org.eclipse.equinox.p2.query.IQuery; import org.eclipse.equinox.p2.query.IQueryResult; @@ -77,6 +84,8 @@ */ public final class TargetDefinitionResolver { + private static final SiteXMLAction CATEGORY_FACTORY = new SiteXMLAction((URI) null, (String) null); + private final MavenLogger logger; private final List environments; @@ -148,7 +157,11 @@ public TargetDefinitionContent resolveContentWithExceptions(TargetDefinition def location, repository.getId(), referencedRepositoryMode))); } IQueryable locationUnits = QueryUtil.compoundQueryable(locations); - installableUnitResolver.addLocation((InstallableUnitLocation) locationDefinition, locationUnits); + Collection rootUnits = installableUnitResolver + .addLocation((InstallableUnitLocation) locationDefinition, locationUnits); + unitResultSet.accept( + createCategory(installableUnitLocation.getRepositories().stream().map(r -> r.getLocation()) + .collect(Collectors.joining(", ")), new CollectionResult<>(rootUnits))); } else if (locationDefinition instanceof PathLocation pathLocation) { String resolvePath = resolvePath(pathLocation.getPath(), definition); File fileLocation; @@ -161,32 +174,38 @@ public TargetDefinitionContent resolveContentWithExceptions(TargetDefinition def FileTargetDefinitionContent fileRepositoryRolver = fileRepositories.computeIfAbsent( fileLocation.getAbsolutePath(), key -> new FileTargetDefinitionContent(provisioningAgent, fileLocation)); + IQueryResult result; if (pathLocation instanceof DirectoryLocation || pathLocation instanceof ProfileLocation) { - unitResultSet.addAll( - fileRepositoryRolver.query(QueryUtil.ALL_UNITS, new LoggingProgressMonitor(logger))); + result = fileRepositoryRolver.query(QueryUtil.ALL_UNITS, new LoggingProgressMonitor(logger)); } else if (pathLocation instanceof FeaturesLocation featuresLocation) { IArtifactKey key = org.eclipse.equinox.p2.publisher.eclipse.FeaturesAction .createFeatureArtifactKey(featuresLocation.getId(), featuresLocation.getVersion()); - unitResultSet.addAll(fileRepositoryRolver.query(QueryUtil.createIUQuery(key), - new LoggingProgressMonitor(logger))); + result = fileRepositoryRolver.query(QueryUtil.createIUQuery(key), + new LoggingProgressMonitor(logger)); + } else { + continue; } + unitResultSet.addAll(result); + unitResultSet.accept(createCategory(resolvePath, result)); } else { logger.warn("Target location path '" + fileLocation.getAbsolutePath() + "' does not exist, target resolution might be incomplete."); } - } else if (locationDefinition instanceof MavenGAVLocation location) { + } else if (locationDefinition instanceof MavenGAVLocation mavenLocation) { TargetDefinitionContent targetDefinitionContent = mavenDependenciesResolver - .resolveTargetDefinitionContent(location, includeSourceMode); + .resolveTargetDefinitionContent(mavenLocation, includeSourceMode); mavenLocations.add(targetDefinitionContent); IQueryResult result = targetDefinitionContent.query(QueryUtil.ALL_UNITS, new LoggingProgressMonitor(logger)); unitResultSet.addAll(result); + Set locationUnits = result.toUnmodifiableSet(); if (logger.isDebugEnabled()) { - logger.debug("The following artifacts were resolved from location " + location); - for (IInstallableUnit iu : result.toUnmodifiableSet()) { + logger.debug("The following artifacts were resolved from location " + mavenLocation); + for (IInstallableUnit iu : locationUnits) { logger.debug("\t" + iu); } } + unitResultSet.accept(createCategory(mavenLocation.getLabel(), result)); } else if (locationDefinition instanceof TargetReferenceLocation referenceLocation) { logger.info("Resolving " + referenceLocation.getUri()); String resolvePath = resolvePath(referenceLocation.getUri(), definition); @@ -219,6 +238,7 @@ public TargetDefinitionContent resolveContentWithExceptions(TargetDefinition def IQueryResult result = content.query(QueryUtil.ALL_UNITS, new LoggingProgressMonitor(logger)); unitResultSet.addAll(result); + unitResultSet.accept(createCategory(uri, result)); } else { logger.warn("Target location type '" + locationDefinition.getTypeDescription() + "' is not supported"); } @@ -294,6 +314,14 @@ public IArtifactRepository getArtifactRepository() { }; } + private static IInstallableUnit createCategory(String label, IQueryResult result) { + SiteCategory category = new SiteCategory(); + category.setLabel(label); + category.setName("generated.target.category." + UUID.randomUUID()); + return CATEGORY_FACTORY.createCategoryIU(category, + result.stream().filter(iu -> !iu.getId().endsWith(".feature.jar")).collect(Collectors.toSet())); + } + /** * Converts a "raw" URI string into one that can be used to parse it as an {@link URI}. The * conversion is especially for converting file URIs constructed using maven properties that 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..3225af8565 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 @@ -14,28 +14,45 @@ package org.eclipse.tycho.p2tools; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URLConnection; +import java.nio.file.Files; 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.Properties; +import java.util.Set; +import java.util.TreeSet; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; + +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 +61,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; @@ -66,6 +85,8 @@ @Component(role = MirrorApplicationService.class) public class MirrorApplicationServiceImpl implements MirrorApplicationService { + private static final String P2_INDEX_FILE = "p2.index"; + private static final String MIRROR_FAILURE_MESSAGE = "Mirroring failed"; @Requirement @@ -436,4 +457,86 @@ 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)); + } + writeP2Index(repositoryDestination); + compressXml(repositoryDestination, "artifacts"); + compressXml(repositoryDestination, "content"); + try { + XZCompressor xzCompressor = new XZCompressor(); + xzCompressor.setPreserveOriginalFile(true); + xzCompressor.setRepoFolder(repositoryDestination.getAbsolutePath()); + xzCompressor.compressRepo(); + } catch (IOException e) { + throw new FacadeException("XZ compression failed", e); + } + } + + private void writeP2Index(File repositoryDestination) throws FacadeException { + Properties properties = new Properties(); + properties.setProperty("version", "1"); + properties.setProperty("artifact.repository.factory.order", "artifacts.xml,!"); + properties.setProperty("metadata.repository.factory.order", "content.xml,!"); + try (FileOutputStream stream = new FileOutputStream(new File(repositoryDestination, P2_INDEX_FILE))) { + properties.store(stream, null); + } catch (IOException e) { + throw new FacadeException("writing index file failed", e); + } + } + + private void compressXml(File repositoryDestination, String name) throws FacadeException { + File jarFile = new File(repositoryDestination, name + ".jar"); + File xmlFile = new File(repositoryDestination, name + ".xml"); + try (JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile))) { + jarOutputStream.putNextEntry(new JarEntry(xmlFile.getName())); + Files.copy(xmlFile.toPath(), jarOutputStream); + } catch (IOException e) { + throw new FacadeException("compression failed", e); + } + } } diff --git a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverIncludeSourceTest.java b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverIncludeSourceTest.java index efa0a88ec7..b84971653e 100644 --- a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverIncludeSourceTest.java +++ b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverIncludeSourceTest.java @@ -21,7 +21,11 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import java.util.Set; +import java.util.stream.Collectors; + import org.eclipse.equinox.p2.core.IProvisioningAgent; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; import org.eclipse.equinox.p2.metadata.IVersionedId; import org.eclipse.equinox.p2.metadata.VersionedId; import org.eclipse.equinox.p2.query.QueryUtil; @@ -83,7 +87,7 @@ public void testIncludeSourceWithSlicerMode() throws Exception { assertThat(versionedIdsOf(content), hasItem(BUNDLE_WITH_SOURCES)); assertThat(versionedIdsOf(content), hasItem(SOURCE_BUNDLE)); - assertEquals(2, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(2, getResultSet(content).size()); } @Test @@ -96,7 +100,12 @@ public void testIncludeSourceWithPlannerMode() throws Exception { assertThat(versionedIdsOf(content), hasItem(BUNDLE_WITH_SOURCES)); assertThat(versionedIdsOf(content), hasItem(SOURCE_BUNDLE)); - assertEquals(2, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(2, getResultSet(content).size()); + } + + private Set getResultSet(TargetDefinitionContent content) { + return content.query(QueryUtil.ALL_UNITS, null).stream() + .filter(iu -> !iu.getId().startsWith("generated.target.category.")).collect(Collectors.toSet()); } @Test @@ -108,7 +117,7 @@ public void testNoSourceIncludeWhenIncludeSourceIsFalseWithSlicerMode() throws E lookup(IProvisioningAgent.class)); assertThat(versionedIdsOf(content), not(hasItem(SOURCE_BUNDLE))); - assertEquals(1, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(1, getResultSet(content).size()); } @Test @@ -120,7 +129,7 @@ public void testNoSourceIncludeWhenIncludeSourceIsFalseWithPlannerMode() throws lookup(IProvisioningAgent.class)); assertThat(versionedIdsOf(content), not(hasItem(SOURCE_BUNDLE))); - assertEquals(1, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(1, getResultSet(content).size()); } @Test @@ -134,7 +143,7 @@ public void testCanResolveBundlesWithoutSourcesWithSlicerMode() throws Exception assertThat(versionedIdsOf(content), hasItem(NOSOURCE_BUNDLE)); assertThat(versionedIdsOf(content), hasItem(BUNDLE_WITH_SOURCES)); assertThat(versionedIdsOf(content), hasItem(SOURCE_BUNDLE)); - assertEquals(3, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(3, getResultSet(content).size()); } @Test @@ -148,7 +157,7 @@ public void testCanResolveBundlesWithoutSourcesWithPlannerMode() throws Exceptio assertThat(versionedIdsOf(content), hasItem(NOSOURCE_BUNDLE)); assertThat(versionedIdsOf(content), hasItem(BUNDLE_WITH_SOURCES)); assertThat(versionedIdsOf(content), hasItem(SOURCE_BUNDLE)); - assertEquals(3, content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet().size()); + assertEquals(3, getResultSet(content).size()); } static class WithSourceLocationStub extends LocationStub { diff --git a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java index 78f765f169..f05aeb264c 100644 --- a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java +++ b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/TargetDefinitionResolverTest.java @@ -253,7 +253,11 @@ public boolean matchesSafely(Collection item) { static Collection versionedIdsOf(TargetDefinitionContent content) { Collection result = new ArrayList<>(); for (IInstallableUnit unit : content.query(QueryUtil.ALL_UNITS, null).toUnmodifiableSet()) { - result.add(new VersionedId(unit.getId(), unit.getVersion())); + String id = unit.getId(); + if (id.startsWith("generated.target.category.")) { + continue; + } + result.add(new VersionedId(id, unit.getVersion())); } return result; } diff --git a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java index ca591b0b7b..e700d90ce2 100644 --- a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java +++ b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinition.java @@ -115,6 +115,8 @@ enum DependencyDepth { Element getFeatureTemplate(); + String getLabel(); + @Override public default String getTypeDescription() { return TYPE; diff --git a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java index f64418c913..0cda363213 100644 --- a/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java +++ b/tycho-targetplatform/src/main/java/org/eclipse/tycho/targetplatform/TargetDefinitionFile.java @@ -27,6 +27,7 @@ import java.io.OutputStream; import java.io.StringReader; import java.net.URI; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -197,11 +198,13 @@ private static class MavenLocation implements TargetDefinition.MavenGAVLocation private final DependencyDepth dependencyDepth; private final Collection repositoryReferences; private final Element featureTemplate; + private String label; public MavenLocation(Collection roots, Collection includeDependencyScopes, MissingManifestStrategy manifestStrategy, boolean includeSource, Collection instructions, DependencyDepth dependencyDepth, - Collection repositoryReferences, Element featureTemplate) { + Collection repositoryReferences, Element featureTemplate, + String label) { this.roots = roots; this.includeDependencyScopes = includeDependencyScopes; this.manifestStrategy = manifestStrategy; @@ -209,6 +212,7 @@ public MavenLocation(Collection roots, Collection inclu this.instructions = instructions; this.dependencyDepth = dependencyDepth; this.repositoryReferences = repositoryReferences; + this.label = label; this.featureTemplate = featureTemplate == null ? null : (Element) featureTemplate.cloneNode(true); } @@ -265,6 +269,30 @@ public DependencyDepth getIncludeDependencyDepth() { return dependencyDepth; } + @Override + public String getLabel() { + if (label != null && !label.isBlank()) { + return label; + } + if (featureTemplate != null) { + String featureLabel = featureTemplate.getAttribute("label"); + if (featureLabel != null && !featureLabel.isBlank()) { + return featureLabel; + } + String featureId = featureTemplate.getAttribute("id"); + if (featureId != null && !featureId.isBlank()) { + return featureId; + } + } + if (roots.size() == 1) { + MavenDependency dependency = roots.iterator().next(); + return MessageFormat.format("{0}:{1} ({2})", dependency.getGroupId(), dependency.getArtifactId(), + dependency.getVersion()); + } else { + return MessageFormat.format("{0} Maven Dependencies", roots.size()); + } + } + } private static final class MavenDependencyRoot implements MavenDependency { @@ -668,7 +696,8 @@ private static MavenLocation parseMavenLocation(Element dom) { Element featureTemplate = getChild(dom, "feature"); return new MavenLocation(parseRoots(dom, globalExcludes), scopes, parseManifestStrategy(dom), Boolean.parseBoolean(dom.getAttribute("includeSource")), parseInstructions(dom), - parseDependencyDepth(dom, scope), parseRepositoryReferences(dom), featureTemplate); + parseDependencyDepth(dom, scope), parseRepositoryReferences(dom), featureTemplate, + dom.getAttribute("label")); } private static IULocation parseIULocation(Element dom) {