mintro: add installed_plan

Signed-off-by: Filipe Laíns <lains@riseup.net>
pull/9230/head
Filipe Laíns 3 years ago committed by Daniel Mensinger
parent be3bd9ea64
commit af8b55d49b
  1. 72
      docs/markdown/IDE-integration.md
  2. 76
      mesonbuild/backend/backends.py
  3. 41
      mesonbuild/build.py
  4. 21
      mesonbuild/interpreter/interpreter.py
  5. 31
      mesonbuild/mintro.py
  6. 7
      mesonbuild/modules/cmake.py
  7. 2
      mesonbuild/modules/gnome.py
  8. 6
      mesonbuild/modules/pkgconfig.py
  9. 6
      mesonbuild/modules/python.py
  10. 0
      test cases/unit/98 install all targets/bar-custom.txt
  11. 0
      test cases/unit/98 install all targets/bar-devel.h
  12. 0
      test cases/unit/98 install all targets/bar-notag.txt
  13. 0
      test cases/unit/98 install all targets/foo.in
  14. 0
      test cases/unit/98 install all targets/foo1-devel.h
  15. 0
      test cases/unit/98 install all targets/lib.c
  16. 0
      test cases/unit/98 install all targets/main.c
  17. 2
      test cases/unit/98 install all targets/meson.build
  18. 0
      test cases/unit/98 install all targets/script.py
  19. 0
      test cases/unit/98 install all targets/subdir/bar2-devel.h
  20. 0
      test cases/unit/98 install all targets/subdir/foo2.in
  21. 0
      test cases/unit/98 install all targets/subdir/foo3-devel.h
  22. 9
      test cases/unit/98 install all targets/subdir/lib.c
  23. 3
      test cases/unit/98 install all targets/subdir/main.c
  24. 21
      test cases/unit/98 install all targets/subdir/meson.build
  25. 7
      test cases/unit/98 install all targets/subdir/script.py
  26. 182
      unittests/allplatformstests.py

@ -32,16 +32,17 @@ to be the last file written.
The `meson-info` directory should contain the following files:
| File | Description |
| ------------------------------ | ------------------------------------------------------------------- |
| `intro-benchmarks.json` | Lists all benchmarks |
| `intro-buildoptions.json` | Contains a full list of Meson configuration options for the project |
| `intro-buildsystem_files.json` | Full list of all Meson build files |
| `intro-dependencies.json` | Lists all dependencies used in the project |
| `intro-installed.json` | Contains mapping of files to their installed location |
| `intro-projectinfo.json` | Stores basic information about the project (name, version, etc.) |
| `intro-targets.json` | Full list of all build targets |
| `intro-tests.json` | Lists all tests with instructions how to run them |
| File | Description |
| ------------------------------ | ----------------------------------------------------------------------------- |
| `intro-benchmarks.json` | Lists all benchmarks |
| `intro-buildoptions.json` | Contains a full list of Meson configuration options for the project |
| `intro-buildsystem_files.json` | Full list of all Meson build files |
| `intro-dependencies.json` | Lists all dependencies used in the project |
| `intro-installed.json` | Contains mapping of files to their installed location |
| `intro-install_plan.json` | Dictionary of data types with the source files and their installation details |
| `intro-projectinfo.json` | Stores basic information about the project (name, version, etc.) |
| `intro-targets.json` | Full list of all build targets |
| `intro-tests.json` | Lists all tests with instructions how to run them |
The content of the JSON files is further specified in the remainder of
this document.
@ -119,6 +120,57 @@ The following table shows all valid types for a target.
| `run` | A Meson run target |
| `jar` | A Java JAR target |
### Install plan
The `intro-install_plan.json` file contains a list of the files that are going
to be installed on the system.
The data contains a list of files grouped by data type. Each file maps to a
dictionary containing the `destination` and `tag` keys, with the key being the
file location at build time. `destination` is the destination path using
placeholders for the base directories. New keys may be added in the future.
```json
{
"targets": {
"/path/to/project/builddir/some_executable": {
"destination": "{bindir}/some_executable",
"tag": "runtime"
},
"/path/to/project/builddir/libsomelib.so": {
"destination": "{libdir_shared}/libsomelib.so",
"tag": "runtime"
}
},
"data": {
"/path/to/project/some_data": {
"destination": "{datadir}/some_data",
"tag": null
}
},
"headers": {
"/path/to/project/some_header.h": {
"destination": "{includedir}/some_header.h",
"tag": "devel"
}
}
}
```
Additionally, the `intro-installed.json` file contains the mapping of the
file path at build time to the absolute system location.
```json
{
"/path/to/project/builddir/some_executable": "/usr/bin/some_executable",
"/path/to/project/builddir/libsomelib.so": "/user/lib/libsomelib.so",
"/path/to/project/some_data": "/usr/share/some_data",
"/path/to/project/some_header.h": "/usr/include/some_header.h"
}
```
### Using `--targets` without a build directory
It is also possible to get most targets without a build directory.

