This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ant-antlibs-cyclonedx.git

commit 2278e0992d71bcf9f80ad2bf2dfb5516a8eafe11
Author: Stefan Bodewig <[email protected]>
AuthorDate: Fri May 1 21:39:35 2026 +0200

    support dependencies
---
 src/main/org/apache/ant/cyclonedx/Component.java   | 59 ++++++++++++++++++++++
 .../org/apache/ant/cyclonedx/ComponentBomTask.java | 45 +++++++++++++++++
 src/tests/antunit/componentbom-test.xml            | 35 ++++++++++---
 3 files changed, 132 insertions(+), 7 deletions(-)

diff --git a/src/main/org/apache/ant/cyclonedx/Component.java 
b/src/main/org/apache/ant/cyclonedx/Component.java
index 8dd72a1..26d3fb5 100644
--- a/src/main/org/apache/ant/cyclonedx/Component.java
+++ b/src/main/org/apache/ant/cyclonedx/Component.java
@@ -6,6 +6,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
 import org.apache.tools.ant.types.Resource;
 import org.apache.tools.ant.types.resources.FileProvider;
 import org.apache.tools.ant.types.resources.URLResource;
@@ -29,6 +30,8 @@ public class Component {
     private List<org.cyclonedx.model.ExternalReference> externalReferences = 
new ArrayList<>();
     private org.cyclonedx.model.Component.Scope scope;
     private boolean isExternal = false;
+    private List<Dependency> dependencies = new ArrayList<>();
+    private boolean unknownDependencies = false;
 
     public void add(Resource resource) {
         if (this.resource != null) {
@@ -106,6 +109,22 @@ public class Component {
         this.isExternal = isExternal;
     }
 
+    public void addDependency(Dependency d) {
+        dependencies.add(d);
+    }
+
+    public Iterable<Dependency> getDependencies() {
+        return dependencies;
+    }
+
+    public void setUnknownDependencies(boolean unknownDependencies) {
+        this.unknownDependencies = unknownDependencies;
+    }
+
+    public boolean areDependenciesUnknown() {
+        return unknownDependencies;
+    }
+
     public org.cyclonedx.model.Component toMainCycloneDxComponent(Version 
bomVersion)
         throws IOException {
         if (isExternal) {
@@ -157,6 +176,8 @@ public class Component {
         String bomRef = getBomRef();
         if (bomRef != null) {
             component.setBomRef(bomRef);
+        } else if (!dependencies.isEmpty()) {
+            throw new BuildException("a component with dependencies must 
provide a bomRef");
         }
         if (!externalReferences.isEmpty()) {
             component.setExternalReferences(externalReferences);
@@ -264,4 +285,42 @@ public class Component {
             return r;
         }
     }
+
+    public static class Dependency extends ProjectComponent {
+        private String bomRef;
+        private String componentRef;
+
+        public void setBomRef(String bomRef) {
+            this.bomRef = bomRef;
+        }
+
+        public void setComponentRef(String componentRef) {
+            this.componentRef = componentRef;
+        }
+
+        public String getBomRef() {
+            if (bomRef == null && componentRef == null) {
+                throw new BuildException("bomRef or componentRef is required");
+            }
+            if (bomRef != null && componentRef != null) {
+                throw new BuildException("only one of bomRef and componentRef 
are permitted");
+            }
+            if (bomRef != null) {
+                return bomRef;
+            }
+
+            Object component = getProject().getReference(componentRef);
+            if (component == null) {
+                throw new BuildException("componentRef '" + componentRef + "' 
is unknown");
+            }
+            if (component instanceof Component) {
+                String b = ((Component) component).getBomRef();
+                if (b == null) {
+                    throw new BuildException("component with id '" + 
componentRef + "' doesn't provide a bomRef");
+                }
+                return b;
+            }
+            throw new BuildException("componentRef '" + componentRef + "' 
doesn't refer to a component");
+        }
+    }
 }
diff --git a/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java 
b/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
index 027371d..8bb5528 100644
--- a/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
+++ b/src/main/org/apache/ant/cyclonedx/ComponentBomTask.java
@@ -8,7 +8,9 @@ import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.UUID;
 
 import org.apache.tools.ant.BuildException;
@@ -21,6 +23,7 @@ import org.cyclonedx.generators.BomGeneratorFactory;
 import org.cyclonedx.generators.json.BomJsonGenerator;
 import org.cyclonedx.generators.xml.BomXmlGenerator;
 import org.cyclonedx.model.Bom;
+import org.cyclonedx.model.Dependency;
 import org.cyclonedx.model.LifecycleChoice;
 import org.cyclonedx.model.Lifecycles;
 import org.cyclonedx.model.Metadata;
@@ -93,9 +96,51 @@ public class ComponentBomTask extends Task {
             bom.setComponents(cs);
         }
 
+        addDependencies(bom);
+
         return bom;
     }
 
+    private void addDependencies(Bom bom) {
+        List<Dependency> dependencies = new ArrayList<>();
+        Set<String> bomRefs = new HashSet<>();
+        if (component.getBomRef() != null) {
+            bomRefs.add(component.getBomRef());
+        }
+        for (Component c : additionalComponents) {
+            if (c.getBomRef() != null) {
+                bomRefs.add(c.getBomRef());
+            }
+        }
+
+        if (component.getBomRef() != null) {
+            Dependency dep = new Dependency(component.getBomRef());
+            for (Component.Dependency d : component.getDependencies()) {
+                String br = d.getBomRef();
+                if (!bomRefs.contains(br)) {
+                    throw new BuildException("dependency '" + br + "' is 
unknown");
+                }
+                dep.addDependency(new Dependency(br));
+            }
+            dependencies.add(dep);
+        }
+        for (Component c : additionalComponents) {
+            if (!c.areDependenciesUnknown() && c.getBomRef() != null) {
+                Dependency dep = new Dependency(c.getBomRef());
+                for (Component.Dependency d : c.getDependencies()) {
+                    String br = d.getBomRef();
+                    if (!bomRefs.contains(br)) {
+                        throw new BuildException("dependency '" + br + "' is 
unknown");
+                    }
+                    dep.addDependency(new Dependency(br));
+                }
+                dependencies.add(dep);
+            }
+        }
+
+        bom.setDependencies(dependencies);
+    }
+
     private void writeBom(Bom bom, File bomFile) throws IOException, 
GeneratorException {
         switch (format) {
         case JSON:
diff --git a/src/tests/antunit/componentbom-test.xml 
b/src/tests/antunit/componentbom-test.xml
index 1c6ed7c..dfb61d0 100644
--- a/src/tests/antunit/componentbom-test.xml
+++ b/src/tests/antunit/componentbom-test.xml
@@ -268,18 +268,11 @@
 
   <target name="testMinimalAdditionalComponentData">
     <mkdir dir="${output}"/>
-    <cdx:componentbom bomfile="${output}/bom.json" format="JSON"
-                      xmlns:cdx="antlib:org.apache.ant.cyclonedx">
-      <component name="testname"/>
-      <additionalComponent name="dependency" scope="OPTIONAL"/>
-    </cdx:componentbom>
-    <copy file="${output}/bom.json" todir="/tmp"/>
     <cdx:componentbom bomfile="${output}/bom.xml" format="XML"
                       xmlns:cdx="antlib:org.apache.ant.cyclonedx">
       <component name="testname"/>
       <additionalComponent name="dependency" scope="OPTIONAL"/>
     </cdx:componentbom>
-    <copy file="${output}/bom.xml" todir="/tmp"/>
     <xmlproperty file="${output}/bom.xml"/>
     <au:assertPropertyEquals
         xmlns:au="antlib:org.apache.ant.antunit"
@@ -295,4 +288,32 @@
         value="optional"/>
   </target>
 
+  <target name="testDependencies">
+    <mkdir dir="${output}"/>
+    <cdx:componentbom bomfile="${output}/bom.xml" format="XML"
+                      xmlns:cdx="antlib:org.apache.ant.cyclonedx">
+      <component name="testname" group="org.example" version="1.0">
+        <dependency bomRef="pkg:maven/org.example/[email protected]?type=jar"/>
+      </component>
+      <additionalComponent
+          name="dependency" group="org.example" version="1.0">
+        <dependency componentRef="transitve"/>
+      </additionalComponent>
+      <additionalComponent
+          name="transitive-dependency" group="org.example"
+          version="1.0" id="transitve"
+          unknownDependencies="true"/>
+      <additionalComponent name="empty" group="org.example" version="1.0"/>
+    </cdx:componentbom>
+    <xmlproperty file="${output}/bom.xml"/>
+    <au:assertPropertyEquals
+        xmlns:au="antlib:org.apache.ant.antunit"
+        name="bom.dependencies.dependency(ref)"
+        
value="pkg:maven/org.example/[email protected]?type=jar,pkg:maven/org.example/[email protected]?type=jar,pkg:maven/org.example/[email protected]?type=jar"/>
+    <au:assertPropertyEquals
+        xmlns:au="antlib:org.apache.ant.antunit"
+        name="bom.dependencies.dependency.dependency(ref)"
+        
value="pkg:maven/org.example/[email protected]?type=jar,pkg:maven/org.example/[email protected]?type=jar"/>
+  </target>
+
 </project>

Reply via email to