Fixed compilation with MSVC.
This commit is contained in:
parent
cd727e5a1d
commit
4e579121db
119
SConscript
119
SConscript
@ -1,5 +1,6 @@
|
||||
|
||||
import copy
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import psutil
|
||||
@ -108,12 +109,33 @@ def _rglob(env: Environment, root_path: str, pattern: str, **kwargs):
|
||||
result_nodes.extend(env.Glob(f'{path}/{pattern}', **kwargs))
|
||||
return sorted(result_nodes)
|
||||
|
||||
def _safe_eval(condition: str, locals={}):
|
||||
return eval(condition, {
|
||||
'__builtins__': {
|
||||
'abs': abs, 'all': all, 'any': any, 'ascii': ascii, 'bin': bin, 'bool': bool, 'chr': chr, 'complex': complex,
|
||||
'dict': dict, 'divmod': divmod, 'enumerate': enumerate, 'filter': filter, 'float': float, 'format': format,
|
||||
'hasattr': hasattr, 'hash': hash, 'hex': hex, 'id': id, 'int': int, 'isinstance': isinstance,
|
||||
'issubclass': issubclass, 'len': len, 'list': list, 'map': map, 'max': max, 'min': min, 'next': next,
|
||||
'oct': oct, 'ord': ord, 'pow': pow, 'range': range, 'reversed': reversed, 'round': round, 'set': set,
|
||||
'slice': slice, 'sorted': sorted, 'str': str, 'sum': sum, 'tuple': tuple, 'type': type, 'zip': zip
|
||||
}
|
||||
}, locals)
|
||||
|
||||
def _deps_from_json(env: Environment, deps: dict) -> dict:
|
||||
for _, dep in deps.items():
|
||||
to_remove = []
|
||||
for key, dep in deps.items():
|
||||
if 'condition' in dep:
|
||||
if not _safe_eval(dep['condition'], {
|
||||
'compiler_family': env['COMPILER_FAMILY']
|
||||
}):
|
||||
to_remove.append(key)
|
||||
continue
|
||||
if 'min' in dep and isinstance(dep['min'], list):
|
||||
dep['min'] = tuple(dep['min'])
|
||||
if 'max' in dep and isinstance(dep['max'], list):
|
||||
dep['max'] = tuple(dep['max'])
|
||||
for key in to_remove:
|
||||
del deps[key]
|
||||
return deps
|
||||
|
||||
def _make_interface(env: Environment, dependencies: list = []):
|
||||
@ -126,21 +148,36 @@ def _make_interface(env: Environment, dependencies: list = []):
|
||||
}
|
||||
|
||||
def _lib_filename(env: Environment, name: str, type: str = 'static') -> str:
|
||||
# TODO: windows
|
||||
ext = {
|
||||
'static': 'a',
|
||||
'shared': 'so'
|
||||
}[type]
|
||||
return f'lib{name}.{ext}'
|
||||
if os.name == 'posix':
|
||||
ext = {
|
||||
'static': 'a',
|
||||
'shared': 'so'
|
||||
}[type]
|
||||
return f'lib{name}.{ext}'
|
||||
elif os.name == 'nt':
|
||||
ext = {
|
||||
'static': 'lib',
|
||||
'shared': 'dll'
|
||||
}[type]
|
||||
return f'{name}.{ext}'
|
||||
else:
|
||||
raise Exception('What OS is this?')
|
||||
|
||||
def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'static', allow_fail: bool = False):
|
||||
def _find_lib(env: Environment, name: str, paths: 'list[str]', type : str = 'static', allow_fail: bool = False, use_glob: bool = False):
|
||||
fname = _lib_filename(env, name, type)
|
||||
for path in paths:
|
||||
lib_path = os.path.join(path, _lib_filename(env, name, type))
|
||||
if os.path.exists(lib_path):
|
||||
lib_path = os.path.join(path, fname)
|
||||
if use_glob:
|
||||
files = glob.glob(lib_path)
|
||||
if len(files) == 1:
|
||||
return files[0]
|
||||
elif len(files) > 1:
|
||||
raise Exception(f'Multiple candidates found for library with name {name} in paths: "{", ".join(paths)}" with name: "{", ".join(files)}".')
|
||||
elif os.path.exists(lib_path):
|
||||
return lib_path
|
||||
if allow_fail:
|
||||
return None
|
||||
raise Exception(f'Could not find library with name {name} in paths: "{",".join(paths)}".')
|
||||
raise Exception(f'Could not find library with name {name} in paths: "{", ".join(paths)}" with name: "{fname}".')
|
||||
|
||||
def _error(env: Environment, message: str):
|
||||
print(message, file=sys.stderr)
|
||||
@ -216,22 +253,25 @@ def _version_matches(version, version_spec: _VersionSpec) -> bool:
|
||||
return True
|
||||
|
||||
def _find_version(env: Environment, dependency: _Dependency):
|
||||
versions = dependency.recipe.versions(env, update=False)
|
||||
_sort_versions(versions)
|
||||
for version in versions:
|
||||
if _version_matches(version, dependency.version_spec):
|
||||
canadd = True
|
||||
for depname, depspec in dependency.recipe.dependencies(env, version).items():
|
||||
if not _can_add_dependency(env, depname, _parse_version_spec(depspec)):
|
||||
canadd = False
|
||||
break
|
||||
if canadd:
|
||||
depdeps = []
|
||||
for update in (False, True):
|
||||
versions = dependency.recipe.versions(env, update=update)
|
||||
_sort_versions(versions)
|
||||
for version in versions:
|
||||
if _version_matches(version, dependency.version_spec):
|
||||
canadd = True
|
||||
for depname, depspec in dependency.recipe.dependencies(env, version).items():
|
||||
depdeps.append(_add_dependency(env, depname, _parse_version_spec(depspec)))
|
||||
dependency.version = version
|
||||
dependency.depdeps = depdeps
|
||||
return
|
||||
if not _can_add_dependency(env, depname, _parse_version_spec(depspec)):
|
||||
canadd = False
|
||||
break
|
||||
if canadd:
|
||||
depdeps = []
|
||||
for depname, depspec in dependency.recipe.dependencies(env, version).items():
|
||||
depdeps.append(_add_dependency(env, depname, _parse_version_spec(depspec)))
|
||||
dependency.version = version
|
||||
dependency.depdeps = depdeps
|
||||
return
|
||||
print(f'Available versions: \n{versions}')
|
||||
print(f'Required version: {dependency.version_spec}')
|
||||
raise Exception(f'Could not find a suitable version for dependency {dependency.name}.')
|
||||
|
||||
def _wrap_builder(builder, is_lib: bool = False):
|
||||
@ -469,6 +509,14 @@ if 'TOOLS' in config:
|
||||
env = Environment(tools = tools, variables = vars, ENV = os.environ)
|
||||
env['RECIPES_FOLDERS'] = [Dir('recipes')]
|
||||
env['SYSTEM_CACHE_DIR'] = os.path.join(_find_system_cache_dir(), 'spp_cache')
|
||||
env['CLONE_DIR'] = os.path.join(env['SYSTEM_CACHE_DIR'], 'cloned')
|
||||
env['DOWNLOAD_DIR'] = os.path.join(env['SYSTEM_CACHE_DIR'], 'downloaded')
|
||||
env['UPDATE_REPOSITORIES'] = update_repositories
|
||||
env['CXX_STANDARD'] = config['CXX_STANDARD'] # make it available to everyone
|
||||
env['DEPS_CFLAGS'] = []
|
||||
env['DEPS_CXXFLAGS'] = []
|
||||
env['DEPS_LINKFLAGS'] = []
|
||||
|
||||
print(f'Detected system cache directory: {env["SYSTEM_CACHE_DIR"]}')
|
||||
try:
|
||||
os.makedirs(env['SYSTEM_CACHE_DIR'], exist_ok=True)
|
||||
@ -585,13 +633,13 @@ if env['COMPILER_FAMILY'] == 'gcc' or env['COMPILER_FAMILY'] == 'clang':
|
||||
# -Wtautological-compare triggers in libfmt and doesn't seem too useful anyway
|
||||
env.Append(CCFLAGS = ['-Wno-missing-field-initializers', '-Wno-maybe-uninitialized'])
|
||||
env.Append(CXXFLAGS = ['-Wno-subobject-linkage', '-Wno-dangling-reference', '-Wno-init-list-lifetime', '-Wno-tautological-compare'])
|
||||
|
||||
else: # clang only
|
||||
# no-gnu-anonymous-struct - we don't care
|
||||
env.Append(CCFLAGS = ['-Wno-gnu-anonymous-struct'])
|
||||
env.Append(CXXFLAGS = ['-fexperimental-library']) # enable std::jthread
|
||||
if build_type == 'debug':
|
||||
env.Append(CCFLAGS = ['-g', '-O0'], CPPDEFINES = ['_GLIBCXX_DEBUG'])
|
||||
env.Append(DEPS_CXXFLAGS = ['-D_GLIBCXX_DEBUG'])
|
||||
elif build_type == 'release_debug' or build_type == 'profile':
|
||||
env.Append(CCFLAGS = ['-Wno-unused-variable', '-Wno-unused-parameter', '-Wno-unused-but-set-variable', '-Wno-unused-local-typedef', '-Wno-unused-local-typedefs', '-g', '-O2'], CPPDEFINES = [f'{config["PREPROCESSOR_PREFIX"]}_RELEASE', 'NDEBUG'])
|
||||
if build_type == 'profile':
|
||||
@ -606,20 +654,35 @@ if env['COMPILER_FAMILY'] == 'gcc' or env['COMPILER_FAMILY'] == 'clang':
|
||||
if enable_asan:
|
||||
env.Append(CCFLAGS = ['-fsanitize=address', '-fno-omit-frame-pointer'])
|
||||
env.Append(LINKFLAGS = ['-fsanitize=address'])
|
||||
env.Append(DEPS_CXXFLAGS = ['-fsanitize=address', '-fno-omit-frame-pointer'])
|
||||
env.Append(DEPS_LINKFLAGS = ['-fsanitize=address'])
|
||||
|
||||
elif env['COMPILER_FAMILY'] == 'cl':
|
||||
cxx_version_name = {
|
||||
'c++14': 'c++14',
|
||||
'c++17': 'c++17',
|
||||
'c++20': 'c++20',
|
||||
'c++23': 'c++latest',
|
||||
'c++26': 'c++latest'
|
||||
}.get(env['CXX_STANDARD'], 'c++14') # default to C++14 for older versions
|
||||
# C4201: nonstandard extension used : nameless struct/union - I use it and want to continue using it
|
||||
# C4127: conditional expression is constant - some libs (CRC, format) don't compile with this enabled # TODO: fix?
|
||||
# C4702: unreachable code, issued after MIJIN_FATAL macro
|
||||
# C4251: missing dll-interface of some std types, yaml-cpp doesn't compile with this enabled
|
||||
# C4275: same as above
|
||||
env.Append(CCFLAGS = ['/W4', '/WX', '/wd4201', '/wd4127', '/wd4702', '/wd4251', '/wd4275', '/bigobj', f'/std:{config["CXX_STANDARD"]}', '/permissive-', '/EHsc', '/FS', '/Zc:char8_t'])
|
||||
env.Append(CCFLAGS = ['/W4', '/WX', '/wd4201', '/wd4127', '/wd4702', '/wd4251', '/wd4275', '/bigobj', '/vmg',
|
||||
f'/std:{cxx_version_name}', '/permissive-', '/EHsc', '/FS', '/Zc:char8_t', '/utf-8'])
|
||||
env.Append(CPPDEFINES = ['_CRT_SECURE_NO_WARNINGS']) # I'd like to not use MSVC specific versions of functions because they are "safer" ...
|
||||
env.Append(DEPS_CXXFLAGS = ['/EHsc', '/Zc:char8_t', '/utf-8', '/vmg'])
|
||||
if build_type == 'debug':
|
||||
env.Append(CCFLAGS = ['/Od', '/Zi', '/MDd'], LINKFLAGS = ' /DEBUG')
|
||||
env.Append(CPPDEFINES = ['_DEBUG', '_ITERATOR_DEBUG_LEVEL=2'])
|
||||
env.Append(DEPS_CXXFLAGS = ['/MDd', '/Zi', '/D_DEBUG', '/D_ITERATOR_DEBUG_LEVEL=2'])
|
||||
env.Append(DEPS_LINKFLAGS = ['/DEBUG'])
|
||||
elif build_type == 'release_debug' or build_type == 'profile':
|
||||
env.Append(CCFLAGS = ['/O2', '/Zi'], LINKFLAGS = ' /DEBUG')
|
||||
env.Append(DEPS_CXXFLAGS = ['/Zi'])
|
||||
env.Append(DEPS_LINKFLAGS = ['/DEBUG'])
|
||||
else:
|
||||
env.Append(CCFLAGS = ['/O2'])
|
||||
|
||||
|
@ -6,7 +6,7 @@ import shutil
|
||||
from SCons.Script import *
|
||||
|
||||
_BUILT_STAMPFILE = '.spp_built'
|
||||
_VERSION = 0 # bump if you change how the projects are build to trigger a clean build
|
||||
_VERSION = 2 # bump if you change how the projects are build to trigger a clean build
|
||||
|
||||
Import('env')
|
||||
|
||||
@ -14,19 +14,31 @@ def cmd_quote(s: str) -> str:
|
||||
escaped = s.replace('\\', '\\\\')
|
||||
return f'"{escaped}"'
|
||||
|
||||
def _generate_cmake_c_flags(dependencies: 'list[dict]') -> str:
|
||||
parts = []
|
||||
def _generate_cmake_c_flags(env, dependencies: 'list[dict]') -> str:
|
||||
parts = env['DEPS_CFLAGS'].copy()
|
||||
for dependency in dependencies:
|
||||
for path in dependency.get('CPPPATH', []):
|
||||
parts.append(cmd_quote(f'-I{path}'))
|
||||
return ' '.join(parts)
|
||||
parts.append(f'-I{path}')
|
||||
return cmd_quote(' '.join(parts))
|
||||
|
||||
def _generate_cmake_cxx_flags(dependencies: 'list[dict]') -> str:
|
||||
parts = []
|
||||
def _generate_cmake_cxx_flags(env, dependencies: 'list[dict]') -> str:
|
||||
parts = env['DEPS_CXXFLAGS'].copy()
|
||||
for dependency in dependencies:
|
||||
for path in dependency.get('CPPPATH', []):
|
||||
parts.append(cmd_quote(f'-I{path}'))
|
||||
return ' '.join(parts)
|
||||
parts.append(f'-I{path}')
|
||||
return cmd_quote(' '.join(parts))
|
||||
|
||||
def _get_cmake_cxx_standard(env: Environment) -> str:
|
||||
return env['CXX_STANDARD'][3:] # we use "C++XX", CMake just "XX"
|
||||
|
||||
def _generate_cmake_args(env: Environment, dependencies: 'list[dict]') -> 'list[str]':
|
||||
args = [f'-DCMAKE_C_FLAGS={_generate_cmake_c_flags(env, dependencies)}',
|
||||
f'-DCMAKE_CXX_FLAGS={_generate_cmake_cxx_flags(env, dependencies)}',
|
||||
f'-DCMAKE_CXX_STANDARD={_get_cmake_cxx_standard(env)}']
|
||||
for dependency in dependencies:
|
||||
for name, value in dependency.get('CMAKE_VARS', {}).items():
|
||||
args.append(f'-D{name}={cmd_quote(value)}')
|
||||
return args
|
||||
|
||||
def _calc_version_hash(dependencies: 'list[dict]') -> str:
|
||||
return json.dumps({
|
||||
@ -63,14 +75,14 @@ def _cmake_project(env: Environment, project_root: str, generate_args: 'list[str
|
||||
'profile': 'RelWithDebInfo'
|
||||
}.get(env['BUILD_TYPE'], 'RelWithDebInfo')
|
||||
def run_cmd(args):
|
||||
env.Execute(' '.join([str(s) for s in args]))
|
||||
if env.Execute(' '.join([str(s) for s in args])):
|
||||
Exit(1)
|
||||
# TODO: is this a problem?
|
||||
# environ = os.environ.copy()
|
||||
# environ['CXXFLAGS'] = ' '.join(f'-D{define}' for define in env['CPPDEFINES']) # TODO: who cares about windows?
|
||||
run_cmd(['cmake', '-G', 'Ninja', '-B', build_dir, f'-DCMAKE_BUILD_TYPE={build_type}',
|
||||
f'-DCMAKE_INSTALL_PREFIX={cmd_quote(install_dir)}', '-DBUILD_TESTING=OFF',
|
||||
f'-DCMAKE_C_FLAGS={_generate_cmake_c_flags(dependencies)}',
|
||||
f'-DCMAKE_CXX_FLAGS={_generate_cmake_cxx_flags(dependencies)}', *generate_args, project_root])
|
||||
*_generate_cmake_args(env, dependencies), *generate_args, project_root])
|
||||
run_cmd(['cmake', '--build', *build_args, cmd_quote(build_dir)])
|
||||
run_cmd(['cmake', '--install', *install_args, cmd_quote(build_dir)])
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from SCons.Script import *
|
||||
@ -20,11 +21,15 @@ def versions(env: Environment, update: bool = False):
|
||||
continue
|
||||
result.append((int(match.groups()[0]), int(match.groups()[1]), int(match.groups()[2])))
|
||||
with open(versions_file, 'w') as f:
|
||||
json.dump(versions, f)
|
||||
json.dump(result, f)
|
||||
return result
|
||||
else:
|
||||
with open(versions_file, 'r') as f:
|
||||
return [tuple(v) for v in json.load(f)]
|
||||
try:
|
||||
with open(versions_file, 'r') as f:
|
||||
return [tuple(v) for v in json.load(f)]
|
||||
except:
|
||||
print('boost_versions.json is empty or broken, redownloading.')
|
||||
return versions(env, update=True)
|
||||
|
||||
def dependencies(env: Environment, version) -> 'dict':
|
||||
return {}
|
||||
@ -50,7 +55,12 @@ def cook(env: Environment, version) -> dict:
|
||||
libs.append(fname)
|
||||
else:
|
||||
for lib in set(env['BOOST_LIBS']):
|
||||
libs.append(env.FindLib(f'boost_{lib}', paths=build_result['LIBPATH']))
|
||||
if os.name == 'posix':
|
||||
libs.append(env.FindLib(f'boost_{lib}', paths=build_result['LIBPATH']))
|
||||
elif os.name == 'nt':
|
||||
libs.append(env.FindLib(f'libboost_{lib}-*', paths=build_result['LIBPATH'], use_glob=True))
|
||||
else:
|
||||
raise Exception('Boost not supported on this platform.')
|
||||
return {
|
||||
'CPPPATH': build_result['CPPPATH'],
|
||||
'LIBS': libs
|
||||
|
@ -1,14 +1,25 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from SCons.Script import *
|
||||
|
||||
def _build_lib_name(env: Environment) -> str:
|
||||
if os.name == 'posix':
|
||||
return {
|
||||
'debug': 'png16d'
|
||||
}.get(env['BUILD_TYPE'], 'png16')
|
||||
elif os.name == 'nt':
|
||||
return {
|
||||
'debug': 'libpng16_staticd'
|
||||
}.get(env['BUILD_TYPE'], 'libpng16_static')
|
||||
else:
|
||||
raise Exception('libpng is not supported yet on this OS')
|
||||
|
||||
def _git_cook(env: Environment, repo: dict) -> dict:
|
||||
lib_zlib = env.Cook('zlib')
|
||||
checkout_root = repo['checkout_root']
|
||||
build_result = env.CMakeProject(checkout_root, dependencies = [lib_zlib])
|
||||
lib_name = {
|
||||
'debug': 'png16d'
|
||||
}.get(env['BUILD_TYPE'], 'png16')
|
||||
lib_name = _build_lib_name(env)
|
||||
return {
|
||||
'CPPPATH': build_result['CPPPATH'],
|
||||
'LIBS': [env.FindLib(lib_name, paths=build_result['LIBPATH'])]
|
||||
|
@ -11,7 +11,11 @@ def dependencies(env: Environment, version) -> 'dict':
|
||||
def cook(env: Environment, version) -> dict:
|
||||
repo = env.GitBranch(repo_name = 'mikktspace', remote_url = 'https://github.com/mmikk/MikkTSpace.git', git_ref = 'master')
|
||||
checkout_root = repo['checkout_root']
|
||||
ccflags = env['CCFLAGS'].copy()
|
||||
if env['COMPILER_FAMILY'] == 'cl':
|
||||
ccflags.append('/wd4456')
|
||||
lib_mikktspace = env.StaticLibrary(
|
||||
CCFLAGS = ccflags,
|
||||
CPPPATH = [checkout_root],
|
||||
target = env['LIB_DIR'] + '/mikktspace',
|
||||
source = [os.path.join(repo['checkout_root'], 'mikktspace.c')]
|
||||
|
@ -1,4 +1,5 @@
|
||||
|
||||
import os
|
||||
import re
|
||||
from SCons.Script import *
|
||||
|
||||
@ -6,6 +7,18 @@ _REPO_NAME = 'zlib'
|
||||
_REPO_URL = 'https://github.com/madler/zlib.git'
|
||||
_TAG_PATTERN = re.compile(r'^v([0-9]+)\.([0-9]+)(?:\.([0-9]+))?$')
|
||||
|
||||
def _build_lib_name(env: Environment) -> str:
|
||||
if os.name == 'posix':
|
||||
return {
|
||||
'debug': 'zd'
|
||||
}.get(env['BUILD_TYPE'], 'z')
|
||||
elif os.name == 'nt':
|
||||
return {
|
||||
'debug': 'zlibstaticd'
|
||||
}.get(env['BUILD_TYPE'], 'zlibstatic')
|
||||
else:
|
||||
raise Exception('libpng is not supported yet on this OS')
|
||||
|
||||
def versions(env: Environment, update: bool = False):
|
||||
tags = env.GitTags(repo_name = _REPO_NAME, remote_url = _REPO_URL, force_fetch=update)
|
||||
result = []
|
||||
@ -25,7 +38,14 @@ def cook(env: Environment, version) -> dict:
|
||||
repo = env.GitBranch(repo_name = _REPO_NAME, remote_url = _REPO_URL, git_ref = git_ref)
|
||||
checkout_root = repo['checkout_root']
|
||||
build_result = env.CMakeProject(project_root=checkout_root)
|
||||
include_dir = os.path.join(build_result['install_dir'], 'include')
|
||||
lib_name = _build_lib_name(env)
|
||||
lib_file = env.FindLib(lib_name, paths=build_result['LIBPATH'])
|
||||
return {
|
||||
'CPPPATH': [os.path.join(build_result['install_dir'], 'include')],
|
||||
'LIBS': [env.FindLib('z', paths=build_result['LIBPATH'])]
|
||||
'CPPPATH': [include_dir],
|
||||
'LIBS': [lib_file],
|
||||
'CMAKE_VARS': {
|
||||
'ZLIB_LIBRARY': lib_file,
|
||||
'ZLIB_INCLUDE_DIR': include_dir
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user