Moved install data file generation to base class.

This commit is contained in:
Jussi Pakkanen 2018-07-17 13:04:07 +03:00
parent efba193872
commit 808d229c37
2 changed files with 223 additions and 218 deletions

View File

@ -23,7 +23,7 @@ import subprocess
from ..mesonlib import MesonException, OrderedSet from ..mesonlib import MesonException, OrderedSet
from ..mesonlib import classify_unity_sources from ..mesonlib import classify_unity_sources
from ..mesonlib import File from ..mesonlib import File
from ..compilers import CompilerArgs from ..compilers import CompilerArgs, get_macos_dylib_install_name
from collections import OrderedDict from collections import OrderedDict
import shlex import shlex
@ -922,3 +922,222 @@ class Backend:
for s in self.build.postconf_scripts: for s in self.build.postconf_scripts:
cmd = s['exe'] + s['args'] cmd = s['exe'] + s['args']
subprocess.check_call(cmd, env=child_env) subprocess.check_call(cmd, env=child_env)
def create_install_data_files(self):
if self.environment.is_cross_build():
bins = self.environment.cross_info.config['binaries']
if 'strip' not in bins:
mlog.warning('Cross file does not specify strip binary, result will not be stripped.')
strip_bin = None
else:
strip_bin = mesonlib.stringlistify(bins['strip'])
else:
strip_bin = self.environment.native_strip_bin
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
strip_bin,
self.environment.coredata.get_builtin_option('install_umask'),
self.environment.get_build_command() + ['introspect'])
self.generate_depmf_install(d)
self.generate_target_install(d)
self.generate_header_install(d)
self.generate_man_install(d)
self.generate_data_install(d)
self.generate_custom_install_script(d)
self.generate_subdir_install(d)
return d
def get_target_install_dirs(self, t):
# Find the installation directory.
if isinstance(t, build.SharedModule):
default_install_dir = self.environment.get_shared_module_dir()
elif isinstance(t, build.SharedLibrary):
default_install_dir = self.environment.get_shared_lib_dir()
elif isinstance(t, build.StaticLibrary):
default_install_dir = self.environment.get_static_lib_dir()
elif isinstance(t, build.Executable):
default_install_dir = self.environment.get_bindir()
elif isinstance(t, build.CustomTarget):
default_install_dir = None
else:
assert(isinstance(t, build.BuildTarget))
# XXX: Add BuildTarget-specific install dir cases here
default_install_dir = self.environment.get_libdir()
outdirs = t.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
# False (which means we want this specific output out of many
# outputs to not be installed).
custom_install_dir = True
else:
custom_install_dir = False
outdirs[0] = default_install_dir
return outdirs, custom_install_dir
def get_target_link_deps_mappings(self, t, prefix):
'''
On macOS, we need to change the install names of all built libraries
that a target depends on using install_name_tool so that the target
continues to work after installation. For this, we need a dictionary
mapping of the install_name value to the new one, so we can change them
on install.
'''
result = {}
if isinstance(t, build.StaticLibrary):
return result
for ld in t.get_all_link_deps():
if ld is t or not isinstance(ld, build.SharedLibrary):
continue
old = get_macos_dylib_install_name(ld.prefix, ld.name, ld.suffix, ld.soversion)
if old in result:
continue
fname = ld.get_filename()
outdirs, _ = self.get_target_install_dirs(ld)
new = os.path.join(prefix, outdirs[0], fname)
result.update({old: new})
return result
def generate_target_install(self, d):
for t in self.build.get_targets().values():
if not t.should_install():
continue
outdirs, custom_install_dir = self.get_target_install_dirs(t)
# 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:
m = 'Target {!r} has {} outputs: {!r}, but only {} "install_dir"s were found.\n' \
"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))
install_mode = t.get_custom_install_mode()
# Install the target output(s)
if isinstance(t, build.BuildTarget):
should_strip = self.get_option_for_target('strip', t)
# Install primary build output (library/executable/jar, etc)
# Done separately because of strip/aliases/rpath
if outdirs[0] is not False:
mappings = self.get_target_link_deps_mappings(t, d.prefix)
i = TargetInstallData(self.get_target_filename(t), outdirs[0],
t.get_aliases(), should_strip, mappings,
t.install_rpath, install_mode)
d.targets.append(i)
# On toolchains/platforms that use an import library for
# linking (separate from the shared library with all the
# code), we need to install that too (dll.a/.lib).
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)) and t.get_import_filename():
if custom_install_dir:
# If the DLL is installed into a custom directory,
# install the import library into the same place so
# it doesn't go into a surprising place
implib_install_dir = outdirs[0]
else:
implib_install_dir = self.environment.get_import_lib_dir()
# Install the import library.
i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, {}, False, {}, '', install_mode)
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:]):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode)
d.targets.append(i)
elif isinstance(t, build.CustomTarget):
# If only one install_dir is specified, assume that all
# outputs will be installed into it. This is for
# backwards-compatibility and because it makes sense to
# avoid repetition since this is a common use-case.
#
# 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():
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode)
d.targets.append(i)
else:
for output, outdir in zip(t.get_outputs(), outdirs):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode)
d.targets.append(i)
def generate_custom_install_script(self, d):
result = []
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for i in self.build.install_scripts:
exe = i['exe']
args = i['args']
fixed_args = []
for a in args:
a = a.replace('@SOURCE_ROOT@', srcdir)
a = a.replace('@BUILD_ROOT@', builddir)
fixed_args.append(a)
result.append(build.RunScript(exe, fixed_args))
d.install_scripts = result
def generate_header_install(self, d):
incroot = self.environment.get_includedir()
headers = self.build.get_headers()
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for h in headers:
outdir = h.get_custom_install_dir()
if outdir is None:
outdir = os.path.join(incroot, h.get_install_subdir())
for f in h.get_sources():
if not isinstance(f, File):
msg = 'Invalid header type {!r} can\'t be installed'
raise MesonException(msg.format(f))
abspath = f.absolute_path(srcdir, builddir)
i = [abspath, outdir, h.get_custom_install_mode()]
d.headers.append(i)
def generate_man_install(self, d):
manroot = self.environment.get_mandir()
man = self.build.get_man()
for m in man:
for f in m.get_sources():
num = f.split('.')[-1]
subdir = m.get_custom_install_dir()
if subdir is None:
subdir = os.path.join(manroot, 'man' + num)
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
dstabs = os.path.join(subdir, os.path.basename(f.fname) + '.gz')
i = [srcabs, dstabs, m.get_custom_install_mode()]
d.man.append(i)
def generate_data_install(self, d):
data = self.build.get_data()
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for de in data:
assert(isinstance(de, build.Data))
subdir = de.install_dir
if not subdir:
subdir = os.path.join(self.environment.get_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)
i = [src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode]
d.data.append(i)
def generate_subdir_install(self, d):
for sd in self.build.get_install_subdirs():
src_dir = os.path.join(self.environment.get_source_dir(),
sd.source_subdir,
sd.installable_subdir).rstrip('/')
dst_dir = os.path.join(self.environment.get_prefix(),
sd.install_dir)
if not sd.strip_directory:
dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
d.install_subdirs.append([src_dir, dst_dir, sd.install_mode,
sd.exclude])

