diff --git a/tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiAnalysis.java b/tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiAnalysis.java index 9a5fb0b1da..942b500a44 100644 --- a/tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiAnalysis.java +++ b/tycho-apitools-plugin/src/main/java/org/eclipse/tycho/apitools/ApiAnalysis.java @@ -16,12 +16,18 @@ import java.io.IOException; import java.io.InputStream; import java.io.Serializable; +import java.nio.file.FileVisitResult; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; import java.util.concurrent.Callable; @@ -63,6 +69,11 @@ import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; +import org.eclipse.pde.core.build.IBuild; +import org.eclipse.pde.core.build.IBuildEntry; +import org.eclipse.pde.core.build.IBuildModel; +import org.eclipse.pde.core.plugin.IPluginModelBase; +import org.eclipse.pde.core.plugin.PluginRegistry; import org.eclipse.pde.core.target.ITargetDefinition; import org.eclipse.pde.core.target.ITargetLocation; import org.eclipse.pde.core.target.ITargetPlatformService; @@ -74,6 +85,9 @@ import org.osgi.framework.Bundle; import org.osgi.framework.FrameworkUtil; +/** + * Performs the API Analysis inside the embedded OSGi Frameworks + */ public class ApiAnalysis implements Serializable, Callable { private Collection baselineBundles; @@ -86,7 +100,7 @@ public class ApiAnalysis implements Serializable, Callable { private String binaryArtifact; private String outputDir; - public ApiAnalysis(Collection baselineBundles, Collection dependencyBundles, String baselineName, + ApiAnalysis(Collection baselineBundles, Collection dependencyBundles, String baselineName, Path apiFilterFile, Path apiPreferences, Path projectDir, boolean debug, Path binaryArtifact, Path outputDir) { this.targetBundles = dependencyBundles.stream().map(ApiAnalysis::pathAsString).toList(); @@ -222,32 +236,126 @@ private BundleComponent importProject() throws CoreException, IOException { } private void createOutputFolder(IProject project, IPath projectPath) throws IOException, CoreException { + Map outputJars = computeOutputJars(project); IJavaProject javaProject = JavaCore.create(project); if (javaProject != null) { - IPath fullPath = project.getFolder(outputDir).getFullPath(); + IFolder outputFolder = project.getFolder(outputDir); // FIXME see bug https://github.com/eclipse-pde/eclipse.pde/issues/801 // it can happen that project output location != maven compiled classes, usually // eclipse uses output = bin/ while maven uses target/classes if not // specifically configured to be even - javaProject.setOutputLocation(fullPath, null); - makeOutputFolder(javaProject.getOutputLocation(), projectPath); + IPath mainOutputLocation = javaProject.getOutputLocation(); + IPath mainRealPath = getRealPath(mainOutputLocation, outputJars, outputFolder); + makeOutputFolder(mainOutputLocation, mainRealPath); IClasspathEntry[] classpath = javaProject.getRawClasspath(); for (IClasspathEntry entry : classpath) { - // FIXME see bug https://github.com/eclipse-pde/eclipse.pde/issues/791 - makeOutputFolder(entry.getOutputLocation(), projectPath); + IPath entryOutputLocation = entry.getOutputLocation(); + if (entryOutputLocation != null) { + IPath realEntryPath = getRealPath(entryOutputLocation, outputJars, outputFolder); + makeOutputFolder(entryOutputLocation, realEntryPath); + } + } + } + } + + private Map computeOutputJars(IProject project) throws CoreException { + Map outputJars = new HashMap(); + IPluginModelBase base = PluginRegistry.findModel(project); + if (base != null) { + IBuildModel model = PluginRegistry.createBuildModel(base); + if (model != null) { + IBuild ibuild = model.getBuild(); + IBuildEntry[] entries = ibuild.getBuildEntries(); + for (IBuildEntry entry : entries) { + String name = entry.getName(); + if (name.startsWith(IBuildEntry.OUTPUT_PREFIX)) { + String key = name.substring(IBuildEntry.OUTPUT_PREFIX.length()); + for (String token : entry.getTokens()) { + outputJars.put(token, key); + } + } + } + } + } + return outputJars; + } + + private IPath getRealPath(IPath eclipseOutputLocation, Map outputJars, IFolder mavenOutputFolder) { + if (eclipseOutputLocation == null) { + return null; + } + IFolder projectFolder = getProjectFolder(eclipseOutputLocation); + for (Entry entry : outputJars.entrySet()) { + IFolder jarFolder = projectFolder.getProject().getFolder(entry.getKey()); + if (jarFolder.equals(projectFolder)) { + String jarOutputPath = entry.getValue(); + if (".".equals(jarOutputPath)) { + return mavenOutputFolder.getFullPath(); + } + return mavenOutputFolder.getParent() + .getFolder(new org.eclipse.core.runtime.Path(jarOutputPath + "-classes")).getFullPath(); } } + return eclipseOutputLocation; } - private void makeOutputFolder(IPath outputLocation, IPath projectPath) throws CoreException, IOException { - if (outputLocation != null) { + private IFolder makeOutputFolder(IPath eclipseOutputLocation, IPath mavenOutputLocation) + throws CoreException, IOException { + if (eclipseOutputLocation != null) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); - IFolder folder = workspace.getRoot().getFolder(outputLocation); + IFolder folder = workspace.getRoot().getFolder(eclipseOutputLocation); if (!folder.exists()) { folder.create(true, true, new NullProgressMonitor()); } + if (mavenOutputLocation != null && !eclipseOutputLocation.equals(mavenOutputLocation)) { + copy(getFile(mavenOutputLocation), getFile(eclipseOutputLocation)); + } + folder.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); + return folder; } + return null; + } + private File getFile(IPath path) { + if (path == null) { + return null; + } + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + IPath location = workspace.getRoot().getFolder(path).getLocation(); + if (location == null) { + return null; + } + return location.toFile(); + } + + private void copy(File from, File to) throws IOException { + if (from == null || to == null || !from.isDirectory() || !to.isDirectory()) { + return; + } + final Path targetPath = to.toPath(); + Files.walkFileTree(from.toPath(), new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) + throws IOException { + Files.createDirectories(targetPath.resolve(from.toPath().relativize(dir))); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException { + Files.copy(file, targetPath.resolve(from.toPath().relativize(file)), + StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); + return FileVisitResult.CONTINUE; + } + }); + } + + private IFolder getProjectFolder(IPath path) { + if (path != null) { + IWorkspace workspace = ResourcesPlugin.getWorkspace(); + return workspace.getRoot().getFolder(path); + } + return null; } private void deleteAllProjects() throws CoreException { diff --git a/tycho-its/projects/api-tools/.mvn/maven.config b/tycho-its/projects/api-tools/api-break/.mvn/maven.config similarity index 100% rename from tycho-its/projects/api-tools/.mvn/maven.config rename to tycho-its/projects/api-tools/api-break/.mvn/maven.config diff --git a/tycho-its/projects/api-tools/api-repo/category.xml b/tycho-its/projects/api-tools/api-break/api-repo/category.xml similarity index 100% rename from tycho-its/projects/api-tools/api-repo/category.xml rename to tycho-its/projects/api-tools/api-break/api-repo/category.xml diff --git a/tycho-its/projects/api-tools/api-repo/pom.xml b/tycho-its/projects/api-tools/api-break/api-repo/pom.xml similarity index 100% rename from tycho-its/projects/api-tools/api-repo/pom.xml rename to tycho-its/projects/api-tools/api-break/api-repo/pom.xml diff --git a/tycho-its/projects/api-tools/bundle1/.classpath b/tycho-its/projects/api-tools/api-break/bundle1/.classpath similarity index 100% rename from tycho-its/projects/api-tools/bundle1/.classpath rename to tycho-its/projects/api-tools/api-break/bundle1/.classpath diff --git a/tycho-its/projects/api-tools/bundle1/.project b/tycho-its/projects/api-tools/api-break/bundle1/.project similarity index 100% rename from tycho-its/projects/api-tools/bundle1/.project rename to tycho-its/projects/api-tools/api-break/bundle1/.project diff --git a/tycho-its/projects/api-tools/bundle1/META-INF/MANIFEST.MF b/tycho-its/projects/api-tools/api-break/bundle1/META-INF/MANIFEST.MF similarity index 100% rename from tycho-its/projects/api-tools/bundle1/META-INF/MANIFEST.MF rename to tycho-its/projects/api-tools/api-break/bundle1/META-INF/MANIFEST.MF diff --git a/tycho-its/projects/api-tools/bundle1/build.properties b/tycho-its/projects/api-tools/api-break/bundle1/build.properties similarity index 100% rename from tycho-its/projects/api-tools/bundle1/build.properties rename to tycho-its/projects/api-tools/api-break/bundle1/build.properties diff --git a/tycho-its/projects/api-tools/bundle1/pom.xml b/tycho-its/projects/api-tools/api-break/bundle1/pom.xml similarity index 100% rename from tycho-its/projects/api-tools/bundle1/pom.xml rename to tycho-its/projects/api-tools/api-break/bundle1/pom.xml diff --git a/tycho-its/projects/api-tools/bundle1/src/bundle/ApiInterface.java b/tycho-its/projects/api-tools/api-break/bundle1/src/bundle/ApiInterface.java similarity index 100% rename from tycho-its/projects/api-tools/bundle1/src/bundle/ApiInterface.java rename to tycho-its/projects/api-tools/api-break/bundle1/src/bundle/ApiInterface.java diff --git a/tycho-its/projects/api-tools/bundle1/src/bundle/ClassA.java b/tycho-its/projects/api-tools/api-break/bundle1/src/bundle/ClassA.java similarity index 100% rename from tycho-its/projects/api-tools/bundle1/src/bundle/ClassA.java rename to tycho-its/projects/api-tools/api-break/bundle1/src/bundle/ClassA.java diff --git a/tycho-its/projects/api-tools/bundle1/src/bundle/InterfaceB.java b/tycho-its/projects/api-tools/api-break/bundle1/src/bundle/InterfaceB.java similarity index 100% rename from tycho-its/projects/api-tools/bundle1/src/bundle/InterfaceB.java rename to tycho-its/projects/api-tools/api-break/bundle1/src/bundle/InterfaceB.java diff --git a/tycho-its/projects/api-tools/bundle2/.classpath b/tycho-its/projects/api-tools/api-break/bundle2/.classpath similarity index 100% rename from tycho-its/projects/api-tools/bundle2/.classpath rename to tycho-its/projects/api-tools/api-break/bundle2/.classpath diff --git a/tycho-its/projects/api-tools/bundle2/.project b/tycho-its/projects/api-tools/api-break/bundle2/.project similarity index 100% rename from tycho-its/projects/api-tools/bundle2/.project rename to tycho-its/projects/api-tools/api-break/bundle2/.project diff --git a/tycho-its/projects/api-tools/bundle2/META-INF/MANIFEST.MF b/tycho-its/projects/api-tools/api-break/bundle2/META-INF/MANIFEST.MF similarity index 100% rename from tycho-its/projects/api-tools/bundle2/META-INF/MANIFEST.MF rename to tycho-its/projects/api-tools/api-break/bundle2/META-INF/MANIFEST.MF diff --git a/tycho-its/projects/api-tools/bundle2/build.properties b/tycho-its/projects/api-tools/api-break/bundle2/build.properties similarity index 100% rename from tycho-its/projects/api-tools/bundle2/build.properties rename to tycho-its/projects/api-tools/api-break/bundle2/build.properties diff --git a/tycho-its/projects/api-tools/bundle2/pom.xml b/tycho-its/projects/api-tools/api-break/bundle2/pom.xml similarity index 100% rename from tycho-its/projects/api-tools/bundle2/pom.xml rename to tycho-its/projects/api-tools/api-break/bundle2/pom.xml diff --git a/tycho-its/projects/api-tools/bundle2/src/bundle/ApiInterface2.java b/tycho-its/projects/api-tools/api-break/bundle2/src/bundle/ApiInterface2.java similarity index 100% rename from tycho-its/projects/api-tools/bundle2/src/bundle/ApiInterface2.java rename to tycho-its/projects/api-tools/api-break/bundle2/src/bundle/ApiInterface2.java diff --git a/tycho-its/projects/api-tools/pom.xml b/tycho-its/projects/api-tools/api-break/pom.xml similarity index 100% rename from tycho-its/projects/api-tools/pom.xml rename to tycho-its/projects/api-tools/api-break/pom.xml diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java index 5f3f8f7db6..f19d313a43 100644 --- a/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java +++ b/tycho-its/src/test/java/org/eclipse/tycho/test/apitools/ApiToolsTest.java @@ -44,9 +44,9 @@ public void testGenerate() throws Exception { } @Test - public void testVerify() throws Exception { + public void testApiBreak() throws Exception { Verifier verifier = getVerifier("api-tools", true, true); - File repo = ResourceUtil.resolveTestResource("repositories/api-tools"); + File repo = ResourceUtil.resolveTestResource("repositories/api-tools/api-break"); verifier.addCliOption("-DbaselineRepo=" + repo.toURI()); assertThrows(VerificationException.class, () -> verifier.executeGoals(List.of("clean", "verify")), () -> {