introspection: untangle install_plan implemetation, fix a bunch of wrong ones

Generally plumb through the values of get_option() passed to
install_dir, and use this to establish the install plan name. Fixes
several odd cases, such as:

- {datadir} being prepended to "share" or "include"

- dissociating custom install directories and writing them out as
  {prefix}/share/foo or {prefix}/lib/python3.10/site-packages

This is the second half of #9478
Fixes #10601
This commit is contained in:
Eli Schwartz 2022-08-01 23:51:43 -04:00
parent e19e9ce6f1
commit 3e73d4d77d
No known key found for this signature in database
GPG Key ID: CEB167EFB5722BD6
6 changed files with 53 additions and 48 deletions

View File

@ -1534,7 +1534,7 @@ class Backend:
for t in self.build.get_targets().values(): for t in self.build.get_targets().values():
if not t.should_install(): if not t.should_install():
continue continue
outdirs, default_install_dir_name, custom_install_dir = t.get_install_dir() outdirs, install_dir_names, custom_install_dir = t.get_install_dir()
# Sanity-check the outputs and install_dirs # Sanity-check the outputs and install_dirs
num_outdirs, num_out = len(outdirs), len(t.get_outputs()) num_outdirs, num_out = len(outdirs), len(t.get_outputs())
if num_outdirs != 1 and num_outdirs != num_out: if num_outdirs != 1 and num_outdirs != num_out:
@ -1544,7 +1544,9 @@ class Backend:
raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs)) raise MesonException(m.format(t.name, num_out, t.get_outputs(), num_outdirs))
assert len(t.install_tag) == num_out assert len(t.install_tag) == num_out
install_mode = t.get_custom_install_mode() install_mode = t.get_custom_install_mode()
first_outdir = outdirs[0] # because mypy get's confused type narrowing in lists # because mypy get's confused type narrowing in lists
first_outdir = outdirs[0]
first_outdir_name = install_dir_names[0]
# Install the target output(s) # Install the target output(s)
if isinstance(t, build.BuildTarget): if isinstance(t, build.BuildTarget):
@ -1570,7 +1572,7 @@ class Backend:
tag = t.install_tag[0] or ('devel' if isinstance(t, build.StaticLibrary) else 'runtime') tag = t.install_tag[0] or ('devel' if isinstance(t, build.StaticLibrary) else 'runtime')
mappings = t.get_link_deps_mapping(d.prefix) mappings = t.get_link_deps_mapping(d.prefix)
i = TargetInstallData(self.get_target_filename(t), first_outdir, i = TargetInstallData(self.get_target_filename(t), first_outdir,
default_install_dir_name, first_outdir_name,
should_strip, mappings, t.rpath_dirs_to_remove, should_strip, mappings, t.rpath_dirs_to_remove,
t.install_rpath, install_mode, t.subproject, t.install_rpath, install_mode, t.subproject,
tag=tag, can_strip=can_strip) tag=tag, can_strip=can_strip)
@ -1595,7 +1597,7 @@ class Backend:
implib_install_dir = self.environment.get_import_lib_dir() implib_install_dir = self.environment.get_import_lib_dir()
# Install the import library; may not exist for shared modules # Install the import library; may not exist for shared modules
i = TargetInstallData(self.get_target_filename_for_linking(t), i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, default_install_dir_name, implib_install_dir, first_outdir_name,
False, {}, set(), '', install_mode, False, {}, set(), '', install_mode,
t.subproject, optional=isinstance(t, build.SharedModule), t.subproject, optional=isinstance(t, build.SharedModule),
tag='devel') tag='devel')
@ -1604,19 +1606,19 @@ class Backend:
if not should_strip and t.get_debug_filename(): if not should_strip and t.get_debug_filename():
debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename()) debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename())
i = TargetInstallData(debug_file, first_outdir, i = TargetInstallData(debug_file, first_outdir,
default_install_dir_name, first_outdir_name,
False, {}, set(), '', False, {}, set(), '',
install_mode, t.subproject, install_mode, t.subproject,
optional=True, tag='devel') optional=True, tag='devel')
d.targets.append(i) d.targets.append(i)
# Install secondary outputs. Only used for Vala right now. # Install secondary outputs. Only used for Vala right now.
if num_outdirs > 1: if num_outdirs > 1:
for output, outdir, tag in zip(t.get_outputs()[1:], outdirs[1:], t.install_tag[1:]): for output, outdir, outdir_name, tag in zip(t.get_outputs()[1:], outdirs[1:], install_dir_names[1:], t.install_tag[1:]):
# User requested that we not install this output # User requested that we not install this output
if outdir is False: if outdir is False:
continue continue
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, default_install_dir_name, False, {}, set(), None, i = TargetInstallData(f, outdir, outdir_name, False, {}, set(), None,
install_mode, t.subproject, install_mode, t.subproject,
tag=tag) tag=tag)
d.targets.append(i) d.targets.append(i)
@ -1635,18 +1637,18 @@ class Backend:
if first_outdir is not False: if first_outdir is not False:
for output, tag in zip(t.get_outputs(), t.install_tag): for output, tag in zip(t.get_outputs(), t.install_tag):
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, first_outdir, default_install_dir_name, i = TargetInstallData(f, first_outdir, first_outdir_name,
False, {}, set(), None, install_mode, False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default, t.subproject, optional=not t.build_by_default,
tag=tag) tag=tag)
d.targets.append(i) d.targets.append(i)
else: else:
for output, outdir, tag in zip(t.get_outputs(), outdirs, t.install_tag): for output, outdir, outdir_name, tag in zip(t.get_outputs(), outdirs, install_dir_names, t.install_tag):
# User requested that we not install this output # User requested that we not install this output
if outdir is False: if outdir is False:
continue continue
f = os.path.join(self.get_target_dir(t), output) f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, default_install_dir_name, i = TargetInstallData(f, outdir, outdir_name,
False, {}, set(), None, install_mode, False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default, t.subproject, optional=not t.build_by_default,
tag=tag) tag=tag)

View File

@ -628,7 +628,7 @@ class Target(HoldableObject):
# False (which means we want this specific output out of many # False (which means we want this specific output out of many
# outputs to not be installed). # outputs to not be installed).
custom_install_dir = True custom_install_dir = True
default_install_dir_name = None install_dir_names = [getattr(i, 'optname', None) for i in outdirs]
else: else:
custom_install_dir = False custom_install_dir = False
# if outdirs is empty we need to set to something, otherwise we set # if outdirs is empty we need to set to something, otherwise we set
@ -637,8 +637,9 @@ class Target(HoldableObject):
outdirs[0] = default_install_dir outdirs[0] = default_install_dir
else: else:
outdirs = [default_install_dir] outdirs = [default_install_dir]
install_dir_names = [default_install_dir_name] * len(outdirs)
return outdirs, default_install_dir_name, custom_install_dir return outdirs, install_dir_names, custom_install_dir
def get_basename(self) -> str: def get_basename(self) -> str:
return self.name return self.name

View File

@ -2342,14 +2342,8 @@ class Interpreter(InterpreterBase, HoldableObject):
'"rename" and "sources" argument lists must be the same length if "rename" is given. ' '"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)}.') f'Rename has {len(rename)} elements and sources has {len(sources)}.')
install_dir_name = kwargs['install_dir']
if install_dir_name:
if not os.path.isabs(install_dir_name):
install_dir_name = os.path.join('{datadir}', install_dir_name)
else:
install_dir_name = '{datadir}'
return self.install_data_impl(sources, kwargs['install_dir'], kwargs['install_mode'], return self.install_data_impl(sources, kwargs['install_dir'], kwargs['install_mode'],
rename, kwargs['install_tag'], install_dir_name, rename, kwargs['install_tag'],
preserve_path=kwargs['preserve_path']) preserve_path=kwargs['preserve_path'])
def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: T.Optional[str], def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: T.Optional[str],
@ -2361,7 +2355,9 @@ class Interpreter(InterpreterBase, HoldableObject):
"""Just the implementation with no validation.""" """Just the implementation with no validation."""
idir = install_dir or '' idir = install_dir or ''
idir_name = install_dir_name or idir idir_name = install_dir_name or idir or '{datadir}'
if isinstance(idir_name, P_OBJ.OptionString):
idir_name = idir_name.optname
dirs = collections.defaultdict(list) dirs = collections.defaultdict(list)
ret_data = [] ret_data = []
if preserve_path: if preserve_path:
@ -2594,10 +2590,13 @@ class Interpreter(InterpreterBase, HoldableObject):
if not idir: if not idir:
raise InterpreterException( raise InterpreterException(
'"install_dir" must be specified when "install" in a configure_file is true') '"install_dir" must be specified when "install" in a configure_file is true')
idir_name = idir
if isinstance(idir_name, P_OBJ.OptionString):
idir_name = idir_name.optname
cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname) cfile = mesonlib.File.from_built_file(ofile_path, ofile_fname)
install_mode = kwargs['install_mode'] install_mode = kwargs['install_mode']
install_tag = kwargs['install_tag'] install_tag = kwargs['install_tag']
self.build.data.append(build.Data([cfile], idir, idir, install_mode, self.subproject, self.build.data.append(build.Data([cfile], idir, idir_name, install_mode, self.subproject,
install_tag=install_tag, data_type='configure')) install_tag=install_tag, data_type='configure'))
return mesonlib.File.from_built_file(self.subdir, output) return mesonlib.File.from_built_file(self.subdir, output)

