Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for building sub-bundle with tycho-bnd-extension #4666

Merged
merged 1 commit into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ bin/
*.iml
.idea/
.tycho-consumer-pom.xml
.polyglot.*
## Eclipse metadata files
org.eclipse.core.resources.prefs
org.eclipse.m2e.core.prefs
Expand All @@ -21,3 +22,4 @@ pom-model-classic.xml
project-dependencies.xml
project-units.xml
requirements.txt
.DS_Store
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;

import org.apache.maven.AbstractMavenLifecycleParticipant;
import org.apache.maven.MavenExecutionException;
Expand All @@ -36,6 +37,7 @@
import org.eclipse.tycho.core.bnd.BndPluginManager;

import aQute.bnd.build.Project;
import aQute.bnd.build.SubProject;
import aQute.bnd.build.Workspace;
import aQute.bnd.osgi.Constants;

Expand Down Expand Up @@ -69,46 +71,66 @@ public class BndMavenLifecycleParticipant extends AbstractMavenLifecycleParticip
@Override
public void afterProjectsRead(MavenSession session) throws MavenExecutionException {
Map<MavenProject, Project> bndProjects = getProjects(session);
Map<String, MavenProject> manifestFirstProjects = getManifestFirstProjects(session, bndProjects.keySet());
Map<String, MavenProject> bndWorkspaceProjects = bndProjects.entrySet().stream()
.collect(Collectors.toMap(e -> e.getValue().getName(), Entry<MavenProject, Project>::getKey, (a, b) -> {
logger.warn(
"Your reactor build contains duplicate BND projects from different workspace, build order might be insufficient!");
logger.warn("\tProject 1 (selected): " + a.getBasedir());
logger.warn("\tProject 2 (ignored): " + b.getBasedir());
return a;
}));
Map<String, BndMavenProject> manifestFirstProjects = getManifestFirstProjects(session, bndProjects.keySet());
Map<String, BndMavenProject> bndWorkspaceProjects = new HashMap<>();
for (Entry<MavenProject, Project> entry : bndProjects.entrySet()) {
MavenProject mavenProject = entry.getKey();
Project project = entry.getValue();
logger.debug("==" + mavenProject.getId() + "==");
List<SubProject> subProjects = project.getSubProjects();
logger.debug("Main: " + project.getName());
if (subProjects.isEmpty()) {
bndWorkspaceProjects.put(project.getName(), new BndMavenProject(mavenProject, project, null));
} else {
for (SubProject subProject : subProjects) {
logger.debug("Sub: " + subProject.getName());
bndWorkspaceProjects.put(project.getName() + "." + subProject.getName(),
new BndMavenProject(mavenProject, project, subProject.getName()));
}
}
}
Map<MavenProject, Set<String>> dependencyMap = new HashMap<>();
for (Entry<MavenProject, Project> entry : bndProjects.entrySet()) {
MavenProject mavenProject = entry.getKey();
Set<String> added = getProjectSet(mavenProject, dependencyMap);
Project bndProject = entry.getValue();
try {

for (Entry<String, String> mapping : BND_TO_MAVEN_MAPPING) {
Set<String> requirements = bndProject.getMergedParameters(mapping.getKey()).keySet();
String mavenScope = mapping.getValue();
for (String required : requirements) {
MavenProject requiredMavenProject = bndWorkspaceProjects.get(required);
if (requiredMavenProject == null) {
requiredMavenProject = manifestFirstProjects.get(required);
BndMavenProject bndMavenProject = bndWorkspaceProjects.get(required);
if (bndMavenProject == null) {
bndMavenProject = manifestFirstProjects.get(required);
}
if (requiredMavenProject == null || requiredMavenProject == mavenProject) {
if (bndMavenProject == null || bndMavenProject.mavenProject() == mavenProject) {
continue;
}
MavenProject requiredMavenProject = bndMavenProject.mavenProject();
logger.debug(mavenProject.getId() + " depends on reactor project "
+ requiredMavenProject.getId() + " ...");
Dependency dependency = new Dependency();
dependency.setGroupId(requiredMavenProject.getGroupId());
dependency.setArtifactId(requiredMavenProject.getArtifactId());
dependency.setVersion(requiredMavenProject.getVersion());
dependency.setScope(mavenScope);
mavenProject.getDependencies().add(dependency);
if (bndMavenProject.classifier() != null) {
Dependency clone = dependency.clone();
clone.setClassifier(bndMavenProject.classifier());
clone.setType("jar");
addDependency(mavenProject, clone, added);
}
dependency.setType(requiredMavenProject.getPackaging());
addDependency(mavenProject, dependency, added);
}
}
} catch (Exception e) {
logError("Can't get dependents of project " + mavenProject.getId(), e);
}
}
for (MavenProject mavenProject : manifestFirstProjects.values()) {
for (BndMavenProject bndMavenProject : manifestFirstProjects.values()) {
MavenProject mavenProject = bndMavenProject.mavenProject();
Set<String> added = getProjectSet(mavenProject, dependencyMap);
try {
File file = new File(mavenProject.getBasedir(), "build.properties");
if (file.isFile()) {
Expand All @@ -121,17 +143,17 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti
continue;
}
for (String bundle : property.split(",")) {
MavenProject requiredMavenProject = bndWorkspaceProjects.get(bundle.trim());
if (requiredMavenProject == null || requiredMavenProject == mavenProject) {
BndMavenProject requiredMavenProject = bndWorkspaceProjects.get(bundle.trim());
if (requiredMavenProject == null || requiredMavenProject.mavenProject() == mavenProject) {
continue;
}
logger.debug(mavenProject.getId() + " depends on reactor project "
+ requiredMavenProject.getId() + " ...");
+ requiredMavenProject.mavenProject().getId() + " ...");
Dependency dependency = new Dependency();
dependency.setGroupId(requiredMavenProject.getGroupId());
dependency.setArtifactId(requiredMavenProject.getArtifactId());
dependency.setVersion(requiredMavenProject.getVersion());
mavenProject.getDependencies().add(dependency);
dependency.setGroupId(requiredMavenProject.mavenProject().getGroupId());
dependency.setArtifactId(requiredMavenProject.mavenProject().getArtifactId());
dependency.setVersion(requiredMavenProject.mavenProject().getVersion());
addDependency(mavenProject, dependency, added);
}
}
} catch (Exception e) {
Expand All @@ -141,8 +163,8 @@ public void afterProjectsRead(MavenSession session) throws MavenExecutionExcepti
}
}

private Map<String, MavenProject> getManifestFirstProjects(MavenSession session, Set<MavenProject> existing) {
Map<String, MavenProject> result = new HashMap<String, MavenProject>();
private Map<String, BndMavenProject> getManifestFirstProjects(MavenSession session, Set<MavenProject> existing) {
Map<String, BndMavenProject> result = new HashMap<>();
for (MavenProject mavenProject : session.getProjects()) {
if (existing.contains(mavenProject)) {
continue;
Expand All @@ -155,7 +177,7 @@ private Map<String, MavenProject> getManifestFirstProjects(MavenSession session,
String value = manifest.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
if (value != null) {
String bsn = value.split(";", 2)[0];
result.put(bsn, mavenProject);
result.put(bsn, new BndMavenProject(mavenProject, null, null));
}
} catch (Exception e) {
logError("Can't read project " + mavenProject.getId(), e);
Expand Down Expand Up @@ -208,4 +230,21 @@ private void logError(String msg, Exception e) {
}
}

private static Set<String> getProjectSet(MavenProject mavenProject, Map<MavenProject, Set<String>> dependencyMap) {
Set<String> set = dependencyMap.computeIfAbsent(mavenProject, nil -> new HashSet<>());
mavenProject.getDependencies().stream().map(d -> getKey(d)).forEach(set::add);
return set;
}

private static void addDependency(MavenProject mavenProject, Dependency dependency, Set<String> added) {
if (added.add(getKey(dependency))) {
mavenProject.getDependencies().add(dependency);
}
}

private static String getKey(Dependency dependency) {
return dependency.getManagementKey() + ":" + dependency.getVersion() + ":"
+ Objects.requireNonNullElse(dependency.getClassifier(), "");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2025 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
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.tycho.bnd.maven;

import org.apache.maven.project.MavenProject;

import aQute.bnd.build.Project;

record BndMavenProject(MavenProject mavenProject, Project project, String classifier) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public final void execute() throws MojoExecutionException, MojoFailureException

}

private void checkResult(Report report, boolean errorOk) throws MojoFailureException {
protected void checkResult(Report report, boolean errorOk) throws MojoFailureException {
List<String> warnings = report.getWarnings();
for (String warning : warnings) {
getLog().warn(warning);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,51 @@
package org.eclipse.tycho.bnd.mojos;

import java.io.File;
import java.util.List;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProjectHelper;

import aQute.bnd.build.Project;
import aQute.bnd.build.ProjectBuilder;
import aQute.bnd.build.SubProject;
import aQute.bnd.osgi.Jar;

@Mojo(name = "build", defaultPhase = LifecyclePhase.PACKAGE, requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true)
public class BndBuildMojo extends AbstractBndProjectMojo {

@Component
MavenProjectHelper helper;

@Override
protected void execute(Project project) throws Exception {
File[] files = project.build();
if (files != null && files.length > 0) {
Artifact artifact = mavenProject.getArtifact();
artifact.setFile(files[0]);
List<SubProject> subProjects = project.getSubProjects();
if (subProjects.isEmpty()) {
getLog().info(String.format("Building bundle '%s'", project.getName()));
File[] files = project.build();
if (files != null && files.length > 0) {
Artifact artifact = mavenProject.getArtifact();
artifact.setFile(files[0]);
}
} else {
for (SubProject subProject : subProjects) {
ProjectBuilder builder = subProject.getProjectBuilder();
getLog().info(String.format("Building sub bundle '%s'", subProject.getName()));
Jar jar = builder.build();
checkResult(builder, project.getWorkspace().isFailOk());
String jarName = jar.getName();
String name = subProject.getName();
File file = new File(mavenProject.getBuild().getDirectory(), String.format("%s.jar", jarName));
file.getParentFile().mkdirs();
jar.write(file);
helper.attachArtifact(mavenProject, "jar", name, file);
}
}

}

}
Loading
Loading