diff options
38 files changed, 281 insertions, 152 deletions
diff --git a/docs/markdown/Builtin-options.md b/docs/markdown/Builtin-options.md index 05578c7..55352aa 100644 --- a/docs/markdown/Builtin-options.md +++ b/docs/markdown/Builtin-options.md @@ -36,7 +36,7 @@ Installation options are all relative to the prefix, except: | sysconfdir | etc | Sysconf data directory | | localstatedir | var | Localstate data directory | | sharedstatedir | com | Architecture-independent data directory | -| werror | false | Treat warnings as erros | +| werror | false | Treat warnings as errors | | warnlevel {1, 2, 3} | 1 | Set the warning level. From 1 = lowest to 3 = highest | | layout {mirror,flat} | mirror | Build directory layout. | | default-library {shared, static, both} | shared | Default library type. | diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 1841618..08ff1e2 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -356,6 +356,10 @@ the list of sources for the target. The `modules` keyword of `dependency` works just like it does with Boost. It tells which subparts of Qt the program uses. +You can set the `main` keyword argument to `true` to use the `WinMain()` +function provided by qtmain static library (this argument does nothing on platforms +other than Windows). + Setting the optional `private_headers` keyword to true adds the private header include path of the given module(s) to the compiler flags. (since v0.47.0) diff --git a/docs/markdown/Quick-guide.md b/docs/markdown/Quick-guide.md index e0a528e..549dcfc 100644 --- a/docs/markdown/Quick-guide.md +++ b/docs/markdown/Quick-guide.md @@ -23,7 +23,7 @@ generate native VS and XCode project files. On Ubuntu these can be easily installed with the following command: ```console -$ sudo apt-get install python3 ninja-build +$ sudo apt-get install python3 python3-pip ninja-build ``` The best way to get Meson is to `pip install` it for your user diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index a6a35f9..b616d3e 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -142,6 +142,8 @@ library. In addition it supports the following extra methods: - `get_shared_lib()` returns the shared library build target - `get_static_lib()` returns the static library build target +*Added 0.46.0* + ### build_target() Creates a build target whose type can be set dynamically with the diff --git a/docs/markdown/Windows-module.md b/docs/markdown/Windows-module.md index 39f1ba6..8c01a41 100644 --- a/docs/markdown/Windows-module.md +++ b/docs/markdown/Windows-module.md @@ -21,3 +21,10 @@ has the following keyword argument. - `include_directories` lists directories to be both searched by the resource compiler for referenced resource files, and added to the preprocessor include search path. + +The resource compiler executable used is the first which exists from the +following list: + +1. The `windres` executable given in the `[binaries]` section of the cross-file +2. The `WINDRES` environment variable +3. The resource compiler which is part of the same toolset as the C or C++ compiler in use. diff --git a/docs/images/buildtime.png b/docs/markdown/images/buildtime.png Binary files differindex 2a44422..2a44422 100644 --- a/docs/images/buildtime.png +++ b/docs/markdown/images/buildtime.png diff --git a/docs/images/conftime.png b/docs/markdown/images/conftime.png Binary files differindex 63754db..63754db 100644 --- a/docs/images/conftime.png +++ b/docs/markdown/images/conftime.png diff --git a/docs/images/emptytime.png b/docs/markdown/images/emptytime.png Binary files differindex d80eab9..d80eab9 100644 --- a/docs/images/emptytime.png +++ b/docs/markdown/images/emptytime.png diff --git a/docs/images/glib_build.png b/docs/markdown/images/glib_build.png Binary files differindex ddb9947..ddb9947 100644 --- a/docs/images/glib_build.png +++ b/docs/markdown/images/glib_build.png diff --git a/docs/images/glib_conf.png b/docs/markdown/images/glib_conf.png Binary files differindex 5de60d5..5de60d5 100644 --- a/docs/images/glib_conf.png +++ b/docs/markdown/images/glib_conf.png diff --git a/docs/images/glib_empty.png b/docs/markdown/images/glib_empty.png Binary files differindex 5976e7f..5976e7f 100644 --- a/docs/images/glib_empty.png +++ b/docs/markdown/images/glib_empty.png diff --git a/docs/images/glib_link.png b/docs/markdown/images/glib_link.png Binary files differindex 23d9044..23d9044 100644 --- a/docs/images/glib_link.png +++ b/docs/markdown/images/glib_link.png diff --git a/docs/images/gtksample.png b/docs/markdown/images/gtksample.png Binary files differindex b6557c4..b6557c4 100644 --- a/docs/images/gtksample.png +++ b/docs/markdown/images/gtksample.png diff --git a/docs/images/py3-install-1.png b/docs/markdown/images/py3-install-1.png Binary files differindex 74f0819..74f0819 100644 --- a/docs/images/py3-install-1.png +++ b/docs/markdown/images/py3-install-1.png diff --git a/docs/images/py3-install-2.png b/docs/markdown/images/py3-install-2.png Binary files differindex 9a8f1fe..9a8f1fe 100644 --- a/docs/images/py3-install-2.png +++ b/docs/markdown/images/py3-install-2.png diff --git a/docs/images/py3-install-3.png b/docs/markdown/images/py3-install-3.png Binary files differindex b702910..b702910 100644 --- a/docs/images/py3-install-3.png +++ b/docs/markdown/images/py3-install-3.png diff --git a/docs/meson.build b/docs/meson.build index c83d5f8..32aab07 100644 --- a/docs/meson.build +++ b/docs/meson.build @@ -17,6 +17,5 @@ documentation = hotdoc.generate_doc(meson.project_name(), ) run_target('upload', - command: [find_program('hotdoc'), 'run', '--conf-file', documentation.config_path(), - '--git-upload'] -)
\ No newline at end of file + command: [find_program('hotdoc'), 'run', '--conf-file', documentation.config_path()] +) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 5232b65..0e7e8e0 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -17,7 +17,6 @@ from .. import build from .. import dependencies from .. import mesonlib from .. import mlog -from .. import compilers import json import subprocess from ..mesonlib import MesonException, OrderedSet @@ -26,6 +25,12 @@ from ..mesonlib import File from ..compilers import CompilerArgs, get_macos_dylib_install_name from collections import OrderedDict import shlex +from functools import lru_cache + +@lru_cache(maxsize=None) +def get_target_macos_dylib_install_name(ld): + return get_macos_dylib_install_name(ld.prefix, ld.name, ld.suffix, ld.soversion) + class CleanTrees: ''' @@ -388,14 +393,13 @@ class Backend: return paths def determine_rpath_dirs(self, target): - link_deps = target.get_all_link_deps() - result = OrderedSet() - for ld in link_deps: - if ld is target: - continue - result.add(self.get_target_dir(ld)) + if self.environment.coredata.get_builtin_option('layout') == 'mirror': + result = target.get_link_dep_subdirs() + else: + result = OrderedSet() + result.add('meson-out') result.update(self.rpaths_for_bundled_shared_libraries(target)) - return list(result) + return tuple(result) def object_filename_from_source(self, target, source): assert isinstance(source, mesonlib.File) @@ -1006,7 +1010,7 @@ class Backend: for ld in t.get_all_link_deps(): if ld is t or not isinstance(ld, build.SharedLibrary): continue - old = get_macos_dylib_install_name(ld.prefix, ld.name, ld.suffix, ld.soversion) + old = get_target_macos_dylib_install_name(ld) if old in result: continue fname = ld.get_filename() diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 6daa939..6b2a00a 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -20,6 +20,7 @@ import subprocess from collections import OrderedDict import itertools from pathlib import PurePath +from functools import lru_cache from . import backends from .. import modules @@ -147,7 +148,6 @@ class NinjaBackend(backends.Backend): super().__init__(build) self.name = 'ninja' self.ninja_filename = 'build.ninja' - self.target_arg_cache = {} self.fortran_deps = {} self.all_outputs = {} @@ -1951,6 +1951,7 @@ rule FORTRAN_DEP_HACK%s incs += compiler.get_include_args(i, False) return incs + @lru_cache(maxsize=None) def _generate_single_compile(self, target, compiler, is_generated=False): base_proxy = self.get_base_options_for_target(target) # Create an empty commands list, and start adding arguments from @@ -2047,12 +2048,7 @@ rule FORTRAN_DEP_HACK%s raise AssertionError('BUG: sources should not contain headers {!r}'.format(src)) compiler = get_compiler_for_source(target.compilers.values(), src) - key = (target, compiler, is_generated) - if key in self.target_arg_cache: - commands = self.target_arg_cache[key] - else: - commands = self._generate_single_compile(target, compiler, is_generated) - self.target_arg_cache[key] = commands + commands = self._generate_single_compile(target, compiler, is_generated) commands = CompilerArgs(commands.compiler, commands) build_dir = self.environment.get_build_dir() @@ -2276,6 +2272,7 @@ rule FORTRAN_DEP_HACK%s return linker.get_link_whole_for(target_args) if len(target_args) else [] @staticmethod + @lru_cache(maxsize=None) def guess_library_absolute_path(linker, libname, search_dirs, patterns): for d in search_dirs: for p in patterns: @@ -2334,7 +2331,7 @@ rule FORTRAN_DEP_HACK%s guessed_dependencies = [] # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker if hasattr(linker, 'get_library_naming'): - search_dirs = list(search_dirs) + linker.get_library_dirs(self.environment) + search_dirs = tuple(search_dirs) + linker.get_library_dirs(self.environment) static_patterns = linker.get_library_naming(self.environment, 'static', strict=True) shared_patterns = linker.get_library_naming(self.environment, 'shared', strict=True) for libname in libs: diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 585a8d3..ec6e1e6 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -16,11 +16,12 @@ import copy, os, re from collections import OrderedDict import itertools, pathlib import pickle +from functools import lru_cache from . import environment from . import dependencies from . import mlog -from .mesonlib import File, MesonException, listify, extract_as_list +from .mesonlib import File, MesonException, listify, extract_as_list, OrderedSet from .mesonlib import typeslistify, stringlistify, classify_unity_sources from .mesonlib import get_filenames_templates_dict, substitute_values from .mesonlib import for_windows, for_darwin, for_cygwin, for_android, has_path_sep @@ -668,12 +669,21 @@ class BuildTarget(Target): def get_all_link_deps(self): return self.get_transitive_link_deps() + @lru_cache(maxsize=None) def get_transitive_link_deps(self): result = [] for i in self.link_targets: result += i.get_all_link_deps() return result + @lru_cache(maxsize=None) + def get_link_dep_subdirs(self): + result = OrderedSet() + for i in self.link_targets: + result.add(i.get_subdir()) + result.update(i.get_link_dep_subdirs()) + return result + def get_custom_install_dir(self): return self.install_dir diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index bde9f63..1d531a6 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -119,6 +119,7 @@ class CCompiler(Compiler): return None, fname # The default behavior is this, override in MSVC + @functools.lru_cache(maxsize=None) def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): if self.id == 'clang' and self.clang_type == compilers.CLANG_OSX: return self.build_osx_rpath_args(build_dir, rpath_paths, build_rpath) @@ -222,7 +223,7 @@ class CCompiler(Compiler): p = Path(p) if p.exists(): paths.append(p.resolve().as_posix()) - return paths + return tuple(paths) def get_compiler_dirs(self, env, name): ''' @@ -232,23 +233,19 @@ class CCompiler(Compiler): for line in stdo.split('\n'): if line.startswith(name + ':'): return CCompiler._split_fetch_real_dirs(line.split('=', 1)[1]) - return [] + return () + @functools.lru_cache() def get_library_dirs(self, env): - key = (tuple(self.exelist), env) - if key not in self.library_dirs_cache: - self.library_dirs_cache[key] = self.get_compiler_dirs(env, 'libraries') - return self.library_dirs_cache[key][:] + return self.get_compiler_dirs(env, 'libraries') + @functools.lru_cache() def get_program_dirs(self, env): ''' Programs used by the compiler. Also where toolchain DLLs such as libstdc++-6.dll are found with MinGW. ''' - key = (tuple(self.exelist), env) - if key not in self.program_dirs_cache: - self.program_dirs_cache[key] = self.get_compiler_dirs(env, 'programs') - return self.program_dirs_cache[key][:] + return self.get_compiler_dirs(env, 'programs') def get_pic_args(self): return ['-fPIC'] @@ -907,7 +904,7 @@ class CCompiler(Compiler): patterns += self._get_patterns(env, prefixes, stlibext, False) else: raise AssertionError('BUG: unknown libtype {!r}'.format(libtype)) - return patterns + return tuple(patterns) @staticmethod def _sort_shlibs_openbsd(libs): diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 88e9bce..40d6880 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -923,7 +923,7 @@ class Compiler: raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language())) def get_library_dirs(self, *args, **kwargs): - return [] + return () def has_multi_arguments(self, args, env): raise EnvironmentException( @@ -1381,12 +1381,12 @@ class ElbrusCompiler(GnuCompiler): os_env = os.environ.copy() os_env['LC_ALL'] = 'C' stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] - paths = [] + paths = () for line in stdo.split('\n'): if line.startswith('libraries:'): # lcc does not include '=' in --print-search-dirs output. libstr = line.split(' ', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':')] + paths = (os.path.realpath(p) for p in libstr.split(':')) break return paths @@ -1394,12 +1394,12 @@ class ElbrusCompiler(GnuCompiler): os_env = os.environ.copy() os_env['LC_ALL'] = 'C' stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=os_env)[1] - paths = [] + paths = () for line in stdo.split('\n'): if line.startswith('programs:'): # lcc does not include '=' in --print-search-dirs output. libstr = line.split(' ', 1)[1] - paths = [os.path.realpath(p) for p in libstr.split(':')] + paths = (os.path.realpath(p) for p in libstr.split(':')) break return paths diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 5433b36..ee9e0c2 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -18,7 +18,6 @@ from ..mesonlib import EnvironmentException, version_compare, is_windows, is_osx from .compilers import ( GCC_STANDARD, - GCC_CYGWIN, GCC_OSX, d_dmd_buildtype_args, d_gdc_buildtype_args, diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 633228c..a34ebf7 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -56,7 +56,7 @@ class DependencyMethods(Enum): SYSCONFIG = 'sysconfig' # Specify using a "program"-config style tool CONFIG_TOOL = 'config-tool' - # For backewards compatibility + # For backwards compatibility SDLCONFIG = 'sdlconfig' CUPSCONFIG = 'cups-config' PCAPCONFIG = 'pcap-config' @@ -1413,11 +1413,17 @@ def find_external_dependency(name, env, kwargs): # if the dependency was found if d.found(): - info = d.log_info() - if info: - info = ', ' + info + info = [] + if d.version: + info.append(d.version) - mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), (d.version if d.version else '') + info) + log_info = d.log_info() + if log_info: + info.append('(' + log_info + ')') + + info = ' '.join(info) + + mlog.log(type_text, mlog.bold(display_name), details + 'found:', mlog.green('YES'), info) return d diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index bcaf2e8..5ee8b09 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -80,6 +80,9 @@ class GTestDependency(ExternalDependency): else: return 'building self' + def log_tried(self): + return 'system' + @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) @@ -161,6 +164,9 @@ class GMockDependency(ExternalDependency): else: return 'building self' + def log_tried(self): + return 'system' + @classmethod def _factory(cls, environment, kwargs): methods = cls._process_method_kw(kwargs) diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 65a2803..5164512 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -397,6 +397,8 @@ class Python3Dependency(ExternalDependency): else: return [DependencyMethods.PKGCONFIG] + def log_tried(self): + return 'sysconfig' class PcapDependency(ExternalDependency): diff --git a/mesonbuild/dependencies/platform.py b/mesonbuild/dependencies/platform.py index 0c05156..5b2003f 100644 --- a/mesonbuild/dependencies/platform.py +++ b/mesonbuild/dependencies/platform.py @@ -34,3 +34,6 @@ class AppleFrameworks(ExternalDependency): self.link_args += ['-framework', f] self.is_found = mesonlib.is_osx() + + def log_tried(self): + return 'framework' diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index f19a76d..a46afbb 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -24,7 +24,7 @@ from collections import OrderedDict from .. import mlog from .. import mesonlib from ..mesonlib import ( - MesonException, Popen_safe, extract_as_list, for_windows, for_cygwin, + MesonException, Popen_safe, extract_as_list, for_windows, version_compare_many ) from ..environment import detect_cpu @@ -72,6 +72,8 @@ class GLDependency(ExternalDependency): else: return [DependencyMethods.PKGCONFIG] + def log_tried(self): + return 'system' class GnuStepDependency(ConfigToolDependency): @@ -207,6 +209,10 @@ class QtBaseDependency(ExternalDependency): raise DependencyException('No ' + self.qtname + ' modules specified.') self.from_text = 'pkg-config' + self.qtmain = kwargs.get('main', False) + if not isinstance(self.qtmain, bool): + raise DependencyException('"main" argument must be a boolean') + # Keep track of the detection methods used, for logging purposes. methods = [] # Prefer pkg-config, then fallback to `qmake -query` @@ -259,17 +265,32 @@ class QtBaseDependency(ExternalDependency): for dir in mod_private_inc: self.compile_args.append('-I' + dir) self.link_args += m.get_link_args() - self.is_found = True - self.version = m.version - self.pcdep = list(modules.values()) - # Try to detect moc, uic, rcc + if 'Core' in modules: core = modules['Core'] else: corekwargs = {'required': 'false', 'silent': 'true'} core = PkgConfigDependency(self.qtpkgname + 'Core', self.env, corekwargs, language=self.language) - self.pcdep.append(core) + modules['Core'] = core + + if for_windows(self.env.is_cross_build(), self.env) and self.qtmain: + # Check if we link with debug binaries + debug_lib_name = self.qtpkgname + 'Core' + self._get_modules_lib_suffix(True) + is_debug = False + for arg in core.get_link_args(): + if arg == '-l%s' % debug_lib_name or arg.endswith('%s.lib' % debug_lib_name) or arg.endswith('%s.a' % debug_lib_name): + is_debug = True + break + libdir = core.get_pkgconfig_variable('libdir', {}) + if not self._link_with_qtmain(is_debug, libdir): + self.is_found = False + return + + self.is_found = True + self.version = m.version + self.pcdep = list(modules.values()) + # Try to detect moc, uic, rcc # Used by self.compilers_detect() self.bindir = self.get_pkgconfig_host_bins(core) if not self.bindir: @@ -320,42 +341,62 @@ class QtBaseDependency(ExternalDependency): incdir = qvars['QT_INSTALL_HEADERS'] self.compile_args.append('-I' + incdir) libdir = qvars['QT_INSTALL_LIBS'] - if for_cygwin(self.env.is_cross_build(), self.env): - shlibext = '.dll.a' - else: - shlibext = '.so' # Used by self.compilers_detect() self.bindir = self.get_qmake_host_bins(qvars) self.is_found = True + + is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug' + modules_lib_suffix = self._get_modules_lib_suffix(is_debug) + for module in mods: mincdir = os.path.join(incdir, 'Qt' + module) self.compile_args.append('-I' + mincdir) + + if module == 'QuickTest': + define_base = 'QMLTEST' + elif module == 'Test': + define_base = 'TESTLIB' + else: + define_base = module.upper() + self.compile_args.append('-DQT_%s_LIB' % define_base) + if self.private_headers: priv_inc = self.get_private_includes(mincdir, module) for dir in priv_inc: self.compile_args.append('-I' + dir) - if for_windows(self.env.is_cross_build(), self.env): - is_debug = self.env.coredata.get_builtin_option('buildtype') == 'debug' - dbg = 'd' if is_debug else '' - if self.qtver == '4': - base_name = 'Qt' + module + dbg + '4' - else: - base_name = 'Qt5' + module + dbg - libfile = os.path.join(libdir, base_name + '.lib') - if not os.path.isfile(libfile): - # MinGW can link directly to .dll - libfile = os.path.join(self.bindir, base_name + '.dll') - if not os.path.isfile(libfile): - self.is_found = False - break + libfile = self.clib_compiler.find_library(self.qtpkgname + module + modules_lib_suffix, + self.env, + libdir) + if libfile: + libfile = libfile[0] else: - libfile = os.path.join(libdir, 'lib{}{}{}'.format(self.qtpkgname, module, shlibext)) - if not os.path.isfile(libfile): - self.is_found = False - break + self.is_found = False + break self.link_args.append(libfile) + + if for_windows(self.env.is_cross_build(), self.env) and self.qtmain: + if not self._link_with_qtmain(is_debug, libdir): + self.is_found = False + return qmake + def _get_modules_lib_suffix(self, is_debug): + suffix = '' + if for_windows(self.env.is_cross_build(), self.env): + if is_debug: + suffix += 'd' + if self.qtver == '4': + suffix += '4' + return suffix + + def _link_with_qtmain(self, is_debug, libdir): + base_name = 'qtmaind' if is_debug else 'qtmain' + qtmain = self.clib_compiler.find_library(base_name, self.env, libdir) + if qtmain: + self.link_args.append(qtmain[0]) + return True + return False + def _framework_detect(self, qvars, modules, kwargs): libdir = qvars['QT_INSTALL_LIBS'] @@ -408,7 +449,7 @@ class QtBaseDependency(ExternalDependency): return 'modules: ' + module_str def log_info(self): - return '`{}`'.format(self.from_text) + return '{}'.format(self.from_text) def log_tried(self): return self.from_text @@ -574,7 +615,7 @@ class VulkanDependency(ExternalDependency): if DependencyMethods.PKGCONFIG in methods: candidates.append(functools.partial(PkgConfigDependency, 'vulkan', environment, kwargs)) - if DependencyMethods.PKGCONFIG in methods: + if DependencyMethods.SYSTEM in methods: candidates.append(functools.partial(VulkanDependency, environment, kwargs)) return candidates @@ -582,3 +623,6 @@ class VulkanDependency(ExternalDependency): @staticmethod def get_methods(): return [DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM] + + def log_tried(self): + return 'system' diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 9e9b5fc..3a1e1e6 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -15,7 +15,7 @@ import configparser, os, platform, re, shlex, shutil, subprocess from . import coredata -from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, LDCLinker +from .linkers import ArLinker, ArmarLinker, VisualStudioLinker, DLinker from . import mesonlib from .mesonlib import EnvironmentException, Popen_safe from . import mlog @@ -341,7 +341,6 @@ class Environment: self.vs_static_linker = ['lib'] self.gcc_static_linker = ['gcc-ar'] self.clang_static_linker = ['llvm-ar'] - self.ldc2_static_linker = ['ldc2'] # Various prefixes and suffixes for import libraries, shared libraries, # static libraries, and executables. @@ -885,10 +884,11 @@ This is probably wrong, it should always point to the native compiler.''' % evar # Use llvm-ar if available; needed for LTO linkers = [self.clang_static_linker, self.default_static_linker] elif isinstance(compiler, compilers.DCompiler): + # Prefer static linkers over linkers used by D compilers if mesonlib.is_windows(): - linkers = [self.vs_static_linker, self.ldc2_static_linker] + linkers = [self.vs_static_linker, compiler.get_linker_exelist()] else: - linkers = [self.default_static_linker, self.ldc2_static_linker] + linkers = [self.default_static_linker, compiler.get_linker_exelist()] else: linkers = [self.default_static_linker] popen_exceptions = {} @@ -906,8 +906,12 @@ This is probably wrong, it should always point to the native compiler.''' % evar return VisualStudioLinker(linker) if p.returncode == 0 and ('armar' in linker or 'armar.exe' in linker): return ArmarLinker(linker) + if 'DMD32 D Compiler' in out or 'DMD64 D Compiler' in out: + return DLinker(linker, compiler.is_64, compiler.is_msvc) if 'LDC - the LLVM D compiler' in out: - return LDCLinker(linker) + return DLinker(linker, compiler.is_64, compiler.is_msvc) + if 'GDC' in out and ' based on D ' in out: + return DLinker(linker, compiler.is_64, compiler.is_msvc) if p.returncode == 0: return ArLinker(linker) if p.returncode == 1 and err.startswith('usage'): # OSX diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 93106b3..30ca5d8 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .mesonlib import Popen_safe +from .mesonlib import Popen_safe, is_windows from . import mesonlib class StaticLinker: @@ -138,11 +138,12 @@ class ArmarLinker(ArLinker): # armar cann't accept arguments using the @rsp syntax return False -class LDCLinker(StaticLinker): - - def __init__(self, exelist): +class DLinker(StaticLinker): + def __init__(self, exelist, is_64, is_msvc): self.exelist = exelist - self.id = 'ldc2' + self.id = exelist[0] + self.is_64 = is_64 + self.is_msvc = is_msvc def can_linker_accept_rsp(self): return mesonlib.is_windows() @@ -163,6 +164,12 @@ class LDCLinker(StaticLinker): return [] def get_linker_always_args(self): + if is_windows(): + if self.is_64: + return ['-m64'] + elif self.is_msvc and self.id == 'dmd': + return ['-m32mscoff'] + return ['-m32'] return [] def get_coverage_link_args(self): diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index b150731..cd925e5 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -43,6 +43,10 @@ def create_parser(): help=argparse.SUPPRESS) p.add_argument('--fatal-meson-warnings', action='store_true', dest='fatal_warnings', help='Make all Meson warnings fatal') + p.add_argument('--reconfigure', action='store_true', + help='Set options and reconfigure the project. Useful when new ' + + 'options have been added to the project and the default value ' + + 'is not working.') p.add_argument('builddir', nargs='?', default=None) p.add_argument('sourcedir', nargs='?', default=None) return p @@ -57,8 +61,10 @@ def wrapmodetype(string): class MesonApp: - def __init__(self, dir1, dir2, handshake, options): - (self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake) + def __init__(self, options): + (self.source_dir, self.build_dir) = self.validate_dirs(options.builddir, + options.sourcedir, + options.reconfigure) self.options = options def has_build_file(self, dirname): @@ -66,6 +72,15 @@ class MesonApp: return os.path.exists(fname) def validate_core_dirs(self, dir1, dir2): + if dir1 is None: + if dir2 is None: + if not os.path.exists('meson.build') and os.path.exists('../meson.build'): + dir2 = '..' + else: + raise MesonException('Must specify at least one directory name.') + dir1 = os.getcwd() + if dir2 is None: + dir2 = os.getcwd() ndir1 = os.path.abspath(os.path.realpath(dir1)) ndir2 = os.path.abspath(os.path.realpath(dir2)) if not os.path.exists(ndir1): @@ -86,21 +101,23 @@ class MesonApp: return ndir2, ndir1 raise MesonException('Neither directory contains a build file %s.' % environment.build_filename) - def validate_dirs(self, dir1, dir2, handshake): + def validate_dirs(self, dir1, dir2, reconfigure): (src_dir, build_dir) = self.validate_core_dirs(dir1, dir2) priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat') if os.path.exists(priv_dir): - if not handshake: - print('Directory already configured, exiting Meson. Just run your build command\n' - '(e.g. ninja) and Meson will regenerate as necessary. If ninja fails, run ninja\n' - 'reconfigure to force Meson to regenerate.\n' + if not reconfigure: + print('Directory already configured.\n' + '\nJust run your build command (e.g. ninja) and Meson will regenerate as necessary.\n' + 'If ninja fails, run "ninja reconfigure" or "meson --reconfigure"\n' + 'to force Meson to regenerate.\n' '\nIf build failures persist, manually wipe your build directory to clear any\n' 'stored system data.\n' - '\nTo change option values, run meson configure instead.') - sys.exit(0) + '\nTo change option values, run "meson configure" instead.') + sys.exit(1) else: - if handshake: - raise RuntimeError('Something went terribly wrong. Please file a bug.') + if reconfigure: + print('Directory does not contain a valid build tree:\n{}'.format(build_dir)) + sys.exit(1) return src_dir, build_dir def check_pkgconfig_envvar(self, env): @@ -317,7 +334,11 @@ def run(original_args, mainfile): # No special command? Do the basic setup/reconf. if len(args) >= 2 and args[0] == '--internal': - if args[1] != 'regenerate': + if args[1] == 'regenerate': + # Rewrite "meson --internal regenerate" command line to + # "meson --reconfigure" + args = ['--reconfigure'] + args[2:] + else: script = args[1] try: sys.exit(run_script_command(args[1:])) @@ -325,29 +346,14 @@ def run(original_args, mainfile): mlog.error('\nError in {} helper script:'.format(script)) mlog.exception(e) sys.exit(1) - args = args[2:] - handshake = True - else: - handshake = False parser = create_parser() args = mesonlib.expand_arguments(args) options = parser.parse_args(args) coredata.parse_cmd_line_options(options) - dir1 = options.builddir - dir2 = options.sourcedir try: - if dir1 is None: - if dir2 is None: - if not os.path.exists('meson.build') and os.path.exists('../meson.build'): - dir2 = '..' - else: - raise MesonException('Must specify at least one directory name.') - dir1 = os.getcwd() - if dir2 is None: - dir2 = os.getcwd() - app = MesonApp(dir1, dir2, handshake, options) + app = MesonApp(options) except Exception as e: # Log directory does not exist, so just print # to stdout. diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py index 1f7368a..e621938 100644 --- a/mesonbuild/modules/hotdoc.py +++ b/mesonbuild/modules/hotdoc.py @@ -87,13 +87,16 @@ class HotdocTargetBuilder: self.cmd.extend([option, value]) def check_extra_arg_type(self, arg, value): + value = getattr(value, 'held_object', value) if isinstance(value, list): for v in value: self.check_extra_arg_type(arg, v) return - if not isinstance(value, (str, bool, mesonlib.File)): - raise InvalidArguments('Argument "%s=%s" should be a string.' % (arg, value)) + valid_types = (str, bool, mesonlib.File, build.IncludeDirs) + if not isinstance(value, valid_types): + raise InvalidArguments('Argument "%s=%s" should be of type: %s.' % ( + arg, value, [t.__name__ for t in valid_types])) def process_extra_args(self): for arg, value in self.kwargs.items(): @@ -198,9 +201,16 @@ class HotdocTargetBuilder: def flatten_config_command(self): cmd = [] for arg in mesonlib.listify(self.cmd, flatten=True): + arg = getattr(arg, 'held_object', arg) if isinstance(arg, mesonlib.File): arg = arg.absolute_path(self.state.environment.get_source_dir(), self.state.environment.get_build_dir()) + elif isinstance(arg, build.IncludeDirs): + for inc_dir in arg.get_incdirs(): + cmd.append(os.path.join(self.sourcedir, arg.get_curdir(), inc_dir)) + cmd.append(os.path.join(self.builddir, arg.get_curdir(), inc_dir)) + + continue cmd.append(arg) @@ -249,6 +259,7 @@ class HotdocTargetBuilder: self.check_forbiden_args() file_types = (str, mesonlib.File) self.process_known_arg("--index", file_types, mandatory=True, value_processor=self.ensure_file) + self.process_known_arg("--project-version", str, mandatory=True) self.process_known_arg("--sitemap", file_types, mandatory=True, value_processor=self.ensure_file) self.process_known_arg("--html-extra-theme", str, value_processor=self.ensure_dir) self.process_known_arg(None, list, "include_paths", force_list=True, diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 87209d6..59e845c 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -49,33 +49,47 @@ class WindowsModule(ExtensionModule): raise MesonException('Resource include dirs should be include_directories().') extra_args += get_include_args(inc_dirs) - if comp.id == 'msvc': - rescomp = ExternalProgram('rc', silent=True) - res_args = extra_args + ['/nologo', '/fo@OUTPUT@', '@INPUT@'] + rescomp = None + # FIXME: Does not handle `native: true` executables, see + # https://github.com/mesonbuild/meson/issues/1531 + if state.environment.is_cross_build(): + # If cross compiling see if windres has been specified in the + # cross file before trying to find it another way. + cross_info = state.environment.cross_info + rescomp = ExternalProgram.from_cross_info(cross_info, 'windres') + + if not rescomp or not rescomp.found(): + if 'WINDRES' in os.environ: + # Pick-up env var WINDRES if set. This is often used for + # specifying an arch-specific windres. + rescomp = ExternalProgram('windres', command=os.environ.get('WINDRES'), silent=True) + + if not rescomp or not rescomp.found(): + if comp.id == 'msvc': + rescomp = ExternalProgram('rc', silent=True) + else: + rescomp = ExternalProgram('windres', silent=True) + + if not rescomp.found(): + raise MesonException('Could not find Windows resource compiler') + + if 'rc' in rescomp.get_path(): + # RC is used to generate .res files, a special binary resource + # format, which can be passed directly to LINK (apparently LINK uses + # CVTRES internally to convert this to a COFF object) suffix = 'res' + res_args = extra_args + ['/nologo', '/fo@OUTPUT@', '@INPUT@'] else: + # ld only supports object files, so windres is used to generate a + # COFF object + suffix = 'o' + res_args = extra_args + ['@INPUT@', '@OUTPUT@'] + m = 'Argument {!r} has a space which may not work with windres due to ' \ 'a MinGW bug: https://sourceware.org/bugzilla/show_bug.cgi?id=4933' for arg in extra_args: if ' ' in arg: mlog.warning(m.format(arg)) - rescomp = None - # FIXME: Does not handle `native: true` executables, see - # https://github.com/mesonbuild/meson/issues/1531 - if state.environment.is_cross_build(): - # If cross compiling see if windres has been specified in the - # cross file before trying to find it another way. - cross_info = state.environment.cross_info - rescomp = ExternalProgram.from_cross_info(cross_info, 'windres') - if not rescomp or not rescomp.found(): - # Pick-up env var WINDRES if set. This is often used for - # specifying an arch-specific windres. - rescomp = ExternalProgram(os.environ.get('WINDRES', 'windres'), silent=True) - res_args = extra_args + ['@INPUT@', '@OUTPUT@'] - suffix = 'o' - if not rescomp.found(): - raise MesonException('Could not find Windows resource compiler {!r}' - ''.format(rescomp.get_path())) res_targets = [] @@ -115,7 +129,7 @@ class WindowsModule(ExtensionModule): } # instruct binutils windres to generate a preprocessor depfile - if comp.id != 'msvc': + if 'windres' in rescomp.get_path(): res_kwargs['depfile'] = res_kwargs['output'] + '.d' res_kwargs['command'] += ['--preprocessor-arg=-MD', '--preprocessor-arg=-MQ@OUTPUT@', '--preprocessor-arg=-MF@DEPFILE@'] diff --git a/run_unittests.py b/run_unittests.py index 214bc16..96802cc 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -575,22 +575,22 @@ class InternalTests(unittest.TestCase): ''' Unit test for the library search patterns used by find_library() ''' - unix_static = ['lib{}.a', '{}.a'] - msvc_static = ['lib{}.a', 'lib{}.lib', '{}.a', '{}.lib'] + unix_static = ('lib{}.a', '{}.a') + msvc_static = ('lib{}.a', 'lib{}.lib', '{}.a', '{}.lib') # This is the priority list of pattern matching for library searching - patterns = {'openbsd': {'shared': ['lib{}.so', '{}.so', 'lib{}.so.[0-9]*.[0-9]*'], + patterns = {'openbsd': {'shared': ('lib{}.so', '{}.so', 'lib{}.so.[0-9]*.[0-9]*'), 'static': unix_static}, - 'linux': {'shared': ['lib{}.so', '{}.so'], + 'linux': {'shared': ('lib{}.so', '{}.so'), 'static': unix_static}, - 'darwin': {'shared': ['lib{}.dylib', '{}.dylib'], + 'darwin': {'shared': ('lib{}.dylib', '{}.dylib'), 'static': unix_static}, - 'cygwin': {'shared': ['cyg{}.dll', 'cyg{}.dll.a', 'lib{}.dll', - 'lib{}.dll.a', '{}.dll', '{}.dll.a'], - 'static': ['cyg{}.a'] + unix_static}, - 'windows-msvc': {'shared': ['lib{}.lib', '{}.lib'], + 'cygwin': {'shared': ('cyg{}.dll', 'cyg{}.dll.a', 'lib{}.dll', + 'lib{}.dll.a', '{}.dll', '{}.dll.a'), + 'static': ('cyg{}.a',) + unix_static}, + 'windows-msvc': {'shared': ('lib{}.lib', '{}.lib'), 'static': msvc_static}, - 'windows-mingw': {'shared': ['lib{}.dll.a', 'lib{}.lib', 'lib{}.dll', - '{}.dll.a', '{}.lib', '{}.dll'], + 'windows-mingw': {'shared': ('lib{}.dll.a', 'lib{}.lib', 'lib{}.dll', + '{}.dll.a', '{}.lib', '{}.dll'), 'static': msvc_static}} env = get_fake_env('', '', '') cc = env.detect_c_compiler(False) @@ -3375,7 +3375,7 @@ class LinuxlikeTests(BasePlatformTests): # Confirm that the dependency was found with qmake mesonlog = self.get_meson_log() self.assertRegex('\n'.join(mesonlog), - r'Dependency qt5 \(modules: Core\) found: YES .*, `(qmake|qmake-qt5)`\n') + r'Dependency qt5 \(modules: Core\) found: YES .* \((qmake|qmake-qt5)\)\n') def _test_soname_impl(self, libpath, install): if is_cygwin() or is_osx(): diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build index fd90e36..662f9b5 100644 --- a/test cases/frameworks/16 sdl2/meson.build +++ b/test cases/frameworks/16 sdl2/meson.build @@ -10,6 +10,12 @@ e = executable('sdl2prog', 'sdl2prog.c', dependencies : sdl2_dep) test('sdl2test', e) +if sdl2_dep.type_name() == 'extraframeworks' + # The SDL OSX framework does not ship with detection executables + # so skip the remaining tests. + subdir_done() +endif + # Ensure that we can find it with sdl2-config too, using the legacy method name configdep = dependency('sdl2', method : 'sdlconfig') diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index ae80287..7ac945e 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -33,7 +33,7 @@ foreach qt : ['qt4', 'qt5'] nocoredep = dependency(qt, modules : ['Gui'], required : qt == 'qt5', method : get_option('method')) # If qt4 modules are found, test that. qt5 is required. - qtdep = dependency(qt, modules : qt_modules, private_headers: true, required : qt == 'qt5', method : get_option('method')) + qtdep = dependency(qt, modules : qt_modules, main : true, private_headers: true, required : qt == 'qt5', method : get_option('method')) if qtdep.found() qtmodule = import(qt) @@ -62,7 +62,8 @@ foreach qt : ['qt4', 'qt5'] sources : ['main.cpp', 'mainWindow.cpp', # Sources that don't need preprocessing. prep, prep_rcc], dependencies : qtdep, - cpp_args: extra_cpp_args) + cpp_args: extra_cpp_args, + gui_app : true) # We need a console test application because some test environments # do not have an X server. diff --git a/test cases/unit/35 dist script/replacer.py b/test cases/unit/35 dist script/replacer.py index 92bcef0..adda365 100755 --- a/test cases/unit/35 dist script/replacer.py +++ b/test cases/unit/35 dist script/replacer.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -import os, sys +import os import pathlib source_root = pathlib.Path(os.environ['MESON_DIST_ROOT']) diff --git a/tools/dircondenser.py b/tools/dircondenser.py index c87b967..58c44a2 100755 --- a/tools/dircondenser.py +++ b/tools/dircondenser.py @@ -67,7 +67,7 @@ def condense(dirname): i = _i + 1 if e[0] != i: old_name = str(e[0]) + ' ' + e[1] - new_name = str(i) + ' ' + e[1] + new_name = str(i) + ' ' + e[1] #print('git mv "%s" "%s"' % (old_name, new_name)) subprocess.check_call(['git', 'mv', old_name, new_name]) replacements.append((old_name, new_name)) |