aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild/backend/backends.py
diff options
context:
space:
mode:
Diffstat (limited to 'mesonbuild/backend/backends.py')
-rw-r--r--mesonbuild/backend/backends.py272
1 files changed, 131 insertions, 141 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 4b368e7..e43c8c7 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -13,6 +13,7 @@
# limitations under the License.
from collections import OrderedDict
+from dataclasses import dataclass, InitVar
from functools import lru_cache
from itertools import chain
from pathlib import Path
@@ -61,11 +62,11 @@ if T.TYPE_CHECKING:
# Assembly files cannot be unitified and neither can LLVM IR files
LANGS_CANT_UNITY = ('d', 'fortran', 'vala')
+@dataclass(eq=False)
class RegenInfo:
- def __init__(self, source_dir: str, build_dir: str, depfiles: T.List[str]):
- self.source_dir = source_dir
- self.build_dir = build_dir
- self.depfiles = depfiles
+ source_dir: str
+ build_dir: str
+ depfiles: T.List[str]
class TestProtocol(enum.Enum):
@@ -97,28 +98,30 @@ class TestProtocol(enum.Enum):
return 'tap'
+@dataclass(eq=False)
class CleanTrees:
'''
Directories outputted by custom targets that have to be manually cleaned
because on Linux `ninja clean` only deletes empty directories.
'''
- def __init__(self, build_dir: str, trees: T.List[str]):
- self.build_dir = build_dir
- self.trees = trees
+ build_dir: str
+ trees: T.List[str]
+@dataclass(eq=False)
class InstallData:
- def __init__(self, source_dir: str, build_dir: str, prefix: str, libdir: str,
- strip_bin: T.List[str], install_umask: T.Union[str, int],
- mesonintrospect: T.List[str], version: str,
- is_cross_build: bool):
- # TODO: in python 3.8 or with typing_Extensions install_umask could be:
- # `T.Union[T.Literal['preserve'], int]`, which would be more accurate.
- self.source_dir = source_dir
- self.build_dir = build_dir
- self.prefix = prefix
- self.libdir = libdir
- self.strip_bin = strip_bin
- self.install_umask = install_umask
+ source_dir: str
+ build_dir: str
+ prefix: str
+ libdir: str
+ strip_bin: T.List[str]
+ # TODO: in python 3.8 or with typing_Extensions this could be:
+ # `T.Union[T.Literal['preserve'], int]`, which would be more accurate.
+ install_umask: T.Union[str, int]
+ mesonintrospect: T.List[str]
+ version: str
+ is_cross_build: bool
+
+ def __post_init__(self) -> None:
self.targets: T.List[TargetInstallData] = []
self.headers: T.List[InstallDataBase] = []
self.man: T.List[InstallDataBase] = []
@@ -127,59 +130,52 @@ class InstallData:
self.symlinks: T.List[InstallSymlinkData] = []
self.install_scripts: T.List[ExecutableSerialisation] = []
self.install_subdirs: T.List[SubdirInstallData] = []
- self.mesonintrospect = mesonintrospect
- self.version = version
- self.is_cross_build = is_cross_build
+@dataclass(eq=False)
class TargetInstallData:
-
+ fname: str
+ outdir: str
+ outdir_name: InitVar[str]
+ aliases: T.Dict[str, str]
+ strip: bool
+ install_name_mappings: T.Mapping[str, str]
+ rpath_dirs_to_remove: T.Set[bytes]
+ install_rpath: str
# TODO: install_mode should just always be a FileMode object
+ install_mode: T.Optional['FileMode']
+ subproject: str
+ optional: bool = False
+ tag: T.Optional[str] = None
- def __init__(self, fname: str, outdir: str, outdir_name: str, aliases: T.Dict[str, str],
- strip: bool, install_name_mappings: T.Mapping[str, str], rpath_dirs_to_remove: T.Set[bytes],
- install_rpath: str, install_mode: T.Optional['FileMode'],
- subproject: str, optional: bool = False, tag: T.Optional[str] = None):
- self.fname = fname
- self.outdir = outdir
- self.out_name = os.path.join(outdir_name, os.path.basename(fname))
- self.aliases = aliases
- self.strip = strip
- self.install_name_mappings = install_name_mappings
- self.rpath_dirs_to_remove = rpath_dirs_to_remove
- self.install_rpath = install_rpath
- self.install_mode = install_mode
- self.subproject = subproject
- self.optional = optional
- self.tag = tag
+ def __post_init__(self, outdir_name: str) -> None:
+ self.out_name = os.path.join(outdir_name, os.path.basename(self.fname))
+@dataclass(eq=False)
class InstallEmptyDir:
- def __init__(self, path: str, install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None):
- self.path = path
- self.install_mode = install_mode
- self.subproject = subproject
- self.tag = tag
+ path: str
+ install_mode: 'FileMode'
+ subproject: str
+ tag: T.Optional[str] = None
+@dataclass(eq=False)
class InstallDataBase:
- def __init__(self, path: str, install_path: str, install_path_name: str,
- install_mode: 'FileMode', subproject: str, tag: T.Optional[str] = None,
- data_type: T.Optional[str] = None):
- self.path = path
- self.install_path = install_path
- self.install_path_name = install_path_name
- self.install_mode = install_mode
- self.subproject = subproject
- self.tag = tag
- self.data_type = data_type
-
+ path: str
+ install_path: str
+ install_path_name: str
+ install_mode: 'FileMode'
+ subproject: str
+ tag: T.Optional[str] = None
+ data_type: T.Optional[str] = None
+
+@dataclass(eq=False)
class InstallSymlinkData:
- def __init__(self, target: str, name: str, install_path: str,
- subproject: str, tag: T.Optional[str] = None):
- self.target = target
- self.name = name
- self.install_path = install_path
- self.subproject = subproject
- self.tag = tag
+ target: str
+ name: str
+ install_path: str
+ subproject: str
+ tag: T.Optional[str] = None
+# cannot use dataclass here because "exclude" is out of order
class SubdirInstallData(InstallDataBase):
def __init__(self, path: str, install_path: str, install_path_name: str,
install_mode: 'FileMode', exclude: T.Tuple[T.Set[str], T.Set[str]],
@@ -187,64 +183,53 @@ class SubdirInstallData(InstallDataBase):
super().__init__(path, install_path, install_path_name, install_mode, subproject, tag, data_type)
self.exclude = exclude
+@dataclass(eq=False)
class ExecutableSerialisation:
# XXX: should capture and feed default to False, instead of None?
- def __init__(self, cmd_args: T.List[str],
- env: T.Optional[build.EnvironmentVariables] = None,
- exe_wrapper: T.Optional['programs.ExternalProgram'] = None,
- workdir: T.Optional[str] = None,
- extra_paths: T.Optional[T.List] = None,
- capture: T.Optional[bool] = None,
- feed: T.Optional[bool] = None,
- tag: T.Optional[str] = None,
- verbose: bool = False,
- ) -> None:
- self.cmd_args = cmd_args
- self.env = env
- if exe_wrapper is not None:
- assert isinstance(exe_wrapper, programs.ExternalProgram)
- self.exe_wrapper = exe_wrapper
- self.workdir = workdir
- self.extra_paths = extra_paths
- self.capture = capture
- self.feed = feed
+ cmd_args: T.List[str]
+ env: T.Optional[build.EnvironmentVariables] = None
+ exe_wrapper: T.Optional['programs.ExternalProgram'] = None
+ workdir: T.Optional[str] = None
+ extra_paths: T.Optional[T.List] = None
+ capture: T.Optional[bool] = None
+ feed: T.Optional[bool] = None
+ tag: T.Optional[str] = None
+ verbose: bool = False
+
+ def __post_init__(self) -> None:
+ if self.exe_wrapper is not None:
+ assert isinstance(self.exe_wrapper, programs.ExternalProgram)
self.pickled = False
self.skip_if_destdir = False
- self.verbose = verbose
self.subproject = ''
- self.tag = tag
+@dataclass(eq=False)
class TestSerialisation:
- def __init__(self, name: str, project: str, suite: T.List[str], fname: T.List[str],
- is_cross_built: bool, exe_wrapper: T.Optional[programs.ExternalProgram],
- needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str],
- env: build.EnvironmentVariables, should_fail: bool,
- timeout: T.Optional[int], workdir: T.Optional[str],
- extra_paths: T.List[str], protocol: TestProtocol, priority: int,
- cmd_is_built: bool, depends: T.List[str], version: str):
- self.name = name
- self.project_name = project
- self.suite = suite
- self.fname = fname
- self.is_cross_built = is_cross_built
- if exe_wrapper is not None:
- assert isinstance(exe_wrapper, programs.ExternalProgram)
- self.exe_wrapper = exe_wrapper
- self.is_parallel = is_parallel
- self.cmd_args = cmd_args
- self.env = env
- self.should_fail = should_fail
- self.timeout = timeout
- self.workdir = workdir
- self.extra_paths = extra_paths
- self.protocol = protocol
- self.priority = priority
- self.needs_exe_wrapper = needs_exe_wrapper
- self.cmd_is_built = cmd_is_built
- self.depends = depends
- self.version = version
+ name: str
+ project_name: str
+ suite: T.List[str]
+ fname: T.List[str]
+ is_cross_built: bool
+ exe_wrapper: T.Optional[programs.ExternalProgram]
+ needs_exe_wrapper: bool
+ is_parallel: bool
+ cmd_args: T.List[str]
+ env: build.EnvironmentVariables
+ should_fail: bool
+ timeout: T.Optional[int]
+ workdir: T.Optional[str]
+ extra_paths: T.List[str]
+ protocol: TestProtocol
+ priority: int
+ cmd_is_built: bool
+ depends: T.List[str]
+ version: str
+
+ def __post_init__(self) -> None:
+ if self.exe_wrapper is not None:
+ assert isinstance(self.exe_wrapper, programs.ExternalProgram)
def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']:
@@ -708,7 +693,6 @@ class Backend:
return False
def get_external_rpath_dirs(self, target: build.BuildTarget) -> T.Set[str]:
- dirs: T.Set[str] = set()
args: T.List[str] = []
for lang in LANGUAGES_USING_LDFLAGS:
try:
@@ -719,6 +703,11 @@ class Backend:
args.extend(e)
except Exception:
pass
+ return self.get_rpath_dirs_from_link_args(args)
+
+ @staticmethod
+ def get_rpath_dirs_from_link_args(args: T.List[str]) -> T.Set[str]:
+ dirs: T.Set[str] = set()
# Match rpath formats:
# -Wl,-rpath=
# -Wl,-rpath,
@@ -751,37 +740,38 @@ class Backend:
return dirs
@lru_cache(maxsize=None)
- def rpaths_for_bundled_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]':
- paths: T.List[str] = []
+ def rpaths_for_non_system_absolute_shared_libraries(self, target: build.BuildTarget, exclude_system: bool = True) -> 'ImmutableListProtocol[str]':
+ paths: OrderedSet[str] = OrderedSet()
for dep in target.external_deps:
if not isinstance(dep, (dependencies.ExternalLibrary, dependencies.PkgConfigDependency)):
continue
- la = dep.link_args
- if len(la) != 1 or not os.path.isabs(la[0]):
- continue
- # The only link argument is an absolute path to a library file.
- libpath = la[0]
- libdir = os.path.dirname(libpath)
- if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment):
- # No point in adding system paths.
- continue
- # Don't remove rpaths specified in LDFLAGS.
- if libdir in self.get_external_rpath_dirs(target):
- continue
- # Windows doesn't support rpaths, but we use this function to
- # emulate rpaths by setting PATH, so also accept DLLs here
- if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']:
- continue
- if libdir.startswith(self.environment.get_source_dir()):
- rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:]
- assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute'
- paths.append(os.path.join(self.build_to_src, rel_to_src))
- else:
- paths.append(libdir)
+ for libpath in dep.link_args:
+ # For all link args that are absolute paths to a library file, add RPATH args
+ if not os.path.isabs(libpath):
+ continue
+ libdir = os.path.dirname(libpath)
+ if exclude_system and self._libdir_is_system(libdir, target.compilers, self.environment):
+ # No point in adding system paths.
+ continue
+ # Don't remove rpaths specified in LDFLAGS.
+ if libdir in self.get_external_rpath_dirs(target):
+ continue
+ # Windows doesn't support rpaths, but we use this function to
+ # emulate rpaths by setting PATH, so also accept DLLs here
+ if os.path.splitext(libpath)[1] not in ['.dll', '.lib', '.so', '.dylib']:
+ continue
+ if libdir.startswith(self.environment.get_source_dir()):
+ rel_to_src = libdir[len(self.environment.get_source_dir()) + 1:]
+ assert not os.path.isabs(rel_to_src), f'rel_to_src: {rel_to_src} is absolute'
+ paths.add(os.path.join(self.build_to_src, rel_to_src))
+ else:
+ paths.add(libdir)
+ # Don't remove rpaths specified by the dependency
+ paths.difference_update(self.get_rpath_dirs_from_link_args(dep.link_args))
for i in chain(target.link_targets, target.link_whole_targets):
if isinstance(i, build.BuildTarget):
- paths.extend(self.rpaths_for_bundled_shared_libraries(i, exclude_system))
- return paths
+ paths.update(self.rpaths_for_non_system_absolute_shared_libraries(i, exclude_system))
+ return list(paths)
# This may take other types
def determine_rpath_dirs(self, target: T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]
@@ -794,7 +784,7 @@ class Backend:
result = OrderedSet()
result.add('meson-out')
if isinstance(target, build.BuildTarget):
- result.update(self.rpaths_for_bundled_shared_libraries(target))
+ result.update(self.rpaths_for_non_system_absolute_shared_libraries(target))
target.rpath_dirs_to_remove.update([d.encode('utf-8') for d in result])
return tuple(result)
@@ -1068,11 +1058,11 @@ class Backend:
tests.
"""
result: T.Set[str] = set()
- prospectives: T.Set[build.Target] = set()
+ prospectives: T.Set[T.Union[build.BuildTarget, build.CustomTarget, build.CustomTargetIndex]] = set()
if isinstance(target, build.BuildTarget):
prospectives.update(target.get_transitive_link_deps())
# External deps
- for deppath in self.rpaths_for_bundled_shared_libraries(target, exclude_system=False):
+ for deppath in self.rpaths_for_non_system_absolute_shared_libraries(target, exclude_system=False):
result.add(os.path.normpath(os.path.join(self.environment.get_build_dir(), deppath)))
for bdep in extra_bdeps:
prospectives.add(bdep)