@ -130,12 +130,13 @@ class TargetInstallData:
# TODO: install_mode should just always be a FileMode object
def __init__(self, fname: str, outdir: str, aliases: T.Dict[str, str], strip: bool,
install_name_mappings: T.Mapping[str, str], rpath_dirs_to_remove: T.Set[bytes],
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
@ -147,19 +148,22 @@ class TargetInstallData:
self.tag = tag
class InstallDataBase:
def __init__(self, path: str, install_path: str, install_mode: 'FileMode',
subproject: str, tag: T.Optional[str] = None):
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
class SubdirInstallData(InstallDataBase):
def __init__(self, path: str, install_path: str, install_mode: 'FileMode',
exclude: T.Tuple[T.Set[str], T.Set[str]], subproject: str,
tag: T.Optional[str] = None):
super().__init__(path, install_path, install_mode, subproject, tag)
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)
self.exclude = exclude
class ExecutableSerialisation:
@ -1139,11 +1143,13 @@ class Backend:
return
ifilename = os.path.join(self.environment.get_build_dir(), 'depmf.json')
ofilename = os.path.join(self.environment.get_prefix(), self.build.dep_manifest_name)
out_name = os.path.join('{prefix}', self.build.dep_manifest_name)
mfobj = {'type': 'dependency manifest', 'version': '1.0', 'projects': self.build.dep_manifest}
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, '', tag='devel'))
d.data.append(InstallDataBase(ifilename, ofilename, out_name, None, '',
tag='devel', data_type='depmf'))
def get_regen_filelist(self) -> T.List[str]:
'''List of all files whose alteration means that the build
@ -1501,7 +1507,7 @@ class Backend:
for t in self.build.get_targets().values():
if not t.should_install():
continue
outdirs, custom_install_dir = t.get_install_dir(self.environment)
outdirs, install_dir_name, custom_install_dir = t.get_install_dir(self.environment)
# Sanity-check the outputs and install_dirs
num_outdirs, num_out = len(outdirs), len(t.get_outputs())
if num_outdirs != 1 and num_outdirs != num_out:
@ -1534,8 +1540,8 @@ class Backend:
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,
install_dir_name, t.get_aliases(),
should_strip, mappings, t.rpath_dirs_to_remove,
t.install_rpath, install_mode, t.subproject,
tag=tag)
d.targets.append(i)
@ -1554,7 +1560,8 @@ class Backend:
implib_install_dir = self.environment.get_import_lib_dir()
# 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,
implib_install_dir, install_dir_name,
{}, False, {}, set(), '', install_mode,
t.subproject, optional=isinstance(t, build.SharedModule),
tag='devel')
d.targets.append(i)
@ -1562,6 +1569,7 @@ class Backend:
if not should_strip and t.get_debug_filename():
debug_file = os.path.join(self.get_target_dir(t), t.get_debug_filename())
i = TargetInstallData(debug_file, outdirs[0],
install_dir_name,
{}, False, {}, set(), '',
install_mode, t.subproject,
optional=True, tag='devel')
@ -1573,8 +1581,7 @@ class Backend:
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,
i = TargetInstallData(f, outdir, install_dir_name, {}, False, {}, set(), None,
install_mode, t.subproject,
tag=tag)
d.targets.append(i)
@ -1589,8 +1596,10 @@ class Backend:
if num_outdirs == 1 and num_out > 1:
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,
if not install_dir_name:
dir_name = os.path.join('{prefix}', outdirs[0])
i = TargetInstallData(f, outdirs[0], dir_name, {},
False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default,
tag=tag)
d.targets.append(i)
@ -1600,8 +1609,10 @@ class Backend:
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,
if not install_dir_name:
dir_name = os.path.join('{prefix}', outdir)
i = TargetInstallData(f, outdir, dir_name,
{}, False, {}, set(), None, install_mode,
t.subproject, optional=not t.build_by_default,
tag=tag)
d.targets.append(i)
@ -1616,19 +1627,21 @@ class Backend:
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for h in headers:
outdir = h.get_custom_install_dir()
outdir = outdir_name = h.get_custom_install_dir()
if outdir is None:
subdir = h.get_install_subdir()
if subdir is None:
outdir = incroot
outdir_name = '{includedir}'
else:
outdir = os.path.join(incroot, subdir)
outdir_name = os.path.join('{includedir}', subdir)
for f in h.get_sources():
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, tag='devel')
i = InstallDataBase(abspath, outdir, outdir_name, h.get_custom_install_mode(), h.subproject, tag='devel')
d.headers.append(i)
def generate_man_install(self, d: InstallData) -> None:
@ -1640,15 +1653,16 @@ class Backend:
subdir = m.get_custom_install_dir()
if subdir is None:
if m.locale:
subdir = os.path.join(manroot, m.locale, 'man' + num)
subdir = os.path.join('{mandir}', m.locale, 'man' + num)
else:
subdir = os.path.join(manroot, 'man' + num)
subdir = os.path.join('{mandir}', 'man' + num)
fname = f.fname
if m.locale: # strip locale from file name
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, tag='man')
dstname = os.path.join(subdir, os.path.basename(fname))
dstabs = dstname.replace('{mandir}', manroot)
i = InstallDataBase(srcabs, dstabs, dstname, m.get_custom_install_mode(), m.subproject, tag='man')
d.man.append(i)
def generate_data_install(self, d: InstallData) -> None:
@ -1658,13 +1672,17 @@ class Backend:
for de in data:
assert isinstance(de, build.Data)
subdir = de.install_dir
subdir_name = de.install_dir_name
if not subdir:
subdir = os.path.join(self.environment.get_datadir(), self.interpreter.build.project_name)
subdir_name = os.path.join('{datadir}', self.interpreter.build.project_name)
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)
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, de.install_mode, de.subproject, tag=tag)
i = InstallDataBase(src_file.absolute_path(srcdir, builddir), dst_abs, dstdir_name,
de.install_mode, de.subproject, tag=tag, data_type=de.data_type)
d.data.append(i)
def generate_subdir_install(self, d: InstallData) -> None:
@ -1678,9 +1696,11 @@ class Backend:
sd.installable_subdir).rstrip('/')
dst_dir = os.path.join(self.environment.get_prefix(),
sd.install_dir)
dst_name = os.path.join('{prefix}', sd.install_dir)
if not sd.strip_directory:
dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
i = SubdirInstallData(src_dir, dst_dir, sd.install_mode, sd.exclude, sd.subproject)
dst_name = os.path.join(dst_dir, os.path.basename(src_dir))
i = SubdirInstallData(src_dir, dst_dir, dst_name, sd.install_mode, sd.exclude, sd.subproject)
d.install_subdirs.append(i)
def get_introspection_data(self, target_id: str, target: build.Target) -> T.List['TargetIntrospectionData']:
@ -1740,7 +1760,7 @@ class Backend:
for t in self.build.get_targets().values():
cross_built = not self.environment.machines.matches_build_machine(t.for_machine)
can_run = not cross_built or not self.environment.need_exe_wrapper()
in_default_dir = t.should_install() and not t.get_install_dir(self.environment)[1]
in_default_dir = t.should_install() and not t.get_install_dir(self.environment)[2]
if not can_run or not in_default_dir:
continue
tdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(t))

@ -519,12 +519,12 @@ class Target(HoldableObject):
return NotImplemented
return self.get_id() >= other.get_id()
def get_default_install_dir(self, env: environment.Environment) -> str:
def get_default_install_dir(self, env: environment.Environment) -> T.Tuple[str, str]:
raise NotImplementedError
def get_install_dir(self, environment: environment.Environment) -> T.Tuple[T.Any, bool]:
def get_install_dir(self, environment: environment.Environment) -> T.Tuple[T.Any, str, bool]:
# Find the installation directory.
default_install_dir = self.get_default_install_dir(environment)
default_install_dir, install_dir_name = self.get_default_install_dir(environment)
outdirs = self.get_custom_install_dir()
if outdirs[0] is not None and outdirs[0] != default_install_dir and outdirs[0] is not True:
# Either the value is set to a non-default value, or is set to
@ -534,7 +534,7 @@ class Target(HoldableObject):
else:
custom_install_dir = False
outdirs[0] = default_install_dir
return outdirs, custom_install_dir
return outdirs, install_dir_name, custom_install_dir
def get_basename(self) -> str:
return self.name
@ -942,8 +942,8 @@ class BuildTarget(Target):
result.update(i.get_link_dep_subdirs())
return result
def get_default_install_dir(self, environment: environment.Environment) -> str:
return environment.get_libdir()
def get_default_install_dir(self, environment: environment.Environment) -> T.Tuple[str, str]:
return environment.get_libdir(), '{libdir}'
def get_custom_install_dir(self):
return self.install_dir
@ -1729,8 +1729,8 @@ class Executable(BuildTarget):
# Remember that this exe was returned by `find_program()` through an override
self.was_returned_by_find_program = False
def get_default_install_dir(self, environment: environment.Environment) -> str:
return environment.get_bindir()
def get_default_install_dir(self, environment: environment.Environment) -> T.Tuple[str, str]:
return environment.get_bindir(), '{bindir}'
def description(self):
'''Human friendly description of the executable'''
@ -1806,8 +1806,8 @@ class StaticLibrary(BuildTarget):
def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
return {}
def get_default_install_dir(self, environment):
return environment.get_static_lib_dir()
def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return environment.get_static_lib_dir(), '{libdir_static}'
def type_suffix(self):
return "@sta"
@ -1866,14 +1866,14 @@ class SharedLibrary(BuildTarget):
old = get_target_macos_dylib_install_name(self)
if old not in mappings:
fname = self.get_filename()
outdirs, _ = self.get_install_dir(self.environment)
outdirs, _, _ = self.get_install_dir(self.environment)
new = os.path.join(prefix, outdirs[0], fname)
result.update({old: new})
mappings.update(result)
return mappings
def get_default_install_dir(self, environment):
return environment.get_shared_lib_dir()
def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return environment.get_shared_lib_dir(), '{libdir_shared}'
def determine_filenames(self, env):
"""
@ -2160,8 +2160,8 @@ class SharedModule(SharedLibrary):
super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
self.typename = 'shared module'
def get_default_install_dir(self, environment):
return environment.get_shared_module_dir()
def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return environment.get_shared_module_dir(), '{moduledir_shared}'
class BothLibraries(SecondLevelHolder):
def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None:
@ -2253,8 +2253,8 @@ class CustomTarget(Target, CommandBase):
if unknowns:
mlog.warning('Unknown keyword arguments in target {}: {}'.format(self.name, ', '.join(unknowns)))
def get_default_install_dir(self, environment):
return None
def get_default_install_dir(self, environment) -> T.Tuple[str, str]:
return None, None
def __repr__(self):
repr_str = "<{0} {1}: {2}>"
@ -2656,12 +2656,14 @@ class ConfigurationData(HoldableObject):
# A bit poorly named, but this represents plain data files to copy
# during install.
class Data(HoldableObject):
def __init__(self, sources: T.List[File], install_dir: str,
def __init__(self, sources: T.List[File], install_dir: str, install_dir_name: str,
install_mode: 'FileMode', subproject: str,
rename: T.List[str] = None,
install_tag: T.Optional[str] = None):
install_tag: T.Optional[str] = None,
data_type: str = None):
self.sources = sources
self.install_dir = install_dir
self.install_dir_name = install_dir_name
self.install_mode = install_mode
self.install_tag = install_tag
if rename is None:
@ -2669,6 +2671,7 @@ class Data(HoldableObject):
else:
self.rename = rename
self.subproject = subproject
self.data_type = data_type
class TestSetup:
def __init__(self, exe_wrapper: T.Optional[T.List[str]], gdb: bool,

@ -1926,14 +1926,24 @@ 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,
kwargs['install_tag'])
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'],
rename, kwargs['install_tag'], install_dir_name)
def install_data_impl(self, sources: T.List[mesonlib.File], install_dir: str,
install_mode: FileMode, rename: T.Optional[str],
tag: T.Optional[str]) -> build.Data:
tag: T.Optional[str],
install_dir_name: T.Optional[str] = None,
install_data_type: T.Optional[str] = None) -> build.Data:
"""Just the implementation with no validation."""
data = build.Data(sources, install_dir, install_mode, self.subproject, rename, tag)
data = build.Data(sources, install_dir, install_dir_name or install_dir, install_mode,
self.subproject, rename, tag, install_data_type)
self.build.data.append(data)
return data
@ -2158,7 +2168,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self
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))
self.build.data.append(build.Data([cfile], idir, idir, install_mode, self.subproject,
install_tag=install_tag, data_type='configure'))
return mesonlib.File.from_built_file(self.subdir, output)
def extract_incdirs(self, kwargs):

@ -74,6 +74,7 @@ def get_meson_introspection_types(coredata: T.Optional[cdata.CoreData] = None,
('dependencies', IntroCommand('List external dependencies', func=lambda: list_deps(coredata), no_bd=list_deps_from_source)),
('scan_dependencies', IntroCommand('Scan for dependencies used in the meson.build file', no_bd=list_deps_from_source)),
('installed', IntroCommand('List all installed files and directories', func=lambda: list_installed(installdata))),
('install_plan', IntroCommand('List all installed files and directories with their details', func=lambda: list_install_plan(installdata))),
('projectinfo', IntroCommand('Information about projects', func=lambda: list_projinfo(builddata), no_bd=list_projinfo_from_source)),
('targets', IntroCommand('List top level targets', func=lambda: list_targets(builddata, installdata, backend), no_bd=list_targets_from_source)),
('tests', IntroCommand('List all unit tests', func=lambda: list_tests(testdata))),
@ -119,6 +120,36 @@ def list_installed(installdata: backends.InstallData) -> T.Dict[str, str]:
res[i.path] = os.path.join(installdata.prefix, i.install_path)
return res
def list_install_plan(installdata: backends.InstallData) -> T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]]:
plan = {
'targets': {
os.path.join(installdata.build_dir, target.fname): {
'destination': target.out_name,
'tag': target.tag or None,
}
for target in installdata.targets
},
} # type: T.Dict[str, T.Dict[str, T.Dict[str, T.Optional[str]]]]
for key, data_list in {
'data': installdata.data,
'man': installdata.man,
'headers': installdata.headers,
}.items():
for data in data_list:
data_type = data.data_type or key
install_path_name = data.install_path_name
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))
elif data_type == 'configure':
install_path_name = os.path.join('{prefix}', install_path_name)
plan[data_type] = plan.get(data_type, {})
plan[data_type][data.path] = {
'destination': install_path_name,
'tag': data.tag or None,
}
return plan
def get_target_dir(coredata: cdata.CoreData, subdir: str) -> str:
if coredata.get_option(OptionKey('layout')) == 'flat':
return 'meson-out'

@ -272,9 +272,10 @@ class CmakeModule(ExtensionModule):
if not self.detect_cmake():
raise mesonlib.MesonException('Unable to find cmake')
pkgroot = kwargs.get('install_dir', None)
pkgroot = pkgroot_name = kwargs.get('install_dir', None)
if pkgroot is None:
pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'cmake', name)
pkgroot_name = os.path.join('{libdir}', 'cmake', name)
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
@ -290,7 +291,7 @@ class CmakeModule(ExtensionModule):
}
mesonlib.do_conf_file(template_file, version_file, conf, 'meson')
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), version_file)], pkgroot, None, state.subproject)
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), version_file)], pkgroot, pkgroot_name, None, state.subproject)
return ModuleReturnValue(res, [res])
def create_package_file(self, infile, outfile, PACKAGE_RELATIVE_PATH, extra, confdata):
@ -375,7 +376,7 @@ class CmakeModule(ExtensionModule):
if conffile not in self.interpreter.build_def_files:
self.interpreter.build_def_files.append(conffile)
res = build.Data([mesonlib.File(True, ofile_path, ofile_fname)], install_dir, None, state.subproject)
res = build.Data([mesonlib.File(True, ofile_path, ofile_fname)], install_dir, install_dir, None, state.subproject)
self.interpreter.build.data.append(res)
return res

@ -1728,7 +1728,7 @@ G_END_DECLS'''
with open(fname, 'w', encoding='utf-8') as ofile:
for package in packages:
ofile.write(package + '\n')
return build.Data([mesonlib.File(True, outdir, fname)], install_dir, None, state.subproject)
return build.Data([mesonlib.File(True, outdir, fname)], install_dir, install_dir, None, state.subproject)
def _get_vapi_link_with(self, target):
link_with = []

