1515from collections import namedtuple
1616from pathlib import Path
1717
18+ from packageurl import PackageURL
1819from packagedcode import models
1920from packagedcode import nevra
2021from packagedcode .licensing import RESOURCE_TO_PACKAGE_LICENSE_FIELDS
@@ -44,7 +45,6 @@ def logger_debug(*args):
4445"""
4546Support for RPMs, installed databases and spec files.
4647"""
47- # TODO: retrieve dependencies
4848
4949# TODO: parse spec files see:
5050# http://www.faqs.org/docs/artu/ch05s02.html#id2906931%29.)
@@ -125,7 +125,6 @@ def to_string(self):
125125 return vr
126126
127127
128- # TODO: add dependencies!!!
129128class BaseRpmInstalledDatabaseHandler (models .DatafileHandler ):
130129
131130 @classmethod
@@ -135,13 +134,45 @@ def parse(cls, location, package_only=False):
135134 loc_path = Path (location )
136135 rpmdb_loc = str (loc_path .parent )
137136
137+ rpm_tags = get_rpm_tags (location , include_desc = True )
138+
139+ if TRACE : logger_debug ('recognize: rpm_tags' , rpm_tags )
140+ if not rpm_tags :
141+ return
142+
143+ dependencies = []
144+ name = rpm_tags .name
145+ version = rpm_tags .version
146+ is_pinned = version is not None and version != ""
147+
148+ # Construct PackageURL without '@version' if version is missing
149+ purl = PackageURL (
150+ type = "rpm" ,
151+ namespace = None , # RPMs typically don't use namespaces
152+ name = name ,
153+ version = version if is_pinned else None
154+ )
155+
156+ # Prepare the dependent package model
157+ dependencies .append (
158+ models .DependentPackage (
159+ purl = purl .to_string (),
160+ scope = "dependencies" ,
161+ is_runtime = True ,
162+ is_optional = False ,
163+ is_pinned = is_pinned ,
164+ extracted_requirement = version ,
165+ )
166+ )
167+
138168 # dump and parse the rpmdb to XMLish
139169 xmlish_loc = collect_installed_rpmdb_xmlish_from_rpmdb_loc (rpmdb_loc = rpmdb_loc )
140170 package_data = parse_rpm_xmlish (
141171 location = xmlish_loc ,
142172 datasource_id = cls .datasource_id ,
143173 package_type = cls .default_package_type ,
144174 package_only = package_only ,
175+ dependencies = dependencies ,
145176 )
146177 # TODO: package_data.namespace = cls.default_package_namespace
147178 return package_data
@@ -225,7 +256,6 @@ def assemble(cls, package_data, resource, codebase, package_adder):
225256 yield resource
226257
227258
228- # TODO: add dependencies!!!
229259class RpmInstalledNdbDatabaseHandler (BaseRpmInstalledDatabaseHandler ):
230260 # used by recent Suse
231261 datasource_id = 'rpm_installed_database_ndb'
@@ -237,8 +267,48 @@ class RpmInstalledNdbDatabaseHandler(BaseRpmInstalledDatabaseHandler):
237267 description = 'RPM installed package NDB database'
238268 documentation_url = 'https://fedoraproject.org/wiki/Changes/NewRpmDBFormat'
239269
270+ @classmethod
271+ def parse (cls , location , package_only = False ):
272+ rpm_tags = get_rpm_tags (location , include_desc = True )
273+
274+ if TRACE : logger_debug ('recognize: rpm_tags' , rpm_tags )
275+ if not rpm_tags :
276+ return
277+
278+ dependencies = []
279+ name = rpm_tags .name
280+ version = rpm_tags .version
281+ is_pinned = version is not None and version != ""
282+
283+ # Construct PackageURL without '@version' if version is missing
284+ purl = PackageURL (
285+ type = "rpm" ,
286+ namespace = None , # RPMs typically don't use namespaces
287+ name = name ,
288+ version = version if is_pinned else None
289+ )
290+
291+ # Prepare the dependent package model
292+ dependencies .append (
293+ models .DependentPackage (
294+ purl = purl .to_string (),
295+ scope = "dependencies" ,
296+ is_runtime = True ,
297+ is_optional = False ,
298+ is_pinned = is_pinned ,
299+ extracted_requirement = version ,
300+ )
301+ )
302+
303+ package_data = dict (
304+ datasource_id = cls .datasource_id ,
305+ type = cls .default_package_type ,
306+ dependencies = dependencies ,
307+ )
308+
309+ yield models .PackageData .from_data (package_data , package_only )
310+
240311
241- # TODO: add dependencies!!!
242312class RpmInstalledSqliteDatabaseHandler (BaseRpmInstalledDatabaseHandler ):
243313 # used by newer RHEL/CentOS/Fedora/CoreOS
244314 # Filetype: SQLite 3.x database, ...
@@ -253,8 +323,48 @@ class RpmInstalledSqliteDatabaseHandler(BaseRpmInstalledDatabaseHandler):
253323 description = 'RPM installed package SQLite database'
254324 documentation_url = 'https://fedoraproject.org/wiki/Changes/Sqlite_Rpmdb'
255325
326+ @classmethod
327+ def parse (cls , location , package_only = False ):
328+ rpm_tags = get_rpm_tags (location , include_desc = True )
329+
330+ if TRACE : logger_debug ('recognize: rpm_tags' , rpm_tags )
331+ if not rpm_tags :
332+ return
333+
334+ dependencies = []
335+ name = rpm_tags .name
336+ version = rpm_tags .version
337+ is_pinned = version is not None and version != ""
338+
339+ # Construct PackageURL without '@version' if version is missing
340+ purl = PackageURL (
341+ type = "rpm" ,
342+ namespace = None , # RPMs typically don't use namespaces
343+ name = name ,
344+ version = version if is_pinned else None
345+ )
346+
347+ # Prepare the dependent package model
348+ dependencies .append (
349+ models .DependentPackage (
350+ purl = purl .to_string (),
351+ scope = "dependencies" ,
352+ is_runtime = True ,
353+ is_optional = False ,
354+ is_pinned = is_pinned ,
355+ extracted_requirement = version ,
356+ )
357+ )
358+
359+ package_data = dict (
360+ datasource_id = cls .datasource_id ,
361+ type = cls .default_package_type ,
362+ dependencies = dependencies ,
363+ )
364+
365+ yield models .PackageData .from_data (package_data , package_only )
366+
256367
257- # TODO: add dependencies!!!
258368class RpmInstalledBdbDatabaseHandler (BaseRpmInstalledDatabaseHandler ):
259369 # used by legacy RHEL/CentOS/Fedora/Suse
260370 datasource_id = 'rpm_installed_database_bdb'
@@ -267,6 +377,47 @@ class RpmInstalledBdbDatabaseHandler(BaseRpmInstalledDatabaseHandler):
267377 description = 'RPM installed package BDB database'
268378 documentation_url = 'https://man7.org/linux/man-pages/man8/rpmdb.8.html'
269379
380+ @classmethod
381+ def parse (cls , location , package_only = False ):
382+ rpm_tags = get_rpm_tags (location , include_desc = True )
383+
384+ if TRACE : logger_debug ('recognize: rpm_tags' , rpm_tags )
385+ if not rpm_tags :
386+ return
387+
388+ dependencies = []
389+ name = rpm_tags .name
390+ version = rpm_tags .version
391+ is_pinned = version is not None and version != ""
392+
393+ # Construct PackageURL without '@version' if version is missing
394+ purl = PackageURL (
395+ type = "rpm" ,
396+ namespace = None , # RPMs typically don't use namespaces
397+ name = name ,
398+ version = version if is_pinned else None
399+ )
400+
401+ # Prepare the dependent package model
402+ dependencies .append (
403+ models .DependentPackage (
404+ purl = purl .to_string (),
405+ scope = "dependencies" ,
406+ is_runtime = True ,
407+ is_optional = False ,
408+ is_pinned = is_pinned ,
409+ extracted_requirement = version ,
410+ )
411+ )
412+
413+ package_data = dict (
414+ datasource_id = cls .datasource_id ,
415+ type = cls .default_package_type ,
416+ dependencies = dependencies ,
417+ )
418+
419+ yield models .PackageData .from_data (package_data , package_only )
420+
270421
271422# TODO: implement me!!@
272423class RpmSpecfileHandler (models .NonAssemblableDatafileHandler ):
@@ -278,7 +429,6 @@ class RpmSpecfileHandler(models.NonAssemblableDatafileHandler):
278429 documentation_url = 'https://en.wikipedia.org/wiki/RPM_Package_Manager'
279430
280431
281- # TODO: add dependencies!!!
282432class RpmArchiveHandler (models .DatafileHandler ):
283433 datasource_id = 'rpm_archive'
284434 path_patterns = ('*.rpm' , '*.src.rpm' , '*.srpm' , '*.mvl' , '*.vip' ,)
@@ -354,6 +504,30 @@ def parse(cls, location, package_only=False):
354504
355505 description = build_description (summary = rpm_tags .summary , description = rpm_tags .description )
356506
507+ dependencies = []
508+ name = rpm_tags .name
509+ version = rpm_tags .version
510+ is_pinned = version is not None and version != ""
511+
512+ # Construct PackageURL without '@version' if version is missing
513+ purl = PackageURL (
514+ type = "rpm" ,
515+ namespace = None , # RPMs typically don't use namespaces
516+ name = name ,
517+ version = version if is_pinned else None
518+ )
519+
520+ # Prepare the dependent package model
521+ dependencies .append (
522+ models .DependentPackage (
523+ purl = purl .to_string (),
524+ scope = "dependencies" ,
525+ is_runtime = True ,
526+ is_optional = False ,
527+ is_pinned = is_pinned ,
528+ extracted_requirement = version ,
529+ )
530+ )
357531 if TRACE :
358532 data = dict (
359533 name = name ,
@@ -363,6 +537,7 @@ def parse(cls, location, package_only=False):
363537 parties = parties ,
364538 extracted_license_statement = rpm_tags .license or None ,
365539 source_packages = source_packages ,
540+ dependencies = dependencies ,
366541 )
367542 logger_debug ('recognize: data to create a package:\n ' , data )
368543
@@ -377,6 +552,7 @@ def parse(cls, location, package_only=False):
377552 parties = parties ,
378553 extracted_license_statement = rpm_tags .license or None ,
379554 source_packages = source_packages ,
555+ dependencies = dependencies ,
380556 )
381557
382558 if TRACE :
0 commit comments