1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
import os
import re
import subprocess
from typing import cast, Optional, Sequence, TextIO, Union
from collections.abc import Mapping
from build.project import Project
from .toolchain import AnyToolchain
def __write_cmake_compiler(f: TextIO, language: str, compiler: str) -> None:
s = compiler.split(' ', 1)
if len(s) == 2:
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
compiler = s[1]
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
def __write_cmake_toolchain_file(f: TextIO, toolchain: AnyToolchain) -> None:
if toolchain.is_darwin:
cmake_system_name = 'Darwin'
elif toolchain.is_windows:
cmake_system_name = 'Windows'
else:
cmake_system_name = 'Linux'
f.write(f"""
set(CMAKE_SYSTEM_NAME {cmake_system_name})
set(CMAKE_SYSTEM_PROCESSOR {toolchain.host_triplet.split('-', 1)[0]})
set(CMAKE_C_COMPILER_TARGET {toolchain.host_triplet})
set(CMAKE_CXX_COMPILER_TARGET {toolchain.host_triplet})
set(CMAKE_C_FLAGS_INIT "{toolchain.cflags} {toolchain.cppflags}")
set(CMAKE_CXX_FLAGS_INIT "{toolchain.cxxflags} {toolchain.cppflags}")
""")
__write_cmake_compiler(f, 'C', toolchain.cc)
__write_cmake_compiler(f, 'CXX', toolchain.cxx)
if cmake_system_name == 'Darwin':
# On macOS, cmake forcibly adds an "-isysroot" flag even if
# one is already present in the flags variable; this breaks
# cross-compiling for iOS, and can be worked around by setting
# the CMAKE_OSX_SYSROOT variable
# (https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html).
m = re.search(r'-isysroot +(\S+)', toolchain.cflags)
if m:
sysroot = m.group(1)
print(f'set(CMAKE_OSX_SYSROOT {sysroot})', file=f)
# search libraries and headers only in the sysroot, not on
# the build host
f.write(f"""
set(CMAKE_FIND_ROOT_PATH "{toolchain.install_prefix};{sysroot}")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
""")
elif cmake_system_name == 'Windows':
# search libraries and headers only in the sysroot, not on
# the build host
f.write(f"""
set(CMAKE_FIND_ROOT_PATH "{toolchain.install_prefix}")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
""")
def configure(toolchain: AnyToolchain, src: str, build: str, args: list[str]=[], env: Optional[Mapping[str, str]]=None) -> None:
cross_args: list[str] = []
if toolchain.is_windows:
cross_args.append('-DCMAKE_RC_COMPILER=' + cast(str, toolchain.windres))
configure = [
'cmake',
src,
'-DCMAKE_INSTALL_PREFIX=' + toolchain.install_prefix,
'-DCMAKE_BUILD_TYPE=release',
'-GNinja',
] + cross_args + args
if toolchain.host_triplet is not None:
# cross-compiling: write a toolchain file
os.makedirs(build, exist_ok=True)
# Several targets need a sysroot to prevent pkg-config from
# looking for libraries on the build host (TODO: fix this
# properly); but we must not do that on Android because the NDK
# has a sysroot already
if not toolchain.is_android and not toolchain.is_darwin:
cross_args.append('-DCMAKE_SYSROOT=' + toolchain.install_prefix)
cmake_toolchain_file = os.path.join(build, 'cmake_toolchain_file')
with open(cmake_toolchain_file, 'w') as f:
__write_cmake_toolchain_file(f, toolchain)
configure.append('-DCMAKE_TOOLCHAIN_FILE=' + cmake_toolchain_file)
if env is None:
env = toolchain.env
else:
env = {**toolchain.env, **env}
print(configure)
subprocess.check_call(configure, env=env, cwd=build)
class CmakeProject(Project):
def __init__(self, url: Union[str, Sequence[str]], md5: str, installed: str,
configure_args: list[str]=[],
windows_configure_args: list[str]=[],
env: Optional[Mapping[str, str]]=None,
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
self.windows_configure_args = windows_configure_args
self.env = env
def configure(self, toolchain: AnyToolchain) -> str:
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure_args = self.configure_args
if toolchain.is_windows:
configure_args = configure_args + self.windows_configure_args
configure(toolchain, src, build, configure_args, self.env)
return build
def _build(self, toolchain: AnyToolchain) -> None:
build = self.configure(toolchain)
subprocess.check_call(['ninja', '-v', 'install'],
cwd=build, env=toolchain.env)
|