Skip to content

Commit 1697461

Browse files
authored
Merge pull request #3206 from nexB/add-nuget-dependencies
Add nuget nuspec dependencies
2 parents caec256 + b62eb68 commit 1697461

7 files changed

+767
-12
lines changed

src/packagedcode/nuget.py

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,75 @@
88
#
99

1010
import xmltodict
11+
from packageurl import PackageURL
1112

1213
from packagedcode import models
1314
from packagedcode.utils import build_description
1415

1516
"""
1617
Handle NuGet packages and their manifests.
1718
"""
18-
# TODO: add dependencies
19+
20+
21+
def get_dependencies(nuspec):
22+
"""
23+
Yield DependentPackage found in a NuGet ``nuspec`` object.
24+
"""
25+
# This is either a list of dependency or a list of group/dependency
26+
# or a single dep or a single group mapping
27+
28+
dependencies = nuspec.get('dependencies') or []
29+
if isinstance(dependencies, dict):
30+
# wrap the mapping in a list if we have more than one dependencies
31+
dependencies = [dependencies]
32+
33+
for depends in dependencies:
34+
groups = depends.get('group') or []
35+
if groups:
36+
if isinstance(groups, dict):
37+
# wrap the mapping in a list
38+
groups = [groups]
39+
40+
for group in groups:
41+
extra_data = dict(framework=group['@targetFramework'])
42+
deps = group.get('dependency') or []
43+
yield from _get_dep_packs(deps, extra_data)
44+
else:
45+
# {'dependency': {'@id': 'jQuery', '@version': '1.9.1'}}
46+
deps = depends.get('dependency') or []
47+
yield from _get_dep_packs(deps, extra_data={})
48+
49+
50+
def _get_dep_packs(deps, extra_data):
51+
"""
52+
Yield DependentPackage found in a NuGet ``deps`` mapping or list of mappings.
53+
"""
54+
if not deps:
55+
return
56+
57+
if isinstance(deps, dict):
58+
# wrap the mapping in a list
59+
deps = [deps]
60+
61+
for dep in deps:
62+
extra = dict(extra_data) or {}
63+
include = dep.get('@include')
64+
if include:
65+
extra['include'] = include
66+
exclude = dep.get('@exclude')
67+
if exclude:
68+
extra['exclude'] = exclude
69+
70+
yield models.DependentPackage(
71+
purl=str(PackageURL(type='nuget', name=dep.get('@id'))),
72+
# this is a range, not a version
73+
extracted_requirement=dep.get('@version'),
74+
scope='dependency',
75+
is_runtime=True,
76+
is_optional=False,
77+
is_resolved=False,
78+
extra_data=extra,
79+
)
1980

2081

2182
def get_urls(name, version, **kwargs):
@@ -46,7 +107,7 @@ class NugetNuspecHandler(models.DatafileHandler):
46107

47108
@classmethod
48109
def parse(cls, location):
49-
with open(location , 'rb') as loc:
110+
with open(location, 'rb') as loc:
50111
parsed = xmltodict.parse(loc)
51112

52113
if not parsed:
@@ -62,14 +123,14 @@ def parse(cls, location):
62123

63124
# Summary: A short description of the package for UI display. If omitted, a
64125
# truncated version of description is used.
65-
description = build_description(nuspec.get('summary') , nuspec.get('description'))
126+
description = build_description(nuspec.get('summary'), nuspec.get('description'))
66127

67128
# title: A human-friendly title of the package, typically used in UI
68129
# displays as on nuget.org and the Package Manager in Visual Studio. If not
69130
# specified, the package ID is used.
70131
title = nuspec.get('title')
71132
if title and title != name:
72-
description = build_description(nuspec.get('title') , description)
133+
description = build_description(title, description)
73134

74135
parties = []
75136
authors = nuspec.get('authors')
@@ -98,7 +159,6 @@ def parse(cls, location):
98159
# This is a SPDX license expression
99160
if 'license' in nuspec:
100161
extracted_license_statement = nuspec.get('license')
101-
# TODO: try to convert to normal license expression
102162
# Deprecated and not a license expression, just a URL
103163
elif 'licenseUrl' in nuspec:
104164
extracted_license_statement = nuspec.get('licenseUrl')
@@ -111,8 +171,10 @@ def parse(cls, location):
111171
description=description or None,
112172
homepage_url=nuspec.get('projectUrl') or None,
113173
parties=parties,
174+
dependencies=list(get_dependencies(nuspec)),
114175
extracted_license_statement=extracted_license_statement,
115176
copyright=nuspec.get('copyright') or None,
116177
vcs_url=vcs_url,
117178
**urls,
118179
)
180+

