aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Mensinger <daniel@mensinger-ka.de>2020-09-29 12:29:15 +0200
committerDaniel Mensinger <daniel@mensinger-ka.de>2020-10-04 10:45:48 +0200
commit77b5c82d0725eb914aa341a617dd9f418de60910 (patch)
treecfdb171f0193e5b74ec3aade2ceb6e6af5865e7f
parent10b44584ff0b1f49ece260a48f89eb59c123616f (diff)
downloadmeson-77b5c82d0725eb914aa341a617dd9f418de60910.zip
meson-77b5c82d0725eb914aa341a617dd9f418de60910.tar.gz
meson-77b5c82d0725eb914aa341a617dd9f418de60910.tar.bz2
cmake: switch to pathlib (fixes #7322)
-rw-r--r--mesonbuild/cmake/client.py24
-rw-r--r--mesonbuild/cmake/common.py43
-rw-r--r--mesonbuild/cmake/executor.py40
-rw-r--r--mesonbuild/cmake/fileapi.py89
-rw-r--r--mesonbuild/cmake/interpreter.py252
-rw-r--r--mesonbuild/cmake/traceparser.py45
-rw-r--r--mesonbuild/dependencies/base.py8
-rw-r--r--mesonbuild/interpreter.py2
-rw-r--r--mesonbuild/mesonlib.py9
-rw-r--r--test cases/cmake/2 advanced/test.json3
-rw-r--r--test cases/cmake/3 advanced no dep/test.json3
11 files changed, 262 insertions, 256 deletions
diff --git a/mesonbuild/cmake/client.py b/mesonbuild/cmake/client.py
index 2089be4..8057c3b 100644
--- a/mesonbuild/cmake/client.py
+++ b/mesonbuild/cmake/client.py
@@ -21,9 +21,9 @@ from ..mesonlib import MachineChoice
from .. import mlog
from contextlib import contextmanager
from subprocess import Popen, PIPE, TimeoutExpired
+from pathlib import Path
import typing as T
import json
-import os
if T.TYPE_CHECKING:
from ..environment import Environment
@@ -128,7 +128,7 @@ class MessageHello(MessageBase):
# Request classes
class RequestHandShake(RequestBase):
- def __init__(self, src_dir: str, build_dir: str, generator: str, vers_major: int, vers_minor: T.Optional[int] = None) -> None:
+ def __init__(self, src_dir: Path, build_dir: Path, generator: str, vers_major: int, vers_minor: T.Optional[int] = None) -> None:
super().__init__('handshake')
self.src_dir = src_dir
self.build_dir = build_dir
@@ -142,13 +142,13 @@ class RequestHandShake(RequestBase):
vers['minor'] = self.vers_minor
# Old CMake versions (3.7) want '/' even on Windows
- src_list = os.path.normpath(self.src_dir).split(os.sep)
- bld_list = os.path.normpath(self.build_dir).split(os.sep)
+ self.src_dir = self.src_dir.resolve()
+ self.build_dir = self.build_dir.resolve()
return {
**super().to_dict(),
- 'sourceDirectory': '/'.join(src_list),
- 'buildDirectory': '/'.join(bld_list),
+ 'sourceDirectory': self.src_dir.as_posix(),
+ 'buildDirectory': self.build_dir.as_posix(),
'generator': self.generator,
'protocolVersion': vers
}
@@ -191,15 +191,15 @@ class ReplyCompute(ReplyBase):
super().__init__(cookie, 'compute')
class ReplyCMakeInputs(ReplyBase):
- def __init__(self, cookie: str, cmake_root: str, src_dir: str, build_files: T.List[CMakeBuildFile]) -> None:
+ def __init__(self, cookie: str, cmake_root: Path, src_dir: Path, build_files: T.List[CMakeBuildFile]) -> None:
super().__init__(cookie, 'cmakeInputs')
self.cmake_root = cmake_root
self.src_dir = src_dir
self.build_files = build_files
def log(self) -> None:
- mlog.log('CMake root: ', mlog.bold(self.cmake_root))
- mlog.log('Source dir: ', mlog.bold(self.src_dir))
+ mlog.log('CMake root: ', mlog.bold(self.cmake_root.as_posix()))
+ mlog.log('Source dir: ', mlog.bold(self.src_dir.as_posix()))
mlog.log('Build files:', mlog.bold(str(len(self.build_files))))
with mlog.nested():
for i in self.build_files:
@@ -304,7 +304,7 @@ class CMakeClient:
raise CMakeException('CMake server query failed')
return reply
- def do_handshake(self, src_dir: str, build_dir: str, generator: str, vers_major: int, vers_minor: T.Optional[int] = None) -> None:
+ def do_handshake(self, src_dir: Path, build_dir: Path, generator: str, vers_major: int, vers_minor: T.Optional[int] = None) -> None:
# CMake prints the hello message on startup
msg = self.readMessage()
if not isinstance(msg, MessageHello):
@@ -327,8 +327,8 @@ class CMakeClient:
files = []
for i in data['buildFiles']:
for j in i['sources']:
- files += [CMakeBuildFile(j, i['isCMake'], i['isTemporary'])]
- return ReplyCMakeInputs(data['cookie'], data['cmakeRootDirectory'], data['sourceDirectory'], files)
+ files += [CMakeBuildFile(Path(j), i['isCMake'], i['isTemporary'])]
+ return ReplyCMakeInputs(data['cookie'], Path(data['cmakeRootDirectory']), Path(data['sourceDirectory']), files)
@contextmanager
def connect(self) -> T.Generator[None, None, None]:
diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py
index f6bd944..bb0c4ae 100644
--- a/mesonbuild/cmake/common.py
+++ b/mesonbuild/cmake/common.py
@@ -17,13 +17,14 @@
from ..mesonlib import MesonException
from .. import mlog
+from pathlib import Path
import typing as T
class CMakeException(MesonException):
pass
class CMakeBuildFile:
- def __init__(self, file: str, is_cmake: bool, is_temp: bool) -> None:
+ def __init__(self, file: Path, is_cmake: bool, is_temp: bool) -> None:
self.file = file
self.is_cmake = is_cmake
self.is_temp = is_temp
@@ -81,7 +82,7 @@ def cmake_defines_to_args(raw: T.Any, permissive: bool = False) -> T.List[str]:
return res
class CMakeInclude:
- def __init__(self, path: str, isSystem: bool = False):
+ def __init__(self, path: Path, isSystem: bool = False):
self.path = path
self.isSystem = isSystem
@@ -94,7 +95,7 @@ class CMakeFileGroup:
self.flags = _flags_to_list(data.get('compileFlags', '')) # type: T.List[str]
self.is_generated = data.get('isGenerated', False) # type: bool
self.language = data.get('language', 'C') # type: str
- self.sources = data.get('sources', []) # type: T.List[str]
+ self.sources = [Path(x) for x in data.get('sources', [])] # type: T.List[Path]
# Fix the include directories
self.includes = [] # type: T.List[CMakeInclude]
@@ -103,9 +104,9 @@ class CMakeFileGroup:
isSystem = i.get('isSystem', False)
assert isinstance(isSystem, bool)
assert isinstance(i['path'], str)
- self.includes += [CMakeInclude(i['path'], isSystem)]
+ self.includes += [CMakeInclude(Path(i['path']), isSystem)]
elif isinstance(i, str):
- self.includes += [CMakeInclude(i)]
+ self.includes += [CMakeInclude(Path(i))]
def log(self) -> None:
mlog.log('flags =', mlog.bold(', '.join(self.flags)))
@@ -116,22 +117,22 @@ class CMakeFileGroup:
mlog.log('sources:')
for i in self.sources:
with mlog.nested():
- mlog.log(i)
+ mlog.log(i.as_posix())
class CMakeTarget:
def __init__(self, data: T.Dict[str, T.Any]) -> None:
- self.artifacts = data.get('artifacts', []) # type: T.List[str]
- self.src_dir = data.get('sourceDirectory', '') # type: str
- self.build_dir = data.get('buildDirectory', '') # type: str
+ self.artifacts = [Path(x) for x in data.get('artifacts', [])] # type: T.List[Path]
+ self.src_dir = Path(data.get('sourceDirectory', '')) # type: Path
+ self.build_dir = Path(data.get('buildDirectory', '')) # type: Path
self.name = data.get('name', '') # type: str
self.full_name = data.get('fullName', '') # type: str
self.install = data.get('hasInstallRule', False) # type: bool
- self.install_paths = list(set(data.get('installPaths', []))) # type: T.List[str]
+ self.install_paths = [Path(x) for x in set(data.get('installPaths', []))] # type: T.List[Path]
self.link_lang = data.get('linkerLanguage', '') # type: str
self.link_libraries = _flags_to_list(data.get('linkLibraries', '')) # type: T.List[str]
self.link_flags = _flags_to_list(data.get('linkFlags', '')) # type: T.List[str]
self.link_lang_flags = _flags_to_list(data.get('linkLanguageFlags', '')) # type: T.List[str]
- # self.link_path = data.get('linkPath', '') # type: str
+ # self.link_path = Path(data.get('linkPath', '')) # type: Path
self.type = data.get('type', 'EXECUTABLE') # type: str
# self.is_generator_provided = data.get('isGeneratorProvided', False) # type: bool
self.files = [] # type: T.List[CMakeFileGroup]
@@ -140,13 +141,13 @@ class CMakeTarget:
self.files += [CMakeFileGroup(i)]
def log(self) -> None:
- mlog.log('artifacts =', mlog.bold(', '.join(self.artifacts)))
- mlog.log('src_dir =', mlog.bold(self.src_dir))
- mlog.log('build_dir =', mlog.bold(self.build_dir))
+ mlog.log('artifacts =', mlog.bold(', '.join([x.as_posix() for x in self.artifacts])))
+ mlog.log('src_dir =', mlog.bold(self.src_dir.as_posix()))
+ mlog.log('build_dir =', mlog.bold(self.build_dir.as_posix()))
mlog.log('name =', mlog.bold(self.name))
mlog.log('full_name =', mlog.bold(self.full_name))
mlog.log('install =', mlog.bold('true' if self.install else 'false'))
- mlog.log('install_paths =', mlog.bold(', '.join(self.install_paths)))
+ mlog.log('install_paths =', mlog.bold(', '.join([x.as_posix() for x in self.install_paths])))
mlog.log('link_lang =', mlog.bold(self.link_lang))
mlog.log('link_libraries =', mlog.bold(', '.join(self.link_libraries)))
mlog.log('link_flags =', mlog.bold(', '.join(self.link_flags)))
@@ -161,17 +162,17 @@ class CMakeTarget:
class CMakeProject:
def __init__(self, data: T.Dict[str, T.Any]) -> None:
- self.src_dir = data.get('sourceDirectory', '') # type: str
- self.build_dir = data.get('buildDirectory', '') # type: str
- self.name = data.get('name', '') # type: str
- self.targets = [] # type: T.List[CMakeTarget]
+ self.src_dir = Path(data.get('sourceDirectory', '')) # type: Path
+ self.build_dir = Path(data.get('buildDirectory', '')) # type: Path
+ self.name = data.get('name', '') # type: str
+ self.targets = [] # type: T.List[CMakeTarget]
for i in data.get('targets', []):
self.targets += [CMakeTarget(i)]
def log(self) -> None:
- mlog.log('src_dir =', mlog.bold(self.src_dir))
- mlog.log('build_dir =', mlog.bold(self.build_dir))
+ mlog.log('src_dir =', mlog.bold(self.src_dir.as_posix()))
+ mlog.log('build_dir =', mlog.bold(self.build_dir.as_posix()))
mlog.log('name =', mlog.bold(self.name))
for idx, i in enumerate(self.targets):
mlog.log('Target {}:'.format(idx))
diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py
index d3588f0..bf89822 100644
--- a/mesonbuild/cmake/executor.py
+++ b/mesonbuild/cmake/executor.py
@@ -189,14 +189,14 @@ class CMakeExecutor:
if always_capture_stderr is not None:
self.always_capture_stderr = always_capture_stderr
- def _cache_key(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_cache_key:
+ def _cache_key(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]]) -> TYPE_cache_key:
fenv = frozenset(env.items()) if env is not None else frozenset()
targs = tuple(args)
- return (self.cmakebin.get_path(), targs, build_dir, fenv)
+ return (self.cmakebin.get_path(), targs, build_dir.as_posix(), fenv)
- def _call_cmout_stderr(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
+ def _call_cmout_stderr(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
cmd = self.cmakebin.get_command() + args
- proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.PIPE, cwd=build_dir, env=env)
+ proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.PIPE, cwd=str(build_dir), env=env) # TODO [PYTHON_37]: drop Path conversion
# stdout and stderr MUST be read at the same time to avoid pipe
# blocking issues. The easiest way to do this is with a separate
@@ -237,9 +237,9 @@ class CMakeExecutor:
return proc.returncode, None, raw_trace
- def _call_cmout(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
+ def _call_cmout(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
cmd = self.cmakebin.get_command() + args
- proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.STDOUT, cwd=build_dir, env=env)
+ proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.STDOUT, cwd=str(build_dir), env=env) # TODO [PYTHON_37]: drop Path conversion
while True:
line = proc.stdout.readline()
if not line:
@@ -249,11 +249,11 @@ class CMakeExecutor:
proc.wait()
return proc.returncode, None, None
- def _call_quiet(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
- os.makedirs(build_dir, exist_ok=True)
+ def _call_quiet(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
+ build_dir.mkdir(parents=True, exist_ok=True)
cmd = self.cmakebin.get_command() + args
- ret = S.run(cmd, env=env, cwd=build_dir, close_fds=False,
- stdout=S.PIPE, stderr=S.PIPE, universal_newlines=False)
+ ret = S.run(cmd, env=env, cwd=str(build_dir), close_fds=False,
+ stdout=S.PIPE, stderr=S.PIPE, universal_newlines=False) # TODO [PYTHON_37]: drop Path conversion
rc = ret.returncode
out = ret.stdout.decode(errors='ignore')
err = ret.stderr.decode(errors='ignore')
@@ -261,7 +261,7 @@ class CMakeExecutor:
mlog.debug("Called `{}` in {} -> {}".format(call, build_dir, rc))
return rc, out, err
- def _call_impl(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
+ def _call_impl(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
if not self.print_cmout:
return self._call_quiet(args, build_dir, env)
else:
@@ -270,7 +270,7 @@ class CMakeExecutor:
else:
return self._call_cmout(args, build_dir, env)
- def call(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]] = None, disable_cache: bool = False) -> TYPE_result:
+ def call(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]] = None, disable_cache: bool = False) -> TYPE_result:
if env is None:
env = os.environ.copy()
@@ -285,28 +285,28 @@ class CMakeExecutor:
cache[key] = self._call_impl(args, build_dir, env)
return cache[key]
- def call_with_fake_build(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]] = None) -> TYPE_result:
+ def call_with_fake_build(self, args: T.List[str], build_dir: Path, env: T.Optional[T.Dict[str, str]] = None) -> TYPE_result:
# First check the cache
cache = CMakeExecutor.class_cmake_cache
key = self._cache_key(args, build_dir, env)
if key in cache:
return cache[key]
- os.makedirs(build_dir, exist_ok=True)
+ build_dir.mkdir(exist_ok=True, parents=True)
# Try to set the correct compiler for C and C++
# This step is required to make try_compile work inside CMake
- fallback = os.path.realpath(__file__) # A file used as a fallback wehen everything else fails
+ fallback = Path(__file__).resolve() # A file used as a fallback wehen everything else fails
compilers = self.environment.coredata.compilers[MachineChoice.BUILD]
def make_abs(exe: str, lang: str) -> str:
- if os.path.isabs(exe):
+ if Path(exe).is_absolute():
return exe
p = shutil.which(exe)
if p is None:
mlog.debug('Failed to find a {} compiler for CMake. This might cause CMake to fail.'.format(lang))
- p = fallback
+ return str(fallback)
return p
def choose_compiler(lang: str) -> T.Tuple[str, str, str, str]:
@@ -331,7 +331,7 @@ class CMakeExecutor:
return make_abs(exe_list[1], lang), make_abs(exe_list[0], lang), comp_id, comp_version
else:
mlog.debug('Failed to find a {} compiler for CMake. This might cause CMake to fail.'.format(lang))
- return fallback, '', 'GNU', ''
+ return str(fallback), '', 'GNU', ''
c_comp, c_launcher, c_id, c_version = choose_compiler('c')
cxx_comp, cxx_launcher, cxx_id, cxx_version = choose_compiler('cpp')
@@ -346,10 +346,10 @@ class CMakeExecutor:
fortran_launcher = fortran_launcher.replace('\\', '/')
# Reset the CMake cache
- (Path(build_dir) / 'CMakeCache.txt').write_text('CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1\n')
+ (build_dir / 'CMakeCache.txt').write_text('CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1\n')
# Fake the compiler files
- comp_dir = Path(build_dir) / 'CMakeFiles' / self.cmakevers
+ comp_dir = build_dir / 'CMakeFiles' / self.cmakevers
comp_dir.mkdir(parents=True, exist_ok=True)
c_comp_file = comp_dir / 'CMakeCCompiler.cmake'
diff --git a/mesonbuild/cmake/fileapi.py b/mesonbuild/cmake/fileapi.py
index 0405145..4ef0caf 100644
--- a/mesonbuild/cmake/fileapi.py
+++ b/mesonbuild/cmake/fileapi.py
@@ -15,21 +15,21 @@
from .common import CMakeException, CMakeBuildFile, CMakeConfiguration
import typing as T
from .. import mlog
-import os
+from pathlib import Path
import json
import re
STRIP_KEYS = ['cmake', 'reply', 'backtrace', 'backtraceGraph', 'version']
class CMakeFileAPI:
- def __init__(self, build_dir: str):
- self.build_dir = build_dir
- self.api_base_dir = os.path.join(self.build_dir, '.cmake', 'api', 'v1')
- self.request_dir = os.path.join(self.api_base_dir, 'query', 'client-meson')
- self.reply_dir = os.path.join(self.api_base_dir, 'reply')
- self.cmake_sources = [] # type: T.List[CMakeBuildFile]
+ def __init__(self, build_dir: Path):
+ self.build_dir = build_dir
+ self.api_base_dir = self.build_dir / '.cmake' / 'api' / 'v1'
+ self.request_dir = self.api_base_dir / 'query' / 'client-meson'
+ self.reply_dir = self.api_base_dir / 'reply'
+ self.cmake_sources = [] # type: T.List[CMakeBuildFile]
self.cmake_configurations = [] # type: T.List[CMakeConfiguration]
- self.kind_resolver_map = {
+ self.kind_resolver_map = {
'codemodel': self._parse_codemodel,
'cmakeFiles': self._parse_cmakeFiles,
}
@@ -41,7 +41,7 @@ class CMakeFileAPI:
return self.cmake_configurations
def setup_request(self) -> None:
- os.makedirs(self.request_dir, exist_ok=True)
+ self.request_dir.mkdir(parents=True, exist_ok=True)
query = {
'requests': [
@@ -50,18 +50,17 @@ class CMakeFileAPI:
]
}
- with open(os.path.join(self.request_dir, 'query.json'), 'w') as fp:
- json.dump(query, fp, indent=2)
+ query_file = self.request_dir / 'query.json'
+ query_file.write_text(json.dumps(query, indent=2))
def load_reply(self) -> None:
- if not os.path.isdir(self.reply_dir):
+ if not self.reply_dir.is_dir():
raise CMakeException('No response from the CMake file API')
- files = os.listdir(self.reply_dir)
root = None
reg_index = re.compile(r'^index-.*\.json$')
- for i in files:
- if reg_index.match(i):
+ for i in self.reply_dir.iterdir():
+ if reg_index.match(i.name):
root = i
break
@@ -74,10 +73,9 @@ class CMakeFileAPI:
index = self._strip_data(index) # Strip unused data (again for loaded files)
# Debug output
- debug_json = os.path.normpath(os.path.join(self.build_dir, '..', 'fileAPI.json'))
- with open(debug_json, 'w') as fp:
- json.dump(index, fp, indent=2)
- mlog.cmd_ci_include(debug_json)
+ debug_json = self.build_dir / '..' / 'fileAPI.json'
+ debug_json.write_text(json.dumps(index, indent=2))
+ mlog.cmd_ci_include(debug_json.as_posix())
# parse the JSON
for i in index['objects']:
@@ -100,17 +98,17 @@ class CMakeFileAPI:
# resolved and the resulting data structure is identical
# to the CMake serve output.
- def helper_parse_dir(dir_entry: T.Dict[str, T.Any]) -> T.Tuple[str, str]:
- src_dir = dir_entry.get('source', '.')
- bld_dir = dir_entry.get('build', '.')
- src_dir = src_dir if os.path.isabs(src_dir) else os.path.join(source_dir, src_dir)
- bld_dir = bld_dir if os.path.isabs(bld_dir) else os.path.join(source_dir, bld_dir)
- src_dir = os.path.normpath(src_dir)
- bld_dir = os.path.normpath(bld_dir)
+ def helper_parse_dir(dir_entry: T.Dict[str, T.Any]) -> T.Tuple[Path, Path]:
+ src_dir = Path(dir_entry.get('source', '.'))
+ bld_dir = Path(dir_entry.get('build', '.'))
+ src_dir = src_dir if src_dir.is_absolute() else source_dir / src_dir
+ bld_dir = bld_dir if bld_dir.is_absolute() else build_dir / bld_dir
+ src_dir = src_dir.resolve()
+ bld_dir = bld_dir.resolve()
return src_dir, bld_dir
- def parse_sources(comp_group: T.Dict[str, T.Any], tgt: T.Dict[str, T.Any]) -> T.Tuple[T.List[str], T.List[str], T.List[int]]:
+ def parse_sources(comp_group: T.Dict[str, T.Any], tgt: T.Dict[str, T.Any]) -> T.Tuple[T.List[Path], T.List[Path], T.List[int]]:
gen = []
src = []
idx = []
@@ -120,9 +118,9 @@ class CMakeFileAPI:
if i >= len(src_list_raw) or 'path' not in src_list_raw[i]:
continue
if src_list_raw[i].get('isGenerated', False):
- gen += [src_list_raw[i]['path']]
+ gen += [Path(src_list_raw[i]['path'])]
else:
- src += [src_list_raw[i]['path']]
+ src += [Path(src_list_raw[i]['path'])]
idx += [i]
return src, gen, idx
@@ -133,8 +131,8 @@ class CMakeFileAPI:
# Parse install paths (if present)
install_paths = []
if 'install' in tgt:
- prefix = tgt['install']['prefix']['path']
- install_paths = [os.path.join(prefix, x['path']) for x in tgt['install']['destinations']]
+ prefix = Path(tgt['install']['prefix']['path'])
+ install_paths = [prefix / x['path'] for x in tgt['install']['destinations']]
install_paths = list(set(install_paths))
# On the first look, it looks really nice that the CMake devs have
@@ -160,7 +158,7 @@ class CMakeFileAPI:
# maybe we can make use of that in addition to the
# implicit dependency detection
tgt_data = {
- 'artifacts': [x.get('path', '') for x in tgt.get('artifacts', [])],
+ 'artifacts': [Path(x.get('path', '')) for x in tgt.get('artifacts', [])],
'sourceDirectory': src_dir,
'buildDirectory': bld_dir,
'name': tgt.get('name', ''),
@@ -269,14 +267,14 @@ class CMakeFileAPI:
self.cmake_configurations += [CMakeConfiguration(cnf_data)]
def _parse_cmakeFiles(self, data: T.Dict[str, T.Any]) -> None:
- assert('inputs' in data)
- assert('paths' in data)
+ assert 'inputs' in data
+ assert 'paths' in data
- src_dir = data['paths']['source']
+ src_dir = Path(data['paths']['source'])
for i in data['inputs']:
- path = i['path']
- path = path if os.path.isabs(path) else os.path.join(src_dir, path)
+ path = Path(i['path'])
+ path = path if path.is_absolute() else src_dir / path
self.cmake_sources += [CMakeBuildFile(path, i.get('isCMake', False), i.get('isGenerated', False))]
def _strip_data(self, data: T.Any) -> T.Any:
@@ -309,14 +307,13 @@ class CMakeFileAPI:
return data
- def _reply_file_content(self, filename: str) -> T.Dict[str, T.Any]:
- real_path = os.path.join(self.reply_dir, filename)
- if not os.path.exists(real_path):
+ def _reply_file_content(self, filename: Path) -> T.Dict[str, T.Any]:
+ real_path = self.reply_dir / filename
+ if not real_path.exists():
raise CMakeException('File "{}" does not exist'.format(real_path))
- with open(real_path, 'r') as fp:
- data = json.load(fp)
- assert isinstance(data, dict)
- for i in data.keys():
- assert isinstance(i, str)
- return data
+ data = json.loads(real_path.read_text())
+ assert isinstance(data, dict)
+ for i in data.keys():
+ assert isinstance(i, str)
+ return data
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 1a7fe7a..aa43ae4 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -21,14 +21,15 @@ from .fileapi import CMakeFileAPI
from .executor import CMakeExecutor
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib
-from ..mesonlib import MachineChoice, OrderedSet, version_compare
+from ..mesonlib import MachineChoice, OrderedSet, version_compare, path_is_in_root, relative_to_if_possible
from ..mesondata import mesondata
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
from enum import Enum
from functools import lru_cache
from pathlib import Path
import typing as T
-import os, re
+import re
+from os import environ
from ..mparser import (
Token,
@@ -52,7 +53,7 @@ if T.TYPE_CHECKING:
from ..backend.backends import Backend
from ..environment import Environment
-TYPE_mixed = T.Union[str, int, bool, BaseNode]
+TYPE_mixed = T.Union[str, int, bool, Path, BaseNode]
TYPE_mixed_list = T.Union[TYPE_mixed, T.Sequence[TYPE_mixed]]
TYPE_mixed_kwargs = T.Dict[str, TYPE_mixed_list]
@@ -141,7 +142,7 @@ def _sanitize_cmake_name(name: str) -> str:
class OutputTargetMap:
rm_so_version = re.compile(r'(\.[0-9]+)+$')
- def __init__(self, build_dir: str):
+ def __init__(self, build_dir: Path):
self.tgt_map = {} # type: T.Dict[str, T.Union['ConverterTarget', 'ConverterCustomTarget']]
self.build_dir = build_dir
@@ -186,37 +187,38 @@ class OutputTargetMap:
new_name = OutputTargetMap.rm_so_version.sub('', new_name)
candidates += ['{}.{}'.format(new_name, i)]
for i in candidates:
- keys += [self._rel_artifact_key(i), os.path.basename(i), self._base_artifact_key(i)]
+ keys += [self._rel_artifact_key(Path(i)), Path(i).name, self._base_artifact_key(Path(i))]
return self._return_first_valid_key(keys)
- def generated(self, name: str) -> T.Optional['ConverterCustomTarget']:
+ def generated(self, name: Path) -> T.Optional['ConverterCustomTarget']:
res = self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)])
assert res is None or isinstance(res, ConverterCustomTarget)
return res
# Utility functions to generate local keys
- def _rel_path(self, fname: str) -> T.Optional[str]:
- fname = os.path.normpath(os.path.join(self.build_dir, fname))
- if os.path.commonpath([self.build_dir, fname]) != self.build_dir:
- return None
- return os.path.relpath(fname, self.build_dir)
+ def _rel_path(self, fname: Path) -> T.Optional[Path]:
+ try:
+ return fname.resolve().relative_to(self.build_dir)
+ except ValueError:
+ pass
+ return None
def _target_key(self, tgt_name: str) -> str:
return '__tgt_{}__'.format(tgt_name)
- def _rel_generated_file_key(self, fname: str) -> T.Optional[str]:
+ def _rel_generated_file_key(self, fname: Path) -> T.Optional[str]:
path = self._rel_path(fname)
- return '__relgen_{}__'.format(path) if path else None
+ return '__relgen_{}__'.format(path.as_posix()) if path else None
- def _base_generated_file_key(self, fname: str) -> str:
- return '__gen_{}__'.format(os.path.basename(fname))
+ def _base_generated_file_key(self, fname: Path) -> str:
+ return '__gen_{}__'.format(fname.name)
- def _rel_artifact_key(self, fname: str) -> T.Optional[str]:
+ def _rel_artifact_key(self, fname: Path) -> T.Optional[str]:
path = self._rel_path(fname)
- return '__relart_{}__'.format(path) if path else None
+ return '__relart_{}__'.format(path.as_posix()) if path else None
- def _base_artifact_key(self, fname: str) -> str:
- return '__art_{}__'.format(os.path.basename(fname))
+ def _base_artifact_key(self, fname: Path) -> str:
+ return '__art_{}__'.format(fname.name)
class ConverterTarget:
def __init__(self, target: CMakeTarget, env: 'Environment') -> None:
@@ -229,7 +231,7 @@ class ConverterTarget:
self.full_name = target.full_name
self.type = target.type
self.install = target.install
- self.install_dir = ''
+ self.install_dir = None # type: T.Optional[Path]
self.link_libraries = target.link_libraries
self.link_flags = target.link_flags + target.link_lang_flags
self.depends_raw = [] # type: T.List[str]
@@ -239,11 +241,11 @@ class ConverterTarget:
self.install_dir = target.install_paths[0]
self.languages = [] # type: T.List[str]
- self.sources = [] # type: T.List[str]
- self.generated = [] # type: T.List[str]
+ self.sources = [] # type: T.List[Path]
+ self.generated = [] # type: T.List[Path]
self.generated_ctgt = [] # type: T.List[CustomTargetReference]
- self.includes = [] # type: T.List[str]
- self.sys_includes = [] # type: T.List[str]
+ self.includes = [] # type: T.List[Path]
+ self.sys_includes = [] # type: T.List[Path]
self.link_with = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
self.object_libs = [] # type: T.List[ConverterTarget]
self.compile_opts = {} # type: T.Dict[str, T.List[str]]
@@ -256,7 +258,7 @@ class ConverterTarget:
# Convert the target name to a valid meson target name
self.name = _sanitize_cmake_name(self.name)
- self.generated_raw = [] # type: T.List[str]
+ self.generated_raw = [] # type: T.List[Path]
for i in target.files:
# Determine the meson language
lang_cmake_to_meson = {val.lower(): key for key, val in language_map.items()}
@@ -286,7 +288,7 @@ class ConverterTarget:
std_regex = re.compile(r'([-]{1,2}std=|/std:v?|[-]{1,2}std:)(.*)')
- def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, install_prefix: str, trace: CMakeTraceParser) -> None:
+ def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: Path, subdir: Path, install_prefix: Path, trace: CMakeTraceParser) -> None:
# Detect setting the C and C++ standard and do additional compiler args manipulation
for i in ['c', 'cpp']:
if i not in self.compile_opts:
@@ -295,7 +297,7 @@ class ConverterTarget:
temp = []
for j in self.compile_opts[i]:
m = ConverterTarget.std_regex.match(j)
- ctgt = output_target_map.generated(j)
+ ctgt = output_target_map.generated(Path(j))
if m:
std = m.group(2)
supported = self._all_lang_stds(i)
@@ -314,7 +316,7 @@ class ConverterTarget:
# Sometimes projects pass generated source files as compiler
# flags. Add these as generated sources to ensure that the
# corresponding custom target is run.2
- self.generated_raw += [j]
+ self.generated_raw += [Path(j)]
temp += [j]
elif j in blacklist_compiler_flags:
pass
@@ -350,7 +352,7 @@ class ConverterTarget:
mlog.debug(str(tgt))
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties:
- self.includes += [x for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
+ self.includes += [Path(x) for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
if 'INTERFACE_LINK_OPTIONS' in tgt.properties:
self.link_flags += [x for x in tgt.properties['INTERFACE_LINK_OPTIONS'] if x]
@@ -402,7 +404,7 @@ class ConverterTarget:
for j in otherDeps:
if j in trace.targets:
to_process += [j]
- elif reg_is_lib.match(j) or os.path.exists(j):
+ elif reg_is_lib.match(j) or Path(j).exists():
libraries += [j]
for j in libraries:
@@ -418,7 +420,7 @@ class ConverterTarget:
# Let meson handle this arcane magic
if ',-rpath,' in i:
continue
- if not os.path.isabs(i):
+ if not Path(i).is_absolute():
link_with = output_target_map.artifact(i)
if link_with:
self.link_with += [link_with]
@@ -432,48 +434,45 @@ class ConverterTarget:
for i in self.languages:
supported += list(lang_suffixes[i])
supported = ['.{}'.format(x) for x in supported]
- self.sources = [x for x in self.sources if any([x.endswith(y) for y in supported])]
- self.generated_raw = [x for x in self.generated_raw if any([x.endswith(y) for y in supported])]
+ self.sources = [x for x in self.sources if any([x.name.endswith(y) for y in supported])]
+ self.generated_raw = [x for x in self.generated_raw if any([x.name.endswith(y) for y in supported])]
# Make paths relative
- def rel_path(x: str, is_header: bool, is_generated: bool) -> T.Optional[str]:
- if not os.path.isabs(x):
- x = os.path.join(self.src_dir, x)
- x = os.path.normpath(x)
- if not os.path.exists(x) and not any([x.endswith(y) for y in obj_suffixes]) and not is_generated:
+ def rel_path(x: Path, is_header: bool, is_generated: bool) -> T.Optional[Path]:
+ if not x.is_absolute():
+ x = self.src_dir / x
+ x = x.resolve()
+ assert x.is_absolute()
+ if not x.exists() and not any([x.name.endswith(y) for y in obj_suffixes]) and not is_generated:
if (
- any([os.path.commonpath([x, os.path.normpath(os.path.join(root_src_dir, y))]) == x for y in self.generated_raw])
- and os.path.commonpath([x, self.env.get_build_dir()]) == self.env.get_build_dir()
+ any([path_is_in_root(root_src_dir / y, x.resolve(), resolve=True) for y in self.generated_raw])
+ and path_is_in_root(x, Path(self.env.get_build_dir()), resolve=True)
):
- os.makedirs(x)
- return os.path.relpath(x, os.path.join(self.env.get_build_dir(), subdir))
+ x.mkdir(parents=True, exist_ok=True)
+ return x.relative_to(Path(self.env.get_build_dir()) / subdir)
else:
- mlog.warning('CMake: path', mlog.bold(x), 'does not exist.')
+ mlog.warning('CMake: path', mlog.bold(x.as_posix()), 'does not exist.')
mlog.warning(' --> Ignoring. This can lead to build errors.')
return None
- if Path(x) in trace.explicit_headers:
+ if x in trace.explicit_headers:
return None
if (
- os.path.isabs(x)
- and os.path.commonpath([x, self.env.get_source_dir()]) == self.env.get_source_dir()
+ path_is_in_root(x, Path(self.env.get_source_dir()))
and not (
- os.path.commonpath([x, root_src_dir]) == root_src_dir or
- os.path.commonpath([x, self.env.get_build_dir()]) == self.env.get_build_dir()
+ path_is_in_root(x, root_src_dir) or
+ path_is_in_root(x, Path(self.env.get_build_dir()))
)
):
- mlog.warning('CMake: path', mlog.bold(x), 'is inside the root project but', mlog.bold('not'), 'inside the subproject.')
+ mlog.warning('CMake: path', mlog.bold(x.as_posix()), 'is inside the root project but', mlog.bold('not'), 'inside the subproject.')
mlog.warning(' --> Ignoring. This can lead to build errors.')
return None
- if os.path.isabs(x) and os.path.commonpath([x, self.env.get_build_dir()]) == self.env.get_build_dir():
- if is_header:
- return os.path.relpath(x, os.path.join(self.env.get_build_dir(), subdir))
- else:
- return os.path.relpath(x, root_src_dir)
- if os.path.isabs(x) and os.path.commonpath([x, root_src_dir]) == root_src_dir:
- return os.path.relpath(x, root_src_dir)
+ if path_is_in_root(x, Path(self.env.get_build_dir())) and is_header:
+ return x.relative_to(Path(self.env.get_build_dir()) / subdir)
+ if path_is_in_root(x, root_src_dir):
+ return x.relative_to(root_src_dir)
return x
- build_dir_rel = os.path.relpath(self.build_dir, os.path.join(self.env.get_build_dir(), subdir))
+ build_dir_rel = self.build_dir.relative_to(Path(self.env.get_build_dir()) / subdir)
self.generated_raw = [rel_path(x, False, True) for x in self.generated_raw]
self.includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.includes)] + [build_dir_rel]))
self.sys_includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.sys_includes)]))
@@ -496,13 +495,13 @@ class ConverterTarget:
self.sources = [x for x in self.sources if x is not None]
# Make sure '.' is always in the include directories
- if '.' not in self.includes:
- self.includes += ['.']
+ if Path('.') not in self.includes:
+ self.includes += [Path('.')]
# make install dir relative to the install prefix
- if self.install_dir and os.path.isabs(self.install_dir):
- if os.path.commonpath([self.install_dir, install_prefix]) == install_prefix:
- self.install_dir = os.path.relpath(self.install_dir, install_prefix)
+ if self.install_dir and self.install_dir.is_absolute():
+ if path_is_in_root(self.install_dir, install_prefix):
+ self.install_dir = self.install_dir.relative_to(install_prefix)
# Remove blacklisted options and libs
def check_flag(flag: str) -> bool:
@@ -523,16 +522,13 @@ class ConverterTarget:
def process_object_libs(self, obj_target_list: T.List['ConverterTarget'], linker_workaround: bool) -> None:
# Try to detect the object library(s) from the generated input sources
- temp = [x for x in self.generated]
- temp = [os.path.basename(x) for x in temp]
- temp = [x for x in temp if any([x.endswith('.' + y) for y in obj_suffixes])]
- temp = [os.path.splitext(x)[0] for x in temp]
+ temp = [x for x in self.generated if any([x.name.endswith('.' + y) for y in obj_suffixes])]
+ stem = [x.stem for x in temp]
exts = self._all_source_suffixes()
# Temp now stores the source filenames of the object files
for i in obj_target_list:
- source_files = [x for x in i.sources + i.generated]
- source_files = [os.path.basename(x) for x in source_files]
- for j in temp:
+ source_files = [x.name for x in i.sources + i.generated]
+ for j in stem:
# On some platforms (specifically looking at you Windows with vs20xy backend) CMake does
# not produce object files with the format `foo.cpp.obj`, instead it skipps the language
# suffix and just produces object files like `foo.obj`. Thus we have to do our best to
@@ -552,7 +548,7 @@ class ConverterTarget:
break
# Filter out object files from the sources
- self.generated = [x for x in self.generated if not any([x.endswith('.' + y) for y in obj_suffixes])]
+ self.generated = [x for x in self.generated if not any([x.name.endswith('.' + y) for y in obj_suffixes])]
def _append_objlib_sources(self, tgt: 'ConverterTarget') -> None:
self.includes += tgt.includes
@@ -618,7 +614,7 @@ class ConverterTarget:
mlog.log(' -- full_name: ', mlog.bold(self.full_name))
mlog.log(' -- type: ', mlog.bold(self.type))
mlog.log(' -- install: ', mlog.bold('true' if self.install else 'false'))
- mlog.log(' -- install_dir: ', mlog.bold(self.install_dir))
+ mlog.log(' -- install_dir: ', mlog.bold(self.install_dir.as_posix() if self.install_dir else ''))
mlog.log(' -- link_libraries: ', mlog.bold(str(self.link_libraries)))
mlog.log(' -- link_with: ', mlog.bold(str(self.link_with)))
mlog.log(' -- object_libs: ', mlog.bold(str(self.object_libs)))
@@ -666,15 +662,15 @@ class ConverterCustomTarget:
ConverterCustomTarget.tgt_counter += 1
self.cmake_name = str(self.name)
self.original_outputs = list(target.outputs)
- self.outputs = [os.path.basename(x) for x in self.original_outputs]
- self.conflict_map = {} # type: T.Dict[str, str]
- self.command = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]]
+ self.outputs = [x.name for x in self.original_outputs]
+ self.conflict_map = {} # type: T.Dict[str, str]
+ self.command = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]]
self.working_dir = target.working_dir
self.depends_raw = target.depends
- self.inputs = [] # type: T.List[T.Union[str, CustomTargetReference]]
- self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
- self.current_bin_dir = Path(target.current_bin_dir)
- self.current_src_dir = Path(target.current_src_dir)
+ self.inputs = [] # type: T.List[T.Union[str, CustomTargetReference]]
+ self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
+ self.current_bin_dir = target.current_bin_dir # type: Path
+ self.current_src_dir = target.current_src_dir # type: Path
self._raw_target = target
# Convert the target name to a valid meson target name
@@ -683,15 +679,15 @@ class ConverterCustomTarget:
def __repr__(self) -> str:
return '<{}: {} {}>'.format(self.__class__.__name__, self.name, self.outputs)
- def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: str, subdir: str, all_outputs: T.List[str], trace: CMakeTraceParser) -> None:
+ def postprocess(self, output_target_map: OutputTargetMap, root_src_dir: Path, all_outputs: T.List[str], trace: CMakeTraceParser) -> None:
# Default the working directory to ${CMAKE_CURRENT_BINARY_DIR}
- if not self.working_dir:
- self.working_dir = self.current_bin_dir.as_posix()
+ if self.working_dir is None:
+ self.working_dir = self.current_bin_dir
# relative paths in the working directory are always relative
# to ${CMAKE_CURRENT_BINARY_DIR}
- if not os.path.isabs(self.working_dir):
- self.working_dir = (self.current_bin_dir / self.working_dir).as_posix()
+ if not self.working_dir.is_absolute():
+ self.working_dir = self.current_bin_dir / self.working_dir
# Modify the original outputs if they are relative. Again,
# relative paths are relative to ${CMAKE_CURRENT_BINARY_DIR}
@@ -700,7 +696,7 @@ class ConverterCustomTarget:
return x
else:
return self.current_bin_dir / x
- self.original_outputs = [ensure_absolute(Path(x)).as_posix() for x in self.original_outputs]
+ self.original_outputs = [ensure_absolute(x) for x in self.original_outputs]
# Ensure that there is no duplicate output in the project so
# that meson can handle cases where the same filename is
@@ -748,18 +744,17 @@ class ConverterCustomTarget:
self.outputs = [self.name + '.h']
# Check dependencies and input files
- root = Path(root_src_dir)
for i in self.depends_raw:
if not i:
continue
raw = Path(i)
art = output_target_map.artifact(i)
tgt = output_target_map.target(i)
- gen = output_target_map.generated(i)
+ gen = output_target_map.generated(raw)
rel_to_root = None
try:
- rel_to_root = raw.relative_to(root)
+ rel_to_root = raw.relative_to(root_src_dir)
except ValueError:
rel_to_root = None
@@ -768,7 +763,7 @@ class ConverterCustomTarget:
# as outputs from other targets.
# See https://github.com/mesonbuild/meson/issues/6632
if not raw.is_absolute() and (self.current_src_dir / raw).exists():
- self.inputs += [(self.current_src_dir / raw).relative_to(root).as_posix()]
+ self.inputs += [(self.current_src_dir / raw).relative_to(root_src_dir).as_posix()]
elif raw.is_absolute() and raw.exists() and rel_to_root is not None:
self.inputs += [rel_to_root.as_posix()]
elif art:
@@ -776,7 +771,7 @@ class ConverterCustomTarget:
elif tgt:
self.depends += [tgt]
elif gen:
- ctgt_ref = gen.get_ref(i)
+ ctgt_ref = gen.get_ref(raw)
assert ctgt_ref is not None
self.inputs += [ctgt_ref]
@@ -793,12 +788,12 @@ class ConverterCustomTarget:
new_deps += [i]
self.depends = list(OrderedSet(new_deps))
- def get_ref(self, fname: str) -> T.Optional[CustomTargetReference]:
- fname = os.path.basename(fname)
+ def get_ref(self, fname: Path) -> T.Optional[CustomTargetReference]:
+ name = fname.name
try:
- if fname in self.conflict_map:
- fname = self.conflict_map[fname]
- idx = self.outputs.index(fname)
+ if name in self.conflict_map:
+ name = self.conflict_map[name]
+ idx = self.outputs.index(name)
return CustomTargetReference(self, idx)
except ValueError:
return None
@@ -818,23 +813,22 @@ class CMakeAPI(Enum):
FILE = 2
class CMakeInterpreter:
- def __init__(self, build: 'Build', subdir: str, src_dir: str, install_prefix: str, env: 'Environment', backend: 'Backend'):
- assert(hasattr(backend, 'name'))
- self.build = build
- self.subdir = subdir
- self.src_dir = src_dir
- self.build_dir_rel = os.path.join(subdir, '__CMake_build')
- self.build_dir = os.path.join(env.get_build_dir(), self.build_dir_rel)
+ def __init__(self, build: 'Build', subdir: Path, src_dir: Path, install_prefix: Path, env: 'Environment', backend: 'Backend'):
+ self.build = build
+ self.subdir = subdir
+ self.src_dir = src_dir
+ self.build_dir_rel = subdir / '__CMake_build'
+ self.build_dir = Path(env.get_build_dir()) / self.build_dir_rel
self.install_prefix = install_prefix
- self.env = env
- self.backend_name = backend.name
- self.linkers = set() # type: T.Set[str]
- self.cmake_api = CMakeAPI.SERVER
- self.client = CMakeClient(self.env)
- self.fileapi = CMakeFileAPI(self.build_dir)
+ self.env = env
+ self.backend_name = backend.name
+ self.linkers = set() # type: T.Set[str]
+ self.cmake_api = CMakeAPI.SERVER
+ self.client = CMakeClient(self.env)
+ self.fileapi = CMakeFileAPI(self.build_dir)
# Raw CMake results
- self.bs_files = [] # type: T.List[str]
+ self.bs_files = [] # type: T.List[Path]
self.codemodel_configs = None # type: T.Optional[T.List[CMakeConfiguration]]
self.raw_trace = None # type: T.Optional[str]
@@ -843,7 +837,7 @@ class CMakeInterpreter:
self.languages = [] # type: T.List[str]
self.targets = [] # type: T.List[ConverterTarget]
self.custom_targets = [] # type: T.List[ConverterCustomTarget]
- self.trace = CMakeTraceParser('', '') # Will be replaced in analyse
+ self.trace = CMakeTraceParser('', Path('.')) # Will be replaced in analyse
self.output_target_map = OutputTargetMap(self.build_dir)
# Generated meson data
@@ -899,16 +893,16 @@ class CMakeInterpreter:
with mlog.nested():
mlog.log('Configuring the build directory with', mlog.bold('CMake'), 'version', mlog.cyan(cmake_exe.version()))
mlog.log(mlog.bold('Running:'), ' '.join(cmake_args))
- mlog.log(mlog.bold(' - build directory: '), self.build_dir)
- mlog.log(mlog.bold(' - source directory: '), self.src_dir)
+ mlog.log(mlog.bold(' - build directory: '), self.build_dir.as_posix())
+ mlog.log(mlog.bold(' - source directory: '), self.src_dir.as_posix())
mlog.log(mlog.bold(' - trace args: '), ' '.join(trace_args))
- mlog.log(mlog.bold(' - preload file: '), str(preload_file))
+ mlog.log(mlog.bold(' - preload file: '), preload_file.as_posix())
mlog.log(mlog.bold(' - disabled policy warnings:'), '[{}]'.format(', '.join(disable_policy_warnings)))
mlog.log()
- os.makedirs(self.build_dir, exist_ok=True)
- os_env = os.environ.copy()
+ self.build_dir.mkdir(parents=True, exist_ok=True)
+ os_env = environ.copy()
os_env['LC_ALL'] = 'C'
- final_args = cmake_args + trace_args + cmcmp_args + pload_args + [self.src_dir]
+ final_args = cmake_args + trace_args + cmcmp_args + pload_args + [self.src_dir.as_posix()]
cmake_exe.set_exec_mode(print_cmout=True, always_capture_stderr=self.trace.requires_stderr())
rc, _, self.raw_trace = cmake_exe.call(final_args, self.build_dir, env=os_env, disable_cache=True)
@@ -933,7 +927,7 @@ class CMakeInterpreter:
# Load the buildsystem file list
cmake_files = self.fileapi.get_cmake_sources()
self.bs_files = [x.file for x in cmake_files if not x.is_cmake and not x.is_temp]
- self.bs_files = [os.path.relpath(x, self.env.get_source_dir()) for x in self.bs_files]
+ self.bs_files = [relative_to_if_possible(x, Path(self.env.get_source_dir())) for x in self.bs_files]
self.bs_files = list(OrderedSet(self.bs_files))
# Load the codemodel configurations
@@ -960,7 +954,7 @@ class CMakeInterpreter:
src_dir = bs_reply.src_dir
self.bs_files = [x.file for x in bs_reply.build_files if not x.is_cmake and not x.is_temp]
- self.bs_files = [os.path.relpath(os.path.join(src_dir, x), self.env.get_source_dir()) for x in self.bs_files]
+ self.bs_files = [relative_to_if_possible(src_dir / x, Path(self.env.get_source_dir()), resolve=True) for x in self.bs_files]
self.bs_files = list(OrderedSet(self.bs_files))
self.codemodel_configs = cm_reply.configs
@@ -1017,7 +1011,7 @@ class CMakeInterpreter:
object_libs = []
custom_target_outputs = [] # type: T.List[str]
for ctgt in self.custom_targets:
- ctgt.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace)
+ ctgt.postprocess(self.output_target_map, self.src_dir, custom_target_outputs, self.trace)
for tgt in self.targets:
tgt.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace)
if tgt.type == 'OBJECT_LIBRARY':
@@ -1045,7 +1039,7 @@ class CMakeInterpreter:
raise CMakeException('CMakeInterpreter was not analysed')
def token(tid: str = 'string', val: TYPE_mixed = '') -> Token:
- return Token(tid, self.subdir, 0, 0, 0, None, val)
+ return Token(tid, self.subdir.as_posix(), 0, 0, 0, None, val)
def string(value: str) -> StringNode:
return StringNode(token(val=value))
@@ -1059,6 +1053,8 @@ class CMakeInterpreter:
def nodeify(value: TYPE_mixed_list) -> BaseNode:
if isinstance(value, str):
return string(value)
+ if isinstance(value, Path):
+ return string(value.as_posix())
elif isinstance(value, bool):
return BooleanNode(token(val=value))
elif isinstance(value, int):
@@ -1084,11 +1080,11 @@ class CMakeInterpreter:
kwargs = {} if kwargs is None else kwargs
args_n = ArgumentNode(token())
if not isinstance(args, list):
- assert isinstance(args, (str, int, bool, BaseNode))
+ assert isinstance(args, (str, int, bool, Path, BaseNode))
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
- func_n = FunctionNode(self.subdir, 0, 0, 0, 0, name, args_n)
+ func_n = FunctionNode(self.subdir.as_posix(), 0, 0, 0, 0, name, args_n)
return func_n
def method(obj: BaseNode, name: str, args: T.Optional[TYPE_mixed_list] = None, kwargs: T.Optional[TYPE_mixed_kwargs] = None) -> MethodNode:
@@ -1096,14 +1092,14 @@ class CMakeInterpreter:
kwargs = {} if kwargs is None else kwargs
args_n = ArgumentNode(token())
if not isinstance(args, list):
- assert isinstance(args, (str, int, bool, BaseNode))
+ assert isinstance(args, (str, int, bool, Path, BaseNode))
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
- return MethodNode(self.subdir, 0, 0, obj, name, args_n)
+ return MethodNode(self.subdir.as_posix(), 0, 0, obj, name, args_n)
def assign(var_name: str, value: BaseNode) -> AssignmentNode:
- return AssignmentNode(self.subdir, 0, 0, var_name, value)
+ return AssignmentNode(self.subdir.as_posix(), 0, 0, var_name, value)
# Generate the root code block and the project function call
root_cb = CodeBlockNode(token())
@@ -1144,7 +1140,7 @@ class CMakeInterpreter:
# First handle inter target dependencies
link_with = [] # type: T.List[IdNode]
objec_libs = [] # type: T.List[IdNode]
- sources = [] # type: T.List[str]
+ sources = [] # type: T.List[Path]
generated = [] # type: T.List[T.Union[IdNode, IndexNode]]
generated_filenames = [] # type: T.List[str]
custom_targets = [] # type: T.List[ConverterCustomTarget]
@@ -1190,7 +1186,7 @@ class CMakeInterpreter:
if not is_header(j) or j in generated_filenames:
continue
- generated += [resolve_ctgt_ref(ctgt.get_ref(j))]
+ generated += [resolve_ctgt_ref(ctgt.get_ref(Path(j)))]
generated_filenames += [j]
# Determine the meson function to use for the build target
@@ -1305,8 +1301,8 @@ class CMakeInterpreter:
command += ['--internal', 'cmake_run_ctgt']
command += ['-o', '@OUTPUT@']
if tgt.original_outputs:
- command += ['-O'] + tgt.original_outputs
- command += ['-d', tgt.working_dir]
+ command += ['-O'] + [x.as_posix() for x in tgt.original_outputs]
+ command += ['-d', tgt.working_dir.as_posix()]
# Generate the commands. Subcommands are separated by ';;;'
for cmd in tgt.command:
diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py
index 5c41196..bee011c 100644
--- a/mesonbuild/cmake/traceparser.py
+++ b/mesonbuild/cmake/traceparser.py
@@ -23,12 +23,11 @@ from ..mesonlib import version_compare
import typing as T
from pathlib import Path
import re
-import os
import json
import textwrap
class CMakeTraceLine:
- def __init__(self, file: str, line: int, func: str, args: T.List[str]) -> None:
+ def __init__(self, file: Path, line: int, func: str, args: T.List[str]) -> None:
self.file = file
self.line = line
self.func = func.lower()
@@ -55,8 +54,8 @@ class CMakeTarget:
self.imported = imported
self.tline = tline
self.depends = [] # type: T.List[str]
- self.current_bin_dir = None # type: T.Optional[str]
- self.current_src_dir = None # type: T.Optional[str]
+ self.current_bin_dir = None # type: T.Optional[Path]
+ self.current_src_dir = None # type: T.Optional[Path]
def __repr__(self) -> str:
s = 'CMake TARGET:\n -- name: {}\n -- type: {}\n -- imported: {}\n -- properties: {{\n{} }}\n -- tline: {}'
@@ -76,12 +75,12 @@ class CMakeTarget:
class CMakeGeneratorTarget(CMakeTarget):
def __init__(self, name: str) -> None:
super().__init__(name, 'CUSTOM', {})
- self.outputs = [] # type: T.List[str]
+ self.outputs = [] # type: T.List[Path]
self.command = [] # type: T.List[T.List[str]]
- self.working_dir = None # type: T.Optional[str]
+ self.working_dir = None # type: T.Optional[Path]
class CMakeTraceParser:
- def __init__(self, cmake_version: str, build_dir: str, permissive: bool = True) -> None:
+ def __init__(self, cmake_version: str, build_dir: Path, permissive: bool = True) -> None:
self.vars = {} # type: T.Dict[str, T.List[str]]
self.targets = {} # type: T.Dict[str, CMakeTarget]
@@ -93,7 +92,7 @@ class CMakeTraceParser:
self.permissive = permissive # type: bool
self.cmake_version = cmake_version # type: str
self.trace_file = 'cmake_trace.txt'
- self.trace_file_path = Path(build_dir) / self.trace_file
+ self.trace_file_path = build_dir / self.trace_file
self.trace_format = 'json-v1' if version_compare(cmake_version, '>=3.17') else 'human'
# State for delayed command execution. Delayed command execution is realised
@@ -339,7 +338,7 @@ class CMakeTraceParser:
target = CMakeGeneratorTarget(name)
def handle_output(key: str, target: CMakeGeneratorTarget) -> None:
- target.outputs += [key]
+ target.outputs += [Path(key)]
def handle_command(key: str, target: CMakeGeneratorTarget) -> None:
if key == 'ARGS':
@@ -349,12 +348,14 @@ class CMakeTraceParser:
def handle_depends(key: str, target: CMakeGeneratorTarget) -> None:
target.depends += [key]
+ working_dir = None
def handle_working_dir(key: str, target: CMakeGeneratorTarget) -> None:
- if target.working_dir is None:
- target.working_dir = key
+ nonlocal working_dir
+ if working_dir is None:
+ working_dir = key
else:
- target.working_dir += ' '
- target.working_dir += key
+ working_dir += ' '
+ working_dir += key
fn = None
@@ -376,9 +377,13 @@ class CMakeTraceParser:
if fn is not None:
fn(i, target)
- target.current_bin_dir = self.var_to_str('MESON_PS_CMAKE_CURRENT_BINARY_DIR')
- target.current_src_dir = self.var_to_str('MESON_PS_CMAKE_CURRENT_SOURCE_DIR')
- target.outputs = self._guess_files(target.outputs)
+ cbinary_dir = self.var_to_str('MESON_PS_CMAKE_CURRENT_BINARY_DIR')
+ csource_dir = self.var_to_str('MESON_PS_CMAKE_CURRENT_SOURCE_DIR')
+
+ target.working_dir = Path(working_dir) if working_dir else None
+ target.current_bin_dir = Path(cbinary_dir) if cbinary_dir else None
+ target.current_src_dir = Path(csource_dir) if csource_dir else None
+ target.outputs = [Path(x) for x in self._guess_files([str(y) for y in target.outputs])]
target.depends = self._guess_files(target.depends)
target.command = [self._guess_files(x) for x in target.command]
@@ -639,7 +644,7 @@ class CMakeTraceParser:
argl = args.split(' ')
argl = list(map(lambda x: x.strip(), argl))
- yield CMakeTraceLine(file, int(line), func, argl)
+ yield CMakeTraceLine(Path(file), int(line), func, argl)
def _lex_trace_json(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]:
lines = trace.splitlines(keepends=False)
@@ -654,7 +659,7 @@ class CMakeTraceParser:
for j in args:
assert isinstance(j, str)
args = [parse_generator_expressions(x) for x in args]
- yield CMakeTraceLine(data['file'], data['line'], data['cmd'], args)
+ yield CMakeTraceLine(Path(data['file']), data['line'], data['cmd'], args)
def _flatten_args(self, args: T.List[str]) -> T.List[str]:
# Split lists in arguments
@@ -681,7 +686,7 @@ class CMakeTraceParser:
if curr_str is None:
curr_str = i
path_found = False
- elif os.path.isfile(curr_str):
+ elif Path(curr_str).is_file():
# Abort concatenation if curr_str is an existing file
fixed_list += [curr_str]
curr_str = i
@@ -697,7 +702,7 @@ class CMakeTraceParser:
fixed_list += [curr_str]
curr_str = None
path_found = False
- elif os.path.exists('{} {}'.format(curr_str, i)):
+ elif Path('{} {}'.format(curr_str, i)).exists():
# Path detected
curr_str = '{} {}'.format(curr_str, i)
path_found = True
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index 7b2f2d9..e622859 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -1528,12 +1528,12 @@ class CMakeDependency(ExternalDependency):
self.compile_args = compileOptions + compileDefinitions + ['-I{}'.format(x) for x in incDirs]
self.link_args = libraries
- def _get_build_dir(self) -> str:
+ def _get_build_dir(self) -> Path:
build_dir = Path(self.cmake_root_dir) / 'cmake_{}'.format(self.name)
build_dir.mkdir(parents=True, exist_ok=True)
- return str(build_dir)
+ return build_dir
- def _setup_cmake_dir(self, cmake_file: str) -> str:
+ def _setup_cmake_dir(self, cmake_file: str) -> Path:
# Setup the CMake build environment and return the "build" directory
build_dir = self._get_build_dir()
@@ -1557,7 +1557,7 @@ cmake_minimum_required(VERSION ${{CMAKE_VERSION}})
project(MesonTemp LANGUAGES {})
""".format(' '.join(cmake_language)) + cmake_txt
- cm_file = Path(build_dir) / 'CMakeLists.txt'
+ cm_file = build_dir / 'CMakeLists.txt'
cm_file.write_text(cmake_txt)
mlog.cmd_ci_include(cm_file.absolute().as_posix())
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index c56eff5..eb67a45 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -2983,7 +2983,7 @@ external dependencies (including libraries) must go to "dependencies".''')
cmake_options = mesonlib.stringlistify(kwargs.get('cmake_options', []))
cmake_options += options.cmake_options
- cm_int = CMakeInterpreter(new_build, subdir, subdir_abs, prefix, new_build.environment, self.backend)
+ cm_int = CMakeInterpreter(new_build, Path(subdir), Path(subdir_abs), Path(prefix), new_build.environment, self.backend)
cm_int.initialise(cmake_options)
cm_int.analyse()
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index f297771..0b614ba 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -1570,6 +1570,15 @@ def path_is_in_root(path: Path, root: Path, resolve: bool = False) -> bool:
return False
return True
+def relative_to_if_possible(path: Path, root: Path, resolve: bool = False) -> Path:
+ try:
+ if resolve:
+ return path.resolve().relative_to(root.resolve())
+ else:
+ return path.relative_to(root)
+ except ValueError:
+ return path
+
class LibType(IntEnum):
"""Enumeration for library types."""
diff --git a/test cases/cmake/2 advanced/test.json b/test cases/cmake/2 advanced/test.json
index e12f530..ff3d5a7 100644
--- a/test cases/cmake/2 advanced/test.json
+++ b/test cases/cmake/2 advanced/test.json
@@ -1,8 +1,7 @@
{
"installed": [
{"type": "expr", "file": "usr/?lib/libcm_cmModLib?so"},
- {"type": "implib", "platform": "cygwin", "file": "usr/lib/libcm_cmModLib"},
- {"type": "implib", "platform": "!cygwin", "file": "usr/bin/libcm_cmModLib"},
+ {"type": "implib", "file": "usr/lib/libcm_cmModLib"},
{"type": "exe", "file": "usr/bin/cm_testEXE"}
],
"tools": {
diff --git a/test cases/cmake/3 advanced no dep/test.json b/test cases/cmake/3 advanced no dep/test.json
index 98a1719..af25a8e 100644
--- a/test cases/cmake/3 advanced no dep/test.json
+++ b/test cases/cmake/3 advanced no dep/test.json
@@ -1,8 +1,7 @@
{
"installed": [
{"type": "expr", "file": "usr/?lib/libcm_cmModLib?so"},
- {"type": "implib", "platform": "cygwin", "file": "usr/lib/libcm_cmModLib"},
- {"type": "implib", "platform": "!cygwin", "file": "usr/bin/libcm_cmModLib"},
+ {"type": "implib", "file": "usr/lib/libcm_cmModLib"},
{"type": "pdb", "file": "usr/bin/cm_cmModLib"},
{"type": "pdb", "file": "usr/bin/cm_testEXE"},
{"type": "exe", "file": "usr/bin/cm_testEXE"},