2024-02-29 22:06:25 +03:00

221 lines
7.5 KiB
Python

import json
import os
import subprocess
import yaml
from impl import utils
from impl.conan_recipe_store import get_recipe, get_recipe_stores
from impl.config import directories
from impl.package_reference import PackageReference
from impl.profiles import ConanProfiles
from impl.debug import handle_build_completed
from impl import conan_cache
def execute_conan_command(command:str, all:bool, build_order:list[str]):
for recipe_store in get_recipe_stores(build_order, not all):
recipe_store.execute_command(command, all)
def build_package(package_reference:PackageReference, profiles: ConanProfiles, remotes:list[str] = None, export_recipe:bool=False, keep_sources:bool=False):
recipe = get_recipe(package_reference)
retrieve_sources = False
try:
if export_recipe:
recipe.export()
sources_location = conan_cache.get_cache_path_source(package_reference)
retrieve_sources = not os.path.isdir(sources_location)
if retrieve_sources:
recipe.source()
recipe.build(profiles, remotes)
finally:
conan_cache.clean_cache(package_reference, sources=retrieve_sources and not keep_sources)
def build_all(build_order:list[str], profiles:ConanProfiles, remotes:list[str] = None, export_recipes:bool=False, keep_sources:bool=False):
for package_name in build_order:
build_package(PackageReference(package_name=package_name), profiles, remotes, export_recipes, keep_sources)
def clean():
subprocess.check_call([utils.get_conan(), 'cache', 'clean', '*'])
subprocess.check_call([utils.get_conan(), 'remove', '--confirm', '*'])
def install_recipe(recipe_path:str, config_path:str, profiles:ConanProfiles, remotes:list[str], allow_build:bool, keep_sources:bool):
try:
cmd = [
utils.get_conan(), 'install',
'-of', directories.install_dir,
'-vvv',
'-pr:h', profiles.host_profile,
'-pr:b', profiles.build_profile,
'--format', 'json'
]
if allow_build:
cmd += ['--build', 'missing']
if remotes and len(remotes) > 0:
for remote in remotes:
cmd += ['-r', remote]
else:
cmd += ['--no-remote']
if config_path:
print(f'Loading options from {config_path}')
with open(config_path, 'r') as f:
config = yaml.safe_load(f)['config']
print(config)
if 'options' in config:
for opt in config['options'].split():
cmd += ['-o:h', opt.strip()]
cmd += [recipe_path]
print(cmd, flush=True)
output = subprocess.check_output(cmd).decode('utf-8')
print("Output from conan install:")
print(output, flush=True)
if not output or len(output) == 0:
print("Empty output, failed to collect dependencies")
return
dependecies_graph = json.loads(output)['graph']['nodes']
for node in dependecies_graph.values():
if node['id'] == '0':
continue
ref = node['ref']
package_id = node['package_id']
print(f'Collecting directories for {ref}:{package_id}')
try:
source_dir = subprocess.check_output([utils.get_conan(), 'cache', 'path', '--folder', 'source', ref]).decode('utf-8').strip()
build_dir = subprocess.check_output([utils.get_conan(), 'cache', 'path', '--folder', 'build', f'{ref}:{package_id}']).decode('utf-8').strip()
handle_build_completed(PackageReference(package_reference=ref), source_dir, build_dir)
except Exception as e:
print(f'Failed to collect directories for {ref}:{package_id}: {e}')
finally:
print('Cleaning cache...')
if not keep_sources:
subprocess.check_call([utils.get_conan(), 'cache', 'clean', '*'])
else:
subprocess.check_call([utils.get_conan(), 'cache', 'clean', '*', '--temp', '--build', '--download'])
def install_package(package_reference:PackageReference, profiles:ConanProfiles, remotes:list[str], allow_build:bool, keep_sources:bool):
recipe = get_recipe(package_reference)
try:
recipe.install(profiles, remotes, allow_build)
except subprocess.CalledProcessError as e:
recipe.export()
recipe.install(profiles, remotes, allow_build)
finally:
conan_cache.clean_cache(package_reference, sources=not keep_sources)
def install_all(build_order:list[str], profiles:ConanProfiles, remotes:list[str], allow_build:bool, keep_sources:bool):
for package_name in build_order:
install_package(PackageReference(package_name=package_name), profiles, remotes, allow_build, keep_sources)
def print_build_order(recipe_path:str, config_path:str, profiles:ConanProfiles, remotes:list[str]):
cmd = [
utils.get_conan(), 'graph', 'info',
'-pr:h', profiles.host_profile,
'-pr:b', profiles.build_profile,
'--build="*"',
'--format', 'json',
]
if remotes and len(remotes) > 0:
for remote in remotes:
cmd += ['-r', remote]
else:
cmd += ['--no-remote']
if config_path:
print(f'Loading options from {config_path}')
with open(config_path, 'r') as f:
config = yaml.safe_load(f)['config']
print(config)
if 'options' in config:
for opt in config['options'].split():
cmd += ['-o:h', opt.strip()]
cmd += [recipe_path]
print(cmd)
dependecies_graph = json.loads(subprocess.check_output(cmd))['graph']['nodes']
build_node_ids = []
dep_node_ids = []
def __update_node_ids(node_ids:list[str], node_id:str):
if node_id in node_ids:
node_ids.remove(node_id)
node_ids.append(node_id)
for node in dependecies_graph.values():
for depenency_id, dependecy in node['dependencies'].items():
if dependecy['build'] == 'True':
__update_node_ids(build_node_ids, depenency_id)
else:
__update_node_ids(dep_node_ids, depenency_id)
build_node_ids.reverse()
dep_node_ids.reverse()
processed_deps = set()
deps = []
def __fill_deps(node_ids:list[str]):
for node_id in node_ids:
dep_node = dependecies_graph[node_id]
if dep_node['ref'] in processed_deps:
continue
processed_deps.add(dep_node['ref'])
deps.append(f'{dep_node["name"]}/{dep_node["version"]}')
__fill_deps(build_node_ids)
__fill_deps(dep_node_ids)
return '\n'.join(deps)
def install_or_build(package_reference:PackageReference, profiles:ConanProfiles, remotes:list[str], allow_build:bool, keep_sources:bool):
recipe = get_recipe(package_reference)
try:
recipe.install(profiles, remotes, False)
except:
if not allow_build:
raise
try:
recipe.install(profiles, remotes, True)
except:
print("conan install failed, trying conan build instead")
build_package(package_reference, profiles, False, keep_sources)
finally:
conan_cache.clean_cache(package_reference, sources=not keep_sources)
def install_or_build_all(build_order:list[str], profiles:ConanProfiles, remotes:list[str], allow_build:bool, keep_sources:bool):
for package_name in build_order:
install_or_build(PackageReference(package_name=package_name), profiles, remotes, allow_build, keep_sources)