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+
89import ast
910import io
1011import logging
1112import os
1213
14+ import saneyaml
1315from packageurl import PackageURL
1416
1517from 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-
4546class 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+
168259def is_constraint_resolved (constraint ):
169- range_characters = {'>' , '<' , '[' , ']' , '>=' , '<=' }
260+ range_characters = {">" , "<" , "[" , "]" , ">=" , "<=" }
170261 return not any (char in range_characters for char in constraint )
171262
263+
172264def 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
0 commit comments