Skip to content

Commit

Permalink
Generates the P2Artifact after binary is published
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
laeubi committed Dec 5, 2023
1 parent 45dd65a commit 90db4f4
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,26 @@

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;
import org.eclipse.equinox.p2.publisher.IPublisherAdvice;
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;
Expand All @@ -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<String, IP2Artifact> publishedArtifacts = new HashMap<>();
private Map<File, IArtifactKey> artifactsToPublish = new HashMap<>();

private Map<String, IP2Artifact> collect;

private List<IArtifactDescriptor> temp = new ArrayList<>();

public FeatureRootfileArtifactRepository(PublisherInfo publisherInfo, File outputDirectory) {
this.publisherInfo = publisherInfo;
Expand All @@ -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);
}
Expand All @@ -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<IPropertyAdvice> 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.<IInstallableUnit> emptySet(), simpleArtifactDescriptor));

descriptors.add(simpleArtifactDescriptor);
} catch (FileNotFoundException e) {
throw new ProvisionException(e.getMessage(), e);
}

return target;
}

String getRootFileArtifactClassifier(String artifactId) {
List<IPublisherAdvice> adviceList = this.publisherInfo.getAdvice();

Expand All @@ -131,6 +105,35 @@ String getRootFileArtifactClassifier(String artifactId) {
}

public Map<String, IP2Artifact> 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<IPropertyAdvice> 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.<IInstallableUnit> emptySet(), artifactDescriptor));
}).collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
return collect;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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;
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, IP2Artifact> attachedArtifacts, int expectedSize,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,30 @@
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;
import org.apache.maven.plugin.MojoFailureException;
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;
Expand All @@ -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;

/**
Expand Down Expand Up @@ -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 <code>.product</code> files are located.
* <p>
Expand All @@ -87,6 +108,7 @@ protected Collection<DependencySeed> publishContent(PublisherServiceFactory publ
getEnvironments(), getQualifier(), interpolator);

List<DependencySeed> seeds = new ArrayList<>();
boolean hasLaunchers = false;
for (final File productFile : eclipseRepositoryProject.getProductFiles(productsDirectory)) {
try {
ProductConfiguration productConfiguration = ProductConfiguration.read(productFile);
Expand All @@ -98,16 +120,75 @@ protected Collection<DependencySeed> 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<IArtifactDescriptor>() {

@Override
public IQueryResult<IArtifactDescriptor> perform(Iterator<IArtifactDescriptor> 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<IArtifactDescriptor>();
}

@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());
Expand All @@ -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 {
Expand Down

0 comments on commit 90db4f4

Please sign in to comment.