Skip to content

Commit ff84e1c

Browse files
Support more cargo workspace cases
Signed-off-by: Ayan Sinha Mahapatra <ayansmahapatra@gmail.com>
1 parent 2a2f512 commit ff84e1c

File tree

10 files changed

+641
-98
lines changed

10 files changed

+641
-98
lines changed

src/packagedcode/cargo.py

Lines changed: 77 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -21,74 +21,7 @@
2121
"""
2222

2323

24-
class CargoTomlHandler(models.DatafileHandler):
25-
datasource_id = 'cargo_toml'
26-
path_patterns = ('*/Cargo.toml', '*/cargo.toml',)
27-
default_package_type = 'cargo'
28-
default_primary_language = 'Rust'
29-
description = 'Rust Cargo.toml package manifest'
30-
documentation_url = 'https://doc.rust-lang.org/cargo/reference/manifest.html'
31-
32-
@classmethod
33-
def parse(cls, location):
34-
package_data = toml.load(location, _dict=dict)
35-
core_package_data = package_data.get('package', {})
36-
workspace = package_data.get('workspace', {})
37-
38-
name = core_package_data.get('name')
39-
version = core_package_data.get('version')
40-
description = core_package_data.get('description') or ''
41-
description = description.strip()
42-
43-
authors = core_package_data.get('authors') or []
44-
parties = list(get_parties(person_names=authors, party_role='author'))
45-
46-
extracted_license_statement = core_package_data.get('license')
47-
# TODO: load as a notice_text
48-
license_file = core_package_data.get('license-file')
49-
50-
keywords = core_package_data.get('keywords') or []
51-
categories = core_package_data.get('categories') or []
52-
keywords.extend(categories)
53-
54-
# cargo dependencies are complex and can be overriden at multiple levels
55-
dependencies = []
56-
for key, value in core_package_data.items():
57-
if key.endswith('dependencies'):
58-
dependencies.extend(dependency_mapper(dependencies=value, scope=key))
59-
60-
# TODO: add file refs:
61-
# - readme, include and exclude
62-
# TODO: other URLs
63-
# - documentation
64-
65-
vcs_url = core_package_data.get('repository')
66-
homepage_url = core_package_data.get('homepage')
67-
repository_homepage_url = name and f'https://crates.io/crates/{name}'
68-
repository_download_url = name and version and f'https://crates.io/api/v1/crates/{name}/{version}/download'
69-
api_data_url = name and f'https://crates.io/api/v1/crates/{name}'
70-
extra_data = {}
71-
if workspace:
72-
extra_data["workspace"] = workspace
73-
74-
yield models.PackageData(
75-
datasource_id=cls.datasource_id,
76-
type=cls.default_package_type,
77-
name=name,
78-
version=version,
79-
primary_language=cls.default_primary_language,
80-
description=description,
81-
parties=parties,
82-
extracted_license_statement=extracted_license_statement,
83-
vcs_url=vcs_url,
84-
homepage_url=homepage_url,
85-
repository_homepage_url=repository_homepage_url,
86-
repository_download_url=repository_download_url,
87-
api_data_url=api_data_url,
88-
dependencies=dependencies,
89-
extra_data=extra_data,
90-
)
91-
24+
class CargoBaseHandler(models.DatafileHandler):
9225
@classmethod
9326
def assemble(cls, package_data, resource, codebase, package_adder):
9427
"""
@@ -161,6 +94,79 @@ def update_resource_package_data(cls, package_data, old_package_data, mapping=No
16194
return old_package_data
16295

16396

97+
98+
class CargoTomlHandler(CargoBaseHandler):
99+
datasource_id = 'cargo_toml'
100+
path_patterns = ('*/Cargo.toml', '*/cargo.toml',)
101+
default_package_type = 'cargo'
102+
default_primary_language = 'Rust'
103+
description = 'Rust Cargo.toml package manifest'
104+
documentation_url = 'https://doc.rust-lang.org/cargo/reference/manifest.html'
105+
106+
@classmethod
107+
def parse(cls, location):
108+
package_data = toml.load(location, _dict=dict)
109+
core_package_data = package_data.get('package', {})
110+
workspace = package_data.get('workspace', {})
111+
112+
name = core_package_data.get('name')
113+
version = core_package_data.get('version')
114+
if isinstance(version, dict) and "workspace" in version:
115+
version = "workspace"
116+
117+
description = core_package_data.get('description') or ''
118+
description = description.strip()
119+
120+
authors = core_package_data.get('authors') or []
121+
parties = list(get_parties(person_names=authors, party_role='author'))
122+
123+
extracted_license_statement = core_package_data.get('license')
124+
# TODO: load as a notice_text
125+
license_file = core_package_data.get('license-file')
126+
127+
keywords = core_package_data.get('keywords') or []
128+
categories = core_package_data.get('categories') or []
129+
keywords.extend(categories)
130+
131+
# cargo dependencies are complex and can be overriden at multiple levels
132+
dependencies = []
133+
for key, value in core_package_data.items():
134+
if key.endswith('dependencies'):
135+
dependencies.extend(dependency_mapper(dependencies=value, scope=key))
136+
137+
# TODO: add file refs:
138+
# - readme, include and exclude
139+
# TODO: other URLs
140+
# - documentation
141+
142+
vcs_url = core_package_data.get('repository')
143+
homepage_url = core_package_data.get('homepage')
144+
repository_homepage_url = name and f'https://crates.io/crates/{name}'
145+
repository_download_url = name and version and f'https://crates.io/api/v1/crates/{name}/{version}/download'
146+
api_data_url = name and f'https://crates.io/api/v1/crates/{name}'
147+
extra_data = {}
148+
if workspace:
149+
extra_data["workspace"] = workspace
150+
151+
yield models.PackageData(
152+
datasource_id=cls.datasource_id,
153+
type=cls.default_package_type,
154+
name=name,
155+
version=version,
156+
primary_language=cls.default_primary_language,
157+
description=description,
158+
parties=parties,
159+
extracted_license_statement=extracted_license_statement,
160+
vcs_url=vcs_url,
161+
homepage_url=homepage_url,
162+
repository_homepage_url=repository_homepage_url,
163+
repository_download_url=repository_download_url,
164+
api_data_url=api_data_url,
165+
dependencies=dependencies,
166+
extra_data=extra_data,
167+
)
168+
169+
164170
CARGO_ATTRIBUTE_MAPPING = {
165171
# Fields in PackageData model: Fields in cargo
166172
"homepage_url": "homepage",
@@ -173,7 +179,8 @@ def update_resource_package_data(cls, package_data, old_package_data, mapping=No
173179
"declared_license_expression_spdx": "declared_license_expression_spdx",
174180
}
175181

176-
class CargoLockHandler(models.DatafileHandler):
182+
183+
class CargoLockHandler(CargoBaseHandler):
177184
datasource_id = 'cargo_lock'
178185
path_patterns = ('*/Cargo.lock', '*/cargo.lock',)
179186
default_package_type = 'cargo'
@@ -220,18 +227,6 @@ def parse(cls, location):
220227
dependencies=dependencies,
221228
)
222229

223-
@classmethod
224-
def assemble(cls, package_data, resource, codebase, package_adder):
225-
"""
226-
Assemble Cargo.toml and possible Cargo.lock datafiles
227-
"""
228-
yield from cls.assemble_from_many_datafiles(
229-
datafile_name_patterns=('Cargo.toml', 'Cargo.lock',),
230-
directory=resource.parent(codebase),
231-
codebase=codebase,
232-
package_adder=package_adder,
233-
)
234-
235230

236231
def dependency_mapper(dependencies, scope='dependencies'):
237232
"""
@@ -261,13 +256,11 @@ def dependency_mapper(dependencies, scope='dependencies'):
261256
)
262257

263258

264-
def get_parties(person_names, party_role, debug=False):
259+
def get_parties(person_names, party_role):
265260
"""
266261
Yields Party of `party_role` given a list of ``person_names`` strings.
267262
https://doc.rust-lang.org/cargo/reference/manifest.html#the-authors-field-optional
268263
"""
269-
if debug:
270-
raise Exception(person_names)
271264
for person_name in person_names:
272265
name, email = parse_person(person_name)
273266
yield models.Party(

src/packagedcode/plugin_package.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
from packagedcode.models import PackageWithResources
3939

4040
TRACE = os.environ.get('SCANCODE_DEBUG_PACKAGE_API', False)
41-
41+
TRACE_DEEP = os.environ.get('SCANCODE_DEBUG_PACKAGE_API_DEEP', False)
42+
TRACE_LICENSE = os.environ.get('SCANCODE_DEBUG_PACKAGE_LICENSE', False)
4243

4344
def logger_debug(*args):
4445
pass
@@ -207,7 +208,7 @@ def process_codebase(self, codebase, strip_root=False, **kwargs):
207208
# If we don't detect license in package_data but there is license detected in file
208209
# we add the license expression from the file to a package
209210
modified = add_license_from_file(resource, codebase)
210-
if TRACE and modified:
211+
if TRACE_LICENSE and modified:
211212
logger_debug(f'packagedcode: process_codebase: add_license_from_file: modified: {modified}')
212213

213214
if codebase.has_single_resource:
@@ -216,7 +217,7 @@ def process_codebase(self, codebase, strip_root=False, **kwargs):
216217
# If there is referenced files in a extracted license statement, we follow
217218
# the references, look for license detections and add them back
218219
modified = list(add_referenced_license_matches_for_package(resource, codebase))
219-
if TRACE and modified:
220+
if TRACE_LICENSE and modified:
220221
logger_debug(f'packagedcode: process_codebase: add_referenced_license_matches_for_package: modified: {modified}')
221222

222223
# If there is a LICENSE file on the same level as the manifest, and no license
@@ -234,7 +235,7 @@ def process_codebase(self, codebase, strip_root=False, **kwargs):
234235
# If there is a unknown reference to a package we add the license
235236
# from the package license detection
236237
modified = list(add_referenced_license_detection_from_package(resource, codebase))
237-
if TRACE and modified:
238+
if TRACE_LICENSE and modified:
238239
logger_debug(f'packagedcode: process_codebase: add_referenced_license_matches_from_package: modified: {modified}')
239240

240241

@@ -244,15 +245,15 @@ def add_license_from_file(resource, codebase):
244245
and the file has license detections, and if so, populate the package_data license
245246
expression and detection fields from the file license.
246247
"""
247-
if TRACE:
248+
if TRACE_LICENSE:
248249
logger_debug(f'packagedcode.plugin_package: add_license_from_file: resource: {resource.path}')
249250

250251
if not resource.is_file:
251252
return
252253

253254
license_detections_file = resource.license_detections
254255

255-
if TRACE:
256+
if TRACE_LICENSE:
256257
logger_debug(f'add_license_from_file: license_detections_file: {license_detections_file}')
257258
if not license_detections_file:
258259
return
@@ -263,7 +264,7 @@ def add_license_from_file(resource, codebase):
263264

264265
for pkg in package_data:
265266
license_detections_pkg = pkg["license_detections"]
266-
if TRACE:
267+
if TRACE_LICENSE:
267268
logger_debug(f'add_license_from_file: license_detections_pkg: {license_detections_pkg}')
268269

269270
if not license_detections_pkg:
@@ -359,7 +360,7 @@ def get_package_and_deps(codebase, package_adder=add_to_package, strip_root=Fals
359360
package_data = PackageData.from_dict(mapping=package_data)
360361

361362
if TRACE:
362-
logger_debug(' get_package_and_deps: package_data:', package_data)
363+
logger_debug(' get_package_and_deps: package_data.purl:', package_data.purl)
363364

364365
# Find a handler for this package datasource to assemble collect
365366
# packages and deps
@@ -375,8 +376,6 @@ def get_package_and_deps(codebase, package_adder=add_to_package, strip_root=Fals
375376
)
376377

377378
for item in items:
378-
if TRACE:
379-
logger_debug(' get_package_and_deps: item:', item)
380379

381380
if isinstance(item, Package):
382381
if strip_root and not has_single_resource:
@@ -385,6 +384,8 @@ def get_package_and_deps(codebase, package_adder=add_to_package, strip_root=Fals
385384
for dfp in item.datafile_paths
386385
]
387386
packages.append(item)
387+
if TRACE:
388+
logger_debug(' get_package_and_deps: Package:', item.purl)
388389

389390
elif isinstance(item, Dependency):
390391
if strip_root and not has_single_resource:
@@ -395,6 +396,9 @@ def get_package_and_deps(codebase, package_adder=add_to_package, strip_root=Fals
395396
seen_resource_paths.add(item.path)
396397

397398
if TRACE:
399+
logger_debug(' get_package_and_deps: Resource:', item.path)
400+
401+
if TRACE_DEEP:
398402
logger_debug(
399403
' get_package_and_deps: seen_resource_path:',
400404
seen_resource_paths,

0 commit comments

Comments
 (0)