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 8479ea335a..242a837210 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; @@ -28,6 +36,12 @@ import org.apache.maven.plugins.annotations.ResolutionScope; 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; @@ -36,14 +50,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; /** @@ -74,6 +92,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. *

@@ -90,6 +111,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); @@ -101,16 +123,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()); @@ -122,7 +203,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 {