Merge pull request #417 from nirbheek/dll-paths

Fix filenames and paths used in DLL shared library generation
pull/636/head
Jussi Pakkanen 8 years ago committed by GitHub
commit 64919b1c74
  1. 20
      mesonbuild/backend/backends.py
  2. 35
      mesonbuild/backend/ninjabackend.py
  3. 229
      mesonbuild/backend/vs2010backend.py
  4. 8
      mesonbuild/backend/xcodebackend.py
  5. 310
      mesonbuild/build.py
  6. 35
      mesonbuild/compilers.py
  7. 87
      mesonbuild/environment.py
  8. 10
      mesonbuild/mesonlib.py
  9. 2
      mesonbuild/scripts/meson_install.py
  10. 70
      run_tests.py
  11. 4
      test cases/common/27 library versions/installed_files.txt
  12. 11
      test cases/common/27 library versions/meson.build
  13. 8
      test cases/common/27 library versions/subdir/meson.build
  14. 5
      test cases/common/46 library chain/installed_files.txt
  15. 2
      test cases/common/46 library chain/subdir/meson.build
  16. 2
      test cases/common/46 library chain/subdir/subdir2/meson.build
  17. 2
      test cases/common/46 library chain/subdir/subdir3/meson.build
  18. 3
      test cases/common/49 subproject/installed_files.txt
  19. 2
      test cases/common/49 subproject/subprojects/sublib/meson.build
  20. 1
      test cases/common/51 pkgconfig-gen/installed_files.txt
  21. 2
      test cases/common/51 pkgconfig-gen/meson.build
  22. 2
      test cases/common/52 custom install dirs/installed_files.txt
  23. 3
      test cases/common/6 linkshared/installed_files.txt
  24. 2
      test cases/common/6 linkshared/meson.build
  25. 2
      test cases/common/60 install script/installed_files.txt
  26. 4
      test cases/common/60 install script/meson.build
  27. 3
      test cases/common/60 install script/myinstall.bat
  28. 4
      test cases/common/60 install script/myinstall.sh
  29. 0
      test cases/common/60 install script/no-installed-files
  30. 6
      test cases/common/67 foreach/installed_files.txt
  31. 16
      test cases/common/76 configure file in custom target/src/meson.build
  32. 3
      test cases/common/8 install/installed_files.txt
  33. 1
      test cases/common/8 install/meson.build
  34. 1
      test cases/common/8 install/shar.c
  35. 2
      test cases/csharp/2 library/installed_files.txt
  36. 9
      test cases/linuxlike/7 library versions/installed_files.txt
  37. 3
      test cases/linuxlike/7 library versions/lib.c
  38. 18
      test cases/linuxlike/7 library versions/meson.build
  39. 3
      test cases/linuxlike/8 subproject library install/installed_files.txt
  40. 6
      test cases/linuxlike/8 subproject library install/meson.build
  41. 21
      test cases/linuxlike/8 subproject library install/subprojects/sublib/include/subdefs.h
  42. 10
      test cases/linuxlike/8 subproject library install/subprojects/sublib/meson.build
  43. 5
      test cases/linuxlike/8 subproject library install/subprojects/sublib/sublib.c
  44. 4
      test cases/osx/2 library versions/installed_files.txt
  45. 3
      test cases/osx/2 library versions/lib.c
  46. 18
      test cases/osx/2 library versions/meson.build
  47. 2
      test cases/rust/1 basic/installed_files.txt
  48. 2
      test cases/rust/2 sharedlib/installed_files.txt
  49. 2
      test cases/rust/3 staticlib/installed_files.txt
  50. 4
      test cases/windows/7 mingw dll versioning/installed_files.txt
  51. 6
      test cases/windows/7 mingw dll versioning/lib.c
  52. 17
      test cases/windows/7 mingw dll versioning/meson.build
  53. 0
      test cases/windows/7 mingw dll versioning/no-installed-files
  54. 4
      test cases/windows/8 msvc dll versioning/installed_files.txt
  55. 6
      test cases/windows/8 msvc dll versioning/lib.c
  56. 16
      test cases/windows/8 msvc dll versioning/meson.build
  57. 0
      test cases/windows/8 msvc dll versioning/no-installed-files

