From 2cfe7ed5b1a9c6fe93ecf10e51f6de9c16a0c9a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Mon, 6 Nov 2023 20:50:24 +0100 Subject: [PATCH] Set the real output location for embedded jars in ApiAnalysis Currently when one configures embedded extra jars these are not found by the ApiAnalysis because they are placed in different location than the main classes. THis computes all jars and compares there resulting path with the current output location updating those if needed. --- .../eclipse/tycho/apitools/ApiAnalysis.java | 126 ++++++++++++++++-- .../{ => api-break}/.mvn/maven.config | 0 .../{ => api-break}/api-repo/category.xml | 0 .../{ => api-break}/api-repo/pom.xml | 0 .../{ => api-break}/bundle1/.classpath | 0 .../{ => api-break}/bundle1/.project | 0 .../bundle1/META-INF/MANIFEST.MF | 0 .../{ => api-break}/bundle1/build.properties | 0 .../api-tools/{ => api-break}/bundle1/pom.xml | 0 .../bundle1/src/bundle/ApiInterface.java | 0 .../bundle1/src/bundle/ClassA.java | 0 .../bundle1/src/bundle/InterfaceB.java | 0 .../{ => api-break}/bundle2/.classpath | 0 .../{ => api-break}/bundle2/.project | 0 .../bundle2/META-INF/MANIFEST.MF | 0 .../{ => api-break}/bundle2/build.properties | 0 .../api-tools/{ => api-break}/bundle2/pom.xml | 0 .../bundle2/src/bundle/ApiInterface2.java | 0 .../api-tools/{ => api-break}/pom.xml | 0 .../tycho/test/apitools/ApiToolsTest.java | 4 +- 20 files changed, 119 insertions(+), 11 deletions(-) rename tycho-its/projects/api-tools/{ => api-break}/.mvn/maven.config (100%) rename tycho-its/projects/api-tools/{ => api-break}/api-repo/category.xml (100%) rename tycho-its/projects/api-tools/{ => api-break}/api-repo/pom.xml (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/.classpath (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/.project (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/META-INF/MANIFEST.MF (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/build.properties (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/pom.xml (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/src/bundle/ApiInterface.java (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/src/bundle/ClassA.java (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle1/src/bundle/InterfaceB.java (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/.classpath (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/.project (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/META-INF/MANIFEST.MF (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/build.properties (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/pom.xml (100%) rename tycho-its/projects/api-tools/{ => api-break}/bundle2/src/bundle/ApiInterface2.java (100%) rename tycho-its/projects/api-tools/{ => api-break}/pom.xml (100%) 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")), () -> {