Skip to content

Commit

Permalink
Synthesize a (bnd) clazz from the JDT model to support export annotation
Browse files Browse the repository at this point in the history
Export annotations are already parsed and the package is given to the
analyzer, but if no export instruction is present the bnd analyzer does
throw them away as it does not know about it in a later stage.

This now synthesize a (bnd) class to emulate what the analyzer would see
for a real class file and inject that instance into the analyzer so the
package is finally considdered.

(cherry picked from commit 140bc01)
  • Loading branch information
laeubi authored and eclipse-tycho-bot committed May 17, 2024
1 parent c930087 commit f90f211
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -183,7 +186,25 @@ private static Map<String, Dependency> collectInitial(MavenProject project, Map<

private Collection<IInstallableUnit> generateWithProcessor(MavenProject project, Processor processor,
Collection<Artifact> 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);
Expand All @@ -197,8 +218,7 @@ private Collection<IInstallableUnit> 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();
Expand All @@ -210,6 +230,12 @@ private Collection<IInstallableUnit> 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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -35,19 +37,25 @@

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;

/**
* Enhances the analyzed classes by information obtained from the source code
*/
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<Path> sourcePaths;
private Map<PackageRef, Clazz> packageInfoMap = new HashMap<>();

public SourceCodeAnalyzerPlugin(List<Path> sourcePaths) {
this.sourcePaths = sourcePaths;
Expand All @@ -56,7 +64,6 @@ public SourceCodeAnalyzerPlugin(List<Path> sourcePaths) {
@Override
public boolean analyzeJar(Analyzer analyzer) throws Exception {
ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
Descriptors descriptors = new Descriptors();
Set<String> seenPackages = new HashSet<>();
Set<Path> analyzedPath = new HashSet<>();
for (Path sourcePath : sourcePaths) {
Expand All @@ -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;
Expand All @@ -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()) {
Expand All @@ -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 {
Expand Down Expand Up @@ -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<TypeRef> 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<TypeRef> annotations() {
return annotations;
}

}

}
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit f90f211

Please sign in to comment.