aboutsummaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2021-08-12 22:13:51 -0400
committerXavier Claessens <xclaesse@gmail.com>2021-08-17 15:19:18 -0400
commit8c5aa031b5ffb4eb3c61083072dcab49c8927d45 (patch)
treef62800f365cf6706fab33283436cbeef7df7206f /mesonbuild
parent6d055b1e27dc6fc0fac967fbe2f439758a4c5d07 (diff)
downloadmeson-8c5aa031b5ffb4eb3c61083072dcab49c8927d45.zip
meson-8c5aa031b5ffb4eb3c61083072dcab49c8927d45.tar.gz
meson-8c5aa031b5ffb4eb3c61083072dcab49c8927d45.tar.bz2
Add install tags
Fixes: #7007.
Diffstat (limited to 'mesonbuild')
-rw-r--r--mesonbuild/backend/backends.py80
-rw-r--r--mesonbuild/backend/ninjabackend.py2
-rw-r--r--mesonbuild/build.py20
-rw-r--r--mesonbuild/interpreter/interpreter.py24
-rw-r--r--mesonbuild/interpreter/mesonmain.py15
-rw-r--r--mesonbuild/minstall.py24
-rw-r--r--mesonbuild/modules/gnome.py4
-rw-r--r--mesonbuild/modules/hotdoc.py1
-rw-r--r--mesonbuild/modules/i18n.py1
-rw-r--r--mesonbuild/modules/pkgconfig.py2
-rw-r--r--mesonbuild/modules/python.py8
-rw-r--r--mesonbuild/modules/qt.py1
12 files changed, 131 insertions, 51 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index aa8e844..6c877ea 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -115,7 +115,8 @@ class InstallData:
class TargetInstallData:
def __init__(self, fname: str, outdir: str, aliases: T.Dict[str, str], strip: bool,
install_name_mappings: T.Dict, rpath_dirs_to_remove: T.Set[bytes],
- install_rpath: str, install_mode: 'FileMode', subproject: str, optional: bool = False):
+ install_rpath: str, install_mode: 'FileMode', subproject: str,
+ optional: bool = False, tag: T.Optional[str] = None):
self.fname = fname
self.outdir = outdir
self.aliases = aliases
@@ -126,22 +127,27 @@ class TargetInstallData:
self.install_mode = install_mode
self.subproject = subproject
self.optional = optional
+ self.tag = tag
class InstallDataBase:
- def __init__(self, path: str, install_path: str, install_mode: 'FileMode', subproject: str):
+ def __init__(self, path: str, install_path: str, install_mode: 'FileMode',
+ subproject: str, tag: T.Optional[str] = None):
self.path = path
self.install_path = install_path
self.install_mode = install_mode
self.subproject = subproject
+ self.tag = tag
class SubdirInstallData(InstallDataBase):
- def __init__(self, path: str, install_path: str, install_mode: 'FileMode', exclude, subproject: str):
- super().__init__(path, install_path, install_mode, subproject)
+ def __init__(self, path: str, install_path: str, install_mode: 'FileMode',
+ exclude, subproject: str, tag: T.Optional[str] = None):
+ super().__init__(path, install_path, install_mode, subproject, tag)
self.exclude = exclude
class ExecutableSerialisation:
def __init__(self, cmd_args, env: T.Optional[build.EnvironmentVariables] = None, exe_wrapper=None,
- workdir=None, extra_paths=None, capture=None, feed=None) -> None:
+ workdir=None, extra_paths=None, capture=None, feed=None,
+ tag: T.Optional[str] = None) -> None:
self.cmd_args = cmd_args
self.env = env
if exe_wrapper is not None:
@@ -155,6 +161,7 @@ class ExecutableSerialisation:
self.skip_if_destdir = False
self.verbose = False
self.subproject = ''
+ self.tag = tag
class TestSerialisation:
def __init__(self, name: str, project: str, suite: T.List[str], fname: T.List[str],
@@ -443,7 +450,8 @@ class Backend:
def get_executable_serialisation(self, cmd, workdir=None,
extra_bdeps=None, capture=None, feed=None,
- env: T.Optional[build.EnvironmentVariables] = None):
+ env: T.Optional[build.EnvironmentVariables] = None,
+ tag: T.Optional[str] = None):
exe = cmd[0]
cmd_args = cmd[1:]
if isinstance(exe, programs.ExternalProgram):
@@ -490,7 +498,7 @@ class Backend:
workdir = workdir or self.environment.get_build_dir()
return ExecutableSerialisation(exe_cmd + cmd_args, env,
exe_wrapper, workdir,
- extra_paths, capture, feed)
+ extra_paths, capture, feed, tag)
def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None,
extra_bdeps=None, capture=None, feed=None,
@@ -1032,7 +1040,7 @@ class Backend:
with open(ifilename, 'w', encoding='utf-8') as f:
f.write(json.dumps(mfobj))
# Copy file from, to, and with mode unchanged
- d.data.append(InstallDataBase(ifilename, ofilename, None, ''))
+ d.data.append(InstallDataBase(ifilename, ofilename, None, '', tag='devel'))
def get_regen_filelist(self):
'''List of all files whose alteration means that the build
@@ -1354,6 +1362,27 @@ class Backend:
with open(install_data_file, 'wb') as ofile:
pickle.dump(self.create_install_data(), ofile)
+ def guess_install_tag(self, fname: str, outdir: T.Optional[str] = None) -> T.Optional[str]:
+ prefix = self.environment.get_prefix()
+ bindir = Path(prefix, self.environment.get_bindir())
+ libdir = Path(prefix, self.environment.get_libdir())
+ incdir = Path(prefix, self.environment.get_includedir())
+ localedir = Path(prefix, self.environment.coredata.get_option(mesonlib.OptionKey('localedir')))
+ dest_path = Path(prefix, outdir, Path(fname).name) if outdir else Path(prefix, fname)
+ if bindir in dest_path.parents:
+ return 'runtime'
+ elif libdir in dest_path.parents:
+ if dest_path.suffix in {'.a', '.pc'}:
+ return 'devel'
+ elif dest_path.suffix in {'.so', '.dll'}:
+ return 'runtime'
+ elif incdir in dest_path.parents:
+ return 'devel'
+ elif localedir in dest_path.parents:
+ return 'i18n'
+ mlog.debug('Failed to guess install tag for', dest_path)
+ return None
+
def generate_target_install(self, d: InstallData) -> None:
for t in self.build.get_targets().values():
if not t.should_install():
@@ -1366,6 +1395,7 @@ class Backend:
"Pass 'false' for outputs that should not be installed and 'true' for\n" \
'using the default installation directory for an output.'
raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs))
+ assert len(t.install_tag) == num_out
install_mode = t.get_custom_install_mode()
# Install the target output(s)
if isinstance(t, build.BuildTarget):
@@ -1387,11 +1417,13 @@ class Backend:
# Install primary build output (library/executable/jar, etc)
# Done separately because of strip/aliases/rpath
if outdirs[0] is not False:
+ tag = t.install_tag[0] or ('devel' if isinstance(t, build.StaticLibrary) else 'runtime')
mappings = t.get_link_deps_mapping(d.prefix, self.environment)
i = TargetInstallData(self.get_target_filename(t), outdirs[0],
t.get_aliases(), should_strip, mappings,
t.rpath_dirs_to_remove,
- t.install_rpath, install_mode, t.subproject)
+ t.install_rpath, install_mode, t.subproject,
+ tag=tag)
d.targets.append(i)
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)):
@@ -1409,7 +1441,8 @@ class Backend:
# Install the import library; may not exist for shared modules
i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, {}, False, {}, set(), '', install_mode,
- t.subproject, optional=isinstance(t, build.SharedModule))
+ t.subproject, optional=isinstance(t, build.SharedModule),
+ tag='devel')
d.targets.append(i)
if not should_strip and t.get_debug_filename():
@@ -1417,17 +1450,19 @@ class Backend:
i = TargetInstallData(debug_file, outdirs[0],
{}, False, {}, set(), '',
install_mode, t.subproject,
- optional=True)
+ optional=True, tag='devel')
d.targets.append(i)
# Install secondary outputs. Only used for Vala right now.
if num_outdirs > 1:
- for output, outdir in zip(t.get_outputs()[1:], outdirs[1:]):
+ for output, outdir, tag in zip(t.get_outputs()[1:], outdirs[1:], t.install_tag[1:]):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
+ tag = tag or self.guess_install_tag(f, outdir)
i = TargetInstallData(f, outdir, {}, False, {}, set(), None,
- install_mode, t.subproject)
+ install_mode, t.subproject,
+ tag=tag)
d.targets.append(i)
elif isinstance(t, build.CustomTarget):
# If only one install_dir is specified, assume that all
@@ -1438,19 +1473,23 @@ class Backend:
# To selectively install only some outputs, pass `false` as
# the install_dir for the corresponding output by index
if num_outdirs == 1 and num_out > 1:
- for output in t.get_outputs():
+ for output, tag in zip(t.get_outputs(), t.install_tag):
f = os.path.join(self.get_target_dir(t), output)
+ tag = tag or self.guess_install_tag(f, outdirs[0])
i = TargetInstallData(f, outdirs[0], {}, False, {}, set(), None, install_mode,
- t.subproject, optional=not t.build_by_default)
+ t.subproject, optional=not t.build_by_default,
+ tag=tag)
d.targets.append(i)
else:
- for output, outdir in zip(t.get_outputs(), outdirs):
+ for output, outdir, tag in zip(t.get_outputs(), outdirs, t.install_tag):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
+ tag = tag or self.guess_install_tag(f, outdir)
i = TargetInstallData(f, outdir, {}, False, {}, set(), None, install_mode,
- t.subproject, optional=not t.build_by_default)
+ t.subproject, optional=not t.build_by_default,
+ tag=tag)
d.targets.append(i)
def generate_custom_install_script(self, d: InstallData) -> None:
@@ -1475,7 +1514,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, h.get_custom_install_mode(), h.subproject)
+ i = InstallDataBase(abspath, outdir, h.get_custom_install_mode(), h.subproject, tag='devel')
d.headers.append(i)
def generate_man_install(self, d: InstallData) -> None:
@@ -1495,7 +1534,7 @@ class Backend:
fname = fname.replace(f'.{m.locale}', '')
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
dstabs = os.path.join(subdir, os.path.basename(fname))
- i = InstallDataBase(srcabs, dstabs, m.get_custom_install_mode(), m.subproject)
+ i = InstallDataBase(srcabs, dstabs, m.get_custom_install_mode(), m.subproject, tag='man')
d.man.append(i)
def generate_data_install(self, d: InstallData):
@@ -1510,7 +1549,8 @@ class Backend:
for src_file, dst_name in zip(de.sources, de.rename):
assert(isinstance(src_file, mesonlib.File))
dst_abs = os.path.join(subdir, dst_name)
- i = InstallDataBase(src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode, de.subproject)
+ tag = de.install_tag or self.guess_install_tag(dst_abs)
+ i = InstallDataBase(src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode, de.subproject, tag=tag)
d.data.append(i)
def generate_subdir_install(self, d: InstallData) -> None:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 871bf9f..68c63b2 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -1513,6 +1513,7 @@ class NinjaBackend(backends.Backend):
args += ['--vapi', os.path.join('..', target.vala_vapi)]
valac_outputs.append(vapiname)
target.outputs += [target.vala_header, target.vala_vapi]
+ target.install_tag += ['devel', 'devel']
# Install header and vapi to default locations if user requests this
if len(target.install_dir) > 1 and target.install_dir[1] is True:
target.install_dir[1] = self.environment.get_includedir()
@@ -1524,6 +1525,7 @@ class NinjaBackend(backends.Backend):
args += ['--gir', os.path.join('..', target.vala_gir)]
valac_outputs.append(girname)
target.outputs.append(target.vala_gir)
+ target.install_tag.append('devel')
# Install GIR to default location if requested by user
if len(target.install_dir) > 3 and target.install_dir[3] is True:
target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0')
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 28ad60e..69a2d76 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -90,6 +90,7 @@ buildtarget_kwargs = {
'install_rpath',
'install_dir',
'install_mode',
+ 'install_tag',
'name_prefix',
'name_suffix',
'native',
@@ -189,7 +190,8 @@ class InstallDir(HoldableObject):
install_mode: 'FileMode',
exclude: T.Tuple[T.Set[str], T.Set[str]],
strip_directory: bool, subproject: str,
- from_source_dir: bool = True):
+ from_source_dir: bool = True,
+ install_tag: T.Optional[str] = None):
self.source_subdir = src_subdir
self.installable_subdir = inst_subdir
self.install_dir = install_dir
@@ -198,6 +200,7 @@ class InstallDir(HoldableObject):
self.strip_directory = strip_directory
self.from_source_dir = from_source_dir
self.subproject = subproject
+ self.install_tag = install_tag
class Build:
@@ -1014,6 +1017,7 @@ class BuildTarget(Target):
self.install_dir = typeslistify(kwargs.get('install_dir', [None]),
(str, bool))
self.install_mode = kwargs.get('install_mode', None)
+ self.install_tag = stringlistify(kwargs.get('install_tag', [None]))
main_class = kwargs.get('main_class', '')
if not isinstance(main_class, str):
raise InvalidArguments('Main class must be a string')
@@ -2206,6 +2210,7 @@ class CustomTarget(Target, CommandBase):
'install',
'install_dir',
'install_mode',
+ 'install_tag',
'build_always',
'build_always_stale',
'depends',
@@ -2339,10 +2344,19 @@ class CustomTarget(Target, CommandBase):
# the list index of that item will not be installed
self.install_dir = typeslistify(kwargs['install_dir'], (str, bool))
self.install_mode = kwargs.get('install_mode', None)
+ # If only one tag is provided, assume all outputs have the same tag.
+ # Otherwise, we must have as much tags as outputs.
+ self.install_tag = typeslistify(kwargs.get('install_tag', [None]), (str, bool))
+ if len(self.install_tag) == 1:
+ self.install_tag = self.install_tag * len(self.outputs)
+ elif len(self.install_tag) != len(self.outputs):
+ m = f'Target {self.name!r} has {len(self.outputs)} outputs but {len(self.install_tag)} "install_tag"s were found.'
+ raise InvalidArguments(m)
else:
self.install = False
self.install_dir = [None]
self.install_mode = None
+ self.install_tag = []
if 'build_always' in kwargs and 'build_always_stale' in kwargs:
raise InvalidArguments('build_always and build_always_stale are mutually exclusive. Combine build_by_default and build_always_stale.')
elif 'build_always' in kwargs:
@@ -2625,10 +2639,12 @@ class ConfigurationData(HoldableObject):
class Data(HoldableObject):
def __init__(self, sources: T.List[File], install_dir: str,
install_mode: 'FileMode', subproject: str,
- rename: T.List[str] = None):
+ rename: T.List[str] = None,
+ install_tag: T.Optional[str] = None):
self.sources = sources
self.install_dir = install_dir
self.install_mode = install_mode
+ self.install_tag = install_tag
if rename is None:
self.rename = [os.path.basename(f.fname) for f in self.sources]
else:
diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py
index ab7efa0..535229c 100644
--- a/mesonbuild/interpreter/interpreter.py
+++ b/mesonbuild/interpreter/interpreter.py
@@ -1598,6 +1598,7 @@ external dependencies (including libraries) must go to "dependencies".''')
def func_subdir_done(self, node, args, kwargs):
raise SubdirDoneRequest()
+ @FeatureNewKwargs('custom_target', '0.60.0', ['install_tag'])
@FeatureNewKwargs('custom_target', '0.57.0', ['env'])
@FeatureNewKwargs('custom_target', '0.48.0', ['console'])
@FeatureNewKwargs('custom_target', '0.47.0', ['install_mode', 'build_always_stale'])
@@ -1606,7 +1607,7 @@ external dependencies (including libraries) must go to "dependencies".''')
@permittedKwargs({'input', 'output', 'command', 'install', 'install_dir', 'install_mode',
'build_always', 'capture', 'depends', 'depend_files', 'depfile',
'build_by_default', 'build_always_stale', 'console', 'env',
- 'feed'})
+ 'feed', 'install_tag'})
@typed_pos_args('custom_target', str)
def func_custom_target(self, node: mparser.FunctionNode, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> build.CustomTarget:
if 'depfile' in kwargs and ('@BASENAME@' in kwargs['depfile'] or '@PLAINNAME@' in kwargs['depfile']):
@@ -1903,6 +1904,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
KwargInfo('sources', ContainerTypeInfo(list, (str, mesonlib.File)), listify=True, default=[]),
KwargInfo('rename', ContainerTypeInfo(list, str), default=[], listify=True, since='0.46.0'),
INSTALL_MODE_KW.evolve(since='0.38.0'),
+ KwargInfo('install_tag', str, since='0.60.0'),
)
def func_install_data(self, node: mparser.BaseNode,
args: T.Tuple[T.List['mesonlib.FileOrString']],
@@ -1915,12 +1917,14 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
'"rename" and "sources" argument lists must be the same length if "rename" is given. '
f'Rename has {len(rename)} elements and sources has {len(sources)}.')
- return self.install_data_impl(sources, kwargs['install_dir'], kwargs['install_mode'], rename)
+ return self.install_data_impl(sources, kwargs['install_dir'], kwargs['install_mode'], rename,
+ kwargs['install_tag'])
def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str,
- install_mode: FileMode, rename: T.Optional[str]) -> build.Data:
+ install_mode: FileMode, rename: T.Optional[str],
+ tag: T.Optional[str]) -> build.Data:
"""Just the implementation with no validation."""
- data = build.Data(sources, install_dir, install_mode, self.subproject, rename)
+ data = build.Data(sources, install_dir, install_mode, self.subproject, rename, tag)
self.build.data.append(data)
return data
@@ -1928,6 +1932,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
@typed_kwargs(
'install_subdir',
KwargInfo('install_dir', str, required=True),
+ KwargInfo('install_tag', str, since='0.60.0'),
KwargInfo('strip_directory', bool, default=False),
KwargInfo('exclude_files', ContainerTypeInfo(list, str),
default=[], listify=True, since='0.42.0',
@@ -1947,7 +1952,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
kwargs['install_mode'],
exclude,
kwargs['strip_directory'],
- self.subproject)
+ self.subproject,
+ install_tag=kwargs['install_tag'])
self.build.install_dirs.append(idir)
return idir
@@ -1956,9 +1962,10 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
@FeatureNewKwargs('configure_file', '0.41.0', ['capture'])
@FeatureNewKwargs('configure_file', '0.50.0', ['install'])
@FeatureNewKwargs('configure_file', '0.52.0', ['depfile'])
+ @FeatureNewKwargs('configure_file', '0.60.0', ['install_tag'])
@permittedKwargs({'input', 'output', 'configuration', 'command', 'copy', 'depfile',
'install_dir', 'install_mode', 'capture', 'install', 'format',
- 'output_format', 'encoding'})
+ 'output_format', 'encoding', 'install_tag'})
@noPosargs
def func_configure_file(self, node, args, kwargs):
if 'output' not in kwargs:
@@ -2139,7 +2146,10 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
'is true')
cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
install_mode = self._get_kwarg_install_mode(kwargs)
- self.build.data.append(build.Data([cfile], idir, install_mode, self.subproject))
+ install_tag = kwargs.get('install_tag')
+ if install_tag is not None and not isinstance(install_tag, str):
+ raise InvalidArguments('install_tag keyword argument must be string')
+ self.build.data.append(build.Data([cfile], idir, install_mode, self.subproject, install_tag=install_tag))
return mesonlib.File.from_built_file(self.subdir, output)
def extract_incdirs(self, kwargs):
diff --git a/mesonbuild/interpreter/mesonmain.py b/mesonbuild/interpreter/mesonmain.py
index 97a695b..ea16b46 100644
--- a/mesonbuild/interpreter/mesonmain.py
+++ b/mesonbuild/interpreter/mesonmain.py
@@ -7,9 +7,9 @@ from .. import mlog
from ..mesonlib import MachineChoice, OptionKey
from ..programs import OverrideProgram, ExternalProgram
-from ..interpreterbase import (MesonInterpreterObject, FeatureNewKwargs, FeatureNew, FeatureDeprecated,
+from ..interpreterbase import (MesonInterpreterObject, FeatureNew, FeatureDeprecated,
typed_pos_args, permittedKwargs, noArgsFlattening, noPosargs, noKwargs,
- MesonVersionString, InterpreterException)
+ typed_kwargs, KwargInfo, MesonVersionString, InterpreterException)
from .interpreterobjects import (ExecutableHolder, ExternalProgramHolder,
CustomTargetHolder, CustomTargetIndexHolder,
@@ -107,20 +107,19 @@ class MesonMain(MesonInterpreterObject):
'0.55.0', self.interpreter.subproject)
return script_args
- @FeatureNewKwargs('add_install_script', '0.57.0', ['skip_if_destdir'])
- @permittedKwargs({'skip_if_destdir'})
+ @typed_kwargs('add_install_script',
+ KwargInfo('skip_if_destdir', bool, default=False, since='0.57.0'),
+ KwargInfo('install_tag', str, since='0.60.0'))
def add_install_script_method(self, args: 'T.Tuple[T.Union[str, mesonlib.File, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder], ...]', kwargs):
if len(args) < 1:
raise InterpreterException('add_install_script takes one or more arguments')
if isinstance(args[0], mesonlib.File):
FeatureNew.single_use('Passing file object to script parameter of add_install_script',
'0.57.0', self.interpreter.subproject)
- skip_if_destdir = kwargs.get('skip_if_destdir', False)
- if not isinstance(skip_if_destdir, bool):
- raise InterpreterException('skip_if_destdir keyword argument must be boolean')
script_args = self._process_script_args('add_install_script', args[1:], allow_built=True)
script = self._find_source_script(args[0], script_args)
- script.skip_if_destdir = skip_if_destdir
+ script.skip_if_destdir = kwargs['skip_if_destdir']
+ script.tag = kwargs['install_tag']
self.build.install_scripts.append(script)
@permittedKwargs(set())
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index d8faad6..6660d6c 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -25,7 +25,7 @@ import sys
import typing as T
from . import environment
-from .backend.backends import InstallData
+from .backend.backends import InstallData, InstallDataBase, TargetInstallData, ExecutableSerialisation
from .coredata import major_versions_differ, MesonVersionMismatchException
from .coredata import version as coredata_version
from .mesonlib import Popen_safe, RealPathAction, is_windows
@@ -56,6 +56,7 @@ if T.TYPE_CHECKING:
destdir: str
dry_run: bool
skip_subprojects: str
+ tags: str
symlink_warning = '''Warning: trying to copy a symlink that points to a file. This will copy the file,
@@ -81,6 +82,8 @@ def add_arguments(parser: argparse.ArgumentParser) -> None:
help='Doesn\'t actually install, but print logs. (Since 0.57.0)')
parser.add_argument('--skip-subprojects', nargs='?', const='*', default='',
help='Do not install files from given subprojects. (Since 0.58.0)')
+ parser.add_argument('--tags', default=None,
+ help='Install only targets having one of the given tags. (Since 0.60.0)')
class DirMaker:
def __init__(self, lf: T.TextIO, makedirs: T.Callable[..., None]):
@@ -303,6 +306,7 @@ class Installer:
# ['*'] means skip all,
# ['sub1', ...] means skip only those.
self.skip_subprojects = [i.strip() for i in options.skip_subprojects.split(',')]
+ self.tags = [i.strip() for i in options.tags.split(',')] if options.tags else None
def remove(self, *args: T.Any, **kwargs: T.Any) -> None:
if not self.dry_run:
@@ -371,8 +375,10 @@ class Installer:
return run_exe(*args, **kwargs)
return 0
- def install_subproject(self, subproject: str) -> bool:
- if subproject and (subproject in self.skip_subprojects or '*' in self.skip_subprojects):
+ def should_install(self, d: T.Union[TargetInstallData, InstallDataBase, ExecutableSerialisation]) -> bool:
+ if d.subproject and (d.subproject in self.skip_subprojects or '*' in self.skip_subprojects):
+ return False
+ if self.tags and d.tag not in self.tags:
return False
return True
@@ -552,7 +558,7 @@ class Installer:
def install_subdirs(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for i in d.install_subdirs:
- if not self.install_subproject(i.subproject):
+ if not self.should_install(i):
continue
self.did_install_something = True
full_dst_dir = get_destdir_path(destdir, fullprefix, i.install_path)
@@ -562,7 +568,7 @@ class Installer:
def install_data(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for i in d.data:
- if not self.install_subproject(i.subproject):
+ if not self.should_install(i):
continue
fullfilename = i.path
outfilename = get_destdir_path(destdir, fullprefix, i.install_path)
@@ -573,7 +579,7 @@ class Installer:
def install_man(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for m in d.man:
- if not self.install_subproject(m.subproject):
+ if not self.should_install(m):
continue
full_source_filename = m.path
outfilename = get_destdir_path(destdir, fullprefix, m.install_path)
@@ -584,7 +590,7 @@ class Installer:
def install_headers(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for t in d.headers:
- if not self.install_subproject(t.subproject):
+ if not self.should_install(t):
continue
fullfilename = t.path
fname = os.path.basename(fullfilename)
@@ -605,7 +611,7 @@ class Installer:
env['MESON_INSTALL_QUIET'] = '1'
for i in d.install_scripts:
- if not self.install_subproject(i.subproject):
+ if not self.should_install(i):
continue
name = ' '.join(i.cmd_args)
if i.skip_if_destdir and destdir:
@@ -625,7 +631,7 @@ class Installer:
def install_targets(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix: str) -> None:
for t in d.targets:
- if not self.install_subproject(t.subproject):
+ if not self.should_install(t):
continue
if not os.path.exists(t.fname):
# For example, import libraries of shared modules are optional
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index a9cd1d3..a2f98bc 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -747,6 +747,7 @@ class GnomeModule(ExtensionModule):
scankwargs['install'] = kwargs['install']
scankwargs['install_dir'] = kwargs.get('install_dir_gir',
os.path.join(state.environment.get_datadir(), 'gir-1.0'))
+ scankwargs['install_tag'] = 'devel'
if 'build_by_default' in kwargs:
scankwargs['build_by_default'] = kwargs['build_by_default']
@@ -764,6 +765,7 @@ class GnomeModule(ExtensionModule):
typelib_kwargs['install'] = kwargs['install']
typelib_kwargs['install_dir'] = kwargs.get('install_dir_typelib',
os.path.join(state.environment.get_libdir(), 'girepository-1.0'))
+ typelib_kwargs['install_tag'] = 'typelib'
if 'build_by_default' in kwargs:
typelib_kwargs['build_by_default'] = kwargs['build_by_default']
@@ -1146,7 +1148,7 @@ class GnomeModule(ExtensionModule):
state.test(check_args, env=check_env, workdir=check_workdir, depends=custom_target)
res = [custom_target, alias_target]
if kwargs.get('install', True):
- res.append(state.backend.get_executable_serialisation(command + args))
+ res.append(state.backend.get_executable_serialisation(command + args, tag='doc'))
return ModuleReturnValue(custom_target, res)
def _get_build_args(self, kwargs, state, depends):
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 4dccd06..19a1728 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -354,6 +354,7 @@ class HotdocTargetBuilder:
'--builddir', os.path.join(self.builddir, self.subdir)] +
self.hotdoc.get_command() +
['run', '--conf-file', hotdoc_config_name])
+ install_script.tag = 'doc'
return (target, install_script)
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index 57d25fd..539f82b 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -181,6 +181,7 @@ class I18nModule(ExtensionModule):
# to custom_targets. Crude hack: set the build target's subdir manually.
# Bonus: the build tree has something usable as an uninstalled bindtextdomain() target dir.
'install_dir': path.join(install_dir, l, 'LC_MESSAGES'),
+ 'install_tag': 'i18n',
}
gmotarget = build.CustomTarget(l+'.mo', path.join(state.subdir, l, 'LC_MESSAGES'), state.subproject, gmo_kwargs)
targets.append(gmotarget)
diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py
index 48bbc34..e57c4f6 100644
--- a/mesonbuild/modules/pkgconfig.py
+++ b/mesonbuild/modules/pkgconfig.py
@@ -548,7 +548,7 @@ class PkgConfigModule(ExtensionModule):
self._generate_pkgconfig_file(state, deps, subdirs, name, description, url,
version, pcfile, conflicts, variables,
unescaped_variables, False, dataonly)
- res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, None, state.subproject)
+ res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, None, state.subproject, install_tag='devel')
variables = self.interpreter.extract_variables(kwargs, argname='uninstalled_variables', dict_new=True)
variables = parse_variable_list(variables)
unescaped_variables = self.interpreter.extract_variables(kwargs, argname='unescaped_uninstalled_variables')
diff --git a/mesonbuild/modules/python.py b/mesonbuild/modules/python.py
index f38becf..65a73a7 100644
--- a/mesonbuild/modules/python.py
+++ b/mesonbuild/modules/python.py
@@ -344,6 +344,7 @@ if T.TYPE_CHECKING:
pure: bool
subdir: str
+ install_tag: T.Optional[str]
class PythonInstallation(ExternalProgramHolder):
@@ -436,14 +437,15 @@ class PythonInstallation(ExternalProgramHolder):
return dep
@typed_pos_args('install_data', varargs=(str, mesonlib.File))
- @typed_kwargs('python_installation.install_sources', _PURE_KW, _SUBDIR_KW)
+ @typed_kwargs('python_installation.install_sources', _PURE_KW, _SUBDIR_KW,
+ KwargInfo('install_tag', str, since='0.60.0'))
def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]],
kwargs: 'PyInstallKw') -> 'Data':
+ tag = kwargs['install_tag'] or 'runtime'
return self.interpreter.install_data_impl(
self.interpreter.source_strings_to_files(args[0]),
self._get_install_dir_impl(kwargs['pure'], kwargs['subdir']),
- mesonlib.FileMode(),
- None)
+ mesonlib.FileMode(), rename=None, tag=tag)
@noPosargs
@typed_kwargs('python_installation.install_dir', _PURE_KW, _SUBDIR_KW)
diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py
index 5efd668..ed66ae9 100644
--- a/mesonbuild/modules/qt.py
+++ b/mesonbuild/modules/qt.py
@@ -513,6 +513,7 @@ class QtBaseModule(ExtensionModule):
lrelease_kwargs = {'output': '@BASENAME@.qm',
'input': ts,
'install': kwargs.get('install', False),
+ 'install_tag': 'i18n',
'build_by_default': kwargs.get('build_by_default', False),
'command': cmd}
if install_dir is not None: