aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorArsen Arsenović <arsen@aarsen.me>2022-07-12 15:26:22 +0200
committerEli Schwartz <eschwartz93@gmail.com>2023-09-13 21:44:40 -0400
commit0af126fec798d6dbb0d1ad52168cc1f3f1758acd (patch)
tree41e4a51789de1e92881b29e7a7d9f13e5f369f8f /mesonbuild
parent56ef698426bb58b7ffd32b0711e064b54166e10f (diff)
downloadmeson-0af126fec798d6dbb0d1ad52168cc1f3f1758acd.zip
meson-0af126fec798d6dbb0d1ad52168cc1f3f1758acd.tar.gz
meson-0af126fec798d6dbb0d1ad52168cc1f3f1758acd.tar.bz2
install_{data,headers,subdir}: implement follow_symlinks
This permits users who rely on following symlinks to stay on the old default of following them.
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/backends.py14
-rw-r--r--mesonbuild/build.py3
-rw-r--r--mesonbuild/interpreter/interpreter.py20
-rw-r--r--mesonbuild/interpreter/kwargs.py3
-rw-r--r--mesonbuild/interpreter/type_checking.py6
-rw-r--r--mesonbuild/minstall.py31
6 files changed, 53 insertions, 24 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 62cf162..1d2283f 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -172,6 +172,7 @@ class InstallDataBase:
subproject: str
tag: T.Optional[str] = None
data_type: T.Optional[str] = None
+ follow_symlinks: T.Optional[bool] = None
@dataclass(eq=False)
class InstallSymlinkData:
@@ -186,8 +187,9 @@ class InstallSymlinkData:
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]],
- subproject: str, tag: T.Optional[str] = None, data_type: T.Optional[str] = None):
- super().__init__(path, install_path, install_path_name, install_mode, subproject, tag, data_type)
+ subproject: str, tag: T.Optional[str] = None, data_type: T.Optional[str] = None,
+ follow_symlinks: T.Optional[bool] = None):
+ super().__init__(path, install_path, install_path_name, install_mode, subproject, tag, data_type, follow_symlinks)
self.exclude = exclude
@@ -1832,7 +1834,7 @@ class Backend:
if not isinstance(f, File):
raise MesonException(f'Invalid header type {f!r} can\'t be installed')
abspath = f.absolute_path(srcdir, builddir)
- i = InstallDataBase(abspath, outdir, outdir_name, h.get_custom_install_mode(), h.subproject, tag='devel')
+ i = InstallDataBase(abspath, outdir, outdir_name, h.get_custom_install_mode(), h.subproject, tag='devel', follow_symlinks=h.follow_symlinks)
d.headers.append(i)
def generate_man_install(self, d: InstallData) -> None:
@@ -1877,7 +1879,8 @@ class Backend:
dstdir_name = os.path.join(subdir_name, dst_name)
tag = de.install_tag or self.guess_install_tag(dst_abs)
i = InstallDataBase(src_file.absolute_path(srcdir, builddir), dst_abs, dstdir_name,
- de.install_mode, de.subproject, tag=tag, data_type=de.data_type)
+ de.install_mode, de.subproject, tag=tag, data_type=de.data_type,
+ follow_symlinks=de.follow_symlinks)
d.data.append(i)
def generate_symlink_install(self, d: InstallData) -> None:
@@ -1908,7 +1911,8 @@ class Backend:
dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
dst_name = os.path.join(dst_name, os.path.basename(src_dir))
tag = sd.install_tag or self.guess_install_tag(os.path.join(sd.install_dir, 'dummy'))
- i = SubdirInstallData(src_dir, dst_dir, dst_name, sd.install_mode, sd.exclude, sd.subproject, tag)
+ i = SubdirInstallData(src_dir, dst_dir, dst_name, sd.install_mode, sd.exclude, sd.subproject, tag,
+ follow_symlinks=sd.follow_symlinks)
d.install_subdirs.append(i)
def get_introspection_data(self, target_id: str, target: build.Target) -> T.List['TargetIntrospectionData']:
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 0943703..fece0be 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -156,6 +156,7 @@ class Headers(HoldableObject):
custom_install_dir: T.Optional[str]
custom_install_mode: 'FileMode'
subproject: str
+ follow_symlinks: T.Optional[bool] = None
# TODO: we really don't need any of these methods, but they're preserved to
# keep APIs relying on them working.
@@ -214,6 +215,7 @@ class InstallDir(HoldableObject):
subproject: str
from_source_dir: bool = True
install_tag: T.Optional[str] = None
+ follow_symlinks: T.Optional[bool] = None
@dataclass(eq=False)
class DepManifest:
@@ -2973,6 +2975,7 @@ class Data(HoldableObject):
rename: T.List[str] = None
install_tag: T.Optional[str] = None
data_type: str = None
+ follow_symlinks: T.Optional[bool] = None
def __post_init__(self) -> None:
if self.rename is None:
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index 838ad2f..6fcefd1 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -78,6 +78,7 @@ from .type_checking import (
INSTALL_KW,
INSTALL_DIR_KW,
INSTALL_MODE_KW,
+ INSTALL_FOLLOW_SYMLINKS,
LINK_WITH_KW,
LINK_WHOLE_KW,
CT_INSTALL_TAG_KW,
@@ -2238,6 +2239,7 @@ class Interpreter(InterpreterBase, HoldableObject):
KwargInfo('subdir', (str, NoneType)),
INSTALL_MODE_KW.evolve(since='0.47.0'),
INSTALL_DIR_KW,
+ INSTALL_FOLLOW_SYMLINKS,
)
def func_install_headers(self, node: mparser.BaseNode,
args: T.Tuple[T.List['mesonlib.FileOrString']],
@@ -2264,7 +2266,8 @@ class Interpreter(InterpreterBase, HoldableObject):
for childdir in dirs:
h = build.Headers(dirs[childdir], os.path.join(install_subdir, childdir), kwargs['install_dir'],
- install_mode, self.subproject)
+ install_mode, self.subproject,
+ follow_symlinks=kwargs['follow_symlinks'])
ret_headers.append(h)
self.build.headers.append(h)
@@ -2459,6 +2462,7 @@ class Interpreter(InterpreterBase, HoldableObject):
INSTALL_TAG_KW.evolve(since='0.60.0'),
INSTALL_DIR_KW,
PRESERVE_PATH_KW.evolve(since='0.64.0'),
+ INSTALL_FOLLOW_SYMLINKS,
)
def func_install_data(self, node: mparser.BaseNode,
args: T.Tuple[T.List['mesonlib.FileOrString']],
@@ -2486,15 +2490,16 @@ class Interpreter(InterpreterBase, HoldableObject):
install_mode = self._warn_kwarg_install_mode_sticky(kwargs['install_mode'])
return self.install_data_impl(sources, install_dir, install_mode, rename, kwargs['install_tag'],
- preserve_path=kwargs['preserve_path'])
+ preserve_path=kwargs['preserve_path'],
+ follow_symlinks=kwargs['follow_symlinks'])
def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str,
install_mode: FileMode, rename: T.Optional[str],
tag: T.Optional[str],
install_data_type: T.Optional[str] = None,
- preserve_path: bool = False) -> build.Data:
+ preserve_path: bool = False,
+ follow_symlinks: T.Optional[bool] = None) -> build.Data:
install_dir_name = install_dir.optname if isinstance(install_dir, P_OBJ.OptionString) else install_dir
-
dirs = collections.defaultdict(list)
if preserve_path:
for file in sources:
@@ -2506,7 +2511,8 @@ class Interpreter(InterpreterBase, HoldableObject):
ret_data = []
for childdir, files in dirs.items():
d = build.Data(files, os.path.join(install_dir, childdir), os.path.join(install_dir_name, childdir),
- install_mode, self.subproject, rename, tag, install_data_type)
+ install_mode, self.subproject, rename, tag, install_data_type,
+ follow_symlinks)
ret_data.append(d)
self.build.data.extend(ret_data)
@@ -2525,6 +2531,7 @@ class Interpreter(InterpreterBase, HoldableObject):
validator=lambda x: 'cannot be absolute' if any(os.path.isabs(d) for d in x) else None),
INSTALL_MODE_KW.evolve(since='0.38.0'),
INSTALL_TAG_KW.evolve(since='0.60.0'),
+ INSTALL_FOLLOW_SYMLINKS,
)
def func_install_subdir(self, node: mparser.BaseNode, args: T.Tuple[str],
kwargs: 'kwtypes.FuncInstallSubdir') -> build.InstallDir:
@@ -2550,7 +2557,8 @@ class Interpreter(InterpreterBase, HoldableObject):
exclude,
kwargs['strip_directory'],
self.subproject,
- install_tag=kwargs['install_tag'])
+ install_tag=kwargs['install_tag'],
+ follow_symlinks=kwargs['follow_symlinks'])
self.build.install_dirs.append(idir)
return idir
diff --git a/mesonbuild/interpreter/kwargs.py b/mesonbuild/interpreter/kwargs.py
index 1aee414..e67ebf0 100644
--- a/mesonbuild/interpreter/kwargs.py
+++ b/mesonbuild/interpreter/kwargs.py
@@ -124,6 +124,7 @@ class FuncInstallSubdir(TypedDict):
exclude_files: T.List[str]
exclude_directories: T.List[str]
install_mode: FileMode
+ follow_symlinks: T.Optional[bool]
class FuncInstallData(TypedDict):
@@ -132,6 +133,7 @@ class FuncInstallData(TypedDict):
sources: T.List[FileOrString]
rename: T.List[str]
install_mode: FileMode
+ follow_symlinks: T.Optional[bool]
class FuncInstallHeaders(TypedDict):
@@ -139,6 +141,7 @@ class FuncInstallHeaders(TypedDict):
install_dir: T.Optional[str]
install_mode: FileMode
subdir: T.Optional[str]
+ follow_symlinks: T.Optional[bool]
class FuncInstallMan(TypedDict):
diff --git a/mesonbuild/interpreter/type_checking.py b/mesonbuild/interpreter/type_checking.py
index 28c9152..047aff8 100644
--- a/mesonbuild/interpreter/type_checking.py
+++ b/mesonbuild/interpreter/type_checking.py
@@ -368,6 +368,12 @@ CT_INSTALL_TAG_KW: KwargInfo[T.List[T.Union[str, bool]]] = KwargInfo(
INSTALL_TAG_KW: KwargInfo[T.Optional[str]] = KwargInfo('install_tag', (str, NoneType))
+INSTALL_FOLLOW_SYMLINKS: KwargInfo[T.Optional[bool]] = KwargInfo(
+ 'follow_symlinks',
+ (bool, NoneType),
+ since='1.3.0',
+)
+
INSTALL_KW = KwargInfo('install', bool, default=False)
CT_INSTALL_DIR_KW: KwargInfo[T.List[T.Union[str, Literal[False]]]] = KwargInfo(
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index 0d397b2..5f8629b 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -64,9 +64,11 @@ if T.TYPE_CHECKING:
strip: bool
-symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file,
-but this will be changed in a future version of Meson to copy the symlink as is. Please update your
-build definitions so that it will not break when the change happens.'''
+symlink_warning = '''\
+Warning: trying to copy a symlink that points to a file. This currently copies
+the file by default, but will be changed in a future version of Meson to copy
+the link instead. Set follow_symlinks to true to preserve current behavior, or
+false to copy the link.'''
selinux_updates: T.List[str] = []
@@ -389,7 +391,8 @@ class Installer:
return from_time <= to_time
def do_copyfile(self, from_file: str, to_file: str,
- makedirs: T.Optional[T.Tuple[T.Any, str]] = None) -> bool:
+ makedirs: T.Optional[T.Tuple[T.Any, str]] = None,
+ follow_symlinks: T.Optional[bool] = None) -> bool:
outdir = os.path.split(to_file)[0]
if not os.path.isfile(from_file) and not os.path.islink(from_file):
raise MesonException(f'Tried to install something that isn\'t a file: {from_file!r}')
@@ -417,10 +420,10 @@ class Installer:
# Dangling symlink. Replicate as is.
self.copy(from_file, outdir, follow_symlinks=False)
else:
- # Remove this entire branch when changing the behaviour to duplicate
- # symlinks rather than copying what they point to.
- print(symlink_warning)
- self.copy2(from_file, to_file)
+ if follow_symlinks is None:
+ follow_symlinks = True # TODO: change to False when removing the warning
+ print(symlink_warning)
+ self.copy2(from_file, to_file, follow_symlinks=follow_symlinks)
else:
self.copy2(from_file, to_file)
selinux_updates.append(to_file)
@@ -454,7 +457,7 @@ class Installer:
def do_copydir(self, data: InstallData, src_dir: str, dst_dir: str,
exclude: T.Optional[T.Tuple[T.Set[str], T.Set[str]]],
- install_mode: 'FileMode', dm: DirMaker) -> None:
+ install_mode: 'FileMode', dm: DirMaker, follow_symlinks: T.Optional[bool] = None) -> None:
'''
Copies the contents of directory @src_dir into @dst_dir.
@@ -519,7 +522,7 @@ class Installer:
dm.makedirs(parent_dir)
self.copystat(os.path.dirname(abs_src), parent_dir)
# FIXME: what about symlinks?
- self.do_copyfile(abs_src, abs_dst)
+ self.do_copyfile(abs_src, abs_dst, follow_symlinks=follow_symlinks)
self.set_mode(abs_dst, install_mode, data.install_umask)
def do_install(self, datafilename: str) -> None:
@@ -613,7 +616,8 @@ class Installer:
full_dst_dir = get_destdir_path(destdir, fullprefix, i.install_path)
self.log(f'Installing subdir {i.path} to {full_dst_dir}')
dm.makedirs(full_dst_dir, exist_ok=True)
- self.do_copydir(d, i.path, full_dst_dir, i.exclude, i.install_mode, dm)
+ self.do_copydir(d, i.path, full_dst_dir, i.exclude, i.install_mode, dm,
+ follow_symlinks=i.follow_symlinks)
def install_data(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for i in d.data:
@@ -622,7 +626,7 @@ class Installer:
fullfilename = i.path
outfilename = get_destdir_path(destdir, fullprefix, i.install_path)
outdir = os.path.dirname(outfilename)
- if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)):
+ if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir), follow_symlinks=i.follow_symlinks):
self.did_install_something = True
self.set_mode(outfilename, i.install_mode, d.install_umask)
@@ -668,7 +672,8 @@ class Installer:
fname = os.path.basename(fullfilename)
outdir = get_destdir_path(destdir, fullprefix, t.install_path)
outfilename = os.path.join(outdir, fname)
- if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir)):
+ if self.do_copyfile(fullfilename, outfilename, makedirs=(dm, outdir),
+ follow_symlinks=t.follow_symlinks):
self.did_install_something = True
self.set_mode(outfilename, t.install_mode, d.install_umask)