diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index fcc3d3b56..d2693b252 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 a1bccc26a..b84a14491 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 d262e6bc3..12f224c51 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 0ce90cedc..e64866d08 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 c60f37f15..b610bb8d9 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 8ee86bc75..55a438430 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 3868301de..47414caa3 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 837f78ac3..1672d95de 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 = ' + +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 000000000..66470ab59 --- /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 000000000..67b6f4d00 --- /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 000000000..504aa4e22 --- /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 61eda27c5..c7dab9f6f 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 5a7c7d6da..85acff291 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 5a7c7d6da..85acff291 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 000000000..8c2a8f296 --- /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 000000000..cf7dfdd07 --- /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 000000000..23a33435b --- /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 000000000..e69de29bb 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 000000000..5f6e26a1d --- /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 000000000..cf7dfdd07 --- /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 000000000..0c361735b --- /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 000000000..e69de29bb