Skip to content

Commit 638aeb3

Browse files
committed
Add handler for conandata.yml
- Parse and collect version and download_url for conan package - Override the assembly to enhance the package data from conanfile.py Signed-off-by: Keshav Priyadarshi <[email protected]>
1 parent c85819a commit 638aeb3

File tree

3 files changed

+121
-21
lines changed

3 files changed

+121
-21
lines changed

src/packagedcode/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
conda.CondaMetaYamlHandler,
8080

8181
conan.ConanFileHandler,
82+
conan.ConanDataHandler,
8283

8384
cran.CranDescriptionFileHandler,
8485

src/packagedcode/conan.py

Lines changed: 113 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
# See https://github.com/nexB/scancode-toolkit for support or download.
66
# See https://aboutcode.org for more information about nexB OSS projects.
77
#
8+
89
import ast
910
import io
1011
import logging
1112
import os
1213

14+
import saneyaml
1315
from packageurl import PackageURL
1416

1517
from packagedcode import models
@@ -41,7 +43,6 @@ def logger_debug(*args):
4143
return logger.debug(" ".join(isinstance(a, str) and a or repr(a) for a in args))
4244

4345

44-
4546
class ConanFileParser(ast.NodeVisitor):
4647
def __init__(self):
4748
self.name = None
@@ -53,25 +54,26 @@ def __init__(self):
5354
self.license = None
5455
self.keywords = []
5556
self.requires = []
56-
57+
5758
def to_dict(self):
5859
return {
59-
'name': self.name,
60-
'version': self.version,
61-
'description': self.description,
62-
'author':self.author,
63-
'homepage_url':self.homepage_url,
64-
'vcs_url':self.vcs_url,
65-
'license':self.license,
66-
'keywords':self.keywords,
67-
'requires':self.requires,
60+
"name": self.name,
61+
"version": self.version,
62+
"description": self.description,
63+
"author": self.author,
64+
"homepage_url": self.homepage_url,
65+
"vcs_url": self.vcs_url,
66+
"license": self.license,
67+
"keywords": self.keywords,
68+
"requires": self.requires,
6869
}
6970

70-
7171
def visit_Assign(self, node):
7272
if not node.targets or not isinstance(node.targets[0], ast.Name):
7373
return
74-
if not node.value or not (isinstance(node.value, ast.Constant) or isinstance(node.value, ast.Tuple)):
74+
if not node.value or not (
75+
isinstance(node.value, ast.Constant) or isinstance(node.value, ast.Tuple)
76+
):
7577
return
7678
variable_name = node.targets[0].id
7779
values = node.value
@@ -159,30 +161,120 @@ def parse(cls, location):
159161
version=parser.version,
160162
description=parser.description,
161163
homepage_url=parser.homepage_url,
162-
vcs_url=parser.vcs_url,
163164
keywords=parser.keywords,
164-
declared_license_expression=parser.license,
165+
extracted_license_statement=parser.license,
165166
dependencies=dependencies,
166167
)
167168

