aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md1
-rw-r--r--mesonbuild/backend/backends.py16
-rw-r--r--mesonbuild/backend/ninjabackend.py6
-rw-r--r--mesonbuild/backend/vs2010backend.py4
-rw-r--r--mesonbuild/build.py56
-rwxr-xr-xrun_project_tests.py16
-rw-r--r--test cases/common/154 shared module resolving symbol in executable/meson.build24
-rw-r--r--test cases/common/154 shared module resolving symbol in executable/module.c16
-rw-r--r--test cases/common/154 shared module resolving symbol in executable/prog.c60
-rw-r--r--test cases/failing/57 link with executable/meson.build4
-rw-r--r--test cases/failing/57 link with executable/module.c4
-rw-r--r--test cases/failing/57 link with executable/prog.c5
-rw-r--r--test cases/windows/12 exe implib/installed_files.txt4
-rw-r--r--test cases/windows/12 exe implib/meson.build7
-rw-r--r--test cases/windows/12 exe implib/prog.c6
15 files changed, 218 insertions, 11 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index e45adf6..261d898 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -248,6 +248,7 @@ Executable supports the following keyword arguments. Note that just like the pos
- `name_suffix` the string that will be used as the extension for the target by overriding the default. By default on Windows this is `exe` and on other platforms it is omitted.
- `build_by_default` causes, when set to true, to have this target be built by default, that is, when invoking plain `ninja`, the default value is true for all built target types, since 0.38.0
- `override_options` takes an array of strings in the same format as `project`'s `default_options` overriding the values of these options for this target only, since 0.40.0
+- `implib` when set to true, an import library is generated for the executable, used when the returned build target object appears elsewhere in `link_with:`, on platforms where this is meaningful (e.g. Windows), since 0.42.0
The list of `sources`, `objects`, and `dependencies` is always flattened, which means you can freely nest and add lists while creating the final list. As a corollary, the best way to handle a 'disabled dependency' is by assigning an empty list `[]` to it and passing it like any other dependency to the `dependencies:` keyword argument.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index cadb655..f967de0 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -140,7 +140,12 @@ class Backend:
return os.path.join(self.get_target_dir(target), link_lib)
elif isinstance(target, build.StaticLibrary):
return os.path.join(self.get_target_dir(target), target.get_filename())
- raise AssertionError('BUG: Tried to link to something that\'s not a library')
+ elif isinstance(target, build.Executable):
+ if target.import_filename:
+ return os.path.join(self.get_target_dir(target), target.get_import_filename())
+ else:
+ return None
+ raise AssertionError('BUG: Tried to link to {!r} which is not linkable'.format(target))
def get_target_dir(self, target):
if self.environment.coredata.get_builtin_option('layout') == 'mirror':
@@ -463,12 +468,13 @@ class Backend:
def build_target_link_arguments(self, compiler, deps):
args = []
for d in deps:
- if not isinstance(d, (build.StaticLibrary, build.SharedLibrary)):
+ if not (d.is_linkable_target()):
raise RuntimeError('Tried to link with a non-library target "%s".' % d.get_basename())
+ d_arg = self.get_target_filename_for_linking(d)
+ if not d_arg:
+ continue
if isinstance(compiler, (compilers.LLVMDCompiler, compilers.DmdDCompiler)):
- d_arg = '-L' + self.get_target_filename_for_linking(d)
- else:
- d_arg = self.get_target_filename_for_linking(d)
+ d_arg = '-L' + d_arg
args.append(d_arg)
return args
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 7f974ee..f10d516 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -693,7 +693,8 @@ int dummy;
# 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) and t.get_import_filename():
+ if (isinstance(t, build.SharedLibrary) or
+ isinstance(t, 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
@@ -2256,6 +2257,9 @@ rule FORTRAN_DEP_HACK
# If gui_app, and that's significant on this platform
if target.gui_app and hasattr(linker, 'get_gui_app_args'):
commands += linker.get_gui_app_args()
+ # If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio)
+ if target.import_filename:
+ commands += linker.gen_import_library_args(os.path.join(target.subdir, target.import_filename))
elif isinstance(target, build.SharedLibrary):
if isinstance(target, build.SharedModule):
commands += linker.get_std_shared_module_link_args()
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 57b0437..4a92155 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -951,10 +951,12 @@ class Vs2010Backend(backends.Backend):
ofile.text = '$(OutDir)%s' % target.get_filename()
subsys = ET.SubElement(link, 'SubSystem')
subsys.text = subsystem
- if isinstance(target, build.SharedLibrary):
+ if (isinstance(target, build.SharedLibrary) or
+ isinstance(target, build.Executable)) and target.get_import_filename():
# DLLs built with MSVC always have an import library except when
# they're data-only DLLs, but we don't support those yet.
ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename()
+ if isinstance(target, build.SharedLibrary):
# Add module definitions file, if provided
if target.vs_module_defs:
relpath = os.path.join(down, target.vs_module_defs.rel_to_builddir(self.build_to_src))
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index fb56cea..df24c7f 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -74,6 +74,9 @@ known_lib_kwargs.update({'version': True, # Only for shared libs
'rust_crate_type': True, # Only for Rust libs
})
+known_exe_kwargs = known_basic_kwargs.copy()
+known_exe_kwargs.update({'implib': True,
+ })
class InvalidArguments(MesonException):
pass
@@ -841,8 +844,8 @@ You probably should put it in link_with instead.''')
for t in flatten(target):
if hasattr(t, 'held_object'):
t = t.held_object
- if not isinstance(t, (StaticLibrary, SharedLibrary)):
- raise InvalidArguments('Link target {!r} is not library.'.format(t))
+ if not t.is_linkable_target():
+ raise InvalidArguments('Link target {!r} is not linkable.'.format(t))
if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic:
msg = "Can't link non-PIC static library {!r} into shared library {!r}. ".format(t.name, self.name)
msg += "Use the 'pic' option to static_library to build with PIC."
@@ -986,6 +989,9 @@ You probably should put it in link_with instead.''')
return True
return False
+ def is_linkable_target(self):
+ return False
+
class Generator:
def __init__(self, args, kwargs):
@@ -1122,9 +1128,49 @@ class Executable(BuildTarget):
self.filename += '.' + self.suffix
self.outputs = [self.filename]
+ # The import library this target will generate
+ self.import_filename = None
+ # The import library that Visual Studio would generate (and accept)
+ self.vs_import_filename = None
+ # The import library that GCC would generate (and prefer)
+ self.gcc_import_filename = None
+
+ # if implib:true appears, this target is linkwith:-able, but that only
+ # means something on Windows platforms.
+ self.is_linkwithable = False
+ if 'implib' in kwargs and kwargs['implib']:
+ self.is_linkwithable = True
+ if for_windows(is_cross, environment) or for_cygwin(is_cross, environment):
+ self.vs_import_filename = '{0}.lib'.format(self.name)
+ self.gcc_import_filename = 'lib{0}.exe.a'.format(self.name)
+
+ if self.get_using_msvc():
+ self.import_filename = self.vs_import_filename
+ else:
+ self.import_filename = self.gcc_import_filename
+
def type_suffix(self):
return "@exe"
+ def check_unknown_kwargs(self, kwargs):
+ self.check_unknown_kwargs_int(kwargs, known_exe_kwargs)
+
+ def get_import_filename(self):
+ """
+ The name of the import library that will be outputted by the compiler
+
+ Returns None if there is no import library required for this platform
+ """
+ return self.import_filename
+
+ def get_import_filenameslist(self):
+ if self.import_filename:
+ return [self.vs_import_filename, self.gcc_import_filename]
+ return []
+
+ def is_linkable_target(self):
+ return self.is_linkwithable
+
class StaticLibrary(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
if 'pic' not in kwargs and 'b_staticpic' in environment.coredata.base_options:
@@ -1176,6 +1222,9 @@ class StaticLibrary(BuildTarget):
else:
raise InvalidArguments('Invalid rust_crate_type "{0}": must be a string.'.format(rust_crate_type))
+ def is_linkable_target(self):
+ return True
+
class SharedLibrary(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
self.soversion = None
@@ -1405,6 +1454,9 @@ class SharedLibrary(BuildTarget):
def type_suffix(self):
return "@sha"
+ def is_linkable_target(self):
+ return True
+
# A shared library that is meant to be used with dlopen rather than linking
# into something else.
class SharedModule(SharedLibrary):
diff --git a/run_project_tests.py b/run_project_tests.py
index 3420946..69a778e 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -183,7 +183,7 @@ def get_relative_files_list_from_dir(fromdir):
paths.append(path)
return paths
-def platform_fix_name(fname):
+def platform_fix_name(fname, compiler):
if '?lib' in fname:
if mesonlib.is_cygwin():
fname = re.sub(r'\?lib(.*)\.dll$', r'cyg\1.dll', fname)
@@ -195,6 +195,16 @@ def platform_fix_name(fname):
if mesonlib.is_windows() or mesonlib.is_cygwin():
return fname + '.exe'
+ if fname.startswith('?msvc:'):
+ fname = fname[6:]
+ if compiler != 'cl':
+ return None
+
+ if fname.startswith('?gcc:'):
+ fname = fname[5:]
+ if compiler == 'cl':
+ return None
+
return fname
def validate_install(srcdir, installdir, compiler):
@@ -210,7 +220,9 @@ def validate_install(srcdir, installdir, compiler):
elif os.path.exists(info_file):
with open(info_file) as f:
for line in f:
- expected[platform_fix_name(line.strip())] = False
+ line = platform_fix_name(line.strip(), compiler)
+ if line:
+ expected[line] = False
# Check if expected files were found
for fname in expected:
file_path = os.path.join(installdir, fname)
diff --git a/test cases/common/154 shared module resolving symbol in executable/meson.build b/test cases/common/154 shared module resolving symbol in executable/meson.build
new file mode 100644
index 0000000..06c9902
--- /dev/null
+++ b/test cases/common/154 shared module resolving symbol in executable/meson.build
@@ -0,0 +1,24 @@
+project('shared module resolving symbol in executable', 'c')
+
+# The shared module contains a reference to the symbol 'func_from_executable',
+# which is always provided by the executable which loads it. This symbol can be
+# resolved at run-time by an ELF loader. But when building PE/COFF objects, all
+# symbols must be resolved at link-time, so an implib is generated for the
+# executable, and the shared module linked with it.
+#
+# See testcase 125 for an example of the more complex portability gymnastics
+# required if we do not know (at link-time) what provides the symbol.
+
+link_flags = []
+if host_machine.system() != 'windows'
+ # Needed to export dynamic symbols from the executable
+ link_flags += ['-rdynamic']
+ # Need to add this manually because Meson won't add it automatically because
+ # it doesn't know that we are loading a module from the build directory.
+ link_flags += ['-Wl,-rpath,' + meson.current_build_dir()]
+endif
+
+dl = meson.get_compiler('c').find_library('dl', required: false)
+e = executable('prog', 'prog.c', dependencies: dl, implib: true, link_args: link_flags)
+m = shared_module('module', 'module.c', link_with: e)
+test('test', e, args: m)
diff --git a/test cases/common/154 shared module resolving symbol in executable/module.c b/test cases/common/154 shared module resolving symbol in executable/module.c
new file mode 100644
index 0000000..64374d5
--- /dev/null
+++ b/test cases/common/154 shared module resolving symbol in executable/module.c
@@ -0,0 +1,16 @@
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+extern int func_from_executable(void);
+
+int DLL_PUBLIC func(void) {
+ return func_from_executable();
+}
diff --git a/test cases/common/154 shared module resolving symbol in executable/prog.c b/test cases/common/154 shared module resolving symbol in executable/prog.c
new file mode 100644
index 0000000..746c192
--- /dev/null
+++ b/test cases/common/154 shared module resolving symbol in executable/prog.c
@@ -0,0 +1,60 @@
+#include <stdio.h>
+#include <assert.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <dlfcn.h>
+#endif
+
+#if defined _WIN32 || defined __CYGWIN__
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+typedef int (*fptr) (void);
+
+int DLL_PUBLIC
+func_from_executable(void)
+{
+ return 42;
+}
+
+int
+main (int argc, char **argv)
+{
+ int expected, actual;
+ fptr importedfunc;
+
+#ifdef _WIN32
+ HMODULE h = LoadLibraryA(argv[1]);
+#else
+ void *h = dlopen(argv[1], RTLD_NOW);
+#endif
+ assert(h != NULL);
+
+#ifdef _WIN32
+ importedfunc = (fptr) GetProcAddress (h, "func");
+#else
+ importedfunc = (fptr) dlsym(h, "func");
+#endif
+ assert(importedfunc != NULL);
+ assert(importedfunc != func_from_executable);
+
+ actual = (*importedfunc)();
+ expected = func_from_executable();
+ assert(actual == expected);
+
+#ifdef _WIN32
+ FreeLibrary(h);
+#else
+ dlclose(h);
+#endif
+
+ return 0;
+}
diff --git a/test cases/failing/57 link with executable/meson.build b/test cases/failing/57 link with executable/meson.build
new file mode 100644
index 0000000..186b3e5
--- /dev/null
+++ b/test cases/failing/57 link with executable/meson.build
@@ -0,0 +1,4 @@
+project('link with exe', 'c')
+
+e = executable('prog', 'prog.c')
+m = shared_module('module', 'module.c', link_with: e)
diff --git a/test cases/failing/57 link with executable/module.c b/test cases/failing/57 link with executable/module.c
new file mode 100644
index 0000000..dc0124a
--- /dev/null
+++ b/test cases/failing/57 link with executable/module.c
@@ -0,0 +1,4 @@
+
+int func(void) {
+ return 42;
+}
diff --git a/test cases/failing/57 link with executable/prog.c b/test cases/failing/57 link with executable/prog.c
new file mode 100644
index 0000000..f3836d7
--- /dev/null
+++ b/test cases/failing/57 link with executable/prog.c
@@ -0,0 +1,5 @@
+int
+main (int argc, char **argv)
+{
+ return 0;
+}
diff --git a/test cases/windows/12 exe implib/installed_files.txt b/test cases/windows/12 exe implib/installed_files.txt
new file mode 100644
index 0000000..d0ea8f6
--- /dev/null
+++ b/test cases/windows/12 exe implib/installed_files.txt
@@ -0,0 +1,4 @@
+usr/bin/prog.exe
+usr/bin/prog.pdb
+?gcc:usr/lib/libprog.exe.a
+?msvc:usr/lib/prog.lib
diff --git a/test cases/windows/12 exe implib/meson.build b/test cases/windows/12 exe implib/meson.build
new file mode 100644
index 0000000..2393e79
--- /dev/null
+++ b/test cases/windows/12 exe implib/meson.build
@@ -0,0 +1,7 @@
+project('wintest', 'c')
+
+# Test that we can produce an implib for an executable on Windows, and that it
+# is installed along with the executable
+
+prog = executable('prog', 'prog.c', install: true, implib: true)
+test('wintest', prog)
diff --git a/test cases/windows/12 exe implib/prog.c b/test cases/windows/12 exe implib/prog.c
new file mode 100644
index 0000000..6d5a9f5
--- /dev/null
+++ b/test cases/windows/12 exe implib/prog.c
@@ -0,0 +1,6 @@
+#include <windows.h>
+
+int __declspec(dllexport)
+main(int argc, char **argv) {
+ return 0;
+}