Skip to content

Instantly share code, notes, and snippets.

@czoido
Last active June 23, 2023 13:37
Show Gist options
  • Save czoido/5d4ff14a700ed03e674662fd44681289 to your computer and use it in GitHub Desktop.
Save czoido/5d4ff14a700ed03e674662fd44681289 to your computer and use it in GitHub Desktop.
import json
import os
import yaml
import ast
from conan.api.conan_api import ConanAPI
from conan.errors import ConanException
fail_counter = 0
sucess_counter = 0
def parse_recipe_info(conanfile):
tree = ast.parse(conanfile)
desc = ""
lic = ""
for node in ast.walk(tree):
if isinstance(node, ast.ClassDef):
for body_node in node.body:
if isinstance(body_node, ast.Assign) and len(body_node.targets) == 1:
target = body_node.targets[0]
if isinstance(target, ast.Name) and target.id == "description":
value_node = body_node.value
if isinstance(value_node, ast.Str):
desc = value_node.s
if isinstance(target, ast.Name) and target.id == "license":
value_node = body_node.value
if isinstance(value_node, ast.Str):
lic = value_node.s
return desc, lic
def get_methods_from_conanfile_derived_class(conanfile):
root = ast.parse(conanfile)
info = {}
for node in ast.walk(root):
if isinstance(node, ast.ClassDef):
base_classes = [base.id for base in node.bases if isinstance(base, ast.Name)]
if 'ConanFile' in base_classes:
for sub_node in node.body:
if isinstance(sub_node, ast.FunctionDef) and sub_node.name == "package_info":
for stmt in sub_node.body:
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
func = stmt.value.func
line = ast.unparse(stmt)
if isinstance(func, ast.Attribute) and func.attr == 'set_property' and 'cpp_info.components' not in line:
# Check if the function is called on a component
if isinstance(func.value, ast.Attribute) \
and func.value.attr == 'cpp_info' \
and isinstance(func.value.value, ast.Name) \
and func.value.value.id == 'self':
args = stmt.value.args
if len(args) == 2 and isinstance(args[0], ast.Str) and isinstance(args[1], ast.Str):
if args[0].s == "cmake_file_name" or args[0].s == "cmake_target_name":
info[args[0].s] = str(args[1].s)
elif 'cpp_info.components' in line and 'set_property' in line:
component_name = str(func.value.slice.value)
args = stmt.value.args
if len(args) == 2 and isinstance(args[0], ast.Str) and isinstance(args[1], ast.Str):
if not component_name.startswith("_"):
info["components"] = info.get("components", {})
info["components"][component_name] = info["components"].get(component_name, {})
if args[0].s == "cmake_target_name":
info["components"][component_name]["cmake_target_name"] = args[1].s
return info
root_dir = 'conan-center-index/recipes'
packages_info = {}
conan_api = ConanAPI()
for dirpath, dirnames, filenames in os.walk(root_dir):
if 'config.yml' in filenames:
unique_folders = set()
recipe_name = os.path.basename(dirpath)
with open(os.path.join(dirpath, 'config.yml'), 'r') as f:
data = yaml.safe_load(f)
for version_info in data['versions'].values():
unique_folders.add(version_info['folder'])
recipe_folder = 'all'
if not 'all' in unique_folders:
# this is not perfect but good enough, order aplphabetically
sorted_folders = sorted(unique_folders, reverse=True)
recipe_folder = sorted_folders[0]
recipe_path = os.path.join(dirpath, recipe_folder, "conanfile.py")
if not os.path.exists(recipe_path):
raise Exception(recipe_path)
else:
with open(recipe_path, 'r') as f:
content = f.read()
packages_info[recipe_name] = {}
try:
conanfile = conan_api.local.inspect(os.path.abspath(recipe_path), None, None)
conanfile_json = conanfile.serialize()
packages_info[recipe_name]["description"] = conanfile_json.get("description", "").replace("\n", "").replace(" ", "")
license = conanfile_json.get("license", "")
packages_info[recipe_name]["license"] = [license] if type(license) != list else license
packages_info[recipe_name]["v2"] = True
except ConanException as exc:
description, license = parse_recipe_info(content)
if not description:
raise exc
packages_info[recipe_name]["description"] = description.replace("\n", "").replace(" ", "")
packages_info[recipe_name]["license"] = [license] if type(license) != list else license
packages_info[recipe_name]["v2"] = False
try:
info = get_methods_from_conanfile_derived_class(content)
if info:
packages_info[recipe_name].update(info)
sucess_counter += 1
except Exception as exc:
fail_counter += 1
json_data = json.dumps({"libraries": packages_info}, indent=4)
print(json_data)
# print("####################")
# print("Total failures:", fail_counter)
# print("Total successes:", sucess_counter)
# print("####################")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment