Skip to content

Commit c45a30b

Browse files
Update cocoapods podfile.lock parser
Reference: aboutcode-org/scancode.io#1279 Signed-off-by: Ayan Sinha Mahapatra <[email protected]>
1 parent c326a0b commit c45a30b

File tree

7 files changed

+3212
-101
lines changed

7 files changed

+3212
-101
lines changed

src/packagedcode/cocoapods.py

Lines changed: 129 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
from packagedcode import models
2121
from packagedcode import spec
22-
from packagedcode import utils
22+
from packagedcode.utils import get_base_purl
23+
from packagedcode.utils import build_description
2324

2425
"""
2526
Handle cocoapods packages manifests for macOS and iOS
@@ -232,7 +233,7 @@ def parse(cls, location, package_only=False):
232233
extracted_license_statement = podspec.get('license')
233234
summary = podspec.get('summary')
234235
description = podspec.get('description')
235-
description = utils.build_description(
236+
description = build_description(
236237
summary=summary,
237238
description=description,
238239
)
@@ -292,6 +293,7 @@ class PodfileLockHandler(BasePodHandler):
292293
default_primary_language = 'Objective-C'
293294
description = 'Cocoapods Podfile.lock'
294295
documentation_url = 'https://guides.cocoapods.org/using/the-podfile.html'
296+
is_lockfile = True
295297

296298
@classmethod
297299
def parse(cls, location, package_only=False):
@@ -301,48 +303,168 @@ def parse(cls, location, package_only=False):
301303
with open(location) as pfl:
302304
data = saneyaml.load(pfl)
303305

304-
pods = data['PODS']
305-
dependencies = []
306+
pods = data.get('PODS')
306307

308+
# collect versions of all dependencies
309+
versions_by_base_purl = {}
307310
for pod in pods:
308311
if isinstance(pod, dict):
309312
for main_pod, _dep_pods in pod.items():
313+
purl, xreq = parse_dep_requirements(main_pod)
314+
base_purl = get_base_purl(purl.to_string())
315+
versions_by_base_purl[base_purl] = xreq
316+
317+
elif isinstance(pod, str):
318+
purl, xreq = parse_dep_requirements(pod)
319+
base_purl = get_base_purl(purl.to_string())
320+
versions_by_base_purl[base_purl] = xreq
321+
322+
direct_dependencies = data.get('DEPENDENCIES')
323+
direct_dependency_purls = []
324+
for direct_dep in direct_dependencies:
325+
purl, _xreq = parse_dep_requirements(direct_dep)
326+
base_purl = get_base_purl(purl.to_string())
327+
direct_dependency_purls.append(base_purl)
328+
329+
spec_repos = data.get('SPEC REPOS')
330+
spec_by_base_purl = {}
331+
for spec_repo, packages in spec_repos.items():
332+
for package in packages:
333+
purl, _xreq = parse_dep_requirements(package)
334+
base_purl = get_base_purl(purl.to_string())
335+
spec_by_base_purl[base_purl] = spec_repo
336+
337+
checksums = data.get('SPEC CHECKSUMS')
338+
checksum_by_base_purl = {}
339+
for name, checksum in checksums.items():
340+
purl, _xreq = parse_dep_requirements(name)
341+
base_purl = get_base_purl(purl.to_string())
342+
checksum_by_base_purl[base_purl] = checksum
343+
344+
dependencies = []
345+
for pod in pods:
346+
# dependencies with mappings have direct dependencies
347+
if isinstance(pod, dict):
348+
for main_pod, dep_pods in pod.items():
310349

311350
purl, xreq = parse_dep_requirements(main_pod)
351+
base_purl = get_base_purl(purl.to_string())
352+
353+
dependencies_for_resolved = []
354+
for dep_pod in dep_pods:
355+
dep_purl, _dep_xreq = parse_dep_requirements(dep_pod)
356+
base_dep_purl = get_base_purl(dep_purl.to_string())
357+
358+
dep_version = versions_by_base_purl.get(base_dep_purl)
359+
if not dep_purl.version:
360+
purl_mapping = dep_purl.to_dict()
361+
purl_mapping["version"] = dep_version
362+
dep_purl = PackageURL(**purl_mapping)
363+
364+
dependency_for_resolved = models.DependentPackage(
365+
purl=dep_purl.to_string(),
366+
# FIXME: why dev?
367+
scope='requires',
368+
extracted_requirement=xreq,
369+
is_runtime=False,
370+
is_optional=True,
371+
is_resolved=True,
372+
is_direct=True,
373+
).to_dict()
374+
dependencies_for_resolved.append(dependency_for_resolved)
375+
376+
resolved_package_mapping = dict(
377+
datasource_id=cls.datasource_id,
378+
type=cls.default_package_type,
379+
primary_language=cls.default_primary_language,
380+
namespace=purl.namespace,
381+
name=purl.name,
382+
version=purl.version,
383+
dependencies=dependencies_for_resolved,
384+
is_virtual=True,
385+
)
386+
resolved_package = models.PackageData.from_data(resolved_package_mapping)
387+
388+
checksum = checksum_by_base_purl.get(base_purl)
389+
if checksum:
390+
resolved_package.sha1 = checksum
391+
392+
is_direct = False
393+
if base_purl in direct_dependency_purls:
394+
is_direct = True
395+
396+
spec_repo = spec_by_base_purl.get(base_purl)
397+
if spec_repo:
398+
resolved_package.extra_data["spec_repo"] = spec_repo
312399

313400
dependencies.append(
314401
models.DependentPackage(
315-
purl=str(purl),
402+
purl=purl.to_string(),
316403
# FIXME: why dev?
317404
scope='requires',
318405
extracted_requirement=xreq,
319406
is_runtime=False,
320407
is_optional=True,
321408
is_resolved=True,
409+
is_direct=is_direct,
410+
resolved_package=resolved_package,
322411
)
323412
)
324413

414+
# These packages have no direct dependencies
325415
elif isinstance(pod, str):
326-
327416
purl, xreq = parse_dep_requirements(pod)
417+
base_purl = get_base_purl(purl.to_string())
418+
resolved_package_mapping = dict(
419+
datasource_id=cls.datasource_id,
420+
type=cls.default_package_type,
421+
primary_language=cls.default_primary_language,
422+
namespace=purl.namespace,
423+
name=purl.name,
424+
version=purl.version,
425+
is_virtual=True,
426+
)
427+
resolved_package = models.PackageData.from_data(resolved_package_mapping)
428+
429+
checksum = checksum_by_base_purl.get(base_purl)
430+
if checksum:
431+
resolved_package.sha1 = checksum
432+
433+
is_direct = False
434+
if base_purl in direct_dependency_purls:
435+
is_direct = True
436+
437+
spec_repo = spec_by_base_purl.get(base_purl)
438+
if spec_repo:
439+
resolved_package.extra_data["spec_repo"] = spec_repo
328440

329441
dependencies.append(
330442
models.DependentPackage(
331-
purl=str(purl),
443+
purl=purl.to_string(),
332444
# FIXME: why dev?
333445
scope='requires',
334446
extracted_requirement=xreq,
335447
is_runtime=False,
336448
is_optional=True,
337449
is_resolved=True,
450+
is_direct=is_direct,
451+
resolved_package=resolved_package,
338452
)
339453
)
340454

455+
podfile_checksum = data.get('PODFILE CHECKSUM')
456+
cocoapods_version = data.get('COCOAPODS')
457+
extra_data = {
458+
'cocoapods': cocoapods_version,
459+
'podfile_checksum': podfile_checksum,
460+
}
461+
341462
package_data = dict(
342463
datasource_id=cls.datasource_id,
343464
type=cls.default_package_type,
344465
primary_language=cls.default_primary_language,
345466
dependencies=dependencies,
467+
extra_data=extra_data,
346468
)
347469
yield models.PackageData.from_data(package_data, package_only)
348470

0 commit comments

Comments
 (0)