From 46e845ea88526f082250cb8a731538baeb57582b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Sun, 7 Jan 2024 11:37:46 +0100 Subject: [PATCH] Add a category for mirrored feature / bundle project and filter IUs Currently when mirroring the target platform of a bundle/feature it mirrors everything that is reachable. Also it creates an update-site that has not categories. This now filters the IUs based on the project units, adds a category for it and allows to even filter categories completely if desired. This also contains the migration of previous used deployableFeature in test to using this new way of creating an update-site, as part of this the deployableFeature option is also deprecated. (cherry picked from commit 963075e8db4d96baee6c3a5ad7fb47e161a9bd6d) --- .../target/MirrorTargetPlatformMojo.java | 68 +++++++++++++++++-- .../facade/MirrorApplicationService.java | 9 ++- .../p2tools/MirrorApplicationServiceImpl.java | 6 +- .../featureDotQualifier/pom.xml | 17 +++-- .../TYCHO0439repositoryCategories/pom.xml | 17 +++-- tycho-its/projects/brokenp2data/pom.xml | 3 - .../custom.bundle.feature/pom.xml | 17 +++-- .../RepositoryCategoriesTest.java | 62 ++++++++--------- .../plugins/p2/CategoryP2MetadataMojo.java | 42 +++++++++++- .../tycho/packaging/PackageFeatureMojo.java | 9 ++- 10 files changed, 190 insertions(+), 60 deletions(-) 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 index bc7c9c61e3..0818a39ae5 100644 --- 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 @@ -13,7 +13,11 @@ package org.eclipse.tycho.target; import java.io.File; +import java.net.URI; import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; @@ -24,9 +28,18 @@ import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.plugins.annotations.ResolutionScope; import org.apache.maven.project.MavenProject; +import org.eclipse.core.runtime.CoreException; +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.IInstallableUnit; +import org.eclipse.equinox.p2.query.CollectionResult; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.query.IQueryable; +import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository; import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository; +import org.eclipse.tycho.PackagingType; import org.eclipse.tycho.ReactorProject; import org.eclipse.tycho.TargetPlatform; import org.eclipse.tycho.TargetPlatformService; @@ -35,6 +48,7 @@ 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.InstallableUnitSlicer; import org.eclipse.tycho.p2maven.ListCompositeArtifactRepository; import org.eclipse.tycho.repository.registry.facade.ReactorRepositoryManager; @@ -43,9 +57,11 @@ * 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) +@Mojo(name = "mirror-target-platform", threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, defaultPhase = LifecyclePhase.PACKAGE) public class MirrorTargetPlatformMojo extends AbstractMojo { + private static final SiteXMLAction CATEGORY_FACTORY = new SiteXMLAction((URI) null, (String) null); + @Parameter(property = "project", readonly = true) private MavenProject project; @@ -55,6 +71,9 @@ public class MirrorTargetPlatformMojo extends AbstractMojo { @Parameter(defaultValue = "${project.id}") private String name; + @Parameter(defaultValue = "true") + private boolean includeCategories = true; + @Component private TargetPlatformService platformService; @@ -67,6 +86,9 @@ public class MirrorTargetPlatformMojo extends AbstractMojo { @Component private IProvisioningAgent agent; + @Component + private InstallableUnitSlicer installableUnitSlicer; + @Override public void execute() throws MojoExecutionException, MojoFailureException { ReactorProject reactorProject = DefaultReactorProject.adapt(project); @@ -78,16 +100,54 @@ public void execute() throws MojoExecutionException, MojoFailureException { IArtifactRepository sourceArtifactRepository = targetPlatform.getArtifactRepository(); IMetadataRepository sourceMetadataRepository = targetPlatform.getMetadataRepository(); PublishingRepository publishingRepository = repositoryManager.getPublishingRepository(reactorProject); - getLog().info("Mirroring target platform, this can take a while ..."); try { + IMetadataRepository projectRepository = publishingRepository.getMetadataRepository(); 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); + List.of(sourceMetadataRepository, projectRepository), agent); + IQueryable mirrorUnits; + if (PackagingType.TYPE_ECLIPSE_TARGET_DEFINITION.equals(project.getPackaging())) { + //for a target platform we like to mirror everything... + mirrorUnits = metadataRepository; + } else { + //for everything else we want to mirror only items that are required by the project + try { + IQueryResult query = projectRepository.query(QueryUtil.ALL_UNITS, null); + Set rootIus = query.toSet(); + String label; + String projectName = project.getName(); + if (projectName != null && !projectName.isBlank()) { + label = projectName; + } else { + label = project.getId(); + } + rootIus.add(createCategory(label, query)); + mirrorUnits = installableUnitSlicer.computeDependencies(rootIus, metadataRepository); + } catch (CoreException e) { + throw new MojoFailureException("Failed to compute dependencies to mirror", e); + } + } + Set toMirror = mirrorUnits.query(QueryUtil.ALL_UNITS, null).toSet(); + if (!includeCategories) { + //remove any categories from the result + toMirror.removeIf(QueryUtil::isCategory); + } + getLog().info( + "Mirroring " + toMirror.size() + " unit(s) from the target platform, this can take a while ..."); + mirrorService.mirrorDirect(artifactRepository, new CollectionResult(toMirror), + destination, name); } catch (FacadeException e) { throw new MojoFailureException(e.getMessage(), e.getCause()); } } + private static IInstallableUnit createCategory(String label, IQueryResult result) { + SiteCategory category = new SiteCategory(); + category.setLabel(label); + category.setName("generated.project.category." + UUID.randomUUID()); + return CATEGORY_FACTORY.createCategoryIU(category, + result.stream().filter(iu -> !iu.getId().endsWith(".feature.jar")).collect(Collectors.toSet())); + } + } 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 2cd396d248..6c9f772622 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,8 +17,9 @@ import java.util.Collection; import java.util.Map; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.query.IQueryable; 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; @@ -121,9 +122,11 @@ void mirrorStandalone(RepositoryReferences sources, DestinationRepositoryDescrip * the destination * @param repositoryName * the name of the new repository + * @throws FacadeException */ - void mirrorDirect(IArtifactRepository sourceArtifactRepository, IMetadataRepository sourceMetadataRepository, - File repositoryDestination, String repositoryName) throws FacadeException; + void mirrorDirect(IArtifactRepository sourceArtifactRepository, + IQueryable sourceMetadataRepository, File repositoryDestination, String repositoryName) + throws FacadeException; /** * Modifies the artifact repository to add mapping rules to download Maven released artifacts 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 3225af8565..4709107cdb 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 @@ -62,6 +62,7 @@ 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.IQueryable; import org.eclipse.equinox.p2.query.QueryUtil; import org.eclipse.equinox.p2.repository.IRepositoryManager; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; @@ -459,8 +460,9 @@ public void addMavenMappingRules(File repository, URI[] mavenRepositories) throw } @Override - public void mirrorDirect(IArtifactRepository sourceArtifactRepository, IMetadataRepository sourceMetadataRepository, - File repositoryDestination, String repositoryName) throws FacadeException { + public void mirrorDirect(IArtifactRepository sourceArtifactRepository, + IQueryable sourceMetadataRepository, File repositoryDestination, String repositoryName) + throws FacadeException { if (repositoryDestination.exists()) { FileUtils.deleteQuietly(repositoryDestination); } diff --git a/tycho-its/projects/TYCHO0383dotQualifierMatching/featureDotQualifier/pom.xml b/tycho-its/projects/TYCHO0383dotQualifierMatching/featureDotQualifier/pom.xml index cbe79ed013..c62a0f93c7 100644 --- a/tycho-its/projects/TYCHO0383dotQualifierMatching/featureDotQualifier/pom.xml +++ b/tycho-its/projects/TYCHO0383dotQualifierMatching/featureDotQualifier/pom.xml @@ -32,11 +32,20 @@ org.eclipse.tycho - tycho-packaging-plugin + target-platform-configuration ${tycho-version} - - true - + + + inject + + mirror-target-platform + + + + + ${project.build.directory}/site + false + diff --git a/tycho-its/projects/TYCHO0439repositoryCategories/pom.xml b/tycho-its/projects/TYCHO0439repositoryCategories/pom.xml index 033073b31c..6a1f543414 100644 --- a/tycho-its/projects/TYCHO0439repositoryCategories/pom.xml +++ b/tycho-its/projects/TYCHO0439repositoryCategories/pom.xml @@ -31,11 +31,20 @@ org.eclipse.tycho - tycho-packaging-plugin + target-platform-configuration ${tycho-version} - - true - + + + inject + + mirror-target-platform + + + + + ${project.build.directory}/site + false + org.eclipse.tycho diff --git a/tycho-its/projects/brokenp2data/pom.xml b/tycho-its/projects/brokenp2data/pom.xml index 41de980146..384f934f90 100644 --- a/tycho-its/projects/brokenp2data/pom.xml +++ b/tycho-its/projects/brokenp2data/pom.xml @@ -25,9 +25,6 @@ org.eclipse.tycho tycho-packaging-plugin ${tycho-version} - - true - diff --git a/tycho-its/projects/custom-bundle-plugin/custom-bundle-parent/custom.bundle.feature/pom.xml b/tycho-its/projects/custom-bundle-plugin/custom-bundle-parent/custom.bundle.feature/pom.xml index 4401d0e80e..3c6ebbeb4f 100644 --- a/tycho-its/projects/custom-bundle-plugin/custom-bundle-parent/custom.bundle.feature/pom.xml +++ b/tycho-its/projects/custom-bundle-plugin/custom-bundle-parent/custom.bundle.feature/pom.xml @@ -15,11 +15,20 @@ org.eclipse.tycho - tycho-packaging-plugin + target-platform-configuration ${tycho-version} - - true - + + + inject + + mirror-target-platform + + + + + ${project.build.directory}/site + false + diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO0439repositoryCategories/RepositoryCategoriesTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO0439repositoryCategories/RepositoryCategoriesTest.java index 5fcc59a91f..790943ebfd 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO0439repositoryCategories/RepositoryCategoriesTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/TYCHO0439repositoryCategories/RepositoryCategoriesTest.java @@ -28,41 +28,41 @@ public class RepositoryCategoriesTest extends AbstractTychoIntegrationTest { - @Test - public void testDeployableFeature() throws Exception { - Verifier v01 = getVerifier("TYCHO0439repositoryCategories"); - v01.executeGoal("install"); - v01.verifyErrorFreeLog(); + @Test + public void testDeployableFeature() throws Exception { + Verifier v01 = getVerifier("TYCHO0439repositoryCategories"); + v01.executeGoal("install"); + v01.verifyErrorFreeLog(); - File site = new File(v01.getBasedir(), "target/site"); - Assert.assertTrue(site.isDirectory()); + File site = new File(v01.getBasedir(), "target/site"); + Assert.assertTrue(site.isDirectory()); - File content = new File(site, "content.jar"); - Assert.assertTrue(content.isFile()); + File content = new File(site, "content.jar"); + Assert.assertTrue(content.getAbsolutePath() + " is not a file!", content.isFile()); - boolean found = false; + boolean found = false; - XMLParser parser = new XMLParser(); - Document document = null; - ZipFile contentJar = new ZipFile(content); - try { - ZipEntry contentXmlEntry = contentJar.getEntry("content.xml"); - document = parser.parse(new XMLIOSource(contentJar.getInputStream(contentXmlEntry))); - } finally { - contentJar.close(); - } - Element repository = document.getRootElement(); - all_units: for (Element unit : repository.getChild("units").getChildren("unit")) { - for (Element property : unit.getChild("properties").getChildren("property")) { - if ("org.eclipse.equinox.p2.type.category".equals(property.getAttributeValue("name")) - && Boolean.parseBoolean(property.getAttributeValue("value"))) { - found = true; - break all_units; - } - } - } + XMLParser parser = new XMLParser(); + Document document = null; + ZipFile contentJar = new ZipFile(content); + try { + ZipEntry contentXmlEntry = contentJar.getEntry("content.xml"); + document = parser.parse(new XMLIOSource(contentJar.getInputStream(contentXmlEntry))); + } finally { + contentJar.close(); + } + Element repository = document.getRootElement(); + all_units: for (Element unit : repository.getChild("units").getChildren("unit")) { + for (Element property : unit.getChild("properties").getChildren("property")) { + if ("org.eclipse.equinox.p2.type.category".equals(property.getAttributeValue("name")) + && Boolean.parseBoolean(property.getAttributeValue("value"))) { + found = true; + break all_units; + } + } + } - Assert.assertTrue("Custom category", found); - } + Assert.assertTrue("Custom category is missing: " + content.getAbsolutePath(), found); + } } diff --git a/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/CategoryP2MetadataMojo.java b/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/CategoryP2MetadataMojo.java index c149fd6df3..dc130d6aac 100644 --- a/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/CategoryP2MetadataMojo.java +++ b/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/CategoryP2MetadataMojo.java @@ -13,15 +13,20 @@ package org.eclipse.tycho.plugins.p2; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.net.MalformedURLException; +import java.nio.file.Files; import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; import org.eclipse.equinox.internal.p2.updatesite.CategoryPublisherApplication; +import org.eclipse.equinox.p2.internal.repository.tools.XZCompressor; /** * Adds category IUs to existing metadata repository. @@ -42,16 +47,49 @@ protected CategoryPublisherApplication getPublisherApplication() { @Override protected void addArguments(List arguments) throws IOException, MalformedURLException { + File location = getUpdateSiteLocation(); arguments.add("-metadataRepository"); - arguments.add(getUpdateSiteLocation().toURL().toExternalForm()); + arguments.add(location.toURI().toURL().toExternalForm()); arguments.add("-categoryDefinition"); - arguments.add(categoryDefinition.toURL().toExternalForm()); + arguments.add(categoryDefinition.toURI().toURL().toExternalForm()); } @Override public void execute() throws MojoExecutionException, MojoFailureException { synchronized (LOCK) { + File location = getUpdateSiteLocation(); + File xmlFile = new File(location, "content.xml"); + File jarFile = new File(location, "content.jar"); + File xzFile = new File(location, "content.xml.xz"); + boolean jar = jarFile.isFile(); + boolean xz = xzFile.isFile(); + if (xmlFile.isFile()) { + if (jar) { + jarFile.delete(); + } + } + if (xz) { + xzFile.delete(); + } super.execute(); + try { + if (jar && xmlFile.exists()) { + //need to recreate the jar + try (JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile))) { + jarOutputStream.putNextEntry(new JarEntry(xmlFile.getName())); + Files.copy(xmlFile.toPath(), jarOutputStream); + } + } + if (xz) { + //need to recreate the xz + XZCompressor xzCompressor = new XZCompressor(); + xzCompressor.setPreserveOriginalFile(true); + xzCompressor.setRepoFolder(location.getAbsolutePath()); + xzCompressor.compressRepo(); + } + } catch (IOException e) { + throw new MojoFailureException("compress content failed", e); + } } } } diff --git a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/PackageFeatureMojo.java b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/PackageFeatureMojo.java index c4c393f64c..74319d9c3d 100644 --- a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/PackageFeatureMojo.java +++ b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/PackageFeatureMojo.java @@ -103,10 +103,13 @@ public class PackageFeatureMojo extends AbstractTychoPackagingMojo { private String finalName; /** - * If set to true, standard eclipse update site directory with feature content will - * be created under target folder. - */ + * If set to true, standard eclipse update site directory with + * feature content will be created under target folder. + * + * @deprecated use the new mirror-target-platform instead. + */ @Parameter(defaultValue = "false") + @Deprecated private boolean deployableFeature = false; @Parameter(defaultValue = "${project.build.directory}/site")