aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2016-07-13 22:18:50 +0300
committerGitHub <noreply@github.com>2016-07-13 22:18:50 +0300
commit64919b1c7463d2adceec961350f33707cae9718b (patch)
tree1be6f8d33b822ab20d443fdde18a107f3b45c5a6
parent38a896ae5154b54865db57bbd8f3ddc69a8083ba (diff)
parentf8d75883724e115083bff1befa75def42be282c5 (diff)
downloadmeson-64919b1c7463d2adceec961350f33707cae9718b.zip
meson-64919b1c7463d2adceec961350f33707cae9718b.tar.gz
meson-64919b1c7463d2adceec961350f33707cae9718b.tar.bz2
Merge pull request #417 from nirbheek/dll-paths
Fix filenames and paths used in DLL shared library generation
-rw-r--r--mesonbuild/backend/backends.py20
-rw-r--r--mesonbuild/backend/ninjabackend.py35
-rw-r--r--mesonbuild/backend/vs2010backend.py229
-rw-r--r--mesonbuild/backend/xcodebackend.py8
-rw-r--r--mesonbuild/build.py310
-rw-r--r--mesonbuild/compilers.py35
-rw-r--r--mesonbuild/environment.py87
-rw-r--r--mesonbuild/mesonlib.py10
-rw-r--r--mesonbuild/scripts/meson_install.py2
-rwxr-xr-xrun_tests.py70
-rw-r--r--test cases/common/27 library versions/installed_files.txt4
-rw-r--r--test cases/common/27 library versions/meson.build11
-rw-r--r--test cases/common/27 library versions/subdir/meson.build8
-rw-r--r--test cases/common/46 library chain/installed_files.txt5
-rw-r--r--test cases/common/46 library chain/subdir/meson.build2
-rw-r--r--test cases/common/46 library chain/subdir/subdir2/meson.build2
-rw-r--r--test cases/common/46 library chain/subdir/subdir3/meson.build2
-rw-r--r--test cases/common/49 subproject/installed_files.txt3
-rw-r--r--test cases/common/49 subproject/subprojects/sublib/meson.build2
-rw-r--r--test cases/common/51 pkgconfig-gen/installed_files.txt1
-rw-r--r--test cases/common/51 pkgconfig-gen/meson.build2
-rw-r--r--test cases/common/52 custom install dirs/installed_files.txt2
-rw-r--r--test cases/common/6 linkshared/installed_files.txt3
-rw-r--r--test cases/common/6 linkshared/meson.build2
-rw-r--r--test cases/common/60 install script/installed_files.txt2
-rw-r--r--test cases/common/60 install script/meson.build4
-rw-r--r--test cases/common/60 install script/myinstall.bat3
-rwxr-xr-xtest cases/common/60 install script/myinstall.sh4
-rw-r--r--test cases/common/60 install script/no-installed-files0
-rw-r--r--test cases/common/67 foreach/installed_files.txt6
-rw-r--r--test cases/common/76 configure file in custom target/src/meson.build16
-rw-r--r--test cases/common/8 install/installed_files.txt3
-rw-r--r--test cases/common/8 install/meson.build1
-rw-r--r--test cases/common/8 install/shar.c1
-rw-r--r--test cases/csharp/2 library/installed_files.txt2
-rw-r--r--test cases/linuxlike/7 library versions/installed_files.txt9
-rw-r--r--test cases/linuxlike/7 library versions/lib.c3
-rw-r--r--test cases/linuxlike/7 library versions/meson.build18
-rw-r--r--test cases/linuxlike/8 subproject library install/installed_files.txt3
-rw-r--r--test cases/linuxlike/8 subproject library install/meson.build6
-rw-r--r--test cases/linuxlike/8 subproject library install/subprojects/sublib/include/subdefs.h21
-rw-r--r--test cases/linuxlike/8 subproject library install/subprojects/sublib/meson.build10
-rw-r--r--test cases/linuxlike/8 subproject library install/subprojects/sublib/sublib.c5
-rw-r--r--test cases/osx/2 library versions/installed_files.txt4
-rw-r--r--test cases/osx/2 library versions/lib.c3
-rw-r--r--test cases/osx/2 library versions/meson.build18
-rw-r--r--test cases/rust/1 basic/installed_files.txt2
-rw-r--r--test cases/rust/2 sharedlib/installed_files.txt2
-rw-r--r--test cases/rust/3 staticlib/installed_files.txt2
-rw-r--r--test cases/windows/7 mingw dll versioning/installed_files.txt4
-rw-r--r--test cases/windows/7 mingw dll versioning/lib.c6
-rw-r--r--test cases/windows/7 mingw dll versioning/meson.build17
-rw-r--r--test cases/windows/7 mingw dll versioning/no-installed-files0
-rw-r--r--test cases/windows/8 msvc dll versioning/installed_files.txt4
-rw-r--r--test cases/windows/8 msvc dll versioning/lib.c6
-rw-r--r--test cases/windows/8 msvc dll versioning/meson.build16
-rw-r--r--test cases/windows/8 msvc dll versioning/no-installed-files0
57 files changed, 808 insertions, 248 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index fcc3d3b..d2693b2 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -110,9 +110,13 @@ class Backend():
# On some platforms (msvc for instance), the file that is used for
# dynamic linking is not the same as the dynamic library itself. This
# file is called an import library, and we want to link against that.
- # On platforms where this distinction is not important, the import
- # library is the same as the dynamic library itself.
- return os.path.join(self.get_target_dir(target), target.get_import_filename())
+ # On all other platforms, we link to the library directly.
+ if isinstance(target, build.SharedLibrary):
+ link_lib = target.get_import_filename() or target.get_filename()
+ 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')
def get_target_dir(self, target):
if self.environment.coredata.get_builtin_option('layout') == 'mirror':
@@ -496,11 +500,19 @@ class Backend():
if isinstance(i, build.Executable):
cmd += self.exe_object_to_cmd_array(i)
continue
- if isinstance(i, build.CustomTarget):
+ elif isinstance(i, build.CustomTarget):
# GIR scanner will attempt to execute this binary but
# it assumes that it is in path, so always give it a full path.
tmp = i.get_filename()[0]
i = os.path.join(self.get_target_dir(i), tmp)
+ elif isinstance(i, mesonlib.File):
+ i = os.path.join(i.subdir, i.fname)
+ if absolute_paths:
+ i = os.path.join(self.environment.get_build_dir(), i)
+ # FIXME: str types are blindly added and ignore the 'absolute_paths' argument
+ elif not isinstance(i, str):
+ err_msg = 'Argument {0} is of unknown type {1}'
+ raise RuntimeError(err_msg.format(str(i), str(type(i))))
for (j, src) in enumerate(srcs):
i = i.replace('@INPUT%d@' % j, src)
for (j, res) in enumerate(ofilenames):
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index a1bccc2..b84a144 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -496,18 +496,33 @@ int dummy;
pickle.dump(d, ofile)
def generate_target_install(self, d):
- libdir = self.environment.get_libdir()
- bindir = self.environment.get_bindir()
-
should_strip = self.environment.coredata.get_builtin_option('strip')
for t in self.build.get_targets().values():
if t.should_install():
+ # Find the installation directory
outdir = t.get_custom_install_dir()
- if outdir is None:
- if isinstance(t, build.Executable):
- outdir = bindir
- else:
- outdir = libdir
+ if outdir is not None:
+ pass
+ elif isinstance(t, build.SharedLibrary):
+ # For toolchains/platforms that need an import library for
+ # linking (separate from the shared library with all the
+ # code), we need to install the import library (dll.a/.lib)
+ if t.get_import_filename():
+ # Install the import library.
+ i = [self.get_target_filename_for_linking(t),
+ self.environment.get_import_lib_dir(),
+ # It has no aliases, should not be stripped, and
+ # doesn't have an install_rpath
+ [], False, '']
+ d.targets.append(i)
+ outdir = self.environment.get_shared_lib_dir()
+ elif isinstance(t, build.SharedLibrary):
+ outdir = self.environment.get_static_lib_dir()
+ elif isinstance(t, build.Executable):
+ outdir = self.environment.get_bindir()
+ else:
+ # XXX: Add BuildTarget-specific install dir cases here
+ outdir = self.environment.get_libdir()
i = [self.get_target_filename(t), outdir, t.get_aliaslist(),\
should_strip, t.install_rpath]
d.targets.append(i)
@@ -1665,8 +1680,12 @@ rule FORTRAN_DEP_HACK
else:
soversion = None
commands += linker.get_soname_args(target.name, abspath, soversion)
+ # This is only visited when using the Visual Studio toolchain
if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
+ # This is only visited when building for Windows using either MinGW/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.StaticLibrary):
commands += linker.get_std_link_args()
else:
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index d262e6b..12f224c 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -28,6 +28,27 @@ import xml.dom.minidom
from ..mesonlib import MesonException
from ..environment import Environment
+def split_o_flags_args(args):
+ """
+ Splits any /O args and returns them. Does not take care of flags overriding
+ previous ones. Skips non-O flag arguments.
+
+ ['/Ox', '/Ob1'] returns ['/Ox', '/Ob1']
+ ['/Oxj', '/MP'] returns ['/Ox', '/Oj']
+ """
+ o_flags = []
+ for arg in args:
+ if not arg.startswith('/O'):
+ continue
+ flags = list(arg[2:])
+ # Assume that this one can't be clumped with the others since it takes
+ # an argument itself
+ if 'b' in flags:
+ o_flags.append(arg)
+ else:
+ o_flags += ['/O' + f for f in flags]
+ return o_flags
+
class RegenInfo():
def __init__(self, source_dir, build_dir, depfiles):
self.source_dir = source_dir
@@ -76,9 +97,14 @@ class Vs2010Backend(backends.Backend):
outputs = []
custom_target_include_dirs = []
custom_target_output_files = []
+ target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target))
+ down = self.target_to_build_root(target)
for genlist in target.get_generated_sources():
if isinstance(genlist, build.CustomTarget):
- custom_target_output_files += [os.path.join(self.get_target_dir(genlist), i) for i in genlist.output]
+ for i in genlist.output:
+ # Path to the generated source from the current vcxproj dir via the build root
+ ipath = os.path.join(down, self.get_target_dir(genlist), i)
+ custom_target_output_files.append(ipath)
idir = self.relpath(self.get_target_dir(genlist), self.get_target_dir(target))
if idir not in custom_target_include_dirs:
custom_target_include_dirs.append(idir)
@@ -89,7 +115,6 @@ class Vs2010Backend(backends.Backend):
outfilelist = genlist.get_outfilelist()
exe_arr = self.exe_object_to_cmd_array(exe)
base_args = generator.get_arglist()
- target_private_dir = self.relpath(self.get_target_private_dir(target), self.get_target_dir(target))
for i in range(len(infilelist)):
if len(infilelist) == len(outfilelist):
sole_output = os.path.join(target_private_dir, outfilelist[i])
@@ -123,7 +148,17 @@ class Vs2010Backend(backends.Backend):
def generate(self, interp):
self.resolve_source_conflicts()
self.interpreter = interp
- self.platform = 'Win32'
+ target_machine = self.interpreter.builtin['target_machine'].cpu_family_method(None, None)
+ if target_machine.endswith('64'):
+ # amd64 or x86_64
+ self.platform = 'x64'
+ elif target_machine == 'x86':
+ # x86
+ self.platform = 'Win32'
+ elif 'arm' in target_machine.lower():
+ self.platform = 'ARM'
+ else:
+ raise MesonException('Unsupported Visual Studio platform: ' + target_machine)
self.buildtype = self.environment.coredata.get_builtin_option('buildtype')
sln_filename = os.path.join(self.environment.get_build_dir(), self.build.project_name + '.sln')
projlist = self.generate_projects()
@@ -404,6 +439,37 @@ class Vs2010Backend(backends.Backend):
def quote_define_cmdline(cls, arg):
return re.sub(r'^([-/])D(.*?)="(.*)"$', r'\1D\2=\"\3\"', arg)
+ @staticmethod
+ def split_link_args(args):
+ """
+ Split a list of link arguments into three lists:
+ * library search paths
+ * library filenames (or paths)
+ * other link arguments
+ """
+ lpaths = []
+ libs = []
+ other = []
+ for arg in args:
+ if arg.startswith('/LIBPATH:'):
+ lpath = arg[9:]
+ # De-dup library search paths by removing older entries when
+ # a new one is found. This is necessary because unlike other
+ # search paths such as the include path, the library is
+ # searched for in the newest (right-most) search path first.
+ if lpath in lpaths:
+ lpaths.remove(lpath)
+ lpaths.append(lpath)
+ # It's ok if we miss libraries with non-standard extensions here.
+ # They will go into the general link arguments.
+ elif arg.endswith('.lib') or arg.endswith('.a'):
+ # De-dup
+ if arg not in libs:
+ libs.append(arg)
+ else:
+ other.append(arg)
+ return (lpaths, libs, other)
+
def gen_vcxproj(self, target, ofname, guid, compiler):
mlog.debug('Generating vcxproj %s.' % target.name)
entrypoint = 'WinMainCRTStartup'
@@ -424,11 +490,15 @@ class Vs2010Backend(backends.Backend):
return self.gen_run_target_vcxproj(target, ofname, guid)
else:
raise MesonException('Unknown target type for %s' % target.get_basename())
+ # Prefix to use to access the build root from the vcxproj dir
down = self.target_to_build_root(target)
+ # Prefix to use to access the source tree's root from the vcxproj dir
proj_to_src_root = os.path.join(down, self.build_to_src)
+ # Prefix to use to access the source tree's subdir from the vcxproj dir
proj_to_src_dir = os.path.join(proj_to_src_root, target.subdir)
(sources, headers, objects, languages) = self.split_sources(target.sources)
- buildtype = self.buildtype
+ buildtype_args = compiler.get_buildtype_args(self.buildtype)
+ buildtype_link_args = compiler.get_buildtype_linker_args(self.buildtype)
project_name = target.name
target_name = target.name
root = ET.Element('Project', {'DefaultTargets' : "Build",
@@ -438,9 +508,10 @@ class Vs2010Backend(backends.Backend):
prjconf = ET.SubElement(confitems, 'ProjectConfiguration',
{'Include' : self.buildtype + '|' + self.platform})
p = ET.SubElement(prjconf, 'Configuration')
- p.text= buildtype
+ p.text= self.buildtype
pl = ET.SubElement(prjconf, 'Platform')
pl.text = self.platform
+ # Globals
globalgroup = ET.SubElement(root, 'PropertyGroup', Label='Globals')
guidelem = ET.SubElement(globalgroup, 'ProjectGuid')
guidelem.text = guid
@@ -453,13 +524,68 @@ class Vs2010Backend(backends.Backend):
pname= ET.SubElement(globalgroup, 'ProjectName')
pname.text = project_name
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.Default.props')
+ # Start configuration
type_config = ET.SubElement(root, 'PropertyGroup', Label='Configuration')
ET.SubElement(type_config, 'ConfigurationType').text = conftype
ET.SubElement(type_config, 'CharacterSet').text = 'MultiByte'
if self.platform_toolset:
ET.SubElement(type_config, 'PlatformToolset').text = self.platform_toolset
+ # FIXME: Meson's LTO support needs to be integrated here
ET.SubElement(type_config, 'WholeProgramOptimization').text = 'false'
- ET.SubElement(type_config, 'UseDebugLibraries').text = 'true'
+ # Let VS auto-set the RTC level
+ ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'Default'
+ o_flags = split_o_flags_args(buildtype_args)
+ if '/Oi' in o_flags:
+ ET.SubElement(type_config, 'IntrinsicFunctions').text = 'true'
+ if '/Ob1' in o_flags:
+ ET.SubElement(type_config, 'InlineFunctionExpansion').text = 'OnlyExplicitInline'
+ elif '/Ob2' in o_flags:
+ ET.SubElement(type_config, 'InlineFunctionExpansion').text = 'AnySuitable'
+ # Size-preserving flags
+ if '/Os' in o_flags:
+ ET.SubElement(type_config, 'FavorSizeOrSpeed').text = 'Size'
+ else:
+ ET.SubElement(type_config, 'FavorSizeOrSpeed').text = 'Speed'
+ # Incremental linking increases code size
+ if '/INCREMENTAL:NO' in buildtype_link_args:
+ ET.SubElement(type_config, 'LinkIncremental').text = 'false'
+ # CRT type; debug or release
+ if '/MDd' in buildtype_args:
+ ET.SubElement(type_config, 'UseDebugLibraries').text = 'true'
+ ET.SubElement(type_config, 'RuntimeLibrary').text = 'MultiThreadedDebugDLL'
+ else:
+ ET.SubElement(type_config, 'UseDebugLibraries').text = 'false'
+ ET.SubElement(type_config, 'RuntimeLibrary').text = 'MultiThreadedDLL'
+ # Debug format
+ if '/ZI' in buildtype_args:
+ ET.SubElement(type_config, 'DebugInformationFormat').text = 'EditAndContinue'
+ elif '/Zi' in buildtype_args:
+ ET.SubElement(type_config, 'DebugInformationFormat').text = 'ProgramDatabase'
+ elif '/Z7' in buildtype_args:
+ ET.SubElement(type_config, 'DebugInformationFormat').text = 'OldStyle'
+ # Generate Debug info
+ if '/DEBUG' in buildtype_link_args:
+ ET.SubElement(type_config, 'GenerateDebugInformation').text = 'true'
+ # Runtime checks
+ if '/RTC1' in buildtype_args:
+ ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'EnableFastChecks'
+ elif '/RTCu' in buildtype_args:
+ ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'UninitializedLocalUsageCheck'
+ elif '/RTCs' in buildtype_args:
+ ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'StackFrameRuntimeCheck'
+ # Optimization flags
+ if '/Ox' in o_flags:
+ ET.SubElement(type_config, 'Optimization').text = 'Full'
+ elif '/O2' in o_flags:
+ ET.SubElement(type_config, 'Optimization').text = 'MaxSpeed'
+ elif '/O1' in o_flags:
+ ET.SubElement(type_config, 'Optimization').text = 'MinSpace'
+ elif '/Od' in o_flags:
+ ET.SubElement(type_config, 'Optimization').text = 'Disabled'
+ # Warning level
+ warning_level = self.environment.coredata.get_builtin_option('warning_level')
+ ET.SubElement(type_config, 'WarningLevel').text = 'Level' + warning_level
+ # End configuration
ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.props')
generated_files, custom_target_output_files, generated_files_include_dirs = self.generate_custom_generator_commands(target, root)
(gen_src, gen_hdrs, gen_objs, gen_langs) = self.split_sources(generated_files)
@@ -467,6 +593,7 @@ class Vs2010Backend(backends.Backend):
gen_src += custom_src
gen_hdrs += custom_hdrs
gen_langs += custom_langs
+ # Project information
direlem = ET.SubElement(root, 'PropertyGroup')
fver = ET.SubElement(direlem, '_ProjectFileVersion')
fver.text = self.project_file_version
@@ -474,15 +601,13 @@ class Vs2010Backend(backends.Backend):
outdir.text = '.\\'
intdir = ET.SubElement(direlem, 'IntDir')
intdir.text = target.get_id() + '\\'
- tname = ET.SubElement(direlem, 'TargetName')
- tname.text = target_name
- inclinc = ET.SubElement(direlem, 'LinkIncremental')
- inclinc.text = 'true'
+ tfilename = os.path.splitext(target.get_filename())
+ ET.SubElement(direlem, 'TargetName').text = tfilename[0]
+ ET.SubElement(direlem, 'TargetExt').text = tfilename[1]
+ # Build information
compiles = ET.SubElement(root, 'ItemDefinitionGroup')
clconf = ET.SubElement(compiles, 'ClCompile')
- opt = ET.SubElement(clconf, 'Optimization')
- opt.text = 'disabled'
inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), self.get_target_dir(target)),
proj_to_src_dir] + generated_files_include_dirs
@@ -496,18 +621,24 @@ class Vs2010Backend(backends.Backend):
for l, args in target.extra_args.items():
if l in extra_args:
extra_args[l] += compiler.unix_compile_flags_to_native(args)
- general_args = compiler.get_buildtype_args(self.buildtype).copy()
# FIXME all the internal flags of VS (optimization etc) are represented
# by their own XML elements. In theory we should split all flags to those
# that have an XML element and those that don't and serialise them
# properly. This is a crapton of work for no real gain, so just dump them
# here.
- general_args += compiler.get_option_compile_args(self.environment.coredata.compiler_options)
+ general_args = compiler.get_option_compile_args(self.environment.coredata.compiler_options)
for d in target.get_external_deps():
- try:
- general_args += d.compile_args
- except AttributeError:
- pass
+ # Cflags required by external deps might have UNIX-specific flags,
+ # so filter them out if needed
+ d_compile_args = compiler.unix_compile_flags_to_native(d.get_compile_args())
+ for arg in d_compile_args:
+ if arg.startswith('-I'):
+ inc_dir = arg[2:]
+ # De-dup
+ if inc_dir not in inc_dirs:
+ inc_dirs.append(inc_dir)
+ else:
+ general_args.append(arg)
for l, args in extra_args.items():
extra_args[l] = [Vs2010Backend.quote_define_cmdline(x) for x in args]
@@ -540,8 +671,6 @@ class Vs2010Backend(backends.Backend):
preproc = ET.SubElement(clconf, 'PreprocessorDefinitions')
rebuild = ET.SubElement(clconf, 'MinimalRebuild')
rebuild.text = 'true'
- rtlib = ET.SubElement(clconf, 'RuntimeLibrary')
- rtlib.text = 'MultiThreadedDebugDLL'
funclink = ET.SubElement(clconf, 'FunctionLevelLinking')
funclink.text = 'true'
pch_node = ET.SubElement(clconf, 'PrecompiledHeader')
@@ -564,28 +693,39 @@ class Vs2010Backend(backends.Backend):
pch_out = ET.SubElement(clconf, 'PrecompiledHeaderOutputFile')
pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % pch_source[2]
- warnings = ET.SubElement(clconf, 'WarningLevel')
- warnings.text = 'Level3'
- debinfo = ET.SubElement(clconf, 'DebugInformationFormat')
- debinfo.text = 'EditAndContinue'
resourcecompile = ET.SubElement(compiles, 'ResourceCompile')
ET.SubElement(resourcecompile, 'PreprocessorDefinitions')
link = ET.SubElement(compiles, 'Link')
# Put all language args here, too.
extra_link_args = compiler.get_option_link_args(self.environment.coredata.compiler_options)
+ # FIXME: Can these buildtype linker args be added as tags in the
+ # vcxproj file (similar to buildtype compiler args) instead of in
+ # AdditionalOptions?
extra_link_args += compiler.get_buildtype_linker_args(self.buildtype)
for l in self.environment.coredata.external_link_args.values():
- extra_link_args += compiler.unix_link_flags_to_native(l)
- extra_link_args += compiler.unix_link_flags_to_native(target.link_args)
+ extra_link_args += l
+ if not isinstance(target, build.StaticLibrary):
+ extra_link_args += target.link_args
+ # External deps must be last because target link libraries may depend on them.
+ for dep in target.get_external_deps():
+ extra_link_args += dep.get_link_args()
+ for d in target.get_dependencies():
+ if isinstance(d, build.StaticLibrary):
+ for dep in d.get_external_deps():
+ extra_link_args += dep.get_link_args()
+ extra_link_args = compiler.unix_link_flags_to_native(extra_link_args)
+ (additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args)
if len(extra_link_args) > 0:
extra_link_args.append('%(AdditionalOptions)')
ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
+ if len(additional_libpaths) > 0:
+ additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
+ ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
- additional_links = []
+ # Add more libraries to be linked if needed
for t in target.get_dependencies():
lobj = self.build.targets[t.get_id()]
- rel_path = self.relpath(lobj.subdir, target.subdir)
- linkname = os.path.join(rel_path, lobj.get_import_filename())
+ linkname = os.path.join(down, self.get_target_filename_for_linking(lobj))
additional_links.append(linkname)
for lib in self.get_custom_target_provided_libraries(target):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))
@@ -600,26 +740,37 @@ class Vs2010Backend(backends.Backend):
ET.SubElement(link, 'AdditionalDependencies').text = ';'.join(additional_links)
ofile = ET.SubElement(link, 'OutputFile')
ofile.text = '$(OutDir)%s' % target.get_filename()
- addlibdir = ET.SubElement(link, 'AdditionalLibraryDirectories')
- addlibdir.text = '%(AdditionalLibraryDirectories)'
subsys = ET.SubElement(link, 'SubSystem')
subsys.text = subsystem
- gendeb = ET.SubElement(link, 'GenerateDebugInformation')
- gendeb.text = 'true'
if isinstance(target, build.SharedLibrary):
+ # 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()
- pdb = ET.SubElement(link, 'ProgramDataBaseFileName')
- pdb.text = '$(OutDir}%s.pdb' % target_name
+ # 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))
+ ET.SubElement(link, 'ModuleDefinitionFile').text = relpath
+ if '/ZI' in buildtype_args or '/Zi' in buildtype_args:
+ pdb = ET.SubElement(link, 'ProgramDataBaseFileName')
+ pdb.text = '$(OutDir}%s.pdb' % target_name
if isinstance(target, build.Executable):
ET.SubElement(link, 'EntryPointSymbol').text = entrypoint
targetmachine = ET.SubElement(link, 'TargetMachine')
- targetmachine.text = 'MachineX86'
+ targetplatform = self.platform.lower()
+ if targetplatform == 'win32':
+ targetmachine.text = 'MachineX86'
+ elif targetplatform == 'x64':
+ targetmachine.text = 'MachineX64'
+ elif targetplatform == 'arm':
+ targetmachine.text = 'MachineARM'
+ else:
+ raise MesonException('Unsupported Visual Studio target machine: ' + targetmachine)
extra_files = target.extra_files
if len(headers) + len(gen_hdrs) + len(extra_files) > 0:
inc_hdrs = ET.SubElement(root, 'ItemGroup')
for h in headers:
- relpath = h.rel_to_builddir(proj_to_src_root)
+ relpath = os.path.join(down, h.rel_to_builddir(self.build_to_src))
ET.SubElement(inc_hdrs, 'CLInclude', Include=relpath)
for h in gen_hdrs:
ET.SubElement(inc_hdrs, 'CLInclude', Include=h)
@@ -630,7 +781,7 @@ class Vs2010Backend(backends.Backend):
if len(sources) + len(gen_src) + len(pch_sources) > 0:
inc_src = ET.SubElement(root, 'ItemGroup')
for s in sources:
- relpath = s.rel_to_builddir(proj_to_src_root)
+ relpath = os.path.join(down, s.rel_to_builddir(self.build_to_src))
inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=relpath)
self.add_pch(inc_cl, proj_to_src_dir, pch_sources, s)
self.add_additional_options(s, inc_cl, extra_args, additional_options_set)
@@ -658,7 +809,7 @@ class Vs2010Backend(backends.Backend):
if self.has_objects(objects, additional_objects, gen_objs):
inc_objs = ET.SubElement(root, 'ItemGroup')
for s in objects:
- relpath = s.rel_to_builddir(proj_to_src_root)
+ relpath = os.path.join(down, s.rel_to_builddir(self.build_to_src))
ET.SubElement(inc_objs, 'Object', Include=relpath)
for s in additional_objects:
ET.SubElement(inc_objs, 'Object', Include=s)
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 0ce90ce..e64866d 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -292,12 +292,10 @@ class XCodeBackend(backends.Backend):
reftype = 0
if isinstance(t, build.Executable):
typestr = 'compiled.mach-o.executable'
- path = t.get_filename()
+ path = fname
elif isinstance(t, build.SharedLibrary):
- # OSX has a completely different shared library
- # naming scheme so do this manually.
typestr = self.get_xcodetype('dummy.dylib')
- path = t.get_osx_filename()
+ path = fname
else:
typestr = self.get_xcodetype(fname)
path = '"%s"' % t.get_filename()
@@ -626,7 +624,7 @@ class XCodeBackend(backends.Backend):
headerdirs.append(os.path.join(self.environment.get_build_dir(), cd))
for l in target.link_targets:
abs_path = os.path.join(self.environment.get_build_dir(),
- l.subdir, buildtype, l.get_osx_filename())
+ l.subdir, buildtype, l.get_filename())
dep_libs.append("'%s'" % abs_path)
if isinstance(l, build.SharedLibrary):
links_dylib = True
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index c60f37f..b610bb8 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -16,8 +16,9 @@ from . import coredata
from . import environment
from . import dependencies
from . import mlog
-import copy, os
+import copy, os, re
from .mesonlib import File, flatten, MesonException
+from .environment import for_windows, for_darwin
known_basic_kwargs = {'install' : True,
'c_pch' : True,
@@ -71,6 +72,38 @@ We are fully aware that these are not really usable or pleasant ways to do
this but it's the best we can do given the way shell quoting works.
'''
+def sources_are_suffix(sources, suffix):
+ for source in sources:
+ if source.endswith('.' + suffix):
+ return True
+ return False
+
+def compiler_is_msvc(sources, is_cross, env):
+ """
+ Since each target does not currently have the compiler information attached
+ to it, we must do this detection manually here.
+
+ This detection is purposely incomplete and will cause bugs if other code is
+ extended and this piece of code is forgotten.
+ """
+ compiler = None
+ if sources_are_suffix(sources, 'c'):
+ try:
+ compiler = env.detect_c_compiler(is_cross)
+ except MesonException:
+ return False
+ elif sources_are_suffix(sources, 'cxx') or \
+ sources_are_suffix(sources, 'cpp') or \
+ sources_are_suffix(sources, 'cc'):
+ try:
+ compiler = env.detect_cpp_compiler(is_cross)
+ except MesonException:
+ return False
+ if compiler and compiler.get_id() == 'msvc':
+ return True
+ return False
+
+
class InvalidArguments(MesonException):
pass
@@ -209,6 +242,10 @@ class BuildTarget():
raise InvalidArguments('Build target %s has no sources.' % name)
self.validate_sources()
+ def __repr__(self):
+ repr_str = "<{0} {1}: {2}>"
+ return repr_str.format(self.__class__.__name__, self.get_id(), self.filename)
+
def get_id(self):
# This ID must also be a valid file name on all OSs.
# It should also avoid shell metacharacters for obvious
@@ -593,6 +630,10 @@ class Generator():
self.exe = exe
self.process_kwargs(kwargs)
+ def __repr__(self):
+ repr_str = "<{0}: {1}>"
+ return repr_str.format(self.__class__.__name__, self.exe)
+
def get_exe(self):
return self.exe
@@ -670,15 +711,19 @@ class GeneratedList():
class Executable(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
- self.prefix = ''
- self.suffix = environment.get_exe_suffix()
- suffix = environment.get_exe_suffix()
- if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
- suffix = 'exe'
- if suffix != '':
- self.filename = self.name + '.' + suffix
- else:
- self.filename = self.name
+ # Unless overriden, executables have no suffix or prefix. Except on
+ # Windows and with C#/Mono executables where the suffix is 'exe'
+ if not hasattr(self, 'prefix'):
+ self.prefix = ''
+ if not hasattr(self, 'suffix'):
+ # Executable for Windows or C#/Mono
+ if for_windows(is_cross, environment) or sources_are_suffix(self.sources, 'cs'):
+ self.suffix = 'exe'
+ else:
+ self.suffix = ''
+ self.filename = self.name
+ if self.suffix:
+ self.filename += '.' + self.suffix
def type_suffix(self):
return "@exe"
@@ -686,52 +731,161 @@ class Executable(BuildTarget):
class StaticLibrary(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
- if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
+ if sources_are_suffix(self.sources, 'cs'):
raise InvalidArguments('Static libraries not supported for C#.')
+ # By default a static library is named libfoo.a even on Windows because
+ # MSVC does not have a consistent convention for what static libraries
+ # are called. The MSVC CRT uses libfoo.lib syntax but nothing else uses
+ # it and GCC only looks for static libraries called foo.lib and
+ # libfoo.a. However, we cannot use foo.lib because that's the same as
+ # the import library. Using libfoo.a is ok because people using MSVC
+ # always pass the library filename while linking anyway.
if not hasattr(self, 'prefix'):
- self.prefix = environment.get_static_lib_prefix()
- self.suffix = environment.get_static_lib_suffix()
- if len(self.sources) > 0 and self.sources[0].endswith('.rs'):
- self.suffix = 'rlib'
+ self.prefix = 'lib'
+ if not hasattr(self, 'suffix'):
+ # Rust static library crates have .rlib suffix
+ if sources_are_suffix(self.sources, 'rs'):
+ self.suffix = 'rlib'
+ else:
+ self.suffix = 'a'
self.filename = self.prefix + self.name + '.' + self.suffix
- def get_import_filename(self):
- return self.filename
-
- def get_osx_filename(self):
- return self.get_filename()
-
def type_suffix(self):
return "@sta"
class SharedLibrary(BuildTarget):
def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs):
- self.version = None
self.soversion = None
+ self.ltversion = None
self.vs_module_defs = None
- super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs);
- if len(self.sources) > 0 and self.sources[0].endswith('.cs'):
+ # 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
+ super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs)
+ if not hasattr(self, 'prefix'):
+ self.prefix = None
+ if not hasattr(self, 'suffix'):
+ self.suffix = None
+ self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ self.determine_filenames(is_cross, environment)
+
+ def determine_filenames(self, is_cross, env):
+ """
+ See https://github.com/mesonbuild/meson/pull/417 for details.
+
+ First we determine the filename template (self.filename_tpl), then we
+ set the output filename (self.filename).
+
+ The template is needed while creating aliases (self.get_aliaslist),
+ which are needed while generating .so shared libraries for Linux.
+
+ Besides this, there's also the import library name, which is only used
+ on Windows since on that platform the linker uses a separate library
+ called the "import library" during linking instead of the shared
+ library (DLL). The toolchain will output an import library in one of
+ two formats: GCC or Visual Studio.
+
+ When we're building with Visual Studio, the import library that will be
+ generated by the toolchain is self.vs_import_filename, and with
+ MinGW/GCC, it's self.gcc_import_filename. self.import_filename will
+ always contain the import library name this target will generate.
+ """
+ prefix = ''
+ suffix = ''
+ self.filename_tpl = self.basic_filename_tpl
+ # If the user already provided the prefix and suffix to us, we don't
+ # need to do any filename suffix/prefix detection.
+ # NOTE: manual prefix/suffix override is currently only tested for C/C++
+ if self.prefix != None and self.suffix != None:
+ pass
+ # C# and Mono
+ elif sources_are_suffix(self.sources, 'cs'):
+ prefix = ''
+ suffix = 'dll'
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ # Rust
+ elif sources_are_suffix(self.sources, 'rs'):
+ # Currently, we always build --crate-type=rlib
prefix = 'lib'
+ suffix = 'rlib'
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ # C, C++, Swift, Vala
+ # Only Windows uses a separate import library for linking
+ # For all other targets/platforms import_filename stays None
+ elif for_windows(is_cross, env):
suffix = 'dll'
+ self.vs_import_filename = '{0}.lib'.format(self.name)
+ self.gcc_import_filename = 'lib{0}.dll.a'.format(self.name)
+ if compiler_is_msvc(self.sources, is_cross, env):
+ # Shared library is of the form foo.dll
+ prefix = ''
+ # Import library is called foo.lib
+ self.import_filename = self.vs_import_filename
+ # Assume GCC-compatible naming
+ else:
+ # Shared library is of the form libfoo.dll
+ prefix = 'lib'
+ # Import library is called libfoo.dll.a
+ self.import_filename = self.gcc_import_filename
+ # Shared library has the soversion if it is defined
+ if self.soversion:
+ self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
+ else:
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ elif for_darwin(is_cross, env):
+ prefix = 'lib'
+ suffix = 'dylib'
+ if self.soversion:
+ # libfoo.X.dylib
+ self.filename_tpl = '{0.prefix}{0.name}.{0.soversion}.{0.suffix}'
+ else:
+ # libfoo.dylib
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
else:
- prefix = environment.get_shared_lib_prefix()
- suffix = environment.get_shared_lib_suffix()
- if not hasattr(self, 'prefix'):
- self.prefix = prefix
- if not hasattr(self, 'suffix'):
- if len(self.sources) > 0 and self.sources[0].endswith('.rs'):
- self.suffix = 'rlib'
+ prefix = 'lib'
+ suffix = 'so'
+ if self.ltversion:
+ # libfoo.so.X[.Y[.Z]] (.Y and .Z are optional)
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.ltversion}'
+ elif self.soversion:
+ # libfoo.so.X
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.soversion}'
else:
- self.suffix = suffix
- self.importsuffix = environment.get_import_lib_suffix()
- self.filename = self.prefix + self.name + '.' + self.suffix
+ # No versioning, libfoo.so
+ self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
+ if self.prefix == None:
+ self.prefix = prefix
+ if self.suffix == None:
+ self.suffix = suffix
+ self.filename = self.filename_tpl.format(self)
def process_kwargs(self, kwargs, environment):
super().process_kwargs(kwargs, environment)
+ # Shared library version
if 'version' in kwargs:
- self.set_version(kwargs['version'])
+ self.ltversion = kwargs['version']
+ if not isinstance(self.ltversion, str):
+ raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__)
+ if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion):
+ raise InvalidArguments('Invalid Shared library version "{0}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.'.format(self.ltversion))
+ # Try to extract/deduce the soversion
if 'soversion' in kwargs:
- self.set_soversion(kwargs['soversion'])
+ self.soversion = kwargs['soversion']
+ if isinstance(self.soversion, int):
+ self.soversion = str(self.soversion)
+ if not isinstance(self.soversion, str):
+ raise InvalidArguments('Shared library soversion is not a string or integer.')
+ try:
+ int(self.soversion)
+ except ValueError:
+ raise InvalidArguments('Shared library soversion must be a valid integer')
+ elif self.ltversion:
+ # library version is defined, get the soversion from that
+ self.soversion = self.ltversion.split('.')[0]
+ # Visual Studio module-definitions file
if 'vs_module_defs' in kwargs:
path = kwargs['vs_module_defs']
if (os.path.isabs(path)):
@@ -742,46 +896,41 @@ class SharedLibrary(BuildTarget):
def check_unknown_kwargs(self, kwargs):
self.check_unknown_kwargs_int(kwargs, known_shlib_kwargs)
- def get_shbase(self):
- return self.prefix + self.name + '.' + self.suffix
-
def get_import_filename(self):
- return self.prefix + self.name + '.' + self.importsuffix
+ """
+ The name of the import library that will be outputted by the compiler
- def get_all_link_deps(self):
- return [self] + self.get_transitive_link_deps()
-
- def get_filename(self):
- '''Works on all platforms except OSX, which does its own thing.'''
- fname = self.get_shbase()
- if self.version is None:
- return fname
- else:
- return fname + '.' + self.version
-
- def get_osx_filename(self):
- if self.version is None:
- return self.get_shbase()
- return self.prefix + self.name + '.' + self.version + '.' + self.suffix
+ Returns None if there is no import library required for this platform
+ """
+ return self.import_filename
- def set_version(self, version):
- if not isinstance(version, str):
- raise InvalidArguments('Shared library version is not a string.')
- self.version = version
+ def get_import_filenameslist(self):
+ if self.import_filename:
+ return [self.vs_import_filename, self.gcc_import_filename]
+ return []
- def set_soversion(self, version):
- if isinstance(version, int):
- version = str(version)
- if not isinstance(version, str):
- raise InvalidArguments('Shared library soversion is not a string or integer.')
- self.soversion = version
+ def get_all_link_deps(self):
+ return [self] + self.get_transitive_link_deps()
def get_aliaslist(self):
- aliases = []
- if self.soversion is not None:
- aliases.append(self.get_shbase() + '.' + self.soversion)
- if self.version is not None:
- aliases.append(self.get_shbase())
+ """
+ If the versioned library name is libfoo.so.0.100.0, aliases are:
+ * libfoo.so.0 (soversion)
+ * libfoo.so (unversioned; for linking)
+ """
+ # Aliases are only useful with .so libraries. Also if the .so library
+ # ends with .so (no versioning), we don't need aliases.
+ if self.suffix != 'so' or self.filename.endswith('.so'):
+ return []
+ # Unversioned alias: libfoo.so
+ aliases = [self.basic_filename_tpl.format(self)]
+ # If ltversion != soversion we create an soversion alias: libfoo.so.X
+ if self.ltversion and self.ltversion != self.soversion:
+ if not self.soversion:
+ # This is done in self.process_kwargs()
+ raise AssertionError('BUG: If library version is defined, soversion must have been defined')
+ alias_tpl = self.filename_tpl.replace('ltversion', 'soversion')
+ aliases.append(alias_tpl.format(self))
return aliases
def type_suffix(self):
@@ -815,6 +964,10 @@ class CustomTarget:
mlog.log(mlog.bold('Warning:'), 'Unknown keyword arguments in target %s: %s' %
(self.name, ', '.join(unknowns)))
+ def __repr__(self):
+ repr_str = "<{0} {1}: {2}>"
+ return repr_str.format(self.__class__.__name__, self.get_id(), self.command)
+
def get_id(self):
return self.name + self.type_suffix()
@@ -851,7 +1004,7 @@ class CustomTarget:
for i, c in enumerate(cmd):
if hasattr(c, 'held_object'):
c = c.held_object
- if isinstance(c, str):
+ if isinstance(c, str) or isinstance(c, File):
final_cmd.append(c)
elif isinstance(c, dependencies.ExternalProgram):
if not c.found():
@@ -867,8 +1020,6 @@ class CustomTarget:
if not isinstance(s, str):
raise InvalidArguments('Array as argument %d contains a non-string.' % i)
final_cmd.append(s)
- elif isinstance(c, File):
- final_cmd.append(os.path.join(c.subdir, c.fname))
else:
raise InvalidArguments('Argument %s in "command" is invalid.' % i)
self.command = final_cmd
@@ -944,6 +1095,10 @@ class RunTarget:
self.dependencies = dependencies
self.subdir = subdir
+ def __repr__(self):
+ repr_str = "<{0} {1}: {2}>"
+ return repr_str.format(self.__class__.__name__, self.get_id(), self.command)
+
def get_id(self):
return self.name + self.type_suffix()
@@ -994,6 +1149,12 @@ class ConfigureFile():
self.targetname = targetname
self.configuration_data = configuration_data
+ def __repr__(self):
+ repr_str = "<{0}: {1} -> {2}>"
+ src = os.path.join(self.subdir, self.sourcename)
+ dst = os.path.join(self.subdir, self.targetname)
+ return repr_str.format(self.__class__.__name__, src, dst)
+
def get_configuration_data(self):
return self.configuration_data
@@ -1011,6 +1172,9 @@ class ConfigurationData():
super().__init__()
self.values = {}
+ def __repr__(self):
+ return repr(self.values)
+
def get(self, name):
return self.values[name]
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 8ee86bc..55a4384 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -274,6 +274,13 @@ class Compiler():
def get_linker_always_args(self):
return []
+ def gen_import_library_args(self, implibname):
+ """
+ Used only on Windows for libraries that need an import library.
+ This currently means C, C++, Fortran.
+ """
+ return []
+
def get_options(self):
return {} # build afresh every time
@@ -473,6 +480,14 @@ class CCompiler(Compiler):
def get_linker_search_args(self, dirname):
return ['-L'+dirname]
+ def gen_import_library_args(self, implibname):
+ """
+ The name of the outputted import library
+
+ This implementation is used only on Windows by compilers that use GNU ld
+ """
+ return ['-Wl,--out-implib=' + implibname]
+
def sanity_check_impl(self, work_dir, environment, sname, code):
mlog.debug('Sanity testing ' + self.language + ' compiler:', ' '.join(self.exelist))
mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
@@ -1499,6 +1514,10 @@ class VisualStudioCCompiler(CCompiler):
objname = os.path.splitext(pchname)[0] + '.obj'
return (objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname ])
+ def gen_import_library_args(self, implibname):
+ "The name of the outputted import library"
+ return ['/IMPLIB:' + implibname]
+
def build_rpath_args(self, build_dir, rpath_paths, install_rpath):
return []
@@ -2057,6 +2076,14 @@ class GnuFortranCompiler(FortranCompiler):
def get_always_args(self):
return ['-pipe']
+ def gen_import_library_args(self, implibname):
+ """
+ The name of the outputted import library
+
+ Used only on Windows
+ """
+ return ['-Wl,--out-implib=' + implibname]
+
class G95FortranCompiler(FortranCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):
super().__init__(exelist, version, is_cross, exe_wrapper=None)
@@ -2068,6 +2095,14 @@ class G95FortranCompiler(FortranCompiler):
def get_always_args(self):
return ['-pipe']
+ def gen_import_library_args(self, implibname):
+ """
+ The name of the outputted import library
+
+ Used only on Windows
+ """
+ return ['-Wl,--out-implib=' + implibname]
+
class SunFortranCompiler(FortranCompiler):
def __init__(self, exelist, version, is_cross, exe_wrapper=None):
super().__init__(exelist, version, is_cross, exe_wrapper=None)
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 3868301..47414ca 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -80,11 +80,37 @@ def detect_system():
return platform.system().lower()
+def for_windows(is_cross, env):
+ """
+ Host machine is windows?
+
+ Note: 'host' is the machine on which compiled binaries will run
+ """
+ if not is_cross:
+ return mesonlib.is_windows()
+ elif env.cross_info.has_host():
+ return env.cross_info.config['host_machine']['system'] == 'windows'
+ return False
+
+def for_darwin(is_cross, env):
+ """
+ Host machine is Darwin (iOS/OS X)?
+
+ Note: 'host' is the machine on which compiled binaries will run
+ """
+ if not is_cross:
+ return mesonlib.is_osx()
+ elif env.cross_info.has_host():
+ return env.cross_info.config['host_machine']['system'] == 'darwin'
+ return False
+
+
class Environment():
private_dir = 'meson-private'
log_dir = 'meson-logs'
coredata_file = os.path.join(private_dir, 'coredata.dat')
version_regex = '\d+(\.\d+)+(-[a-zA-Z0-9]+)?'
+
def __init__(self, source_dir, build_dir, main_script_file, options, original_cmd_line_args):
assert(os.path.isabs(main_script_file))
assert(not os.path.islink(main_script_file))
@@ -122,33 +148,22 @@ class Environment():
self.default_static_linker = 'ar'
self.vs_static_linker = 'lib'
+ # Various prefixes and suffixes for import libraries, shared libraries,
+ # static libraries, and executables.
+ # Versioning is added to these names in the backends as-needed.
cross = self.is_cross_build()
if (not cross and mesonlib.is_windows()) \
or (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'windows'):
self.exe_suffix = 'exe'
- if self.detect_c_compiler(cross).get_id() == 'msvc':
- self.import_lib_suffix = 'lib'
- else:
- # MinGW-GCC doesn't generate and can't link with a .lib
- # It uses the DLL file as the import library
- self.import_lib_suffix = 'dll'
- self.shared_lib_suffix = 'dll'
- self.shared_lib_prefix = ''
- self.static_lib_suffix = 'lib'
- self.static_lib_prefix = ''
self.object_suffix = 'obj'
+ self.shared_lib_dir = self.get_bindir()
else:
self.exe_suffix = ''
- if (not cross and mesonlib.is_osx()) or \
- (cross and self.cross_info.has_host() and self.cross_info.config['host_machine']['system'] == 'darwin'):
- self.shared_lib_suffix = 'dylib'
- else:
- self.shared_lib_suffix = 'so'
- self.shared_lib_prefix = 'lib'
- self.static_lib_suffix = 'a'
- self.static_lib_prefix = 'lib'
self.object_suffix = 'o'
- self.import_lib_suffix = self.shared_lib_suffix
+ self.shared_lib_dir = self.get_libdir()
+ # Common to all platforms
+ self.import_lib_dir = self.get_libdir()
+ self.static_lib_dir = self.get_libdir()
def is_cross_build(self):
return self.cross_info is not None
@@ -641,22 +656,17 @@ class Environment():
def get_exe_suffix(self):
return self.exe_suffix
- # On Windows (MSVC) the library has suffix dll
- # but you link against a file that has suffix lib.
- def get_import_lib_suffix(self):
- return self.import_lib_suffix
+ def get_import_lib_dir(self):
+ "Install dir for the import library (library used for linking)"
+ return self.import_lib_dir
- def get_shared_lib_prefix(self):
- return self.shared_lib_prefix
+ def get_shared_lib_dir(self):
+ "Install dir for the shared library"
+ return self.shared_lib_dir
- def get_shared_lib_suffix(self):
- return self.shared_lib_suffix
-
- def get_static_lib_prefix(self):
- return self.static_lib_prefix
-
- def get_static_lib_suffix(self):
- return self.static_lib_suffix
+ def get_static_lib_dir(self):
+ "Install dir for the static library"
+ return self.static_lib_dir
def get_object_suffix(self):
return self.object_suffix
@@ -682,17 +692,6 @@ class Environment():
def get_datadir(self):
return self.coredata.get_builtin_option('datadir')
- def find_library(self, libname, dirs):
- if dirs is None:
- dirs = mesonlib.get_library_dirs()
- suffixes = [self.get_shared_lib_suffix(), self.get_static_lib_suffix()]
- prefix = self.get_shared_lib_prefix()
- for d in dirs:
- for suffix in suffixes:
- trial = os.path.join(d, prefix + libname + '.' + suffix)
- if os.path.isfile(trial):
- return trial
-
def get_args_from_envvars(lang):
if lang == 'c':
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index 837f78a..1672d95 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -28,6 +28,16 @@ class File:
self.subdir = subdir
self.fname = fname
+ def __str__(self):
+ return os.path.join(self.subdir, self.fname)
+
+ def __repr__(self):
+ ret = '<File: {0}'
+ if not self.is_built:
+ ret += ' (not built)'
+ ret += '>'
+ return ret.format(os.path.join(self.subdir, self.fname))
+
@staticmethod
def from_source_file(source_root, subdir, fname):
if not os.path.isfile(os.path.join(source_root, subdir, fname)):
diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py
index 1de5a00..1924b95 100644
--- a/mesonbuild/scripts/meson_install.py
+++ b/mesonbuild/scripts/meson_install.py
@@ -52,7 +52,7 @@ def do_install(datafilename):
def install_subdirs(data):
for (src_dir, inst_dir, dst_dir) in data.install_subdirs:
- if src_dir.endswith('/'):
+ if src_dir.endswith('/') or src_dir.endswith('\\'):
src_dir = src_dir[:-1]
src_prefix = os.path.join(src_dir, inst_dir)
print('Installing subdir %s to %s.' % (src_prefix, dst_dir))
diff --git a/run_tests.py b/run_tests.py
index 6f51a19..0919d84 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -124,44 +124,52 @@ def setup_commands(backend):
test_commands = [ninja_command, 'test', 'benchmark']
install_commands = [ninja_command, 'install']
-def platform_fix_filename(fname):
- if mesonlib.is_osx():
- if fname.endswith('.so'):
- return fname[:-2] + 'dylib'
- return fname.replace('.so.', '.dylib.')
- elif mesonlib.is_windows():
- if fname.endswith('.so'):
- (p, f) = os.path.split(fname)
- f = f[3:-2] + 'dll'
- return os.path.join(p, f)
- if fname.endswith('.a'):
- return fname[:-1] + 'lib'
+def get_relative_files_list_from_dir(fromdir):
+ paths = []
+ for (root, _, files) in os.walk(fromdir):
+ reldir = os.path.relpath(root, start=fromdir)
+ for f in files:
+ path = os.path.join(reldir, f).replace('\\', '/')
+ if path.startswith('./'):
+ path = path[2:]
+ paths.append(path)
+ return paths
+
+def platform_fix_exe_name(fname):
+ if not fname.endswith('?exe'):
+ return fname
+ fname = fname[:-4]
+ if mesonlib.is_windows():
+ return fname + '.exe'
return fname
def validate_install(srcdir, installdir):
- if mesonlib.is_windows():
- # Don't really know how Windows installs should work
- # so skip.
- return ''
+ # List of installed files
info_file = os.path.join(srcdir, 'installed_files.txt')
+ # If this exists, the test does not install any other files
+ noinst_file = 'usr/no-installed-files'
expected = {}
found = {}
- if os.path.exists(info_file):
+ ret_msg = ''
+ # Generate list of expected files
+ if os.path.exists(os.path.join(installdir, noinst_file)):
+ expected[noinst_file] = False
+ elif os.path.exists(info_file):
for line in open(info_file):
- expected[platform_fix_filename(line.strip())] = True
- for root, _, files in os.walk(installdir):
- for fname in files:
- found_name = os.path.join(root, fname)[len(installdir)+1:]
- found[found_name] = True
- expected = set(expected)
- found = set(found)
- missing = expected - found
- for fname in missing:
- return 'Expected file %s missing.' % fname
- extra = found - expected
- for fname in extra:
- return 'Found extra file %s.' % fname
- return ''
+ expected[platform_fix_exe_name(line.strip())] = False
+ # Check if expected files were found
+ for fname in expected:
+ if os.path.exists(os.path.join(installdir, fname)):
+ expected[fname] = True
+ for (fname, found) in expected.items():
+ if not found:
+ ret_msg += 'Expected file {0} missing.\n'.format(fname)
+ # Check if there are any unexpected files
+ found = get_relative_files_list_from_dir(installdir)
+ for fname in found:
+ if fname not in expected:
+ ret_msg += 'Extra file {0} found.\n'.format(fname)
+ return ret_msg
def log_text_file(logfile, testdir, stdo, stde):
global stop, executor, futures
diff --git a/test cases/common/27 library versions/installed_files.txt b/test cases/common/27 library versions/installed_files.txt
index a218d29..e10d1dd 100644
--- a/test cases/common/27 library versions/installed_files.txt
+++ b/test cases/common/27 library versions/installed_files.txt
@@ -1,3 +1 @@
-usr/lib/libsomelib.so
-usr/lib/libsomelib.so.0
-usr/lib/libsomelib.so.1.2.3
+usr/lib/prefixsomelib.suffix
diff --git a/test cases/common/27 library versions/meson.build b/test cases/common/27 library versions/meson.build
index cebcd64..2e2bef7 100644
--- a/test cases/common/27 library versions/meson.build
+++ b/test cases/common/27 library versions/meson.build
@@ -1,6 +1,9 @@
project('library versions', 'c')
-lib = shared_library('somelib', 'lib.c', \
-version : '1.2.3', \
-soversion : '0', \
-install : true)
+shared_library('somelib', 'lib.c',
+ name_prefix : 'prefix',
+ name_suffix : 'suffix',
+ install_dir : 'lib',
+ install : true)
+
+subdir('subdir')
diff --git a/test cases/common/27 library versions/subdir/meson.build b/test cases/common/27 library versions/subdir/meson.build
new file mode 100644
index 0000000..b51033d
--- /dev/null
+++ b/test cases/common/27 library versions/subdir/meson.build
@@ -0,0 +1,8 @@
+# Test that using files generated with configure_file as sources works.
+# We do this inside a subdir so that the path isn't accidentally correct
+# because there is no structure in the build dir.
+genlib = configure_file(input : '../lib.c',
+ output : 'genlib.c',
+ configuration : configuration_data())
+shared_library('genlib', genlib,
+ install : false)
diff --git a/test cases/common/46 library chain/installed_files.txt b/test cases/common/46 library chain/installed_files.txt
index 8d38fd9..c7dab9f 100644
--- a/test cases/common/46 library chain/installed_files.txt
+++ b/test cases/common/46 library chain/installed_files.txt
@@ -1,4 +1 @@
-usr/bin/prog
-usr/lib/liblib1.so
-usr/lib/liblib2.so
-usr/lib/liblib3.so
+usr/bin/prog?exe
diff --git a/test cases/common/46 library chain/subdir/meson.build b/test cases/common/46 library chain/subdir/meson.build
index c165abe..ab71bda 100644
--- a/test cases/common/46 library chain/subdir/meson.build
+++ b/test cases/common/46 library chain/subdir/meson.build
@@ -1,4 +1,4 @@
subdir('subdir2')
subdir('subdir3')
-lib1 = shared_library('lib1', 'lib1.c', install : true, link_with : [lib2, lib3])
+lib1 = shared_library('lib1', 'lib1.c', install : false, link_with : [lib2, lib3])
diff --git a/test cases/common/46 library chain/subdir/subdir2/meson.build b/test cases/common/46 library chain/subdir/subdir2/meson.build
index 2281e4b..befd94d 100644
--- a/test cases/common/46 library chain/subdir/subdir2/meson.build
+++ b/test cases/common/46 library chain/subdir/subdir2/meson.build
@@ -1 +1 @@
-lib2 = shared_library('lib2', 'lib2.c', install : true)
+lib2 = shared_library('lib2', 'lib2.c', install : false)
diff --git a/test cases/common/46 library chain/subdir/subdir3/meson.build b/test cases/common/46 library chain/subdir/subdir3/meson.build
index f049c79..7bd249a 100644
--- a/test cases/common/46 library chain/subdir/subdir3/meson.build
+++ b/test cases/common/46 library chain/subdir/subdir3/meson.build
@@ -1 +1 @@
-lib3 = shared_library('lib3', 'lib3.c', install : true)
+lib3 = shared_library('lib3', 'lib3.c', install : false)
diff --git a/test cases/common/49 subproject/installed_files.txt b/test cases/common/49 subproject/installed_files.txt
index f2e1e27..dc09fb7 100644
--- a/test cases/common/49 subproject/installed_files.txt
+++ b/test cases/common/49 subproject/installed_files.txt
@@ -1,3 +1,2 @@
-usr/bin/user
-usr/lib/libsublib.so
+usr/bin/user?exe
usr/share/sublib/sublib.depmf
diff --git a/test cases/common/49 subproject/subprojects/sublib/meson.build b/test cases/common/49 subproject/subprojects/sublib/meson.build
index 269f815..acaf1bf 100644
--- a/test cases/common/49 subproject/subprojects/sublib/meson.build
+++ b/test cases/common/49 subproject/subprojects/sublib/meson.build
@@ -13,7 +13,7 @@ if meson.project_version() != '1.0.0'
endif
i = include_directories('include')
-l = shared_library('sublib', 'sublib.c', include_directories : i, install : true,
+l = shared_library('sublib', 'sublib.c', include_directories : i, install : false,
c_args : '-DBUILDING_SUB=2')
t = executable('simpletest', 'simpletest.c', include_directories : i, link_with : l)
test('plain', t)
diff --git a/test cases/common/51 pkgconfig-gen/installed_files.txt b/test cases/common/51 pkgconfig-gen/installed_files.txt
index cc5d34b..d6a23d7 100644
--- a/test cases/common/51 pkgconfig-gen/installed_files.txt
+++ b/test cases/common/51 pkgconfig-gen/installed_files.txt
@@ -1,3 +1,2 @@
usr/include/simple.h
-usr/lib/libsimple.so
usr/lib/pkgconfig/simple.pc
diff --git a/test cases/common/51 pkgconfig-gen/meson.build b/test cases/common/51 pkgconfig-gen/meson.build
index 4044b3d..b5f6921 100644
--- a/test cases/common/51 pkgconfig-gen/meson.build
+++ b/test cases/common/51 pkgconfig-gen/meson.build
@@ -2,7 +2,7 @@ project('pkgconfig-gen', 'c')
pkgg = import('pkgconfig')
-lib = shared_library('simple', 'simple.c', install : true)
+lib = shared_library('simple', 'simple.c')
libver = '1.0'
h = install_headers('simple.h')
diff --git a/test cases/common/52 custom install dirs/installed_files.txt b/test cases/common/52 custom install dirs/installed_files.txt
index dc495e2..1b8b561 100644
--- a/test cases/common/52 custom install dirs/installed_files.txt
+++ b/test cases/common/52 custom install dirs/installed_files.txt
@@ -1,4 +1,4 @@
-usr/dib/dab/dub/prog
+usr/dib/dab/dub/prog?exe
usr/some/dir/sample.h
usr/woman/prog.1.gz
usr/meow/datafile.cat
diff --git a/test cases/common/6 linkshared/installed_files.txt b/test cases/common/6 linkshared/installed_files.txt
index 2a4c330..c7dab9f 100644
--- a/test cases/common/6 linkshared/installed_files.txt
+++ b/test cases/common/6 linkshared/installed_files.txt
@@ -1,2 +1 @@
-usr/bin/prog
-usr/lib/libmylib.so
+usr/bin/prog?exe
diff --git a/test cases/common/6 linkshared/meson.build b/test cases/common/6 linkshared/meson.build
index 1ecb052..846b4a0 100644
--- a/test cases/common/6 linkshared/meson.build
+++ b/test cases/common/6 linkshared/meson.build
@@ -2,7 +2,7 @@ project('shared library linking test', 'c', 'cpp')
lib = shared_library('mylib',
'libfile.c' # Split to different lines before and after the comma to test parser.
- , install : true)
+ , install : false) # Don't install libraries in common tests; the path is platform-specific
exe = executable('prog', 'main.c', link_with : lib, install : true)
test('runtest', exe)
diff --git a/test cases/common/60 install script/installed_files.txt b/test cases/common/60 install script/installed_files.txt
index 58315f9..94e3164 100644
--- a/test cases/common/60 install script/installed_files.txt
+++ b/test cases/common/60 install script/installed_files.txt
@@ -1,2 +1,2 @@
-usr/bin/prog
+usr/bin/prog?exe
usr/diiba/daaba/file.dat
diff --git a/test cases/common/60 install script/meson.build b/test cases/common/60 install script/meson.build
index 66db0a7..6cab840 100644
--- a/test cases/common/60 install script/meson.build
+++ b/test cases/common/60 install script/meson.build
@@ -1,8 +1,8 @@
project('custom install script', 'c')
if meson.get_compiler('c').get_id() == 'msvc'
- meson.add_install_script('myinstall.bat')
+ install_data('no-installed-files', install_dir : '')
else
meson.add_install_script('myinstall.sh')
+ executable('prog', 'prog.c', install : true)
endif
-executable('prog', 'prog.c', install : true)
diff --git a/test cases/common/60 install script/myinstall.bat b/test cases/common/60 install script/myinstall.bat
deleted file mode 100644
index 7036077..0000000
--- a/test cases/common/60 install script/myinstall.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-@ECHO OFF
-
-echo At this point we could do something.
diff --git a/test cases/common/60 install script/myinstall.sh b/test cases/common/60 install script/myinstall.sh
index 42a415e..79512c9 100755
--- a/test cases/common/60 install script/myinstall.sh
+++ b/test cases/common/60 install script/myinstall.sh
@@ -4,9 +4,7 @@ set -eu
echo Starting custom installation step
-# These commands fail on Windows, but we don't really care.
-
mkdir -p "${DESTDIR}${MESON_INSTALL_PREFIX}/diiba/daaba"
touch "${DESTDIR}${MESON_INSTALL_PREFIX}/diiba/daaba/file.dat"
-echo Finishing custom install step
+echo Finished custom install step
diff --git a/test cases/common/60 install script/no-installed-files b/test cases/common/60 install script/no-installed-files
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/60 install script/no-installed-files
diff --git a/test cases/common/67 foreach/installed_files.txt b/test cases/common/67 foreach/installed_files.txt
index 36c6386..2930ff0 100644
--- a/test cases/common/67 foreach/installed_files.txt
+++ b/test cases/common/67 foreach/installed_files.txt
@@ -1,3 +1,3 @@
-usr/bin/prog1
-usr/bin/prog2
-usr/bin/prog3
+usr/bin/prog1?exe
+usr/bin/prog2?exe
+usr/bin/prog3?exe
diff --git a/test cases/common/76 configure file in custom target/src/meson.build b/test cases/common/76 configure file in custom target/src/meson.build
index b049e95..cbbce5c 100644
--- a/test cases/common/76 configure file in custom target/src/meson.build
+++ b/test cases/common/76 configure file in custom target/src/meson.build
@@ -2,3 +2,19 @@ custom_target('thing',
output : 'final.dat',
input : cfile,
command : [find_program('mycompiler.py'), '@INPUT@', '@OUTPUT@'])
+
+# Test usage of a `configure_file` as part of the command list
+py3 = find_program('python3', required : false)
+if not py3.found()
+ # Maybe 'python' is Python 3
+ py3 = find_program('python')
+endif
+
+compiler = configure_file(input : 'mycompiler.py',
+ output : 'mycompiler2.py',
+ configuration : configuration_data())
+
+custom_target('thing2',
+output : 'final2.dat',
+input : cfile,
+command : [py3, compiler, '@INPUT@', '@OUTPUT@'])
diff --git a/test cases/common/8 install/installed_files.txt b/test cases/common/8 install/installed_files.txt
index 1e9166b..3bc66ab 100644
--- a/test cases/common/8 install/installed_files.txt
+++ b/test cases/common/8 install/installed_files.txt
@@ -1,3 +1,2 @@
-usr/bin/prog
-usr/lib/libshar.so
+usr/bin/prog?exe
usr/lib/libstat.a
diff --git a/test cases/common/8 install/meson.build b/test cases/common/8 install/meson.build
index 677a2ee..92ff3a0 100644
--- a/test cases/common/8 install/meson.build
+++ b/test cases/common/8 install/meson.build
@@ -1,5 +1,4 @@
project('install test', 'c')
stlib = static_library('stat', 'stat.c', install : true)
-shlib = shared_library('shar', 'shar.c', install : true)
exe = executable('prog', 'prog.c', install : true)
diff --git a/test cases/common/8 install/shar.c b/test cases/common/8 install/shar.c
deleted file mode 100644
index 9c816a6..0000000
--- a/test cases/common/8 install/shar.c
+++ /dev/null
@@ -1 +0,0 @@
-int func() { return 15; }
diff --git a/test cases/csharp/2 library/installed_files.txt b/test cases/csharp/2 library/installed_files.txt
index b830634..4ebea55 100644
--- a/test cases/csharp/2 library/installed_files.txt
+++ b/test cases/csharp/2 library/installed_files.txt
@@ -1,2 +1,2 @@
usr/bin/prog.exe
-usr/lib/libhelper.dll
+usr/lib/helper.dll
diff --git a/test cases/linuxlike/7 library versions/installed_files.txt b/test cases/linuxlike/7 library versions/installed_files.txt
new file mode 100644
index 0000000..b997e53
--- /dev/null
+++ b/test cases/linuxlike/7 library versions/installed_files.txt
@@ -0,0 +1,9 @@
+usr/lib/libsome.so
+usr/lib/libsome.so.0
+usr/lib/libsome.so.1.2.3
+usr/lib/libnoversion.so
+usr/lib/libonlyversion.so
+usr/lib/libonlyversion.so.1
+usr/lib/libonlyversion.so.1.4.5
+usr/lib/libonlysoversion.so
+usr/lib/libonlysoversion.so.5
diff --git a/test cases/linuxlike/7 library versions/lib.c b/test cases/linuxlike/7 library versions/lib.c
new file mode 100644
index 0000000..67b6f4d
--- /dev/null
+++ b/test cases/linuxlike/7 library versions/lib.c
@@ -0,0 +1,3 @@
+int myFunc() {
+ return 55;
+}
diff --git a/test cases/linuxlike/7 library versions/meson.build b/test cases/linuxlike/7 library versions/meson.build
new file mode 100644
index 0000000..504aa4e
--- /dev/null
+++ b/test cases/linuxlike/7 library versions/meson.build
@@ -0,0 +1,18 @@
+project('library versions', 'c')
+
+shared_library('some', 'lib.c',
+ version : '1.2.3',
+ soversion : '0',
+ install : true)
+
+shared_library('noversion', 'lib.c',
+ install : true)
+
+shared_library('onlyversion', 'lib.c',
+ version : '1.4.5',
+ install : true)
+
+shared_library('onlysoversion', 'lib.c',
+ # Also test that int soversion is acceptable
+ soversion : 5,
+ install : true)
diff --git a/test cases/linuxlike/8 subproject library install/installed_files.txt b/test cases/linuxlike/8 subproject library install/installed_files.txt
new file mode 100644
index 0000000..5c4a301
--- /dev/null
+++ b/test cases/linuxlike/8 subproject library install/installed_files.txt
@@ -0,0 +1,3 @@
+usr/lib/libsublib.so
+usr/lib/libsublib.so.5
+usr/lib/libsublib.so.2.1.0
diff --git a/test cases/linuxlike/8 subproject library install/meson.build b/test cases/linuxlike/8 subproject library install/meson.build
new file mode 100644
index 0000000..63e57cf
--- /dev/null
+++ b/test cases/linuxlike/8 subproject library install/meson.build
@@ -0,0 +1,6 @@
+project('subproj lib install', 'c',
+ version : '2.3.4',
+ license : 'mylicense')
+
+# Test that the subproject library gets installed
+subproject('sublib', version : '1.0.0')
diff --git a/test cases/linuxlike/8 subproject library install/subprojects/sublib/include/subdefs.h b/test cases/linuxlike/8 subproject library install/subprojects/sublib/include/subdefs.h
new file mode 100644
index 0000000..681c7b8
--- /dev/null
+++ b/test cases/linuxlike/8 subproject library install/subprojects/sublib/include/subdefs.h
@@ -0,0 +1,21 @@
+#ifndef SUBDEFS_H_
+#define SUBDEFS_H_
+
+#if defined _WIN32 || defined __CYGWIN__
+#if defined BUILDING_SUB
+ #define DLL_PUBLIC __declspec(dllexport)
+#else
+ #define DLL_PUBLIC __declspec(dllimport)
+#endif
+#else
+ #if defined __GNUC__
+ #define DLL_PUBLIC __attribute__ ((visibility("default")))
+ #else
+ #pragma message ("Compiler does not support symbol visibility.")
+ #define DLL_PUBLIC
+ #endif
+#endif
+
+int DLL_PUBLIC subfunc();
+
+#endif
diff --git a/test cases/linuxlike/8 subproject library install/subprojects/sublib/meson.build b/test cases/linuxlike/8 subproject library install/subprojects/sublib/meson.build
new file mode 100644
index 0000000..97658fa
--- /dev/null
+++ b/test cases/linuxlike/8 subproject library install/subprojects/sublib/meson.build
@@ -0,0 +1,10 @@
+project('subproject', 'c',
+ version : '1.0.0',
+ license : ['sublicense1', 'sublicense2'])
+
+i = include_directories('include')
+shared_library('sublib', 'sublib.c',
+ version : '2.1.0',
+ soversion : 5,
+ include_directories : i, install : true,
+ c_args : '-DBUILDING_SUB=2')
diff --git a/test cases/linuxlike/8 subproject library install/subprojects/sublib/sublib.c b/test cases/linuxlike/8 subproject library install/subprojects/sublib/sublib.c
new file mode 100644
index 0000000..c13326b
--- /dev/null
+++ b/test cases/linuxlike/8 subproject library install/subprojects/sublib/sublib.c
@@ -0,0 +1,5 @@
+#include<subdefs.h>
+
+int DLL_PUBLIC subfunc() {
+ return 42;
+}
diff --git a/test cases/osx/2 library versions/installed_files.txt b/test cases/osx/2 library versions/installed_files.txt
new file mode 100644
index 0000000..66470ab
--- /dev/null
+++ b/test cases/osx/2 library versions/installed_files.txt
@@ -0,0 +1,4 @@
+usr/lib/libsome.0.dylib
+usr/lib/libnoversion.dylib
+usr/lib/libonlyversion.1.dylib
+usr/lib/libonlysoversion.5.dylib
diff --git a/test cases/osx/2 library versions/lib.c b/test cases/osx/2 library versions/lib.c
new file mode 100644
index 0000000..67b6f4d
--- /dev/null
+++ b/test cases/osx/2 library versions/lib.c
@@ -0,0 +1,3 @@
+int myFunc() {
+ return 55;
+}
diff --git a/test cases/osx/2 library versions/meson.build b/test cases/osx/2 library versions/meson.build
new file mode 100644
index 0000000..504aa4e
--- /dev/null
+++ b/test cases/osx/2 library versions/meson.build
@@ -0,0 +1,18 @@
+project('library versions', 'c')
+
+shared_library('some', 'lib.c',
+ version : '1.2.3',
+ soversion : '0',
+ install : true)
+
+shared_library('noversion', 'lib.c',
+ install : true)
+
+shared_library('onlyversion', 'lib.c',
+ version : '1.4.5',
+ install : true)
+
+shared_library('onlysoversion', 'lib.c',
+ # Also test that int soversion is acceptable
+ soversion : 5,
+ install : true)
diff --git a/test cases/rust/1 basic/installed_files.txt b/test cases/rust/1 basic/installed_files.txt
index 61eda27..c7dab9f 100644
--- a/test cases/rust/1 basic/installed_files.txt
+++ b/test cases/rust/1 basic/installed_files.txt
@@ -1 +1 @@
-usr/bin/prog
+usr/bin/prog?exe
diff --git a/test cases/rust/2 sharedlib/installed_files.txt b/test cases/rust/2 sharedlib/installed_files.txt
index 5a7c7d6..85acff2 100644
--- a/test cases/rust/2 sharedlib/installed_files.txt
+++ b/test cases/rust/2 sharedlib/installed_files.txt
@@ -1,2 +1,2 @@
-usr/bin/prog
+usr/bin/prog?exe
usr/lib/libstuff.rlib
diff --git a/test cases/rust/3 staticlib/installed_files.txt b/test cases/rust/3 staticlib/installed_files.txt
index 5a7c7d6..85acff2 100644
--- a/test cases/rust/3 staticlib/installed_files.txt
+++ b/test cases/rust/3 staticlib/installed_files.txt
@@ -1,2 +1,2 @@
-usr/bin/prog
+usr/bin/prog?exe
usr/lib/libstuff.rlib
diff --git a/test cases/windows/7 mingw dll versioning/installed_files.txt b/test cases/windows/7 mingw dll versioning/installed_files.txt
new file mode 100644
index 0000000..8c2a8f2
--- /dev/null
+++ b/test cases/windows/7 mingw dll versioning/installed_files.txt
@@ -0,0 +1,4 @@
+usr/bin/libsome-0.dll
+usr/lib/libsome.dll.a
+usr/bin/libnoversion.dll
+usr/lib/libnoversion.dll.a
diff --git a/test cases/windows/7 mingw dll versioning/lib.c b/test cases/windows/7 mingw dll versioning/lib.c
new file mode 100644
index 0000000..cf7dfdd
--- /dev/null
+++ b/test cases/windows/7 mingw dll versioning/lib.c
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int myFunc() {
+ return 55;
+}
diff --git a/test cases/windows/7 mingw dll versioning/meson.build b/test cases/windows/7 mingw dll versioning/meson.build
new file mode 100644
index 0000000..23a3343
--- /dev/null
+++ b/test cases/windows/7 mingw dll versioning/meson.build
@@ -0,0 +1,17 @@
+project('mingw dll versioning', 'c')
+
+cc = meson.get_compiler('c')
+
+# Test that MinGW/GCC creates correctly-named dll files and dll.a files,
+# and also installs them in the right place
+if cc.get_id() != 'msvc'
+ shared_library('some', 'lib.c',
+ version : '1.2.3',
+ soversion : '0',
+ install : true)
+
+ shared_library('noversion', 'lib.c',
+ install : true)
+else
+ install_data('no-installed-files', install_dir : '')
+endif
diff --git a/test cases/windows/7 mingw dll versioning/no-installed-files b/test cases/windows/7 mingw dll versioning/no-installed-files
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/windows/7 mingw dll versioning/no-installed-files
diff --git a/test cases/windows/8 msvc dll versioning/installed_files.txt b/test cases/windows/8 msvc dll versioning/installed_files.txt
new file mode 100644
index 0000000..5f6e26a
--- /dev/null
+++ b/test cases/windows/8 msvc dll versioning/installed_files.txt
@@ -0,0 +1,4 @@
+usr/bin/some-0.dll
+usr/lib/some.lib
+usr/bin/noversion.dll
+usr/lib/noversion.lib
diff --git a/test cases/windows/8 msvc dll versioning/lib.c b/test cases/windows/8 msvc dll versioning/lib.c
new file mode 100644
index 0000000..cf7dfdd
--- /dev/null
+++ b/test cases/windows/8 msvc dll versioning/lib.c
@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int myFunc() {
+ return 55;
+}
diff --git a/test cases/windows/8 msvc dll versioning/meson.build b/test cases/windows/8 msvc dll versioning/meson.build
new file mode 100644
index 0000000..0c36173
--- /dev/null
+++ b/test cases/windows/8 msvc dll versioning/meson.build
@@ -0,0 +1,16 @@
+project('msvc dll versioning', 'c')
+
+cc = meson.get_compiler('c')
+
+# Test that MSVC creates foo-0.dll and bar.dll
+if cc.get_id() == 'msvc'
+ shared_library('some', 'lib.c',
+ version : '1.2.3',
+ soversion : '0',
+ install : true)
+
+ shared_library('noversion', 'lib.c',
+ install : true)
+else
+ install_data('no-installed-files', install_dir : '')
+endif
diff --git a/test cases/windows/8 msvc dll versioning/no-installed-files b/test cases/windows/8 msvc dll versioning/no-installed-files
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/windows/8 msvc dll versioning/no-installed-files