conan-recipes/utils/impl/conan_recipe.py
2023-07-20 12:03:21 +03:00

212 lines
7.6 KiB
Python

import os
import subprocess
import json
from impl.package_config_provider import package_config_provider
from impl.package_reference import PackageReference
from impl.utils import get_conan
from impl.profiles import ConanProfiles
from impl.config import directories
from impl.debug import handle_build_completed
class ConanRecipe:
installed_build_folders = []
def __init__(self, recipe_dir:str, package_reference:PackageReference):
self.recipe_dir = recipe_dir
self.reference = package_reference
self.config = package_config_provider.get_package_config(package_reference.name)
if not self.config:
raise RuntimeError(f"Package config for `{package_reference}` not found")
@property
def is_build_tool(self):
return 'build_tool' in self.config and self.config['build_tool']
@property
def is_python_require(self):
return 'python_require' in self.config and self.config['python_require']
@property
def local_source_dir(self):
return os.path.join(self.recipe_dir, 'src')
@property
def test_package_dir(self):
return os.path.join(self.recipe_dir, 'test_package')
@property
def local_build_dirs(self):
return [
directories.build_dir,
os.path.join(self.recipe_dir, 'build'),
os.path.join(self.recipe_dir, 'build-debug'),
os.path.join(self.recipe_dir, 'build-release'),
os.path.join(self.recipe_dir, 'build-relwithdebinfo'),
os.path.join(self.recipe_dir, 'build-minsizerel'),
] + self.installed_build_folders
@property
def local_test_build_dirs(self):
return [
os.path.join(self.test_package_dir, 'build'),
os.path.join(self.test_package_dir, 'build-debug'),
os.path.join(self.test_package_dir, 'build-release'),
os.path.join(self.test_package_dir, 'build-relwithdebinfo'),
os.path.join(self.test_package_dir, 'build-minsizerel'),
]
def export(self):
cmd = [
get_conan(), 'export', self.recipe_dir,
'--version', self.reference.version,
'--user', self.reference.user,
'--channel', self.reference.channel,
'--no-remote',
]
subprocess.check_call(cmd)
def source(self):
cmd = [
get_conan(), 'source', self.recipe_dir,
'--version', self.reference.version,
'--user', self.reference.user,
'--channel', self.reference.channel,
]
subprocess.check_call([get_conan(), '--version'])
subprocess.check_call(cmd)
def __run_build_command(self, cmd:str, profiles:ConanProfiles, remotes:list[str] = None, additional_options:list[str] = None, include_recipe=True, force_build_profile=False):
cmd = [
get_conan(), cmd,
'--version', self.reference.version,
'--user', self.reference.user,
'--channel', self.reference.channel,
'-vvv',
'-pr:h', profiles.get_profile(self.is_build_tool or force_build_profile),
'-pr:b', profiles.build_profile,
'-of', directories.build_dir,
]
if 'options' in self.config:
for opt in self.config['options'].split():
cmd += ['-o:h', opt.strip()]
if additional_options:
cmd += additional_options
if remotes and len(remotes) > 0:
for remote in remotes:
cmd += ['-r', remote]
else:
cmd += ['--no-remote']
if include_recipe:
cmd += [self.recipe_dir]
print(cmd)
subprocess.check_call(cmd)
def build(self, profiles:ConanProfiles, remotes:list[str] = None):
if self.is_python_require:
print(f"Skipping build for python_require package `{self.reference}`")
return
build_missing = 'allow-build-missing' in self.config and self.config['allow-build-missing']
additional_options=['--build', 'missing'] if build_missing else None
if not self.is_build_tool and 'use-both-profiles' in self.config and self.config['use-both-profiles']:
print(f"Building `{self.reference}` with build profile...", flush=True)
self.__run_build_command('build', profiles, additional_options=additional_options, force_build_profile=True, remotes=remotes)
print(f"== Creating Conan package with build profile...", flush=True)
self.__run_build_command('export-pkg', profiles, force_build_profile=True, remotes=remotes)
print(f"Building `{self.reference}`...", flush=True)
self.__run_build_command('build', profiles, additional_options=additional_options, remotes=remotes)
print(f"== Creating Conan package...", flush=True)
self.__run_build_command('export-pkg', profiles)
handle_build_completed(self.reference, self.local_source_dir, self.local_build_dirs)
def __get_package_id(self, install_output:bytes):
try:
nodes = json.loads(install_output)['graph']['nodes']
for node in nodes.values():
if str(self.reference) in node['ref']:
return node['package_id']
return None
except:
return None
def __get_build_folder(self, package_id:str):
try:
return subprocess.check_output([
get_conan(), 'cache', 'path', f'{self.reference}:{package_id}', '--folder=build'
]).decode('utf-8').strip()
except subprocess.CalledProcessError:
return None
def __get_cache_source_folder(self, package_id:str):
try:
return subprocess.check_output([
get_conan(), 'cache', 'path', f'{self.reference}', '--folder=source'
]).decode('utf-8').strip()
except subprocess.CalledProcessError:
return None
def install(self, profiles:ConanProfiles, remotes:list[str] = None, build_missing:bool = False):
print(f"Installing `{self.reference}`...", flush=True)
cmd = [
get_conan(), 'install',
'-of', directories.install_dir,
'--tool-requires' if self.is_build_tool else '--requires', str(self.reference),
'-vvv',
'-pr:h', profiles.host_profile,
'-pr:b', profiles.build_profile,
'--format', 'json'
]
if build_missing:
cmd += ['--build', 'missing']
if remotes and len(remotes) > 0:
for remote in remotes:
cmd += ['-r', remote]
else:
cmd += ['--no-remote']
if 'options' in self.config:
for opt in self.config['options'].split():
cmd += ['-o:h', opt.strip()]
package_id = self.__get_package_id(subprocess.check_output(cmd))
print(F'Package ID: {package_id}')
if package_id:
build_folder = self.__get_build_folder(package_id)
if build_folder:
print(F'Build folder: {build_folder}')
self.installed_build_folders.append(build_folder)
handle_build_completed(self.reference, self.__get_cache_source_folder(package_id), build_folder)
def execute_command(self, command:str):
if command == 'export-recipes':
self.export()
elif command == 'update-sources':
self.source()
def upload(self, remote_name:str, with_binaries:bool):
args = [get_conan(), 'upload', '--check', '--confirm', '-r', remote_name, str(self.reference)]
if not with_binaries:
args += ['--only-recipe']
subprocess.check_call(args)