@ -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):

@ -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:

@ -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)

@ -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

@ -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]

@ -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)

@ -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':

@ -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)):

@ -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))

@ -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

@ -1,3 +1 @@
usr/lib/libsomelib.so
usr/lib/libsomelib.so.0
usr/lib/libsomelib.so.1.2.3
usr/lib/prefixsomelib.suffix

@ -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')

@ -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)

@ -1,4 +1 @@
usr/bin/prog
usr/lib/liblib1.so
usr/lib/liblib2.so
usr/lib/liblib3.so
usr/bin/prog?exe

@ -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])

@ -1 +1 @@
lib2 = shared_library('lib2', 'lib2.c', install : true)
lib2 = shared_library('lib2', 'lib2.c', install : false)

@ -1 +1 @@
lib3 = shared_library('lib3', 'lib3.c', install : true)
lib3 = shared_library('lib3', 'lib3.c', install : false)

@ -1,3 +1,2 @@
usr/bin/user
usr/lib/libsublib.so
usr/bin/user?exe
usr/share/sublib/sublib.depmf

@ -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)

@ -1,3 +1,2 @@
usr/include/simple.h
usr/lib/libsimple.so
usr/lib/pkgconfig/simple.pc

@ -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')

@ -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

@ -1,2 +1 @@
usr/bin/prog
usr/lib/libmylib.so
usr/bin/prog?exe

@ -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)

@ -1,2 +1,2 @@
usr/bin/prog
usr/bin/prog?exe
usr/diiba/daaba/file.dat

@ -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)

@ -1,3 +0,0 @@
@ECHO OFF
echo At this point we could do something.

@ -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

@ -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

@ -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@'])

@ -1,3 +1,2 @@
usr/bin/prog
usr/lib/libshar.so
usr/bin/prog?exe
usr/lib/libstat.a

@ -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)

@ -1 +0,0 @@
int func() { return 15; }

@ -1,2 +1,2 @@
usr/bin/prog.exe
usr/lib/libhelper.dll
usr/lib/helper.dll

@ -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

@ -0,0 +1,3 @@
int myFunc() {
return 55;
}

@ -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)

@ -0,0 +1,3 @@
usr/lib/libsublib.so
usr/lib/libsublib.so.5
usr/lib/libsublib.so.2.1.0

@ -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')

@ -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

@ -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')

@ -0,0 +1,5 @@
#include<subdefs.h>
int DLL_PUBLIC subfunc() {
return 42;
}

@ -0,0 +1,4 @@
usr/lib/libsome.0.dylib
usr/lib/libnoversion.dylib
usr/lib/libonlyversion.1.dylib
usr/lib/libonlysoversion.5.dylib

@ -0,0 +1,3 @@
int myFunc() {
return 55;
}

@ -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)

@ -1 +1 @@
usr/bin/prog
usr/bin/prog?exe

@ -1,2 +1,2 @@
usr/bin/prog
usr/bin/prog?exe
usr/lib/libstuff.rlib

@ -1,2 +1,2 @@
usr/bin/prog
usr/bin/prog?exe
usr/lib/libstuff.rlib

@ -0,0 +1,4 @@
usr/bin/libsome-0.dll
usr/lib/libsome.dll.a
usr/bin/libnoversion.dll
usr/lib/libnoversion.dll.a

@ -0,0 +1,6 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
int myFunc() {
return 55;
}

@ -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

@ -0,0 +1,4 @@
usr/bin/some-0.dll
usr/lib/some.lib
usr/bin/noversion.dll
usr/lib/noversion.lib

@ -0,0 +1,6 @@
#ifdef _WIN32
__declspec(dllexport)
#endif
int myFunc() {
return 55;
}

@ -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
Loading…
Cancel
Save