Last active
June 23, 2023 13:37
-
-
Save czoido/5d4ff14a700ed03e674662fd44681289 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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