Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Fedorenko2012-06-13 11:31:35 -0400
committerIgor Fedorenko2012-06-30 16:27:34 -0400
commit67bd28c28fcf3a26c167cf3f6893387d96c35336 (patch)
tree2572ec02486479d26942e79bc3ed99f0626e447f
parent6a03d3275de753ceccaa97a7cb9fc764f93c91e6 (diff)
downloadorg.eclipse.tycho-67bd28c28fcf3a26c167cf3f6893387d96c35336.tar.gz
org.eclipse.tycho-67bd28c28fcf3a26c167cf3f6893387d96c35336.tar.xz
org.eclipse.tycho-67bd28c28fcf3a26c167cf3f6893387d96c35336.zip
362883 enforce same p2 id/version means same artifact contents
Compare build artifacts with configured baseline repository(ies). In strict mode (the default) fail the build if artifacts have same id/version but different contents. In non-strict mode, replace build artifact with baseline version. Implementation provides content-specific comparators for zip/jar files, bundle manifest files and properties files. Change-Id: I974f8f9e2072c03422663502e7b9fc813fdd31ab Signed-off-by: Igor Fedorenko <igor@ifedorenko.com>
-rw-r--r--pom.xml1
-rw-r--r--tycho-artifactcomparator/pom.xml57
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactComparator.java18
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactDelta.java29
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ClassfileComparator.java67
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/CompoundArtifactDelta.java59
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ContentsComparator.java20
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/DefaultContentsComparator.java29
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ManifestComparator.java104
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/NestedZipComparator.java49
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/PropertiesComparator.java68
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/SimpleArtifactDelta.java33
-rw-r--r--tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ZipComparatorImpl.java151
-rw-r--r--tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/testdata/JavaClass.java26
-rw-r--r--tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/CompoundArtifactDeltaTest.java52
-rw-r--r--tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/ContentsComparatorTest.java66
-rw-r--r--tycho-artifactcomparator/src/test/resources/manifest/MANIFEST.MF9
-rw-r--r--tycho-artifactcomparator/src/test/resources/manifest/MANIFEST2.MF9
-rw-r--r--tycho-artifactcomparator/src/test/resources/manifest/MANIFEST3.MF8
-rw-r--r--tycho-artifactcomparator/src/test/resources/properties/props.properties2
-rw-r--r--tycho-artifactcomparator/src/test/resources/properties/props2.properties7
-rw-r--r--tycho-artifactcomparator/src/test/resources/properties/props3.properties2
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/P2Artifact.java6
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteMetadataRepositoryManager.java3
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.tools.impl/META-INF/MANIFEST.MF8
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.tools.impl/OSGI-INF/baseline.xml8
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.tools.impl/src/main/java/org/eclipse/tycho/p2/tools/baseline/BaselineServiceImpl.java198
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.tools.shared/META-INF/MANIFEST.MF4
-rw-r--r--tycho-bundles/org.eclipse.tycho.p2.tools.shared/src/main/java/org/eclipse/tycho/p2/tools/baseline/facade/BaselineService.java27
-rw-r--r--tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoP2RuntimeLocator.java3
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/artifacts.jarbin0 -> 828 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/binary/baseline.feature01_root_1.0.0.1bin0 -> 140 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/content.jarbin0 -> 2919 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature01_1.0.0.1.jarbin0 -> 1904 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature02_1.0.0.1.jarbin0 -> 1822 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01.source_1.0.0.1.jarbin0 -> 1313 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01_1.0.0.1.jarbin0 -> 2323 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle02_1.0.0.1.jarbin0 -> 2217 bytes
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle01/META-INF/MANIFEST.MF4
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle01/build.properties4
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle01/pom.xml32
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle01/src/baseline/bundle01/Bundle01Class.java6
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle02/META-INF/MANIFEST.MF4
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle02/build.properties4
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle02/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/bundle02/src/baseline/bundle01/Bundle01Class.java6
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature01/build.properties2
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature01/feature.xml27
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature01/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature01/rootfile.txt0
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature02/build.properties1
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature02/feature.xml6
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/feature02/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/pom.xml79
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/repository/baseline.repository.product11
-rw-r--r--tycho-its/projects/362883_baseline/baseline/src/repository/pom.xml15
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/META-INF/MANIFEST.MF4
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/build.properties5
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/pom.xml32
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/source.txt0
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/src/baseline/bundle01/Bundle01Class.java6
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/pom.xml81
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/repository/baseline.repository.product11
-rw-r--r--tycho-its/projects/362883_baseline/changedattachedartifact/repository/pom.xml15
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/bundle01/META-INF/MANIFEST.MF4
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/bundle01/build.properties4
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/bundle01/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/bundle01/src/baseline/bundle01/Bundle01Class.java6
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/feature02/build.properties1
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/feature02/feature.xml13
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/feature02/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/pom.xml84
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/repository/baseline.repository.product10
-rw-r--r--tycho-its/projects/362883_baseline/contentchanged/repository/pom.xml15
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/feature02/build.properties2
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/feature02/feature.xml6
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/feature02/pom.xml14
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/feature02/rootfile.txt0
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/pom.xml81
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/repository/baseline.repository.product10
-rw-r--r--tycho-its/projects/362883_baseline/newattachedartifact/repository/pom.xml15
-rw-r--r--tycho-its/src/test/java/org/eclipse/tycho/test/bug362883_baseline/BaselineValidateAndReplaceTest.java160
-rw-r--r--tycho-p2/tycho-p2-plugin/pom.xml14
-rw-r--r--tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/BaselineValidator.java201
-rw-r--r--tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/P2MetadataMojo.java48
-rw-r--r--tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/Repository.java38
86 files changed, 2256 insertions, 18 deletions
diff --git a/pom.xml b/pom.xml
index c5eddaf44..75c43dc95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -487,6 +487,7 @@ $CMD -DpomFile=org.eclipse.jdt.compiler.apt.pom \
<!-- tycho-p2 -->
<module>tycho-p2</module>
+ <module>tycho-artifactcomparator</module>
</modules>
<profiles>
diff --git a/tycho-artifactcomparator/pom.xml b/tycho-artifactcomparator/pom.xml
new file mode 100644
index 000000000..775107a47
--- /dev/null
+++ b/tycho-artifactcomparator/pom.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ - Copyright (c) 2012 Sonatype Inc. and others.
+ - All rights reserved. This program and the accompanying materials
+ - are made available under the terms of the Eclipse Public License v1.0
+ - which accompanies this distribution, and is available at
+ - http://www.eclipse.org/legal/epl-v10.html
+ -
+ - Contributors:
+ - Sonatype Inc. - initial API and implementation
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho</artifactId>
+ <version>0.16.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>tycho-artifactcomparator</artifactId>
+
+ <dependencies>
+ <dependency>
+ <!-- CQ https://dev.eclipse.org/ipzilla/show_bug.cgi?id=6591 -->
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm-debug-all</artifactId>
+ <version>4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-component-annotations</artifactId>
+ </dependency>
+
+ <dependency>
+ <artifactId>junit</artifactId>
+ <groupId>junit</groupId>
+ </dependency>
+ <dependency>
+ <groupId>org.sonatype.sisu</groupId>
+ <artifactId>sisu-inject-plexus</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-component-metadata</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactComparator.java
new file mode 100644
index 000000000..e1b749ade
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactComparator.java
@@ -0,0 +1,18 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.artifactcomparator;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface ArtifactComparator {
+ public ArtifactDelta getDelta(File baseline, File reactor) throws IOException;
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactDelta.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactDelta.java
new file mode 100644
index 000000000..0f818dd58
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/artifactcomparator/ArtifactDelta.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.artifactcomparator;
+
+
+/**
+ * Represents both simple and compound artifact delta.
+ */
+public interface ArtifactDelta {
+
+ /**
+ * @return description of the delta, never null.
+ */
+ public String getMessage();
+
+ /**
+ * @return detailed description of the delta, never null.
+ */
+ public String getDetailedMessage();
+
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ClassfileComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ClassfileComparator.java
new file mode 100644
index 000000000..d11103285
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ClassfileComparator.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.util.IOUtil;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.util.TraceClassVisitor;
+
+@Component(role = ContentsComparator.class, hint = ClassfileComparator.TYPE)
+public class ClassfileComparator implements ContentsComparator {
+ public static final String TYPE = "class";
+
+ // there are two alternative ways to compare classfiels
+ // JDT ClassFileBytesDisassembler, but it depends on workbench, so out of question
+ // P2 JarComparator... which is a fork (yes, a fork) of JDT ClassFileBytesDisassembler,
+ // which is not exported, so can't use this either.
+
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException {
+ byte[] bytes = IOUtil.toByteArray(baseline);
+ byte[] bytes2 = IOUtil.toByteArray(reactor);
+
+ boolean equal;
+ try {
+ String disassemble = disassemble(bytes);
+ String disassemble2 = disassemble(bytes2);
+ equal = disassemble.equals(disassemble2);
+ } catch (IllegalArgumentException e) {
+ // asm could not disassemble one of the classes, fallback to byte-to-byte comparison
+ equal = Arrays.equals(bytes, bytes2);
+ }
+
+ return !equal ? new SimpleArtifactDelta("different") : null;
+ }
+
+ private String disassemble(byte[] bytes) {
+ ClassReader reader = new ClassReader(bytes);
+ ClassNode clazz = new ClassNode();
+ reader.accept(clazz, Opcodes.ASM4 | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+
+ // rendering human-readable bytecode is an eyecandy, we can compare ClassNodes directly
+
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ clazz.accept(new TraceClassVisitor(writer));
+ writer.flush();
+ writer.close();
+ return buffer.toString();
+ }
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/CompoundArtifactDelta.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/CompoundArtifactDelta.java
new file mode 100644
index 000000000..fea8f7a92
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/CompoundArtifactDelta.java
@@ -0,0 +1,59 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+public class CompoundArtifactDelta extends SimpleArtifactDelta {
+
+ private final Map<String, ArtifactDelta> members;
+
+ public CompoundArtifactDelta(String message, Map<String, ArtifactDelta> members) {
+ super(message);
+ if (members == null || members.isEmpty()) {
+ throw new IllegalArgumentException();
+ }
+ this.members = Collections.unmodifiableMap(new LinkedHashMap<String, ArtifactDelta>(members));
+ }
+
+ public Map<String, ArtifactDelta> getMembers() {
+ return members;
+ }
+
+ @Override
+ public String getDetailedMessage() {
+ StringBuilder message = new StringBuilder(getMessage()).append("\n");
+ appendDetailedMessage(message, 1);
+ return message.toString();
+ }
+
+ protected void appendDetailedMessage(StringBuilder message, int indent) {
+ for (Map.Entry<String, ArtifactDelta> member : members.entrySet()) {
+ indent(message, indent);
+ message.append(member.getKey()).append(": ").append(member.getValue().getMessage());
+ message.append("\n");
+
+ if (member.getValue() instanceof CompoundArtifactDelta) {
+ ((CompoundArtifactDelta) member.getValue()).appendDetailedMessage(message, indent + 1);
+ }
+ }
+ }
+
+ private void indent(StringBuilder message, int indent) {
+ for (int i = 0; i < indent; i++) {
+ message.append(" ");
+ }
+ }
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ContentsComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ContentsComparator.java
new file mode 100644
index 000000000..6ba6b2de5
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ContentsComparator.java
@@ -0,0 +1,20 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+public interface ContentsComparator {
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException;
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/DefaultContentsComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/DefaultContentsComparator.java
new file mode 100644
index 000000000..fb076900b
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/DefaultContentsComparator.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.util.IOUtil;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+@Component(role = ContentsComparator.class, hint = DefaultContentsComparator.TYPE)
+public class DefaultContentsComparator implements ContentsComparator {
+
+ public static final String TYPE = "default";
+
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException {
+ return !IOUtil.contentEquals(baseline, reactor) ? new SimpleArtifactDelta("different") : null;
+ }
+
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ManifestComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ManifestComparator.java
new file mode 100644
index 000000000..bbf6e8805
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ManifestComparator.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.util.jar.Manifest;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+@Component(role = ContentsComparator.class, hint = ManifestComparator.TYPE)
+public class ManifestComparator implements ContentsComparator {
+
+ public static final String TYPE = "manifest";
+
+ private static final Collection<Name> IGNORED_KEYS;
+
+ static {
+ ArrayList<Name> ignoredKeys = new ArrayList<Name>();
+
+ // these keys are added by plexus archiver
+ ignoredKeys.add(new Name("Archiver-Version"));
+ ignoredKeys.add(new Name("Created-By"));
+ ignoredKeys.add(new Name("Build-Jdk"));
+ ignoredKeys.add(new Name("Built-By"));
+
+ // lets be friendly to bnd/maven-bundle-plugin
+ ignoredKeys.add(new Name("Bnd-LastModified"));
+ ignoredKeys.add(new Name("Tool"));
+
+ // this is common attribute not supported by Tycho yet
+ ignoredKeys.add(new Name("Eclipse-SourceReferences"));
+
+ // TODO make to possible to disable default ignores and add custom ignore
+
+ IGNORED_KEYS = Collections.unmodifiableCollection(ignoredKeys);
+ }
+
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException {
+ TreeMap<String, ArtifactDelta> result = new TreeMap<String, ArtifactDelta>();
+
+ Manifest manifest = new Manifest(baseline);
+ Manifest manifest2 = new Manifest(reactor);
+
+ Attributes attributes = manifest.getMainAttributes();
+ Attributes attributes2 = manifest2.getMainAttributes();
+
+ Set<Name> names = new LinkedHashSet<Name>();
+ names.addAll(getNames(attributes));
+ names.addAll(getNames(attributes2));
+
+ for (Name key : names) {
+ String value = attributes.getValue(key);
+ if (value == null) {
+ addDelta(result, key, "not present in baseline version");
+ continue;
+ }
+
+ String value2 = attributes2.getValue(key);
+ if (value2 == null) {
+ addDelta(result, key, "present in baseline version only");
+ continue;
+ }
+
+ if (value != null ? !value.equals(value2) : value2 != null) {
+ addDelta(result, key, "baseline='" + value + "' != reactor='" + value2 + "'");
+ }
+ }
+
+ return !result.isEmpty() ? new CompoundArtifactDelta("different", result) : null;
+ }
+
+ protected void addDelta(TreeMap<String, ArtifactDelta> result, Name key, String message) {
+ result.put(key.toString(), new SimpleArtifactDelta(message));
+ }
+
+ protected Set<Name> getNames(Attributes attributes) {
+ Set<Name> result = new LinkedHashSet<Name>();
+ for (Object key : attributes.keySet()) {
+ Name name = (Name) key;
+ if (!IGNORED_KEYS.contains(name)) {
+ result.add(name);
+ }
+ }
+ return result;
+ }
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/NestedZipComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/NestedZipComparator.java
new file mode 100644
index 000000000..079045a68
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/NestedZipComparator.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.util.FileUtils;
+import org.codehaus.plexus.util.io.RawInputStreamFacade;
+import org.eclipse.tycho.artifactcomparator.ArtifactComparator;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+@Component(role = ContentsComparator.class, hint = NestedZipComparator.TYPE)
+public class NestedZipComparator implements ContentsComparator {
+ public static final String TYPE = "zip";
+
+ @Requirement(hint = ZipComparatorImpl.TYPE)
+ private ArtifactComparator zipComparator;
+
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException {
+ File zip = File.createTempFile("zip", ".zip");
+ try {
+ FileUtils.copyStreamToFile(new RawInputStreamFacade(baseline), zip);
+
+ File zip2 = File.createTempFile("zip2", ".zip");
+ try {
+ FileUtils.copyStreamToFile(new RawInputStreamFacade(reactor), zip2);
+ return zipComparator.getDelta(zip, zip2);
+ } finally {
+ zip2.delete();
+ }
+
+ } finally {
+ zip.delete();
+ }
+ }
+
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/PropertiesComparator.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/PropertiesComparator.java
new file mode 100644
index 000000000..20eaf3b1c
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/PropertiesComparator.java
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+@Component(role = ContentsComparator.class, hint = PropertiesComparator.TYPE)
+public class PropertiesComparator implements ContentsComparator {
+ public static final String TYPE = "properties";
+
+ public ArtifactDelta getDelta(InputStream baseline, InputStream reactor) throws IOException {
+ TreeMap<String, ArtifactDelta> result = new TreeMap<String, ArtifactDelta>();
+
+ Properties props = new Properties();
+ props.load(baseline);
+ Properties props2 = new Properties();
+ props2.load(reactor);
+
+ Set<String> names = new LinkedHashSet<String>();
+ addAll(names, props);
+ addAll(names, props2);
+
+ for (String name : names) {
+ String value = props.getProperty(name);
+ if (value == null) {
+ result.put(name, new SimpleArtifactDelta("not present in baseline version"));
+ continue;
+ }
+
+ String value2 = props2.getProperty(name);
+ if (value2 == null) {
+ result.put(name, new SimpleArtifactDelta("present in baseline version only"));
+ continue;
+ }
+
+ if (value != null ? !value.equals(value2) : value2 != null) {
+ result.put(name, new SimpleArtifactDelta("baseline='" + value + "' != reactor='" + value2 + "'"));
+ }
+ }
+
+ return !result.isEmpty() ? new CompoundArtifactDelta("properties files differ", result) : null;
+ }
+
+ private void addAll(Set<String> names, Properties props) {
+ for (Object key : props.keySet()) {
+ Object value = props.get(key);
+ if (key instanceof String && value instanceof String) {
+ names.add((String) key);
+ }
+ }
+ }
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/SimpleArtifactDelta.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/SimpleArtifactDelta.java
new file mode 100644
index 000000000..69a249ce8
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/SimpleArtifactDelta.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+public class SimpleArtifactDelta implements ArtifactDelta {
+
+ private final String message;
+
+ public SimpleArtifactDelta(String message) {
+ if (message == null) {
+ throw new IllegalArgumentException();
+ }
+ this.message = message;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getDetailedMessage() {
+ return message;
+ }
+}
diff --git a/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ZipComparatorImpl.java b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ZipComparatorImpl.java
new file mode 100644
index 000000000..e7e42f848
--- /dev/null
+++ b/tycho-artifactcomparator/src/main/java/org/eclipse/tycho/zipcomparator/internal/ZipComparatorImpl.java
@@ -0,0 +1,151 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.zipcomparator.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.SelectorUtils;
+import org.eclipse.tycho.artifactcomparator.ArtifactComparator;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+
+@Component(role = ArtifactComparator.class, hint = ZipComparatorImpl.TYPE)
+public class ZipComparatorImpl implements ArtifactComparator {
+
+ public static final String TYPE = "zip";
+
+ private static final Collection<String> IGNORED_PATTERNS;
+
+ static {
+ ArrayList<String> ignoredPatterns = new ArrayList<String>();
+
+ ignoredPatterns.add("meta-inf/maven/**");
+
+ IGNORED_PATTERNS = Collections.unmodifiableList(ignoredPatterns);
+ }
+
+ @Requirement
+ private Map<String, ContentsComparator> comparators;
+
+ public ArtifactDelta getDelta(File baseline, File reactor) throws IOException {
+ Map<String, ArtifactDelta> result = new LinkedHashMap<String, ArtifactDelta>();
+
+ ZipFile jar = new ZipFile(baseline);
+ try {
+ ZipFile jar2 = new ZipFile(reactor);
+ try {
+ Map<String, ZipEntry> entries = toEntryMap(jar);
+ Map<String, ZipEntry> entries2 = toEntryMap(jar2);
+
+ Set<String> names = new TreeSet<String>();
+ names.addAll(entries.keySet());
+ names.addAll(entries2.keySet());
+
+ for (String name : names) {
+ ZipEntry entry = entries.get(name);
+ if (entry == null) {
+ result.put(name, new SimpleArtifactDelta("not present in baseline"));
+ continue;
+ }
+ ZipEntry entry2 = entries2.get(name);
+ if (entry2 == null) {
+ result.put(name, new SimpleArtifactDelta("present in baseline only"));
+ continue;
+ }
+
+ InputStream is = jar.getInputStream(entry);
+ try {
+ InputStream is2 = jar2.getInputStream(entry2);
+ try {
+ ContentsComparator comparator = comparators.get(getContentType(name));
+ ArtifactDelta differences = comparator.getDelta(is, is2);
+ if (differences != null) {
+ result.put(name, differences);
+ continue;
+ }
+ } finally {
+ IOUtil.close(is2);
+ }
+ } finally {
+ IOUtil.close(is);
+ }
+ }
+ } finally {
+ try {
+ jar2.close();
+ } catch (IOException e) {
+ // too bad
+ }
+ }
+ } finally {
+ try {
+ jar.close();
+ } catch (IOException e) {
+ // ouch
+ }
+ }
+ return !result.isEmpty() ? new CompoundArtifactDelta("different", result) : null;
+ }
+
+ private String getContentType(String name) {
+ name = name.toLowerCase(Locale.ENGLISH);
+ if (name.endsWith(".class")) {
+ return ClassfileComparator.TYPE;
+ }
+ if (name.endsWith(".jar") || name.endsWith(".zip")) {
+ return NestedZipComparator.TYPE;
+ }
+ if (name.endsWith(".properties") || name.endsWith(".mappings")) {
+ // .mapping comes from org.eclipse.equinox.p2.internal.repository.comparator.JarComparator
+ return PropertiesComparator.TYPE;
+ }
+ if ("meta-inf/manifest.mf".equals(name)) {
+ return ManifestComparator.TYPE;
+ }
+ return DefaultContentsComparator.TYPE;
+ }
+
+ private Map<String, ZipEntry> toEntryMap(ZipFile zip) {
+ Map<String, ZipEntry> result = new LinkedHashMap<String, ZipEntry>();
+ Enumeration<? extends ZipEntry> entries = zip.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (!entry.isDirectory() && !isIgnored(entry.getName())) {
+ result.put(entry.getName(), entry);
+ }
+ }
+ return result;
+ }
+
+ private boolean isIgnored(String name) {
+ for (String pattern : IGNORED_PATTERNS) {
+ if (SelectorUtils.matchPath(pattern, name, false)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/testdata/JavaClass.java b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/testdata/JavaClass.java
new file mode 100644
index 000000000..a9e0e1679
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/testdata/JavaClass.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.jarcomparator.testdata;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.codehaus.plexus.component.annotations.Component;
+
+@Component(role = JavaClass.class)
+public class JavaClass {
+ public Set<String> getStrings() throws IOException {
+ return new LinkedHashSet<String>() {
+ private static final long serialVersionUID = 711052831237148688L;
+ };
+ }
+}
diff --git a/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/CompoundArtifactDeltaTest.java b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/CompoundArtifactDeltaTest.java
new file mode 100644
index 000000000..c929eb135
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/CompoundArtifactDeltaTest.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.jarcomparator.tests;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import junit.framework.Assert;
+
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+import org.eclipse.tycho.zipcomparator.internal.CompoundArtifactDelta;
+import org.eclipse.tycho.zipcomparator.internal.SimpleArtifactDelta;
+import org.junit.Test;
+
+public class CompoundArtifactDeltaTest {
+
+ @Test
+ public void testGetDetailedMessage() {
+ Map<String, ArtifactDelta> manifest = new TreeMap<String, ArtifactDelta>();
+ manifest.put("name1", new SimpleArtifactDelta("present in baseline only"));
+
+ Map<String, ArtifactDelta> main = new TreeMap<String, ArtifactDelta>();
+ main.put("path/file1", new SimpleArtifactDelta("different"));
+ main.put("path/file2", new SimpleArtifactDelta("not present in baseline"));
+ main.put("META-INF/MANIFEST.MF", new CompoundArtifactDelta("different", manifest));
+
+ Map<String, ArtifactDelta> delta = new TreeMap<String, ArtifactDelta>();
+ delta.put("<main>", new CompoundArtifactDelta("different", main));
+ delta.put("sources", new SimpleArtifactDelta("different"));
+
+ ArtifactDelta subject = new CompoundArtifactDelta(
+ "Baseline and reactor artifacts have the same version but different contents", delta);
+
+ String expected = "Baseline and reactor artifacts have the same version but different contents\n" //
+ + " <main>: different\n" //
+ + " META-INF/MANIFEST.MF: different\n" //
+ + " name1: present in baseline only\n" //
+ + " path/file1: different\n" //
+ + " path/file2: not present in baseline\n" //
+ + " sources: different\n";
+
+ Assert.assertEquals(expected, subject.getDetailedMessage());
+ }
+}
diff --git a/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/ContentsComparatorTest.java b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/ContentsComparatorTest.java
new file mode 100644
index 000000000..10ce423a5
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/java/org/eclipse/tycho/jarcomparator/tests/ContentsComparatorTest.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.jarcomparator.tests;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+import org.codehaus.plexus.PlexusTestCase;
+import org.codehaus.plexus.util.IOUtil;
+import org.eclipse.tycho.zipcomparator.internal.ClassfileComparator;
+import org.eclipse.tycho.zipcomparator.internal.ContentsComparator;
+import org.eclipse.tycho.zipcomparator.internal.ManifestComparator;
+import org.eclipse.tycho.zipcomparator.internal.PropertiesComparator;
+import org.junit.Assert;
+
+public class ContentsComparatorTest extends PlexusTestCase {
+ public void testManifest() throws Exception {
+ Assert.assertTrue(isContentEqual(ManifestComparator.TYPE, "src/test/resources/manifest/MANIFEST.MF",
+ "src/test/resources/manifest/MANIFEST.MF"));
+ Assert.assertTrue(isContentEqual(ManifestComparator.TYPE, "src/test/resources/manifest/MANIFEST.MF",
+ "src/test/resources/manifest/MANIFEST2.MF"));
+ Assert.assertFalse(isContentEqual(ManifestComparator.TYPE, "src/test/resources/manifest/MANIFEST.MF",
+ "src/test/resources/manifest/MANIFEST3.MF"));
+ }
+
+ public void testClassfile() throws Exception {
+ Assert.assertTrue(isContentEqual(ClassfileComparator.TYPE,
+ "target/test-classes/org/eclipse/tycho/jarcomparator/testdata/JavaClass.class",
+ "target/test-classes/org/eclipse/tycho/jarcomparator/testdata/JavaClass.class"));
+ Assert.assertFalse(isContentEqual(ClassfileComparator.TYPE,
+ "target/test-classes/org/eclipse/tycho/jarcomparator/testdata/JavaClass.class",
+ "target/test-classes/org/eclipse/tycho/jarcomparator/testdata/JavaClass$1.class"));
+ }
+
+ public void testProperties() throws Exception {
+ Assert.assertTrue(isContentEqual(PropertiesComparator.TYPE, "src/test/resources/properties/props.properties",
+ "src/test/resources/properties/props.properties"));
+ Assert.assertTrue(isContentEqual(PropertiesComparator.TYPE, "src/test/resources/properties/props.properties",
+ "src/test/resources/properties/props2.properties"));
+ Assert.assertFalse(isContentEqual(PropertiesComparator.TYPE, "src/test/resources/properties/props.properties",
+ "src/test/resources/properties/props3.properties"));
+ }
+
+ private boolean isContentEqual(String type, String baseline, String reactor) throws Exception {
+ ContentsComparator comparator = lookup(ContentsComparator.class, type);
+ InputStream is = new FileInputStream(baseline);
+ try {
+ InputStream is2 = new FileInputStream(reactor);
+ try {
+ return comparator.getDelta(is, is2) == null;
+ } finally {
+ IOUtil.close(is2);
+ }
+ } finally {
+ IOUtil.close(is);
+ }
+ }
+}
diff --git a/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST.MF b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST.MF
new file mode 100644
index 000000000..a080ae2ce
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: igor
+Build-Jdk: 1.6.0_31
+Bundle-Version: 1.0.0.1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
+
diff --git a/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST2.MF b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST2.MF
new file mode 100644
index 000000000..3b309d2b0
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST2.MF
@@ -0,0 +1,9 @@
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: notigor
+Build-Jdk: 1.6.0_26
+Bundle-Version: 1.0.0.1
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
+
diff --git a/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST3.MF b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST3.MF
new file mode 100644
index 000000000..72cc96ca7
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/manifest/MANIFEST3.MF
@@ -0,0 +1,8 @@
+Manifest-Version: 1.0
+Archiver-Version: Plexus Archiver
+Created-By: Apache Maven
+Built-By: igor
+Build-Jdk: 1.6.0_31
+Bundle-Version: 1.0.0.2
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
diff --git a/tycho-artifactcomparator/src/test/resources/properties/props.properties b/tycho-artifactcomparator/src/test/resources/properties/props.properties
new file mode 100644
index 000000000..c6f5c73ef
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/properties/props.properties
@@ -0,0 +1,2 @@
+property=value
+property2=value2
diff --git a/tycho-artifactcomparator/src/test/resources/properties/props2.properties b/tycho-artifactcomparator/src/test/resources/properties/props2.properties
new file mode 100644
index 000000000..b9466c419
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/properties/props2.properties
@@ -0,0 +1,7 @@
+property=value
+
+
+# org.eclipse.equinox.p2.internal.repository.comparator.JarComparator.compareProperties(String, InputStream, InputStream)
+# ignores comments, so make sure we do too
+property2=val\
+ue2
diff --git a/tycho-artifactcomparator/src/test/resources/properties/props3.properties b/tycho-artifactcomparator/src/test/resources/properties/props3.properties
new file mode 100644
index 000000000..0da750156
--- /dev/null
+++ b/tycho-artifactcomparator/src/test/resources/properties/props3.properties
@@ -0,0 +1,2 @@
+property=value
+property2=value3
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/P2Artifact.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/P2Artifact.java
index fafe39506..5866373d2 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/P2Artifact.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/impl/publisher/P2Artifact.java
@@ -11,6 +11,7 @@
package org.eclipse.tycho.p2.impl.publisher;
import java.io.File;
+import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -25,7 +26,8 @@ public class P2Artifact implements IP2Artifact {
private final Set<Object> installableUnits;
private final Object artifactDescriptor;
- public P2Artifact(File location, Set<IInstallableUnit> installableUnits, IArtifactDescriptor artifactDescriptor) {
+ public P2Artifact(File location, Collection<IInstallableUnit> installableUnits,
+ IArtifactDescriptor artifactDescriptor) {
this.location = location;
this.installableUnits = Collections.unmodifiableSet(toRawSet(installableUnits));
this.artifactDescriptor = artifactDescriptor;
@@ -43,7 +45,7 @@ public class P2Artifact implements IP2Artifact {
return artifactDescriptor;
}
- private static <T> Set<Object> toRawSet(Set<T> set) {
+ private static <T> Set<Object> toRawSet(Collection<T> set) {
return new LinkedHashSet<Object>(set);
}
}
diff --git a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteMetadataRepositoryManager.java b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteMetadataRepositoryManager.java
index 4b2555d67..9816f7099 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteMetadataRepositoryManager.java
+++ b/tycho-bundles/org.eclipse.tycho.p2.resolver.impl/src/main/java/org/eclipse/tycho/p2/remote/RemoteMetadataRepositoryManager.java
@@ -53,8 +53,7 @@ class RemoteMetadataRepositoryManager implements IMetadataRepositoryManager {
public IMetadataRepository createRepository(URI location, String name, String type, Map<String, String> properties)
throws ProvisionException, OperationCanceledException {
- // not needed for remote repositories
- throw new UnsupportedOperationException();
+ return delegate.createRepository(translate(location), name, type, properties);
}
public IProvisioningAgent getAgent() {
diff --git a/tycho-bundles/org.eclipse.tycho.p2.tools.impl/META-INF/MANIFEST.MF b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/META-INF/MANIFEST.MF
index 583caba45..e4e3fb9ba 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.tools.impl/META-INF/MANIFEST.MF
+++ b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/META-INF/MANIFEST.MF
@@ -20,8 +20,14 @@ Require-Bundle: org.eclipse.equinox.p2.director.app;bundle-version="[1.0.200,2.0
org.eclipse.equinox.p2.director;bundle-version="[2.2.0,3.0.0)"
Import-Package: org.eclipse.tycho,
org.eclipse.tycho.core.facade,
+ org.eclipse.tycho.core.resolver.shared,
+ org.eclipse.tycho.p2.impl.publisher,
+ org.eclipse.tycho.p2.metadata,
+ org.eclipse.tycho.p2.remote,
org.eclipse.tycho.p2.repository,
+ org.eclipse.tycho.p2.resolver.facade,
org.eclipse.tycho.p2.tools,
+ org.eclipse.tycho.p2.tools.baseline.facade,
org.eclipse.tycho.p2.tools.director.facade,
org.eclipse.tycho.p2.tools.mirroring.facade,
org.eclipse.tycho.p2.tools.publisher.facade,
@@ -32,7 +38,7 @@ Import-Package: org.eclipse.tycho,
org.eclipse.tycho.repository.registry.facade,
org.osgi.framework;version="[1.5.0,2.0.0)"
Service-Component: OSGI-INF/directorapp.xml, OSGI-INF/publisherfactory.xml, OSGI-INF/mirrorapp.xml,
- OSGI-INF/verifierservice.xml
+ OSGI-INF/verifierservice.xml, OSGI-INF/baseline.xml
Bundle-Activator: org.eclipse.tycho.p2.tools.impl.Activator
Bundle-Vendor: %providerName
Bundle-ActivationPolicy: lazy
diff --git a/tycho-bundles/org.eclipse.tycho.p2.tools.impl/OSGI-INF/baseline.xml b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/OSGI-INF/baseline.xml
new file mode 100644
index 000000000..f066e41e4
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/OSGI-INF/baseline.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0">
+ <implementation class="org.eclipse.tycho.p2.tools.baseline.BaselineServiceImpl"/>
+ <service>
+ <provide interface="org.eclipse.tycho.p2.tools.baseline.facade.BaselineService"/>
+ </service>
+ <reference bind="setRemoteAgentManager" cardinality="1..1" interface="org.eclipse.tycho.p2.remote.RemoteAgentManager" name="RemoteAgentManager" policy="static"/>
+</scr:component>
diff --git a/tycho-bundles/org.eclipse.tycho.p2.tools.impl/src/main/java/org/eclipse/tycho/p2/tools/baseline/BaselineServiceImpl.java b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/src/main/java/org/eclipse/tycho/p2/tools/baseline/BaselineServiceImpl.java
new file mode 100644
index 000000000..2e4d44c6b
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.tools.impl/src/main/java/org/eclipse/tycho/p2/tools/baseline/BaselineServiceImpl.java
@@ -0,0 +1,198 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.p2.tools.baseline;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository;
+import org.eclipse.equinox.internal.p2.metadata.repository.CompositeMetadataRepository;
+import org.eclipse.equinox.p2.core.IProvisioningAgent;
+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.metadata.Version;
+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.IArtifactDescriptor;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
+import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager;
+import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager;
+import org.eclipse.tycho.core.resolver.shared.MavenRepositoryLocation;
+import org.eclipse.tycho.p2.impl.publisher.P2Artifact;
+import org.eclipse.tycho.p2.metadata.IP2Artifact;
+import org.eclipse.tycho.p2.remote.IRepositoryIdManager;
+import org.eclipse.tycho.p2.remote.RemoteAgentManager;
+import org.eclipse.tycho.p2.tools.baseline.facade.BaselineService;
+
+@SuppressWarnings("restriction")
+public class BaselineServiceImpl implements BaselineService {
+
+ private IProgressMonitor monitor = new NullProgressMonitor();
+
+ // @Inject
+ private RemoteAgentManager remoteAgentManager;
+
+ public Map<String, IP2Artifact> getProjectBaseline(Collection<MavenRepositoryLocation> baselineLocations,
+ Map<String, IP2Artifact> reactor, File target, boolean disableP2Mirrors) {
+
+ // baseline repository may contain artifacts with the same id/version but different contents
+ // compared to what is installed (or cached) locally.
+ // current local repository layout does not track per-repository artifacts and does not allow
+ // multiple different artifacts with same id/version.
+
+ CompositeMetadataRepository baselineUnits;
+ CompositeArtifactRepository baselineArtifacts;
+
+ try {
+ IProvisioningAgent remoteAgent = remoteAgentManager.getProvisioningAgent(disableP2Mirrors);
+ IRepositoryIdManager remoteRepositoryIdManager = (IRepositoryIdManager) remoteAgent
+ .getService(IRepositoryIdManager.SERVICE_NAME);
+ IMetadataRepositoryManager remoteMetadataRepositoryManager = (IMetadataRepositoryManager) remoteAgent
+ .getService(IMetadataRepositoryManager.SERVICE_NAME);
+ IArtifactRepositoryManager remoteArtifactRepositoryManager = (IArtifactRepositoryManager) remoteAgent
+ .getService(IArtifactRepositoryManager.SERVICE_NAME);
+
+ baselineUnits = CompositeMetadataRepository.createMemoryComposite(remoteAgent);
+ baselineArtifacts = CompositeArtifactRepository.createMemoryComposite(remoteAgent);
+
+ for (MavenRepositoryLocation location : baselineLocations) {
+ URI url = location.getURL();
+
+ try {
+ remoteRepositoryIdManager.addMapping(location.getId(), url);
+
+ // TODO offline mode https://bugs.eclipse.org/bugs/show_bug.cgi?id=337022
+
+ // not strictly necessary, but makes sure metadata download is visible in the console/log
+ remoteMetadataRepositoryManager.loadRepository(url, monitor);
+ remoteArtifactRepositoryManager.loadRepository(url, monitor);
+
+ baselineUnits.addChild(url);
+ baselineArtifacts.addChild(url);
+ } catch (ProvisionException e) {
+ // baseline repository may not exist yet
+ // TODO log a warning message
+ }
+ }
+ } catch (ProvisionException e) {
+ throw new RuntimeException(e);
+ }
+
+ Map<String, IP2Artifact> result = new LinkedHashMap<String, IP2Artifact>();
+
+ for (Map.Entry<String, IP2Artifact> reactorArtifact : reactor.entrySet()) {
+ IArtifactDescriptor descriptor = (IArtifactDescriptor) reactorArtifact.getValue().getArtifactDescriptor();
+
+ IArtifactDescriptor baselineDescriptor = getBaselineDescriptor(baselineArtifacts, descriptor);
+ if (baselineDescriptor == null) {
+ continue;
+ }
+
+ IArtifactKey baslineKey = baselineDescriptor.getArtifactKey();
+ String format = baselineDescriptor.getProperty(IArtifactDescriptor.FORMAT);
+ File baselineArtifact = new File(target, baslineKey.getClassifier() + "/" + baslineKey.getId() + "/"
+ + baslineKey.getVersion() + (format != null ? "." + format : ""));
+
+ try {
+ baselineArtifact.getParentFile().mkdirs();
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(baselineArtifact));
+ try {
+ baselineArtifacts.getRawArtifact(baselineDescriptor, os, monitor);
+ } finally {
+ try {
+ os.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ List<IInstallableUnit> units = new ArrayList<IInstallableUnit>();
+ for (Object _unit : reactorArtifact.getValue().getInstallableUnits()) {
+ IInstallableUnit unit = (IInstallableUnit) _unit;
+
+ IInstallableUnit baselineUnit = getBaselineUnit(baselineUnits, unit.getId(), unit.getVersion());
+ if (baselineUnit != null) {
+ units.add(baselineUnit);
+ }
+ }
+
+ result.put(reactorArtifact.getKey(), new P2Artifact(baselineArtifact, units, descriptor));
+ }
+
+ return !result.isEmpty() ? result : null;
+ }
+
+ private IArtifactDescriptor getBaselineDescriptor(IArtifactRepository baseline, IArtifactDescriptor descriptor) {
+ IArtifactDescriptor[] baselineDescriptors = baseline.getArtifactDescriptors(descriptor.getArtifactKey());
+
+ for (IArtifactDescriptor baselineDescriptor : baselineDescriptors) {
+ if (eq(descriptor.getProperty(IArtifactDescriptor.FORMAT),
+ baselineDescriptor.getProperty(IArtifactDescriptor.FORMAT))
+ && Arrays.equals(descriptor.getProcessingSteps(), baselineDescriptor.getProcessingSteps())) {
+ return baselineDescriptor;
+ }
+ }
+
+ return null;
+ }
+
+ private static <T> boolean eq(T a, T b) {
+ return a != null ? a.equals(b) : b == null;
+ }
+
+ private IInstallableUnit getBaselineUnit(IQueryable<IInstallableUnit> units, String id, Version version) {
+ IQueryResult<IInstallableUnit> result = units.query(QueryUtil.createIUQuery(id, version), monitor);
+
+ if (result.isEmpty()) {
+ return null;
+ }
+
+ Iterator<IInstallableUnit> iterator = result.iterator();
+
+ IInstallableUnit unit = iterator.next();
+
+ if (iterator.hasNext()) {
+ throw new IllegalArgumentException();
+ }
+
+ return unit;
+ }
+
+ public boolean isMetadataEqual(IP2Artifact baseline, IP2Artifact reactor) {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ // OSGi DS
+
+ public void setRemoteAgentManager(RemoteAgentManager remoteAgentManager) {
+ this.remoteAgentManager = remoteAgentManager;
+ }
+
+}
diff --git a/tycho-bundles/org.eclipse.tycho.p2.tools.shared/META-INF/MANIFEST.MF b/tycho-bundles/org.eclipse.tycho.p2.tools.shared/META-INF/MANIFEST.MF
index 7de0b9460..cb658679b 100644
--- a/tycho-bundles/org.eclipse.tycho.p2.tools.shared/META-INF/MANIFEST.MF
+++ b/tycho-bundles/org.eclipse.tycho.p2.tools.shared/META-INF/MANIFEST.MF
@@ -6,12 +6,16 @@ Bundle-Description: Exposes tools for manipulating p2 data, which are implemente
Bundle-SymbolicName: org.eclipse.tycho.p2.tools.shared
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Export-Package: org.eclipse.tycho.p2.tools,
+ org.eclipse.tycho.p2.tools.baseline.facade,
org.eclipse.tycho.p2.tools.director.facade,
org.eclipse.tycho.p2.tools.mirroring.facade,
org.eclipse.tycho.p2.tools.publisher.facade,
org.eclipse.tycho.p2.tools.verifier.facade
Import-Package: org.eclipse.tycho,
+ org.eclipse.tycho.artifacts,
org.eclipse.tycho.core.facade,
+ org.eclipse.tycho.core.resolver.shared,
+ org.eclipse.tycho.p2.metadata,
org.eclipse.tycho.p2.repository,
org.eclipse.tycho.repository.registry.facade
Bundle-Vendor: %providerName
diff --git a/tycho-bundles/org.eclipse.tycho.p2.tools.shared/src/main/java/org/eclipse/tycho/p2/tools/baseline/facade/BaselineService.java b/tycho-bundles/org.eclipse.tycho.p2.tools.shared/src/main/java/org/eclipse/tycho/p2/tools/baseline/facade/BaselineService.java
new file mode 100644
index 000000000..bc5cd0d17
--- /dev/null
+++ b/tycho-bundles/org.eclipse.tycho.p2.tools.shared/src/main/java/org/eclipse/tycho/p2/tools/baseline/facade/BaselineService.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.p2.tools.baseline.facade;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Map;
+
+import org.eclipse.tycho.core.resolver.shared.MavenRepositoryLocation;
+import org.eclipse.tycho.p2.metadata.IP2Artifact;
+
+public interface BaselineService {
+
+ public Map<String, IP2Artifact> getProjectBaseline(Collection<MavenRepositoryLocation> baselineLocations,
+ Map<String, IP2Artifact> reactor, File target, boolean disableP2Mirrors);
+
+ public boolean isMetadataEqual(IP2Artifact ip2Artifact, IP2Artifact ip2Artifact2);
+
+}
diff --git a/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoP2RuntimeLocator.java b/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoP2RuntimeLocator.java
index 20976c383..f68ea80f0 100644
--- a/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoP2RuntimeLocator.java
+++ b/tycho-core/src/main/java/org/eclipse/tycho/osgi/runtime/TychoP2RuntimeLocator.java
@@ -57,7 +57,8 @@ public class TychoP2RuntimeLocator implements EquinoxRuntimeLocator, DependencyR
"org.eclipse.tycho.p2.tools.publisher.facade", //
"org.eclipse.tycho.p2.tools.mirroring.facade", //
"org.eclipse.tycho.p2.tools.verifier.facade", //
- "org.eclipse.tycho.repository.registry.facade" };
+ "org.eclipse.tycho.repository.registry.facade",//
+ "org.eclipse.tycho.p2.tools.baseline.facade" };
@Requirement
private Logger logger;
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/artifacts.jar b/tycho-its/projects/362883_baseline/baseline/repository/artifacts.jar
new file mode 100644
index 000000000..16407439e
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/artifacts.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/binary/baseline.feature01_root_1.0.0.1 b/tycho-its/projects/362883_baseline/baseline/repository/binary/baseline.feature01_root_1.0.0.1
new file mode 100644
index 000000000..5f255646d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/binary/baseline.feature01_root_1.0.0.1
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/content.jar b/tycho-its/projects/362883_baseline/baseline/repository/content.jar
new file mode 100644
index 000000000..6847bfabd
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/content.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature01_1.0.0.1.jar b/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature01_1.0.0.1.jar
new file mode 100644
index 000000000..27b9e4e99
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature01_1.0.0.1.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature02_1.0.0.1.jar b/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature02_1.0.0.1.jar
new file mode 100644
index 000000000..290fb6579
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/features/baseline.feature02_1.0.0.1.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01.source_1.0.0.1.jar b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01.source_1.0.0.1.jar
new file mode 100644
index 000000000..1079ee7bb
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01.source_1.0.0.1.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01_1.0.0.1.jar b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01_1.0.0.1.jar
new file mode 100644
index 000000000..c7b92816d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle01_1.0.0.1.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle02_1.0.0.1.jar b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle02_1.0.0.1.jar
new file mode 100644
index 000000000..375a32bd0
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/repository/plugins/baseline.bundle02_1.0.0.1.jar
Binary files differ
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle01/META-INF/MANIFEST.MF b/tycho-its/projects/362883_baseline/baseline/src/bundle01/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..a529906ae
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle01/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
+Bundle-Version: 1.0.0.qualifier
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle01/build.properties b/tycho-its/projects/362883_baseline/baseline/src/bundle01/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle01/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle01/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/bundle01/pom.xml
new file mode 100644
index 000000000..23c4c3cc3
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle01/pom.xml
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.bundle01</artifactId>
+ <packaging>eclipse-plugin</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-source-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <executions>
+ <execution>
+ <id>plugin-source</id>
+ <goals>
+ <goal>plugin-source</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle01/src/baseline/bundle01/Bundle01Class.java b/tycho-its/projects/362883_baseline/baseline/src/bundle01/src/baseline/bundle01/Bundle01Class.java
new file mode 100644
index 000000000..3e2d6865a
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle01/src/baseline/bundle01/Bundle01Class.java
@@ -0,0 +1,6 @@
+package baseline.bundle01;
+
+public class Bundle01Class
+{
+
+}
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle02/META-INF/MANIFEST.MF b/tycho-its/projects/362883_baseline/baseline/src/bundle02/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..4cb95fb7d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle02/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle02
+Bundle-Version: 1.0.0.qualifier
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle02/build.properties b/tycho-its/projects/362883_baseline/baseline/src/bundle02/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle02/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle02/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/bundle02/pom.xml
new file mode 100644
index 000000000..2ac4ded6a
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle02/pom.xml
@@ -0,0 +1,14 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.bundle02</artifactId>
+ <packaging>eclipse-plugin</packaging>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/bundle02/src/baseline/bundle01/Bundle01Class.java b/tycho-its/projects/362883_baseline/baseline/src/bundle02/src/baseline/bundle01/Bundle01Class.java
new file mode 100644
index 000000000..3e2d6865a
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/bundle02/src/baseline/bundle01/Bundle01Class.java
@@ -0,0 +1,6 @@
+package baseline.bundle01;
+
+public class Bundle01Class
+{
+
+}
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature01/build.properties b/tycho-its/projects/362883_baseline/baseline/src/feature01/build.properties
new file mode 100644
index 000000000..49b106f3d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature01/build.properties
@@ -0,0 +1,2 @@
+bin.includes = feature.xml
+root = file:rootfile.txt
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature01/feature.xml b/tycho-its/projects/362883_baseline/baseline/src/feature01/feature.xml
new file mode 100644
index 000000000..b52df99ec
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature01/feature.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="baseline.feature01"
+ version="1.0.0.qualifier">
+
+ <plugin
+ id="baseline.bundle01"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="baseline.bundle01.source"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+ <plugin
+ id="baseline.bundle02"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature01/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/feature01/pom.xml
new file mode 100644
index 000000000..368966790
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature01/pom.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.feature01</artifactId>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature01/rootfile.txt b/tycho-its/projects/362883_baseline/baseline/src/feature01/rootfile.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature01/rootfile.txt
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature02/build.properties b/tycho-its/projects/362883_baseline/baseline/src/feature02/build.properties
new file mode 100644
index 000000000..64f93a9f0
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature02/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature02/feature.xml b/tycho-its/projects/362883_baseline/baseline/src/feature02/feature.xml
new file mode 100644
index 000000000..c27846516
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature02/feature.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="baseline.feature02"
+ version="1.0.0.qualifier">
+
+</feature>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/feature02/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/feature02/pom.xml
new file mode 100644
index 000000000..f1820c91b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/feature02/pom.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.feature02</artifactId>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/pom.xml
new file mode 100644
index 000000000..4f5a23f39
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/pom.xml
@@ -0,0 +1,79 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <properties>
+ <e342-repo>http://download.eclipse.org/releases/ganymede</e342-repo>
+ <versionQualifier>1</versionQualifier>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>e342</id>
+ <layout>p2</layout>
+ <url>${e342-repo}</url>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>bundle01</module>
+ <module>bundle02</module>
+ <module>feature01</module>
+ <module>feature02</module>
+ <module>repository</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-maven-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <environments>
+ <environment>
+ <os>win32</os>
+ <ws>win32</ws>
+ <arch>x86</arch>
+ </environment>
+ </environments>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-packaging-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <forceContextQualifier>${versionQualifier}</forceContextQualifier>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <baselineRepositories>
+ <repository>
+ <url>${baseline-repo}</url>
+ </repository>
+ </baselineRepositories>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/repository/baseline.repository.product b/tycho-its/projects/362883_baseline/baseline/src/repository/baseline.repository.product
new file mode 100644
index 000000000..bc1d94422
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/repository/baseline.repository.product
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product uid="baseline.repository" version="1.0.0.qualifier" useFeatures="true" includeLaunchers="false">
+
+ <features>
+ <feature id="baseline.feature01" version="0.0.0"/>
+ <feature id="baseline.feature02" version="0.0.0"/>
+ </features>
+
+</product>
diff --git a/tycho-its/projects/362883_baseline/baseline/src/repository/pom.xml b/tycho-its/projects/362883_baseline/baseline/src/repository/pom.xml
new file mode 100644
index 000000000..f6efedb1b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/baseline/src/repository/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.repository</artifactId>
+ <packaging>eclipse-repository</packaging>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/META-INF/MANIFEST.MF b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..a529906ae
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
+Bundle-Version: 1.0.0.qualifier
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/build.properties b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/build.properties
new file mode 100644
index 000000000..c90635911
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/build.properties
@@ -0,0 +1,5 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
+src.includes = source.txt
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/pom.xml b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/pom.xml
new file mode 100644
index 000000000..23c4c3cc3
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/pom.xml
@@ -0,0 +1,32 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.bundle01</artifactId>
+ <packaging>eclipse-plugin</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-source-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <executions>
+ <execution>
+ <id>plugin-source</id>
+ <goals>
+ <goal>plugin-source</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/source.txt b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/source.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/source.txt
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/src/baseline/bundle01/Bundle01Class.java b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/src/baseline/bundle01/Bundle01Class.java
new file mode 100644
index 000000000..3e2d6865a
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/bundle01/src/baseline/bundle01/Bundle01Class.java
@@ -0,0 +1,6 @@
+package baseline.bundle01;
+
+public class Bundle01Class
+{
+
+}
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/pom.xml b/tycho-its/projects/362883_baseline/changedattachedartifact/pom.xml
new file mode 100644
index 000000000..bf4cccdab
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/pom.xml
@@ -0,0 +1,81 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <description>
+ Attached build01.source includes new file.
+ </description>
+
+ <properties>
+ <e342-repo>http://download.eclipse.org/releases/ganymede</e342-repo>
+
+ <versionQualifier>1</versionQualifier>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>e342</id>
+ <layout>p2</layout>
+ <url>${e342-repo}</url>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>bundle01</module>
+ <module>repository</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-maven-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <environments>
+ <environment>
+ <os>win32</os>
+ <ws>win32</ws>
+ <arch>x86</arch>
+ </environment>
+ </environments>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-packaging-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <forceContextQualifier>${versionQualifier}</forceContextQualifier>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <baselineRepositories>
+ <repository>
+ <url>${baseline-repo}</url>
+ </repository>
+ </baselineRepositories>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/repository/baseline.repository.product b/tycho-its/projects/362883_baseline/changedattachedartifact/repository/baseline.repository.product
new file mode 100644
index 000000000..463d81ae9
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/repository/baseline.repository.product
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product uid="baseline.repository" version="1.0.0.qualifier" useFeatures="false" includeLaunchers="false">
+
+ <plugins>
+ <plugin id="baseline.bundle01" version="0.0.0"/>
+ <plugin id="baseline.bundle01.source" version="0.0.0"/>
+ </plugins>
+
+</product>
diff --git a/tycho-its/projects/362883_baseline/changedattachedartifact/repository/pom.xml b/tycho-its/projects/362883_baseline/changedattachedartifact/repository/pom.xml
new file mode 100644
index 000000000..f6efedb1b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/changedattachedartifact/repository/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.repository</artifactId>
+ <packaging>eclipse-repository</packaging>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/bundle01/META-INF/MANIFEST.MF b/tycho-its/projects/362883_baseline/contentchanged/bundle01/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..a529906ae
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/bundle01/META-INF/MANIFEST.MF
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: baseline.bundle01
+Bundle-Version: 1.0.0.qualifier
diff --git a/tycho-its/projects/362883_baseline/contentchanged/bundle01/build.properties b/tycho-its/projects/362883_baseline/contentchanged/bundle01/build.properties
new file mode 100644
index 000000000..34d2e4d2d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/bundle01/build.properties
@@ -0,0 +1,4 @@
+source.. = src/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/tycho-its/projects/362883_baseline/contentchanged/bundle01/pom.xml b/tycho-its/projects/362883_baseline/contentchanged/bundle01/pom.xml
new file mode 100644
index 000000000..828c53fd0
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/bundle01/pom.xml
@@ -0,0 +1,14 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.bundle01</artifactId>
+ <packaging>eclipse-plugin</packaging>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/bundle01/src/baseline/bundle01/Bundle01Class.java b/tycho-its/projects/362883_baseline/contentchanged/bundle01/src/baseline/bundle01/Bundle01Class.java
new file mode 100644
index 000000000..dd3c2f1d4
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/bundle01/src/baseline/bundle01/Bundle01Class.java
@@ -0,0 +1,6 @@
+package baseline.bundle01;
+
+public class Bundle01Class implements java.io.Serializable
+{
+
+}
diff --git a/tycho-its/projects/362883_baseline/contentchanged/feature02/build.properties b/tycho-its/projects/362883_baseline/contentchanged/feature02/build.properties
new file mode 100644
index 000000000..64f93a9f0
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/feature02/build.properties
@@ -0,0 +1 @@
+bin.includes = feature.xml
diff --git a/tycho-its/projects/362883_baseline/contentchanged/feature02/feature.xml b/tycho-its/projects/362883_baseline/contentchanged/feature02/feature.xml
new file mode 100644
index 000000000..ef3644d2f
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/feature02/feature.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="baseline.feature02"
+ version="1.0.0.qualifier">
+
+ <plugin
+ id="baseline.bundle01"
+ download-size="0"
+ install-size="0"
+ version="0.0.0"
+ unpack="false"/>
+
+</feature>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/feature02/pom.xml b/tycho-its/projects/362883_baseline/contentchanged/feature02/pom.xml
new file mode 100644
index 000000000..f1820c91b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/feature02/pom.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.feature02</artifactId>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/pom.xml b/tycho-its/projects/362883_baseline/contentchanged/pom.xml
new file mode 100644
index 000000000..150f68235
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/pom.xml
@@ -0,0 +1,84 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <!--
+ Compared to baseline
+ bundle01/Bundle01Class.java implements new interface
+ feature02/feature.xml includes bundle01
+ -->
+
+ <properties>
+ <e342-repo>http://download.eclipse.org/releases/ganymede</e342-repo>
+
+ <versionQualifier>1</versionQualifier>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>e342</id>
+ <layout>p2</layout>
+ <url>${e342-repo}</url>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>bundle01</module>
+ <module>feature02</module>
+ <module>repository</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-maven-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <environments>
+ <environment>
+ <os>win32</os>
+ <ws>win32</ws>
+ <arch>x86</arch>
+ </environment>
+ </environments>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-packaging-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <forceContextQualifier>${versionQualifier}</forceContextQualifier>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <baselineRepositories>
+ <repository>
+ <url>${baseline-repo}</url>
+ </repository>
+ </baselineRepositories>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/repository/baseline.repository.product b/tycho-its/projects/362883_baseline/contentchanged/repository/baseline.repository.product
new file mode 100644
index 000000000..f20eb74f6
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/repository/baseline.repository.product
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product uid="baseline.repository" version="1.0.0.qualifier" useFeatures="true" includeLaunchers="false">
+
+ <features>
+ <feature id="baseline.feature02" version="0.0.0"/>
+ </features>
+
+</product>
diff --git a/tycho-its/projects/362883_baseline/contentchanged/repository/pom.xml b/tycho-its/projects/362883_baseline/contentchanged/repository/pom.xml
new file mode 100644
index 000000000..f6efedb1b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/contentchanged/repository/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.repository</artifactId>
+ <packaging>eclipse-repository</packaging>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/feature02/build.properties b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/build.properties
new file mode 100644
index 000000000..49b106f3d
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/build.properties
@@ -0,0 +1,2 @@
+bin.includes = feature.xml
+root = file:rootfile.txt
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/feature02/feature.xml b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/feature.xml
new file mode 100644
index 000000000..c27846516
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/feature.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<feature
+ id="baseline.feature02"
+ version="1.0.0.qualifier">
+
+</feature>
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/feature02/pom.xml b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/pom.xml
new file mode 100644
index 000000000..f1820c91b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/pom.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.feature02</artifactId>
+ <packaging>eclipse-feature</packaging>
+</project>
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/feature02/rootfile.txt b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/rootfile.txt
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/feature02/rootfile.txt
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/pom.xml b/tycho-its/projects/362883_baseline/newattachedartifact/pom.xml
new file mode 100644
index 000000000..c8c071600
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/pom.xml
@@ -0,0 +1,81 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+
+ <description>
+ feature02 has rootfiles, which baseline build of the feature did not have
+ </description>
+
+ <properties>
+ <e342-repo>http://download.eclipse.org/releases/ganymede</e342-repo>
+
+ <versionQualifier>1</versionQualifier>
+ </properties>
+
+ <repositories>
+ <repository>
+ <id>e342</id>
+ <layout>p2</layout>
+ <url>${e342-repo}</url>
+ </repository>
+ </repositories>
+
+ <modules>
+ <module>feature02</module>
+ <module>repository</module>
+ </modules>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-maven-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <extensions>true</extensions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>target-platform-configuration</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <environments>
+ <environment>
+ <os>win32</os>
+ <ws>win32</ws>
+ <arch>x86</arch>
+ </environment>
+ </environments>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-packaging-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <forceContextQualifier>${versionQualifier}</forceContextQualifier>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-p2-plugin</artifactId>
+ <version>${tycho-version}</version>
+ <configuration>
+ <baselineRepositories>
+ <repository>
+ <url>${baseline-repo}</url>
+ </repository>
+ </baselineRepositories>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/repository/baseline.repository.product b/tycho-its/projects/362883_baseline/newattachedartifact/repository/baseline.repository.product
new file mode 100644
index 000000000..f20eb74f6
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/repository/baseline.repository.product
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<?pde version="3.5"?>
+
+<product uid="baseline.repository" version="1.0.0.qualifier" useFeatures="true" includeLaunchers="false">
+
+ <features>
+ <feature id="baseline.feature02" version="0.0.0"/>
+ </features>
+
+</product>
diff --git a/tycho-its/projects/362883_baseline/newattachedartifact/repository/pom.xml b/tycho-its/projects/362883_baseline/newattachedartifact/repository/pom.xml
new file mode 100644
index 000000000..f6efedb1b
--- /dev/null
+++ b/tycho-its/projects/362883_baseline/newattachedartifact/repository/pom.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>baseline</groupId>
+ <artifactId>baseline-parent</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>baseline.repository</artifactId>
+ <packaging>eclipse-repository</packaging>
+
+</project>
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/bug362883_baseline/BaselineValidateAndReplaceTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/bug362883_baseline/BaselineValidateAndReplaceTest.java
new file mode 100644
index 000000000..59868544a
--- /dev/null
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/bug362883_baseline/BaselineValidateAndReplaceTest.java
@@ -0,0 +1,160 @@
+package org.eclipse.tycho.test.bug362883_baseline;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.maven.it.VerificationException;
+import org.apache.maven.it.Verifier;
+import org.codehaus.plexus.util.FileUtils;
+import org.eclipse.tycho.test.AbstractTychoIntegrationTest;
+import org.eclipse.tycho.test.util.ResourceUtil;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class BaselineValidateAndReplaceTest extends AbstractTychoIntegrationTest {
+
+ private static File baselineRepo;
+
+ @BeforeClass
+ public static void setupClass() throws IOException {
+ baselineRepo = new File("projects/362883_baseline/baseline/repository").getCanonicalFile();
+ }
+
+ private Verifier getVerifier(String project, File baselineRepo) throws Exception {
+ Verifier verifier = getVerifier("/362883_baseline/" + project, false);
+ verifier.getCliOptions().add("-De342-repo=" + ResourceUtil.P2Repositories.ECLIPSE_342.toString());
+ verifier.getCliOptions().add("-Dbaseline-repo=" + baselineRepo.toURI().toString());
+ return verifier;
+ }
+
+ @Test
+ public void testBaselineRepositoryDoesNotExist() throws Exception {
+ // likely initial state is when baseline repository url points at empty or garbage location
+ File notARepository = new File("baseline/src").getCanonicalFile();
+ Verifier verifier = getVerifier("baseline/src", notARepository);
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyErrorFreeLog();
+ }
+
+ @Test
+ public void testRebuildOfTheSameCodebase() throws Exception {
+ Verifier verifier = getVerifier("baseline/src", baselineRepo);
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyErrorFreeLog();
+
+ File repository = new File(verifier.getBasedir(), "repository/target/repository");
+
+ assertBaselineContents(repository, "features/baseline.feature01_1.0.0.1.jar");
+ assertBaselineContents(repository, "plugins/baseline.bundle01_1.0.0.1.jar");
+ assertBaselineContents(repository, "plugins/baseline.bundle01.source_1.0.0.1.jar");
+ }
+
+ @Test
+ public void testNewVersion() throws Exception {
+ Verifier verifier = getVerifier("baseline/src", baselineRepo);
+ verifier.getCliOptions().add("-DversionQualifier=2");
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyErrorFreeLog();
+
+ File repository = new File(verifier.getBasedir(), "repository/target/repository");
+
+ assertFileDoesNotExist(repository, "features/baseline.feature01_1.0.0.1.jar");
+ assertFileDoesNotExist(repository, "plugins/baseline.bundle01_1.0.0.1.jar");
+ assertFileDoesNotExist(repository, "plugins/baseline.bundle01.source_1.0.0.1.jar");
+
+ assertFileExists(repository, "features/baseline.feature01_1.0.0.2.jar");
+ assertFileExists(repository, "plugins/baseline.bundle01_1.0.0.2.jar");
+ assertFileExists(repository, "plugins/baseline.bundle01.source_1.0.0.2.jar");
+ }
+
+ @Test
+ public void testContentChangedStrict() throws Exception {
+ Verifier verifier = getVerifier("contentchanged", baselineRepo);
+
+ try {
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ } catch (VerificationException expected) {
+ //
+ }
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+ }
+
+ @Test
+ public void testContentChangedNonStrict() throws Exception {
+ Verifier verifier = getVerifier("contentchanged", baselineRepo);
+
+ verifier.getCliOptions().add("-Dtycho.baseline.strict=false");
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+ }
+
+ @Test
+ public void testNewAttachedArtifactStrict() throws Exception {
+ Verifier verifier = getVerifier("newattachedartifact", baselineRepo);
+
+ try {
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ } catch (VerificationException expected) {
+ //
+ }
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+ }
+
+ @Test
+ public void testNewAttachedArtifactNonStrict() throws Exception {
+ Verifier verifier = getVerifier("newattachedartifact", baselineRepo);
+
+ verifier.getCliOptions().add("-Dtycho.baseline.strict=false");
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+
+ File basedir = new File(verifier.getBasedir());
+
+ assertFileDoesNotExist(basedir, "feature02/target/baseline.feature02_root-1.0.0.1-root.zip");
+ assertFileDoesNotExist(basedir, "repository/target/repository/binary/baseline.feature02_root_1.0.0.1");
+
+ // TODO ideally, verify artifacts are detached from the project and are not installed/deployed
+ }
+
+ @Test
+ public void testChangedAttachedArtifactStrict() throws Exception {
+ Verifier verifier = getVerifier("changedattachedartifact", baselineRepo);
+
+ try {
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ } catch (VerificationException expected) {
+ //
+ }
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+ }
+
+ @Test
+ public void testChangedAttachedArtifactNonStrict() throws Exception {
+ Verifier verifier = getVerifier("changedattachedartifact", baselineRepo);
+
+ verifier.getCliOptions().add("-Dtycho.baseline.strict=false");
+
+ verifier.executeGoals(Arrays.asList("clean", "package"));
+ verifier.verifyTextInLog("baseline and reactor have same version but different contents");
+
+ File repository = new File(verifier.getBasedir(), "repository/target/repository");
+ assertBaselineContents(repository, "plugins/baseline.bundle01.source_1.0.0.1.jar");
+ }
+
+ private void assertBaselineContents(File repository, String path) throws IOException {
+ Assert.assertTrue(isBaselineContents(repository, path));
+ }
+
+ private boolean isBaselineContents(File repository, String path) throws IOException {
+ File file = new File(repository, path);
+ File baselineFile = new File(baselineRepo, path);
+ return FileUtils.contentEquals(baselineFile, file);
+ }
+}
diff --git a/tycho-p2/tycho-p2-plugin/pom.xml b/tycho-p2/tycho-p2-plugin/pom.xml
index cdf5d7c19..bcaa69d55 100644
--- a/tycho-p2/tycho-p2-plugin/pom.xml
+++ b/tycho-p2/tycho-p2-plugin/pom.xml
@@ -45,6 +45,11 @@
<artifactId>tycho-p2-facade</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.tycho</groupId>
+ <artifactId>tycho-artifactcomparator</artifactId>
+ <version>${project.version}</version>
+ </dependency>
<dependency>
<groupId>org.eclipse.tycho</groupId>
@@ -54,4 +59,13 @@
</dependency>
</dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-component-metadata</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
</project>
diff --git a/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/BaselineValidator.java b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/BaselineValidator.java
new file mode 100644
index 000000000..68ebefcd1
--- /dev/null
+++ b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/BaselineValidator.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.plugins.p2;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.component.annotations.Component;
+import org.codehaus.plexus.component.annotations.Requirement;
+import org.codehaus.plexus.logging.Logger;
+import org.codehaus.plexus.util.FileUtils;
+import org.eclipse.sisu.equinox.EquinoxServiceFactory;
+import org.eclipse.tycho.artifactcomparator.ArtifactComparator;
+import org.eclipse.tycho.artifactcomparator.ArtifactDelta;
+import org.eclipse.tycho.core.TargetPlatformConfiguration;
+import org.eclipse.tycho.core.resolver.shared.MavenRepositoryLocation;
+import org.eclipse.tycho.core.utils.TychoProjectUtils;
+import org.eclipse.tycho.p2.metadata.IP2Artifact;
+import org.eclipse.tycho.p2.repository.RepositoryLayoutHelper;
+import org.eclipse.tycho.p2.tools.baseline.facade.BaselineService;
+import org.eclipse.tycho.zipcomparator.internal.CompoundArtifactDelta;
+import org.eclipse.tycho.zipcomparator.internal.SimpleArtifactDelta;
+
+@Component(role = BaselineValidator.class)
+public class BaselineValidator {
+
+ @Requirement
+ private Logger log;
+
+ @Requirement(hint = "zip")
+ private ArtifactComparator zipComparator;
+
+ @Requirement
+ private EquinoxServiceFactory equinox;
+
+ public Map<String, IP2Artifact> validateAndReplace(MavenProject project, Map<String, IP2Artifact> reactorMetadata,
+ List<Repository> baselineRepositories, boolean strictBaseline) throws IOException, MojoExecutionException {
+ if (baselineRepositories != null && !baselineRepositories.isEmpty()) {
+ TargetPlatformConfiguration configuration = TychoProjectUtils.getTargetPlatformConfiguration(project);
+
+ List<MavenRepositoryLocation> _repositories = new ArrayList<MavenRepositoryLocation>();
+ for (Repository repository : baselineRepositories) {
+ if (repository.getUrl() != null) {
+ _repositories.add(new MavenRepositoryLocation(repository.getId(), repository.getUrl()));
+ }
+ }
+
+ File baselineBasedir = new File(project.getBuild().getDirectory(), "baseline");
+
+ BaselineService baselineService = getService(BaselineService.class);
+
+ Map<String, IP2Artifact> baselineMetadata = baselineService.getProjectBaseline(_repositories,
+ reactorMetadata, baselineBasedir, configuration.isDisableP2Mirrors());
+
+ if (baselineMetadata != null) {
+ ArtifactDelta delta = getDelta(baselineService, baselineMetadata, reactorMetadata);
+ if (delta != null) {
+ String message;
+ if (log.isDebugEnabled() || strictBaseline) {
+ message = delta.getDetailedMessage();
+ } else {
+ message = delta.getMessage();
+ }
+ if (strictBaseline) {
+ throw new MojoExecutionException(message.toString());
+ }
+ log.warn(project.toString() + ": " + message);
+ }
+
+ // replace reactor artifacts with baseline
+ ArrayList<String> replaced = new ArrayList<String>();
+ for (Map.Entry<String, IP2Artifact> artifact : baselineMetadata.entrySet()) {
+ String classifier = artifact.getKey();
+ FileUtils
+ .copyFile(artifact.getValue().getLocation(), reactorMetadata.get(classifier).getLocation());
+ if (classifier != null) {
+ replaced.add(classifier);
+ }
+ }
+
+ // un-attached and delete artifacts present in reactor but not in baseline
+ ArrayList<String> removed = new ArrayList<String>();
+ for (Map.Entry<String, IP2Artifact> artifact : reactorMetadata.entrySet()) {
+ String classifier = artifact.getKey();
+ if (classifier != null && artifact.getValue() != null && !baselineMetadata.containsKey(classifier)) {
+ List<Artifact> attachedArtifacts = project.getAttachedArtifacts();
+ ListIterator<Artifact> iterator = attachedArtifacts.listIterator();
+ while (iterator.hasNext()) {
+ if (classifier.equals(iterator.next().getClassifier())) {
+ iterator.remove();
+ break;
+ }
+ }
+ artifact.getValue().getLocation().delete();
+ removed.add(classifier);
+ }
+ }
+
+ // Reactor can have less attached artifacts than baseline build did. This is not a problem because
+ // reactor artifacts are never used, they are either replaced with corresponding baseline artifacts
+ // or other build fails, so the build either produces consistent output or no output at all.
+
+ reactorMetadata = baselineMetadata;
+
+ if (log.isInfoEnabled()) {
+ StringBuilder msg = new StringBuilder();
+ msg.append(project.toString());
+ msg.append("\n The main artifact has been replaced with the baseline version.\n");
+ if (!replaced.isEmpty()) {
+ msg.append(" The following attached artifacts have been replaced with the baseline version: ");
+ msg.append(replaced.toString());
+ msg.append("\n");
+ }
+ if (!removed.isEmpty()) {
+ msg.append(" The following attached artifacts are not present in the baseline and have been removed: ");
+ msg.append(removed.toString());
+ msg.append("\n");
+ }
+ log.info(msg.toString());
+ }
+ } else {
+ log.info("No baseline version " + project);
+ }
+ }
+ return reactorMetadata;
+ }
+
+ private ArtifactDelta getDelta(BaselineService baselineService, Map<String, IP2Artifact> baselineMetadata,
+ Map<String, IP2Artifact> generatedMetadata) throws IOException {
+
+ Map<String, ArtifactDelta> result = new LinkedHashMap<String, ArtifactDelta>();
+
+ // baseline never includes more artifacts
+ for (String classifier : generatedMetadata.keySet()) {
+ IP2Artifact baselineArtifact = baselineMetadata.get(classifier);
+ IP2Artifact reactorArtifact = generatedMetadata.get(classifier);
+
+ if (baselineArtifact == null) {
+ result.put(classifier, new SimpleArtifactDelta("not present in baseline"));
+ continue;
+ }
+
+ if (!baselineService.isMetadataEqual(baselineArtifact, reactorArtifact)) {
+ result.put(classifier, new SimpleArtifactDelta("p2 metadata different"));
+ continue;
+ }
+
+ // the following types of artifacts are produced/consumed by tycho as of 0.16
+ // - bundle jar and jar.pack.gz artifacts
+ // - feature jar artifacts
+ // - feature rootfiles zip artifacts
+
+ if (RepositoryLayoutHelper.PACK200_CLASSIFIER.equals(classifier)) {
+ // in the unlikely event that reactor and baseline pack200 files have different contents
+ // but bundle jar files are the same, the build will silently use baseline pack200 file
+ continue;
+ }
+
+ try {
+ ArtifactDelta delta = zipComparator.getDelta(baselineArtifact.getLocation(),
+ reactorArtifact.getLocation());
+ if (delta != null) {
+ result.put(classifier, delta);
+ }
+ } catch (IOException e) {
+ // do byte-to-byte comparison if jar comparison fails for whatever reason
+ if (!FileUtils.contentEquals(baselineArtifact.getLocation(), reactorArtifact.getLocation())) {
+ result.put(classifier, new SimpleArtifactDelta("different"));
+ }
+ }
+ }
+
+ return !result.isEmpty() ? new CompoundArtifactDelta(
+ "baseline and reactor have same version but different contents", result) : null;
+ }
+
+ private <T> T getService(Class<T> type) {
+ T service = equinox.getService(type);
+ if (service == null) {
+ throw new IllegalStateException("Could not acquire service " + type);
+ }
+ return service;
+ }
+
+}
diff --git a/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/P2MetadataMojo.java b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/P2MetadataMojo.java
index 56518ec55..88603ba38 100644
--- a/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/P2MetadataMojo.java
+++ b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/P2MetadataMojo.java
@@ -70,21 +70,40 @@ public class P2MetadataMojo extends AbstractMojo {
private List<String> supportedProjectTypes = Arrays.asList("eclipse-plugin", "eclipse-test-plugin",
"eclipse-feature");
- private P2Generator p2;
+ /**
+ * Baseline build repository(ies).
+ * <p/>
+ * P2 assumes that the same artifact type, id and version represent the same artifact. If
+ * baselineRepositories parameter is specified, this assumption is validated and optionally
+ * enforced.
+ *
+ * @parameter
+ */
+ private List<Repository> baselineRepositories;
+
+ /**
+ * If true, the default, fail the build if reactor build produced artifacts with the same type,
+ * id and version but different contents than artifacts available from baseline repository(ies).
+ *
+ * @parameter expression="${tycho.baseline.strict}" default-value="true"
+ */
+ private boolean strictBaseline;
+
+ /**
+ * @component
+ */
+ private BaselineValidator baselineValidator;
public void execute() throws MojoExecutionException, MojoFailureException {
attachP2Metadata();
}
- protected P2Generator getP2Generator() {
- if (p2 == null) {
- p2 = equinox.getService(P2Generator.class);
-
- if (p2 == null) {
- throw new IllegalStateException("Could not acquire P2 metadata service");
- }
+ private <T> T getService(Class<T> type) {
+ T service = equinox.getService(type);
+ if (service == null) {
+ throw new IllegalStateException("Could not acquire service " + type);
}
- return p2;
+ return service;
}
protected void attachP2Metadata() throws MojoExecutionException {
@@ -113,17 +132,22 @@ public class P2MetadataMojo extends AbstractMojo {
}
}
- Map<String, IP2Artifact> generateMetadata = getP2Generator().generateMetadata(artifacts, targetDir);
+ P2Generator p2generator = getService(P2Generator.class);
+
+ Map<String, IP2Artifact> generatedMetadata = p2generator.generateMetadata(artifacts, targetDir);
+
+ generatedMetadata = baselineValidator.validateAndReplace(project, generatedMetadata, baselineRepositories,
+ strictBaseline);
File contentsXml = new File(targetDir, FILE_NAME_P2_METADATA);
File artifactsXml = new File(targetDir, FILE_NAME_P2_ARTIFACTS);
- getP2Generator().persistMetadata(generateMetadata, contentsXml, artifactsXml);
+ p2generator.persistMetadata(generatedMetadata, contentsXml, artifactsXml);
projectHelper.attachArtifact(project, EXTENSION_P2_METADATA, CLASSIFIER_P2_METADATA, contentsXml);
projectHelper.attachArtifact(project, EXTENSION_P2_ARTIFACTS, CLASSIFIER_P2_ARTIFACTS, artifactsXml);
ReactorProject reactorProject = DefaultReactorProject.adapt(project);
- for (Map.Entry<String, IP2Artifact> entry : generateMetadata.entrySet()) {
+ for (Map.Entry<String, IP2Artifact> entry : generatedMetadata.entrySet()) {
String classifier = entry.getKey();
IP2Artifact p2artifact = entry.getValue();
diff --git a/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/Repository.java b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/Repository.java
new file mode 100644
index 000000000..cf99873f3
--- /dev/null
+++ b/tycho-p2/tycho-p2-plugin/src/main/java/org/eclipse/tycho/plugins/p2/Repository.java
@@ -0,0 +1,38 @@
+/*******************************************************************************
+ * Copyright (c) 2012 Sonatype Inc. and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Sonatype Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.plugins.p2;
+
+import java.net.URI;
+
+import org.eclipse.tycho.core.resolver.shared.MavenRepositoryLocation;
+
+// TODO reconcile with MavenRepositoryLocation
+public class Repository {
+
+ private String id;
+ private URI url;
+
+ // no-args constructor is used by parameter injection
+ public Repository() {
+ }
+
+ public URI getUrl() {
+ return url;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public MavenRepositoryLocation toRepositoryLocation() {
+ return new MavenRepositoryLocation(id, url);
+ }
+}

Back to the top