source-engine/scripts/waifulib/conan.py

247 lines
7.1 KiB
Python
Raw Normal View History

2021-04-28 13:36:34 +08:00
# encoding: utf-8
# conan.py -- Conan Package Manager integration
# Copyright (C) 2019 a1batross
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
from waflib import Logs, Utils
from waflib.Configure import conf
import os
import sys
import subprocess
import json
def options(opt):
grp = opt.add_option_group('Conan options')
grp.add_option('--disable-conan', action = 'store_true', default = False, dest = 'NO_CONAN',
help = 'completely disable Conan')
grp.add_option('--force-conan', action = 'store_true', default = False, dest = 'CONAN_MANDATORY',
help = 'require Conan, useful for testing')
grp.add_option('--conan-profile', action = 'store', default = None, dest = 'CONAN_PROFILE',
help = 'set conan profile')
# grp.add_option('')
def conan(ctx, args, quiet=False):
# print('%s %s' % (ctx.env.CONAN[0], arg_str))
argv = [ctx.env.CONAN[0]]
argv += Utils.to_list(args)
ret = b''
retval = False
ctx.logger.info('argv: {}'.format(argv))
if quiet:
try:
ret = subprocess.check_output(argv, cwd=ctx.bldnode.abspath())
ctx.logger.info('output: \n{}'.format(ret))
except subprocess.CalledProcessError as e:
ret = e.output
ctx.logger.info('FAIL!!!\nretval: {}\noutput: \n{}'.format(e.returncode, ret))
else:
retval = True
else:
retval = subprocess.call(argv, cwd=ctx.bldnode.abspath())
if retval != 0:
ctx.logger.info('FAIL!!!\nretval: {}'.format(retval))
retval = False
else:
retval = True
if sys.version_info > (3, 0):
ret = ret.decode('utf-8')
ret = ret.strip().replace('\r\n', '\n')
return (retval, ret)
@conf
def add_conan_remote(ctx, name, url):
"""
Adds conan remote
:param name: name of remote
:type name: string
:param url: url path
:type url: string
"""
if not ctx.env.CONAN:
ctx.fatal("Conan is not installed!")
ctx.start_msg('Checking if conan has %s remote' % name)
[success,remotes] = conan(ctx, 'remote list --raw', quiet=True)
if not success:
ctx.end_msg('no')
ctx.fatal('conan has failed to list remotes')
found = False
for v in remotes.splitlines():
a = v.split(' ')
if a[0] == name:
if a[1] == url:
found = True
else:
ctx.end_msg('no')
ctx.fatal('''Conan already has %s remote, but it points to another remote!
You can remote it with:\n
$ %s remote remove %s''' % (name, ctx.env.CONAN[0], name))
break
if not found:
ctx.end_msg('no')
ctx.start_msg('Adding new %s remote to conan' % name)
if conan(ctx, 'remote add %s %s' % (name, url), quiet=True) == False:
ctx.end_msg('fail', color='RED')
ctx.fatal('conan has failed to add %s remote' % name)
ctx.end_msg('done')
else:
ctx.end_msg('yes')
@conf
def parse_conan_json(ctx, name):
with open(os.path.join(ctx.bldnode.abspath(), 'conanbuildinfo.json')) as jsonfile:
cfg = json.load(jsonfile)
deps = cfg["dependencies"]
ctx.env['HAVE_%s' % name] = True
for dep in deps:
def to_u8(arr):
if sys.version_info > (3, 0):
return arr
ret = []
for i in arr:
ret += [ i.encode('utf8') ]
return ret
ctx.env['INCLUDES_%s' % name] += to_u8(dep['include_paths'])
ctx.env['LIB_%s' % name] += to_u8(dep['libs'])
ctx.env['LIBPATH_%s' % name] += to_u8(dep['lib_paths'])
ctx.env['DEFINES_%s' % name] += to_u8(dep['defines'])
ctx.env['CFLAGS_%s' % name] += to_u8(dep['cflags'])
ctx.env['CXXFLAGS_%s' % name] += to_u8(dep['cflags'])
ctx.env['LINKFLAGS_%s' % name] += to_u8(dep['sharedlinkflags'])
return
def conan_update_profile(ctx, settings, profile):
args = ['profile', 'update']
for (key, value) in settings.items():
args2 = args + [ 'settings.%s=%s' % (key, value), profile ]
if conan(ctx, args2, quiet=True) == False:
ctx.fatal('Can\'t update profile')
@conf
def add_dependency(ctx, pkg, *k, **kw):
"""
Retrieves and adds depedency during configuration stage
:param pkg: package name in conan format
:type pkg: string
:param remote: remote name, optional
:type remote: string
:param options: package options, optional
:type options: dict
:param uselib_store: set uselib name, optional
:type uselib_store: string
"""
if not ctx.env.CONAN:
ctx.fatal("Conan is not installed!")
name = pkg.split('/')[0]
ctx.msg(msg='Downloading dependency %s' % name, result='in process', color='BLUE')
args = ['install', pkg, '-g', 'json', '--build=missing', '-pr', ctx.env.CONAN_PROFILE]
if 'remote' in kw:
args += ['-r', kw['remote']]
if 'options' in kw:
for (key, value) in kw['options'].items():
args += ['-o', '%s=%s' % (key, value)]
if conan(ctx, args):
if 'uselib_store' in kw:
uselib = kw['uselib_store']
else: uselib = name.upper() # we just use upper names everywhere
ctx.parse_conan_json(uselib)
ctx.msg(msg='Downloading dependency %s' % name, result='ok', color='GREEN')
return
ctx.msg(msg='Downloading dependency %s' % name, result='fail', color='RED')
ctx.fatal('Conan has failed installing dependency %s' % pkg)
def configure(conf):
# already configured
if conf.env.CONAN:
return
# respect project settings
if not conf.env.CONAN_MANDATORY:
conf.env.CONAN_MANDATORY = conf.options.CONAN_MANDATORY
# disabled by user request
if conf.options.NO_CONAN and not conf.env.CONAN_MANDATORY:
conf.env.CONAN = None
return
conf.find_program('conan', mandatory=conf.env.MANDATORY)
if not conf.env.CONAN:
return
conf.start_msg('Checking conan version')
ver = conan(conf, '--version', quiet=True)
if not ver:
conf.end_msg('fail')
if conf.env.CONAN_MANDATORY:
conf.fatal('Conan has failed! Check your conan installation')
else:
Logs.warn('Conan has failed! Check your conan installation. Continuing...')
if conf.options.CONAN_PROFILE:
conf.env.CONAN_PROFILE = conf.options.CONAN_PROFILE
else:
profile = conf.env.CONAN_PROFILE = os.path.join(conf.bldnode.abspath(), 'temp_profile')
settings = dict()
conan(conf, ['profile', 'new', profile, '--detect', '--force'], quiet=True)
# NOTE: Conan installs even 32-bit runtime packages on x86_64 for now :(
# it may potentially break system on Linux
if conf.env.DEST_SIZEOF_VOID_P == 4 and conf.env.DEST_CPU in ['x86', 'x86_64'] and conf.env.DEST_OS != 'linux':
settings['arch'] = 'x86'
if conf.env.DEST_OS2 == 'android':
settings['os'] = 'Android'
if conf.env.COMPILER_CC == 'msvc':
settings['compiler.runtime'] = 'MT'
conan_update_profile(conf, settings, profile)
# I think Conan is respecting environment CC/CXX values, so it's not need
# to specify compiler here
#compiler = conf.env.COMPILER_CC
#if conf.env.COMPILER_CC == 'msvc':
# compiler = 'Visual Studio'
#elif conf.env.DEST_OS == 'darwin' and conf.env.COMPILER_CC == 'clang':
# compiler = 'apple-clang'
#elif conf.env.COMPILER_CC == 'suncc':
# compiler = 'sun-cc'
#settings['compiler'] = compiler
conf.end_msg(ver)