@ -537,18 +537,20 @@ class PkgConfigModule(ExtensionModule):
unescaped_variables = parse_variable_list(unescaped_variables)
pcfile = filebase + '.pc'
pkgroot = kwargs.get('install_dir', default_install_dir)
pkgroot = pkgroot_name = kwargs.get('install_dir', default_install_dir)
if pkgroot is None:
if mesonlib.is_freebsd():
pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('prefix')), 'libdata', 'pkgconfig')
pkgroot_name = os.path.join('{prefix}', 'libdata', 'pkgconfig')
else:
pkgroot = os.path.join(state.environment.coredata.get_option(mesonlib.OptionKey('libdir')), 'pkgconfig')
pkgroot_name = os.path.join('{libdir}', 'pkgconfig')
if not isinstance(pkgroot, str):
raise mesonlib.MesonException('Install_dir must be a string.')
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, install_tag='devel')
res = build.Data([mesonlib.File(True, state.environment.get_scratch_dir(), pcfile)], pkgroot, pkgroot_name, 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')

@ -493,7 +493,8 @@ class PythonInstallation(ExternalProgramHolder):
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(), rename=None, tag=tag)
mesonlib.FileMode(), rename=None, tag=tag, install_data_type='python',
install_dir_name=self._get_install_dir_name_impl(kwargs['pure'], kwargs['subdir']))
@noPosargs
@typed_kwargs('python_installation.install_dir', _PURE_KW, _SUBDIR_KW)
@ -504,6 +505,9 @@ class PythonInstallation(ExternalProgramHolder):
return os.path.join(
self.purelib_install_path if pure else self.platlib_install_path, subdir)
def _get_install_dir_name_impl(self, pure: bool, subdir: str) -> str:
return os.path.join('{py_purelib}' if pure else '{py_platlib}', subdir)
@noPosargs
@noKwargs
def language_version_method(self, args: T.List['TYPE_var'], kwargs: 'TYPE_kwargs') -> str:

@ -1,5 +1,7 @@
project('install tag', 'c')
subdir('subdir')
# Those files should not be tagged
configure_file(input: 'foo.in', output: 'foo-notag.h',
configuration: {'foo': 'bar'},

@ -0,0 +1,9 @@
#if defined _WIN32 || defined __CYGWIN__
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC
#endif
int DLL_PUBLIC foo(void) {
return 0;
}

@ -0,0 +1,3 @@
int main(int argc, char *argv[]) {
return 0;
}

@ -0,0 +1,21 @@
configure_file(input: 'foo2.in', output: 'foo2.h',
configuration: {'foo': 'bar'},
install_dir: get_option('datadir'),
install: true,
)
custom_target('ct4',
output: ['out1.txt', 'out2.txt'],
command: ['script.py', '@OUTPUT@'],
install_dir: get_option('datadir'),
install: true,
)
install_headers('foo3-devel.h')
install_data('bar2-devel.h',
install_dir: get_option('includedir'),
)
executable('app2', 'main.c',
install: true,
)
both_libraries('both2', 'lib.c',
install: true,
)

@ -0,0 +1,7 @@
#!/usr/bin/env python3
import sys
for f in sys.argv[1:]:
with open(f, 'w') as f:
pass

@ -52,7 +52,7 @@ from mesonbuild.compilers import (
)
from mesonbuild.dependencies import PkgConfigDependency
from mesonbuild.build import Target, ConfigurationData
from mesonbuild.build import Target, ConfigurationData, Executable, SharedLibrary, StaticLibrary
import mesonbuild.modules.pkgconfig
from mesonbuild.scripts import destdir_join
@ -2837,6 +2837,7 @@ class AllPlatformTests(BasePlatformTests):
'buildsystem_files',
'dependencies',
'installed',
'install_plan',
'projectinfo',
'targets',
'tests',
@ -3772,7 +3773,7 @@ class AllPlatformTests(BasePlatformTests):
self.assertEqual(sorted(link_args), sorted(['-flto']))
def test_install_tag(self) -> None:
testdir = os.path.join(self.unit_test_dir, '98 install tag')
testdir = os.path.join(self.unit_test_dir, '98 install all targets')
self.init(testdir)
self.build()
@ -3804,29 +3805,36 @@ class AllPlatformTests(BasePlatformTests):
expected_devel = expected_common | {
Path(installpath, 'usr/include'),
Path(installpath, 'usr/include/foo1-devel.h'),
Path(installpath, 'usr/include/bar-devel.h'),
Path(installpath, 'usr/include/bar2-devel.h'),
Path(installpath, 'usr/include/foo1-devel.h'),
Path(installpath, 'usr/include/foo2-devel.h'),
Path(installpath, 'usr/include/foo3-devel.h'),
Path(installpath, 'usr/include/out-devel.h'),
Path(installpath, 'usr/lib'),
Path(installpath, 'usr/lib/libstatic.a'),
Path(installpath, 'usr/lib/libboth.a'),
Path(installpath, 'usr/lib/libboth2.a'),
}
if cc.get_id() in {'msvc', 'clang-cl'}:
expected_devel |= {
Path(installpath, 'usr/bin'),
Path(installpath, 'usr/bin/app.pdb'),
Path(installpath, 'usr/bin/app2.pdb'),
Path(installpath, 'usr/bin/both.pdb'),
Path(installpath, 'usr/bin/both2.pdb'),
Path(installpath, 'usr/bin/bothcustom.pdb'),
Path(installpath, 'usr/bin/shared.pdb'),
Path(installpath, 'usr/lib/both.lib'),
Path(installpath, 'usr/lib/both2.lib'),
Path(installpath, 'usr/lib/bothcustom.lib'),
Path(installpath, 'usr/lib/shared.lib'),
}
elif is_windows() or is_cygwin():
expected_devel |= {
Path(installpath, 'usr/lib/libboth.dll.a'),
Path(installpath, 'usr/lib/libboth2.dll.a'),
Path(installpath, 'usr/lib/libshared.dll.a'),
Path(installpath, 'usr/lib/libbothcustom.dll.a'),
}
@ -3834,8 +3842,10 @@ class AllPlatformTests(BasePlatformTests):
expected_runtime = expected_common | {
Path(installpath, 'usr/bin'),
Path(installpath, 'usr/bin/' + exe_name('app')),
Path(installpath, 'usr/bin/' + exe_name('app2')),
Path(installpath, 'usr/' + shared_lib_name('shared')),
Path(installpath, 'usr/' + shared_lib_name('both')),
Path(installpath, 'usr/' + shared_lib_name('both2')),
}
expected_custom = expected_common | {
@ -3863,6 +3873,9 @@ class AllPlatformTests(BasePlatformTests):
Path(installpath, 'usr/share/out1-notag.txt'),
Path(installpath, 'usr/share/out2-notag.txt'),
Path(installpath, 'usr/share/out3-notag.txt'),
Path(installpath, 'usr/share/foo2.h'),
Path(installpath, 'usr/share/out1.txt'),
Path(installpath, 'usr/share/out2.txt'),
}
def do_install(tags=None):
@ -3876,3 +3889,166 @@ class AllPlatformTests(BasePlatformTests):
self.assertEqual(sorted(expected_custom), do_install('custom'))
self.assertEqual(sorted(expected_runtime_custom), do_install('runtime,custom'))
self.assertEqual(sorted(expected_all), do_install())
def test_introspect_install_plan(self):
testdir = os.path.join(self.unit_test_dir, '98 install all targets')
introfile = os.path.join(self.builddir, 'meson-info', 'intro-install_plan.json')
self.init(testdir)
self.assertPathExists(introfile)
with open(introfile, encoding='utf-8') as fp:
res = json.load(fp)
env = get_fake_env(testdir, self.builddir, self.prefix)
def output_name(name, type_):
return type_(name=name, subdir=None, subproject=None,
for_machine=MachineChoice.HOST, sources=[],
objects=[], environment=env, kwargs={}).filename
shared_lib_name = lambda name: output_name(name, SharedLibrary)
static_lib_name = lambda name: output_name(name, StaticLibrary)
exe_name = lambda name: output_name(name, Executable)
expected = {
'targets': {
f'{self.builddir}/out1-notag.txt': {
'destination': '{prefix}/share/out1-notag.txt',
'tag': None,
},
f'{self.builddir}/out2-notag.txt': {
'destination': '{prefix}/share/out2-notag.txt',
'tag': None,
},
f'{self.builddir}/libstatic.a': {
'destination': '{libdir_static}/libstatic.a',
'tag': 'devel',
},
f'{self.builddir}/' + exe_name('app'): {
'destination': '{bindir}/' + exe_name('app'),
'tag': 'runtime',
},
f'{self.builddir}/subdir/' + exe_name('app2'): {
'destination': '{bindir}/' + exe_name('app2'),
'tag': 'runtime',
},
f'{self.builddir}/' + shared_lib_name('shared'): {
'destination': '{libdir_shared}/' + shared_lib_name('shared'),
'tag': 'runtime',
},
f'{self.builddir}/' + shared_lib_name('both'): {
'destination': '{libdir_shared}/' + shared_lib_name('both'),
'tag': 'runtime',
},
f'{self.builddir}/' + static_lib_name('both'): {
'destination': '{libdir_static}/' + static_lib_name('both'),
'tag': 'devel',
},
f'{self.builddir}/' + shared_lib_name('bothcustom'): {
'destination': '{libdir_shared}/' + shared_lib_name('bothcustom'),
'tag': 'custom',
},
f'{self.builddir}/' + static_lib_name('bothcustom'): {
'destination': '{libdir_static}/' + static_lib_name('bothcustom'),
'tag': 'custom',
},
f'{self.builddir}/subdir/' + shared_lib_name('both2'): {
'destination': '{libdir_shared}/' + shared_lib_name('both2'),
'tag': 'runtime',
},
f'{self.builddir}/subdir/' + static_lib_name('both2'): {
'destination': '{libdir_static}/' + static_lib_name('both2'),
'tag': 'devel',
},
f'{self.builddir}/out1-custom.txt': {
'destination': '{prefix}/share/out1-custom.txt',
'tag': 'custom',
},
f'{self.builddir}/out2-custom.txt': {
'destination': '{prefix}/share/out2-custom.txt',
'tag': 'custom',
},
f'{self.builddir}/out3-custom.txt': {
'destination': '{prefix}/share/out3-custom.txt',
'tag': 'custom',
},
f'{self.builddir}/subdir/out1.txt': {
'destination': '{prefix}/share/out1.txt',
'tag': None,
},
f'{self.builddir}/subdir/out2.txt': {
'destination': '{prefix}/share/out2.txt',
'tag': None,
},
f'{self.builddir}/out-devel.h': {
'destination': '{prefix}/include/out-devel.h',
'tag': 'devel',
},
f'{self.builddir}/out3-notag.txt': {
'destination': '{prefix}/share/out3-notag.txt',
'tag': None,
},
},
'configure': {
f'{self.builddir}/foo-notag.h': {
'destination': '{prefix}/share/foo-notag.h',
'tag': None,
},
f'{self.builddir}/foo2-devel.h': {
'destination': '{prefix}/include/foo2-devel.h',
'tag': 'devel',
},
f'{self.builddir}/foo-custom.h': {
'destination': '{prefix}/share/foo-custom.h',
'tag': 'custom',
},
f'{self.builddir}/subdir/foo2.h': {
'destination': '{prefix}/share/foo2.h',
'tag': None,
},
},
'data': {
f'{testdir}/bar-notag.txt': {
'destination': '{datadir}/share/bar-notag.txt',
'tag': None,
},
f'{testdir}/bar-devel.h': {
'destination': '{datadir}/include/bar-devel.h',
'tag': 'devel',
},
f'{testdir}/bar-custom.txt': {
'destination': '{datadir}/share/bar-custom.txt',
'tag': 'custom',
},
f'{testdir}/subdir/bar2-devel.h': {
'destination': '{datadir}/include/bar2-devel.h',
'tag': 'devel',
},
},
'headers': {
f'{testdir}/foo1-devel.h': {
'destination': '{includedir}/foo1-devel.h',
'tag': 'devel',
},
f'{testdir}/subdir/foo3-devel.h': {
'destination': '{includedir}/foo3-devel.h',
'tag': 'devel',
},
}
}
fix_path = lambda path: os.path.sep.join(path.split('/'))
expected_fixed = {
data_type: {
fix_path(source): {
key: fix_path(value) if key == 'destination' else value
for key, value in attributes.items()
}
for source, attributes in files.items()
}
for data_type, files in expected.items()
}
for data_type, files in expected_fixed.items():
for file, details in files.items():
with self.subTest(key='{}.{}'.format(data_type, file)):
self.assertEqual(res[data_type][file], details)

Loading…
Cancel
Save