tests/packagedcode/data/nuget/Castle.Core.nuspec.json.expected

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,138 @@
6969
"source_packages": [],
7070
"file_references": [],
7171
"extra_data": {},
72-
"dependencies": [],
72+
"dependencies": [
73+
{
74+
"purl": "pkg:nuget/NETStandard.Library",
75+
"extracted_requirement": "1.6.1",
76+
"scope": "dependency",
77+
"is_runtime": true,
78+
"is_optional": false,
79+
"is_resolved": false,
80+
"resolved_package": {},
81+
"extra_data": {
82+
"framework": ".NETStandard1.3",
83+
"exclude": "Build,Analyzers"
84+
}
85+
},
86+
{
87+
"purl": "pkg:nuget/System.Diagnostics.TraceSource",
88+
"extracted_requirement": "4.3.0",
89+
"scope": "dependency",
90+
"is_runtime": true,
91+
"is_optional": false,
92+
"is_resolved": false,
93+
"resolved_package": {},
94+
"extra_data": {
95+
"framework": ".NETStandard1.3",
96+
"exclude": "Build,Analyzers"
97+
}
98+
},
99+
{
100+
"purl": "pkg:nuget/System.Reflection.TypeExtensions",
101+
"extracted_requirement": "4.3.0",
102+
"scope": "dependency",
103+
"is_runtime": true,
104+
"is_optional": false,
105+
"is_resolved": false,
106+
"resolved_package": {},
107+
"extra_data": {
108+
"framework": ".NETStandard1.3",
109+
"exclude": "Build,Analyzers"
110+
}
111+
},
112+
{
113+
"purl": "pkg:nuget/System.Xml.XmlDocument",
114+
"extracted_requirement": "4.3.0",
115+
"scope": "dependency",
116+
"is_runtime": true,
117+
"is_optional": false,
118+
"is_resolved": false,
119+
"resolved_package": {},
120+
"extra_data": {
121+
"framework": ".NETStandard1.3",
122+
"exclude": "Build,Analyzers"
123+
}
124+
},
125+
{
126+
"purl": "pkg:nuget/System.Dynamic.Runtime",
127+
"extracted_requirement": "4.3.0",
128+
"scope": "dependency",
129+
"is_runtime": true,
130+
"is_optional": false,
131+
"is_resolved": false,
132+
"resolved_package": {},
133+
"extra_data": {
134+
"framework": ".NETStandard1.3",
135+
"exclude": "Build,Analyzers"
136+
}
137+
},
138+
{
139+
"purl": "pkg:nuget/System.Reflection",
140+
"extracted_requirement": "4.3.0",
141+
"scope": "dependency",
142+
"is_runtime": true,
143+
"is_optional": false,
144+
"is_resolved": false,
145+
"resolved_package": {},
146+
"extra_data": {
147+
"framework": ".NETStandard1.3",
148+
"exclude": "Build,Analyzers"
149+
}
150+
},
151+
{
152+
"purl": "pkg:nuget/System.Reflection.Emit",
153+
"extracted_requirement": "4.3.0",
154+
"scope": "dependency",
155+
"is_runtime": true,
156+
"is_optional": false,
157+
"is_resolved": false,
158+
"resolved_package": {},
159+
"extra_data": {
160+
"framework": ".NETStandard1.3",
161+
"exclude": "Build,Analyzers"
162+
}
163+
},
164+
{
165+
"purl": "pkg:nuget/System.Collections.Specialized",
166+
"extracted_requirement": "4.3.0",
167+
"scope": "dependency",
168+
"is_runtime": true,
169+
"is_optional": false,
170+
"is_resolved": false,
171+
"resolved_package": {},
172+
"extra_data": {
173+
"framework": ".NETStandard1.3",
174+
"exclude": "Build,Analyzers"
175+
}
176+
},
177+
{
178+
"purl": "pkg:nuget/System.ComponentModel",
179+
"extracted_requirement": "4.3.0",
180+
"scope": "dependency",
181+
"is_runtime": true,
182+
"is_optional": false,
183+
"is_resolved": false,
184+
"resolved_package": {},
185+
"extra_data": {
186+
"framework": ".NETStandard1.3",
187+
"exclude": "Build,Analyzers"
188+
}
189+
},
190+
{
191+
"purl": "pkg:nuget/System.ComponentModel.TypeConverter",
192+
"extracted_requirement": "4.3.0",
193+
"scope": "dependency",
194+
"is_runtime": true,
195+
"is_optional": false,
196+
"is_resolved": false,
197+
"resolved_package": {},
198+
"extra_data": {
199+
"framework": ".NETStandard1.3",
200+
"exclude": "Build,Analyzers"
201+
}
202+
}
203+
],
73204
"repository_homepage_url": "https://www.nuget.org/packages/Castle.Core/4.2.1",
74205
"repository_download_url": "https://www.nuget.org/api/v2/package/Castle.Core/4.2.1",
75206
"api_data_url": "https://api.nuget.org/v3/registration3/castle.core/4.2.1.json",

0 commit comments

Comments
 (0)