diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/PdeInstallableUnitProvider.java b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/PdeInstallableUnitProvider.java index 842bdb815c..d968b5dbd9 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/PdeInstallableUnitProvider.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/PdeInstallableUnitProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023 Christoph Läubrich and others. + * Copyright (c) 2023, 2024 Christoph Läubrich and others. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -49,7 +49,10 @@ import org.eclipse.tycho.resolver.InstallableUnitProvider; import aQute.bnd.osgi.Builder; +import aQute.bnd.osgi.Clazz; import aQute.bnd.osgi.Constants; +import aQute.bnd.osgi.Descriptors.PackageRef; +import aQute.bnd.osgi.Descriptors.TypeRef; import aQute.bnd.osgi.Jar; import aQute.bnd.osgi.Processor; import aQute.bnd.version.MavenVersion; @@ -183,7 +186,25 @@ private static Map collectInitial(MavenProject project, Map< private Collection generateWithProcessor(MavenProject project, Processor processor, Collection artifacts) throws Exception { - try (Builder analyzer = new Builder(processor)) { + SourceCodeAnalyzerPlugin plugin = new SourceCodeAnalyzerPlugin( + project.getCompileSourceRoots().stream().map(Path::of).toList()); + try (Builder analyzer = new Builder(processor) { + @Override + public Clazz getPackageInfo(PackageRef packageRef) { + Clazz info = super.getPackageInfo(packageRef); + if (info == null) { + return plugin.getPackageInfo(packageRef); + } + return info; + } + + @Override + public Clazz findClass(TypeRef typeRef) throws Exception { + //TODO instead of override the getPackageInfo(...) we can also use this but + //in that case we probably need to implement more in the JDTClazz as it is called from different places + return super.findClass(typeRef); + }; + }) { analyzer.setBase(project.getBasedir()); Jar jar = new Jar(project.getArtifactId()); analyzer.setJar(jar); @@ -197,8 +218,7 @@ private Collection generateWithProcessor(MavenProject project, } } } - analyzer.addBasicPlugin( - new SourceCodeAnalyzerPlugin(project.getCompileSourceRoots().stream().map(Path::of).toList())); + analyzer.addBasicPlugin(plugin); analyzer.setProperty(Constants.NOEXTRAHEADERS, "true"); analyzer.build(); Manifest manifest = jar.getManifest(); @@ -210,6 +230,12 @@ private Collection generateWithProcessor(MavenProject project, ManifestUtil.write(manifest, outputStream); String str = new String(outputStream.toByteArray(), StandardCharsets.UTF_8); logger.debug("Generated preliminary manifest for " + project.getId() + ":\r\n" + str); + for (String error : analyzer.getErrors()) { + logger.debug("ERROR: " + error); + } + for (String warn : analyzer.getWarnings()) { + logger.debug("WARN: " + warn); + } } return installableUnitGenerator.getInstallableUnits(manifest); } diff --git a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/SourceCodeAnalyzerPlugin.java b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/SourceCodeAnalyzerPlugin.java index 3c9988ff73..7c05f69deb 100644 --- a/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/SourceCodeAnalyzerPlugin.java +++ b/tycho-core/src/main/java/org/eclipse/tycho/core/bnd/SourceCodeAnalyzerPlugin.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023 Christoph Läubrich and others. + * Copyright (c) 2023, 2024 Christoph Läubrich and others. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -18,8 +18,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.jdt.core.dom.AST; @@ -35,8 +37,11 @@ import aQute.bnd.header.Attrs; import aQute.bnd.osgi.Analyzer; -import aQute.bnd.osgi.Descriptors; +import aQute.bnd.osgi.Clazz; import aQute.bnd.osgi.Descriptors.PackageRef; +import aQute.bnd.osgi.Descriptors.TypeRef; +import aQute.bnd.osgi.FileResource; +import aQute.bnd.osgi.Resource; import aQute.bnd.service.AnalyzerPlugin; /** @@ -44,10 +49,13 @@ */ class SourceCodeAnalyzerPlugin implements AnalyzerPlugin { + private static final String PACKAGE_INFO = "package-info"; private static final String ANNOTATION_VERSION = "org.osgi.annotation.versioning.Version"; private static final String ANNOTATION_EXPORT = "org.osgi.annotation.bundle.Export"; - private static final String PACKAGE_INFO_JAVA = "package-info.java"; + private static final String PACKAGE_INFO_JAVA = PACKAGE_INFO + ".java"; + private static final String PACKAGE_INFO_CLASS = PACKAGE_INFO + ".class"; private List sourcePaths; + private Map packageInfoMap = new HashMap<>(); public SourceCodeAnalyzerPlugin(List sourcePaths) { this.sourcePaths = sourcePaths; @@ -56,7 +64,6 @@ public SourceCodeAnalyzerPlugin(List sourcePaths) { @Override public boolean analyzeJar(Analyzer analyzer) throws Exception { ASTParser parser = ASTParser.newParser(AST.getJLSLatest()); - Descriptors descriptors = new Descriptors(); Set seenPackages = new HashSet<>(); Set analyzedPath = new HashSet<>(); for (Path sourcePath : sourcePaths) { @@ -80,12 +87,16 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO PackageDeclaration packageDecl = cu.getPackage(); if (packageDecl != null) { String packageFqdn = packageDecl.getName().getFullyQualifiedName(); + PackageRef packageRef = analyzer.getPackageRef(packageFqdn); if (seenPackages.add(packageFqdn)) { //make the package available to bnd analyzer - PackageRef packageRef = descriptors.getPackageRef(packageFqdn); analyzer.getContained().put(packageRef); } if (packageInfo) { + JDTClazz clazz = new JDTClazz(analyzer, + packageRef.getBinary() + "/" + PACKAGE_INFO_CLASS, + new FileResource(file), + analyzer.getTypeRef(packageRef.getBinary() + "/" + PACKAGE_INFO)); //check for export annotations boolean export = false; String version = null; @@ -94,6 +105,8 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO String annotationFqdn = annot.getTypeName().getFullyQualifiedName(); if (ANNOTATION_EXPORT.equals(annotationFqdn)) { export = true; + clazz.addAnnotation( + analyzer.getTypeRef(ANNOTATION_EXPORT.replace('.', '/'))); } else if (ANNOTATION_VERSION.equals(annotationFqdn)) { if (annot instanceof NormalAnnotation normal) { for (Object vp : normal.values()) { @@ -112,7 +125,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } } if (export) { - PackageRef packageRef = descriptors.getPackageRef(packageFqdn); + packageInfoMap.put(packageRef, clazz); if (version == null) { analyzer.getContained().put(packageRef); } else { @@ -142,4 +155,33 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx return false; } + Clazz getPackageInfo(PackageRef packageRef) { + return packageInfoMap.get(packageRef); + } + + private static final class JDTClazz extends Clazz { + private Set annotations = new HashSet<>(); + private TypeRef className; + + public JDTClazz(Analyzer analyzer, String path, Resource resource, TypeRef className) { + super(analyzer, path, resource); + this.className = className; + } + + @Override + public TypeRef getClassName() { + return className; + } + + public void addAnnotation(TypeRef typeRef) { + annotations.add(typeRef); + } + + @Override + public Set annotations() { + return annotations; + } + + } + } diff --git a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java index 2cc8e6ad7b..6a3f04dace 100644 --- a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java +++ b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2023 Christoph Läubrich and others. + * Copyright (c) 2023, 2024 Christoph Läubrich and others. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at @@ -31,6 +31,7 @@ import org.codehaus.plexus.component.annotations.Requirement; import org.codehaus.plexus.logging.Logger; import org.eclipse.tycho.ClasspathEntry; +import org.eclipse.tycho.TychoConstants; import org.eclipse.tycho.classpath.ClasspathContributor; import org.eclipse.tycho.helper.PluginConfigurationHelper; import org.eclipse.tycho.helper.PluginRealmHelper; @@ -74,7 +75,10 @@ public BndManifestProcessor(MavenSession mavenSession) { @Override public void processManifest(MavenProject mavenProject, Manifest manifest) { - + if (new File(mavenProject.getBasedir(), TychoConstants.PDE_BND).exists()) { + // we don't want to process manifests already generated by BND! + return; + } if (configurationHelper.getConfiguration().getBoolean("deriveHeaderFromSource") // don't be confused here, we use FALSE as default because it means no such // configuration option defined in the mojo (probably called from different