custom target: Consider all build depends while serializing

Currently, we only consider the build depends of the Executable being
run when serializing custom targets. However, this is not always
sufficient, for example if the executable loads modules at runtime or if
the executable is actually a python script that loads a built module.

For these cases, we need to set PATH on Windows correctly or the custom
target will fail to run at build time complaining about missing DLLs.
pull/2697/head
Nirbheek Chauhan 7 years ago
parent e1bdc098ca
commit 62ba5ca1ec
  1. 30
      mesonbuild/backend/backends.py
  2. 18
      mesonbuild/backend/ninjabackend.py
  3. 3
      mesonbuild/backend/vs2010backend.py
  4. 18
      mesonbuild/build.py

@ -238,8 +238,14 @@ class Backend:
return obj_list
def serialize_executable(self, exe, cmd_args, workdir, env={},
capture=None):
extra_paths=None, capture=None):
import hashlib
if extra_paths is None:
# The callee didn't check if we needed extra paths, so check it here
if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_paths = self.determine_windows_extra_paths(exe, [])
else:
extra_paths = []
# Can't just use exe.name here; it will likely be run more than once
if isinstance(exe, (dependencies.ExternalProgram,
build.BuildTarget, build.CustomTarget)):
@ -272,10 +278,6 @@ class Backend:
exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None)
else:
exe_wrapper = None
if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_paths = self.determine_windows_extra_paths(exe)
else:
extra_paths = []
es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env,
is_cross_built, exe_wrapper, workdir,
extra_paths, capture)
@ -529,23 +531,27 @@ class Backend:
args.append(d_arg)
return args
def determine_windows_extra_paths(self, target):
def determine_windows_extra_paths(self, target, extra_bdeps):
'''On Windows there is no such thing as an rpath.
We must determine all locations of DLLs that this exe
links to and return them so they can be used in unit
tests.'''
if not isinstance(target, build.Executable):
return []
prospectives = target.get_transitive_link_deps()
result = []
prospectives = []
if isinstance(target, build.Executable):
prospectives = target.get_transitive_link_deps()
# External deps
for deppath in self.rpaths_for_bundled_shared_libraries(target):
result.append(os.path.normpath(os.path.join(self.environment.get_build_dir(), deppath)))
for bdep in extra_bdeps:
prospectives += bdep.get_transitive_link_deps()
# Internal deps
for ld in prospectives:
if ld == '' or ld == '.':
continue
dirseg = os.path.join(self.environment.get_build_dir(), self.get_target_dir(ld))
if dirseg not in result:
result.append(dirseg)
for deppath in self.rpaths_for_bundled_shared_libraries(target):
result.append(os.path.normpath(os.path.join(self.environment.get_build_dir(), deppath)))
return result
def write_benchmark_file(self, datafile):
@ -576,7 +582,7 @@ class Backend:
else:
exe_wrapper = None
if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_paths = self.determine_windows_extra_paths(exe)
extra_paths = self.determine_windows_extra_paths(exe, [])
else:
extra_paths = []
cmd_args = []

@ -504,22 +504,30 @@ int dummy;
# Add a dependency on all the outputs of this target
for output in d.get_outputs():
elem.add_dep(os.path.join(self.get_target_dir(d), output))
serialize = False
extra_paths = []
# If the target requires capturing stdout, then use the serialized
# executable wrapper to capture that output and save it to a file.
#
if target.capture:
serialize = True
# If the command line requires a newline, also use the wrapper, as
# ninja does not support them in its build rule syntax.
#
if any('\n' in c for c in cmd):
serialize = True
# Windows doesn't have -rpath, so for EXEs that need DLLs built within
# the project, we need to set PATH so the DLLs are found. We use
# a serialized executable wrapper for that and check if the
# CustomTarget command needs extra paths first.
if (target.capture or any('\n' in c for c in cmd) or
((mesonlib.is_windows() or mesonlib.is_cygwin()) and
self.determine_windows_extra_paths(target.command[0]))):
if mesonlib.is_windows() or mesonlib.is_cygwin():
extra_bdeps = target.get_transitive_build_target_deps()
extra_paths = self.determine_windows_extra_paths(target.command[0], extra_bdeps)
if extra_paths:
serialize = True
if serialize:
exe_data = self.serialize_executable(target.command[0], cmd[1:],
# All targets are built from the build dir
self.environment.get_build_dir(),
extra_paths=extra_paths,
capture=ofilenames[0] if target.capture else None)
cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
cmd_type = 'meson_exe.py custom'

@ -429,9 +429,12 @@ class Vs2010Backend(backends.Backend):
# Always use a wrapper because MSBuild eats random characters when
# there are many arguments.
tdir_abs = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
extra_bdeps = target.get_transitive_build_target_deps()
extra_paths = self.determine_windows_extra_paths(target.command[0], extra_bdeps)
exe_data = self.serialize_executable(target.command[0], cmd[1:],
# All targets run from the target dir
tdir_abs,
extra_paths=extra_paths,
capture=ofilenames[0] if target.capture else None)
wrapper_cmd = self.environment.get_build_command() + ['--internal', 'exe', exe_data]
ET.SubElement(customstep, 'Command').text = ' '.join(self.quote_arguments(wrapper_cmd))

@ -1549,6 +1549,24 @@ class CustomTarget(Target):
deps.append(c)
return deps
def get_transitive_build_target_deps(self):
'''
Recursively fetch the build targets that this custom target depends on,
whether through `command:`, `depends:`, or `sources:` The recursion is
only performed on custom targets.
This is useful for setting PATH on Windows for finding required DLLs.
F.ex, if you have a python script that loads a C module that links to
other DLLs in your project.
'''
bdeps = set()
deps = self.get_target_dependencies()
for d in deps:
if isinstance(d, BuildTarget):
bdeps.add(d)
elif isinstance(d, CustomTarget):
bdeps.update(d.get_transitive_build_target_deps())
return bdeps
def flatten_command(self, cmd):
cmd = listify(cmd, unholder=True)
final_cmd = []

Loading…
Cancel
Save