View File

@ -28,7 +28,7 @@ from .. import build
from .. import mlog from .. import mlog
from .. import dependencies from .. import dependencies
from .. import compilers from .. import compilers
from ..compilers import CompilerArgs, CCompiler, get_macos_dylib_install_name from ..compilers import CompilerArgs, CCompiler
from ..linkers import ArLinker from ..linkers import ArLinker
from ..mesonlib import File, MesonException, OrderedSet from ..mesonlib import File, MesonException, OrderedSet
from ..mesonlib import get_compiler_for_source, has_path_sep from ..mesonlib import get_compiler_for_source, has_path_sep
@ -669,33 +669,13 @@ int dummy;
def generate_install(self, outfile): def generate_install(self, outfile):
install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat') install_data_file = os.path.join(self.environment.get_scratch_dir(), 'install.dat')
if self.environment.is_cross_build():
bins = self.environment.cross_info.config['binaries'] d = self.create_install_data_files()
if 'strip' not in bins:
mlog.warning('Cross file does not specify strip binary, result will not be stripped.')
strip_bin = None
else:
strip_bin = mesonlib.stringlistify(bins['strip'])
else:
strip_bin = self.environment.native_strip_bin
d = InstallData(self.environment.get_source_dir(),
self.environment.get_build_dir(),
self.environment.get_prefix(),
strip_bin,
self.environment.coredata.get_builtin_option('install_umask'),
self.environment.get_build_command() + ['introspect'])
elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY') elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY')
elem.add_dep('all') elem.add_dep('all')
elem.add_item('DESC', 'Installing files.') elem.add_item('DESC', 'Installing files.')
elem.add_item('COMMAND', self.environment.get_build_command() + ['install', '--no-rebuild']) elem.add_item('COMMAND', self.environment.get_build_command() + ['install', '--no-rebuild'])
elem.add_item('pool', 'console') elem.add_item('pool', 'console')
self.generate_depmf_install(d)
self.generate_target_install(d)
self.generate_header_install(d)
self.generate_man_install(d)
self.generate_data_install(d)
self.generate_custom_install_script(d)
self.generate_subdir_install(d)
elem.write(outfile) elem.write(outfile)
# Alias that runs the target defined above # Alias that runs the target defined above
self.create_target_alias('meson-install', outfile) self.create_target_alias('meson-install', outfile)
@ -703,200 +683,6 @@ int dummy;
with open(install_data_file, 'wb') as ofile: with open(install_data_file, 'wb') as ofile:
pickle.dump(d, ofile) pickle.dump(d, ofile)
def get_target_install_dirs(self, t):
# Find the installation directory.
if isinstance(t, build.SharedModule):
default_install_dir = self.environment.get_shared_module_dir()
elif isinstance(t, build.SharedLibrary):
default_install_dir = self.environment.get_shared_lib_dir()
elif isinstance(t, build.StaticLibrary):
default_install_dir = self.environment.get_static_lib_dir()
elif isinstance(t, build.Executable):
default_install_dir = self.environment.get_bindir()
elif isinstance(t, build.CustomTarget):
default_install_dir = None
else:
assert(isinstance(t, build.BuildTarget))
# XXX: Add BuildTarget-specific install dir cases here
default_install_dir = self.environment.get_libdir()
outdirs = t.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
# False (which means we want this specific output out of many
# outputs to not be installed).
custom_install_dir = True
else:
custom_install_dir = False
outdirs[0] = default_install_dir
return outdirs, custom_install_dir
def get_target_link_deps_mappings(self, t, prefix):
'''
On macOS, we need to change the install names of all built libraries
that a target depends on using install_name_tool so that the target
continues to work after installation. For this, we need a dictionary
mapping of the install_name value to the new one, so we can change them
on install.
'''
result = {}
if isinstance(t, build.StaticLibrary):
return result
for ld in t.get_all_link_deps():
if ld is t or not isinstance(ld, build.SharedLibrary):
continue
old = get_macos_dylib_install_name(ld.prefix, ld.name, ld.suffix, ld.soversion)
if old in result:
continue
fname = ld.get_filename()
outdirs, _ = self.get_target_install_dirs(ld)
new = os.path.join(prefix, outdirs[0], fname)
result.update({old: new})
return result
def generate_target_install(self, d):
for t in self.build.get_targets().values():
if not t.should_install():
continue
outdirs, custom_install_dir = self.get_target_install_dirs(t)
# 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:
m = 'Target {!r} has {} outputs: {!r}, but only {} "install_dir"s were found.\n' \
"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))
install_mode = t.get_custom_install_mode()
# Install the target output(s)
if isinstance(t, build.BuildTarget):
should_strip = self.get_option_for_target('strip', t)
# Install primary build output (library/executable/jar, etc)
# Done separately because of strip/aliases/rpath
if outdirs[0] is not False:
mappings = self.get_target_link_deps_mappings(t, d.prefix)
i = TargetInstallData(self.get_target_filename(t), outdirs[0],
t.get_aliases(), should_strip, mappings,
t.install_rpath, install_mode)
d.targets.append(i)
# On toolchains/platforms that use an import library for
# linking (separate from the shared library with all the
# code), we need to install that too (dll.a/.lib).
if isinstance(t, (build.SharedLibrary, build.SharedModule, build.Executable)) and t.get_import_filename():
if custom_install_dir:
# If the DLL is installed into a custom directory,
# install the import library into the same place so
# it doesn't go into a surprising place
implib_install_dir = outdirs[0]
else:
implib_install_dir = self.environment.get_import_lib_dir()
# Install the import library.
i = TargetInstallData(self.get_target_filename_for_linking(t),
implib_install_dir, {}, False, {}, '', install_mode)
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:]):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode)
d.targets.append(i)
elif isinstance(t, build.CustomTarget):
# If only one install_dir is specified, assume that all
# outputs will be installed into it. This is for
# backwards-compatibility and because it makes sense to
# avoid repetition since this is a common use-case.
#
# 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():
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode)
d.targets.append(i)
else:
for output, outdir in zip(t.get_outputs(), outdirs):
# User requested that we not install this output
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode)
d.targets.append(i)
def generate_custom_install_script(self, d):
result = []
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for i in self.build.install_scripts:
exe = i['exe']
args = i['args']
fixed_args = []
for a in args:
a = a.replace('@SOURCE_ROOT@', srcdir)
a = a.replace('@BUILD_ROOT@', builddir)
fixed_args.append(a)
result.append(build.RunScript(exe, fixed_args))
d.install_scripts = result
def generate_header_install(self, d):
incroot = self.environment.get_includedir()
headers = self.build.get_headers()
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for h in headers:
outdir = h.get_custom_install_dir()
if outdir is None:
outdir = os.path.join(incroot, h.get_install_subdir())
for f in h.get_sources():
if not isinstance(f, File):
msg = 'Invalid header type {!r} can\'t be installed'
raise MesonException(msg.format(f))
abspath = f.absolute_path(srcdir, builddir)
i = [abspath, outdir, h.get_custom_install_mode()]
d.headers.append(i)
def generate_man_install(self, d):
manroot = self.environment.get_mandir()
man = self.build.get_man()
for m in man:
for f in m.get_sources():
num = f.split('.')[-1]
subdir = m.get_custom_install_dir()
if subdir is None:
subdir = os.path.join(manroot, 'man' + num)
srcabs = f.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir())
dstabs = os.path.join(subdir, os.path.basename(f.fname) + '.gz')
i = [srcabs, dstabs, m.get_custom_install_mode()]
d.man.append(i)
def generate_data_install(self, d):
data = self.build.get_data()
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for de in data:
assert(isinstance(de, build.Data))
subdir = de.install_dir
if not subdir:
subdir = os.path.join(self.environment.get_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)
i = [src_file.absolute_path(srcdir, builddir), dst_abs, de.install_mode]
d.data.append(i)
def generate_subdir_install(self, d):
for sd in self.build.get_install_subdirs():
src_dir = os.path.join(self.environment.get_source_dir(),
sd.source_subdir,
sd.installable_subdir).rstrip('/')
dst_dir = os.path.join(self.environment.get_prefix(),
sd.install_dir)
if not sd.strip_directory:
dst_dir = os.path.join(dst_dir, os.path.basename(src_dir))
d.install_subdirs.append([src_dir, dst_dir, sd.install_mode,
sd.exclude])
def generate_tests(self, outfile): def generate_tests(self, outfile):
self.serialize_tests() self.serialize_tests()
cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild'] cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild']