Skip to content

Commit

Permalink
Add p2-aware model converter for CycloneDX SBOM generation
Browse files Browse the repository at this point in the history
  • Loading branch information
ptziegler committed Jan 24, 2024
1 parent bf04783 commit 521d937
Show file tree
Hide file tree
Showing 20 changed files with 1,061 additions and 41 deletions.
3 changes: 2 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2008, 2022 Sonatype Inc. and others.
- Copyright (c) 2008, 2024 Sonatype Inc. and others.
- All rights reserved. This program and the accompanying materials
- are made available under the terms of the Eclipse Public License v1.0
- which accompanies this distribution, and is available at
Expand Down Expand Up @@ -537,6 +537,7 @@
<module>tycho-ds-plugin</module>
<module>tycho-buildtimestamp-jgit</module>
<module>tycho-baseline-plugin</module>
<module>tycho-sbom</module>
<!-- surefire -->
<module>tycho-surefire</module>
<!-- release -->
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.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -67,7 +67,7 @@ public File findArtifact(Artifact artifact) {
}).orElse(null);
}

private Optional<MavenProject> getTychoReactorProject(Artifact artifact) {
public Optional<MavenProject> getTychoReactorProject(Artifact artifact) {
if (isTychoReactorArtifact(artifact)) {
String projectKey = ArtifactUtils.key(artifact.getGroupId(), artifact.getArtifactId(),
artifact.getVersion());
Expand All @@ -81,13 +81,20 @@ private Optional<MavenProject> getTychoReactorProject(Artifact artifact) {
return Optional.empty();
}

private boolean isTychoReactorArtifact(Artifact artifact) {
public boolean isTychoReactorArtifact(Artifact artifact) {
if (artifact.getClassifier() == null || artifact.getClassifier().isBlank()) {
return PackagingType.TYCHO_PACKAGING_TYPES.contains(artifact.getProperty("type", ""));
return PackagingType.TYCHO_PACKAGING_TYPES.contains(getPackagingType(artifact));
}
return false;
}

public String getPackagingType(Artifact artifact) {
if (artifact != null) {
return artifact.getProperty("type", "");
}
return null;
}

@Override
public List<String> findVersions(Artifact artifact) {
return getTychoReactorProject(artifact).map(project -> List.of(artifact.getVersion())).orElse(List.of());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*******************************************************************************
* Copyright (c) 2024 Patrick Ziegler 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:
* Patrick Ziegler - initial API and implementation
*******************************************************************************/

package org.eclipse.tycho.p2.tools;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;

import org.eclipse.equinox.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.p2.metadata.IRequirement;

/**
* Utility class for converting a flat dependency into a dependency tree. The tree is structured in
* such a way that an IU {@code a} is a child of another IU {@code b}, if and only if {@code a} is
* required by {@code b}. If {@code b} is required by multiple IUs, the first one is selected.<br>
* Used by e.g. the dependency-tree Mojo, in order to mimic the behavior of the native Maven
* dependency-tree Mojo.
*/
public final class DependencyTreeNode {
private static final Comparator<IInstallableUnit> COMPARATOR = Comparator.comparing(IInstallableUnit::getId,
String.CASE_INSENSITIVE_ORDER);
private final IInstallableUnit iu;
private final IRequirement satisfies;
private final List<DependencyTreeNode> children = new ArrayList<>();

private DependencyTreeNode(IInstallableUnit iu, IRequirement satisfies) {
this.iu = iu;
this.satisfies = satisfies;
}

public IInstallableUnit getInstallableUnit() {
return iu;
}

public IRequirement getRequirement() {
return satisfies;
}

public List<DependencyTreeNode> getChildren() {
return Collections.unmodifiableList(children);
}

/**
* Create the dependency tree based on the {@code initial} IUs. A tree node is created for each
* IU of {@code initial}. The children of a node correspond to all IUs that are (directly)
* required by the parent IU. Each IU in {@code units} only appears once, even if it required by
* multiple IUs.
*
* @param initial
* The "direct" IUs referenced by a given artifact.
* @param units
* All IUs that are required by a given artifact, excluding {@code initial}.
* @param unmapped
* A subset of {@code units}, which are not contained in the dependency tree. Meaning
* that those IUs are not necessarily required to satisfy the dependencies of an
* aritfact.
* @return A list of dependency tree models. Each model in this list matches an IU of
* {@code initial}.
*/
public static List<DependencyTreeNode> create(List<IInstallableUnit> initial, Set<IInstallableUnit> units,
Set<IInstallableUnit> unmapped) {
List<DependencyTreeNode> rootNodes = new ArrayList<>();
for (int i = 0; i < initial.size(); ++i) {
DependencyTreeNode rootNode = new DependencyTreeNode(initial.get(i), null);
create(rootNode, initial.get(i), units);
rootNodes.add(rootNode);
}
unmapped.addAll(units);
return rootNodes;
}

private static void create(DependencyTreeNode node, IInstallableUnit unit, Set<IInstallableUnit> units) {
List<IInstallableUnit> collected = new ArrayList<>();
Map<IInstallableUnit, IRequirement> requirementsMap = new HashMap<>();
//
Stream.concat(unit.getRequirements().stream(), unit.getMetaRequirements().stream()).forEach(requirement -> {
for (Iterator<IInstallableUnit> iterator = units.iterator(); iterator.hasNext();) {
IInstallableUnit other = iterator.next();
if (other.satisfies(requirement)) {
collected.add(other);
requirementsMap.put(other, requirement);
iterator.remove();
}
}
});
//
Collections.sort(collected, COMPARATOR);
for (IInstallableUnit iu : collected) {
IRequirement satisfies = requirementsMap.get(iu);
DependencyTreeNode childNode = new DependencyTreeNode(iu, satisfies);
node.children.add(childNode);
create(childNode, iu, units);
}
}

/**
* Returns the IU (if present) that is contained by this node. Overwritten to make debugging
* easier.
*/
@Override
public String toString() {
return Objects.toString(iu);
}
}
8 changes: 7 additions & 1 deletion tycho-its/pom.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2008, 2022 Sonatype Inc. and others.
- Copyright (c) 2008, 2024 Sonatype Inc. and others.
- All rights reserved. This program and the accompanying materials
- are made available under the terms of the Eclipse Public License v1.0
- which accompanies this distribution, and is available at
Expand Down Expand Up @@ -214,6 +214,12 @@
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<!-- sbom tests -->
<dependency>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-core-java</artifactId>
<version>8.0.3</version>
</dependency>

<!-- artifacts to be tested -->
<dependency>
Expand Down
8 changes: 8 additions & 0 deletions tycho-its/projects/sbom/.mvn/extensions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<extensions>
<extension>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-build</artifactId>
<version>${tycho-version}</version>
</extension>
</extensions>
1 change: 1 addition & 0 deletions tycho-its/projects/sbom/.mvn/maven.config
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
-Dtycho-version=5.0.0-SNAPSHOT
1 change: 1 addition & 0 deletions tycho-its/projects/sbom/example.feature/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin.includes = feature.xml
23 changes: 23 additions & 0 deletions tycho-its/projects/sbom/example.feature/feature.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<feature
id="example.feature"
label="Feature with SBOM"
version="1.0.0.20240107">

<description url="http://www.example.com/description">
[Enter Feature Description here.]
</description>

<copyright url="http://www.example.com/copyright">
[Enter Copyright Description here.]
</copyright>

<license url="http://www.example.com/license">
[Enter License Description here.]
</license>

<plugin
id="example.plugin"
version="0.0.0"/>

</feature>
11 changes: 11 additions & 0 deletions tycho-its/projects/sbom/example.plugin/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Plugin with SBOM
Bundle-SymbolicName: example.plugin
Bundle-Version: 1.0.0.20240107
Require-Bundle: org.eclipse.core.databinding;bundle-version="1.13.100",
org.eclipse.core.databinding.beans;bundle-version="1.10.100",
org.eclipse.core.databinding.observable;bundle-version="1.13.100",
org.eclipse.core.databinding.property;bundle-version="1.10.100"
Automatic-Module-Name: example.plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
4 changes: 4 additions & 0 deletions tycho-its/projects/sbom/example.plugin/build.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
78 changes: 78 additions & 0 deletions tycho-its/projects/sbom/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>tycho-demo</groupId>
<artifactId>sbom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>

<properties>
<tycho-version>5.0.0-SNAPSHOT</tycho-version>
<tycho.sbom.url>https://www.example.p2.repo/</tycho.sbom.url>
</properties>

<modules>
<module>example.feature</module>
<module>example.plugin</module>
<module>product</module>
<module>repository</module>
</modules>

<build>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-maven-plugin</artifactId>
<version>${tycho-version}</version>
<extensions>true</extensions>
</plugin>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>target-platform-configuration</artifactId>
<version>${tycho-version}</version>
<configuration>
<target>
<file>../target-definition.target</file>
</target>
<environments>
<environment>
<os>linux</os>
<ws>gtk</ws>
<arch>x86_64</arch>
</environment>
</environments>
</configuration>
</plugin>
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.7.9</version>
<configuration>
<includeTestScope>true</includeTestScope>
</configuration>
<executions>
<execution>
<goals>
<goal>makeBom</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-sbom</artifactId>
<version>${tycho-version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-p2-director-plugin</artifactId>
<version>${tycho-version}</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
29 changes: 29 additions & 0 deletions tycho-its/projects/sbom/product/example.product
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<?pde version="3.5"?>

<product uid="example" version="1.0.0.20240107" type="mixed" includeLaunchers="true" autoIncludeRequirements="false">

<configIni use="default">
</configIni>

<launcherArgs>
<vmArgsMac>-XstartOnFirstThread -Dorg.eclipse.swt.internal.carbon.smallFonts
</vmArgsMac>
</launcherArgs>

<plugins>
<plugin id="example.plugin"/>
<plugin id="org.eclipse.core.databinding"/>
<plugin id="org.eclipse.core.databinding.beans"/>
<plugin id="org.eclipse.core.databinding.observable"/>
<plugin id="org.eclipse.core.databinding.property"/>
<plugin id="org.eclipse.equinox.common"/>
<plugin id="org.eclipse.osgi"/>
</plugins>

<features>
<feature id="example.feature" installMode="root"/>
</features>


</product>
5 changes: 5 additions & 0 deletions tycho-its/projects/sbom/repository/category.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<site>
<feature id="example.feature"/>
<bundle id="example.plugin"/>
</site>
11 changes: 11 additions & 0 deletions tycho-its/projects/sbom/target-definition.target
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?pde version="3.8"?>
<target name="target-definition">
<locations>
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
<repository location="https://download.eclipse.org/releases/2023-12/"/>
<unit id="org.eclipse.sdk.feature.group" version="0.0.0"/>
<unit id="org.eclipse.equinox.sdk.feature.group" version="0.0.0"/>
</location>
</locations>
</target>
Loading

0 comments on commit 521d937

Please sign in to comment.