From 0f5e7218764e7bf99a7bbd5cb153529ed262ef8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Tue, 5 Dec 2023 08:54:46 +0100 Subject: [PATCH] Generates the P2Artifact after binary is published Currently we generate the P2Artifact (+ IArtifactDescriptor) at time where the output stream is requested, that has the problem that the file actually do not exits at this point and we can not calculate the checksum for feature root artifacts. This postpones the creation to the stage where the file is already published. --- .../FeatureRootfileArtifactRepository.java | 105 +++++++++--------- .../module/ModuleArtifactRepository.java | 6 +- ...FeatureRootfileArtifactRepositoryTest.java | 10 +- .../p2/publisher/PublishProductMojo.java | 85 +++++++++++++- 4 files changed, 145 insertions(+), 61 deletions(-) diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/publisher/FeatureRootfileArtifactRepository.java b/tycho-core/src/main/java/org/eclipse/tycho/core/publisher/FeatureRootfileArtifactRepository.java index 0257245a14..62445943ed 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/publisher/FeatureRootfileArtifactRepository.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/publisher/FeatureRootfileArtifactRepository.java @@ -14,17 +14,18 @@ import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; -import org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactDescriptor; import org.eclipse.equinox.p2.core.ProvisionException; import org.eclipse.equinox.p2.metadata.IArtifactKey; import org.eclipse.equinox.p2.metadata.IInstallableUnit; @@ -32,6 +33,7 @@ import org.eclipse.equinox.p2.publisher.PublisherInfo; import org.eclipse.equinox.p2.publisher.actions.IPropertyAdvice; import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor; import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.p2.metadata.IP2Artifact; @@ -41,11 +43,19 @@ import org.eclipse.tycho.p2maven.advices.MavenPropertiesAdvice; public class FeatureRootfileArtifactRepository extends TransientArtifactRepository { + //for backward compatibility + @SuppressWarnings("deprecation") + private static final String PROP_EXTENSION = TychoConstants.PROP_EXTENSION; + private final File outputDirectory; private final PublisherInfo publisherInfo; - private Map publishedArtifacts = new HashMap<>(); + private Map artifactsToPublish = new HashMap<>(); + + private Map collect; + + private List temp = new ArrayList<>(); public FeatureRootfileArtifactRepository(PublisherInfo publisherInfo, File outputDirectory) { this.publisherInfo = publisherInfo; @@ -56,8 +66,18 @@ public FeatureRootfileArtifactRepository(PublisherInfo publisherInfo, File outpu public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws ProvisionException { IArtifactKey artifactKey = descriptor.getArtifactKey(); if (artifactKey != null && PublisherHelper.BINARY_ARTIFACT_CLASSIFIER.equals(artifactKey.getClassifier())) { + if (!publisherInfo + .getAdvice(null, false, artifactKey.getId(), artifactKey.getVersion(), IPropertyAdvice.class) + .stream().anyMatch(advice -> advice instanceof MavenPropertiesAdvice)) { + throw new ProvisionException("MavenPropertiesAdvice does not exist for artifact: " + artifactKey); + } + File outputFile = new File(this.outputDirectory, artifactKey.getId() + "-" + artifactKey.getVersion() + "-" + + TychoConstants.ROOTFILE_CLASSIFIER + "." + TychoConstants.ROOTFILE_EXTENSION); try { - return createRootfileOutputStream(artifactKey); + temp.add(descriptor); + descriptors.add(descriptor); + artifactsToPublish.put(outputFile, artifactKey); + return new BufferedOutputStream(new FileOutputStream(outputFile)); } catch (IOException e) { throw new ProvisionException(e.getMessage(), e); } @@ -66,52 +86,6 @@ public OutputStream getOutputStream(IArtifactDescriptor descriptor) throws Provi return super.getOutputStream(descriptor); } - private OutputStream createRootfileOutputStream(IArtifactKey artifactKey) throws ProvisionException, IOException { - File outputFile = new File(this.outputDirectory, artifactKey.getId() + "-" + artifactKey.getVersion() + "-" - + TychoConstants.ROOTFILE_CLASSIFIER + "." + TychoConstants.ROOTFILE_EXTENSION); - - OutputStream target = null; - try { - SimpleArtifactDescriptor simpleArtifactDescriptor = (SimpleArtifactDescriptor) createArtifactDescriptor( - artifactKey); - - Collection advices = publisherInfo.getAdvice(null, false, - simpleArtifactDescriptor.getArtifactKey().getId(), - simpleArtifactDescriptor.getArtifactKey().getVersion(), IPropertyAdvice.class); - - boolean mavenPropAdviceExists = false; - for (IPropertyAdvice entry : advices) { - if (entry instanceof MavenPropertiesAdvice) { - mavenPropAdviceExists = true; - entry.getArtifactProperties(null, simpleArtifactDescriptor); - } - } - - if (!mavenPropAdviceExists) { - throw new ProvisionException( - "MavenPropertiesAdvice does not exist for artifact: " + simpleArtifactDescriptor); - } - - String mavenArtifactClassifier = getRootFileArtifactClassifier( - simpleArtifactDescriptor.getArtifactKey().getId()); - simpleArtifactDescriptor.setProperty(TychoConstants.PROP_CLASSIFIER, mavenArtifactClassifier); - //Type and extension are the same for rootfiles ... - simpleArtifactDescriptor.setProperty(TychoConstants.PROP_EXTENSION, TychoConstants.ROOTFILE_EXTENSION); - simpleArtifactDescriptor.setProperty(TychoConstants.PROP_TYPE, TychoConstants.ROOTFILE_EXTENSION); - - target = new BufferedOutputStream(new FileOutputStream(outputFile)); - - this.publishedArtifacts.put(mavenArtifactClassifier, - new P2Artifact(outputFile, Collections. emptySet(), simpleArtifactDescriptor)); - - descriptors.add(simpleArtifactDescriptor); - } catch (FileNotFoundException e) { - throw new ProvisionException(e.getMessage(), e); - } - - return target; - } - String getRootFileArtifactClassifier(String artifactId) { List adviceList = this.publisherInfo.getAdvice(); @@ -131,6 +105,35 @@ String getRootFileArtifactClassifier(String artifactId) { } public Map getPublishedArtifacts() { - return publishedArtifacts; + if (collect == null) { + this.descriptors.removeAll(temp); + collect = artifactsToPublish.entrySet().stream().map(entry -> { + File outputFile = entry.getKey(); + IArtifactKey artifactKey = entry.getValue(); + IArtifactDescriptor artifactDescriptor = PublisherHelper.createArtifactDescriptor(publisherInfo, + artifactKey, outputFile); + Collection advices = publisherInfo.getAdvice(null, false, + artifactDescriptor.getArtifactKey().getId(), artifactDescriptor.getArtifactKey().getVersion(), + IPropertyAdvice.class); + + for (IPropertyAdvice advice : advices) { + if (advice instanceof MavenPropertiesAdvice) { + advice.getArtifactProperties(null, artifactDescriptor); + } + } + String mavenArtifactClassifier = getRootFileArtifactClassifier( + artifactDescriptor.getArtifactKey().getId()); + if (artifactDescriptor instanceof ArtifactDescriptor impl) { + impl.setProperty(TychoConstants.PROP_CLASSIFIER, mavenArtifactClassifier); + //Type and extension are the same for rootfiles ... + impl.setProperty(PROP_EXTENSION, TychoConstants.ROOTFILE_EXTENSION); + impl.setProperty(TychoConstants.PROP_TYPE, TychoConstants.ROOTFILE_EXTENSION); + } + addDescriptor(artifactDescriptor); + return Map.entry(mavenArtifactClassifier, + new P2Artifact(outputFile, Collections. emptySet(), artifactDescriptor)); + }).collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + } + return collect; } } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactRepository.java b/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactRepository.java index fad2fa4a2f..cefcb70d1a 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactRepository.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/p2/repository/module/ModuleArtifactRepository.java @@ -212,7 +212,7 @@ public IArtifactSink newAddingArtifactSink(IArtifactKey key, WriteSessionContext @Override protected void internalStore(IProgressMonitor monitor) { try { - internalStoreWithException(); + saveToDisk(); } catch (IOException e) { String message = "Error while writing repository to " + p2DataFile; // TODO 393004 Use a specific type? @@ -222,7 +222,7 @@ protected void internalStore(IProgressMonitor monitor) { private void storeOrProvisioningException() throws ProvisionException { try { - internalStoreWithException(); + saveToDisk(); } catch (IOException e) { String message = "Error while writing repository to " + p2DataFile; int code = ProvisionException.REPOSITORY_FAILED_WRITE; @@ -231,7 +231,7 @@ private void storeOrProvisioningException() throws ProvisionException { } } - private void internalStoreWithException() throws IOException { + public void saveToDisk() throws IOException { ArtifactsIO io = new ArtifactsIO(); io.writeXML(flattenedValues().collect(toSet()), p2DataFile); } diff --git a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/FeatureRootfileArtifactRepositoryTest.java b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/FeatureRootfileArtifactRepositoryTest.java index 06a8608ae1..167717a83b 100644 --- a/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/FeatureRootfileArtifactRepositoryTest.java +++ b/tycho-core/src/test/java/org/eclipse/tycho/p2resolver/FeatureRootfileArtifactRepositoryTest.java @@ -107,11 +107,11 @@ public void testRepoWithInitEmptyAttachedArtifacts() { } private void assertMavenProperties(IArtifactDescriptor descriptor, String root) { - Assert.assertEquals(descriptor.getProperty("maven-groupId"), "artifactGroupId"); - Assert.assertEquals(descriptor.getProperty("maven-artifactId"), "artifactId"); - Assert.assertEquals(descriptor.getProperty("maven-version"), "artifactVersion"); - Assert.assertEquals(descriptor.getProperty("maven-classifier"), root); - Assert.assertEquals(descriptor.getProperty("maven-extension"), "zip"); + Assert.assertEquals("artifactGroupId", descriptor.getProperty("maven-groupId")); + Assert.assertEquals("artifactId", descriptor.getProperty("maven-artifactId")); + Assert.assertEquals("artifactVersion", descriptor.getProperty("maven-version")); + Assert.assertEquals(root, descriptor.getProperty("maven-classifier")); + Assert.assertEquals("zip", descriptor.getProperty("maven-extension")); } private void assertAttachedArtifact(Map attachedArtifacts, int expectedSize, diff --git a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java index 38802be410..2a509f0d5f 100644 --- a/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java +++ b/tycho-p2-publisher-plugin/src/main/java/org/eclipse/tycho/plugins/p2/publisher/PublishProductMojo.java @@ -14,9 +14,17 @@ package org.eclipse.tycho.plugins.p2.publisher; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Collection; +import java.util.HexFormat; +import java.util.Iterator; import java.util.List; import org.apache.maven.plugin.MojoExecutionException; @@ -24,6 +32,12 @@ import org.apache.maven.plugins.annotations.*; import org.codehaus.plexus.archiver.ArchiverException; import org.codehaus.plexus.archiver.UnArchiver; +import org.eclipse.equinox.p2.metadata.expression.IExpression; +import org.eclipse.equinox.p2.query.Collector; +import org.eclipse.equinox.p2.query.IQuery; +import org.eclipse.equinox.p2.query.IQueryResult; +import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; +import org.eclipse.equinox.p2.repository.artifact.IFileArtifactRepository; import org.eclipse.tycho.ArtifactDescriptor; import org.eclipse.tycho.ArtifactType; import org.eclipse.tycho.DependencyArtifacts; @@ -33,14 +47,18 @@ import org.eclipse.tycho.PackagingType; import org.eclipse.tycho.PlatformPropertiesUtils; import org.eclipse.tycho.TargetEnvironment; +import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.core.TychoProject; import org.eclipse.tycho.core.maven.TychoInterpolator; import org.eclipse.tycho.core.osgitools.EclipseRepositoryProject; import org.eclipse.tycho.core.resolver.shared.DependencySeed; import org.eclipse.tycho.core.utils.TychoProjectUtils; import org.eclipse.tycho.model.ProductConfiguration; +import org.eclipse.tycho.p2.repository.PublishingRepository; +import org.eclipse.tycho.p2.repository.module.ModuleArtifactRepository; import org.eclipse.tycho.p2.tools.publisher.facade.PublishProductTool; import org.eclipse.tycho.p2.tools.publisher.facade.PublisherServiceFactory; +import org.eclipse.tycho.repository.registry.facade.ReactorRepositoryManager; import org.osgi.framework.Version; /** @@ -71,6 +89,9 @@ public final class PublishProductMojo extends AbstractPublishMojo { @Component(role = TychoProject.class, hint = PackagingType.TYPE_ECLIPSE_REPOSITORY) private EclipseRepositoryProject eclipseRepositoryProject; + @Component + private ReactorRepositoryManager reactorRepoManager; + /** * The directory where .product files are located. *

@@ -87,6 +108,7 @@ protected Collection publishContent(PublisherServiceFactory publ getEnvironments(), getQualifier(), interpolator); List seeds = new ArrayList<>(); + boolean hasLaunchers = false; for (final File productFile : eclipseRepositoryProject.getProductFiles(productsDirectory)) { try { ProductConfiguration productConfiguration = ProductConfiguration.read(productFile); @@ -98,16 +120,75 @@ protected Collection publishContent(PublisherServiceFactory publ + " does not contain the mandatory attribute 'version'. Please ensure you entered a version in the product file."); } + boolean includeLaunchers = productConfiguration.includeLaunchers(); seeds.addAll(publisher.publishProduct(productFile, - productConfiguration.includeLaunchers() ? getExpandedLauncherBinaries() : null, FLAVOR)); + includeLaunchers ? getExpandedLauncherBinaries() : null, FLAVOR)); + hasLaunchers |= includeLaunchers; } catch (IOException e) { throw new MojoExecutionException( "I/O exception while writing product definition or copying launcher icons", e); } } + if (hasLaunchers) { + //We must calculate checksums! + File artifactsXml = new File(getProject().getBuild().getDirectory(), TychoConstants.FILE_NAME_P2_ARTIFACTS); + if (artifactsXml.isFile()) { + PublishingRepository publishingRepository = reactorRepoManager + .getPublishingRepository(getReactorProject().getIdentities()); + IFileArtifactRepository repository = publishingRepository.getArtifactRepository(); + repository.descriptorQueryable().query(new IQuery() { + + @Override + public IQueryResult perform(Iterator iterator) { + while (iterator.hasNext()) { + IArtifactDescriptor descriptor = (IArtifactDescriptor) iterator.next(); + File artifactFile = repository.getArtifactFile(descriptor); + if (artifactFile != null) { + try { + String digest = digest(artifactFile); + updateCheckSum(descriptor, digest); + } catch (NoSuchAlgorithmException e) { + } catch (IOException e) { + } + } + } + return new Collector(); + } + + @Override + public IExpression getExpression() { + return null; + } + }, null); + if (repository instanceof ModuleArtifactRepository module) { + try { + module.saveToDisk(); + } catch (IOException e) { + } + } + } + } return seeds; } + private void updateCheckSum(IArtifactDescriptor descriptor, String digest) { + if (descriptor instanceof org.eclipse.equinox.p2.repository.artifact.spi.ArtifactDescriptor arti) { + arti.setProperty("download.checksum.sha-256", digest); + } + } + + private String digest(File artifactFile) throws IOException, NoSuchAlgorithmException { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + try (InputStream stream = new DigestInputStream(new FileInputStream(artifactFile), md)) { + try { + stream.transferTo(OutputStream.nullOutputStream()); + } finally { + stream.close(); + } + } + return HexFormat.of().formatHex(md.digest()); + } + private File getExpandedLauncherBinaries() throws MojoExecutionException, MojoFailureException { // TODO 364134 take the executable feature from the target platform instead DependencyArtifacts dependencyArtifacts = TychoProjectUtils.getDependencyArtifacts(getReactorProject()); @@ -119,7 +200,7 @@ private File getExpandedLauncherBinaries() throws MojoExecutionException, MojoFa "Unable to locate feature 'org.eclipse.equinox.executable'. This feature is required for native product launchers."); } checkMacOSLauncherCompatibility(artifact); - File equinoxExecFeature = artifact.getLocation(true); + File equinoxExecFeature = artifact.fetchArtifact().join(); if (equinoxExecFeature.isDirectory()) { return equinoxExecFeature.getAbsoluteFile(); } else {