diff options
65 files changed, 4417 insertions, 3478 deletions
diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 805ceaa..e709fd5 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -127,7 +127,7 @@ This also creates two targets for translations `help-$project-update-po` and `he * `sources`: list of pages * `media`: list of media such as images -* `symlink_media`: if media should be symlinked not copied (defaults to `true` since 0.41.0) +* `symlink_media`: if media should be symlinked not copied (defaults to `true` since 0.42.0) * `languages`: list of languages for translations Note that very old versions of yelp may not support symlinked media; At least 3.10 should work. diff --git a/docs/markdown/Qt5-module.md b/docs/markdown/Qt5-module.md index 2b733bb..64d2920 100644 --- a/docs/markdown/Qt5-module.md +++ b/docs/markdown/Qt5-module.md @@ -8,8 +8,11 @@ This method takes four keyword arguments, `moc_headers`, `moc_sources`, `ui_file ```meson qt5 = import('qt5') -qt5_dep = dependency('qt5', ...) +qt5_dep = dependency('qt5', modules: ['Core', 'Gui']) moc_files = qt5.preprocess(moc_headers : 'myclass.h') executable('myprog', 'main.cpp', 'myclass.cpp', moc_files, dependencies : qt5_dep) ``` + + +The 'modules' argument is used to include Qt modules in the project. See the Qt documentation for the [list of modules](http://doc.qt.io/qt-5/qtmodules.html). diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 492666b..dd77d6d 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -510,11 +510,12 @@ Joins the given strings into a file system path segment. For example `join_paths Builds a library that is either static or shared depending on the value of `default_library` user option. You should use this instead of [`shared_library`](#shared_library) or [`static_library`](#static_library) most of the time. This allows you to toggle your entire project (including subprojects) from shared to static with only one option. -The keyword arguments for this are the same as for [`executable`](#executable) with the following addition: +The keyword arguments for this are the same as for [`executable`](#executable) with the following additions: - `name_prefix` the string that will be used as the suffix for the target by overriding the default (only used for libraries). By default this is `lib` on all platforms and compilers except with MSVC where it is omitted. +- `rust_crate_type` specifies the crate type for Rust libraries. Defaults to `dylib` for shared libraries and `rlib` for static libraries. -`static_library` and `shared_library` also accept this keyword argument. +`static_library` and `shared_library` also accept these keyword arguments. ### message() diff --git a/docs/markdown/Release-notes-for-0.42.0.md b/docs/markdown/Release-notes-for-0.42.0.md index dcb86c3..7c66870 100644 --- a/docs/markdown/Release-notes-for-0.42.0.md +++ b/docs/markdown/Release-notes-for-0.42.0.md @@ -11,3 +11,10 @@ short-description: Release notes for 0.42 (preliminary) Creating distribution tarballs can now be made out of projects based on Mercurial. As before, this remains possible only with the Ninja backend. + +## Keyword argument verification + +Meson will now check the keyword arguments used when calling any function +and print a warning if any of the keyword arguments is not known. In the +future this will become a hard error. + diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index b1f6d85..e8090e7 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1,4 +1,4 @@ -# Copyright 2012-2016 The Meson development team +# Copyright 2012-2017 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,9 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import shlex -import os, sys, pickle, re -import subprocess, shutil +import os, pickle, re, shlex, shutil, subprocess, sys from collections import OrderedDict from . import backends @@ -25,6 +23,7 @@ from .. import mlog from .. import dependencies from .. import compilers from ..compilers import CompilerArgs +from ..linkers import ArLinker from ..mesonlib import File, MesonException, OrderedSet from ..mesonlib import get_meson_script, get_compiler_for_source from .backends import CleanTrees, InstallData @@ -1100,7 +1099,13 @@ int dummy; args += ['--library=' + target.name] # Outputted header hname = os.path.join(self.get_target_dir(target), target.vala_header) - args += ['-H', hname, '--use-header'] + args += ['-H', hname] + if self.is_unity(target): + # Without this the declarations will get duplicated in the .c + # files and cause a build failure when all of them are + # #include-d in one .c file. + # https://github.com/mesonbuild/meson/issues/1969 + args += ['--use-header'] valac_outputs.append(hname) # Outputted vapi file vapiname = os.path.join(self.get_target_dir(target), target.vala_vapi) @@ -1159,8 +1164,10 @@ int dummy; args = ['--crate-type'] if isinstance(target, build.Executable): cratetype = 'bin' + elif hasattr(target, 'rust_crate_type'): + cratetype = target.rust_crate_type elif isinstance(target, build.SharedLibrary): - cratetype = 'rlib' + cratetype = 'dylib' elif isinstance(target, build.StaticLibrary): cratetype = 'rlib' else: @@ -1179,6 +1186,36 @@ int dummy; if d == '': d = '.' args += ['-L', d] + has_shared_deps = False + for dep in target.get_dependencies(): + if isinstance(dep, build.SharedLibrary): + has_shared_deps = True + if isinstance(target, build.SharedLibrary) or has_shared_deps: + # add prefer-dynamic if any of the Rust libraries we link + # against are dynamic, otherwise we'll end up with + # multiple implementations of crates + args += ['-C', 'prefer-dynamic'] + + # build the usual rpath arguments as well... + + # Set runtime-paths so we can run executables without needing to set + # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. + if '/' in target.name or '\\' in target.name: + # Target names really should not have slashes in them, but + # unfortunately we did not check for that and some downstream projects + # now have them. Once slashes are forbidden, remove this bit. + target_slashname_workaround_dir = os.path.join(os.path.split(target.name)[0], + self.get_target_dir(target)) + else: + target_slashname_workaround_dir = self.get_target_dir(target) + rpath_args = rustc.build_rpath_args(self.environment.get_build_dir(), + target_slashname_workaround_dir, + self.determine_rpath_dirs(target), + target.install_rpath) + # ... but then add rustc's sysroot to account for rustup + # installations + for rpath_arg in rpath_args: + args += ['-C', 'link-arg=' + rpath_arg + ':' + os.path.join(rustc.get_sysroot(), 'lib')] element = NinjaBuildElement(self.all_outputs, target_name, 'rust_COMPILER', relsrc) if len(orderdeps) > 0: element.add_orderdep(orderdeps) @@ -1186,6 +1223,8 @@ int dummy; element.add_item('targetdep', depfile) element.add_item('cratetype', cratetype) element.write(outfile) + if isinstance(target, build.SharedLibrary): + self.generate_shsym(outfile, target) def swift_module_file_name(self, target): return os.path.join(self.get_target_private_dir(target), @@ -1323,7 +1362,7 @@ int dummy; # gcc-ar blindly pass the --plugin argument to `ar` and you cannot pass # options as arguments while using the @file.rsp syntax. # See: https://github.com/mesonbuild/meson/issues/1646 - if mesonlib.is_windows() and not isinstance(static_linker, compilers.ArLinker): + if mesonlib.is_windows() and not isinstance(static_linker, ArLinker): command_template = ''' command = {executable} @$out.rsp rspfile = $out.rsp rspfile_content = $LINK_ARGS {output_args} $in @@ -1335,7 +1374,7 @@ int dummy; # them out to fix this properly on Windows. See: # https://github.com/mesonbuild/meson/issues/1517 # https://github.com/mesonbuild/meson/issues/1526 - if isinstance(static_linker, compilers.ArLinker) and not mesonlib.is_windows(): + if isinstance(static_linker, ArLinker) and not mesonlib.is_windows(): # `ar` has no options to overwrite archives. It always appends, # which is never what we want. Delete an existing library first if # it exists. https://github.com/mesonbuild/meson/issues/1355 @@ -1567,7 +1606,7 @@ rule FORTRAN_DEP_HACK output_args=' '.join(compiler.get_output_args('$out')), compile_only_args=' '.join(compiler.get_compile_only_args()) ) - description = ' description = Compiling %s object $out.\n' % langname.title() + description = ' description = Compiling %s object $out.\n' % compiler.get_display_language() if compiler.get_id() == 'msvc': deps = ' deps = msvc\n' else: @@ -2321,8 +2360,9 @@ rule FORTRAN_DEP_HACK # Target names really should not have slashes in them, but # unfortunately we did not check for that and some downstream projects # now have them. Once slashes are forbidden, remove this bit. - target_slashname_workaround_dir = os.path.join(os.path.split(target.name)[0], - self.get_target_dir(target)) + target_slashname_workaround_dir = os.path.join( + os.path.split(target.name)[0], + self.get_target_dir(target)) else: target_slashname_workaround_dir = self.get_target_dir(target) commands += linker.build_rpath_args(self.environment.get_build_dir(), diff --git a/mesonbuild/build.py b/mesonbuild/build.py index ba30fec..c73ba3a 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -71,6 +71,7 @@ known_lib_kwargs.update({'version': True, # Only for shared libs 'vala_vapi': True, 'vala_gir': True, 'pic': True, # Only for static libs + 'rust_crate_type': True, # Only for Rust libs }) @@ -1123,6 +1124,14 @@ class StaticLibrary(BuildTarget): super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) if 'cs' in self.compilers: raise InvalidArguments('Static libraries not supported for C#.') + if 'rust' in self.compilers: + # If no crate type is specified, or it's the generic lib type, use rlib + if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib': + mlog.debug('Defaulting Rust static library target crate type to rlib') + self.rust_crate_type = 'rlib' + # Don't let configuration proceed with a non-static crate type + elif self.rust_crate_type not in ['rlib', 'staticlib']: + raise InvalidArguments('Crate type "{0}" invalid for static libraries; must be "rlib" or "staticlib"'.format(self.rust_crate_type)) # 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 @@ -1133,9 +1142,12 @@ class StaticLibrary(BuildTarget): if not hasattr(self, 'prefix'): self.prefix = 'lib' if not hasattr(self, 'suffix'): - # Rust static library crates have .rlib suffix if 'rust' in self.compilers: - self.suffix = 'rlib' + if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'rlib': + # default Rust static library suffix + self.suffix = 'rlib' + elif self.rust_crate_type == 'staticlib': + self.suffix = 'a' else: self.suffix = 'a' self.filename = self.prefix + self.name + '.' + self.suffix @@ -1147,6 +1159,15 @@ class StaticLibrary(BuildTarget): def check_unknown_kwargs(self, kwargs): self.check_unknown_kwargs_int(kwargs, known_lib_kwargs) + def process_kwargs(self, kwargs, environment): + super().process_kwargs(kwargs, environment) + if 'rust_crate_type' in kwargs: + rust_crate_type = kwargs['rust_crate_type'] + if isinstance(rust_crate_type, str): + self.rust_crate_type = rust_crate_type + else: + raise InvalidArguments('Invalid rust_crate_type "{0}": must be a string.'.format(rust_crate_type)) + class SharedLibrary(BuildTarget): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): self.soversion = None @@ -1159,6 +1180,14 @@ class SharedLibrary(BuildTarget): # 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 'rust' in self.compilers: + # If no crate type is specified, or it's the generic lib type, use dylib + if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib': + mlog.debug('Defaulting Rust dynamic library target crate type to "dylib"') + self.rust_crate_type = 'dylib' + # Don't let configuration proceed with a non-dynamic crate type + elif self.rust_crate_type not in ['dylib', 'cdylib']: + raise InvalidArguments('Crate type "{0}" invalid for dynamic libraries; must be "dylib" or "cdylib"'.format(self.rust_crate_type)) if not hasattr(self, 'prefix'): self.prefix = None if not hasattr(self, 'suffix'): @@ -1200,12 +1229,6 @@ class SharedLibrary(BuildTarget): prefix = '' suffix = 'dll' self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}' - # Rust - elif 'rust' in self.compilers: - # 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 @@ -1315,6 +1338,12 @@ class SharedLibrary(BuildTarget): raise InvalidArguments( 'Shared library vs_module_defs must be either a string, ' 'a file object or a Custom Target') + if 'rust_crate_type' in kwargs: + rust_crate_type = kwargs['rust_crate_type'] + if isinstance(rust_crate_type, str): + self.rust_crate_type = rust_crate_type + else: + raise InvalidArguments('Invalid rust_crate_type "{0}": must be a string.'.format(rust_crate_type)) def check_unknown_kwargs(self, kwargs): self.check_unknown_kwargs_int(kwargs, known_lib_kwargs) diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py deleted file mode 100644 index 3b9972f..0000000 --- a/mesonbuild/compilers.py +++ /dev/null @@ -1,3303 +0,0 @@ -# Copyright 2012-2014 The Meson development team - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import re -import shutil -import contextlib -import subprocess, os.path -import tempfile -from .import mesonlib -from . import mlog -from .mesonlib import EnvironmentException, MesonException, version_compare, Popen_safe -from . import coredata - -"""This file contains the data files of all compilers Meson knows -about. To support a new compiler, add its information below. -Also add corresponding autodetection code in environment.py.""" - -header_suffixes = ('h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di') -obj_suffixes = ('o', 'obj', 'res') -lib_suffixes = ('a', 'lib', 'dll', 'dylib', 'so') -# Mapping of language to suffixes of files that should always be in that language -# This means we can't include .h headers here since they could be C, C++, ObjC, etc. -lang_suffixes = { - 'c': ('c',), - 'cpp': ('cpp', 'cc', 'cxx', 'c++', 'hh', 'hpp', 'ipp', 'hxx'), - # f90, f95, f03, f08 are for free-form fortran ('f90' recommended) - # f, for, ftn, fpp are for fixed-form fortran ('f' or 'for' recommended) - 'fortran': ('f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp'), - 'd': ('d', 'di'), - 'objc': ('m',), - 'objcpp': ('mm',), - 'rust': ('rs',), - 'vala': ('vala', 'vapi'), - 'cs': ('cs',), - 'swift': ('swift',), - 'java': ('java',), -} -cpp_suffixes = lang_suffixes['cpp'] + ('h',) -c_suffixes = lang_suffixes['c'] + ('h',) -# List of languages that can be linked with C code directly by the linker -# used in build.py:process_compilers() and build.py:get_dynamic_linker() -clike_langs = ('objcpp', 'objc', 'd', 'cpp', 'c', 'fortran',) -clike_suffixes = () -for _l in clike_langs: - clike_suffixes += lang_suffixes[_l] -clike_suffixes += ('h', 'll', 's') - -# All these are only for C-like languages; see `clike_langs` above. - -def sort_clike(lang): - ''' - Sorting function to sort the list of languages according to - reversed(compilers.clike_langs) and append the unknown langs in the end. - The purpose is to prefer C over C++ for files that can be compiled by - both such as assembly, C, etc. Also applies to ObjC, ObjC++, etc. - ''' - if lang not in clike_langs: - return 1 - return -clike_langs.index(lang) - -def is_header(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - suffix = fname.split('.')[-1] - return suffix in header_suffixes - -def is_source(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - suffix = fname.split('.')[-1].lower() - return suffix in clike_suffixes - -def is_assembly(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - return fname.split('.')[-1].lower() == 's' - -def is_llvm_ir(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - return fname.split('.')[-1] == 'll' - -def is_object(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - suffix = fname.split('.')[-1] - return suffix in obj_suffixes - -def is_library(fname): - if hasattr(fname, 'fname'): - fname = fname.fname - suffix = fname.split('.')[-1] - return suffix in lib_suffixes - -gnulike_buildtype_args = {'plain': [], - # -O0 is passed for improved debugging information with gcc - # See https://github.com/mesonbuild/meson/pull/509 - 'debug': ['-O0', '-g'], - 'debugoptimized': ['-O2', '-g'], - 'release': ['-O3'], - 'minsize': ['-Os', '-g']} - -msvc_buildtype_args = {'plain': [], - 'debug': ["/MDd", "/ZI", "/Ob0", "/Od", "/RTC1"], - 'debugoptimized': ["/MD", "/Zi", "/O2", "/Ob1"], - 'release': ["/MD", "/O2", "/Ob2"], - 'minsize': ["/MD", "/Zi", "/Os", "/Ob1"], - } - -apple_buildtype_linker_args = {'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': [], - } - -gnulike_buildtype_linker_args = {'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': ['-Wl,-O1'], - 'minsize': [], - } - -msvc_buildtype_linker_args = {'plain': [], - 'debug': [], - 'debugoptimized': [], - 'release': [], - 'minsize': ['/INCREMENTAL:NO'], - } - -java_buildtype_args = {'plain': [], - 'debug': ['-g'], - 'debugoptimized': ['-g'], - 'release': [], - 'minsize': [], - } - -rust_buildtype_args = {'plain': [], - 'debug': ['-C', 'debuginfo=2'], - 'debugoptimized': ['-C', 'debuginfo=2', '-C', 'opt-level=2'], - 'release': ['-C', 'opt-level=3'], - 'minsize': [], # In a future release: ['-C', 'opt-level=s'], - } - -d_gdc_buildtype_args = {'plain': [], - 'debug': ['-g', '-O0'], - 'debugoptimized': ['-g', '-O'], - 'release': ['-O3', '-frelease'], - 'minsize': [], - } - -d_ldc_buildtype_args = {'plain': [], - 'debug': ['-g', '-O0'], - 'debugoptimized': ['-g', '-O'], - 'release': ['-O3', '-release'], - 'minsize': [], - } - -d_dmd_buildtype_args = {'plain': [], - 'debug': ['-g'], - 'debugoptimized': ['-g', '-O'], - 'release': ['-O', '-release'], - 'minsize': [], - } - -mono_buildtype_args = {'plain': [], - 'debug': ['-debug'], - 'debugoptimized': ['-debug', '-optimize+'], - 'release': ['-optimize+'], - 'minsize': [], - } - -swift_buildtype_args = {'plain': [], - 'debug': ['-g'], - 'debugoptimized': ['-g', '-O'], - 'release': ['-O'], - 'minsize': [], - } - -gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32', - '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] - -msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib', - 'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib', - 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] - -gnu_color_args = {'auto': ['-fdiagnostics-color=auto'], - 'always': ['-fdiagnostics-color=always'], - 'never': ['-fdiagnostics-color=never'], - } - -clang_color_args = {'auto': ['-Xclang', '-fcolor-diagnostics'], - 'always': ['-Xclang', '-fcolor-diagnostics'], - 'never': ['-Xclang', '-fno-color-diagnostics'], - } - -base_options = {'b_pch': coredata.UserBooleanOption('b_pch', 'Use precompiled headers', True), - 'b_lto': coredata.UserBooleanOption('b_lto', 'Use link time optimization', False), - 'b_sanitize': coredata.UserComboOption('b_sanitize', - 'Code sanitizer to use', - ['none', 'address', 'thread', 'undefined', 'memory'], - 'none'), - 'b_lundef': coredata.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), - 'b_asneeded': coredata.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), - 'b_pgo': coredata.UserComboOption('b_pgo', 'Use profile guide optimization', - ['off', 'generate', 'use'], - 'off'), - 'b_coverage': coredata.UserBooleanOption('b_coverage', - 'Enable coverage tracking.', - False), - 'b_colorout': coredata.UserComboOption('b_colorout', 'Use colored output', - ['auto', 'always', 'never'], - 'always'), - 'b_ndebug': coredata.UserBooleanOption('b_ndebug', - 'Disable asserts', - False), - 'b_staticpic': coredata.UserBooleanOption('b_staticpic', - 'Build static libraries as position independent', - True), - } - -def sanitizer_compile_args(value): - if value == 'none': - return [] - args = ['-fsanitize=' + value] - if value == 'address': - args.append('-fno-omit-frame-pointer') - return args - -def sanitizer_link_args(value): - if value == 'none': - return [] - args = ['-fsanitize=' + value] - return args - -def get_base_compile_args(options, compiler): - args = [] - # FIXME, gcc/clang specific. - try: - if options['b_lto'].value: - args.append('-flto') - except KeyError: - pass - try: - args += compiler.get_colorout_args(options['b_colorout'].value) - except KeyError: - pass - try: - args += sanitizer_compile_args(options['b_sanitize'].value) - except KeyError: - pass - try: - pgo_val = options['b_pgo'].value - if pgo_val == 'generate': - args.append('-fprofile-generate') - elif pgo_val == 'use': - args.append('-fprofile-use') - except KeyError: - pass - try: - if options['b_coverage'].value: - args += compiler.get_coverage_args() - except KeyError: - pass - try: - if options['b_ndebug'].value: - args += ['-DNDEBUG'] - except KeyError: - pass - return args - -def get_base_link_args(options, linker, is_shared_module): - args = [] - # FIXME, gcc/clang specific. - try: - if options['b_lto'].value: - args.append('-flto') - except KeyError: - pass - try: - args += sanitizer_link_args(options['b_sanitize'].value) - except KeyError: - pass - try: - pgo_val = options['b_pgo'].value - if pgo_val == 'generate': - args.append('-fprofile-generate') - elif pgo_val == 'use': - args.append('-fprofile-use') - except KeyError: - pass - try: - if not is_shared_module and 'b_lundef' in linker.base_options and options['b_lundef'].value: - args.append('-Wl,--no-undefined') - except KeyError: - pass - try: - if 'b_asneeded' in linker.base_options and options['b_asneeded'].value: - args.append('-Wl,--as-needed') - except KeyError: - pass - try: - if options['b_coverage'].value: - args += linker.get_coverage_link_args() - except KeyError: - pass - return args - -class CrossNoRunException(MesonException): - pass - -class RunResult: - def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'): - self.compiled = compiled - self.returncode = returncode - self.stdout = stdout - self.stderr = stderr - -class CompilerArgs(list): - ''' - Class derived from list() that manages a list of compiler arguments. Should - be used while constructing compiler arguments from various sources. Can be - operated with ordinary lists, so this does not need to be used everywhere. - - All arguments must be inserted and stored in GCC-style (-lfoo, -Idir, etc) - and can converted to the native type of each compiler by using the - .to_native() method to which you must pass an instance of the compiler or - the compiler class. - - New arguments added to this class (either with .append(), .extend(), or +=) - are added in a way that ensures that they override previous arguments. - For example: - - >>> a = ['-Lfoo', '-lbar'] - >>> a += ['-Lpho', '-lbaz'] - >>> print(a) - ['-Lpho', '-Lfoo', '-lbar', '-lbaz'] - - Arguments will also be de-duped if they can be de-duped safely. - - Note that because of all this, this class is not commutative and does not - preserve the order of arguments if it is safe to not. For example: - >>> ['-Ifoo', '-Ibar'] + ['-Ifez', '-Ibaz', '-Werror'] - ['-Ifez', '-Ibaz', '-Ifoo', '-Ibar', '-Werror'] - >>> ['-Ifez', '-Ibaz', '-Werror'] + ['-Ifoo', '-Ibar'] - ['-Ifoo', '-Ibar', '-Ifez', '-Ibaz', '-Werror'] - - ''' - # NOTE: currently this class is only for C-like compilers, but it can be - # extended to other languages easily. Just move the following to the - # compiler class and initialize when self.compiler is set. - - # Arg prefixes that override by prepending instead of appending - prepend_prefixes = ('-I', '-L') - # Arg prefixes and args that must be de-duped by returning 2 - dedup2_prefixes = ('-I', '-L', '-D', '-U') - dedup2_suffixes = () - dedup2_args = () - # Arg prefixes and args that must be de-duped by returning 1 - dedup1_prefixes = ('-l',) - dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') - # Match a .so of the form path/to/libfoo.so.0.1.0 - # Only UNIX shared libraries require this. Others have a fixed extension. - dedup1_regex = re.compile(r'([\/\\]|\A)lib.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') - dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread') - compiler = None - - def _check_args(self, args): - cargs = [] - if len(args) > 2: - raise TypeError("CompilerArgs() only accepts at most 2 arguments: " - "The compiler, and optionally an initial list") - elif not args: - return cargs - elif len(args) == 1: - if isinstance(args[0], (Compiler, StaticLinker)): - self.compiler = args[0] - else: - raise TypeError("you must pass a Compiler instance as one of " - "the arguments") - elif len(args) == 2: - if isinstance(args[0], (Compiler, StaticLinker)): - self.compiler = args[0] - cargs = args[1] - elif isinstance(args[1], (Compiler, StaticLinker)): - cargs = args[0] - self.compiler = args[1] - else: - raise TypeError("you must pass a Compiler instance as one of " - "the two arguments") - else: - raise AssertionError('Not reached') - return cargs - - def __init__(self, *args): - super().__init__(self._check_args(args)) - - @classmethod - def _can_dedup(cls, arg): - ''' - Returns whether the argument can be safely de-duped. This is dependent - on three things: - - a) Whether an argument can be 'overriden' by a later argument. For - example, -DFOO defines FOO and -UFOO undefines FOO. In this case, we - can safely remove the previous occurance and add a new one. The same - is true for include paths and library paths with -I and -L. For - these we return `2`. See `dedup2_prefixes` and `dedup2_args`. - b) Arguments that once specified cannot be undone, such as `-c` or - `-pipe`. New instances of these can be completely skipped. For these - we return `1`. See `dedup1_prefixes` and `dedup1_args`. - c) Whether it matters where or how many times on the command-line - a particular argument is present. This can matter for symbol - resolution in static or shared libraries, so we cannot de-dup or - reorder them. For these we return `0`. This is the default. - - In addition to these, we handle library arguments specially. - With GNU ld, we surround library arguments with -Wl,--start/end-group - to recursively search for symbols in the libraries. This is not needed - with other linkers. - ''' - - # A standalone argument must never be deduplicated because it is - # defined by what comes _after_ it. Thus dedupping this: - # -D FOO -D BAR - # would yield either - # -D FOO BAR - # or - # FOO -D BAR - # both of which are invalid. - if arg in cls.dedup2_prefixes: - return 0 - if arg in cls.dedup2_args or \ - arg.startswith(cls.dedup2_prefixes) or \ - arg.endswith(cls.dedup2_suffixes): - return 2 - if arg in cls.dedup1_args or \ - arg.startswith(cls.dedup1_prefixes) or \ - arg.endswith(cls.dedup1_suffixes) or \ - re.search(cls.dedup1_regex, arg): - return 1 - return 0 - - @classmethod - def _should_prepend(cls, arg): - if arg.startswith(cls.prepend_prefixes): - return True - return False - - def to_native(self): - # Check if we need to add --start/end-group for circular dependencies - # between static libraries. - if get_compiler_uses_gnuld(self.compiler): - group_started = False - for each in self: - if not each.startswith('-l') and not each.endswith('.a'): - continue - i = self.index(each) - if not group_started: - # First occurance of a library - self.insert(i, '-Wl,--start-group') - group_started = True - # Last occurance of a library - if group_started: - self.insert(i + 1, '-Wl,--end-group') - return self.compiler.unix_args_to_native(self) - - def append_direct(self, arg): - ''' - Append the specified argument without any reordering or de-dup - ''' - super().append(arg) - - def extend_direct(self, iterable): - ''' - Extend using the elements in the specified iterable without any - reordering or de-dup - ''' - super().extend(iterable) - - def __add__(self, args): - new = CompilerArgs(self, self.compiler) - new += args - return new - - def __iadd__(self, args): - ''' - Add two CompilerArgs while taking into account overriding of arguments - and while preserving the order of arguments as much as possible - ''' - pre = [] - post = [] - if not isinstance(args, list): - raise TypeError('can only concatenate list (not "{}") to list'.format(args)) - for arg in args: - # If the argument can be de-duped, do it either by removing the - # previous occurance of it and adding a new one, or not adding the - # new occurance. - dedup = self._can_dedup(arg) - if dedup == 1: - # Argument already exists and adding a new instance is useless - if arg in self or arg in pre or arg in post: - continue - if dedup == 2: - # Remove all previous occurances of the arg and add it anew - if arg in self: - self.remove(arg) - if arg in pre: - pre.remove(arg) - if arg in post: - post.remove(arg) - if self._should_prepend(arg): - pre.append(arg) - else: - post.append(arg) - # Insert at the beginning - self[:0] = pre - # Append to the end - super().__iadd__(post) - return self - - def __radd__(self, args): - new = CompilerArgs(args, self.compiler) - new += self - return new - - def __mul__(self, args): - raise TypeError("can't multiply compiler arguments") - - def __imul__(self, args): - raise TypeError("can't multiply compiler arguments") - - def __rmul__(self, args): - raise TypeError("can't multiply compiler arguments") - - def append(self, arg): - self.__iadd__([arg]) - - def extend(self, args): - self.__iadd__(args) - -class Compiler: - def __init__(self, exelist, version): - if isinstance(exelist, str): - self.exelist = [exelist] - elif isinstance(exelist, list): - self.exelist = exelist - else: - raise TypeError('Unknown argument to Compiler') - # In case it's been overriden by a child class already - if not hasattr(self, 'file_suffixes'): - self.file_suffixes = lang_suffixes[self.language] - if not hasattr(self, 'can_compile_suffixes'): - self.can_compile_suffixes = set(self.file_suffixes) - self.default_suffix = self.file_suffixes[0] - self.version = version - self.base_options = [] - - def __repr__(self): - repr_str = "<{0}: v{1} `{2}`>" - return repr_str.format(self.__class__.__name__, self.version, - ' '.join(self.exelist)) - - def can_compile(self, src): - if hasattr(src, 'fname'): - src = src.fname - suffix = os.path.splitext(src)[1].lower() - if suffix and suffix[1:] in self.can_compile_suffixes: - return True - return False - - def get_id(self): - return self.id - - def get_language(self): - return self.language - - def get_default_suffix(self): - return self.default_suffix - - def get_exelist(self): - return self.exelist[:] - - def get_builtin_define(self, *args, **kwargs): - raise EnvironmentException('%s does not support get_builtin_define.' % self.id) - - def has_builtin_define(self, *args, **kwargs): - raise EnvironmentException('%s does not support has_builtin_define.' % self.id) - - def get_always_args(self): - return [] - - 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 - - def get_option_compile_args(self, options): - return [] - - def get_option_link_args(self, options): - return [] - - def has_header(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support header checks.' % self.language) - - def has_header_symbol(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support header symbol checks.' % self.language) - - def compiles(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support compile checks.' % self.language) - - def links(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support link checks.' % self.language) - - def run(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support run checks.' % self.language) - - def sizeof(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support sizeof checks.' % self.language) - - def alignment(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support alignment checks.' % self.language) - - def has_function(self, *args, **kwargs): - raise EnvironmentException('Language %s does not support function checks.' % self.language) - - @classmethod - def unix_args_to_native(cls, args): - "Always returns a copy that can be independently mutated" - return args[:] - - def find_library(self, *args, **kwargs): - raise EnvironmentException('Language {} does not support library finding.'.format(self.language)) - - def get_library_dirs(self): - return [] - - def has_argument(self, arg, env): - return self.has_multi_arguments([arg], env) - - def has_multi_arguments(self, args, env): - raise EnvironmentException( - 'Language {} does not support has_multi_arguments.'.format( - self.language)) - - def get_cross_extra_flags(self, environment, link): - extra_flags = [] - if self.is_cross and environment: - if 'properties' in environment.cross_info.config: - props = environment.cross_info.config['properties'] - lang_args_key = self.language + '_args' - extra_flags += props.get(lang_args_key, []) - lang_link_args_key = self.language + '_link_args' - if link: - extra_flags += props.get(lang_link_args_key, []) - return extra_flags - - def _get_compile_output(self, dirname, mode): - # In pre-processor mode, the output is sent to stdout and discarded - if mode == 'preprocess': - return None - # Extension only matters if running results; '.exe' is - # guaranteed to be executable on every platform. - if mode == 'link': - suffix = 'exe' - else: - suffix = 'obj' - return os.path.join(dirname, 'output.' + suffix) - - @contextlib.contextmanager - def compile(self, code, extra_args=None, mode='link'): - if extra_args is None: - extra_args = [] - try: - with tempfile.TemporaryDirectory() as tmpdirname: - if isinstance(code, str): - srcname = os.path.join(tmpdirname, - 'testfile.' + self.default_suffix) - with open(srcname, 'w') as ofile: - ofile.write(code) - elif isinstance(code, mesonlib.File): - srcname = code.fname - output = self._get_compile_output(tmpdirname, mode) - - # Construct the compiler command-line - commands = CompilerArgs(self) - commands.append(srcname) - commands += extra_args - commands += self.get_always_args() - if mode == 'compile': - commands += self.get_compile_only_args() - # Preprocess mode outputs to stdout, so no output args - if mode == 'preprocess': - commands += self.get_preprocess_only_args() - else: - commands += self.get_output_args(output) - # Generate full command-line with the exelist - commands = self.get_exelist() + commands.to_native() - mlog.debug('Running compile:') - mlog.debug('Working directory: ', tmpdirname) - mlog.debug('Command line: ', ' '.join(commands), '\n') - mlog.debug('Code:\n', code) - p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname) - mlog.debug('Compiler stdout:\n', p.stdo) - mlog.debug('Compiler stderr:\n', p.stde) - p.input_name = srcname - p.output_name = output - yield p - except (PermissionError, OSError): - # On Windows antivirus programs and the like hold on to files so - # they can't be deleted. There's not much to do in this case. Also, - # catch OSError because the directory is then no longer empty. - pass - - def get_colorout_args(self, colortype): - return [] - - # Some compilers (msvc) write debug info to a separate file. - # These args specify where it should be written. - def get_compile_debugfile_args(self, rel_obj, **kwargs): - return [] - - def get_link_debugfile_args(self, rel_obj): - return [] - - def get_std_shared_lib_link_args(self): - return [] - - def get_std_shared_module_link_args(self): - return self.get_std_shared_lib_link_args() - - def get_link_whole_for(self, args): - if isinstance(args, list) and not args: - return [] - raise EnvironmentException('Language %s does not support linking whole archives.' % self.language) - - def build_unix_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - if not rpath_paths and not install_rpath: - return [] - # The rpaths we write must be relative, because otherwise - # they have different length depending on the build - # directory. This breaks reproducible builds. - rel_rpaths = [] - for p in rpath_paths: - if p == from_dir: - relative = '' # relpath errors out in this case - else: - relative = os.path.relpath(p, from_dir) - rel_rpaths.append(relative) - paths = ':'.join([os.path.join('$ORIGIN', p) for p in rel_rpaths]) - if len(paths) < len(install_rpath): - padding = 'X' * (len(install_rpath) - len(paths)) - if not paths: - paths = padding - else: - paths = paths + ':' + padding - args = ['-Wl,-rpath,' + paths] - if get_compiler_is_linuxlike(self): - # Rpaths to use while linking must be absolute. These are not - # written to the binary. Needed only with GNU ld: - # https://sourceware.org/bugzilla/show_bug.cgi?id=16936 - # Not needed on Windows or other platforms that don't use RPATH - # https://github.com/mesonbuild/meson/issues/1897 - lpaths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) - args += ['-Wl,-rpath-link,' + lpaths] - return args - -class CCompiler(Compiler): - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - # If a child ObjC or CPP class has already set it, don't set it ourselves - if not hasattr(self, 'language'): - self.language = 'c' - super().__init__(exelist, version) - self.id = 'unknown' - self.is_cross = is_cross - self.can_compile_suffixes.add('h') - if isinstance(exe_wrapper, str): - self.exe_wrapper = [exe_wrapper] - else: - self.exe_wrapper = exe_wrapper - - def needs_static_linker(self): - return True # When compiling static libraries, so yes. - - def get_always_args(self): - ''' - Args that are always-on for all C compilers other than MSVC - ''' - return ['-pipe'] + get_largefile_args(self) - - def get_linker_debug_crt_args(self): - """ - Arguments needed to select a debug crt for the linker - This is only needed for MSVC - """ - return [] - - def get_no_stdinc_args(self): - return ['-nostdinc'] - - def get_no_stdlib_link_args(self): - return ['-nostdlib'] - - def get_warn_args(self, level): - return self.warn_args[level] - - def get_no_warn_args(self): - # Almost every compiler uses this for disabling warnings - return ['-w'] - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return [] - - def split_shlib_to_parts(self, fname): - return None, fname - - # The default behavior is this, override in MSVC - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) - - def get_dependency_gen_args(self, outtarget, outfile): - return ['-MMD', '-MQ', outtarget, '-MF', outfile] - - def depfile_for_object(self, objfile): - return objfile + '.' + self.get_depfile_suffix() - - def get_depfile_suffix(self): - return 'd' - - def get_exelist(self): - return self.exelist[:] - - def get_linker_exelist(self): - return self.exelist[:] - - def get_preprocess_only_args(self): - return ['-E', '-P'] - - def get_compile_only_args(self): - return ['-c'] - - def get_no_optimization_args(self): - return ['-O0'] - - def get_compiler_check_args(self): - ''' - Get arguments useful for compiler checks such as being permissive in - the code quality and not doing any optimization. - ''' - return self.get_no_optimization_args() - - def get_output_args(self, target): - return ['-o', target] - - def get_linker_output_args(self, outputname): - return ['-o', outputname] - - def get_coverage_args(self): - return ['--coverage'] - - def get_coverage_link_args(self): - return ['--coverage'] - - def get_werror_args(self): - return ['-Werror'] - - def get_std_exe_link_args(self): - return [] - - def get_include_args(self, path, is_system): - if path == '': - path = '.' - if is_system: - return ['-isystem', path] - return ['-I' + path] - - def get_std_shared_lib_link_args(self): - return ['-shared'] - - def get_library_dirs(self): - stdo = Popen_safe(self.exelist + ['--print-search-dirs'])[1] - for line in stdo.split('\n'): - if line.startswith('libraries:'): - libstr = line.split('=', 1)[1] - return libstr.split(':') - return [] - - def get_pic_args(self): - return ['-fPIC'] - - def name_string(self): - return ' '.join(self.exelist) - - def get_pch_use_args(self, pch_dir, header): - return ['-include', os.path.split(header)[-1]] - - def get_pch_name(self, header_name): - return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() - - 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)) - - extra_flags = [] - source_name = os.path.join(work_dir, sname) - binname = sname.rsplit('.', 1)[0] - if self.is_cross: - binname += '_cross' - if self.exe_wrapper is None: - # Linking cross built apps is painful. You can't really - # tell if you should use -nostdlib or not and for example - # on OSX the compiler binary is the same but you need - # a ton of compiler flags to differentiate between - # arm and x86_64. So just compile. - extra_flags += self.get_cross_extra_flags(environment, link=False) - extra_flags += self.get_compile_only_args() - else: - extra_flags += self.get_cross_extra_flags(environment, link=True) - # Is a valid executable output for all toolchains and platforms - binname += '.exe' - # Write binary check source - binary_name = os.path.join(work_dir, binname) - with open(source_name, 'w') as ofile: - ofile.write(code) - # Compile sanity check - cmdlist = self.exelist + extra_flags + [source_name] + self.get_output_args(binary_name) - pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir) - mlog.debug('Sanity check compiler command line:', ' '.join(cmdlist)) - mlog.debug('Sanity check compile stdout:') - mlog.debug(stdo) - mlog.debug('-----\nSanity check compile stderr:') - mlog.debug(stde) - mlog.debug('-----') - if pc.returncode != 0: - raise EnvironmentException('Compiler {0} can not compile programs.'.format(self.name_string())) - # Run sanity check - if self.is_cross: - if self.exe_wrapper is None: - # Can't check if the binaries run so we have to assume they do - return - cmdlist = self.exe_wrapper + [binary_name] - else: - cmdlist = [binary_name] - mlog.debug('Running test binary command: ' + ' '.join(cmdlist)) - pe = subprocess.Popen(cmdlist) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by {0} compiler {1} are not runnable.'.format(self.language, self.name_string())) - - def sanity_check(self, work_dir, environment): - code = 'int main(int argc, char **argv) { int class=0; return class; }\n' - return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) - - def has_header(self, hname, prefix, env, extra_args=None, dependencies=None): - fargs = {'prefix': prefix, 'header': hname} - code = '''{prefix} - #ifdef __has_include - #if !__has_include("{header}") - #error "Header '{header}' could not be found" - #endif - #else - #include <{header}> - #endif''' - return self.compiles(code.format(**fargs), env, extra_args, - dependencies, 'preprocess') - - def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): - fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} - t = '''{prefix} - #include <{header}> - int main () {{ - /* If it's not defined as a macro, try to use as a symbol */ - #ifndef {symbol} - {symbol}; - #endif - }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) - - def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): - if extra_args is None: - extra_args = [] - elif isinstance(extra_args, str): - extra_args = [extra_args] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] - # Collect compiler arguments - args = CompilerArgs(self) - for d in dependencies: - # Add compile flags needed by dependencies - args += d.get_compile_args() - if mode == 'link': - # Add link flags needed to find dependencies - args += d.get_link_args() - # Select a CRT if needed since we're linking - if mode == 'link': - args += self.get_linker_debug_crt_args() - # Read c_args/cpp_args/etc from the cross-info file (if needed) - args += self.get_cross_extra_flags(env, link=(mode == 'link')) - if not self.is_cross: - if mode == 'preprocess': - # Add CPPFLAGS from the env. - args += env.coredata.external_preprocess_args[self.language] - elif mode == 'compile': - # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env - args += env.coredata.external_args[self.language] - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.external_link_args[self.language] - args += self.get_compiler_check_args() - # extra_args must override all other arguments, so we add them last - args += extra_args - return args - - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): - args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - # We only want to compile; not link - with self.compile(code, args.to_native(), mode) as p: - return p.returncode == 0 - - def _links_wrapper(self, code, env, extra_args, dependencies): - "Shares common code between self.links and self.run" - args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link') - return self.compile(code, args) - - def links(self, code, env, extra_args=None, dependencies=None): - with self._links_wrapper(code, env, extra_args, dependencies) as p: - return p.returncode == 0 - - def run(self, code, env, extra_args=None, dependencies=None): - if self.is_cross and self.exe_wrapper is None: - raise CrossNoRunException('Can not run test applications in this cross environment.') - with self._links_wrapper(code, env, extra_args, dependencies) as p: - if p.returncode != 0: - mlog.debug('Could not compile test file %s: %d\n' % ( - p.input_name, - p.returncode)) - return RunResult(False) - if self.is_cross: - cmdlist = self.exe_wrapper + [p.output_name] - else: - cmdlist = p.output_name - try: - pe, so, se = Popen_safe(cmdlist) - except Exception as e: - mlog.debug('Could not run: %s (error: %s)\n' % (cmdlist, e)) - return RunResult(False) - - mlog.debug('Program stdout:\n') - mlog.debug(so) - mlog.debug('Program stderr:\n') - mlog.debug(se) - return RunResult(True, pe.returncode, so, se) - - def _compile_int(self, expression, prefix, env, extra_args, dependencies): - fargs = {'prefix': prefix, 'expression': expression} - t = '''#include <stdio.h> - {prefix} - int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) - - def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): - if isinstance(guess, int): - if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies): - return guess - - cur = low - while low < high: - cur = int((low + high) / 2) - if cur == low: - break - - if self._compile_int('%s >= %d' % (expression, cur), prefix, env, extra_args, dependencies): - low = cur - else: - high = cur - - if self._compile_int('%s == %d' % (expression, cur), prefix, env, extra_args, dependencies): - return cur - raise EnvironmentException('Cross-compile check overflowed') - - def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - if self.is_cross: - return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies) - fargs = {'prefix': prefix, 'expression': expression} - t = '''#include<stdio.h> - {prefix} - int main(int argc, char **argv) {{ - printf("%ld\\n", (long)({expression})); - return 0; - }};''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) - if not res.compiled: - return -1 - if res.returncode != 0: - raise EnvironmentException('Could not run compute_int test binary.') - return int(res.stdout) - - def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - fargs = {'prefix': prefix, 'type': typename} - t = '''#include <stdio.h> - {prefix} - int main(int argc, char **argv) {{ - {type} something; - }}''' - if not self.compiles(t.format(**fargs), env, extra_args, dependencies): - return -1 - return self.cross_compute_int('sizeof(%s)' % typename, 1, 128, None, prefix, env, extra_args, dependencies) - - def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - fargs = {'prefix': prefix, 'type': typename} - if self.is_cross: - return self.cross_sizeof(typename, prefix, env, extra_args, dependencies) - t = '''#include<stdio.h> - {prefix} - int main(int argc, char **argv) {{ - printf("%ld\\n", (long)(sizeof({type}))); - return 0; - }};''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) - if not res.compiled: - return -1 - if res.returncode != 0: - raise EnvironmentException('Could not run sizeof test binary.') - return int(res.stdout) - - def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - fargs = {'prefix': prefix, 'type': typename} - t = '''#include <stdio.h> - {prefix} - int main(int argc, char **argv) {{ - {type} something; - }}''' - if not self.compiles(t.format(**fargs), env, extra_args, dependencies): - return -1 - t = '''#include <stddef.h> - {prefix} - struct tmp {{ - char c; - {type} target; - }};''' - return self.cross_compute_int('offsetof(struct tmp, target)', 1, 1024, None, t.format(**fargs), env, extra_args, dependencies) - - def alignment(self, typename, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - if self.is_cross: - return self.cross_alignment(typename, prefix, env, extra_args, dependencies) - fargs = {'prefix': prefix, 'type': typename} - t = '''#include <stdio.h> - #include <stddef.h> - {prefix} - struct tmp {{ - char c; - {type} target; - }}; - int main(int argc, char **argv) {{ - printf("%d", (int)offsetof(struct tmp, target)); - return 0; - }}''' - res = self.run(t.format(**fargs), env, extra_args, dependencies) - if not res.compiled: - raise EnvironmentException('Could not compile alignment test.') - if res.returncode != 0: - raise EnvironmentException('Could not run alignment test binary.') - align = int(res.stdout) - if align == 0: - raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename) - return align - - def get_define(self, dname, prefix, env, extra_args, dependencies): - delim = '"MESON_GET_DEFINE_DELIMITER"' - fargs = {'prefix': prefix, 'define': dname, 'delim': delim} - code = ''' - {prefix} - #ifndef {define} - # define {define} - #endif - {delim}\n{define}''' - args = self._get_compiler_check_args(env, extra_args, dependencies, - mode='preprocess').to_native() - with self.compile(code.format(**fargs), args, 'preprocess') as p: - if p.returncode != 0: - raise EnvironmentException('Could not get define {!r}'.format(dname)) - # Get the preprocessed value after the delimiter, - # minus the extra newline at the end - return p.stdo.split(delim + '\n')[-1][:-1] - - @staticmethod - def _no_prototype_templ(): - """ - Try to find the function without a prototype from a header by defining - our own dummy prototype and trying to link with the C library (and - whatever else the compiler links in by default). This is very similar - to the check performed by Autoconf for AC_CHECK_FUNCS. - """ - # Define the symbol to something else since it is defined by the - # includes or defines listed by the user or by the compiler. This may - # include, for instance _GNU_SOURCE which must be defined before - # limits.h, which includes features.h - # Then, undef the symbol to get rid of it completely. - head = ''' - #define {func} meson_disable_define_of_{func} - {prefix} - #include <limits.h> - #undef {func} - ''' - # Override any GCC internal prototype and declare our own definition for - # the symbol. Use char because that's unlikely to be an actual return - # value for a function which ensures that we override the definition. - head += ''' - #ifdef __cplusplus - extern "C" - #endif - char {func} (); - ''' - # The actual function call - main = ''' - int main () {{ - return {func} (); - }}''' - return head, main - - @staticmethod - def _have_prototype_templ(): - """ - Returns a head-er and main() call that uses the headers listed by the - user for the function prototype while checking if a function exists. - """ - # Add the 'prefix', aka defines, includes, etc that the user provides - # This may include, for instance _GNU_SOURCE which must be defined - # before limits.h, which includes features.h - head = '{prefix}\n#include <limits.h>\n' - # We don't know what the function takes or returns, so return it as an int. - # Just taking the address or comparing it to void is not enough because - # compilers are smart enough to optimize it away. The resulting binary - # is not run so we don't care what the return value is. - main = '''\nint main() {{ - void *a = (void*) &{func}; - long b = (long) a; - return (int) b; - }}''' - return head, main - - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): - """ - First, this function looks for the symbol in the default libraries - provided by the compiler (stdlib + a few others usually). If that - fails, it checks if any of the headers specified in the prefix provide - an implementation of the function, and if that fails, it checks if it's - implemented as a compiler-builtin. - """ - if extra_args is None: - extra_args = [] - - # Short-circuit if the check is already provided by the cross-info file - varname = 'has function ' + funcname - varname = varname.replace(' ', '_') - if self.is_cross: - val = env.cross_info.config['properties'].get(varname, None) - if val is not None: - if isinstance(val, bool): - return val - raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) - - fargs = {'prefix': prefix, 'func': funcname} - - # glibc defines functions that are not available on Linux as stubs that - # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail - # instead of detecting the stub as a valid symbol. - # We already included limits.h earlier to ensure that these are defined - # for stub functions. - stubs_fail = ''' - #if defined __stub_{func} || defined __stub___{func} - fail fail fail this function is not going to work - #endif - ''' - - # If we have any includes in the prefix supplied by the user, assume - # that the user wants us to use the symbol prototype defined in those - # includes. If not, then try to do the Autoconf-style check with - # a dummy prototype definition of our own. - # This is needed when the linker determines symbol availability from an - # SDK based on the prototype in the header provided by the SDK. - # Ignoring this prototype would result in the symbol always being - # marked as available. - if '#include' in prefix: - head, main = self._have_prototype_templ() - else: - head, main = self._no_prototype_templ() - templ = head + stubs_fail + main - - if self.links(templ.format(**fargs), env, extra_args, dependencies): - return True - - # MSVC does not have compiler __builtin_-s. - if self.get_id() == 'msvc': - return False - - # Detect function as a built-in - # - # Some functions like alloca() are defined as compiler built-ins which - # are inlined by the compiler and you can't take their address, so we - # need to look for them differently. On nice compilers like clang, we - # can just directly use the __has_builtin() macro. - fargs['no_includes'] = '#include' not in prefix - t = '''{prefix} - int main() {{ - #ifdef __has_builtin - #if !__has_builtin(__builtin_{func}) - #error "__builtin_{func} not found" - #endif - #elif ! defined({func}) - /* Check for __builtin_{func} only if no includes were added to the - * prefix above, which means no definition of {func} can be found. - * We would always check for this, but we get false positives on - * MSYS2 if we do. Their toolchain is broken, but we can at least - * give them a workaround. */ - #if {no_includes:d} - __builtin_{func}; - #else - #error "No definition for __builtin_{func} found in the prefix" - #endif - #endif - }}''' - return self.links(t.format(**fargs), env, extra_args, dependencies) - - def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'} - # Create code that accesses all members - members = '' - for member in membernames: - members += '{}.{};\n'.format(fargs['name'], member) - fargs['members'] = members - t = '''{prefix} - void bar() {{ - {type} {name}; - {members} - }};''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) - - def has_type(self, typename, prefix, env, extra_args, dependencies=None): - fargs = {'prefix': prefix, 'type': typename} - t = '''{prefix} - void bar() {{ - sizeof({type}); - }};''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) - - def symbols_have_underscore_prefix(self, env): - ''' - Check if the compiler prefixes an underscore to global C symbols - ''' - symbol_name = b'meson_uscore_prefix' - code = '''#ifdef __cplusplus - extern "C" { - #endif - void ''' + symbol_name.decode() + ''' () {} - #ifdef __cplusplus - } - #endif - ''' - args = self.get_cross_extra_flags(env, link=False) - args += self.get_compiler_check_args() - n = 'symbols_have_underscore_prefix' - with self.compile(code, args, 'compile') as p: - if p.returncode != 0: - m = 'BUG: Unable to compile {!r} check: {}' - raise RuntimeError(m.format(n, p.stdo)) - if not os.path.isfile(p.output_name): - m = 'BUG: Can\'t find compiled test code for {!r} check' - raise RuntimeError(m.format(n)) - with open(p.output_name, 'rb') as o: - for line in o: - # Check if the underscore form of the symbol is somewhere - # in the output file. - if b'_' + symbol_name in line: - return True - # Else, check if the non-underscored form is present - elif symbol_name in line: - return False - raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n)) - - def find_library(self, libname, env, extra_dirs): - # First try if we can just add the library as -l. - code = '''int main(int argc, char **argv) { - return 0; -} - ''' - if extra_dirs and isinstance(extra_dirs, str): - extra_dirs = [extra_dirs] - # Gcc + co seem to prefer builtin lib dirs to -L dirs. - # Only try to find std libs if no extra dirs specified. - if not extra_dirs: - args = ['-l' + libname] - if self.links(code, env, extra_args=args): - return args - # Not found? Try to find the library file itself. - extra_dirs += self.get_library_dirs() - suffixes = ['so', 'dylib', 'lib', 'dll', 'a'] - for d in extra_dirs: - for suffix in suffixes: - trial = os.path.join(d, 'lib' + libname + '.' + suffix) - if os.path.isfile(trial): - return [trial] - trial2 = os.path.join(d, libname + '.' + suffix) - if os.path.isfile(trial2): - return [trial2] - return None - - def thread_flags(self): - return ['-pthread'] - - def thread_link_flags(self): - return ['-pthread'] - - def has_multi_arguments(self, args, env): - return self.compiles('int i;\n', env, extra_args=args) - -class CPPCompiler(CCompiler): - def __init__(self, exelist, version, is_cross, exe_wrap): - # If a child ObjCPP class has already set it, don't set it ourselves - if not hasattr(self, 'language'): - self.language = 'cpp' - CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - - def get_no_stdinc_args(self): - return ['-nostdinc++'] - - def sanity_check(self, work_dir, environment): - code = 'class breakCCompiler;int main(int argc, char **argv) { return 0; }\n' - return self.sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code) - - def get_compiler_check_args(self): - # -fpermissive allows non-conforming code to compile which is necessary - # for many C++ checks. Particularly, the has_header_symbol check is - # too strict without this and always fails. - return super().get_compiler_check_args() + ['-fpermissive'] - - def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): - # Check if it's a C-like symbol - if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies): - return True - # Check if it's a class or a template - if extra_args is None: - extra_args = [] - fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} - t = '''{prefix} - #include <{header}> - using {symbol}; - int main () {{ return 0; }}''' - return self.compiles(t.format(**fargs), env, extra_args, dependencies) - -class ObjCCompiler(CCompiler): - def __init__(self, exelist, version, is_cross, exe_wrap): - self.language = 'objc' - CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - - def sanity_check(self, work_dir, environment): - # TODO try to use sanity_check_impl instead of duplicated code - source_name = os.path.join(work_dir, 'sanitycheckobjc.m') - binary_name = os.path.join(work_dir, 'sanitycheckobjc') - extra_flags = self.get_cross_extra_flags(environment, link=False) - if self.is_cross: - extra_flags += self.get_compile_only_args() - with open(source_name, 'w') as ofile: - ofile.write('#import<stdio.h>\n' - 'int main(int argc, char **argv) { return 0; }\n') - pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('ObjC compiler %s can not compile programs.' % self.name_string()) - if self.is_cross: - # Can't check if the binaries run so we have to assume they do - return - pe = subprocess.Popen(binary_name) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by ObjC compiler %s are not runnable.' % self.name_string()) - -class ObjCPPCompiler(CPPCompiler): - def __init__(self, exelist, version, is_cross, exe_wrap): - self.language = 'objcpp' - CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - - def sanity_check(self, work_dir, environment): - # TODO try to use sanity_check_impl instead of duplicated code - source_name = os.path.join(work_dir, 'sanitycheckobjcpp.mm') - binary_name = os.path.join(work_dir, 'sanitycheckobjcpp') - extra_flags = self.get_cross_extra_flags(environment, link=False) - if self.is_cross: - extra_flags += self.get_compile_only_args() - with open(source_name, 'w') as ofile: - ofile.write('#import<stdio.h>\n' - 'class MyClass;' - 'int main(int argc, char **argv) { return 0; }\n') - pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('ObjC++ compiler %s can not compile programs.' % self.name_string()) - if self.is_cross: - # Can't check if the binaries run so we have to assume they do - return - pe = subprocess.Popen(binary_name) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by ObjC++ compiler %s are not runnable.' % self.name_string()) - -class MonoCompiler(Compiler): - def __init__(self, exelist, version): - self.language = 'cs' - super().__init__(exelist, version) - self.id = 'mono' - self.monorunner = 'mono' - - def get_output_args(self, fname): - return ['-out:' + fname] - - def get_link_args(self, fname): - return ['-r:' + fname] - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return [] - - def get_werror_args(self): - return ['-warnaserror'] - - def split_shlib_to_parts(self, fname): - return None, fname - - def build_rpath_args(self, build_dir, rpath_paths, install_rpath): - return [] - - def get_dependency_gen_args(self, outtarget, outfile): - return [] - - def get_linker_exelist(self): - return self.exelist[:] - - def get_compile_only_args(self): - return [] - - def get_linker_output_args(self, outputname): - return [] - - def get_coverage_args(self): - return [] - - def get_coverage_link_args(self): - return [] - - def get_std_exe_link_args(self): - return [] - - def get_include_args(self, path): - return [] - - def get_pic_args(self): - return [] - - def name_string(self): - return ' '.join(self.exelist) - - def get_pch_use_args(self, pch_dir, header): - return [] - - def get_pch_name(self, header_name): - return '' - - def sanity_check(self, work_dir, environment): - src = 'sanity.cs' - obj = 'sanity.exe' - source_name = os.path.join(work_dir, src) - with open(source_name, 'w') as ofile: - ofile.write('''public class Sanity { - static public void Main () { - } -} -''') - pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) - cmdlist = [self.monorunner, obj] - pe = subprocess.Popen(cmdlist, cwd=work_dir) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string()) - - def needs_static_linker(self): - return False - - def get_buildtype_args(self, buildtype): - return mono_buildtype_args[buildtype] - -class JavaCompiler(Compiler): - def __init__(self, exelist, version): - self.language = 'java' - super().__init__(exelist, version) - self.id = 'unknown' - self.javarunner = 'java' - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return [] - - def get_werror_args(self): - return ['-Werror'] - - def split_shlib_to_parts(self, fname): - return None, fname - - def build_rpath_args(self, build_dir, rpath_paths, install_rpath): - return [] - - def get_dependency_gen_args(self, outtarget, outfile): - return [] - - def get_linker_exelist(self): - return self.exelist[:] - - def get_compile_only_args(self): - return [] - - def get_output_args(self, subdir): - if subdir == '': - subdir = './' - return ['-d', subdir, '-s', subdir] - - def get_linker_output_args(self, outputname): - return [] - - def get_coverage_args(self): - return [] - - def get_coverage_link_args(self): - return [] - - def get_std_exe_link_args(self): - return [] - - def get_include_args(self, path): - return [] - - def get_pic_args(self): - return [] - - def name_string(self): - return ' '.join(self.exelist) - - def get_pch_use_args(self, pch_dir, header): - return [] - - def get_pch_name(self, header_name): - return '' - - def get_buildtype_args(self, buildtype): - return java_buildtype_args[buildtype] - - def sanity_check(self, work_dir, environment): - src = 'SanityCheck.java' - obj = 'SanityCheck' - source_name = os.path.join(work_dir, src) - with open(source_name, 'w') as ofile: - ofile.write('''class SanityCheck { - public static void main(String[] args) { - int i; - } -} -''') - pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Java compiler %s can not compile programs.' % self.name_string()) - runner = shutil.which(self.javarunner) - if runner: - cmdlist = [runner, obj] - pe = subprocess.Popen(cmdlist, cwd=work_dir) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by Java compiler %s are not runnable.' % self.name_string()) - else: - m = "Java Virtual Machine wasn't found, but it's needed by Meson. " \ - "Please install a JRE.\nIf you have specific needs where this " \ - "requirement doesn't make sense, please open a bug at " \ - "https://github.com/mesonbuild/meson/issues/new and tell us " \ - "all about it." - raise EnvironmentException(m) - - def needs_static_linker(self): - return False - -class ValaCompiler(Compiler): - def __init__(self, exelist, version): - self.language = 'vala' - super().__init__(exelist, version) - self.version = version - self.id = 'valac' - self.is_cross = False - - def name_string(self): - return ' '.join(self.exelist) - - def needs_static_linker(self): - return False # Because compiles into C. - - def get_output_args(self, target): - return ['-o', target] - - def get_compile_only_args(self): - return ['-C'] - - def get_pic_args(self): - return [] - - def get_always_args(self): - return ['-C'] - - def get_warn_args(self, warning_level): - return [] - - def get_no_warn_args(self): - return ['--disable-warnings'] - - def get_werror_args(self): - return ['--fatal-warnings'] - - def sanity_check(self, work_dir, environment): - code = 'class MesonSanityCheck : Object { }' - args = self.get_cross_extra_flags(environment, link=False) - with self.compile(code, args, 'compile') as p: - if p.returncode != 0: - msg = 'Vala compiler {!r} can not compile programs' \ - ''.format(self.name_string()) - raise EnvironmentException(msg) - - def get_buildtype_args(self, buildtype): - if buildtype == 'debug' or buildtype == 'debugoptimized' or buildtype == 'minsize': - return ['--debug'] - return [] - - def find_library(self, libname, env, extra_dirs): - if extra_dirs and isinstance(extra_dirs, str): - extra_dirs = [extra_dirs] - # Valac always looks in the default vapi dir, so only search there if - # no extra dirs are specified. - if not extra_dirs: - code = 'class MesonFindLibrary : Object { }' - vapi_args = ['--pkg', libname] - args = self.get_cross_extra_flags(env, link=False) - args += vapi_args - with self.compile(code, args, 'compile') as p: - if p.returncode == 0: - return vapi_args - # Not found? Try to find the vapi file itself. - for d in extra_dirs: - vapi = os.path.join(d, libname + '.vapi') - if os.path.isfile(vapi): - return [vapi] - mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname)) - return None - -class RustCompiler(Compiler): - def __init__(self, exelist, version): - self.language = 'rust' - super().__init__(exelist, version) - self.id = 'rustc' - - def needs_static_linker(self): - return False - - def name_string(self): - return ' '.join(self.exelist) - - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanity.rs') - output_name = os.path.join(work_dir, 'rusttest') - with open(source_name, 'w') as ofile: - ofile.write('''fn main() { -} -''') - pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string()) - if subprocess.call(output_name) != 0: - raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string()) - - def get_dependency_gen_args(self, outfile): - return ['--dep-info', outfile] - - def get_buildtype_args(self, buildtype): - return rust_buildtype_args[buildtype] - -class SwiftCompiler(Compiler): - def __init__(self, exelist, version): - self.language = 'swift' - super().__init__(exelist, version) - self.version = version - self.id = 'llvm' - self.is_cross = False - - def get_linker_exelist(self): - return self.exelist[:] - - def name_string(self): - return ' '.join(self.exelist) - - def needs_static_linker(self): - return True - - def get_werror_args(self): - return ['--fatal-warnings'] - - def get_dependency_gen_args(self, outtarget, outfile): - return ['-emit-dependencies'] - - def depfile_for_object(self, objfile): - return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix() - - def get_depfile_suffix(self): - return 'd' - - def get_output_args(self, target): - return ['-o', target] - - def get_linker_output_args(self, target): - return ['-o', target] - - def get_header_import_args(self, headername): - return ['-import-objc-header', headername] - - def get_warn_args(self, level): - return [] - - def get_buildtype_args(self, buildtype): - return swift_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - return [] - - def get_std_exe_link_args(self): - return ['-emit-executable'] - - def get_module_args(self, modname): - return ['-module-name', modname] - - def get_mod_gen_args(self): - return ['-emit-module'] - - def build_rpath_args(self, *args): - return [] # FIXME - - def get_include_args(self, dirname): - return ['-I' + dirname] - - def get_compile_only_args(self): - return ['-c'] - - def sanity_check(self, work_dir, environment): - src = 'swifttest.swift' - source_name = os.path.join(work_dir, src) - output_name = os.path.join(work_dir, 'swifttest') - with open(source_name, 'w') as ofile: - ofile.write('''print("Swift compilation is working.") -''') - extra_flags = self.get_cross_extra_flags(environment, link=True) - pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Swift compiler %s can not compile programs.' % self.name_string()) - if subprocess.call(output_name) != 0: - raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string()) - -class DCompiler(Compiler): - def __init__(self, exelist, version, is_cross): - self.language = 'd' - super().__init__(exelist, version) - self.id = 'unknown' - self.is_cross = is_cross - - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanity.d') - output_name = os.path.join(work_dir, 'dtest') - with open(source_name, 'w') as ofile: - ofile.write('''void main() { -} -''') - pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + [source_name], cwd=work_dir) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) - if subprocess.call(output_name) != 0: - raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) - - def needs_static_linker(self): - return True - - def name_string(self): - return ' '.join(self.exelist) - - def get_linker_exelist(self): - return self.exelist[:] - - def get_preprocess_only_args(self): - return ['-E'] - - def get_compile_only_args(self): - return ['-c'] - - def depfile_for_object(self, objfile): - return objfile + '.' + self.get_depfile_suffix() - - def get_depfile_suffix(self): - return 'dep' - - def get_pic_args(self): - return ['-fPIC'] - - def get_std_shared_lib_link_args(self): - return ['-shared'] - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - # FIXME: Make this work for Windows, MacOS and cross-compiling - return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, is_shared_module) - - def get_unittest_args(self): - return ['-unittest'] - - def get_buildtype_linker_args(self, buildtype): - return [] - - def get_std_exe_link_args(self): - return [] - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - # This method is to be used by LDC and DMD. - # GDC can deal with the verbatim flags. - if not rpath_paths and not install_rpath: - return [] - paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) - if len(paths) < len(install_rpath): - padding = 'X' * (len(install_rpath) - len(paths)) - if not paths: - paths = padding - else: - paths = paths + ':' + padding - return ['-L-rpath={}'.format(paths)] - - def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): - if extra_args is None: - extra_args = [] - elif isinstance(extra_args, str): - extra_args = [extra_args] - if dependencies is None: - dependencies = [] - elif not isinstance(dependencies, list): - dependencies = [dependencies] - # Collect compiler arguments - args = CompilerArgs(self) - for d in dependencies: - # Add compile flags needed by dependencies - args += d.get_compile_args() - if mode == 'link': - # Add link flags needed to find dependencies - args += d.get_link_args() - - if mode == 'compile': - # Add DFLAGS from the env - args += env.coredata.external_args[self.language] - elif mode == 'link': - # Add LDFLAGS from the env - args += env.coredata.external_link_args[self.language] - # extra_args must override all other arguments, so we add them last - args += extra_args - return args - - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): - args = self._get_compiler_check_args(env, extra_args, dependencies, mode) - - with self.compile(code, args, mode) as p: - return p.returncode == 0 - - def has_multi_arguments(self, args, env): - return self.compiles('int i;\n', env, extra_args=args) - - @classmethod - def translate_args_to_nongnu(cls, args): - dcargs = [] - # Translate common arguments to flags the LDC/DMD compilers - # can understand. - # The flags might have been added by pkg-config files, - # and are therefore out of the user's control. - for arg in args: - if arg == '-pthread': - continue - if arg.startswith('-Wl,'): - linkargs = arg[arg.index(',') + 1:].split(',') - for la in linkargs: - dcargs.append('-L' + la.strip()) - continue - elif arg.startswith('-l'): - # translate library link flag - dcargs.append('-L' + arg) - continue - elif arg.startswith('-L/') or arg.startswith('-L./'): - # we need to handle cases where -L is set by e.g. a pkg-config - # setting to select a linker search path. We can however not - # unconditionally prefix '-L' with '-L' because the user might - # have set this flag too to do what it is intended to for this - # compiler (pass flag through to the linker) - # Hence, we guess here whether the flag was intended to pass - # a linker search path. - dcargs.append('-L' + arg) - continue - dcargs.append(arg) - - return dcargs - -class GnuDCompiler(DCompiler): - def __init__(self, exelist, version, is_cross): - DCompiler.__init__(self, exelist, version, is_cross) - self.id = 'gcc' - default_warn_args = ['-Wall', '-Wdeprecated'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic'] - - def get_colorout_args(self, colortype): - if mesonlib.version_compare(self.version, '>=4.9.0'): - return gnu_color_args[colortype][:] - return [] - - def get_dependency_gen_args(self, outtarget, outfile): - return ['-fmake-deps=' + outfile] - - def get_output_args(self, target): - return ['-o', target] - - def get_linker_output_args(self, target): - return ['-o', target] - - def get_include_args(self, path, is_system): - return ['-I' + path] - - def get_warn_args(self, level): - return self.warn_args[level] - - def get_werror_args(self): - return ['-Werror'] - - def get_linker_search_args(self, dirname): - return ['-L' + dirname] - - def get_buildtype_args(self, buildtype): - return d_gdc_buildtype_args[buildtype] - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) - - def get_unittest_args(self): - return ['-funittest'] - -class LLVMDCompiler(DCompiler): - def __init__(self, exelist, version, is_cross): - DCompiler.__init__(self, exelist, version, is_cross) - self.id = 'llvm' - self.base_options = ['b_coverage', 'b_colorout'] - - def get_colorout_args(self, colortype): - if colortype == 'always': - return ['-enable-color'] - return [] - - def get_dependency_gen_args(self, outtarget, outfile): - # LDC using the -deps flag returns a non-Makefile dependency-info file, which - # the backends can not use. So we disable this feature for now. - return [] - - def get_output_args(self, target): - return ['-of', target] - - def get_linker_output_args(self, target): - return ['-of', target] - - def get_include_args(self, path, is_system): - return ['-I' + path] - - def get_warn_args(self, level): - if level == '2' or level == '3': - return ['-wi', '-dw'] - else: - return ['-wi'] - - def get_werror_args(self): - return ['-w'] - - def get_coverage_args(self): - return ['-cov'] - - def get_buildtype_args(self, buildtype): - return d_ldc_buildtype_args[buildtype] - - def get_pic_args(self): - return ['-relocation-model=pic'] - - def get_linker_search_args(self, dirname): - # -L is recognized as "add this to the search path" by the linker, - # while the compiler recognizes it as "pass to linker". So, the first - # -L is for the compiler, telling it to pass the second -L to the linker. - return ['-L-L' + dirname] - - @classmethod - def unix_args_to_native(cls, args): - return cls.translate_args_to_nongnu(args) - -class DmdDCompiler(DCompiler): - def __init__(self, exelist, version, is_cross): - DCompiler.__init__(self, exelist, version, is_cross) - self.id = 'dmd' - self.base_options = ['b_coverage', 'b_colorout'] - - def get_colorout_args(self, colortype): - if colortype == 'always': - return ['-color=on'] - return [] - - def get_dependency_gen_args(self, outtarget, outfile): - # LDC using the -deps flag returns a non-Makefile dependency-info file, which - # the backends can not use. So we disable this feature for now. - return [] - - def get_output_args(self, target): - return ['-of' + target] - - def get_werror_args(self): - return ['-w'] - - def get_linker_output_args(self, target): - return ['-of' + target] - - def get_include_args(self, path, is_system): - return ['-I' + path] - - def get_warn_args(self, level): - return ['-wi'] - - def get_coverage_args(self): - return ['-cov'] - - def get_linker_search_args(self, dirname): - # -L is recognized as "add this to the search path" by the linker, - # while the compiler recognizes it as "pass to linker". So, the first - # -L is for the compiler, telling it to pass the second -L to the linker. - return ['-L-L' + dirname] - - def get_buildtype_args(self, buildtype): - return d_dmd_buildtype_args[buildtype] - - def get_std_shared_lib_link_args(self): - return ['-shared', '-defaultlib=libphobos2.so'] - - @classmethod - def unix_args_to_native(cls, args): - return cls.translate_args_to_nongnu(args) - -class VisualStudioCCompiler(CCompiler): - std_warn_args = ['/W3'] - std_opt_args = ['/O2'] - - def __init__(self, exelist, version, is_cross, exe_wrap): - CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - self.id = 'msvc' - # /showIncludes is needed for build dependency tracking in Ninja - # See: https://ninja-build.org/manual.html#_deps - self.always_args = ['/nologo', '/showIncludes'] - self.warn_args = {'1': ['/W2'], - '2': ['/W3'], - '3': ['/W4']} - self.base_options = ['b_pch'] # FIXME add lto, pgo and the like - - # Override CCompiler.get_always_args - def get_always_args(self): - return self.always_args - - def get_linker_debug_crt_args(self): - """ - Arguments needed to select a debug crt for the linker - - Sometimes we need to manually select the CRT (C runtime) to use with - MSVC. One example is when trying to link with static libraries since - MSVC won't auto-select a CRT for us in that case and will error out - asking us to select one. - """ - return ['/MDd'] - - def get_buildtype_args(self, buildtype): - return msvc_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - return msvc_buildtype_linker_args[buildtype] - - def get_pch_suffix(self): - return 'pch' - - def get_pch_name(self, header): - chopped = os.path.split(header)[-1].split('.')[:-1] - chopped.append(self.get_pch_suffix()) - pchname = '.'.join(chopped) - return pchname - - def get_pch_use_args(self, pch_dir, header): - base = os.path.split(header)[-1] - pchname = self.get_pch_name(header) - return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)] - - def get_preprocess_only_args(self): - return ['/EP'] - - def get_compile_only_args(self): - return ['/c'] - - def get_no_optimization_args(self): - return ['/Od'] - - def get_output_args(self, target): - if target.endswith('.exe'): - return ['/Fe' + target] - return ['/Fo' + target] - - def get_dependency_gen_args(self, outtarget, outfile): - return [] - - def get_linker_exelist(self): - return ['link'] # FIXME, should have same path as compiler. - - def get_linker_always_args(self): - return ['/nologo'] - - def get_linker_output_args(self, outputname): - return ['/OUT:' + outputname] - - def get_linker_search_args(self, dirname): - return ['/LIBPATH:' + dirname] - - def get_pic_args(self): - return [] # PIC is handled by the loader on Windows - - def get_std_shared_lib_link_args(self): - return ['/DLL'] - - def gen_vs_module_defs_args(self, defsfile): - if not isinstance(defsfile, str): - raise RuntimeError('Module definitions file should be str') - # With MSVC, DLLs only export symbols that are explicitly exported, - # so if a module defs file is specified, we use that to export symbols - return ['/DEF:' + defsfile] - - def gen_pch_args(self, header, source, pchname): - 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, from_dir, rpath_paths, install_rpath): - return [] - - # FIXME, no idea what these should be. - def thread_flags(self): - return [] - - def thread_link_flags(self): - return [] - - def get_options(self): - return {'c_winlibs': coredata.UserStringArrayOption('c_winlibs', - 'Windows libs to link against.', - msvc_winlibs) - } - - def get_option_link_args(self, options): - return options['c_winlibs'].value[:] - - @classmethod - def unix_args_to_native(cls, args): - result = [] - for i in args: - # -mms-bitfields is specific to MinGW-GCC - # -pthread is only valid for GCC - if i in ('-mms-bitfields', '-pthread'): - continue - if i.startswith('-L'): - i = '/LIBPATH:' + i[2:] - # Translate GNU-style -lfoo library name to the import library - elif i.startswith('-l'): - name = i[2:] - if name in ('m', 'c', 'pthread'): - # With MSVC, these are provided by the C runtime which is - # linked in by default - continue - else: - i = name + '.lib' - # -pthread in link flags is only used on Linux - elif i == '-pthread': - continue - result.append(i) - return result - - def get_werror_args(self): - return ['/WX'] - - def get_include_args(self, path, is_system): - if path == '': - path = '.' - # msvc does not have a concept of system header dirs. - return ['-I' + path] - - # Visual Studio is special. It ignores some arguments it does not - # understand and you can't tell it to error out on those. - # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t - def has_multi_arguments(self, args, env): - warning_text = '9002' - code = 'int i;\n' - (fd, srcname) = tempfile.mkstemp(suffix='.' + self.default_suffix) - os.close(fd) - with open(srcname, 'w') as ofile: - ofile.write(code) - # Read c_args/cpp_args/etc from the cross-info file (if needed) - extra_args = self.get_cross_extra_flags(env, link=False) - extra_args += self.get_compile_only_args() - commands = self.exelist + args + extra_args + [srcname] - mlog.debug('Running VS compile:') - mlog.debug('Command line: ', ' '.join(commands)) - mlog.debug('Code:\n', code) - p, stdo, stde = Popen_safe(commands, cwd=os.path.split(srcname)[0]) - if p.returncode != 0: - return False - return not(warning_text in stde or warning_text in stdo) - - def get_compile_debugfile_args(self, rel_obj, pch=False): - pdbarr = rel_obj.split('.')[:-1] - pdbarr += ['pdb'] - args = ['/Fd' + '.'.join(pdbarr)] - # When generating a PDB file with PCH, all compile commands write - # to the same PDB file. Hence, we need to serialize the PDB - # writes using /FS since we do parallel builds. This slows down the - # build obviously, which is why we only do this when PCH is on. - # This was added in Visual Studio 2013 (MSVC 18.0). Before that it was - # always on: https://msdn.microsoft.com/en-us/library/dn502518.aspx - if pch and mesonlib.version_compare(self.version, '>=18.0'): - args = ['/FS'] + args - return args - - def get_link_debugfile_args(self, targetfile): - pdbarr = targetfile.split('.')[:-1] - pdbarr += ['pdb'] - return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)] - - def get_link_whole_for(self, args): - # Only since VS2015 - if not isinstance(args, list): - args = [args] - return ['/WHOLEARCHIVE:' + x for x in args] - - -class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): - def __init__(self, exelist, version, is_cross, exe_wrap): - self.language = 'cpp' - CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - self.base_options = ['b_pch'] # FIXME add lto, pgo and the like - - def get_options(self): - return {'cpp_eh': coredata.UserComboOption('cpp_eh', - 'C++ exception handling type.', - ['none', 'a', 's', 'sc'], - 'sc'), - 'cpp_winlibs': coredata.UserStringArrayOption('cpp_winlibs', - 'Windows libs to link against.', - msvc_winlibs) - } - - def get_option_compile_args(self, options): - args = [] - std = options['cpp_eh'] - if std.value != 'none': - args.append('/EH' + std.value) - return args - - def get_option_link_args(self, options): - return options['cpp_winlibs'].value[:] - - def get_compiler_check_args(self): - # Visual Studio C++ compiler doesn't support -fpermissive, - # so just use the plain C args. - return super(VisualStudioCCompiler, self).get_compiler_check_args() - -GCC_STANDARD = 0 -GCC_OSX = 1 -GCC_MINGW = 2 -GCC_CYGWIN = 3 - -CLANG_STANDARD = 0 -CLANG_OSX = 1 -CLANG_WIN = 2 -# Possibly clang-cl? - -ICC_STANDARD = 0 -ICC_OSX = 1 -ICC_WIN = 2 - -def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): - if soversion is None: - sostr = '' - else: - sostr = '.' + soversion - if gcc_type in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN): - # Might not be correct for mingw but seems to work. - return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] - elif gcc_type == GCC_OSX: - if is_shared_module: - return [] - return ['-install_name', os.path.join(path, 'lib' + shlib_name + '.dylib')] - else: - raise RuntimeError('Not implemented yet.') - -def get_compiler_is_linuxlike(compiler): - if (getattr(compiler, 'gcc_type', None) == GCC_STANDARD) or \ - (getattr(compiler, 'clang_type', None) == CLANG_STANDARD) or \ - (getattr(compiler, 'icc_type', None) == ICC_STANDARD): - return True - return False - -def get_compiler_uses_gnuld(c): - # FIXME: Perhaps we should detect the linker in the environment? - # FIXME: Assumes that *BSD use GNU ld, but they might start using lld soon - if (getattr(c, 'gcc_type', None) in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN)) or \ - (getattr(c, 'clang_type', None) in (CLANG_STANDARD, CLANG_WIN)) or \ - (getattr(c, 'icc_type', None) in (ICC_STANDARD, ICC_WIN)): - return True - return False - -def get_largefile_args(compiler): - ''' - Enable transparent large-file-support for 32-bit UNIX systems - ''' - if get_compiler_is_linuxlike(compiler): - # Enable large-file support unconditionally on all platforms other - # than macOS and Windows. macOS is now 64-bit-only so it doesn't - # need anything special, and Windows doesn't have automatic LFS. - # You must use the 64-bit counterparts explicitly. - # glibc, musl, and uclibc, and all BSD libcs support this. On Android, - # support for transparent LFS is available depending on the version of - # Bionic: https://github.com/android/platform_bionic#32-bit-abi-bugs - # https://code.google.com/p/android/issues/detail?id=64613 - # - # If this breaks your code, fix it! It's been 20+ years! - return ['-D_FILE_OFFSET_BITS=64'] - # We don't enable -D_LARGEFILE64_SOURCE since that enables - # transitionary features and must be enabled by programs that use - # those features explicitly. - return [] - - -class GnuCompiler: - # Functionality that is common to all GNU family compilers. - def __init__(self, gcc_type, defines): - self.id = 'gcc' - self.gcc_type = gcc_type - self.defines = defines or {} - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', - 'b_colorout', 'b_ndebug', 'b_staticpic'] - if self.gcc_type != GCC_OSX: - self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') - # All GCC backends can do assembly - self.can_compile_suffixes.add('s') - - def get_colorout_args(self, colortype): - if mesonlib.version_compare(self.version, '>=4.9.0'): - return gnu_color_args[colortype][:] - return [] - - def get_warn_args(self, level): - args = super().get_warn_args(level) - if mesonlib.version_compare(self.version, '<4.8.0') and '-Wpedantic' in args: - # -Wpedantic was added in 4.8.0 - # https://gcc.gnu.org/gcc-4.8/changes.html - args[args.index('-Wpedantic')] = '-pedantic' - return args - - def has_builtin_define(self, define): - return define in self.defines - - def get_builtin_define(self, define): - if define in self.defines: - return self.defines[define] - - def get_pic_args(self): - if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX): - return [] # On Window and OS X, pic is always on. - return ['-fPIC'] - - def get_buildtype_args(self, buildtype): - return gnulike_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - if self.gcc_type == GCC_OSX: - return apple_buildtype_linker_args[buildtype] - return gnulike_buildtype_linker_args[buildtype] - - def get_pch_suffix(self): - return 'gch' - - def split_shlib_to_parts(self, fname): - return os.path.split(fname)[0], fname - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) - - def get_std_shared_lib_link_args(self): - if self.gcc_type == GCC_OSX: - return ['-bundle'] - return ['-shared'] - - def get_link_whole_for(self, args): - return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] - - def gen_vs_module_defs_args(self, defsfile): - if not isinstance(defsfile, str): - raise RuntimeError('Module definitions file should be str') - # On Windows targets, .def files may be specified on the linker command - # line like an object file. - if self.gcc_type in (GCC_CYGWIN, GCC_MINGW): - return [defsfile] - # For other targets, discard the .def file. - return [] - - def get_gui_app_args(self): - if self.gcc_type in (GCC_CYGWIN, GCC_MINGW): - return ['-mwindows'] - return [] - -class GnuCCompiler(GnuCompiler, CCompiler): - def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): - CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - GnuCompiler.__init__(self, gcc_type, defines) - default_warn_args = ['-Wall', '-Winvalid-pch'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', - ['none', 'c89', 'c99', 'c11', - 'gnu89', 'gnu99', 'gnu11'], - 'none')} - if self.gcc_type == GCC_MINGW: - opts.update({ - 'c_winlibs': coredata.UserStringArrayOption('c_winlibs', 'Standard Win libraries to link against', - gnu_winlibs), }) - return opts - - def get_option_compile_args(self, options): - args = [] - std = options['c_std'] - if std.value != 'none': - args.append('-std=' + std.value) - return args - - def get_option_link_args(self, options): - if self.gcc_type == GCC_MINGW: - return options['c_winlibs'].value[:] - return [] - - def get_std_shared_lib_link_args(self): - return ['-shared'] - - -class GnuCPPCompiler(GnuCompiler, CPPCompiler): - - def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap, defines): - CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - GnuCompiler.__init__(self, gcc_type, defines) - default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none', 'c++03', 'c++11', 'c++14', 'c++1z', - 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'], - 'none'), - 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', - 'STL debug mode', - False)} - if self.gcc_type == GCC_MINGW: - opts.update({ - 'cpp_winlibs': coredata.UserStringArrayOption('cpp_winlibs', 'Standard Win libraries to link against', - gnu_winlibs), }) - return opts - - def get_option_compile_args(self, options): - args = [] - std = options['cpp_std'] - if std.value != 'none': - args.append('-std=' + std.value) - if options['cpp_debugstl'].value: - args.append('-D_GLIBCXX_DEBUG=1') - return args - - def get_option_link_args(self, options): - if self.gcc_type == GCC_MINGW: - return options['cpp_winlibs'].value[:] - return [] - - -class GnuObjCCompiler(GnuCompiler, ObjCCompiler): - - def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): - ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - GnuCompiler.__init__(self, gcc_type, defines) - default_warn_args = ['-Wall', '-Winvalid-pch'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - -class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): - - def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): - ObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - GnuCompiler.__init__(self, gcc_type, defines) - default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - -class ClangCompiler: - def __init__(self, clang_type): - self.id = 'clang' - self.clang_type = clang_type - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', - 'b_ndebug', 'b_staticpic', 'b_colorout'] - if self.clang_type != CLANG_OSX: - self.base_options.append('b_lundef') - self.base_options.append('b_asneeded') - # All Clang backends can do assembly and LLVM IR - self.can_compile_suffixes.update(['ll', 's']) - - def get_pic_args(self): - if self.clang_type in (CLANG_WIN, CLANG_OSX): - return [] # On Window and OS X, pic is always on. - return ['-fPIC'] - - def get_colorout_args(self, colortype): - return clang_color_args[colortype][:] - - def get_buildtype_args(self, buildtype): - return gnulike_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - if self.clang_type == CLANG_OSX: - return apple_buildtype_linker_args[buildtype] - return gnulike_buildtype_linker_args[buildtype] - - def get_pch_suffix(self): - return 'pch' - - def get_pch_use_args(self, pch_dir, header): - # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136 - # This flag is internal to Clang (or at least not documented on the man page) - # so it might change semantics at any time. - return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))] - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - if self.clang_type == CLANG_STANDARD: - gcc_type = GCC_STANDARD - elif self.clang_type == CLANG_OSX: - gcc_type = GCC_OSX - elif self.clang_type == CLANG_WIN: - gcc_type = GCC_MINGW - else: - raise MesonException('Unreachable code when converting clang type to gcc type.') - return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) - - def has_multi_arguments(self, args, env): - return super().has_multi_arguments( - ['-Werror=unknown-warning-option'] + args, - env) - - def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): - if extra_args is None: - extra_args = [] - # Starting with XCode 8, we need to pass this to force linker - # visibility to obey OS X and iOS minimum version targets with - # -mmacosx-version-min, -miphoneos-version-min, etc. - # https://github.com/Homebrew/homebrew-core/issues/3727 - if self.clang_type == CLANG_OSX and version_compare(self.version, '>=8.0'): - extra_args.append('-Wl,-no_weak_imports') - return super().has_function(funcname, prefix, env, extra_args, dependencies) - - def get_std_shared_module_link_args(self): - if self.clang_type == CLANG_OSX: - return ['-bundle', '-Wl,-undefined,dynamic_lookup'] - return ['-shared'] - - def get_link_whole_for(self, args): - if self.clang_type == CLANG_OSX: - result = [] - for a in args: - result += ['-Wl,-force_load', a] - return result - return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] - - -class ClangCCompiler(ClangCompiler, CCompiler): - def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None): - CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - ClangCompiler.__init__(self, clang_type) - default_warn_args = ['-Wall', '-Winvalid-pch'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - return {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', - ['none', 'c89', 'c99', 'c11', - 'gnu89', 'gnu99', 'gnu11'], - 'none')} - - def get_option_compile_args(self, options): - args = [] - std = options['c_std'] - if std.value != 'none': - args.append('-std=' + std.value) - return args - - def get_option_link_args(self, options): - return [] - - -class ClangCPPCompiler(ClangCompiler, CPPCompiler): - def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): - CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - ClangCompiler.__init__(self, cltype) - default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - return {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none', 'c++03', 'c++11', 'c++14', 'c++1z', - 'gnu++11', 'gnu++14', 'gnu++1z'], - 'none')} - - def get_option_compile_args(self, options): - args = [] - std = options['cpp_std'] - if std.value != 'none': - args.append('-std=' + std.value) - return args - - def get_option_link_args(self, options): - return [] - -class ClangObjCCompiler(ClangCompiler, GnuObjCCompiler): - def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): - GnuObjCCompiler.__init__(self, exelist, version, cltype, is_cross, exe_wrapper) - ClangCompiler.__init__(self, cltype) - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] - -class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler): - def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): - GnuObjCPPCompiler.__init__(self, exelist, version, cltype, is_cross, exe_wrapper) - ClangCompiler.__init__(self, cltype) - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] - - -# Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1 -class IntelCompiler: - def __init__(self, icc_type): - self.id = 'intel' - self.icc_type = icc_type - self.lang_header = 'none' - self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', - 'b_colorout', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded'] - # Assembly - self.can_compile_suffixes.add('s') - - def get_pic_args(self): - return ['-fPIC'] - - def get_buildtype_args(self, buildtype): - return gnulike_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - return gnulike_buildtype_linker_args[buildtype] - - def get_pch_suffix(self): - return 'pchi' - - def get_pch_use_args(self, pch_dir, header): - return ['-pch', '-pch_dir', os.path.join(pch_dir), '-x', - self.lang_header, '-include', header, '-x', 'none'] - - def get_pch_name(self, header_name): - return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() - - def split_shlib_to_parts(self, fname): - return os.path.split(fname)[0], fname - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - if self.icc_type == ICC_STANDARD: - gcc_type = GCC_STANDARD - elif self.icc_type == ICC_OSX: - gcc_type = GCC_OSX - elif self.icc_type == ICC_WIN: - gcc_type = GCC_MINGW - else: - raise MesonException('Unreachable code when converting icc type to gcc type.') - return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) - - def get_std_shared_lib_link_args(self): - # FIXME: Don't know how icc works on OSX - # if self.icc_type == ICC_OSX: - # return ['-bundle'] - return ['-shared'] - - -class IntelCCompiler(IntelCompiler, CCompiler): - def __init__(self, exelist, version, icc_type, is_cross, exe_wrapper=None): - CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - IntelCompiler.__init__(self, icc_type) - self.lang_header = 'c-header' - default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', '-Wpch-messages'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - c_stds = ['c89', 'c99'] - g_stds = ['gnu89', 'gnu99'] - if mesonlib.version_compare(self.version, '>=16.0.0'): - c_stds += ['c11'] - opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', - ['none'] + c_stds + g_stds, - 'none')} - return opts - - def get_option_compile_args(self, options): - args = [] - std = options['c_std'] - if std.value != 'none': - args.append('-std=' + std.value) - return args - - def get_std_shared_lib_link_args(self): - return ['-shared'] - - def has_multi_arguments(self, args, env): - return super().has_multi_arguments(args + ['-diag-error', '10006'], env) - - -class IntelCPPCompiler(IntelCompiler, CPPCompiler): - def __init__(self, exelist, version, icc_type, is_cross, exe_wrap): - CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) - IntelCompiler.__init__(self, icc_type) - self.lang_header = 'c++-header' - default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', - '-Wpch-messages', '-Wnon-virtual-dtor'] - self.warn_args = {'1': default_warn_args, - '2': default_warn_args + ['-Wextra'], - '3': default_warn_args + ['-Wextra', '-Wpedantic']} - - def get_options(self): - c_stds = [] - g_stds = ['gnu++98'] - if mesonlib.version_compare(self.version, '>=15.0.0'): - c_stds += ['c++11', 'c++14'] - g_stds += ['gnu++11'] - if mesonlib.version_compare(self.version, '>=16.0.0'): - c_stds += ['c++17'] - if mesonlib.version_compare(self.version, '>=17.0.0'): - g_stds += ['gnu++14'] - opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none'] + c_stds + g_stds, - 'none'), - 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', - 'STL debug mode', - False)} - return opts - - def get_option_compile_args(self, options): - args = [] - std = options['cpp_std'] - if std.value != 'none': - args.append('-std=' + std.value) - if options['cpp_debugstl'].value: - args.append('-D_GLIBCXX_DEBUG=1') - return args - - def get_option_link_args(self, options): - return [] - - def has_multi_arguments(self, args, env): - return super().has_multi_arguments(args + ['-diag-error', '10006'], env) - - -class FortranCompiler(Compiler): - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - self.language = 'fortran' - super().__init__(exelist, version) - self.is_cross = is_cross - self.exe_wrapper = exe_wrapper - # Not really correct but I don't have Fortran compilers to test with. Sorry. - self.gcc_type = GCC_STANDARD - self.id = "IMPLEMENTATION CLASSES MUST SET THIS" - - def name_string(self): - return ' '.join(self.exelist) - - def get_pic_args(self): - if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX): - return [] # On Window and OS X, pic is always on. - return ['-fPIC'] - - def get_std_shared_lib_link_args(self): - return ['-shared'] - - def needs_static_linker(self): - return True - - def sanity_check(self, work_dir, environment): - source_name = os.path.join(work_dir, 'sanitycheckf.f90') - binary_name = os.path.join(work_dir, 'sanitycheckf') - with open(source_name, 'w') as ofile: - ofile.write('''program prog - print *, "Fortran compilation is working." -end program prog -''') - extra_flags = self.get_cross_extra_flags(environment, link=True) - pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) - pc.wait() - if pc.returncode != 0: - raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string()) - if self.is_cross: - if self.exe_wrapper is None: - # Can't check if the binaries run so we have to assume they do - return - cmdlist = self.exe_wrapper + [binary_name] - else: - cmdlist = [binary_name] - pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - pe.wait() - if pe.returncode != 0: - raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string()) - - def get_std_warn_args(self, level): - return FortranCompiler.std_warn_args - - def get_buildtype_args(self, buildtype): - return gnulike_buildtype_args[buildtype] - - def get_buildtype_linker_args(self, buildtype): - if mesonlib.is_osx(): - return apple_buildtype_linker_args[buildtype] - return gnulike_buildtype_linker_args[buildtype] - - def split_shlib_to_parts(self, fname): - return os.path.split(fname)[0], fname - - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) - - def get_dependency_gen_args(self, outtarget, outfile): - # Disabled until this is fixed: - # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62162 - # return ['-cpp', '-MMD', '-MQ', outtarget] - return [] - - def get_output_args(self, target): - return ['-o', target] - - def get_preprocess_only_args(self): - return ['-E'] - - def get_compile_only_args(self): - return ['-c'] - - def get_linker_exelist(self): - return self.exelist[:] - - def get_linker_output_args(self, outputname): - return ['-o', outputname] - - def get_include_args(self, path, is_system): - return ['-I' + path] - - def get_module_outdir_args(self, path): - return ['-J' + path] - - def depfile_for_object(self, objfile): - return objfile + '.' + self.get_depfile_suffix() - - def get_depfile_suffix(self): - return 'd' - - def get_std_exe_link_args(self): - return [] - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) - - def module_name_to_filename(self, module_name): - return module_name.lower() + '.mod' - - def get_warn_args(self, level): - return ['-Wall'] - - def get_no_warn_args(self): - return ['-w'] - - -class GnuFortranCompiler(FortranCompiler): - def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): - super().__init__(exelist, version, is_cross, exe_wrapper=None) - self.gcc_type = gcc_type - self.defines = defines or {} - self.id = 'gcc' - - def has_builtin_define(self, define): - return define in self.defines - - def get_builtin_define(self, define): - if define in self.defines: - return self.defines[define] - - 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) - self.id = 'g95' - - def get_module_outdir_args(self, path): - return ['-fmod=' + path] - - def get_always_args(self): - return ['-pipe'] - - def get_no_warn_args(self): - # FIXME: Confirm that there's no compiler option to disable all warnings - return [] - - 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) - self.id = 'sun' - - def get_dependency_gen_args(self, outtarget, outfile): - return ['-fpp'] - - def get_always_args(self): - return [] - - def get_warn_args(self, level): - return [] - - def get_module_outdir_args(self, path): - return ['-moddir=' + path] - -class IntelFortranCompiler(IntelCompiler, FortranCompiler): - std_warn_args = ['-warn', 'all'] - - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - self.file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp') - FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) - # FIXME: Add support for OS X and Windows in detect_fortran_compiler so - # we are sent the type of compiler - IntelCompiler.__init__(self, ICC_STANDARD) - self.id = 'intel' - - def get_module_outdir_args(self, path): - return ['-module', path] - - def get_warn_args(self, level): - return IntelFortranCompiler.std_warn_args - -class PathScaleFortranCompiler(FortranCompiler): - std_warn_args = ['-fullwarn'] - - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper=None) - self.id = 'pathscale' - - def get_module_outdir_args(self, path): - return ['-module', path] - - def get_std_warn_args(self, level): - return PathScaleFortranCompiler.std_warn_args - -class PGIFortranCompiler(FortranCompiler): - std_warn_args = ['-Minform=inform'] - - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper=None) - self.id = 'pgi' - - def get_module_outdir_args(self, path): - return ['-module', path] - - def get_warn_args(self, level): - return PGIFortranCompiler.std_warn_args - - def get_no_warn_args(self): - return ['-silent'] - - -class Open64FortranCompiler(FortranCompiler): - std_warn_args = ['-fullwarn'] - - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper=None) - self.id = 'open64' - - def get_module_outdir_args(self, path): - return ['-module', path] - - def get_warn_args(self, level): - return Open64FortranCompiler.std_warn_args - -class NAGFortranCompiler(FortranCompiler): - std_warn_args = [] - - def __init__(self, exelist, version, is_cross, exe_wrapper=None): - super().__init__(exelist, version, is_cross, exe_wrapper=None) - self.id = 'nagfor' - - def get_module_outdir_args(self, path): - return ['-mdir', path] - - def get_warn_args(self, level): - return NAGFortranCompiler.std_warn_args - -class StaticLinker: - pass - -class VisualStudioLinker(StaticLinker): - always_args = ['/NOLOGO'] - - def __init__(self, exelist): - self.exelist = exelist - - def get_exelist(self): - return self.exelist[:] - - def get_std_link_args(self): - return [] - - def get_buildtype_linker_args(self, buildtype): - return [] - - def get_output_args(self, target): - return ['/OUT:' + target] - - def get_coverage_link_args(self): - return [] - - def get_always_args(self): - return VisualStudioLinker.always_args - - def get_linker_always_args(self): - return VisualStudioLinker.always_args - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - return [] - - def thread_link_flags(self): - return [] - - def get_option_link_args(self, options): - return [] - - @classmethod - def unix_args_to_native(cls, args): - return VisualStudioCCompiler.unix_args_to_native(args) - - def get_link_debugfile_args(self, targetfile): - # Static libraries do not have PDB files - return [] - -class ArLinker(StaticLinker): - - def __init__(self, exelist): - self.exelist = exelist - self.id = 'ar' - pc, stdo = Popen_safe(self.exelist + ['-h'])[0:2] - # Enable deterministic builds if they are available. - if '[D]' in stdo: - self.std_args = ['csrD'] - else: - self.std_args = ['csr'] - - def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): - return [] - - def get_exelist(self): - return self.exelist[:] - - def get_std_link_args(self): - return self.std_args - - def get_output_args(self, target): - return [target] - - def get_buildtype_linker_args(self, buildtype): - return [] - - def get_linker_always_args(self): - return [] - - def get_coverage_link_args(self): - return [] - - def get_always_args(self): - return [] - - def thread_link_flags(self): - return [] - - def get_option_link_args(self, options): - return [] - - @classmethod - def unix_args_to_native(cls, args): - return args[:] - - def get_link_debugfile_args(self, targetfile): - return [] diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py new file mode 100644 index 0000000..f09f252 --- /dev/null +++ b/mesonbuild/compilers/__init__.py @@ -0,0 +1,161 @@ +# Copyright 2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Public symbols for compilers sub-package when using 'from . import compilers' +__all__ = [ + 'CLANG_OSX', + 'CLANG_STANDARD', + 'CLANG_WIN', + 'GCC_CYGWIN', + 'GCC_MINGW', + 'GCC_OSX', + 'GCC_STANDARD', + 'ICC_OSX', + 'ICC_STANDARD', + 'ICC_WIN', + + 'base_options', + 'clike_langs', + 'c_suffixes', + 'cpp_suffixes', + 'get_base_compile_args', + 'get_base_link_args', + 'is_assembly', + 'is_header', + 'is_library', + 'is_llvm_ir', + 'is_object', + 'is_source', + 'lang_suffixes', + 'sanitizer_compile_args', + 'sort_clike', + + 'CCompiler', + 'ClangCCompiler', + 'ClangCompiler', + 'ClangCPPCompiler', + 'ClangObjCCompiler', + 'ClangObjCPPCompiler', + 'CompilerArgs', + 'CPPCompiler', + 'DCompiler', + 'DmdDCompiler', + 'FortranCompiler', + 'G95FortranCompiler', + 'GnuCCompiler', + 'GnuCompiler', + 'GnuCPPCompiler', + 'GnuDCompiler', + 'GnuFortranCompiler', + 'GnuObjCCompiler', + 'GnuObjCPPCompiler', + 'IntelCompiler', + 'IntelCCompiler', + 'IntelCPPCompiler', + 'IntelFortranCompiler', + 'JavaCompiler', + 'LLVMDCompiler', + 'MonoCompiler', + 'NAGFortranCompiler', + 'ObjCCompiler', + 'ObjCPPCompiler', + 'Open64FortranCompiler', + 'PathScaleFortranCompiler', + 'PGIFortranCompiler', + 'RustCompiler', + 'SunFortranCompiler', + 'SwiftCompiler', + 'ValaCompiler', + 'VisualStudioCCompiler', + 'VisualStudioCPPCompiler', +] + +# Bring symbols from each module into compilers sub-package namespace +from .compilers import ( + GCC_OSX, + GCC_MINGW, + GCC_CYGWIN, + GCC_STANDARD, + CLANG_OSX, + CLANG_WIN, + CLANG_STANDARD, + ICC_OSX, + ICC_WIN, + ICC_STANDARD, + base_options, + clike_langs, + c_suffixes, + cpp_suffixes, + get_base_compile_args, + get_base_link_args, + is_header, + is_source, + is_assembly, + is_llvm_ir, + is_object, + is_library, + lang_suffixes, + sanitizer_compile_args, + sort_clike, + ClangCompiler, + CompilerArgs, + GnuCompiler, + IntelCompiler, +) +from .c import ( + CCompiler, + ClangCCompiler, + GnuCCompiler, + IntelCCompiler, + VisualStudioCCompiler, +) +from .cpp import ( + CPPCompiler, + ClangCPPCompiler, + GnuCPPCompiler, + IntelCPPCompiler, + VisualStudioCPPCompiler, +) +from .cs import MonoCompiler +from .d import ( + DCompiler, + DmdDCompiler, + GnuDCompiler, + LLVMDCompiler, +) +from .fortran import ( + FortranCompiler, + G95FortranCompiler, + GnuFortranCompiler, + IntelFortranCompiler, + NAGFortranCompiler, + Open64FortranCompiler, + PathScaleFortranCompiler, + PGIFortranCompiler, + SunFortranCompiler, +) +from .java import JavaCompiler +from .objc import ( + ObjCCompiler, + ClangObjCCompiler, + GnuObjCCompiler, +) +from .objcpp import ( + ObjCPPCompiler, + ClangObjCPPCompiler, + GnuObjCPPCompiler, +) +from .rust import RustCompiler +from .swift import SwiftCompiler +from .vala import ValaCompiler diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py new file mode 100644 index 0000000..cf9d1ee --- /dev/null +++ b/mesonbuild/compilers/c.py @@ -0,0 +1,1007 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess, os.path, tempfile + +from .. import mlog +from .. import coredata +from ..mesonlib import EnvironmentException, version_compare, Popen_safe + +from .compilers import ( + GCC_MINGW, + get_largefile_args, + gnu_winlibs, + msvc_buildtype_args, + msvc_buildtype_linker_args, + msvc_winlibs, + ClangCompiler, + Compiler, + CompilerArgs, + CrossNoRunException, + GnuCompiler, + IntelCompiler, + RunResult, +) + + +class CCompiler(Compiler): + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + # If a child ObjC or CPP class has already set it, don't set it ourselves + if not hasattr(self, 'language'): + self.language = 'c' + super().__init__(exelist, version) + self.id = 'unknown' + self.is_cross = is_cross + self.can_compile_suffixes.add('h') + if isinstance(exe_wrapper, str): + self.exe_wrapper = [exe_wrapper] + else: + self.exe_wrapper = exe_wrapper + + def needs_static_linker(self): + return True # When compiling static libraries, so yes. + + def get_always_args(self): + ''' + Args that are always-on for all C compilers other than MSVC + ''' + return ['-pipe'] + get_largefile_args(self) + + def get_linker_debug_crt_args(self): + """ + Arguments needed to select a debug crt for the linker + This is only needed for MSVC + """ + return [] + + def get_no_stdinc_args(self): + return ['-nostdinc'] + + def get_no_stdlib_link_args(self): + return ['-nostdlib'] + + def get_warn_args(self, level): + return self.warn_args[level] + + def get_no_warn_args(self): + # Almost every compiler uses this for disabling warnings + return ['-w'] + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + return [] + + def split_shlib_to_parts(self, fname): + return None, fname + + # The default behavior is this, override in MSVC + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) + + def get_dependency_gen_args(self, outtarget, outfile): + return ['-MMD', '-MQ', outtarget, '-MF', outfile] + + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'd' + + def get_exelist(self): + return self.exelist[:] + + def get_linker_exelist(self): + return self.exelist[:] + + def get_preprocess_only_args(self): + return ['-E', '-P'] + + def get_compile_only_args(self): + return ['-c'] + + def get_no_optimization_args(self): + return ['-O0'] + + def get_compiler_check_args(self): + ''' + Get arguments useful for compiler checks such as being permissive in + the code quality and not doing any optimization. + ''' + return self.get_no_optimization_args() + + def get_output_args(self, target): + return ['-o', target] + + def get_linker_output_args(self, outputname): + return ['-o', outputname] + + def get_coverage_args(self): + return ['--coverage'] + + def get_coverage_link_args(self): + return ['--coverage'] + + def get_werror_args(self): + return ['-Werror'] + + def get_std_exe_link_args(self): + return [] + + def get_include_args(self, path, is_system): + if path == '': + path = '.' + if is_system: + return ['-isystem', path] + return ['-I' + path] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def get_library_dirs(self): + stdo = Popen_safe(self.exelist + ['--print-search-dirs'])[1] + for line in stdo.split('\n'): + if line.startswith('libraries:'): + libstr = line.split('=', 1)[1] + return libstr.split(':') + return [] + + def get_pic_args(self): + return ['-fPIC'] + + def name_string(self): + return ' '.join(self.exelist) + + def get_pch_use_args(self, pch_dir, header): + return ['-include', os.path.split(header)[-1]] + + def get_pch_name(self, header_name): + return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() + + 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.get_display_language() + ' compiler:', ' '.join(self.exelist)) + mlog.debug('Is cross compiler: %s.' % str(self.is_cross)) + + extra_flags = [] + source_name = os.path.join(work_dir, sname) + binname = sname.rsplit('.', 1)[0] + if self.is_cross: + binname += '_cross' + if self.exe_wrapper is None: + # Linking cross built apps is painful. You can't really + # tell if you should use -nostdlib or not and for example + # on OSX the compiler binary is the same but you need + # a ton of compiler flags to differentiate between + # arm and x86_64. So just compile. + extra_flags += self.get_cross_extra_flags(environment, link=False) + extra_flags += self.get_compile_only_args() + else: + extra_flags += self.get_cross_extra_flags(environment, link=True) + # Is a valid executable output for all toolchains and platforms + binname += '.exe' + # Write binary check source + binary_name = os.path.join(work_dir, binname) + with open(source_name, 'w') as ofile: + ofile.write(code) + # Compile sanity check + cmdlist = self.exelist + extra_flags + [source_name] + self.get_output_args(binary_name) + pc, stdo, stde = Popen_safe(cmdlist, cwd=work_dir) + mlog.debug('Sanity check compiler command line:', ' '.join(cmdlist)) + mlog.debug('Sanity check compile stdout:') + mlog.debug(stdo) + mlog.debug('-----\nSanity check compile stderr:') + mlog.debug(stde) + mlog.debug('-----') + if pc.returncode != 0: + raise EnvironmentException('Compiler {0} can not compile programs.'.format(self.name_string())) + # Run sanity check + if self.is_cross: + if self.exe_wrapper is None: + # Can't check if the binaries run so we have to assume they do + return + cmdlist = self.exe_wrapper + [binary_name] + else: + cmdlist = [binary_name] + mlog.debug('Running test binary command: ' + ' '.join(cmdlist)) + pe = subprocess.Popen(cmdlist) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by {0} compiler {1} are not runnable.'.format(self.language, self.name_string())) + + def sanity_check(self, work_dir, environment): + code = 'int main(int argc, char **argv) { int class=0; return class; }\n' + return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) + + def has_header(self, hname, prefix, env, extra_args=None, dependencies=None): + fargs = {'prefix': prefix, 'header': hname} + code = '''{prefix} + #ifdef __has_include + #if !__has_include("{header}") + #error "Header '{header}' could not be found" + #endif + #else + #include <{header}> + #endif''' + return self.compiles(code.format(**fargs), env, extra_args, + dependencies, 'preprocess') + + def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): + fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} + t = '''{prefix} + #include <{header}> + int main () {{ + /* If it's not defined as a macro, try to use as a symbol */ + #ifndef {symbol} + {symbol}; + #endif + }}''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) + + def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): + if extra_args is None: + extra_args = [] + elif isinstance(extra_args, str): + extra_args = [extra_args] + if dependencies is None: + dependencies = [] + elif not isinstance(dependencies, list): + dependencies = [dependencies] + # Collect compiler arguments + args = CompilerArgs(self) + for d in dependencies: + # Add compile flags needed by dependencies + args += d.get_compile_args() + if mode == 'link': + # Add link flags needed to find dependencies + args += d.get_link_args() + # Select a CRT if needed since we're linking + if mode == 'link': + args += self.get_linker_debug_crt_args() + # Read c_args/cpp_args/etc from the cross-info file (if needed) + args += self.get_cross_extra_flags(env, link=(mode == 'link')) + if not self.is_cross: + if mode == 'preprocess': + # Add CPPFLAGS from the env. + args += env.coredata.external_preprocess_args[self.language] + elif mode == 'compile': + # Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env + args += env.coredata.external_args[self.language] + elif mode == 'link': + # Add LDFLAGS from the env + args += env.coredata.external_link_args[self.language] + args += self.get_compiler_check_args() + # extra_args must override all other arguments, so we add them last + args += extra_args + return args + + def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): + args = self._get_compiler_check_args(env, extra_args, dependencies, mode) + # We only want to compile; not link + with self.compile(code, args.to_native(), mode) as p: + return p.returncode == 0 + + def _links_wrapper(self, code, env, extra_args, dependencies): + "Shares common code between self.links and self.run" + args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link') + return self.compile(code, args) + + def links(self, code, env, extra_args=None, dependencies=None): + with self._links_wrapper(code, env, extra_args, dependencies) as p: + return p.returncode == 0 + + def run(self, code, env, extra_args=None, dependencies=None): + if self.is_cross and self.exe_wrapper is None: + raise CrossNoRunException('Can not run test applications in this cross environment.') + with self._links_wrapper(code, env, extra_args, dependencies) as p: + if p.returncode != 0: + mlog.debug('Could not compile test file %s: %d\n' % ( + p.input_name, + p.returncode)) + return RunResult(False) + if self.is_cross: + cmdlist = self.exe_wrapper + [p.output_name] + else: + cmdlist = p.output_name + try: + pe, so, se = Popen_safe(cmdlist) + except Exception as e: + mlog.debug('Could not run: %s (error: %s)\n' % (cmdlist, e)) + return RunResult(False) + + mlog.debug('Program stdout:\n') + mlog.debug(so) + mlog.debug('Program stderr:\n') + mlog.debug(se) + return RunResult(True, pe.returncode, so, se) + + def _compile_int(self, expression, prefix, env, extra_args, dependencies): + fargs = {'prefix': prefix, 'expression': expression} + t = '''#include <stdio.h> + {prefix} + int main() {{ static int a[1-2*!({expression})]; a[0]=0; return 0; }}''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) + + def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): + if isinstance(guess, int): + if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies): + return guess + + cur = low + while low < high: + cur = int((low + high) / 2) + if cur == low: + break + + if self._compile_int('%s >= %d' % (expression, cur), prefix, env, extra_args, dependencies): + low = cur + else: + high = cur + + if self._compile_int('%s == %d' % (expression, cur), prefix, env, extra_args, dependencies): + return cur + raise EnvironmentException('Cross-compile check overflowed') + + def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + if self.is_cross: + return self.cross_compute_int(expression, low, high, guess, prefix, env, extra_args, dependencies) + fargs = {'prefix': prefix, 'expression': expression} + t = '''#include<stdio.h> + {prefix} + int main(int argc, char **argv) {{ + printf("%ld\\n", (long)({expression})); + return 0; + }};''' + res = self.run(t.format(**fargs), env, extra_args, dependencies) + if not res.compiled: + return -1 + if res.returncode != 0: + raise EnvironmentException('Could not run compute_int test binary.') + return int(res.stdout) + + def cross_sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'type': typename} + t = '''#include <stdio.h> + {prefix} + int main(int argc, char **argv) {{ + {type} something; + }}''' + if not self.compiles(t.format(**fargs), env, extra_args, dependencies): + return -1 + return self.cross_compute_int('sizeof(%s)' % typename, 1, 128, None, prefix, env, extra_args, dependencies) + + def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'type': typename} + if self.is_cross: + return self.cross_sizeof(typename, prefix, env, extra_args, dependencies) + t = '''#include<stdio.h> + {prefix} + int main(int argc, char **argv) {{ + printf("%ld\\n", (long)(sizeof({type}))); + return 0; + }};''' + res = self.run(t.format(**fargs), env, extra_args, dependencies) + if not res.compiled: + return -1 + if res.returncode != 0: + raise EnvironmentException('Could not run sizeof test binary.') + return int(res.stdout) + + def cross_alignment(self, typename, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'type': typename} + t = '''#include <stdio.h> + {prefix} + int main(int argc, char **argv) {{ + {type} something; + }}''' + if not self.compiles(t.format(**fargs), env, extra_args, dependencies): + return -1 + t = '''#include <stddef.h> + {prefix} + struct tmp {{ + char c; + {type} target; + }};''' + return self.cross_compute_int('offsetof(struct tmp, target)', 1, 1024, None, t.format(**fargs), env, extra_args, dependencies) + + def alignment(self, typename, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + if self.is_cross: + return self.cross_alignment(typename, prefix, env, extra_args, dependencies) + fargs = {'prefix': prefix, 'type': typename} + t = '''#include <stdio.h> + #include <stddef.h> + {prefix} + struct tmp {{ + char c; + {type} target; + }}; + int main(int argc, char **argv) {{ + printf("%d", (int)offsetof(struct tmp, target)); + return 0; + }}''' + res = self.run(t.format(**fargs), env, extra_args, dependencies) + if not res.compiled: + raise EnvironmentException('Could not compile alignment test.') + if res.returncode != 0: + raise EnvironmentException('Could not run alignment test binary.') + align = int(res.stdout) + if align == 0: + raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename) + return align + + def get_define(self, dname, prefix, env, extra_args, dependencies): + delim = '"MESON_GET_DEFINE_DELIMITER"' + fargs = {'prefix': prefix, 'define': dname, 'delim': delim} + code = ''' + {prefix} + #ifndef {define} + # define {define} + #endif + {delim}\n{define}''' + args = self._get_compiler_check_args(env, extra_args, dependencies, + mode='preprocess').to_native() + with self.compile(code.format(**fargs), args, 'preprocess') as p: + if p.returncode != 0: + raise EnvironmentException('Could not get define {!r}'.format(dname)) + # Get the preprocessed value after the delimiter, + # minus the extra newline at the end + return p.stdo.split(delim + '\n')[-1][:-1] + + @staticmethod + def _no_prototype_templ(): + """ + Try to find the function without a prototype from a header by defining + our own dummy prototype and trying to link with the C library (and + whatever else the compiler links in by default). This is very similar + to the check performed by Autoconf for AC_CHECK_FUNCS. + """ + # Define the symbol to something else since it is defined by the + # includes or defines listed by the user or by the compiler. This may + # include, for instance _GNU_SOURCE which must be defined before + # limits.h, which includes features.h + # Then, undef the symbol to get rid of it completely. + head = ''' + #define {func} meson_disable_define_of_{func} + {prefix} + #include <limits.h> + #undef {func} + ''' + # Override any GCC internal prototype and declare our own definition for + # the symbol. Use char because that's unlikely to be an actual return + # value for a function which ensures that we override the definition. + head += ''' + #ifdef __cplusplus + extern "C" + #endif + char {func} (); + ''' + # The actual function call + main = ''' + int main () {{ + return {func} (); + }}''' + return head, main + + @staticmethod + def _have_prototype_templ(): + """ + Returns a head-er and main() call that uses the headers listed by the + user for the function prototype while checking if a function exists. + """ + # Add the 'prefix', aka defines, includes, etc that the user provides + # This may include, for instance _GNU_SOURCE which must be defined + # before limits.h, which includes features.h + head = '{prefix}\n#include <limits.h>\n' + # We don't know what the function takes or returns, so return it as an int. + # Just taking the address or comparing it to void is not enough because + # compilers are smart enough to optimize it away. The resulting binary + # is not run so we don't care what the return value is. + main = '''\nint main() {{ + void *a = (void*) &{func}; + long b = (long) a; + return (int) b; + }}''' + return head, main + + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + """ + First, this function looks for the symbol in the default libraries + provided by the compiler (stdlib + a few others usually). If that + fails, it checks if any of the headers specified in the prefix provide + an implementation of the function, and if that fails, it checks if it's + implemented as a compiler-builtin. + """ + if extra_args is None: + extra_args = [] + + # Short-circuit if the check is already provided by the cross-info file + varname = 'has function ' + funcname + varname = varname.replace(' ', '_') + if self.is_cross: + val = env.cross_info.config['properties'].get(varname, None) + if val is not None: + if isinstance(val, bool): + return val + raise EnvironmentException('Cross variable {0} is not a boolean.'.format(varname)) + + fargs = {'prefix': prefix, 'func': funcname} + + # glibc defines functions that are not available on Linux as stubs that + # fail with ENOSYS (such as e.g. lchmod). In this case we want to fail + # instead of detecting the stub as a valid symbol. + # We already included limits.h earlier to ensure that these are defined + # for stub functions. + stubs_fail = ''' + #if defined __stub_{func} || defined __stub___{func} + fail fail fail this function is not going to work + #endif + ''' + + # If we have any includes in the prefix supplied by the user, assume + # that the user wants us to use the symbol prototype defined in those + # includes. If not, then try to do the Autoconf-style check with + # a dummy prototype definition of our own. + # This is needed when the linker determines symbol availability from an + # SDK based on the prototype in the header provided by the SDK. + # Ignoring this prototype would result in the symbol always being + # marked as available. + if '#include' in prefix: + head, main = self._have_prototype_templ() + else: + head, main = self._no_prototype_templ() + templ = head + stubs_fail + main + + if self.links(templ.format(**fargs), env, extra_args, dependencies): + return True + + # MSVC does not have compiler __builtin_-s. + if self.get_id() == 'msvc': + return False + + # Detect function as a built-in + # + # Some functions like alloca() are defined as compiler built-ins which + # are inlined by the compiler and you can't take their address, so we + # need to look for them differently. On nice compilers like clang, we + # can just directly use the __has_builtin() macro. + fargs['no_includes'] = '#include' not in prefix + t = '''{prefix} + int main() {{ + #ifdef __has_builtin + #if !__has_builtin(__builtin_{func}) + #error "__builtin_{func} not found" + #endif + #elif ! defined({func}) + /* Check for __builtin_{func} only if no includes were added to the + * prefix above, which means no definition of {func} can be found. + * We would always check for this, but we get false positives on + * MSYS2 if we do. Their toolchain is broken, but we can at least + * give them a workaround. */ + #if {no_includes:d} + __builtin_{func}; + #else + #error "No definition for __builtin_{func} found in the prefix" + #endif + #endif + }}''' + return self.links(t.format(**fargs), env, extra_args, dependencies) + + def has_members(self, typename, membernames, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'} + # Create code that accesses all members + members = '' + for member in membernames: + members += '{}.{};\n'.format(fargs['name'], member) + fargs['members'] = members + t = '''{prefix} + void bar() {{ + {type} {name}; + {members} + }};''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) + + def has_type(self, typename, prefix, env, extra_args, dependencies=None): + fargs = {'prefix': prefix, 'type': typename} + t = '''{prefix} + void bar() {{ + sizeof({type}); + }};''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) + + def symbols_have_underscore_prefix(self, env): + ''' + Check if the compiler prefixes an underscore to global C symbols + ''' + symbol_name = b'meson_uscore_prefix' + code = '''#ifdef __cplusplus + extern "C" { + #endif + void ''' + symbol_name.decode() + ''' () {} + #ifdef __cplusplus + } + #endif + ''' + args = self.get_cross_extra_flags(env, link=False) + args += self.get_compiler_check_args() + n = 'symbols_have_underscore_prefix' + with self.compile(code, args, 'compile') as p: + if p.returncode != 0: + m = 'BUG: Unable to compile {!r} check: {}' + raise RuntimeError(m.format(n, p.stdo)) + if not os.path.isfile(p.output_name): + m = 'BUG: Can\'t find compiled test code for {!r} check' + raise RuntimeError(m.format(n)) + with open(p.output_name, 'rb') as o: + for line in o: + # Check if the underscore form of the symbol is somewhere + # in the output file. + if b'_' + symbol_name in line: + return True + # Else, check if the non-underscored form is present + elif symbol_name in line: + return False + raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n)) + + def find_library(self, libname, env, extra_dirs): + # First try if we can just add the library as -l. + code = '''int main(int argc, char **argv) { + return 0; +} + ''' + if extra_dirs and isinstance(extra_dirs, str): + extra_dirs = [extra_dirs] + # Gcc + co seem to prefer builtin lib dirs to -L dirs. + # Only try to find std libs if no extra dirs specified. + if not extra_dirs: + args = ['-l' + libname] + if self.links(code, env, extra_args=args): + return args + # Not found? Try to find the library file itself. + extra_dirs += self.get_library_dirs() + suffixes = ['so', 'dylib', 'lib', 'dll', 'a'] + for d in extra_dirs: + for suffix in suffixes: + trial = os.path.join(d, 'lib' + libname + '.' + suffix) + if os.path.isfile(trial): + return [trial] + trial2 = os.path.join(d, libname + '.' + suffix) + if os.path.isfile(trial2): + return [trial2] + return None + + def thread_flags(self): + return ['-pthread'] + + def thread_link_flags(self): + return ['-pthread'] + + def has_multi_arguments(self, args, env): + return self.compiles('int i;\n', env, extra_args=args) + + +class ClangCCompiler(ClangCompiler, CCompiler): + def __init__(self, exelist, version, clang_type, is_cross, exe_wrapper=None): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + ClangCompiler.__init__(self, clang_type) + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + return {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none', 'c89', 'c99', 'c11', + 'gnu89', 'gnu99', 'gnu11'], + 'none')} + + def get_option_compile_args(self, options): + args = [] + std = options['c_std'] + if std.value != 'none': + args.append('-std=' + std.value) + return args + + def get_option_link_args(self, options): + return [] + + +class GnuCCompiler(GnuCompiler, CCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + GnuCompiler.__init__(self, gcc_type, defines) + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none', 'c89', 'c99', 'c11', + 'gnu89', 'gnu99', 'gnu11'], + 'none')} + if self.gcc_type == GCC_MINGW: + opts.update({ + 'c_winlibs': coredata.UserStringArrayOption('c_winlibs', 'Standard Win libraries to link against', + gnu_winlibs), }) + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['c_std'] + if std.value != 'none': + args.append('-std=' + std.value) + return args + + def get_option_link_args(self, options): + if self.gcc_type == GCC_MINGW: + return options['c_winlibs'].value[:] + return [] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + +class IntelCCompiler(IntelCompiler, CCompiler): + def __init__(self, exelist, version, icc_type, is_cross, exe_wrapper=None): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + IntelCompiler.__init__(self, icc_type) + self.lang_header = 'c-header' + default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', '-Wpch-messages'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + c_stds = ['c89', 'c99'] + g_stds = ['gnu89', 'gnu99'] + if version_compare(self.version, '>=16.0.0'): + c_stds += ['c11'] + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none'] + c_stds + g_stds, + 'none')} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['c_std'] + if std.value != 'none': + args.append('-std=' + std.value) + return args + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def has_multi_arguments(self, args, env): + return super().has_multi_arguments(args + ['-diag-error', '10006'], env) + + +class VisualStudioCCompiler(CCompiler): + std_warn_args = ['/W3'] + std_opt_args = ['/O2'] + + def __init__(self, exelist, version, is_cross, exe_wrap): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + self.id = 'msvc' + # /showIncludes is needed for build dependency tracking in Ninja + # See: https://ninja-build.org/manual.html#_deps + self.always_args = ['/nologo', '/showIncludes'] + self.warn_args = {'1': ['/W2'], + '2': ['/W3'], + '3': ['/W4']} + self.base_options = ['b_pch'] # FIXME add lto, pgo and the like + + # Override CCompiler.get_always_args + def get_always_args(self): + return self.always_args + + def get_linker_debug_crt_args(self): + """ + Arguments needed to select a debug crt for the linker + + Sometimes we need to manually select the CRT (C runtime) to use with + MSVC. One example is when trying to link with static libraries since + MSVC won't auto-select a CRT for us in that case and will error out + asking us to select one. + """ + return ['/MDd'] + + def get_buildtype_args(self, buildtype): + return msvc_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + return msvc_buildtype_linker_args[buildtype] + + def get_pch_suffix(self): + return 'pch' + + def get_pch_name(self, header): + chopped = os.path.split(header)[-1].split('.')[:-1] + chopped.append(self.get_pch_suffix()) + pchname = '.'.join(chopped) + return pchname + + def get_pch_use_args(self, pch_dir, header): + base = os.path.split(header)[-1] + pchname = self.get_pch_name(header) + return ['/FI' + base, '/Yu' + base, '/Fp' + os.path.join(pch_dir, pchname)] + + def get_preprocess_only_args(self): + return ['/EP'] + + def get_compile_only_args(self): + return ['/c'] + + def get_no_optimization_args(self): + return ['/Od'] + + def get_output_args(self, target): + if target.endswith('.exe'): + return ['/Fe' + target] + return ['/Fo' + target] + + def get_dependency_gen_args(self, outtarget, outfile): + return [] + + def get_linker_exelist(self): + return ['link'] # FIXME, should have same path as compiler. + + def get_linker_always_args(self): + return ['/nologo'] + + def get_linker_output_args(self, outputname): + return ['/OUT:' + outputname] + + def get_linker_search_args(self, dirname): + return ['/LIBPATH:' + dirname] + + def get_pic_args(self): + return [] # PIC is handled by the loader on Windows + + def get_std_shared_lib_link_args(self): + return ['/DLL'] + + def gen_vs_module_defs_args(self, defsfile): + if not isinstance(defsfile, str): + raise RuntimeError('Module definitions file should be str') + # With MSVC, DLLs only export symbols that are explicitly exported, + # so if a module defs file is specified, we use that to export symbols + return ['/DEF:' + defsfile] + + def gen_pch_args(self, header, source, pchname): + 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, from_dir, rpath_paths, install_rpath): + return [] + + # FIXME, no idea what these should be. + def thread_flags(self): + return [] + + def thread_link_flags(self): + return [] + + def get_options(self): + return {'c_winlibs': coredata.UserStringArrayOption('c_winlibs', + 'Windows libs to link against.', + msvc_winlibs) + } + + def get_option_link_args(self, options): + return options['c_winlibs'].value[:] + + @classmethod + def unix_args_to_native(cls, args): + result = [] + for i in args: + # -mms-bitfields is specific to MinGW-GCC + # -pthread is only valid for GCC + if i in ('-mms-bitfields', '-pthread'): + continue + if i.startswith('-L'): + i = '/LIBPATH:' + i[2:] + # Translate GNU-style -lfoo library name to the import library + elif i.startswith('-l'): + name = i[2:] + if name in ('m', 'c', 'pthread'): + # With MSVC, these are provided by the C runtime which is + # linked in by default + continue + else: + i = name + '.lib' + # -pthread in link flags is only used on Linux + elif i == '-pthread': + continue + result.append(i) + return result + + def get_werror_args(self): + return ['/WX'] + + def get_include_args(self, path, is_system): + if path == '': + path = '.' + # msvc does not have a concept of system header dirs. + return ['-I' + path] + + # Visual Studio is special. It ignores some arguments it does not + # understand and you can't tell it to error out on those. + # http://stackoverflow.com/questions/15259720/how-can-i-make-the-microsoft-c-compiler-treat-unknown-flags-as-errors-rather-t + def has_multi_arguments(self, args, env): + warning_text = '9002' + code = 'int i;\n' + (fd, srcname) = tempfile.mkstemp(suffix='.' + self.default_suffix) + os.close(fd) + with open(srcname, 'w') as ofile: + ofile.write(code) + # Read c_args/cpp_args/etc from the cross-info file (if needed) + extra_args = self.get_cross_extra_flags(env, link=False) + extra_args += self.get_compile_only_args() + commands = self.exelist + args + extra_args + [srcname] + mlog.debug('Running VS compile:') + mlog.debug('Command line: ', ' '.join(commands)) + mlog.debug('Code:\n', code) + p, stdo, stde = Popen_safe(commands, cwd=os.path.split(srcname)[0]) + if p.returncode != 0: + return False + return not(warning_text in stde or warning_text in stdo) + + def get_compile_debugfile_args(self, rel_obj, pch=False): + pdbarr = rel_obj.split('.')[:-1] + pdbarr += ['pdb'] + args = ['/Fd' + '.'.join(pdbarr)] + # When generating a PDB file with PCH, all compile commands write + # to the same PDB file. Hence, we need to serialize the PDB + # writes using /FS since we do parallel builds. This slows down the + # build obviously, which is why we only do this when PCH is on. + # This was added in Visual Studio 2013 (MSVC 18.0). Before that it was + # always on: https://msdn.microsoft.com/en-us/library/dn502518.aspx + if pch and version_compare(self.version, '>=18.0'): + args = ['/FS'] + args + return args + + def get_link_debugfile_args(self, targetfile): + pdbarr = targetfile.split('.')[:-1] + pdbarr += ['pdb'] + return ['/DEBUG', '/PDB:' + '.'.join(pdbarr)] + + def get_link_whole_for(self, args): + # Only since VS2015 + if not isinstance(args, list): + args = [args] + return ['/WHOLEARCHIVE:' + x for x in args] diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py new file mode 100644 index 0000000..a200c07 --- /dev/null +++ b/mesonbuild/compilers/compilers.py @@ -0,0 +1,1062 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import contextlib, os.path, re, tempfile + +from ..linkers import StaticLinker +from .. import coredata +from .. import mlog +from .. import mesonlib +from ..mesonlib import EnvironmentException, MesonException, version_compare, Popen_safe + +"""This file contains the data files of all compilers Meson knows +about. To support a new compiler, add its information below. +Also add corresponding autodetection code in environment.py.""" + +header_suffixes = ('h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di') +obj_suffixes = ('o', 'obj', 'res') +lib_suffixes = ('a', 'lib', 'dll', 'dylib', 'so') +# Mapping of language to suffixes of files that should always be in that language +# This means we can't include .h headers here since they could be C, C++, ObjC, etc. +lang_suffixes = { + 'c': ('c',), + 'cpp': ('cpp', 'cc', 'cxx', 'c++', 'hh', 'hpp', 'ipp', 'hxx'), + # f90, f95, f03, f08 are for free-form fortran ('f90' recommended) + # f, for, ftn, fpp are for fixed-form fortran ('f' or 'for' recommended) + 'fortran': ('f90', 'f95', 'f03', 'f08', 'f', 'for', 'ftn', 'fpp'), + 'd': ('d', 'di'), + 'objc': ('m',), + 'objcpp': ('mm',), + 'rust': ('rs',), + 'vala': ('vala', 'vapi'), + 'cs': ('cs',), + 'swift': ('swift',), + 'java': ('java',), +} +cpp_suffixes = lang_suffixes['cpp'] + ('h',) +c_suffixes = lang_suffixes['c'] + ('h',) +# List of languages that can be linked with C code directly by the linker +# used in build.py:process_compilers() and build.py:get_dynamic_linker() +clike_langs = ('objcpp', 'objc', 'd', 'cpp', 'c', 'fortran',) +clike_suffixes = () +for _l in clike_langs: + clike_suffixes += lang_suffixes[_l] +clike_suffixes += ('h', 'll', 's') + +# All these are only for C-like languages; see `clike_langs` above. + +def sort_clike(lang): + ''' + Sorting function to sort the list of languages according to + reversed(compilers.clike_langs) and append the unknown langs in the end. + The purpose is to prefer C over C++ for files that can be compiled by + both such as assembly, C, etc. Also applies to ObjC, ObjC++, etc. + ''' + if lang not in clike_langs: + return 1 + return -clike_langs.index(lang) + +def is_header(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + suffix = fname.split('.')[-1] + return suffix in header_suffixes + +def is_source(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + suffix = fname.split('.')[-1].lower() + return suffix in clike_suffixes + +def is_assembly(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + return fname.split('.')[-1].lower() == 's' + +def is_llvm_ir(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + return fname.split('.')[-1] == 'll' + +def is_object(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + suffix = fname.split('.')[-1] + return suffix in obj_suffixes + +def is_library(fname): + if hasattr(fname, 'fname'): + fname = fname.fname + suffix = fname.split('.')[-1] + return suffix in lib_suffixes + +gnulike_buildtype_args = {'plain': [], + # -O0 is passed for improved debugging information with gcc + # See https://github.com/mesonbuild/meson/pull/509 + 'debug': ['-O0', '-g'], + 'debugoptimized': ['-O2', '-g'], + 'release': ['-O3'], + 'minsize': ['-Os', '-g']} + +msvc_buildtype_args = {'plain': [], + 'debug': ["/MDd", "/ZI", "/Ob0", "/Od", "/RTC1"], + 'debugoptimized': ["/MD", "/Zi", "/O2", "/Ob1"], + 'release': ["/MD", "/O2", "/Ob2"], + 'minsize': ["/MD", "/Zi", "/Os", "/Ob1"], + } + +apple_buildtype_linker_args = {'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': [], + } + +gnulike_buildtype_linker_args = {'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': ['-Wl,-O1'], + 'minsize': [], + } + +msvc_buildtype_linker_args = {'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': ['/INCREMENTAL:NO'], + } + +java_buildtype_args = {'plain': [], + 'debug': ['-g'], + 'debugoptimized': ['-g'], + 'release': [], + 'minsize': [], + } + +rust_buildtype_args = {'plain': [], + 'debug': ['-C', 'debuginfo=2'], + 'debugoptimized': ['-C', 'debuginfo=2', '-C', 'opt-level=2'], + 'release': ['-C', 'opt-level=3'], + 'minsize': [], # In a future release: ['-C', 'opt-level=s'], + } + +d_gdc_buildtype_args = {'plain': [], + 'debug': ['-g', '-O0'], + 'debugoptimized': ['-g', '-O'], + 'release': ['-O3', '-frelease'], + 'minsize': [], + } + +d_ldc_buildtype_args = {'plain': [], + 'debug': ['-g', '-O0'], + 'debugoptimized': ['-g', '-O'], + 'release': ['-O3', '-release'], + 'minsize': [], + } + +d_dmd_buildtype_args = {'plain': [], + 'debug': ['-g'], + 'debugoptimized': ['-g', '-O'], + 'release': ['-O', '-release'], + 'minsize': [], + } + +mono_buildtype_args = {'plain': [], + 'debug': ['-debug'], + 'debugoptimized': ['-debug', '-optimize+'], + 'release': ['-optimize+'], + 'minsize': [], + } + +swift_buildtype_args = {'plain': [], + 'debug': ['-g'], + 'debugoptimized': ['-g', '-O'], + 'release': ['-O'], + 'minsize': [], + } + +gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32', + '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] + +msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib', + 'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib', + 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] + +gnu_color_args = {'auto': ['-fdiagnostics-color=auto'], + 'always': ['-fdiagnostics-color=always'], + 'never': ['-fdiagnostics-color=never'], + } + +clang_color_args = {'auto': ['-Xclang', '-fcolor-diagnostics'], + 'always': ['-Xclang', '-fcolor-diagnostics'], + 'never': ['-Xclang', '-fno-color-diagnostics'], + } + +base_options = {'b_pch': coredata.UserBooleanOption('b_pch', 'Use precompiled headers', True), + 'b_lto': coredata.UserBooleanOption('b_lto', 'Use link time optimization', False), + 'b_sanitize': coredata.UserComboOption('b_sanitize', + 'Code sanitizer to use', + ['none', 'address', 'thread', 'undefined', 'memory'], + 'none'), + 'b_lundef': coredata.UserBooleanOption('b_lundef', 'Use -Wl,--no-undefined when linking', True), + 'b_asneeded': coredata.UserBooleanOption('b_asneeded', 'Use -Wl,--as-needed when linking', True), + 'b_pgo': coredata.UserComboOption('b_pgo', 'Use profile guide optimization', + ['off', 'generate', 'use'], + 'off'), + 'b_coverage': coredata.UserBooleanOption('b_coverage', + 'Enable coverage tracking.', + False), + 'b_colorout': coredata.UserComboOption('b_colorout', 'Use colored output', + ['auto', 'always', 'never'], + 'always'), + 'b_ndebug': coredata.UserBooleanOption('b_ndebug', + 'Disable asserts', + False), + 'b_staticpic': coredata.UserBooleanOption('b_staticpic', + 'Build static libraries as position independent', + True), + } + +def sanitizer_compile_args(value): + if value == 'none': + return [] + args = ['-fsanitize=' + value] + if value == 'address': + args.append('-fno-omit-frame-pointer') + return args + +def sanitizer_link_args(value): + if value == 'none': + return [] + args = ['-fsanitize=' + value] + return args + +def get_base_compile_args(options, compiler): + args = [] + # FIXME, gcc/clang specific. + try: + if options['b_lto'].value: + args.append('-flto') + except KeyError: + pass + try: + args += compiler.get_colorout_args(options['b_colorout'].value) + except KeyError: + pass + try: + args += sanitizer_compile_args(options['b_sanitize'].value) + except KeyError: + pass + try: + pgo_val = options['b_pgo'].value + if pgo_val == 'generate': + args.append('-fprofile-generate') + elif pgo_val == 'use': + args.append('-fprofile-use') + except KeyError: + pass + try: + if options['b_coverage'].value: + args += compiler.get_coverage_args() + except KeyError: + pass + try: + if options['b_ndebug'].value: + args += ['-DNDEBUG'] + except KeyError: + pass + return args + +def get_base_link_args(options, linker, is_shared_module): + args = [] + # FIXME, gcc/clang specific. + try: + if options['b_lto'].value: + args.append('-flto') + except KeyError: + pass + try: + args += sanitizer_link_args(options['b_sanitize'].value) + except KeyError: + pass + try: + pgo_val = options['b_pgo'].value + if pgo_val == 'generate': + args.append('-fprofile-generate') + elif pgo_val == 'use': + args.append('-fprofile-use') + except KeyError: + pass + try: + if not is_shared_module and 'b_lundef' in linker.base_options and options['b_lundef'].value: + args.append('-Wl,--no-undefined') + except KeyError: + pass + try: + if 'b_asneeded' in linker.base_options and options['b_asneeded'].value: + args.append('-Wl,--as-needed') + except KeyError: + pass + try: + if options['b_coverage'].value: + args += linker.get_coverage_link_args() + except KeyError: + pass + return args + +class CrossNoRunException(MesonException): + pass + +class RunResult: + def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'): + self.compiled = compiled + self.returncode = returncode + self.stdout = stdout + self.stderr = stderr + +class CompilerArgs(list): + ''' + Class derived from list() that manages a list of compiler arguments. Should + be used while constructing compiler arguments from various sources. Can be + operated with ordinary lists, so this does not need to be used everywhere. + + All arguments must be inserted and stored in GCC-style (-lfoo, -Idir, etc) + and can converted to the native type of each compiler by using the + .to_native() method to which you must pass an instance of the compiler or + the compiler class. + + New arguments added to this class (either with .append(), .extend(), or +=) + are added in a way that ensures that they override previous arguments. + For example: + + >>> a = ['-Lfoo', '-lbar'] + >>> a += ['-Lpho', '-lbaz'] + >>> print(a) + ['-Lpho', '-Lfoo', '-lbar', '-lbaz'] + + Arguments will also be de-duped if they can be de-duped safely. + + Note that because of all this, this class is not commutative and does not + preserve the order of arguments if it is safe to not. For example: + >>> ['-Ifoo', '-Ibar'] + ['-Ifez', '-Ibaz', '-Werror'] + ['-Ifez', '-Ibaz', '-Ifoo', '-Ibar', '-Werror'] + >>> ['-Ifez', '-Ibaz', '-Werror'] + ['-Ifoo', '-Ibar'] + ['-Ifoo', '-Ibar', '-Ifez', '-Ibaz', '-Werror'] + + ''' + # NOTE: currently this class is only for C-like compilers, but it can be + # extended to other languages easily. Just move the following to the + # compiler class and initialize when self.compiler is set. + + # Arg prefixes that override by prepending instead of appending + prepend_prefixes = ('-I', '-L') + # Arg prefixes and args that must be de-duped by returning 2 + dedup2_prefixes = ('-I', '-L', '-D', '-U') + dedup2_suffixes = () + dedup2_args = () + # Arg prefixes and args that must be de-duped by returning 1 + dedup1_prefixes = ('-l',) + dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a') + # Match a .so of the form path/to/libfoo.so.0.1.0 + # Only UNIX shared libraries require this. Others have a fixed extension. + dedup1_regex = re.compile(r'([\/\\]|\A)lib.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') + dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread') + compiler = None + + def _check_args(self, args): + cargs = [] + if len(args) > 2: + raise TypeError("CompilerArgs() only accepts at most 2 arguments: " + "The compiler, and optionally an initial list") + elif not args: + return cargs + elif len(args) == 1: + if isinstance(args[0], (Compiler, StaticLinker)): + self.compiler = args[0] + else: + raise TypeError("you must pass a Compiler instance as one of " + "the arguments") + elif len(args) == 2: + if isinstance(args[0], (Compiler, StaticLinker)): + self.compiler = args[0] + cargs = args[1] + elif isinstance(args[1], (Compiler, StaticLinker)): + cargs = args[0] + self.compiler = args[1] + else: + raise TypeError("you must pass a Compiler instance as one of " + "the two arguments") + else: + raise AssertionError('Not reached') + return cargs + + def __init__(self, *args): + super().__init__(self._check_args(args)) + + @classmethod + def _can_dedup(cls, arg): + ''' + Returns whether the argument can be safely de-duped. This is dependent + on three things: + + a) Whether an argument can be 'overriden' by a later argument. For + example, -DFOO defines FOO and -UFOO undefines FOO. In this case, we + can safely remove the previous occurance and add a new one. The same + is true for include paths and library paths with -I and -L. For + these we return `2`. See `dedup2_prefixes` and `dedup2_args`. + b) Arguments that once specified cannot be undone, such as `-c` or + `-pipe`. New instances of these can be completely skipped. For these + we return `1`. See `dedup1_prefixes` and `dedup1_args`. + c) Whether it matters where or how many times on the command-line + a particular argument is present. This can matter for symbol + resolution in static or shared libraries, so we cannot de-dup or + reorder them. For these we return `0`. This is the default. + + In addition to these, we handle library arguments specially. + With GNU ld, we surround library arguments with -Wl,--start/end-group + to recursively search for symbols in the libraries. This is not needed + with other linkers. + ''' + + # A standalone argument must never be deduplicated because it is + # defined by what comes _after_ it. Thus dedupping this: + # -D FOO -D BAR + # would yield either + # -D FOO BAR + # or + # FOO -D BAR + # both of which are invalid. + if arg in cls.dedup2_prefixes: + return 0 + if arg in cls.dedup2_args or \ + arg.startswith(cls.dedup2_prefixes) or \ + arg.endswith(cls.dedup2_suffixes): + return 2 + if arg in cls.dedup1_args or \ + arg.startswith(cls.dedup1_prefixes) or \ + arg.endswith(cls.dedup1_suffixes) or \ + re.search(cls.dedup1_regex, arg): + return 1 + return 0 + + @classmethod + def _should_prepend(cls, arg): + if arg.startswith(cls.prepend_prefixes): + return True + return False + + def to_native(self): + # Check if we need to add --start/end-group for circular dependencies + # between static libraries. + if get_compiler_uses_gnuld(self.compiler): + group_started = False + for each in self: + if not each.startswith('-l') and not each.endswith('.a'): + continue + i = self.index(each) + if not group_started: + # First occurance of a library + self.insert(i, '-Wl,--start-group') + group_started = True + # Last occurance of a library + if group_started: + self.insert(i + 1, '-Wl,--end-group') + return self.compiler.unix_args_to_native(self) + + def append_direct(self, arg): + ''' + Append the specified argument without any reordering or de-dup + ''' + super().append(arg) + + def extend_direct(self, iterable): + ''' + Extend using the elements in the specified iterable without any + reordering or de-dup + ''' + super().extend(iterable) + + def __add__(self, args): + new = CompilerArgs(self, self.compiler) + new += args + return new + + def __iadd__(self, args): + ''' + Add two CompilerArgs while taking into account overriding of arguments + and while preserving the order of arguments as much as possible + ''' + pre = [] + post = [] + if not isinstance(args, list): + raise TypeError('can only concatenate list (not "{}") to list'.format(args)) + for arg in args: + # If the argument can be de-duped, do it either by removing the + # previous occurance of it and adding a new one, or not adding the + # new occurance. + dedup = self._can_dedup(arg) + if dedup == 1: + # Argument already exists and adding a new instance is useless + if arg in self or arg in pre or arg in post: + continue + if dedup == 2: + # Remove all previous occurances of the arg and add it anew + if arg in self: + self.remove(arg) + if arg in pre: + pre.remove(arg) + if arg in post: + post.remove(arg) + if self._should_prepend(arg): + pre.append(arg) + else: + post.append(arg) + # Insert at the beginning + self[:0] = pre + # Append to the end + super().__iadd__(post) + return self + + def __radd__(self, args): + new = CompilerArgs(args, self.compiler) + new += self + return new + + def __mul__(self, args): + raise TypeError("can't multiply compiler arguments") + + def __imul__(self, args): + raise TypeError("can't multiply compiler arguments") + + def __rmul__(self, args): + raise TypeError("can't multiply compiler arguments") + + def append(self, arg): + self.__iadd__([arg]) + + def extend(self, args): + self.__iadd__(args) + +class Compiler: + def __init__(self, exelist, version): + if isinstance(exelist, str): + self.exelist = [exelist] + elif isinstance(exelist, list): + self.exelist = exelist + else: + raise TypeError('Unknown argument to Compiler') + # In case it's been overriden by a child class already + if not hasattr(self, 'file_suffixes'): + self.file_suffixes = lang_suffixes[self.language] + if not hasattr(self, 'can_compile_suffixes'): + self.can_compile_suffixes = set(self.file_suffixes) + self.default_suffix = self.file_suffixes[0] + self.version = version + self.base_options = [] + + def __repr__(self): + repr_str = "<{0}: v{1} `{2}`>" + return repr_str.format(self.__class__.__name__, self.version, + ' '.join(self.exelist)) + + def can_compile(self, src): + if hasattr(src, 'fname'): + src = src.fname + suffix = os.path.splitext(src)[1].lower() + if suffix and suffix[1:] in self.can_compile_suffixes: + return True + return False + + def get_id(self): + return self.id + + def get_language(self): + return self.language + + def get_display_language(self): + return self.language.capitalize() + + def get_default_suffix(self): + return self.default_suffix + + def get_exelist(self): + return self.exelist[:] + + def get_builtin_define(self, *args, **kwargs): + raise EnvironmentException('%s does not support get_builtin_define.' % self.id) + + def has_builtin_define(self, *args, **kwargs): + raise EnvironmentException('%s does not support has_builtin_define.' % self.id) + + def get_always_args(self): + return [] + + 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 + + def get_option_compile_args(self, options): + return [] + + def get_option_link_args(self, options): + return [] + + def has_header(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language()) + + def has_header_symbol(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language()) + + def compiles(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support compile checks.' % self.get_display_language()) + + def links(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support link checks.' % self.get_display_language()) + + def run(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support run checks.' % self.get_display_language()) + + def sizeof(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language()) + + def alignment(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language()) + + def has_function(self, *args, **kwargs): + raise EnvironmentException('Language %s does not support function checks.' % self.get_display_language()) + + @classmethod + def unix_args_to_native(cls, args): + "Always returns a copy that can be independently mutated" + return args[:] + + def find_library(self, *args, **kwargs): + raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language())) + + def get_library_dirs(self): + return [] + + def has_argument(self, arg, env): + return self.has_multi_arguments([arg], env) + + def has_multi_arguments(self, args, env): + raise EnvironmentException( + 'Language {} does not support has_multi_arguments.'.format( + self.get_display_language())) + + def get_cross_extra_flags(self, environment, link): + extra_flags = [] + if self.is_cross and environment: + if 'properties' in environment.cross_info.config: + props = environment.cross_info.config['properties'] + lang_args_key = self.language + '_args' + extra_flags += props.get(lang_args_key, []) + lang_link_args_key = self.language + '_link_args' + if link: + extra_flags += props.get(lang_link_args_key, []) + return extra_flags + + def _get_compile_output(self, dirname, mode): + # In pre-processor mode, the output is sent to stdout and discarded + if mode == 'preprocess': + return None + # Extension only matters if running results; '.exe' is + # guaranteed to be executable on every platform. + if mode == 'link': + suffix = 'exe' + else: + suffix = 'obj' + return os.path.join(dirname, 'output.' + suffix) + + @contextlib.contextmanager + def compile(self, code, extra_args=None, mode='link'): + if extra_args is None: + extra_args = [] + try: + with tempfile.TemporaryDirectory() as tmpdirname: + if isinstance(code, str): + srcname = os.path.join(tmpdirname, + 'testfile.' + self.default_suffix) + with open(srcname, 'w') as ofile: + ofile.write(code) + elif isinstance(code, mesonlib.File): + srcname = code.fname + output = self._get_compile_output(tmpdirname, mode) + + # Construct the compiler command-line + commands = CompilerArgs(self) + commands.append(srcname) + commands += extra_args + commands += self.get_always_args() + if mode == 'compile': + commands += self.get_compile_only_args() + # Preprocess mode outputs to stdout, so no output args + if mode == 'preprocess': + commands += self.get_preprocess_only_args() + else: + commands += self.get_output_args(output) + # Generate full command-line with the exelist + commands = self.get_exelist() + commands.to_native() + mlog.debug('Running compile:') + mlog.debug('Working directory: ', tmpdirname) + mlog.debug('Command line: ', ' '.join(commands), '\n') + mlog.debug('Code:\n', code) + p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname) + mlog.debug('Compiler stdout:\n', p.stdo) + mlog.debug('Compiler stderr:\n', p.stde) + p.input_name = srcname + p.output_name = output + yield p + except (PermissionError, OSError): + # On Windows antivirus programs and the like hold on to files so + # they can't be deleted. There's not much to do in this case. Also, + # catch OSError because the directory is then no longer empty. + pass + + def get_colorout_args(self, colortype): + return [] + + # Some compilers (msvc) write debug info to a separate file. + # These args specify where it should be written. + def get_compile_debugfile_args(self, rel_obj, **kwargs): + return [] + + def get_link_debugfile_args(self, rel_obj): + return [] + + def get_std_shared_lib_link_args(self): + return [] + + def get_std_shared_module_link_args(self): + return self.get_std_shared_lib_link_args() + + def get_link_whole_for(self, args): + if isinstance(args, list) and not args: + return [] + raise EnvironmentException('Language %s does not support linking whole archives.' % self.get_display_language()) + + def build_unix_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + if not rpath_paths and not install_rpath: + return [] + # The rpaths we write must be relative, because otherwise + # they have different length depending on the build + # directory. This breaks reproducible builds. + rel_rpaths = [] + for p in rpath_paths: + if p == from_dir: + relative = '' # relpath errors out in this case + else: + relative = os.path.relpath(p, from_dir) + rel_rpaths.append(relative) + paths = ':'.join([os.path.join('$ORIGIN', p) for p in rel_rpaths]) + if len(paths) < len(install_rpath): + padding = 'X' * (len(install_rpath) - len(paths)) + if not paths: + paths = padding + else: + paths = paths + ':' + padding + args = ['-Wl,-rpath,' + paths] + if get_compiler_is_linuxlike(self): + # Rpaths to use while linking must be absolute. These are not + # written to the binary. Needed only with GNU ld: + # https://sourceware.org/bugzilla/show_bug.cgi?id=16936 + # Not needed on Windows or other platforms that don't use RPATH + # https://github.com/mesonbuild/meson/issues/1897 + lpaths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) + args += ['-Wl,-rpath-link,' + lpaths] + return args + + +GCC_STANDARD = 0 +GCC_OSX = 1 +GCC_MINGW = 2 +GCC_CYGWIN = 3 + +CLANG_STANDARD = 0 +CLANG_OSX = 1 +CLANG_WIN = 2 +# Possibly clang-cl? + +ICC_STANDARD = 0 +ICC_OSX = 1 +ICC_WIN = 2 + +def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): + if soversion is None: + sostr = '' + else: + sostr = '.' + soversion + if gcc_type in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN): + # Might not be correct for mingw but seems to work. + return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] + elif gcc_type == GCC_OSX: + if is_shared_module: + return [] + return ['-install_name', os.path.join(path, 'lib' + shlib_name + '.dylib')] + else: + raise RuntimeError('Not implemented yet.') + +def get_compiler_is_linuxlike(compiler): + if (getattr(compiler, 'gcc_type', None) == GCC_STANDARD) or \ + (getattr(compiler, 'clang_type', None) == CLANG_STANDARD) or \ + (getattr(compiler, 'icc_type', None) == ICC_STANDARD): + return True + return False + +def get_compiler_uses_gnuld(c): + # FIXME: Perhaps we should detect the linker in the environment? + # FIXME: Assumes that *BSD use GNU ld, but they might start using lld soon + if (getattr(c, 'gcc_type', None) in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN)) or \ + (getattr(c, 'clang_type', None) in (CLANG_STANDARD, CLANG_WIN)) or \ + (getattr(c, 'icc_type', None) in (ICC_STANDARD, ICC_WIN)): + return True + return False + +def get_largefile_args(compiler): + ''' + Enable transparent large-file-support for 32-bit UNIX systems + ''' + if get_compiler_is_linuxlike(compiler): + # Enable large-file support unconditionally on all platforms other + # than macOS and Windows. macOS is now 64-bit-only so it doesn't + # need anything special, and Windows doesn't have automatic LFS. + # You must use the 64-bit counterparts explicitly. + # glibc, musl, and uclibc, and all BSD libcs support this. On Android, + # support for transparent LFS is available depending on the version of + # Bionic: https://github.com/android/platform_bionic#32-bit-abi-bugs + # https://code.google.com/p/android/issues/detail?id=64613 + # + # If this breaks your code, fix it! It's been 20+ years! + return ['-D_FILE_OFFSET_BITS=64'] + # We don't enable -D_LARGEFILE64_SOURCE since that enables + # transitionary features and must be enabled by programs that use + # those features explicitly. + return [] + + +class GnuCompiler: + # Functionality that is common to all GNU family compilers. + def __init__(self, gcc_type, defines): + self.id = 'gcc' + self.gcc_type = gcc_type + self.defines = defines or {} + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', + 'b_colorout', 'b_ndebug', 'b_staticpic'] + if self.gcc_type != GCC_OSX: + self.base_options.append('b_lundef') + self.base_options.append('b_asneeded') + # All GCC backends can do assembly + self.can_compile_suffixes.add('s') + + def get_colorout_args(self, colortype): + if mesonlib.version_compare(self.version, '>=4.9.0'): + return gnu_color_args[colortype][:] + return [] + + def get_warn_args(self, level): + args = super().get_warn_args(level) + if mesonlib.version_compare(self.version, '<4.8.0') and '-Wpedantic' in args: + # -Wpedantic was added in 4.8.0 + # https://gcc.gnu.org/gcc-4.8/changes.html + args[args.index('-Wpedantic')] = '-pedantic' + return args + + def has_builtin_define(self, define): + return define in self.defines + + def get_builtin_define(self, define): + if define in self.defines: + return self.defines[define] + + def get_pic_args(self): + if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX): + return [] # On Window and OS X, pic is always on. + return ['-fPIC'] + + def get_buildtype_args(self, buildtype): + return gnulike_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + if self.gcc_type == GCC_OSX: + return apple_buildtype_linker_args[buildtype] + return gnulike_buildtype_linker_args[buildtype] + + def get_pch_suffix(self): + return 'gch' + + def split_shlib_to_parts(self, fname): + return os.path.split(fname)[0], fname + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def get_std_shared_lib_link_args(self): + if self.gcc_type == GCC_OSX: + return ['-bundle'] + return ['-shared'] + + def get_link_whole_for(self, args): + return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] + + def gen_vs_module_defs_args(self, defsfile): + if not isinstance(defsfile, str): + raise RuntimeError('Module definitions file should be str') + # On Windows targets, .def files may be specified on the linker command + # line like an object file. + if self.gcc_type in (GCC_CYGWIN, GCC_MINGW): + return [defsfile] + # For other targets, discard the .def file. + return [] + + def get_gui_app_args(self): + if self.gcc_type in (GCC_CYGWIN, GCC_MINGW): + return ['-mwindows'] + return [] + +class ClangCompiler: + def __init__(self, clang_type): + self.id = 'clang' + self.clang_type = clang_type + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', + 'b_ndebug', 'b_staticpic', 'b_colorout'] + if self.clang_type != CLANG_OSX: + self.base_options.append('b_lundef') + self.base_options.append('b_asneeded') + # All Clang backends can do assembly and LLVM IR + self.can_compile_suffixes.update(['ll', 's']) + + def get_pic_args(self): + if self.clang_type in (CLANG_WIN, CLANG_OSX): + return [] # On Window and OS X, pic is always on. + return ['-fPIC'] + + def get_colorout_args(self, colortype): + return clang_color_args[colortype][:] + + def get_buildtype_args(self, buildtype): + return gnulike_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + if self.clang_type == CLANG_OSX: + return apple_buildtype_linker_args[buildtype] + return gnulike_buildtype_linker_args[buildtype] + + def get_pch_suffix(self): + return 'pch' + + def get_pch_use_args(self, pch_dir, header): + # Workaround for Clang bug http://llvm.org/bugs/show_bug.cgi?id=15136 + # This flag is internal to Clang (or at least not documented on the man page) + # so it might change semantics at any time. + return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))] + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + if self.clang_type == CLANG_STANDARD: + gcc_type = GCC_STANDARD + elif self.clang_type == CLANG_OSX: + gcc_type = GCC_OSX + elif self.clang_type == CLANG_WIN: + gcc_type = GCC_MINGW + else: + raise MesonException('Unreachable code when converting clang type to gcc type.') + return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def has_multi_arguments(self, args, env): + return super().has_multi_arguments( + ['-Werror=unknown-warning-option'] + args, + env) + + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if extra_args is None: + extra_args = [] + # Starting with XCode 8, we need to pass this to force linker + # visibility to obey OS X and iOS minimum version targets with + # -mmacosx-version-min, -miphoneos-version-min, etc. + # https://github.com/Homebrew/homebrew-core/issues/3727 + if self.clang_type == CLANG_OSX and version_compare(self.version, '>=8.0'): + extra_args.append('-Wl,-no_weak_imports') + return super().has_function(funcname, prefix, env, extra_args, dependencies) + + def get_std_shared_module_link_args(self): + if self.clang_type == CLANG_OSX: + return ['-bundle', '-Wl,-undefined,dynamic_lookup'] + return ['-shared'] + + def get_link_whole_for(self, args): + if self.clang_type == CLANG_OSX: + result = [] + for a in args: + result += ['-Wl,-force_load', a] + return result + return ['-Wl,--whole-archive'] + args + ['-Wl,--no-whole-archive'] + + +# Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1 +class IntelCompiler: + def __init__(self, icc_type): + self.id = 'intel' + self.icc_type = icc_type + self.lang_header = 'none' + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage', + 'b_colorout', 'b_ndebug', 'b_staticpic', 'b_lundef', 'b_asneeded'] + # Assembly + self.can_compile_suffixes.add('s') + + def get_pic_args(self): + return ['-fPIC'] + + def get_buildtype_args(self, buildtype): + return gnulike_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + return gnulike_buildtype_linker_args[buildtype] + + def get_pch_suffix(self): + return 'pchi' + + def get_pch_use_args(self, pch_dir, header): + return ['-pch', '-pch_dir', os.path.join(pch_dir), '-x', + self.lang_header, '-include', header, '-x', 'none'] + + def get_pch_name(self, header_name): + return os.path.split(header_name)[-1] + '.' + self.get_pch_suffix() + + def split_shlib_to_parts(self, fname): + return os.path.split(fname)[0], fname + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + if self.icc_type == ICC_STANDARD: + gcc_type = GCC_STANDARD + elif self.icc_type == ICC_OSX: + gcc_type = GCC_OSX + elif self.icc_type == ICC_WIN: + gcc_type = GCC_MINGW + else: + raise MesonException('Unreachable code when converting icc type to gcc type.') + return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def get_std_shared_lib_link_args(self): + # FIXME: Don't know how icc works on OSX + # if self.icc_type == ICC_OSX: + # return ['-bundle'] + return ['-shared'] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py new file mode 100644 index 0000000..01525b0 --- /dev/null +++ b/mesonbuild/compilers/cpp.py @@ -0,0 +1,205 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .. import coredata +from ..mesonlib import version_compare + +from .c import CCompiler, VisualStudioCCompiler +from .compilers import ( + GCC_MINGW, + gnu_winlibs, + msvc_winlibs, + ClangCompiler, + GnuCompiler, + IntelCompiler, +) + +class CPPCompiler(CCompiler): + def __init__(self, exelist, version, is_cross, exe_wrap): + # If a child ObjCPP class has already set it, don't set it ourselves + if not hasattr(self, 'language'): + self.language = 'cpp' + CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + + def get_display_language(self): + return 'C++' + + def get_no_stdinc_args(self): + return ['-nostdinc++'] + + def sanity_check(self, work_dir, environment): + code = 'class breakCCompiler;int main(int argc, char **argv) { return 0; }\n' + return self.sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code) + + def get_compiler_check_args(self): + # -fpermissive allows non-conforming code to compile which is necessary + # for many C++ checks. Particularly, the has_header_symbol check is + # too strict without this and always fails. + return super().get_compiler_check_args() + ['-fpermissive'] + + def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): + # Check if it's a C-like symbol + if super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies): + return True + # Check if it's a class or a template + if extra_args is None: + extra_args = [] + fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol} + t = '''{prefix} + #include <{header}> + using {symbol}; + int main () {{ return 0; }}''' + return self.compiles(t.format(**fargs), env, extra_args, dependencies) + + +class ClangCPPCompiler(ClangCompiler, CPPCompiler): + def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + ClangCompiler.__init__(self, cltype) + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + return {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none', 'c++03', 'c++11', 'c++14', 'c++1z', + 'gnu++11', 'gnu++14', 'gnu++1z'], + 'none')} + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_std'] + if std.value != 'none': + args.append('-std=' + std.value) + return args + + def get_option_link_args(self, options): + return [] + + +class GnuCPPCompiler(GnuCompiler, CPPCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrap, defines): + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + GnuCompiler.__init__(self, gcc_type, defines) + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none', 'c++03', 'c++11', 'c++14', 'c++1z', + 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'], + 'none'), + 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', + 'STL debug mode', + False)} + if self.gcc_type == GCC_MINGW: + opts.update({ + 'cpp_winlibs': coredata.UserStringArrayOption('cpp_winlibs', 'Standard Win libraries to link against', + gnu_winlibs), }) + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_std'] + if std.value != 'none': + args.append('-std=' + std.value) + if options['cpp_debugstl'].value: + args.append('-D_GLIBCXX_DEBUG=1') + return args + + def get_option_link_args(self, options): + if self.gcc_type == GCC_MINGW: + return options['cpp_winlibs'].value[:] + return [] + + +class IntelCPPCompiler(IntelCompiler, CPPCompiler): + def __init__(self, exelist, version, icc_type, is_cross, exe_wrap): + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + IntelCompiler.__init__(self, icc_type) + self.lang_header = 'c++-header' + default_warn_args = ['-Wall', '-w3', '-diag-disable:remark', + '-Wpch-messages', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + def get_options(self): + c_stds = [] + g_stds = ['gnu++98'] + if version_compare(self.version, '>=15.0.0'): + c_stds += ['c++11', 'c++14'] + g_stds += ['gnu++11'] + if version_compare(self.version, '>=16.0.0'): + c_stds += ['c++17'] + if version_compare(self.version, '>=17.0.0'): + g_stds += ['gnu++14'] + opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none'] + c_stds + g_stds, + 'none'), + 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', + 'STL debug mode', + False)} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_std'] + if std.value != 'none': + args.append('-std=' + std.value) + if options['cpp_debugstl'].value: + args.append('-D_GLIBCXX_DEBUG=1') + return args + + def get_option_link_args(self, options): + return [] + + def has_multi_arguments(self, args, env): + return super().has_multi_arguments(args + ['-diag-error', '10006'], env) + + +class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): + def __init__(self, exelist, version, is_cross, exe_wrap): + self.language = 'cpp' + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + VisualStudioCCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + self.base_options = ['b_pch'] # FIXME add lto, pgo and the like + + def get_options(self): + return {'cpp_eh': coredata.UserComboOption('cpp_eh', + 'C++ exception handling type.', + ['none', 'a', 's', 'sc'], + 'sc'), + 'cpp_winlibs': coredata.UserStringArrayOption('cpp_winlibs', + 'Windows libs to link against.', + msvc_winlibs) + } + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_eh'] + if std.value != 'none': + args.append('/EH' + std.value) + return args + + def get_option_link_args(self, options): + return options['cpp_winlibs'].value[:] + + def get_compiler_check_args(self): + # Visual Studio C++ compiler doesn't support -fpermissive, + # so just use the plain C args. + return super(VisualStudioCCompiler, self).get_compiler_check_args() diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py new file mode 100644 index 0000000..e6c5b9e --- /dev/null +++ b/mesonbuild/compilers/cs.py @@ -0,0 +1,109 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, subprocess + +from ..mesonlib import EnvironmentException + +from .compilers import Compiler, mono_buildtype_args + +class MonoCompiler(Compiler): + def __init__(self, exelist, version): + self.language = 'cs' + super().__init__(exelist, version) + self.id = 'mono' + self.monorunner = 'mono' + + def get_display_language(self): + return 'C sharp' + + def get_output_args(self, fname): + return ['-out:' + fname] + + def get_link_args(self, fname): + return ['-r:' + fname] + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + return [] + + def get_werror_args(self): + return ['-warnaserror'] + + def split_shlib_to_parts(self, fname): + return None, fname + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + return [] + + def get_linker_exelist(self): + return self.exelist[:] + + def get_compile_only_args(self): + return [] + + def get_linker_output_args(self, outputname): + return [] + + def get_coverage_args(self): + return [] + + def get_coverage_link_args(self): + return [] + + def get_std_exe_link_args(self): + return [] + + def get_include_args(self, path): + return [] + + def get_pic_args(self): + return [] + + def name_string(self): + return ' '.join(self.exelist) + + def get_pch_use_args(self, pch_dir, header): + return [] + + def get_pch_name(self, header_name): + return '' + + def sanity_check(self, work_dir, environment): + src = 'sanity.cs' + obj = 'sanity.exe' + source_name = os.path.join(work_dir, src) + with open(source_name, 'w') as ofile: + ofile.write('''public class Sanity { + static public void Main () { + } +} +''') + pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) + cmdlist = [self.monorunner, obj] + pe = subprocess.Popen(cmdlist, cwd=work_dir) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by Mono compiler %s are not runnable.' % self.name_string()) + + def needs_static_linker(self): + return False + + def get_buildtype_args(self, buildtype): + return mono_buildtype_args[buildtype] diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py new file mode 100644 index 0000000..e1d534f --- /dev/null +++ b/mesonbuild/compilers/d.py @@ -0,0 +1,324 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, subprocess + +from ..mesonlib import EnvironmentException, version_compare + +from .compilers import ( + GCC_STANDARD, + d_dmd_buildtype_args, + d_gdc_buildtype_args, + d_ldc_buildtype_args, + get_gcc_soname_args, + gnu_color_args, + Compiler, + CompilerArgs, +) + +class DCompiler(Compiler): + def __init__(self, exelist, version, is_cross): + self.language = 'd' + super().__init__(exelist, version) + self.id = 'unknown' + self.is_cross = is_cross + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanity.d') + output_name = os.path.join(work_dir, 'dtest') + with open(source_name, 'w') as ofile: + ofile.write('''void main() { +} +''') + pc = subprocess.Popen(self.exelist + self.get_output_args(output_name) + [source_name], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('D compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by D compiler %s are not runnable.' % self.name_string()) + + def needs_static_linker(self): + return True + + def name_string(self): + return ' '.join(self.exelist) + + def get_linker_exelist(self): + return self.exelist[:] + + def get_preprocess_only_args(self): + return ['-E'] + + def get_compile_only_args(self): + return ['-c'] + + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'dep' + + def get_pic_args(self): + return ['-fPIC'] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + # FIXME: Make this work for Windows, MacOS and cross-compiling + return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def get_unittest_args(self): + return ['-unittest'] + + def get_buildtype_linker_args(self, buildtype): + return [] + + def get_std_exe_link_args(self): + return [] + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + # This method is to be used by LDC and DMD. + # GDC can deal with the verbatim flags. + if not rpath_paths and not install_rpath: + return [] + paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) + if len(paths) < len(install_rpath): + padding = 'X' * (len(install_rpath) - len(paths)) + if not paths: + paths = padding + else: + paths = paths + ':' + padding + return ['-L-rpath={}'.format(paths)] + + def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'): + if extra_args is None: + extra_args = [] + elif isinstance(extra_args, str): + extra_args = [extra_args] + if dependencies is None: + dependencies = [] + elif not isinstance(dependencies, list): + dependencies = [dependencies] + # Collect compiler arguments + args = CompilerArgs(self) + for d in dependencies: + # Add compile flags needed by dependencies + args += d.get_compile_args() + if mode == 'link': + # Add link flags needed to find dependencies + args += d.get_link_args() + + if mode == 'compile': + # Add DFLAGS from the env + args += env.coredata.external_args[self.language] + elif mode == 'link': + # Add LDFLAGS from the env + args += env.coredata.external_link_args[self.language] + # extra_args must override all other arguments, so we add them last + args += extra_args + return args + + def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): + args = self._get_compiler_check_args(env, extra_args, dependencies, mode) + + with self.compile(code, args, mode) as p: + return p.returncode == 0 + + def has_multi_arguments(self, args, env): + return self.compiles('int i;\n', env, extra_args=args) + + @classmethod + def translate_args_to_nongnu(cls, args): + dcargs = [] + # Translate common arguments to flags the LDC/DMD compilers + # can understand. + # The flags might have been added by pkg-config files, + # and are therefore out of the user's control. + for arg in args: + if arg == '-pthread': + continue + if arg.startswith('-Wl,'): + linkargs = arg[arg.index(',') + 1:].split(',') + for la in linkargs: + dcargs.append('-L' + la.strip()) + continue + elif arg.startswith('-l'): + # translate library link flag + dcargs.append('-L' + arg) + continue + elif arg.startswith('-L/') or arg.startswith('-L./'): + # we need to handle cases where -L is set by e.g. a pkg-config + # setting to select a linker search path. We can however not + # unconditionally prefix '-L' with '-L' because the user might + # have set this flag too to do what it is intended to for this + # compiler (pass flag through to the linker) + # Hence, we guess here whether the flag was intended to pass + # a linker search path. + dcargs.append('-L' + arg) + continue + dcargs.append(arg) + + return dcargs + + +class GnuDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'gcc' + default_warn_args = ['-Wall', '-Wdeprecated'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + self.base_options = ['b_colorout', 'b_sanitize', 'b_staticpic'] + + def get_colorout_args(self, colortype): + if version_compare(self.version, '>=4.9.0'): + return gnu_color_args[colortype][:] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + return ['-fmake-deps=' + outfile] + + def get_output_args(self, target): + return ['-o', target] + + def get_linker_output_args(self, target): + return ['-o', target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + return self.warn_args[level] + + def get_werror_args(self): + return ['-Werror'] + + def get_linker_search_args(self, dirname): + return ['-L' + dirname] + + def get_buildtype_args(self, buildtype): + return d_gdc_buildtype_args[buildtype] + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) + + def get_unittest_args(self): + return ['-funittest'] + + +class LLVMDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'llvm' + self.base_options = ['b_coverage', 'b_colorout'] + + def get_colorout_args(self, colortype): + if colortype == 'always': + return ['-enable-color'] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + # LDC using the -deps flag returns a non-Makefile dependency-info file, which + # the backends can not use. So we disable this feature for now. + return [] + + def get_output_args(self, target): + return ['-of', target] + + def get_linker_output_args(self, target): + return ['-of', target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + if level == '2' or level == '3': + return ['-wi', '-dw'] + else: + return ['-wi'] + + def get_werror_args(self): + return ['-w'] + + def get_coverage_args(self): + return ['-cov'] + + def get_buildtype_args(self, buildtype): + return d_ldc_buildtype_args[buildtype] + + def get_pic_args(self): + return ['-relocation-model=pic'] + + def get_linker_search_args(self, dirname): + # -L is recognized as "add this to the search path" by the linker, + # while the compiler recognizes it as "pass to linker". So, the first + # -L is for the compiler, telling it to pass the second -L to the linker. + return ['-L-L' + dirname] + + @classmethod + def unix_args_to_native(cls, args): + return cls.translate_args_to_nongnu(args) + + +class DmdDCompiler(DCompiler): + def __init__(self, exelist, version, is_cross): + DCompiler.__init__(self, exelist, version, is_cross) + self.id = 'dmd' + self.base_options = ['b_coverage', 'b_colorout'] + + def get_colorout_args(self, colortype): + if colortype == 'always': + return ['-color=on'] + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + # LDC using the -deps flag returns a non-Makefile dependency-info file, which + # the backends can not use. So we disable this feature for now. + return [] + + def get_output_args(self, target): + return ['-of' + target] + + def get_werror_args(self): + return ['-w'] + + def get_linker_output_args(self, target): + return ['-of' + target] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_warn_args(self, level): + return ['-wi'] + + def get_coverage_args(self): + return ['-cov'] + + def get_linker_search_args(self, dirname): + # -L is recognized as "add this to the search path" by the linker, + # while the compiler recognizes it as "pass to linker". So, the first + # -L is for the compiler, telling it to pass the second -L to the linker. + return ['-L-L' + dirname] + + def get_buildtype_args(self, buildtype): + return d_dmd_buildtype_args[buildtype] + + def get_std_shared_lib_link_args(self): + return ['-shared', '-defaultlib=libphobos2.so'] + + @classmethod + def unix_args_to_native(cls, args): + return cls.translate_args_to_nongnu(args) diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py new file mode 100644 index 0000000..d44b529 --- /dev/null +++ b/mesonbuild/compilers/fortran.py @@ -0,0 +1,289 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, subprocess + +from ..mesonlib import EnvironmentException, is_osx + +from .compilers import ( + GCC_CYGWIN, + GCC_MINGW, + GCC_OSX, + GCC_STANDARD, + ICC_STANDARD, + apple_buildtype_linker_args, + get_gcc_soname_args, + gnulike_buildtype_args, + gnulike_buildtype_linker_args, + Compiler, + IntelCompiler, +) + +class FortranCompiler(Compiler): + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + self.language = 'fortran' + super().__init__(exelist, version) + self.is_cross = is_cross + self.exe_wrapper = exe_wrapper + # Not really correct but I don't have Fortran compilers to test with. Sorry. + self.gcc_type = GCC_STANDARD + self.id = "IMPLEMENTATION CLASSES MUST SET THIS" + + def name_string(self): + return ' '.join(self.exelist) + + def get_pic_args(self): + if self.gcc_type in (GCC_CYGWIN, GCC_MINGW, GCC_OSX): + return [] # On Window and OS X, pic is always on. + return ['-fPIC'] + + def get_std_shared_lib_link_args(self): + return ['-shared'] + + def needs_static_linker(self): + return True + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanitycheckf.f90') + binary_name = os.path.join(work_dir, 'sanitycheckf') + with open(source_name, 'w') as ofile: + ofile.write('''program prog + print *, "Fortran compilation is working." +end program prog +''') + extra_flags = self.get_cross_extra_flags(environment, link=True) + pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Compiler %s can not compile programs.' % self.name_string()) + if self.is_cross: + if self.exe_wrapper is None: + # Can't check if the binaries run so we have to assume they do + return + cmdlist = self.exe_wrapper + [binary_name] + else: + cmdlist = [binary_name] + pe = subprocess.Popen(cmdlist, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by Fortran compiler %s are not runnable.' % self.name_string()) + + def get_std_warn_args(self, level): + return FortranCompiler.std_warn_args + + def get_buildtype_args(self, buildtype): + return gnulike_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + if is_osx(): + return apple_buildtype_linker_args[buildtype] + return gnulike_buildtype_linker_args[buildtype] + + def split_shlib_to_parts(self, fname): + return os.path.split(fname)[0], fname + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + + def get_dependency_gen_args(self, outtarget, outfile): + # Disabled until this is fixed: + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62162 + # return ['-cpp', '-MMD', '-MQ', outtarget] + return [] + + def get_output_args(self, target): + return ['-o', target] + + def get_preprocess_only_args(self): + return ['-E'] + + def get_compile_only_args(self): + return ['-c'] + + def get_linker_exelist(self): + return self.exelist[:] + + def get_linker_output_args(self, outputname): + return ['-o', outputname] + + def get_include_args(self, path, is_system): + return ['-I' + path] + + def get_module_outdir_args(self, path): + return ['-J' + path] + + def depfile_for_object(self, objfile): + return objfile + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'd' + + def get_std_exe_link_args(self): + return [] + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) + + def module_name_to_filename(self, module_name): + return module_name.lower() + '.mod' + + def get_warn_args(self, level): + return ['-Wall'] + + def get_no_warn_args(self): + return ['-w'] + + +class GnuFortranCompiler(FortranCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): + super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.gcc_type = gcc_type + self.defines = defines or {} + self.id = 'gcc' + + def has_builtin_define(self, define): + return define in self.defines + + def get_builtin_define(self, define): + if define in self.defines: + return self.defines[define] + + 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) + self.id = 'g95' + + def get_module_outdir_args(self, path): + return ['-fmod=' + path] + + def get_always_args(self): + return ['-pipe'] + + def get_no_warn_args(self): + # FIXME: Confirm that there's no compiler option to disable all warnings + return [] + + 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) + self.id = 'sun' + + def get_dependency_gen_args(self, outtarget, outfile): + return ['-fpp'] + + def get_always_args(self): + return [] + + def get_warn_args(self, level): + return [] + + def get_module_outdir_args(self, path): + return ['-moddir=' + path] + + +class IntelFortranCompiler(IntelCompiler, FortranCompiler): + std_warn_args = ['-warn', 'all'] + + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + self.file_suffixes = ('f90', 'f', 'for', 'ftn', 'fpp') + FortranCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + # FIXME: Add support for OS X and Windows in detect_fortran_compiler so + # we are sent the type of compiler + IntelCompiler.__init__(self, ICC_STANDARD) + self.id = 'intel' + + def get_module_outdir_args(self, path): + return ['-module', path] + + def get_warn_args(self, level): + return IntelFortranCompiler.std_warn_args + + +class PathScaleFortranCompiler(FortranCompiler): + std_warn_args = ['-fullwarn'] + + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.id = 'pathscale' + + def get_module_outdir_args(self, path): + return ['-module', path] + + def get_std_warn_args(self, level): + return PathScaleFortranCompiler.std_warn_args + +class PGIFortranCompiler(FortranCompiler): + std_warn_args = ['-Minform=inform'] + + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.id = 'pgi' + + def get_module_outdir_args(self, path): + return ['-module', path] + + def get_warn_args(self, level): + return PGIFortranCompiler.std_warn_args + + def get_no_warn_args(self): + return ['-silent'] + + +class Open64FortranCompiler(FortranCompiler): + std_warn_args = ['-fullwarn'] + + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.id = 'open64' + + def get_module_outdir_args(self, path): + return ['-module', path] + + def get_warn_args(self, level): + return Open64FortranCompiler.std_warn_args + + +class NAGFortranCompiler(FortranCompiler): + std_warn_args = [] + + def __init__(self, exelist, version, is_cross, exe_wrapper=None): + super().__init__(exelist, version, is_cross, exe_wrapper=None) + self.id = 'nagfor' + + def get_module_outdir_args(self, path): + return ['-mdir', path] + + def get_warn_args(self, level): + return NAGFortranCompiler.std_warn_args diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py new file mode 100644 index 0000000..0253bfe --- /dev/null +++ b/mesonbuild/compilers/java.py @@ -0,0 +1,115 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, shutil, subprocess + +from ..mesonlib import EnvironmentException + +from .compilers import Compiler, java_buildtype_args + +class JavaCompiler(Compiler): + def __init__(self, exelist, version): + self.language = 'java' + super().__init__(exelist, version) + self.id = 'unknown' + self.javarunner = 'java' + + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + return [] + + def get_werror_args(self): + return ['-Werror'] + + def split_shlib_to_parts(self, fname): + return None, fname + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return [] + + def get_dependency_gen_args(self, outtarget, outfile): + return [] + + def get_linker_exelist(self): + return self.exelist[:] + + def get_compile_only_args(self): + return [] + + def get_output_args(self, subdir): + if subdir == '': + subdir = './' + return ['-d', subdir, '-s', subdir] + + def get_linker_output_args(self, outputname): + return [] + + def get_coverage_args(self): + return [] + + def get_coverage_link_args(self): + return [] + + def get_std_exe_link_args(self): + return [] + + def get_include_args(self, path): + return [] + + def get_pic_args(self): + return [] + + def name_string(self): + return ' '.join(self.exelist) + + def get_pch_use_args(self, pch_dir, header): + return [] + + def get_pch_name(self, header_name): + return '' + + def get_buildtype_args(self, buildtype): + return java_buildtype_args[buildtype] + + def sanity_check(self, work_dir, environment): + src = 'SanityCheck.java' + obj = 'SanityCheck' + source_name = os.path.join(work_dir, src) + with open(source_name, 'w') as ofile: + ofile.write('''class SanityCheck { + public static void main(String[] args) { + int i; + } +} +''') + pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Java compiler %s can not compile programs.' % self.name_string()) + runner = shutil.which(self.javarunner) + if runner: + cmdlist = [runner, obj] + pe = subprocess.Popen(cmdlist, cwd=work_dir) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by Java compiler %s are not runnable.' % self.name_string()) + else: + m = "Java Virtual Machine wasn't found, but it's needed by Meson. " \ + "Please install a JRE.\nIf you have specific needs where this " \ + "requirement doesn't make sense, please open a bug at " \ + "https://github.com/mesonbuild/meson/issues/new and tell us " \ + "all about it." + raise EnvironmentException(m) + + def needs_static_linker(self): + return False diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py new file mode 100644 index 0000000..388e83b --- /dev/null +++ b/mesonbuild/compilers/objc.py @@ -0,0 +1,67 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, subprocess + +from ..mesonlib import EnvironmentException + +from .c import CCompiler +from .compilers import ClangCompiler, GnuCompiler + +class ObjCCompiler(CCompiler): + def __init__(self, exelist, version, is_cross, exe_wrap): + self.language = 'objc' + CCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + + def get_display_language(self): + return 'Objective-C' + + def sanity_check(self, work_dir, environment): + # TODO try to use sanity_check_impl instead of duplicated code + source_name = os.path.join(work_dir, 'sanitycheckobjc.m') + binary_name = os.path.join(work_dir, 'sanitycheckobjc') + extra_flags = self.get_cross_extra_flags(environment, link=False) + if self.is_cross: + extra_flags += self.get_compile_only_args() + with open(source_name, 'w') as ofile: + ofile.write('#import<stdio.h>\n' + 'int main(int argc, char **argv) { return 0; }\n') + pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('ObjC compiler %s can not compile programs.' % self.name_string()) + if self.is_cross: + # Can't check if the binaries run so we have to assume they do + return + pe = subprocess.Popen(binary_name) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by ObjC compiler %s are not runnable.' % self.name_string()) + + +class GnuObjCCompiler(GnuCompiler, ObjCCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): + ObjCCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + GnuCompiler.__init__(self, gcc_type, defines) + default_warn_args = ['-Wall', '-Winvalid-pch'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + +class ClangObjCCompiler(ClangCompiler, GnuObjCCompiler): + def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): + GnuObjCCompiler.__init__(self, exelist, version, cltype, is_cross, exe_wrapper) + ClangCompiler.__init__(self, cltype) + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py new file mode 100644 index 0000000..c2e4647 --- /dev/null +++ b/mesonbuild/compilers/objcpp.py @@ -0,0 +1,68 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path, subprocess + +from ..mesonlib import EnvironmentException + +from .cpp import CPPCompiler +from .compilers import ClangCompiler, GnuCompiler + +class ObjCPPCompiler(CPPCompiler): + def __init__(self, exelist, version, is_cross, exe_wrap): + self.language = 'objcpp' + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap) + + def get_display_language(self): + return 'Objective-C++' + + def sanity_check(self, work_dir, environment): + # TODO try to use sanity_check_impl instead of duplicated code + source_name = os.path.join(work_dir, 'sanitycheckobjcpp.mm') + binary_name = os.path.join(work_dir, 'sanitycheckobjcpp') + extra_flags = self.get_cross_extra_flags(environment, link=False) + if self.is_cross: + extra_flags += self.get_compile_only_args() + with open(source_name, 'w') as ofile: + ofile.write('#import<stdio.h>\n' + 'class MyClass;' + 'int main(int argc, char **argv) { return 0; }\n') + pc = subprocess.Popen(self.exelist + extra_flags + [source_name, '-o', binary_name]) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('ObjC++ compiler %s can not compile programs.' % self.name_string()) + if self.is_cross: + # Can't check if the binaries run so we have to assume they do + return + pe = subprocess.Popen(binary_name) + pe.wait() + if pe.returncode != 0: + raise EnvironmentException('Executables created by ObjC++ compiler %s are not runnable.' % self.name_string()) + + +class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None): + ObjCPPCompiler.__init__(self, exelist, version, is_cross, exe_wrapper) + GnuCompiler.__init__(self, gcc_type, defines) + default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor'] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + ['-Wextra'], + '3': default_warn_args + ['-Wextra', '-Wpedantic']} + + +class ClangObjCPPCompiler(ClangCompiler, GnuObjCPPCompiler): + def __init__(self, exelist, version, cltype, is_cross, exe_wrapper=None): + GnuObjCPPCompiler.__init__(self, exelist, version, cltype, is_cross, exe_wrapper) + ClangCompiler.__init__(self, cltype) + self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage'] diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py new file mode 100644 index 0000000..38ba6a2 --- /dev/null +++ b/mesonbuild/compilers/rust.py @@ -0,0 +1,59 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess, os.path + +from ..mesonlib import EnvironmentException, Popen_safe + +from .compilers import Compiler, rust_buildtype_args + +class RustCompiler(Compiler): + def __init__(self, exelist, version): + self.language = 'rust' + super().__init__(exelist, version) + self.id = 'rustc' + + def needs_static_linker(self): + return False + + def name_string(self): + return ' '.join(self.exelist) + + def sanity_check(self, work_dir, environment): + source_name = os.path.join(work_dir, 'sanity.rs') + output_name = os.path.join(work_dir, 'rusttest') + with open(source_name, 'w') as ofile: + ofile.write('''fn main() { +} +''') + pc = subprocess.Popen(self.exelist + ['-o', output_name, source_name], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Rust compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by Rust compiler %s are not runnable.' % self.name_string()) + + def get_dependency_gen_args(self, outfile): + return ['--dep-info', outfile] + + def get_buildtype_args(self, buildtype): + return rust_buildtype_args[buildtype] + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, install_rpath) + + def get_sysroot(self): + cmd = self.exelist + ['--print', 'sysroot'] + p, stdo, stde = Popen_safe(cmd) + return stdo.split('\n')[0] diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py new file mode 100644 index 0000000..59997d6 --- /dev/null +++ b/mesonbuild/compilers/swift.py @@ -0,0 +1,99 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import subprocess, os.path + +from ..mesonlib import EnvironmentException + +from .compilers import Compiler, swift_buildtype_args + +class SwiftCompiler(Compiler): + def __init__(self, exelist, version): + self.language = 'swift' + super().__init__(exelist, version) + self.version = version + self.id = 'llvm' + self.is_cross = False + + def get_linker_exelist(self): + return self.exelist[:] + + def name_string(self): + return ' '.join(self.exelist) + + def needs_static_linker(self): + return True + + def get_werror_args(self): + return ['--fatal-warnings'] + + def get_dependency_gen_args(self, outtarget, outfile): + return ['-emit-dependencies'] + + def depfile_for_object(self, objfile): + return os.path.splitext(objfile)[0] + '.' + self.get_depfile_suffix() + + def get_depfile_suffix(self): + return 'd' + + def get_output_args(self, target): + return ['-o', target] + + def get_linker_output_args(self, target): + return ['-o', target] + + def get_header_import_args(self, headername): + return ['-import-objc-header', headername] + + def get_warn_args(self, level): + return [] + + def get_buildtype_args(self, buildtype): + return swift_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + return [] + + def get_std_exe_link_args(self): + return ['-emit-executable'] + + def get_module_args(self, modname): + return ['-module-name', modname] + + def get_mod_gen_args(self): + return ['-emit-module'] + + def build_rpath_args(self, *args): + return [] # FIXME + + def get_include_args(self, dirname): + return ['-I' + dirname] + + def get_compile_only_args(self): + return ['-c'] + + def sanity_check(self, work_dir, environment): + src = 'swifttest.swift' + source_name = os.path.join(work_dir, src) + output_name = os.path.join(work_dir, 'swifttest') + with open(source_name, 'w') as ofile: + ofile.write('''print("Swift compilation is working.") +''') + extra_flags = self.get_cross_extra_flags(environment, link=True) + pc = subprocess.Popen(self.exelist + extra_flags + ['-emit-executable', '-o', output_name, src], cwd=work_dir) + pc.wait() + if pc.returncode != 0: + raise EnvironmentException('Swift compiler %s can not compile programs.' % self.name_string()) + if subprocess.call(output_name) != 0: + raise EnvironmentException('Executables created by Swift compiler %s are not runnable.' % self.name_string()) diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py new file mode 100644 index 0000000..21c040c --- /dev/null +++ b/mesonbuild/compilers/vala.py @@ -0,0 +1,90 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os.path + +from .. import mlog +from ..mesonlib import EnvironmentException + +from .compilers import Compiler + +class ValaCompiler(Compiler): + def __init__(self, exelist, version): + self.language = 'vala' + super().__init__(exelist, version) + self.version = version + self.id = 'valac' + self.is_cross = False + + def name_string(self): + return ' '.join(self.exelist) + + def needs_static_linker(self): + return False # Because compiles into C. + + def get_output_args(self, target): + return ['-o', target] + + def get_compile_only_args(self): + return ['-C'] + + def get_pic_args(self): + return [] + + def get_always_args(self): + return ['-C'] + + def get_warn_args(self, warning_level): + return [] + + def get_no_warn_args(self): + return ['--disable-warnings'] + + def get_werror_args(self): + return ['--fatal-warnings'] + + def sanity_check(self, work_dir, environment): + code = 'class MesonSanityCheck : Object { }' + args = self.get_cross_extra_flags(environment, link=False) + with self.compile(code, args, 'compile') as p: + if p.returncode != 0: + msg = 'Vala compiler {!r} can not compile programs' \ + ''.format(self.name_string()) + raise EnvironmentException(msg) + + def get_buildtype_args(self, buildtype): + if buildtype == 'debug' or buildtype == 'debugoptimized' or buildtype == 'minsize': + return ['--debug'] + return [] + + def find_library(self, libname, env, extra_dirs): + if extra_dirs and isinstance(extra_dirs, str): + extra_dirs = [extra_dirs] + # Valac always looks in the default vapi dir, so only search there if + # no extra dirs are specified. + if not extra_dirs: + code = 'class MesonFindLibrary : Object { }' + vapi_args = ['--pkg', libname] + args = self.get_cross_extra_flags(env, link=False) + args += vapi_args + with self.compile(code, args, 'compile') as p: + if p.returncode == 0: + return vapi_args + # Not found? Try to find the vapi file itself. + for d in extra_dirs: + vapi = os.path.join(d, libname + '.vapi') + if os.path.isfile(vapi): + return [vapi] + mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname)) + return None diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index d21c6cc..1e4e04b 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -12,15 +12,57 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os -import platform -import re +import configparser, os, platform, re, shlex, shutil, subprocess -from .compilers import * +from . import coredata +from .linkers import ArLinker, VisualStudioLinker +from . import mesonlib from .mesonlib import EnvironmentException, Popen_safe -import configparser -import shlex -import shutil +from . import mlog + +from . import compilers +from .compilers import ( + CLANG_OSX, + CLANG_STANDARD, + CLANG_WIN, + GCC_CYGWIN, + GCC_MINGW, + GCC_OSX, + GCC_STANDARD, + ICC_STANDARD, + is_assembly, + is_header, + is_library, + is_llvm_ir, + is_object, + is_source, +) +from .compilers import ( + ClangCCompiler, + ClangCPPCompiler, + ClangObjCCompiler, + ClangObjCPPCompiler, + G95FortranCompiler, + GnuCCompiler, + GnuCPPCompiler, + GnuFortranCompiler, + GnuObjCCompiler, + GnuObjCPPCompiler, + IntelCCompiler, + IntelCPPCompiler, + IntelFortranCompiler, + JavaCompiler, + MonoCompiler, + NAGFortranCompiler, + Open64FortranCompiler, + PathScaleFortranCompiler, + PGIFortranCompiler, + RustCompiler, + SunFortranCompiler, + ValaCompiler, + VisualStudioCCompiler, + VisualStudioCPPCompiler, +) build_filename = 'meson.build' @@ -472,7 +514,7 @@ class Environment: if 'Free Software Foundation' in out: defines = self.get_gnu_compiler_defines(compiler) if not defines: - popen_exceptions[compiler] = 'no pre-processor defines' + popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) @@ -524,7 +566,7 @@ class Environment: if 'GNU Fortran' in out: defines = self.get_gnu_compiler_defines(compiler) if not defines: - popen_exceptions[compiler] = 'no pre-processor defines' + popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) @@ -575,7 +617,7 @@ class Environment: if 'Free Software Foundation' in out: defines = self.get_gnu_compiler_defines(compiler) if not defines: - popen_exceptions[compiler] = 'no pre-processor defines' + popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) @@ -601,7 +643,7 @@ class Environment: if 'Free Software Foundation' in out: defines = self.get_gnu_compiler_defines(compiler) if not defines: - popen_exceptions[compiler] = 'no pre-processor defines' + popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) version = self.get_gnu_version_from_defines(defines) @@ -684,11 +726,11 @@ class Environment: raise EnvironmentException('Could not execute D compiler "%s"' % ' '.join(exelist)) version = search_version(out) if 'LLVM D compiler' in out: - return LLVMDCompiler(exelist, version, is_cross) + return compilers.LLVMDCompiler(exelist, version, is_cross) elif 'gdc' in out: - return GnuDCompiler(exelist, version, is_cross) + return compilers.GnuDCompiler(exelist, version, is_cross) elif 'Digital Mars' in out: - return DmdDCompiler(exelist, version, is_cross) + return compilers.DmdDCompiler(exelist, version, is_cross) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_swift_compiler(self): @@ -699,7 +741,7 @@ class Environment: raise EnvironmentException('Could not execute Swift compiler "%s"' % ' '.join(exelist)) version = search_version(err) if 'Swift' in err: - return SwiftCompiler(exelist, version) + return compilers.SwiftCompiler(exelist, version) raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_static_linker(self, compiler): @@ -712,12 +754,12 @@ class Environment: evar = 'AR' if evar in os.environ: linkers = [shlex.split(os.environ[evar])] - elif isinstance(compiler, VisualStudioCCompiler): + elif isinstance(compiler, compilers.VisualStudioCCompiler): linkers = [self.vs_static_linker] - elif isinstance(compiler, GnuCompiler): + elif isinstance(compiler, compilers.GnuCompiler): # Use gcc-ar if available; needed for LTO linkers = [self.gcc_static_linker, self.default_static_linker] - elif isinstance(compiler, ClangCompiler): + elif isinstance(compiler, compilers.ClangCompiler): # Use llvm-ar if available; needed for LTO linkers = [self.clang_static_linker, self.default_static_linker] else: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 7f279c1..916529f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -1,5 +1,4 @@ # Copyright 2012-2017 The Meson development team - # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at @@ -26,7 +25,7 @@ from .mesonlib import FileMode, Popen_safe, get_meson_script from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase -from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs +from .interpreterbase import check_stringlist, noPosargs, noKwargs, stringArgs, permittedKwargs from .interpreterbase import InterpreterException, InvalidArguments, InvalidCode from .interpreterbase import InterpreterObject, MutableInterpreterObject from .modules import ModuleReturnValue @@ -741,7 +740,7 @@ class CompilerHolder(InterpreterObject): def unittest_args_method(self, args, kwargs): # At time, only D compilers have this feature. if not hasattr(self.compiler, 'get_unittest_args'): - raise InterpreterException('This {} compiler has no unittest arguments.'.format(self.compiler.language)) + raise InterpreterException('This {} compiler has no unittest arguments.'.format(self.compiler.get_display_language())) return self.compiler.get_unittest_args() def has_member_method(self, args, kwargs): @@ -971,8 +970,7 @@ class CompilerHolder(InterpreterObject): raise InvalidCode('Search directory %s is not an absolute path.' % i) linkargs = self.compiler.find_library(libname, self.environment, search_dirs) if required and not linkargs: - l = self.compiler.language.capitalize() - raise InterpreterException('{} library {!r} not found'.format(l, libname)) + raise InterpreterException('{} library {!r} not found'.format(self.compiler.get_display_language(), libname)) lib = dependencies.ExternalLibrary(libname, linkargs, self.environment, self.compiler.language) return ExternalLibraryHolder(lib) @@ -986,7 +984,7 @@ class CompilerHolder(InterpreterObject): h = mlog.green('YES') else: h = mlog.red('NO') - mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.language, args[0]), h) + mlog.log('Compiler for {} supports argument {}:'.format(self.compiler.get_display_language(), args[0]), h) return result def has_multi_arguments_method(self, args, kwargs): @@ -998,7 +996,7 @@ class CompilerHolder(InterpreterObject): h = mlog.red('NO') mlog.log( 'Compiler for {} supports arguments {}:'.format( - self.compiler.language, ' '.join(args)), + self.compiler.get_display_language(), ' '.join(args)), h) return result @@ -1214,6 +1212,95 @@ class MesonMain(InterpreterObject): return args[1] raise InterpreterException('Unknown cross property: %s.' % propname) +pch_kwargs = set(['c_pch', 'cpp_pch']) + +lang_arg_kwargs = set(['c_args', + 'cpp_args', + 'd_args', + 'fortran_args', + 'java_args', + 'objc_args', + 'objcpp_args', + 'rust_args', + 'vala_args', + ]) + +vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi']) +rust_kwargs = set(['rust_crate_type']) +cs_kwargs = set(['resources']) + +buildtarget_kwargs = set(['build_by_default', + 'dependencies', + 'extra_files', + 'gui_app', + 'link_with', + 'link_whole', + 'link_args', + 'link_depends', + 'include_directories', + 'install', + 'install_rpath', + 'install_dir', + 'name_prefix', + 'name_suffix', + 'native', + 'objects', + 'override_options', + 'pic', + 'sources', + 'vs_module_defs', + ]) + +build_target_common_kwargs = ( + buildtarget_kwargs | + lang_arg_kwargs | + pch_kwargs | + vala_kwargs | + rust_kwargs | + cs_kwargs) + +exe_kwargs = set() +exe_kwargs.update(build_target_common_kwargs) + +shlib_kwargs = (build_target_common_kwargs) | {'version', 'soversion'} +shmod_kwargs = shlib_kwargs +stlib_kwargs = shlib_kwargs + +jar_kwargs = exe_kwargs.copy() +jar_kwargs.update(['main_class']) + +build_target_kwargs = exe_kwargs.copy() +build_target_kwargs.update(['target_type']) + +permitted_kwargs = {'add_global_arguments': {'language'}, + 'add_languages': {'required'}, + 'add_project_arguments': {'language'}, + 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, + 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, + 'build_target': build_target_kwargs, + 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'}, + 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, + 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'}, + 'executable': exe_kwargs, + 'find_program': {'required'}, + 'generator': {'arguments', 'output', 'depfile'}, + 'include_directories': {'is_system'}, + 'install_data': {'install_dir', 'install_mode', 'sources'}, + 'install_headers': {'install_dir', 'subdir'}, + 'install_man': {'install_dir'}, + 'install_subdir': {'install_dir', 'install_mode'}, + 'jar': jar_kwargs, + 'project': {'version', 'meson_version', 'default_options', 'license', 'subproject_dir'}, + 'run_target': {'command', 'depends'}, + 'shared_library': shlib_kwargs, + 'shared_module': shmod_kwargs, + 'static_library': stlib_kwargs, + 'subproject': {'version', 'default_options'}, + 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, + 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'}, + } + + class Interpreter(InterpreterBase): def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects', @@ -1255,53 +1342,53 @@ class Interpreter(InterpreterBase): self.build_def_files = [os.path.join(self.subdir, environment.build_filename)] def build_func_dict(self): - self.funcs.update({'project': self.func_project, - 'message': self.func_message, - 'error': self.func_error, - 'executable': self.func_executable, - 'dependency': self.func_dependency, - 'static_library': self.func_static_lib, - 'shared_library': self.func_shared_lib, - 'shared_module': self.func_shared_module, - 'library': self.func_library, - 'jar': self.func_jar, - 'build_target': self.func_build_target, - 'custom_target': self.func_custom_target, - 'run_target': self.func_run_target, - 'generator': self.func_generator, - 'test': self.func_test, - 'benchmark': self.func_benchmark, - 'install_headers': self.func_install_headers, - 'install_man': self.func_install_man, - 'subdir': self.func_subdir, - 'install_data': self.func_install_data, - 'install_subdir': self.func_install_subdir, - 'configure_file': self.func_configure_file, - 'include_directories': self.func_include_directories, - 'add_global_arguments': self.func_add_global_arguments, + self.funcs.update({'add_global_arguments': self.func_add_global_arguments, 'add_project_arguments': self.func_add_project_arguments, 'add_global_link_arguments': self.func_add_global_link_arguments, 'add_project_link_arguments': self.func_add_project_link_arguments, 'add_test_setup': self.func_add_test_setup, 'add_languages': self.func_add_languages, - 'find_program': self.func_find_program, - 'find_library': self.func_find_library, + 'assert': self.func_assert, + 'benchmark': self.func_benchmark, + 'build_target': self.func_build_target, 'configuration_data': self.func_configuration_data, - 'run_command': self.func_run_command, + 'configure_file': self.func_configure_file, + 'custom_target': self.func_custom_target, + 'declare_dependency': self.func_declare_dependency, + 'dependency': self.func_dependency, + 'environment': self.func_environment, + 'error': self.func_error, + 'executable': self.func_executable, + 'generator': self.func_generator, 'gettext': self.func_gettext, - 'option': self.func_option, 'get_option': self.func_get_option, - 'subproject': self.func_subproject, - 'vcs_tag': self.func_vcs_tag, - 'set_variable': self.func_set_variable, - 'is_variable': self.func_is_variable, 'get_variable': self.func_get_variable, - 'import': self.func_import, 'files': self.func_files, - 'declare_dependency': self.func_declare_dependency, - 'assert': self.func_assert, - 'environment': self.func_environment, + 'find_library': self.func_find_library, + 'find_program': self.func_find_program, + 'include_directories': self.func_include_directories, + 'import': self.func_import, + 'install_data': self.func_install_data, + 'install_headers': self.func_install_headers, + 'install_man': self.func_install_man, + 'install_subdir': self.func_install_subdir, + 'is_variable': self.func_is_variable, + 'jar': self.func_jar, 'join_paths': self.func_join_paths, + 'library': self.func_library, + 'message': self.func_message, + 'option': self.func_option, + 'project': self.func_project, + 'run_target': self.func_run_target, + 'run_command': self.func_run_command, + 'set_variable': self.func_set_variable, + 'subdir': self.func_subdir, + 'subproject': self.func_subproject, + 'shared_library': self.func_shared_lib, + 'shared_module': self.func_shared_module, + 'static_library': self.func_static_lib, + 'test': self.func_test, + 'vcs_tag': self.func_vcs_tag, }) def holderify(self, item): @@ -1402,6 +1489,7 @@ class Interpreter(InterpreterBase): def func_files(self, node, args, kwargs): return [mesonlib.File.from_source_file(self.environment.source_dir, self.subdir, fname) for fname in args] + @permittedKwargs(permitted_kwargs['declare_dependency']) @noPosargs def func_declare_dependency(self, node, args, kwargs): version = kwargs.get('version', self.project_version) @@ -1458,7 +1546,11 @@ class Interpreter(InterpreterBase): if not isinstance(actual, wanted): raise InvalidArguments('Incorrect argument type.') + @noKwargs def func_run_command(self, node, args, kwargs): + return self.run_command_impl(node, args, kwargs) + + def run_command_impl(self, node, args, kwargs, in_builddir=False): if len(args) < 1: raise InterpreterException('Not enough arguments') cmd = args[0] @@ -1491,9 +1583,6 @@ class Interpreter(InterpreterBase): expanded_args.append(a.held_object.get_path()) else: raise InterpreterException('Arguments ' + m.format(a)) - in_builddir = kwargs.get('in_builddir', False) - if not isinstance(in_builddir, bool): - raise InterpreterException('in_builddir must be boolean.') return RunProcess(cmd, expanded_args, srcdir, builddir, self.subdir, get_meson_script(self.environment, 'mesonintrospect'), in_builddir) @@ -1504,6 +1593,7 @@ class Interpreter(InterpreterBase): def func_option(self, nodes, args, kwargs): raise InterpreterException('Tried to call option() in build description file. All options must be in the option file.') + @permittedKwargs(permitted_kwargs['subproject']) @stringArgs def func_subproject(self, nodes, args, kwargs): if len(args) != 1: @@ -1632,6 +1722,7 @@ class Interpreter(InterpreterBase): self.environment.cmd_line_options.projectoptions = newoptions @stringArgs + @permittedKwargs(permitted_kwargs['project']) def func_project(self, node, args, kwargs): if len(args) < 1: raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.') @@ -1678,6 +1769,7 @@ class Interpreter(InterpreterBase): if not self.is_subproject(): self.check_cross_stdlibs() + @permittedKwargs(permitted_kwargs['add_languages']) @stringArgs def func_add_languages(self, node, args, kwargs): return self.add_languages(args, kwargs.get('required', True)) @@ -1794,7 +1886,7 @@ class Interpreter(InterpreterBase): continue else: raise - mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='') + mlog.log('Native %s compiler: ' % comp.get_display_language(), mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='') if not comp.get_language() in self.coredata.external_args: (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp) self.coredata.external_preprocess_args[comp.get_language()] = preproc_args @@ -1802,7 +1894,7 @@ class Interpreter(InterpreterBase): self.coredata.external_link_args[comp.get_language()] = link_args self.build.add_compiler(comp) if need_cross_compiler: - mlog.log('Cross %s compiler: ' % lang, mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='') + mlog.log('Cross %s compiler: ' % cross_comp.get_display_language(), mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='') self.build.add_cross_compiler(cross_comp) if self.environment.is_cross_build() and not need_cross_compiler: self.build.add_cross_compiler(comp) @@ -1821,6 +1913,7 @@ class Interpreter(InterpreterBase): break self.coredata.base_options[optname] = oobj + @permittedKwargs(permitted_kwargs['find_program']) def func_find_program(self, node, args, kwargs): if not args: raise InterpreterException('No program name specified.') @@ -2001,15 +2094,19 @@ class Interpreter(InterpreterBase): mlog.bold(name)) return dep + @permittedKwargs(permitted_kwargs['executable']) def func_executable(self, node, args, kwargs): return self.build_target(node, args, kwargs, ExecutableHolder) + @permittedKwargs(permitted_kwargs['static_library']) def func_static_lib(self, node, args, kwargs): return self.build_target(node, args, kwargs, StaticLibraryHolder) + @permittedKwargs(permitted_kwargs['shared_library']) def func_shared_lib(self, node, args, kwargs): return self.build_target(node, args, kwargs, SharedLibraryHolder) + @permittedKwargs(permitted_kwargs['shared_module']) def func_shared_module(self, node, args, kwargs): return self.build_target(node, args, kwargs, SharedModuleHolder) @@ -2018,9 +2115,12 @@ class Interpreter(InterpreterBase): return self.func_shared_lib(node, args, kwargs) return self.func_static_lib(node, args, kwargs) + @permittedKwargs(permitted_kwargs['jar']) def func_jar(self, node, args, kwargs): + kwargs['target_type'] = 'jar' return self.build_target(node, args, kwargs, JarHolder) + @permittedKwargs(permitted_kwargs['build_target']) def func_build_target(self, node, args, kwargs): if 'target_type' not in kwargs: raise InterpreterException('Missing target_type keyword argument') @@ -2038,6 +2138,7 @@ class Interpreter(InterpreterBase): else: raise InterpreterException('Unknown target_type.') + @permittedKwargs(permitted_kwargs['vcs_tag']) def func_vcs_tag(self, node, args, kwargs): if 'input' not in kwargs or 'output' not in kwargs: raise InterpreterException('Keyword arguments input and output must exist') @@ -2076,6 +2177,7 @@ class Interpreter(InterpreterBase): return self.func_custom_target(node, [kwargs['output']], kwargs) @stringArgs + @permittedKwargs(permitted_kwargs['custom_target']) def func_custom_target(self, node, args, kwargs): if len(args) != 1: raise InterpreterException('custom_target: Only one positional argument is allowed, and it must be a string name') @@ -2084,6 +2186,7 @@ class Interpreter(InterpreterBase): self.add_target(name, tg.held_object) return tg + @permittedKwargs(permitted_kwargs['run_target']) def func_run_target(self, node, args, kwargs): global run_depr_printed if len(args) > 1: @@ -2134,14 +2237,17 @@ class Interpreter(InterpreterBase): self.add_target(name, tg.held_object) return tg + @permittedKwargs(permitted_kwargs['generator']) def func_generator(self, node, args, kwargs): gen = GeneratorHolder(self, args, kwargs) self.generators.append(gen) return gen + @permittedKwargs(permitted_kwargs['benchmark']) def func_benchmark(self, node, args, kwargs): self.add_test(node, args, kwargs, False) + @permittedKwargs(permitted_kwargs['test']) def func_test(self, node, args, kwargs): self.add_test(node, args, kwargs, True) @@ -2211,12 +2317,14 @@ class Interpreter(InterpreterBase): self.build.benchmarks.append(t) mlog.debug('Adding benchmark "', mlog.bold(args[0]), '".', sep='') + @permittedKwargs(permitted_kwargs['install_headers']) def func_install_headers(self, node, args, kwargs): source_files = self.source_strings_to_files(args) h = Headers(source_files, kwargs) self.build.headers.append(h) return h + @permittedKwargs(permitted_kwargs['install_man']) @stringArgs def func_install_man(self, node, args, kwargs): m = Man(self.subdir, args, kwargs) @@ -2280,6 +2388,7 @@ class Interpreter(InterpreterBase): 'permissions arg to be a string or false') return FileMode(*install_mode) + @permittedKwargs(permitted_kwargs['install_data']) def func_install_data(self, node, args, kwargs): kwsource = mesonlib.stringlistify(kwargs.get('sources', [])) raw_sources = args + kwsource @@ -2299,6 +2408,7 @@ class Interpreter(InterpreterBase): self.build.data.append(data.held_object) return data + @permittedKwargs(permitted_kwargs['install_subdir']) @stringArgs def func_install_subdir(self, node, args, kwargs): if len(args) != 1: @@ -2313,6 +2423,7 @@ class Interpreter(InterpreterBase): self.build.install_dirs.append(idir) return idir + @permittedKwargs(permitted_kwargs['configure_file']) def func_configure_file(self, node, args, kwargs): if len(args) > 0: raise InterpreterException("configure_file takes only keyword arguments.") @@ -2388,7 +2499,7 @@ class Interpreter(InterpreterBase): # Substitute @INPUT@, @OUTPUT@, etc here. cmd = mesonlib.substitute_values(kwargs['command'], values) mlog.log('Configuring', mlog.bold(output), 'with command') - res = self.func_run_command(node, cmd, {'in_builddir': True}) + res = self.run_command_impl(node, cmd, {}, True) if res.returncode != 0: raise InterpreterException('Running configure command failed.\n%s\n%s' % (res.stdout, res.stderr)) @@ -2407,6 +2518,7 @@ class Interpreter(InterpreterBase): self.build.data.append(build.Data([cfile], idir)) return mesonlib.File.from_built_file(self.subdir, output) + @permittedKwargs(permitted_kwargs['include_directories']) @stringArgs def func_include_directories(self, node, args, kwargs): src_root = self.environment.get_source_dir() @@ -2443,6 +2555,7 @@ different subdirectory. i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system)) return i + @permittedKwargs(permitted_kwargs['add_test_setup']) @stringArgs def func_add_test_setup(self, node, args, kwargs): if len(args) != 1: @@ -2484,18 +2597,22 @@ different subdirectory. # and just use the master project ones. self.build.test_setups[setup_name] = setupobj + @permittedKwargs(permitted_kwargs['add_global_arguments']) @stringArgs def func_add_global_arguments(self, node, args, kwargs): self.add_global_arguments(node, self.build.global_args, args, kwargs) + @noKwargs @stringArgs def func_add_global_link_arguments(self, node, args, kwargs): self.add_global_arguments(node, self.build.global_link_args, args, kwargs) + @permittedKwargs(permitted_kwargs['add_project_arguments']) @stringArgs def func_add_project_arguments(self, node, args, kwargs): self.add_project_arguments(node, self.build.projects_args, args, kwargs) + @noKwargs @stringArgs def func_add_project_link_arguments(self, node, args, kwargs): self.add_project_arguments(node, self.build.projects_link_args, args, kwargs) @@ -2532,6 +2649,8 @@ different subdirectory. lang = lang.lower() argsdict[lang] = argsdict.get(lang, []) + args + @noKwargs + @noPosargs def func_environment(self, node, args, kwargs): return EnvironmentVariablesHolder() @@ -2685,3 +2804,34 @@ different subdirectory. def is_subproject(self): return self.subproject != '' + + @noKwargs + def func_set_variable(self, node, args, kwargs): + if len(args) != 2: + raise InvalidCode('Set_variable takes two arguments.') + varname = args[0] + value = args[1] + self.set_variable(varname, value) + + @noKwargs + def func_get_variable(self, node, args, kwargs): + if len(args) < 1 or len(args) > 2: + raise InvalidCode('Get_variable takes one or two arguments.') + varname = args[0] + if not isinstance(varname, str): + raise InterpreterException('First argument must be a string.') + try: + return self.variables[varname] + except KeyError: + pass + if len(args) == 2: + return args[1] + raise InterpreterException('Tried to get unknown variable "%s".' % varname) + + @stringArgs + @noKwargs + def func_is_variable(self, node, args, kwargs): + if len(args) != 1: + raise InvalidCode('Is_variable takes two arguments.') + varname = args[0] + return varname in self.variables diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index fb87ea2..d44f71b 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -55,6 +55,19 @@ def stringArgs(f): return f(self, node, args, kwargs) return wrapped +class permittedKwargs: + + def __init__(self, permitted): + self.permitted = permitted + + def __call__(self, f): + def wrapped(s, node, args, kwargs): + for k in kwargs: + if k not in self.permitted: + mlog.warning('Passed invalid keyword argument %s. This will become a hard error in the future.' % k) + return f(s, node, args, kwargs) + return wrapped + class InterpreterException(mesonlib.MesonException): pass @@ -578,52 +591,5 @@ To specify a keyword argument, use : instead of =.''') return isinstance(value, (InterpreterObject, dependencies.Dependency, str, int, list, mesonlib.File)) - def func_build_target(self, node, args, kwargs): - if 'target_type' not in kwargs: - raise InterpreterException('Missing target_type keyword argument') - target_type = kwargs.pop('target_type') - if target_type == 'executable': - return self.func_executable(node, args, kwargs) - elif target_type == 'shared_library': - return self.func_shared_lib(node, args, kwargs) - elif target_type == 'static_library': - return self.func_static_lib(node, args, kwargs) - elif target_type == 'library': - return self.func_library(node, args, kwargs) - elif target_type == 'jar': - return self.func_jar(node, args, kwargs) - else: - raise InterpreterException('Unknown target_type.') - - def func_set_variable(self, node, args, kwargs): - if len(args) != 2: - raise InvalidCode('Set_variable takes two arguments.') - varname = args[0] - value = args[1] - self.set_variable(varname, value) - -# @noKwargs - def func_get_variable(self, node, args, kwargs): - if len(args) < 1 or len(args) > 2: - raise InvalidCode('Get_variable takes one or two arguments.') - varname = args[0] - if not isinstance(varname, str): - raise InterpreterException('First argument must be a string.') - try: - return self.variables[varname] - except KeyError: - pass - if len(args) == 2: - return args[1] - raise InterpreterException('Tried to get unknown variable "%s".' % varname) - - @stringArgs - @noKwargs - def func_is_variable(self, node, args, kwargs): - if len(args) != 1: - raise InvalidCode('Is_variable takes two arguments.') - varname = args[0] - return varname in self.variables - def is_elementary_type(self, v): return isinstance(v, (int, float, str, bool, list)) diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py new file mode 100644 index 0000000..d0d5184 --- /dev/null +++ b/mesonbuild/linkers.py @@ -0,0 +1,114 @@ +# Copyright 2012-2017 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from .mesonlib import Popen_safe + +class StaticLinker: + pass + + +class VisualStudioLinker(StaticLinker): + always_args = ['/NOLOGO'] + + def __init__(self, exelist): + self.exelist = exelist + + def get_exelist(self): + return self.exelist[:] + + def get_std_link_args(self): + return [] + + def get_buildtype_linker_args(self, buildtype): + return [] + + def get_output_args(self, target): + return ['/OUT:' + target] + + def get_coverage_link_args(self): + return [] + + def get_always_args(self): + return VisualStudioLinker.always_args + + def get_linker_always_args(self): + return VisualStudioLinker.always_args + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return [] + + def thread_link_flags(self): + return [] + + def get_option_link_args(self, options): + return [] + + @classmethod + def unix_args_to_native(cls, args): + from .compilers import VisualStudioCCompiler + return VisualStudioCCompiler.unix_args_to_native(args) + + def get_link_debugfile_args(self, targetfile): + # Static libraries do not have PDB files + return [] + + +class ArLinker(StaticLinker): + + def __init__(self, exelist): + self.exelist = exelist + self.id = 'ar' + pc, stdo = Popen_safe(self.exelist + ['-h'])[0:2] + # Enable deterministic builds if they are available. + if '[D]' in stdo: + self.std_args = ['csrD'] + else: + self.std_args = ['csr'] + + def build_rpath_args(self, build_dir, from_dir, rpath_paths, install_rpath): + return [] + + def get_exelist(self): + return self.exelist[:] + + def get_std_link_args(self): + return self.std_args + + def get_output_args(self, target): + return [target] + + def get_buildtype_linker_args(self, buildtype): + return [] + + def get_linker_always_args(self): + return [] + + def get_coverage_link_args(self): + return [] + + def get_always_args(self): + return [] + + def thread_link_flags(self): + return [] + + def get_option_link_args(self, options): + return [] + + @classmethod + def unix_args_to_native(cls, args): + return args[:] + + def get_link_debugfile_args(self, targetfile): + return [] diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index fde3b91..9d75525 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -2,10 +2,26 @@ import os from .. import build from .. import dependencies +from .. import mlog from ..mesonlib import MesonException +from ..interpreterbase import permittedKwargs, noKwargs + +class permittedSnippetKwargs: + + def __init__(self, permitted): + self.permitted = permitted + + def __call__(self, f): + def wrapped(s, interpreter, state, args, kwargs): + for k in kwargs: + if k not in self.permitted: + mlog.warning('Passed invalid keyword argument %s. This will become a hard error in the future.' % k) + return f(s, interpreter, state, args, kwargs) + return wrapped _found_programs = {} + class ExtensionModule: def __init__(self): self.snippets = set() # List of methods that operate only on the interpreter. diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 6c22976..de66ef9 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -30,6 +30,7 @@ from .. import interpreter from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget from . import find_program, get_include_args from . import ExtensionModule +from . import noKwargs, permittedKwargs # gresource compilation is broken due to the way @@ -90,6 +91,8 @@ class GnomeModule(ExtensionModule): mlog.bold('https://github.com/mesonbuild/meson/issues/1387')) gdbuswarning_printed = True + @permittedKwargs({'source_dir', 'c_name', 'dependencies', 'export', 'gresource_bundle', 'install_header', + 'install', 'install_dir', 'extra_args'}) def compile_resources(self, state, args, kwargs): self.__print_gresources_warning(state) glib_version = self._get_native_glib_version(state) @@ -294,7 +297,7 @@ class GnomeModule(ExtensionModule): else: link_command = ['-l' + lib.name] if isinstance(lib, build.SharedLibrary): - libdir = state.backend.get_target_dir(lib) + libdir = os.path.join(state.environment.get_build_dir(), state.backend.get_target_dir(lib)) link_command.append('-L' + libdir) # Needed for the following binutils bug: # https://github.com/mesonbuild/meson/issues/1911 @@ -303,6 +306,8 @@ class GnomeModule(ExtensionModule): for d in state.backend.determine_rpath_dirs(lib): d = os.path.join(state.environment.get_build_dir(), d) link_command.append('-L' + d) + if include_rpath: + link_command.append('-Wl,-rpath,' + d) if include_rpath: link_command.append('-Wl,-rpath,' + libdir) if depends: @@ -375,6 +380,10 @@ class GnomeModule(ExtensionModule): return cflags, ldflags, gi_includes + @permittedKwargs({'sources', 'nsversion', 'namespace', 'symbol_prefix', 'identifier_prefix', + 'export_packagse', 'includes', 'dependencies', 'link_with', 'include_directories', + 'install', 'install_dir_gir', 'install_dir_typelib', 'extra_args', + 'packages'}) def generate_gir(self, state, args, kwargs): if len(args) != 1: raise MesonException('Gir takes one argument') @@ -444,6 +453,7 @@ class GnomeModule(ExtensionModule): 'Gir includes must be str, GirTarget, or list of them') cflags = [] + ldflags = [] for lang, compiler in girtarget.compilers.items(): # XXX: Can you use g-i with any other language? if lang in ('c', 'cpp', 'objc', 'objcpp', 'd'): @@ -456,9 +466,14 @@ class GnomeModule(ExtensionModule): cflags += state.global_args[lang] if state.project_args.get(lang): cflags += state.project_args[lang] - sanitize = compiler.get_options().get('b_sanitize') - if sanitize: + if 'b_sanitize' in compiler.base_options: + sanitize = state.environment.coredata.base_options['b_sanitize'].value cflags += compilers.sanitizer_compile_args(sanitize) + if sanitize == 'address': + ldflags += ['-lasan'] + # FIXME: Linking directly to libasan is not recommended but g-ir-scanner + # does not understand -f LDFLAGS. https://bugzilla.gnome.org/show_bug.cgi?id=783892 + # ldflags += compilers.sanitizer_link_args(sanitize) if kwargs.get('symbol_prefix'): sym_prefix = kwargs.pop('symbol_prefix') if not isinstance(sym_prefix, str): @@ -521,9 +536,10 @@ class GnomeModule(ExtensionModule): # ldflags will be misinterpreted by gir scanner (showing # spurious dependencies) but building GStreamer fails if they # are not used here. - dep_cflags, ldflags, gi_includes = self._get_dependencies_flags(deps, state, depends, - use_gir_args=True) + dep_cflags, dep_ldflags, gi_includes = self._get_dependencies_flags(deps, state, depends, + use_gir_args=True) cflags += list(dep_cflags) + ldflags += list(dep_ldflags) scan_command += ['--cflags-begin'] scan_command += cflags scan_command += ['--cflags-end'] @@ -586,6 +602,7 @@ class GnomeModule(ExtensionModule): rv = [scan_target, typelib_target] return ModuleReturnValue(rv, rv) + @noKwargs def compile_schemas(self, state, args, kwargs): if args: raise MesonException('Compile_schemas does not take positional arguments.') @@ -604,6 +621,7 @@ class GnomeModule(ExtensionModule): target_g = build.CustomTarget(targetname, state.subdir, kwargs) return ModuleReturnValue(target_g, [target_g]) + @permittedKwargs({'sources', 'media', 'symlink_media', 'languages'}) def yelp(self, state, args, kwargs): if len(args) < 1: raise MesonException('Yelp requires a project id') @@ -661,6 +679,10 @@ class GnomeModule(ExtensionModule): rv = [inscript, pottarget, potarget] return ModuleReturnValue(None, rv) + @permittedKwargs({'main_xml', 'main_sgml', 'src_dir', 'dependencies', 'install', + 'install_dir', 'scan_args', 'scanobjs_args', 'gobject_typesfile', + 'fixxref_args', 'html_args', 'html_assets', 'content_files', + 'mkdb_args'}) def gtkdoc(self, state, args, kwargs): if len(args) != 1: raise MesonException('Gtkdoc must have one positional argument.') @@ -700,6 +722,8 @@ class GnomeModule(ExtensionModule): for inc_dir in src_dir.get_incdirs(): header_dirs.append(os.path.join(state.environment.get_source_dir(), src_dir.get_curdir(), inc_dir)) + header_dirs.append(os.path.join(state.environment.get_build_dir(), + src_dir.get_curdir(), inc_dir)) else: header_dirs.append(src_dir) @@ -752,6 +776,7 @@ class GnomeModule(ExtensionModule): return args + @noKwargs def gtkdoc_html_dir(self, state, args, kwargs): if len(args) != 1: raise MesonException('Must have exactly one argument.') @@ -781,6 +806,7 @@ class GnomeModule(ExtensionModule): return [] + @permittedKwargs({'interface_prefix', 'namespace', 'object_manager'}) def gdbus_codegen(self, state, args, kwargs): if len(args) != 2: raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') @@ -809,6 +835,9 @@ class GnomeModule(ExtensionModule): ct = build.CustomTarget(target_name, state.subdir, custom_kwargs) return ModuleReturnValue(ct, [ct]) + @permittedKwargs({'sources', 'c_template', 'h_template', 'install_header', 'install_dir', + 'comments', 'identifier_prefix', 'symbol_prefix', 'eprod', 'vprod', + 'fhead', 'fprod', 'ftail', 'vhead', 'vtail', 'depends'}) def mkenums(self, state, args, kwargs): if len(args) != 1: raise MesonException('Mkenums requires one positional argument.') @@ -921,6 +950,8 @@ class GnomeModule(ExtensionModule): # https://github.com/mesonbuild/meson/issues/973 absolute_paths=True) + @permittedKwargs({'sources', 'prefix', 'install_header', 'install_dir', 'stdinc', + 'nostdinc', 'internal', 'skip_source', 'valist_marshallers'}) def genmarshal(self, state, args, kwargs): if len(args) != 1: raise MesonException( @@ -1059,6 +1090,8 @@ class GnomeModule(ExtensionModule): link_with += self._get_vapi_link_with(dep) return link_with + @permittedKwargs({'sources', 'packages', 'metadata_dirs', 'gir_dirs', + 'vapi_dirs', 'install', 'install_dir'}) def generate_vapi(self, state, args, kwargs): if len(args) != 1: raise MesonException('The library name is required') diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index c4e29cf..d35c7f1 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -20,6 +20,7 @@ from .. import coredata, mesonlib, build from ..mesonlib import MesonException from . import ModuleReturnValue from . import ExtensionModule +from . import permittedKwargs PRESET_ARGS = { 'glib': [ @@ -55,6 +56,8 @@ class I18nModule(ExtensionModule): src_dir = path.join(state.environment.get_source_dir(), state.subdir) return [path.join(src_dir, d) for d in dirs] + @permittedKwargs({'languages', 'data_dirs', 'preset', 'args', 'po_dir', 'type', + 'input', 'output', 'install', 'install_dir'}) def merge_file(self, state, args, kwargs): podir = kwargs.pop('po_dir', None) if not podir: @@ -78,6 +81,7 @@ class I18nModule(ExtensionModule): ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs) return ModuleReturnValue(ct, [ct]) + @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages'}) def gettext(self, state, args, kwargs): if len(args) != 1: raise coredata.MesonException('Gettext requires one positional argument (package name).') diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py index 3e11b70..dd2f215 100644 --- a/mesonbuild/modules/modtest.py +++ b/mesonbuild/modules/modtest.py @@ -14,9 +14,11 @@ from . import ModuleReturnValue from . import ExtensionModule +from . import noKwargs class TestModule(ExtensionModule): + @noKwargs def print_hello(self, state, args, kwargs): print('Hello from a Meson module') rv = ModuleReturnValue(None, []) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 09c615a..7b0bb83 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -19,6 +19,7 @@ from .. import mesonlib from .. import mlog from . import ModuleReturnValue from . import ExtensionModule +from . import permittedKwargs class PkgConfigModule(ExtensionModule): @@ -114,6 +115,9 @@ class PkgConfigModule(ExtensionModule): processed_libs.append(l) return processed_libs + @permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase', + 'subdirs', 'requires', 'requires_private', 'libraries_private', + 'install_dir', 'variables'}) def generate(self, state, args, kwargs): if len(args) > 0: raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.') diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 9f01043..6431047 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -18,6 +18,11 @@ from .. import mesonlib, dependencies from . import ExtensionModule from mesonbuild.modules import ModuleReturnValue +from . import noKwargs, permittedSnippetKwargs +from ..interpreter import shlib_kwargs + +mod_kwargs = set() +mod_kwargs.update(shlib_kwargs) class Python3Module(ExtensionModule): @@ -25,6 +30,7 @@ class Python3Module(ExtensionModule): super().__init__() self.snippets.add('extension_module') + @permittedSnippetKwargs(mod_kwargs) def extension_module(self, interpreter, state, args, kwargs): if 'name_prefix' in kwargs: raise mesonlib.MesonException('Name_prefix is set automatically, specifying it is forbidden.') @@ -43,20 +49,19 @@ class Python3Module(ExtensionModule): kwargs['name_suffix'] = suffix return interpreter.func_shared_module(None, args, kwargs) + @noKwargs def find_python(self, state, args, kwargs): py3 = dependencies.ExternalProgram('python3', sys.executable, silent=True) return ModuleReturnValue(py3, [py3]) + @noKwargs def language_version(self, state, args, kwargs): - if args or kwargs: - raise mesonlib.MesonException('language_version() takes no arguments.') return ModuleReturnValue(sysconfig.get_python_version(), []) + @noKwargs def sysconfig_path(self, state, args, kwargs): if len(args) != 1: raise mesonlib.MesonException('sysconfig_path() requires passing the name of path to get.') - if kwargs: - raise mesonlib.MesonException('sysconfig_path() does not accept keywords.') path_name = args[0] valid_names = sysconfig.get_path_names() if path_name not in valid_names: diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 0386291..4056b6d 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -20,6 +20,7 @@ from ..dependencies import Qt4Dependency from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue +from . import permittedKwargs class Qt4Module(ExtensionModule): tools_detected = False @@ -96,6 +97,7 @@ class Qt4Module(ExtensionModule): except Exception: return [] + @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'}) def preprocess(self, state, args, kwargs): rcc_files = kwargs.pop('qresources', []) if not isinstance(rcc_files, list): diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 6497694..6194a23 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -20,6 +20,7 @@ from ..dependencies import Qt5Dependency from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue +from . import permittedKwargs class Qt5Module(ExtensionModule): tools_detected = False @@ -102,6 +103,7 @@ class Qt5Module(ExtensionModule): except Exception: return [] + @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'}) def preprocess(self, state, args, kwargs): rcc_files = kwargs.pop('qresources', []) if not isinstance(rcc_files, list): diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index 17396ae..b0a8db9 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -22,11 +22,13 @@ from .. import mlog from . import GirTarget, TypelibTarget from . import ModuleReturnValue from . import ExtensionModule +from . import noKwargs import os class RPMModule(ExtensionModule): + @noKwargs def generate_spec_template(self, state, args, kwargs): compiler_deps = set() for compiler in state.compilers.values(): diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 3fb0107..6fef5bb 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -20,6 +20,7 @@ from ..mesonlib import MesonException from . import get_include_args from . import ModuleReturnValue from . import ExtensionModule +from . import permittedKwargs class WindowsModule(ExtensionModule): @@ -29,6 +30,7 @@ class WindowsModule(ExtensionModule): return compilers[l] raise MesonException('Resource compilation requires a C or C++ compiler.') + @permittedKwargs({'args', 'include_directories'}) def compile_resources(self, state, args, kwargs): comp = self.detect_compiler(state.compilers) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 75985c3..8400a1a 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -475,12 +475,18 @@ class Parser: def e2(self): left = self.e3() while self.accept('or'): + if isinstance(left, EmptyNode): + raise ParseException('Invalid or clause.', + self.getline(), left.lineno, left.colno) left = OrNode(left, self.e3()) return left def e3(self): left = self.e4() while self.accept('and'): + if isinstance(left, EmptyNode): + raise ParseException('Invalid and clause.', + self.getline(), left.lineno, left.colno) left = AndNode(left, self.e4()) return left @@ -633,6 +639,7 @@ class Parser: def ifblock(self): condition = self.statement() clause = IfClauseNode(condition.lineno, condition.colno) + self.expect('eol') block = self.codeblock() clause.ifs.append(IfNode(clause.lineno, clause.colno, condition, block)) self.elseifblock(clause) diff --git a/run_project_tests.py b/run_project_tests.py index 3c89d75..5994c5a 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -25,7 +25,6 @@ import mesontest from mesonbuild import environment from mesonbuild import mesonlib from mesonbuild import mlog -from mesonbuild import mesonmain from mesonbuild.mesonlib import stringlistify, Popen_safe from mesonbuild.coredata import backendlist import argparse @@ -250,6 +249,23 @@ def log_text_file(logfile, testdir, stdo, stde): executor.shutdown() raise StopException() + +def bold(text): + return mlog.bold(text).get_text(mlog.colorize_console) + + +def green(text): + return mlog.green(text).get_text(mlog.colorize_console) + + +def red(text): + return mlog.red(text).get_text(mlog.colorize_console) + + +def yellow(text): + return mlog.yellow(text).get_text(mlog.colorize_console) + + def run_test_inprocess(testdir): old_stdout = sys.stdout sys.stdout = mystdout = StringIO() @@ -475,10 +491,12 @@ def run_tests(all_tests, log_name_base, extra_args): for name, test_cases, skipped in all_tests: current_suite = ET.SubElement(junit_root, 'testsuite', {'name': name, 'tests': str(len(test_cases))}) + print() if skipped: - print('\nNot running %s tests.\n' % name) + print(bold('Not running %s tests.' % name)) else: - print('\nRunning %s tests.\n' % name) + print(bold('Running %s tests.' % name)) + print() futures = [] for t in test_cases: # Jenkins screws us over by automatically sorting test cases by name @@ -494,7 +512,7 @@ def run_tests(all_tests, log_name_base, extra_args): sys.stdout.flush() result = result.result() if result is None or 'MESON_SKIP_TEST' in result.stdo: - print('Skipping:', t) + print(yellow('Skipping:'), t) current_test = ET.SubElement(current_suite, 'testcase', {'name': testname, 'classname': name}) ET.SubElement(current_test, 'skipped', {}) @@ -502,7 +520,7 @@ def run_tests(all_tests, log_name_base, extra_args): else: without_install = "" if len(install_commands) > 0 else " (without install)" if result.msg != '': - print('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t)) + print(red('Failed test{} during {}: {!r}'.format(without_install, result.step.name, t))) print('Reason:', result.msg) failing_tests += 1 if result.step == BuildStep.configure and result.mlog != no_meson_log_msg: @@ -648,9 +666,9 @@ if __name__ == '__main__': pass for f in pbfiles: os.unlink(f) - print('\nTotal passed tests:', passing_tests) - print('Total failed tests:', failing_tests) - print('Total skipped tests:', skipped_tests) + print('\nTotal passed tests:', green(str(passing_tests))) + print('Total failed tests:', red(str(failing_tests))) + print('Total skipped tests:', yellow(str(skipped_tests))) if failing_tests > 0: print('\nMesonlogs of failing tests\n') for l in failing_logs: diff --git a/run_tests.py b/run_tests.py index 1549979..040f958 100755 --- a/run_tests.py +++ b/run_tests.py @@ -23,6 +23,7 @@ import tempfile import platform from mesonbuild import mesonlib from mesonbuild import mesonmain +from mesonbuild import mlog from mesonbuild.environment import detect_ninja from io import StringIO from enum import Enum @@ -177,7 +178,8 @@ if __name__ == '__main__': if 'APPVEYOR' in os.environ and os.environ['arch'] == 'x86': os.environ.pop('platform') # Run tests - print('Running unittests.\n') + print(mlog.bold('Running unittests.').get_text(mlog.colorize_console)) + print() units = ['InternalTests', 'AllPlatformTests', 'FailureTests'] if mesonlib.is_linux(): units += ['LinuxlikeTests'] @@ -200,7 +202,8 @@ if __name__ == '__main__': returncode += subprocess.call([sys.executable, 'run_unittests.py', '-v'] + units, env=env) # Ubuntu packages do not have a binary without -6 suffix. if should_run_linux_cross_tests(): - print('Running cross compilation tests.\n') + print(mlog.bold('Running cross compilation tests.').get_text(mlog.colorize_console)) + print() returncode += subprocess.call([sys.executable, 'run_cross_test.py', 'cross/ubuntu-armhf.txt'], env=env) returncode += subprocess.call([sys.executable, 'run_project_tests.py'] + sys.argv[1:], env=env) sys.exit(returncode) diff --git a/run_unittests.py b/run_unittests.py index 63462d8..6a50302 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -109,7 +109,7 @@ class InternalTests(unittest.TestCase): def test_compiler_args_class(self): cargsfunc = mesonbuild.compilers.CompilerArgs - c = mesonbuild.environment.CCompiler([], 'fake', False) + c = mesonbuild.compilers.CCompiler([], 'fake', False) # Test that bad initialization fails self.assertRaises(TypeError, cargsfunc, []) self.assertRaises(TypeError, cargsfunc, [], []) @@ -701,7 +701,7 @@ class AllPlatformTests(BasePlatformTests): static_linker = env.detect_static_linker(cc) if is_windows(): raise unittest.SkipTest('https://github.com/mesonbuild/meson/issues/1526') - if not isinstance(static_linker, mesonbuild.compilers.ArLinker): + if not isinstance(static_linker, mesonbuild.linkers.ArLinker): raise unittest.SkipTest('static linker is not `ar`') # Configure self.init(testdir) @@ -936,8 +936,8 @@ class AllPlatformTests(BasePlatformTests): clang = mesonbuild.compilers.ClangCompiler intel = mesonbuild.compilers.IntelCompiler msvc = mesonbuild.compilers.VisualStudioCCompiler - ar = mesonbuild.compilers.ArLinker - lib = mesonbuild.compilers.VisualStudioLinker + ar = mesonbuild.linkers.ArLinker + lib = mesonbuild.linkers.VisualStudioLinker langs = [('c', 'CC'), ('cpp', 'CXX')] if not is_windows(): langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')] diff --git a/test cases/common/127 cpp and asm/retval-arm.S b/test cases/common/127 cpp and asm/retval-arm.S index 8b37197..a892362 100644 --- a/test cases/common/127 cpp and asm/retval-arm.S +++ b/test cases/common/127 cpp and asm/retval-arm.S @@ -2,7 +2,10 @@ .text .globl SYMBOL_NAME(get_retval) +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - mov r0, #0 - mov pc, lr + mov r0, #0 + mov pc, lr diff --git a/test cases/common/127 cpp and asm/retval-x86.S b/test cases/common/127 cpp and asm/retval-x86.S index 06bd75c..f9e8190 100644 --- a/test cases/common/127 cpp and asm/retval-x86.S +++ b/test cases/common/127 cpp and asm/retval-x86.S @@ -2,7 +2,10 @@ .text .globl SYMBOL_NAME(get_retval) +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - xorl %eax, %eax - retl + xorl %eax, %eax + retl diff --git a/test cases/common/127 cpp and asm/retval-x86_64.S b/test cases/common/127 cpp and asm/retval-x86_64.S index 638921e..1a5f3eb 100644 --- a/test cases/common/127 cpp and asm/retval-x86_64.S +++ b/test cases/common/127 cpp and asm/retval-x86_64.S @@ -2,7 +2,10 @@ .text .globl SYMBOL_NAME(get_retval) +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - xorl %eax, %eax - retq + xorl %eax, %eax + retq diff --git a/test cases/common/141 c cpp and asm/retval-arm.S b/test cases/common/141 c cpp and asm/retval-arm.S index 8b37197..a892362 100644 --- a/test cases/common/141 c cpp and asm/retval-arm.S +++ b/test cases/common/141 c cpp and asm/retval-arm.S @@ -2,7 +2,10 @@ .text .globl SYMBOL_NAME(get_retval) +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - mov r0, #0 - mov pc, lr + mov r0, #0 + mov pc, lr diff --git a/test cases/common/141 c cpp and asm/retval-x86.S b/test cases/common/141 c cpp and asm/retval-x86.S index 06bd75c..3cb0237 100644 --- a/test cases/common/141 c cpp and asm/retval-x86.S +++ b/test cases/common/141 c cpp and asm/retval-x86.S @@ -2,7 +2,11 @@ .text .globl SYMBOL_NAME(get_retval) +/* Only supported on Linux with GAS */ +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - xorl %eax, %eax - retl + xorl %eax, %eax + retl diff --git a/test cases/common/141 c cpp and asm/retval-x86_64.S b/test cases/common/141 c cpp and asm/retval-x86_64.S index 638921e..1a5f3eb 100644 --- a/test cases/common/141 c cpp and asm/retval-x86_64.S +++ b/test cases/common/141 c cpp and asm/retval-x86_64.S @@ -2,7 +2,10 @@ .text .globl SYMBOL_NAME(get_retval) +# ifdef __linux__ +.type get_retval, %function +#endif SYMBOL_NAME(get_retval): - xorl %eax, %eax - retq + xorl %eax, %eax + retq diff --git a/test cases/failing/55 wrong shared crate type/foo.rs b/test cases/failing/55 wrong shared crate type/foo.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/failing/55 wrong shared crate type/foo.rs diff --git a/test cases/failing/55 wrong shared crate type/meson.build b/test cases/failing/55 wrong shared crate type/meson.build new file mode 100644 index 0000000..69ac3da --- /dev/null +++ b/test cases/failing/55 wrong shared crate type/meson.build @@ -0,0 +1,3 @@ +project('test', 'rust') + +shared_library('test', 'foo.rs', rust_crate_type : 'staticlib') diff --git a/test cases/failing/56 wrong static crate type/foo.rs b/test cases/failing/56 wrong static crate type/foo.rs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/failing/56 wrong static crate type/foo.rs diff --git a/test cases/failing/56 wrong static crate type/meson.build b/test cases/failing/56 wrong static crate type/meson.build new file mode 100644 index 0000000..c094613 --- /dev/null +++ b/test cases/failing/56 wrong static crate type/meson.build @@ -0,0 +1,3 @@ +project('test', 'rust') + +static_library('test', 'foo.rs', rust_crate_type : 'cdylib') diff --git a/test cases/failing/57 kwarg in module/meson.build b/test cases/failing/57 kwarg in module/meson.build new file mode 100644 index 0000000..b105db1 --- /dev/null +++ b/test cases/failing/57 kwarg in module/meson.build @@ -0,0 +1,5 @@ +project('module test', 'c') + +modtest = import('modtest', i_cause: 'a_build_failure') +modtest.print_hello() + diff --git a/test cases/failing/57 or on new line/meson.build b/test cases/failing/57 or on new line/meson.build new file mode 100644 index 0000000..12f2705 --- /dev/null +++ b/test cases/failing/57 or on new line/meson.build @@ -0,0 +1,7 @@ +project('silent_or', 'c') + +if get_option('foo') == 'true' + or get_option('foo') == 'auto' +else + message('If this message is printed then something is wrong. The or above should give a syntax error.') +endif diff --git a/test cases/failing/57 or on new line/meson_options.txt b/test cases/failing/57 or on new line/meson_options.txt new file mode 100644 index 0000000..3302cf4 --- /dev/null +++ b/test cases/failing/57 or on new line/meson_options.txt @@ -0,0 +1 @@ +option('foo', type: 'combo', choices: ['true', 'false', 'auto'], value: 'auto') diff --git a/test cases/frameworks/10 gtk-doc/doc/foobar-docs.sgml b/test cases/frameworks/10 gtk-doc/doc/foobar-docs.sgml index d23b22f..028b808 100644 --- a/test cases/frameworks/10 gtk-doc/doc/foobar-docs.sgml +++ b/test cases/frameworks/10 gtk-doc/doc/foobar-docs.sgml @@ -34,6 +34,7 @@ </para> </partintro> <xi:include href="xml/foo.xml"/> + <xi:include href="xml/foo-version.xml"/> </reference> </book> diff --git a/test cases/frameworks/10 gtk-doc/include/foo-version.h.in b/test cases/frameworks/10 gtk-doc/include/foo-version.h.in new file mode 100644 index 0000000..30751cd --- /dev/null +++ b/test cases/frameworks/10 gtk-doc/include/foo-version.h.in @@ -0,0 +1,29 @@ +#pragma once + +/** + * SECTION:version + * @section_id: foo-version + * @short_description: <filename>foo-version.h</filename> + * @title: Foo Versioning + */ + +/** + * FOO_MAJOR_VERSION: + * + * The major version of foo. + */ +#define FOO_MAJOR_VERSION (@FOO_MAJOR_VERSION@) + +/** + * FOO_MINOR_VERSION: + * + * The minor version of foo. + */ +#define FOO_MINOR_VERSION (@FOO_MINOR_VERSION@) + +/** + * FOO_MICRO_VERSION: + * + * The micro version of foo. + */ +#define FOO_MICRO_VERSION (@FOO_MICRO_VERSION@) diff --git a/test cases/frameworks/10 gtk-doc/include/meson.build b/test cases/frameworks/10 gtk-doc/include/meson.build new file mode 100644 index 0000000..4c85b80 --- /dev/null +++ b/test cases/frameworks/10 gtk-doc/include/meson.build @@ -0,0 +1,10 @@ +cdata = configuration_data() +parts = meson.project_version().split('.') +cdata.set('FOO_MAJOR_VERSION', parts[0]) +cdata.set('FOO_MINOR_VERSION', parts[1]) +cdata.set('FOO_MICRO_VERSION', parts[2]) +configure_file(input : 'foo-version.h.in', + output : 'foo-version.h', + configuration : cdata, + install : true, + install_dir : get_option('includedir')) diff --git a/test cases/frameworks/10 gtk-doc/meson.build b/test cases/frameworks/10 gtk-doc/meson.build index 95eeefa..4cfcca1 100644 --- a/test cases/frameworks/10 gtk-doc/meson.build +++ b/test cases/frameworks/10 gtk-doc/meson.build @@ -1,4 +1,4 @@ -project('gtkdoctest', 'c') +project('gtkdoctest', 'c', version : '1.0.0') gnome = import('gnome') @@ -6,6 +6,8 @@ assert(gnome.gtkdoc_html_dir('foobar') == 'share/gtkdoc/html/foobar', 'Gtkdoc in inc = include_directories('include') +subdir('include') + # We have to disable this test until this bug fix has landed to # distros https://bugzilla.gnome.org/show_bug.cgi?id=753145 error('MESON_SKIP_TEST can not enable gtk-doc test until upstream fixes have landed.') diff --git a/test cases/rust/2 sharedlib/installed_files.txt b/test cases/rust/2 sharedlib/installed_files.txt index 85acff2..680343d 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?exe -usr/lib/libstuff.rlib +usr/lib/libstuff.so diff --git a/test cases/rust/4 polyglot/installed_files.txt b/test cases/rust/4 polyglot/installed_files.txt new file mode 100644 index 0000000..680343d --- /dev/null +++ b/test cases/rust/4 polyglot/installed_files.txt @@ -0,0 +1,2 @@ +usr/bin/prog?exe +usr/lib/libstuff.so diff --git a/test cases/rust/4 polyglot/meson.build b/test cases/rust/4 polyglot/meson.build new file mode 100644 index 0000000..a20d766 --- /dev/null +++ b/test cases/rust/4 polyglot/meson.build @@ -0,0 +1,5 @@ +project('rust and c polyglot executable', 'c', 'rust') + +l = library('stuff', 'stuff.rs', install : true) +e = executable('prog', 'prog.c', link_with : l, install : true) +test('polyglottest', e) diff --git a/test cases/rust/4 polyglot/prog.c b/test cases/rust/4 polyglot/prog.c new file mode 100644 index 0000000..18f2c36 --- /dev/null +++ b/test cases/rust/4 polyglot/prog.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +void f(); + +int main() { + printf("Hello from C!\n"); + f(); +} diff --git a/test cases/rust/4 polyglot/stuff.rs b/test cases/rust/4 polyglot/stuff.rs new file mode 100644 index 0000000..ecf623c --- /dev/null +++ b/test cases/rust/4 polyglot/stuff.rs @@ -0,0 +1,6 @@ +#![crate_name = "stuff"] + +#[no_mangle] +pub extern fn f() { + println!("Hello from Rust!"); +} diff --git a/test cases/rust/5 polyglot static/installed_files.txt b/test cases/rust/5 polyglot static/installed_files.txt new file mode 100644 index 0000000..2f7a397 --- /dev/null +++ b/test cases/rust/5 polyglot static/installed_files.txt @@ -0,0 +1,2 @@ +usr/bin/prog?exe +usr/lib/libstuff.a diff --git a/test cases/rust/5 polyglot static/meson.build b/test cases/rust/5 polyglot static/meson.build new file mode 100644 index 0000000..76dc790 --- /dev/null +++ b/test cases/rust/5 polyglot static/meson.build @@ -0,0 +1,10 @@ +project('static rust and c polyglot executable', 'c', 'rust') + +deps = [ + meson.get_compiler('c').find_library('dl'), + dependency('threads'), +] + +l = static_library('stuff', 'stuff.rs', rust_crate_type : 'staticlib', install : true) +e = executable('prog', 'prog.c', dependencies: deps, link_with : l, install : true) +test('polyglottest', e) diff --git a/test cases/rust/5 polyglot static/prog.c b/test cases/rust/5 polyglot static/prog.c new file mode 100644 index 0000000..18f2c36 --- /dev/null +++ b/test cases/rust/5 polyglot static/prog.c @@ -0,0 +1,8 @@ +#include <stdio.h> + +void f(); + +int main() { + printf("Hello from C!\n"); + f(); +} diff --git a/test cases/rust/5 polyglot static/stuff.rs b/test cases/rust/5 polyglot static/stuff.rs new file mode 100644 index 0000000..ecf623c --- /dev/null +++ b/test cases/rust/5 polyglot static/stuff.rs @@ -0,0 +1,6 @@ +#![crate_name = "stuff"] + +#[no_mangle] +pub extern fn f() { + println!("Hello from Rust!"); +} diff --git a/test cases/vala/7 shared library/lib/meson.build b/test cases/vala/7 shared library/lib/meson.build index 78646a8..edeeb96 100644 --- a/test cases/vala/7 shared library/lib/meson.build +++ b/test cases/vala/7 shared library/lib/meson.build @@ -1,4 +1,12 @@ -l = shared_library('valalib', 'mylib.vala', dependencies : valadeps) +args = [] +# https://github.com/mesonbuild/meson/issues/1969 +if get_option('unity') == 'on' + vala_args = ['-H', 'mylib.h'] +endif + +l = shared_library('valalib', 'mylib.vala', + vala_args : args, + dependencies : valadeps) shared_library('installed_vala_lib', 'mylib.vala', dependencies : valadeps, |