View File

@ -143,8 +143,6 @@ def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[s
install_path_name = data.install_path_name install_path_name = data.install_path_name
if key == 'headers': # in the headers, install_path_name is the directory if key == 'headers': # in the headers, install_path_name is the directory
install_path_name = os.path.join(install_path_name, os.path.basename(data.path)) install_path_name = os.path.join(install_path_name, os.path.basename(data.path))
elif data_type == 'configure':
install_path_name = os.path.join('{prefix}', install_path_name)
plan[data_type] = plan.get(data_type, {}) plan[data_type] = plan.get(data_type, {})
plan[data_type][data.path] = { plan[data_type][data.path] = {

View File

@ -30,6 +30,7 @@ from ..dependencies.base import process_method_kw
from ..dependencies.detect import get_dep_identifier from ..dependencies.detect import get_dep_identifier
from ..environment import detect_cpu_family from ..environment import detect_cpu_family
from ..interpreter import ExternalProgramHolder, extract_required_kwarg, permitted_dependency_kwargs from ..interpreter import ExternalProgramHolder, extract_required_kwarg, permitted_dependency_kwargs
from ..interpreter import primitives as P_OBJ
from ..interpreter.type_checking import NoneType, PRESERVE_PATH_KW from ..interpreter.type_checking import NoneType, PRESERVE_PATH_KW
from ..interpreterbase import ( from ..interpreterbase import (
noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo, noPosargs, noKwargs, permittedKwargs, ContainerTypeInfo,
@ -514,7 +515,7 @@ class PythonInstallation(ExternalProgramHolder):
if not isinstance(subdir, str): if not isinstance(subdir, str):
raise InvalidArguments('"subdir" argument must be a string.') raise InvalidArguments('"subdir" argument must be a string.')
kwargs['install_dir'] = os.path.join(self.platlib_install_path, subdir) kwargs['install_dir'] = self._get_install_dir_impl(False, subdir)
new_deps = [] new_deps = []
has_pydep = False has_pydep = False
@ -598,11 +599,12 @@ class PythonInstallation(ExternalProgramHolder):
def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]], def install_sources_method(self, args: T.Tuple[T.List[T.Union[str, mesonlib.File]]],
kwargs: 'PyInstallKw') -> 'Data': kwargs: 'PyInstallKw') -> 'Data':
tag = kwargs['install_tag'] or 'runtime' tag = kwargs['install_tag'] or 'runtime'
install_dir = self._get_install_dir_impl(kwargs['pure'], kwargs['subdir'])
return self.interpreter.install_data_impl( return self.interpreter.install_data_impl(
self.interpreter.source_strings_to_files(args[0]), self.interpreter.source_strings_to_files(args[0]),
self._get_install_dir_impl(kwargs['pure'], kwargs['subdir']), install_dir,
mesonlib.FileMode(), rename=None, tag=tag, install_data_type='python', mesonlib.FileMode(), rename=None, tag=tag, install_data_type='python',
install_dir_name=self._get_install_dir_name_impl(kwargs['pure'], kwargs['subdir']), install_dir_name=install_dir.optname,
preserve_path=kwargs['preserve_path']) preserve_path=kwargs['preserve_path'])
@noPosargs @noPosargs
@ -610,12 +612,15 @@ class PythonInstallation(ExternalProgramHolder):
def get_install_dir_method(self, args: T.List['TYPE_var'], kwargs: 'PyInstallKw') -> str: def get_install_dir_method(self, args: T.List['TYPE_var'], kwargs: 'PyInstallKw') -> str:
return self._get_install_dir_impl(kwargs['pure'], kwargs['subdir']) return self._get_install_dir_impl(kwargs['pure'], kwargs['subdir'])
def _get_install_dir_impl(self, pure: bool, subdir: str) -> str: def _get_install_dir_impl(self, pure: bool, subdir: str) -> P_OBJ.OptionString:
return os.path.join( if pure:
self.purelib_install_path if pure else self.platlib_install_path, subdir) base = self.purelib_install_path
name = '{py_purelib}'
else:
base = self.platlib_install_path
name = '{py_platlib}'
def _get_install_dir_name_impl(self, pure: bool, subdir: str) -> str: return P_OBJ.OptionString(os.path.join(base, subdir), os.path.join(name, subdir))
return os.path.join('{py_purelib}' if pure else '{py_platlib}', subdir)
@noPosargs @noPosargs
@noKwargs @noKwargs

View File

@ -4145,11 +4145,11 @@ class AllPlatformTests(BasePlatformTests):
expected = { expected = {
'targets': { 'targets': {
f'{self.builddir}/out1-notag.txt': { f'{self.builddir}/out1-notag.txt': {
'destination': '{prefix}/share/out1-notag.txt', 'destination': '{datadir}/out1-notag.txt',
'tag': None, 'tag': None,
}, },
f'{self.builddir}/out2-notag.txt': { f'{self.builddir}/out2-notag.txt': {
'destination': '{prefix}/share/out2-notag.txt', 'destination': '{datadir}/out2-notag.txt',
'tag': None, 'tag': None,
}, },
f'{self.builddir}/libstatic.a': { f'{self.builddir}/libstatic.a': {
@ -4197,67 +4197,67 @@ class AllPlatformTests(BasePlatformTests):
'tag': 'devel', 'tag': 'devel',
}, },
f'{self.builddir}/out1-custom.txt': { f'{self.builddir}/out1-custom.txt': {
'destination': '{prefix}/share/out1-custom.txt', 'destination': '{datadir}/out1-custom.txt',
'tag': 'custom', 'tag': 'custom',
}, },
f'{self.builddir}/out2-custom.txt': { f'{self.builddir}/out2-custom.txt': {
'destination': '{prefix}/share/out2-custom.txt', 'destination': '{datadir}/out2-custom.txt',
'tag': 'custom', 'tag': 'custom',
}, },
f'{self.builddir}/out3-custom.txt': { f'{self.builddir}/out3-custom.txt': {
'destination': '{prefix}/share/out3-custom.txt', 'destination': '{datadir}/out3-custom.txt',
'tag': 'custom', 'tag': 'custom',
}, },
f'{self.builddir}/subdir/out1.txt': { f'{self.builddir}/subdir/out1.txt': {
'destination': '{prefix}/share/out1.txt', 'destination': '{datadir}/out1.txt',
'tag': None, 'tag': None,
}, },
f'{self.builddir}/subdir/out2.txt': { f'{self.builddir}/subdir/out2.txt': {
'destination': '{prefix}/share/out2.txt', 'destination': '{datadir}/out2.txt',
'tag': None, 'tag': None,
}, },
f'{self.builddir}/out-devel.h': { f'{self.builddir}/out-devel.h': {
'destination': '{prefix}/include/out-devel.h', 'destination': '{includedir}/out-devel.h',
'tag': 'devel', 'tag': 'devel',
}, },
f'{self.builddir}/out3-notag.txt': { f'{self.builddir}/out3-notag.txt': {
'destination': '{prefix}/share/out3-notag.txt', 'destination': '{datadir}/out3-notag.txt',
'tag': None, 'tag': None,
}, },
}, },
'configure': { 'configure': {
f'{self.builddir}/foo-notag.h': { f'{self.builddir}/foo-notag.h': {
'destination': '{prefix}/share/foo-notag.h', 'destination': '{datadir}/foo-notag.h',
'tag': None, 'tag': None,
}, },
f'{self.builddir}/foo2-devel.h': { f'{self.builddir}/foo2-devel.h': {
'destination': '{prefix}/include/foo2-devel.h', 'destination': '{includedir}/foo2-devel.h',
'tag': 'devel', 'tag': 'devel',
}, },
f'{self.builddir}/foo-custom.h': { f'{self.builddir}/foo-custom.h': {
'destination': '{prefix}/share/foo-custom.h', 'destination': '{datadir}/foo-custom.h',
'tag': 'custom', 'tag': 'custom',
}, },
f'{self.builddir}/subdir/foo2.h': { f'{self.builddir}/subdir/foo2.h': {
'destination': '{prefix}/share/foo2.h', 'destination': '{datadir}/foo2.h',
'tag': None, 'tag': None,
}, },
}, },
'data': { 'data': {
f'{testdir}/bar-notag.txt': { f'{testdir}/bar-notag.txt': {
'destination': '{datadir}/share/bar-notag.txt', 'destination': '{datadir}/bar-notag.txt',
'tag': None, 'tag': None,
}, },
f'{testdir}/bar-devel.h': { f'{testdir}/bar-devel.h': {
'destination': '{datadir}/include/bar-devel.h', 'destination': '{includedir}/bar-devel.h',
'tag': 'devel', 'tag': 'devel',
}, },
f'{testdir}/bar-custom.txt': { f'{testdir}/bar-custom.txt': {
'destination': '{datadir}/share/bar-custom.txt', 'destination': '{datadir}/bar-custom.txt',
'tag': 'custom', 'tag': 'custom',
}, },
f'{testdir}/subdir/bar2-devel.h': { f'{testdir}/subdir/bar2-devel.h': {
'destination': '{datadir}/include/bar2-devel.h', 'destination': '{includedir}/bar2-devel.h',
'tag': 'devel', 'tag': 'devel',
}, },
}, },