169+
170+
class ConanDataHandler(models.DatafileHandler):
171+
datasource_id = "conan_conandata_yml"
172+
path_patterns = ("*/conandata.yml",)
173+
default_package_type = "conan"
174+
default_primary_language = "C++"
175+
description = "conan external source"
176+
documentation_url = (
177+
"https://docs.conan.io/2/tutorial/creating_packages/"
178+
"handle_sources_in_packages.html#using-the-conandata-yml-file"
179+
)
180+
181+
@classmethod
182+
def parse(cls, location):
183+
with io.open(location, encoding="utf-8") as loc:
184+
conan_data = loc.read()
185+
186+
conan_data = saneyaml.load(conan_data)
187+
sources = conan_data.get("sources", {})
188+
189+
for version, source in sources.items():
190+
sha256 = source.get("sha256", None)
191+
urls = []
192+
source_urls = source.get("url")
193+
if isinstance(source_urls, str):
194+
urls.append(source_urls)
195+
elif isinstance(source_urls, list):
196+
urls.extend(source_urls)
197+
198+
for url in urls:
199+
yield models.PackageData(
200+
datasource_id=cls.datasource_id,
201+
type=cls.default_package_type,
202+
primary_language=cls.default_primary_language,
203+
namespace=None,
204+
version=version,
205+
download_url=url,
206+
sha256=sha256,
207+
)
208+
209+
@classmethod
210+
def assemble(
211+
cls, package_data, resource, codebase, package_adder=models.add_to_package
212+
):
213+
"""
214+
Assemble
215+
"""
216+
siblings = resource.siblings(codebase)
217+
conanfile_package_resource = [r for r in siblings if r.name == "conanfile.py"]
218+
package_data_dict = package_data.to_dict()
219+
220+
if conanfile_package_resource:
221+
conanfile_package_resource = conanfile_package_resource[0]
222+
223+
conanfile_package_data = conanfile_package_resource.package_data
224+
if conanfile_package_data:
225+
conanfile_package_data = conanfile_package_data[0]
226+
227+
package_data_dict["name"] = conanfile_package_data.get("name")
228+
package_data_dict["description"] = conanfile_package_data.get(
229+
"description"
230+
)
231+
package_data_dict["homepage_url"] = conanfile_package_data.get(
232+
"homepage_url"
233+
)
234+
package_data_dict["keywords"] = conanfile_package_data.get("keywords")
235+
package_data_dict[
236+
"extracted_license_statement"
237+
] = conanfile_package_data.get("extracted_license_statement")
238+
239+
datafile_path = resource.path
240+
pkg_data = models.PackageData.from_dict(package_data_dict)
241+
242+
if pkg_data.purl:
243+
package = models.Package.from_package_data(
244+
package_data=pkg_data,
245+
datafile_path=datafile_path,
246+
)
247+
package.populate_license_fields()
248+
yield package
249+
250+
cls.assign_package_to_resources(
251+
package=package,
252+
resource=resource,
253+
codebase=codebase,
254+
package_adder=package_adder,
255+
)
256+
yield resource
257+
258+
168259
def is_constraint_resolved(constraint):
169-
range_characters = {'>', '<', '[', ']', '>=','<='}
260+
range_characters = {">", "<", "[", "]", ">=", "<="}
170261
return not any(char in range_characters for char in constraint)
171262

263+
172264
def get_dependencies(requires):
173-
dependent_packages=[]
265+
dependent_packages = []
174266
for req in requires:
175-
name, constraint = req.split('/', 1)
267+
name, constraint = req.split("/", 1)
176268
is_resolved = is_constraint_resolved(constraint)
177-
purl = PackageURL(type='conan', name=name)
269+
purl = PackageURL(type="conan", name=name)
178270
dependent_packages.append(
179271
models.DependentPackage(
180272
purl=purl.to_string(),
181-
scope='install',
273+
scope="install",
182274
is_runtime=True,
183275
is_optional=False,
184276
is_resolved=is_resolved,
185-
extracted_requirement=constraint
277+
extracted_requirement=constraint,
186278
)
187279
)
188280
return dependent_packages

tests/packagedcode/data/plugin/help.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,13 @@ Package type: composer
174174
description: PHP composer lockfile
175175
path_patterns: '*composer.lock'
176176
--------------------------------------------
177+
Package type: conan
178+
datasource_id: conan_conandata_yml
179+
documentation URL: https://docs.conan.io/2/tutorial/creating_packages/handle_sources_in_packages.html#using-the-conandata-yml-file
180+
primary language: C++
181+
description: conan external source
182+
path_patterns: '*/conandata.yml'
183+
--------------------------------------------
177184
Package type: conan
178185
datasource_id: conan_conanfile_py
179186
documentation URL: https://docs.conan.io/2.0/reference/conanfile.html

0 commit comments

Comments
 (0)