aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CODEOWNERS6
-rw-r--r--data/shell-completions/zsh/_meson295
-rw-r--r--docs/markdown/Custom-build-targets.md9
-rw-r--r--docs/markdown/Reference-manual.md4
-rw-r--r--docs/markdown/Tutorial.md15
-rw-r--r--mesonbuild/ast/introspection.py15
-rw-r--r--mesonbuild/backend/ninjabackend.py7
-rw-r--r--mesonbuild/cmake/client.py78
-rw-r--r--mesonbuild/cmake/common.py88
-rw-r--r--mesonbuild/cmake/executor.py66
-rw-r--r--mesonbuild/cmake/fileapi.py24
-rw-r--r--mesonbuild/cmake/generator.py3
-rw-r--r--mesonbuild/cmake/interpreter.py369
-rw-r--r--mesonbuild/cmake/traceparser.py77
-rw-r--r--mesonbuild/compilers/c.py6
-rw-r--r--mesonbuild/compilers/compilers.py534
-rw-r--r--mesonbuild/compilers/cpp.py6
-rw-r--r--mesonbuild/compilers/cs.py4
-rw-r--r--mesonbuild/compilers/cuda.py7
-rw-r--r--mesonbuild/compilers/d.py29
-rw-r--r--mesonbuild/compilers/fortran.py8
-rw-r--r--mesonbuild/compilers/java.py4
-rw-r--r--mesonbuild/compilers/mixins/arm.py48
-rw-r--r--mesonbuild/compilers/mixins/c2000.py12
-rw-r--r--mesonbuild/compilers/mixins/ccrx.py16
-rw-r--r--mesonbuild/compilers/mixins/clang.py13
-rw-r--r--mesonbuild/compilers/mixins/clike.py340
-rw-r--r--mesonbuild/compilers/mixins/compcert.py14
-rw-r--r--mesonbuild/compilers/mixins/elbrus.py3
-rw-r--r--mesonbuild/compilers/mixins/emscripten.py15
-rw-r--r--mesonbuild/compilers/mixins/gnu.py38
-rw-r--r--mesonbuild/compilers/mixins/intel.py35
-rw-r--r--mesonbuild/compilers/mixins/islinker.py13
-rw-r--r--mesonbuild/compilers/mixins/pgi.py17
-rw-r--r--mesonbuild/compilers/mixins/visualstudio.py23
-rw-r--r--mesonbuild/compilers/mixins/xc16.py14
-rw-r--r--mesonbuild/compilers/objc.py43
-rw-r--r--mesonbuild/compilers/objcpp.py46
-rw-r--r--mesonbuild/compilers/rust.py11
-rw-r--r--mesonbuild/compilers/swift.py8
-rw-r--r--mesonbuild/compilers/vala.py8
-rw-r--r--mesonbuild/coredata.py5
-rw-r--r--mesonbuild/dependencies/base.py10
-rw-r--r--mesonbuild/environment.py9
-rw-r--r--mesonbuild/linkers.py1
-rw-r--r--mesonbuild/mdist.py2
-rw-r--r--mesonbuild/mesonlib.py2
-rw-r--r--mesonbuild/modules/windows.py9
-rwxr-xr-xrun_mypy.py98
-rwxr-xr-xrun_tests.py2
-rwxr-xr-xrun_unittests.py4
-rw-r--r--test cases/frameworks/1 boost/meson.build4
52 files changed, 1578 insertions, 939 deletions
diff --git a/CODEOWNERS b/CODEOWNERS
index 10fddaf..8ac91ad 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -2,7 +2,7 @@
/mesonbuild/modules/pkgconfig.py @xclaesse
/mesonbuild/modules/cmake.py @mensinda
/mesonbuild/modules/unstable_external_project.py @xclaesse
-/mesonbuild/ast/* @mensinda
-/mesonbuild/cmake/* @mensinda
-/mesonbuild/compilers/* @dcbaker
+/mesonbuild/ast/ @mensinda
+/mesonbuild/cmake/ @mensinda
+/mesonbuild/compilers/ @dcbaker
/mesonbuild/linkers.py @dcbaker
diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson
index e7fe968..f8946de 100644
--- a/data/shell-completions/zsh/_meson
+++ b/data/shell-completions/zsh/_meson
@@ -1,4 +1,4 @@
-#compdef meson mesonconf=meson-configure mesontest=meson-test mesonintrospect=meson-introspect
+#compdef meson
# vim:ts=2 sw=2
@@ -32,13 +32,55 @@ local -i ret
local __meson_backends="(ninja xcode ${(j. .)${:-vs{,2010,2015,2017}}})"
local __meson_build_types="(plain debug debugoptimized minsize release)"
local __meson_wrap_modes="(WrapMode.{default,nofallback,nodownload,forcefallback})"
+local __meson_dist_formats=("xztar" "gztar" "zip")
+local __meson_cd='-C[change into this directory before running]:target dir:_directories'
+local -a __meson_common=(
+ '--prefix=[installation prefix]: :_directories'
+ '--bindir=[executable directory]: :_directories'
+ '--datadir=[data file directory]: :_directories'
+ '--includedir=[header file directory]: :_directories'
+ '--infodir=[info page directory]: :_directories'
+ '--libdir=[library directory]: :_directories'
+ '--libexecdir=[library executable directory]: :_directories'
+ '--localedir=[locale data directory]: :_directories'
+ '--localstatedir=[local state data directory]: :_directories'
+ '--mandir=[manual page directory]: :_directories'
+ '--sbindir=[system executable directory]: :_directories'
+ '--sharedstatedir=[arch-independent data directory]: :_directories'
+ '--sysconfdir=[system configuration directory]: :_directories'
+ '--auto-features=[default value for auto features]:auto features types:(auto disabled enabled)'
+ '--backend=[backend to use]:Meson backend:'"$__meson_backends"
+ '--buildtype=[build type to use]:Meson build type:'"$__meson_build_types"
+ '--debug[turn on building with debug]'
+ '--default-library=[default library type]:default library type:(shared static both)'
+ '--errorlogs[prints the logs from failing tests]'
+ '--install-umask=[default umask for permissions of all installed files]'
+ '--layout=[build directory layout]:build directory layout:(flat mirror)'
+ '--optimization=[optimization level for compiled targets]:optimization:(0 g 1 2 3 s)'
+ '--stdsplit=[split stdout and stderr in test logs]'
+ '--strip[strip targets on install]'
+ '--unity=[unity builds on/off]:whether to do unity builds:(on off subprojects)'
+ '--warnlevel=[compiler warning level]:compiler warning level:warning level:(1 2 3)'
+ '--werror[treat warnings as errors]'
+ '--wrap-mode=[special wrap mode]:wrap mode:'"$__meson_wrap_modes"
+ '--force-fallback-for=[force fallback for listed subprojects]'
+ '--pkg-config-path=[extra paths for HOST pkg-config to search]:paths:_dir_list -s ,'
+ '--build.pkg-config-path=[extra paths for BUILD pkg-config to search]:paths:_dir_list -s ,'
+ '--cmake-prefix-path=[extra prefixes for HOST cmake to search]:paths:_dir_list -s ,'
+ '--build.cmake-prefix-path=[extra prefix for BUILD cmake to search]:paths:_dir_list -s ,'
+)
local -a meson_commands=(
-'setup:set up a build directory'
'configure:configure a project'
-'test:run tests'
+'dist:generate release archive'
+'init:create a new project'
+'install:install one more more targets'
'introspect:query project properties'
+'setup:set up a build directory'
+'test:run tests'
'wrap:manage source dependencies'
+'subprojects:manage subprojects'
+'compile:Build the project'
)
(( $+functions[__meson_is_build_dir] )) || __meson_is_build_dir() {
@@ -68,6 +110,21 @@ local -a meson_commands=(
fi
}
+(( $+functions[__meson_wrap_names] )) || __meson_wrap_names() {
+ local rwraps
+ rwraps="$(_call_program meson meson wrap list)"
+ local -a wraps=(${(@f)rwraps})
+ _describe -t wraps "Meson wraps" wraps
+}
+
+(( $+functions[__meson_installed_wraps] )) || __meson_installed_wraps() {
+ local rwraps
+ if rwraps="$(ls subprojects/ | grep '\.wrap$' | cut -d . -f 1)"; then
+ local -a wraps=(${(@f)rwraps})
+ _describe -t wraps "Meson wraps" wraps
+ fi
+}
+
(( $+functions[_meson_commands] )) || _meson_commands() {
_describe -t commands "Meson subcommands" meson_commands
}
@@ -89,49 +146,30 @@ local -a meson_commands=(
_arguments \
'*-D-[set the value of a build option]:build option:__meson_build_options' \
- '--prefix=[installation prefix]: :_directories' \
- '--libdir=[library directory]: :_directories' \
- '--libexecdir=[library executable directory]: :_directories' \
- '--bindir=[executable directory]: :_directories' \
- '--sbindir=[system executable directory]: :_directories' \
- '--includedir=[header file directory]: :_directories' \
- '--datadir=[data file directory]: :_directories' \
- '--mandir=[manual page directory]: :_directories' \
- '--infodir=[info page directory]: :_directories' \
- '--localedir=[locale data directory]: :_directories' \
- '--sysconfdir=[system configuration directory]: :_directories' \
- '--localstatedir=[local state data directory]: :_directories' \
- '--sharedstatedir=[arch-independent data directory]: :_directories' \
- '--backend=[backend to use]:Meson backend:'"$__meson_backends" \
- '--buildtype=[build type to use]:Meson build type:'"$__meson_build_types" \
- '--strip[strip targets on install]' \
- '--unity=[unity builds on/off]:whether to do unity builds:(on off subprojects)' \
- '--werror[treat warnings as errors]' \
- '--layout=[build directory layout]:build directory layout:(flat mirror)' \
- '--default-library=[default library type]:default library type:(shared static)' \
- '--warnlevel=[compiler warning level]:compiler warning level:warning level:(1 2 3)' \
- '--stdsplit=[split stdout and stderr in test logs]' \
- '--errorlogs=[prints the logs from failing tests]' \
'--cross-file=[cross-compilation environment description]:cross file:_files' \
'--native-file=[build machine compilation environment description]:native file:_files' \
- '--wrap-mode=[special wrap mode]:wrap mode:'"$__meson_wrap_modes" \
+ '--clearcache[clear cached state]' \
+ '--fatal-meson-warnings=[exit when any meson warnings are encountered]' \
+ '(-v --version)'{'-v','--version'}'[print the meson version and exit]' \
+ '--reconfigure=[re-run build configuration]' \
+ '--wipe=[delete saved state and restart using saved command line options]' \
":$firstd directory:_directories" \
"::$secondd directory:_directories" \
- #
+ "${(@)__meson_common}"
}
(( $+functions[_meson-configure] )) || _meson-configure() {
local curcontext="$curcontext"
# TODO: implement 'mesonconf @file'
local -a specs=(
- '--clearcache[clear cached state]'
'*-D-[set the value of a build option]:build option:__meson_build_options'
'::build directory:_directories'
)
_arguments \
'(: -)'{'--help','-h'}'[show a help message and quit]' \
- "${(@)specs}"
+ "${(@)specs}" \
+ "${(@)__meson_common}"
}
(( $+functions[_meson-test] )) || _meson-test() {
@@ -139,22 +177,23 @@ local -a meson_commands=(
# TODO: complete test suites
local -a specs=(
- '(--quiet -q)'{'--quiet','-q'}'[produce less output to the terminal]'
- '(--verbose -v)'{'--verbose','-v'}'[do not redirect stdout and stderr]'
- '(--timeout-multiplier -t)'{'--timeout-multiplier','-t'}'[a multiplier for test timeouts]:Python floating-point number: '
- '-C[directory to cd into]: :_directories'
'--repeat[number of times to run the tests]:number of times to repeat: '
'--no-rebuild[do not rebuild before running tests]'
'--gdb[run tests under gdb]'
+ '--gdb-path=[program to run for gdb (can be wrapper or compaitble program)]:program:_path_commands'
'--list[list available tests]'
'(--wrapper --wrap)'{'--wrapper=','--wrap='}'[wrapper to run tests with]:wrapper program:_path_commands'
- '(--no-suite)--suite[only run tests from this suite]:test suite: '
+ "$__meson_cd"
'(--suite)--no-suite[do not run tests from this suite]:test suite: '
+ '(--no-suite)--suite[only run tests from this suite]:test suite: '
'--no-stdsplit[do not split stderr and stdout in logs]'
'--print-errorlogs[print logs for failing tests]'
'--benchmark[run benchmarks instead of tests]'
'--logbase[base name for log file]:filename: '
'--num-processes[how many threads to use]:number of processes: '
+ '(--verbose -v)'{'--verbose','-v'}'[do not redirect stdout and stderr]'
+ '(--quiet -q)'{'--quiet','-q'}'[produce less output to the terminal]'
+ '(--timeout-multiplier -t)'{'--timeout-multiplier','-t'}'[a multiplier for test timeouts]:Python floating-point number: '
'--setup[which test setup to use]:test setup: '
'--test-args[arguments to pass to the tests]: : '
'*:Meson tests:__meson_test_names'
@@ -165,17 +204,32 @@ local -a meson_commands=(
"${(@)specs}"
}
+(( $+functions[_meson-install] )) || _meson-install() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ "$__meson_cd"
+ '--no-rebuild[Do not rebuild before installing]'
+ '--only-changed[Do not overwrite files that are older than the copied file]'
+ '--quiet[Do not print every fiel that was installed]'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
(( $+functions[_meson-introspect] )) || _meson-introspect() {
local curcontext="$curcontext"
local -a specs=(
- '--targets[list top level targets]'
- '--installed[list all installed files and directories]'
- '--buildsystem-files[list files that belong to the build system]'
- '--buildoptions[list all build options]'
- '--tests[list all unit tests]'
+ '--ast[dump the ASK of the meson file]'
'--benchmarks[list all benchmarks]'
+ '--buildoptions[list all build options]'
+ '--buildsystem-files[list files that belong to the build system]'
'--dependencies[list external dependencies]'
+ '--installed[list all installed files and directories]'
'--projectinfo[show project information]'
+ '--targets[list top level targets]'
+ '--tests[list all unit tests]'
+ '--backend=[backend to use]:Meson backend:'"$__meson_backends"
'::build directory:_directories'
)
_arguments \
@@ -183,8 +237,167 @@ _arguments \
"${(@)specs}"
}
+(( $+functions[_meson-init] )) || _meson-init() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ "$__meson_cd"
+ '(-n --name)'{'-n','--name'}'=[the name of the project (defaults to directory name)]'
+ '(-e --executable)'{'-e','--executable'}'=[the name of the executable target to create (defaults to project name)]'
+ '(-d --deps)'{'-d','--deps'}'=[comma seperated list of dependencies]'
+ '(-l --language)'{'-l','--language'}'=[comma seperated list of languages (autodetected based on sources if unset)]:languages:_values , (c cpp cs cuda d fortran java objc objcpp rust)'
+ '(-b --build)'{'-b','--build'}'[build the project immediately after generation]'
+ '--builddir=[directory for building]:directory:_directories'
+ '(-f --force)'{'-f','--force'}'[overwrite any existing files and directories]'
+ '(-t --type)'{'-t','--type'}'=[project type, defaults to executable]:type:(executable library)'
+ '(-v --version)'{'-v','--version'}'[print the meson version and exit]'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
(( $+functions[_meson-wrap] )) || _meson-wrap() {
- # TODO
+ local -a commands=(
+ 'list:list all available wraps'
+ 'search:search the db by name'
+ 'install:install the specified project'
+ 'update:Update a project to its newest available version'
+ 'info:Show info about a wrap'
+ 'status:Show the status of your subprojects'
+ )
+
+ if (( CURRENT == 2 )); then
+ _describe -t commands "Meson wrap subcommands" commands
+ else
+ local curcontext="$curcontext"
+ cmd="${${commands[(r)$words[2]:*]%%:*}}"
+ if (( $#cmd )); then
+ if [[ $cmd == status ]]; then
+ _message "no options"
+ elif [[ $cmd == "list" ]]; then
+ _arguments '*:meson wraps'
+ elif [[ $cmd == "search" ]]; then
+ _arguments '*:meson wraps'
+ elif [[ $cmd == "install" ]]; then
+ _arguments '*:meson wraps:__meson_wrap_names'
+ elif [[ $cmd == "update" ]]; then
+ _arguments '*:meson wraps:__meson_installed_wraps'
+ elif [[ $cmd == "info" ]]; then
+ _arguments '*:meson wraps:__meson_wrap_name'
+ elif [[ $cmd == "status" ]]; then
+ _arguments '*:'
+ elif [[ $cmd == "promote" ]]; then
+ # TODO: how do you figure out what wraps are provided by subprojects if
+ # they haven't been fetched yet?
+ _arguments '*:'
+ fi
+ else
+ _message "unknown meson wrap command: $words[2]"
+ fi
+ fi
+
+}
+
+(( $+functions[_meson-dist] )) || _meson-dist() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ '--formats=[comma seperated list of archive types to create]:archive formats:_values -s , format '"$__meson_dist_formats"
+ '--include-subprojects[Include source code of subprojects that have been used for the build]'
+ '--no-tests[Do not build and test generated packages]'
+ "$__meson_cd"
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
+(( $+functions[_meson-subprojects-update] )) || _meson-subprojects-update() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ "--rebase[rebase your branch on top of wrap's revision (git only)]"
+ '--sourcedir=[path to source directory]:_directories'
+ '*:subprojects:__meson_installed_wraps'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
+(( $+functions[_meson-subprojects-checkout] )) || _meson-subprojects-checkout() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ '-b[create a new branch]'
+ '--sourcedir=[path to source directory]:_directories'
+ # FIXME: this doesn't work exactly right, but I can't figure it out
+ ':branch name'
+ '*:subprojects:__meson_installed_wraps'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
+(( $+functions[_meson-subprojects-download] )) || _meson-subprojects-download() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ '--sourcedir=[path to source directory]:_directories'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
+(( $+functions[_meson-subprojects-foreach] )) || _meson-subprojects-foreach() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ '--sourcedir=[path to source directory]:_directories'
+ '*:command:_command_names -e'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
+}
+
+(( $+functions[_meson-subprojects] )) || _meson-subprojects() {
+ local -a commands=(
+ 'update:update all subprojects from wrap files'
+ 'checkout:checkout a branch (git only)'
+ 'download:ensure subprojects are fetched, even if not in use. Already downloaded subprojects are not modified.'
+ 'foreach:execute a command in each subproject directory'
+ )
+
+ if (( CURRENT == 2 )); then
+ _describe -t commands "Meson subproject subcommands" commands
+ else
+ local curcontext="$curcontext"
+ cmd="${${commands[(r)$words[2]:*]%%:*}}"
+ if (( $#cmd )); then
+ if [[ $cmd == status ]]; then
+ _message "no options"
+ else
+ _meson-subprojects-$cmd
+ fi
+ else
+ _message "unknown meson subproject command: $words[2]"
+ fi
+ fi
+
+}
+
+(( $+functions[_meson-compile] )) || _meson-compile() {
+ local curcontext="$curcontext"
+ local -a specs=(
+ "$__meson_cd"
+ '--clean[Clean the build directory]'
+ '(-j --jobs)'{'-j','--jobs'}'=[the number fo work jobs to run (if supported)]:_guard "[0-9]#" "number of jobs"'
+ '(-l --load-averate)'{'-l','--load-average'}'=[the system load average to try to maintain (if supported)]:_guard "[0-9]#" "load average"'
+ '(-v --verbose)'{'-v','--verbose'}'[Show more output]'
+ '--ninja-args=[Arguments to pass to ninja (only when using ninja)]'
+ '--vs-args=[Arguments to pass to vs (only when using msbuild)]'
+ )
+_arguments \
+ '(: -)'{'--help','-h'}'[show a help message and quit]' \
+ "${(@)specs}"
}
if [[ $service != meson ]]; then
diff --git a/docs/markdown/Custom-build-targets.md b/docs/markdown/Custom-build-targets.md
index f0b50d8..76bf939 100644
--- a/docs/markdown/Custom-build-targets.md
+++ b/docs/markdown/Custom-build-targets.md
@@ -29,14 +29,15 @@ it does for source generation.
See [Generating Sources](Generating-sources.md) for more information on this topic.
-## Details on compiler invocations
+## Details on command invocation
Meson only permits you to specify one command to run. This is by
design as writing shell pipelines into build definition files leads to
-code that is very hard to maintain. If your compilation requires
+code that is very hard to maintain. If your command requires
multiple steps you need to write a wrapper script that does all the
-necessary work. When doing this you need to be mindful of the
-following issues:
+necessary work.
+
+When doing this you need to be mindful of the following issues:
* do not assume that the command is invoked in any specific directory
* a target called `target` file `outfile` defined in subdir `subdir`
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 9a5ccc5..d315b53 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -397,6 +397,10 @@ the following special string substitutions:
The returned object also has methods that are documented in the
[object methods section](#custom-target-object) below.
+**Note:** Assuming that `command:` is executed by a POSIX `sh` shell is not
+portable, notably to Windows. Instead, consider using a `native: true`
+[executable()](#executable), or a python script.
+
### declare_dependency()
``` meson
diff --git a/docs/markdown/Tutorial.md b/docs/markdown/Tutorial.md
index f108c0c..bf337ba 100644
--- a/docs/markdown/Tutorial.md
+++ b/docs/markdown/Tutorial.md
@@ -75,12 +75,23 @@ When Meson is run it prints the following output.
Now we are ready to build our code.
+
+```console
+$ cd builddir
+$ ninja
```
+
+If your Meson version is newer than 0.55.0, you can use the new
+backend-agnostic build command:
+
+```console
$ cd builddir
$ meson compile
```
-Once that is done we can run the resulting binary.
+For the rest of this document we are going to use the latter form.
+
+Once the executable is built we can run it.
```console
$ ./demo
@@ -135,7 +146,7 @@ need to recreate our build directory, run any sort of magical commands
or the like. Instead we just type the exact same command as if we were
rebuilding our code without any build system changes.
-```
+```console
$ meson compile
```
diff --git a/mesonbuild/ast/introspection.py b/mesonbuild/ast/introspection.py
index f03f7d2..9cfdded 100644
--- a/mesonbuild/ast/introspection.py
+++ b/mesonbuild/ast/introspection.py
@@ -151,14 +151,15 @@ class IntrospectionInterpreter(AstInterpreter):
for for_machine in [MachineChoice.BUILD, MachineChoice.HOST]:
self._add_languages(args, for_machine)
- def _add_languages(self, langs: T.List[TYPE_nvar], for_machine: MachineChoice) -> None:
- langs = self.flatten_args(langs)
+ def _add_languages(self, raw_langs: T.List[TYPE_nvar], for_machine: MachineChoice) -> None:
+ langs = [] # type: T.List[str]
+ for l in self.flatten_args(raw_langs):
+ if isinstance(l, str):
+ langs.append(l)
+ elif isinstance(l, StringNode):
+ langs.append(l.value)
+
for lang in sorted(langs, key=compilers.sort_clink):
- if isinstance(lang, StringNode):
- assert isinstance(lang.value, str)
- lang = lang.value
- if not isinstance(lang, str):
- continue
lang = lang.lower()
if lang not in self.coredata.compilers[for_machine]:
self.environment.detect_compiler_for(lang, for_machine)
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 66fc464..f89b917 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -2647,10 +2647,10 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
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 = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
+ try:
static_patterns = linker.get_library_naming(self.environment, LibType.STATIC, strict=True)
shared_patterns = linker.get_library_naming(self.environment, LibType.SHARED, strict=True)
+ search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
for libname in libs:
# be conservative and record most likely shared and static resolution, because we don't know exactly
# which one the linker will prefer
@@ -2662,6 +2662,9 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
guessed_dependencies.append(staticlibs.resolve().as_posix())
if sharedlibs:
guessed_dependencies.append(sharedlibs.resolve().as_posix())
+ except (mesonlib.MesonException, AttributeError) as e:
+ if 'get_library_naming' not in str(e):
+ raise
return guessed_dependencies + absolute_libs
diff --git a/mesonbuild/cmake/client.py b/mesonbuild/cmake/client.py
index b88a673..2089be4 100644
--- a/mesonbuild/cmake/client.py
+++ b/mesonbuild/cmake/client.py
@@ -17,7 +17,6 @@
from .common import CMakeException, CMakeConfiguration, CMakeBuildFile
from .executor import CMakeExecutor
-from ..environment import Environment
from ..mesonlib import MachineChoice
from .. import mlog
from contextlib import contextmanager
@@ -26,6 +25,9 @@ import typing as T
import json
import os
+if T.TYPE_CHECKING:
+ from ..environment import Environment
+
CMAKE_SERVER_BEGIN_STR = '[== "CMake Server" ==['
CMAKE_SERVER_END_STR = ']== "CMake Server" ==]'
@@ -36,7 +38,7 @@ CMAKE_MESSAGE_TYPES = {
'progress': ['cookie'],
'reply': ['cookie', 'inReplyTo'],
'signal': ['cookie', 'name'],
-}
+} # type: T.Dict[str, T.List[str]]
CMAKE_REPLY_TYPES = {
'handshake': [],
@@ -44,16 +46,16 @@ CMAKE_REPLY_TYPES = {
'compute': [],
'cmakeInputs': ['buildFiles', 'cmakeRootDirectory', 'sourceDirectory'],
'codemodel': ['configurations']
-}
+} # type: T.Dict[str, T.List[str]]
# Base CMake server message classes
class MessageBase:
- def __init__(self, msg_type: str, cookie: str):
+ def __init__(self, msg_type: str, cookie: str) -> None:
self.type = msg_type
self.cookie = cookie
- def to_dict(self) -> dict:
+ def to_dict(self) -> T.Dict[str, T.Union[str, T.List[str], T.Dict[str, int]]]:
return {'type': self.type, 'cookie': self.cookie}
def log(self) -> None:
@@ -62,21 +64,21 @@ class MessageBase:
class RequestBase(MessageBase):
cookie_counter = 0
- def __init__(self, msg_type: str):
+ def __init__(self, msg_type: str) -> None:
super().__init__(msg_type, self.gen_cookie())
@staticmethod
- def gen_cookie():
+ def gen_cookie() -> str:
RequestBase.cookie_counter += 1
return 'meson_{}'.format(RequestBase.cookie_counter)
class ReplyBase(MessageBase):
- def __init__(self, cookie: str, in_reply_to: str):
+ def __init__(self, cookie: str, in_reply_to: str) -> None:
super().__init__('reply', cookie)
self.in_reply_to = in_reply_to
class SignalBase(MessageBase):
- def __init__(self, cookie: str, signal_name: str):
+ def __init__(self, cookie: str, signal_name: str) -> None:
super().__init__('signal', cookie)
self.signal_name = signal_name
@@ -86,7 +88,7 @@ class SignalBase(MessageBase):
# Special Message classes
class Error(MessageBase):
- def __init__(self, cookie: str, message: str):
+ def __init__(self, cookie: str, message: str) -> None:
super().__init__('error', cookie)
self.message = message
@@ -94,7 +96,7 @@ class Error(MessageBase):
mlog.error(mlog.bold('CMake server error:'), mlog.red(self.message))
class Message(MessageBase):
- def __init__(self, cookie: str, message: str):
+ def __init__(self, cookie: str, message: str) -> None:
super().__init__('message', cookie)
self.message = message
@@ -103,19 +105,21 @@ class Message(MessageBase):
pass
class Progress(MessageBase):
- def __init__(self, cookie: str):
+ def __init__(self, cookie: str) -> None:
super().__init__('progress', cookie)
def log(self) -> None:
pass
class MessageHello(MessageBase):
- def __init__(self, supported_protocol_versions: T.List[dict]):
+ def __init__(self, supported_protocol_versions: T.List[T.Dict[str, int]]) -> None:
super().__init__('hello', '')
self.supported_protocol_versions = supported_protocol_versions
def supports(self, major: int, minor: T.Optional[int] = None) -> bool:
for i in self.supported_protocol_versions:
+ assert 'major' in i
+ assert 'minor' in i
if major == i['major']:
if minor is None or minor == i['minor']:
return True
@@ -124,7 +128,7 @@ class MessageHello(MessageBase):
# Request classes
class RequestHandShake(RequestBase):
- def __init__(self, src_dir: str, build_dir: str, generator: str, vers_major: int, vers_minor: T.Optional[int] = None):
+ def __init__(self, src_dir: str, build_dir: str, generator: str, vers_major: int, vers_minor: T.Optional[int] = None) -> None:
super().__init__('handshake')
self.src_dir = src_dir
self.build_dir = build_dir
@@ -132,7 +136,7 @@ class RequestHandShake(RequestBase):
self.vers_major = vers_major
self.vers_minor = vers_minor
- def to_dict(self) -> dict:
+ def to_dict(self) -> T.Dict[str, T.Union[str, T.List[str], T.Dict[str, int]]]:
vers = {'major': self.vers_major}
if self.vers_minor is not None:
vers['minor'] = self.vers_minor
@@ -154,40 +158,40 @@ class RequestConfigure(RequestBase):
super().__init__('configure')
self.args = args
- def to_dict(self) -> dict:
+ def to_dict(self) -> T.Dict[str, T.Union[str, T.List[str], T.Dict[str, int]]]:
res = super().to_dict()
if self.args:
res['cacheArguments'] = self.args
return res
class RequestCompute(RequestBase):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('compute')
class RequestCMakeInputs(RequestBase):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('cmakeInputs')
class RequestCodeModel(RequestBase):
- def __init__(self):
+ def __init__(self) -> None:
super().__init__('codemodel')
# Reply classes
class ReplyHandShake(ReplyBase):
- def __init__(self, cookie: str):
+ def __init__(self, cookie: str) -> None:
super().__init__(cookie, 'handshake')
class ReplyConfigure(ReplyBase):
- def __init__(self, cookie: str):
+ def __init__(self, cookie: str) -> None:
super().__init__(cookie, 'configure')
class ReplyCompute(ReplyBase):
- def __init__(self, cookie: str):
+ def __init__(self, cookie: str) -> None:
super().__init__(cookie, 'compute')
class ReplyCMakeInputs(ReplyBase):
- def __init__(self, cookie: str, cmake_root: str, src_dir: str, build_files: T.List[CMakeBuildFile]):
+ def __init__(self, cookie: str, cmake_root: str, src_dir: str, build_files: T.List[CMakeBuildFile]) -> None:
super().__init__(cookie, 'cmakeInputs')
self.cmake_root = cmake_root
self.src_dir = src_dir
@@ -202,7 +206,7 @@ class ReplyCMakeInputs(ReplyBase):
mlog.log(str(i))
class ReplyCodeModel(ReplyBase):
- def __init__(self, data: dict):
+ def __init__(self, data: T.Dict[str, T.Any]) -> None:
super().__init__(data['cookie'], 'codemodel')
self.configs = []
for i in data['configurations']:
@@ -218,9 +222,9 @@ class ReplyCodeModel(ReplyBase):
# Main client class
class CMakeClient:
- def __init__(self, env: Environment):
+ def __init__(self, env: 'Environment') -> None:
self.env = env
- self.proc = None
+ self.proc = None # type: T.Optional[Popen]
self.type_map = {
'error': lambda data: Error(data['cookie'], data['errorMessage']),
'hello': lambda data: MessageHello(data['supportedProtocolVersions']),
@@ -228,7 +232,7 @@ class CMakeClient:
'progress': lambda data: Progress(data['cookie']),
'reply': self.resolve_type_reply,
'signal': lambda data: SignalBase(data['cookie'], data['name'])
- }
+ } # type: T.Dict[str, T.Callable[[T.Dict[str, T.Any]], MessageBase]]
self.reply_map = {
'handshake': lambda data: ReplyHandShake(data['cookie']),
@@ -236,10 +240,10 @@ class CMakeClient:
'compute': lambda data: ReplyCompute(data['cookie']),
'cmakeInputs': self.resolve_reply_cmakeInputs,
'codemodel': lambda data: ReplyCodeModel(data),
- }
+ } # type: T.Dict[str, T.Callable[[T.Dict[str, T.Any]], ReplyBase]]
- def readMessageRaw(self) -> dict:
- assert(self.proc is not None)
+ def readMessageRaw(self) -> T.Dict[str, T.Any]:
+ assert self.proc is not None
rawData = []
begin = False
while self.proc.poll() is None:
@@ -257,7 +261,11 @@ class CMakeClient:
begin = True # Begin of the message
if rawData:
- return json.loads('\n'.join(rawData))
+ res = json.loads('\n'.join(rawData))
+ assert isinstance(res, dict)
+ for i in res.keys():
+ assert isinstance(i, str)
+ return res
raise CMakeException('Failed to read data from the CMake server')
def readMessage(self) -> MessageBase:
@@ -287,7 +295,7 @@ class CMakeClient:
reply.log()
- def query_checked(self, request: RequestBase, message: str) -> ReplyBase:
+ def query_checked(self, request: RequestBase, message: str) -> MessageBase:
reply = self.query(request)
h = mlog.green('SUCCEEDED') if reply.type == 'reply' else mlog.red('FAILED')
mlog.log(message + ':', h)
@@ -305,7 +313,7 @@ class CMakeClient:
request = RequestHandShake(src_dir, build_dir, generator, vers_major, vers_minor)
self.query_checked(request, 'CMake server handshake')
- def resolve_type_reply(self, data: dict) -> ReplyBase:
+ def resolve_type_reply(self, data: T.Dict[str, T.Any]) -> ReplyBase:
reply_type = data['inReplyTo']
func = self.reply_map.get(reply_type, None)
if not func:
@@ -315,7 +323,7 @@ class CMakeClient:
raise CMakeException('Key "{}" is missing from CMake server message type {}'.format(i, type))
return func(data)
- def resolve_reply_cmakeInputs(self, data: dict) -> ReplyCMakeInputs:
+ def resolve_reply_cmakeInputs(self, data: T.Dict[str, T.Any]) -> ReplyCMakeInputs:
files = []
for i in data['buildFiles']:
for j in i['sources']:
@@ -323,7 +331,7 @@ class CMakeClient:
return ReplyCMakeInputs(data['cookie'], data['cmakeRootDirectory'], data['sourceDirectory'], files)
@contextmanager
- def connect(self):
+ def connect(self) -> T.Generator[None, None, None]:
self.startup()
try:
yield
diff --git a/mesonbuild/cmake/common.py b/mesonbuild/cmake/common.py
index 4510b5d..f6bd944 100644
--- a/mesonbuild/cmake/common.py
+++ b/mesonbuild/cmake/common.py
@@ -23,12 +23,12 @@ class CMakeException(MesonException):
pass
class CMakeBuildFile:
- def __init__(self, file: str, is_cmake: bool, is_temp: bool):
+ def __init__(self, file: str, is_cmake: bool, is_temp: bool) -> None:
self.file = file
self.is_cmake = is_cmake
self.is_temp = is_temp
- def __repr__(self):
+ def __repr__(self) -> str:
return '<{}: {}; cmake={}; temp={}>'.format(self.__class__.__name__, self.file, self.is_cmake, self.is_temp)
def _flags_to_list(raw: str) -> T.List[str]:
@@ -80,29 +80,37 @@ def cmake_defines_to_args(raw: T.Any, permissive: bool = False) -> T.List[str]:
return res
+class CMakeInclude:
+ def __init__(self, path: str, isSystem: bool = False):
+ self.path = path
+ self.isSystem = isSystem
+
+ def __repr__(self) -> str:
+ return '<CMakeInclude: {} -- isSystem = {}>'.format(self.path, self.isSystem)
+
class CMakeFileGroup:
- def __init__(self, data: dict):
- self.defines = data.get('defines', '')
- self.flags = _flags_to_list(data.get('compileFlags', ''))
- self.includes = data.get('includePath', [])
- self.is_generated = data.get('isGenerated', False)
- self.language = data.get('language', 'C')
- self.sources = data.get('sources', [])
+ def __init__(self, data: T.Dict[str, T.Any]) -> None:
+ self.defines = data.get('defines', '') # type: str
+ self.flags = _flags_to_list(data.get('compileFlags', '')) # type: T.List[str]
+ self.is_generated = data.get('isGenerated', False) # type: bool
+ self.language = data.get('language', 'C') # type: str
+ self.sources = data.get('sources', []) # type: T.List[str]
# Fix the include directories
- tmp = []
- for i in self.includes:
+ self.includes = [] # type: T.List[CMakeInclude]
+ for i in data.get('includePath', []):
if isinstance(i, dict) and 'path' in i:
- i['isSystem'] = i.get('isSystem', False)
- tmp += [i]
+ isSystem = i.get('isSystem', False)
+ assert isinstance(isSystem, bool)
+ assert isinstance(i['path'], str)
+ self.includes += [CMakeInclude(i['path'], isSystem)]
elif isinstance(i, str):
- tmp += [{'path': i, 'isSystem': False}]
- self.includes = tmp
+ self.includes += [CMakeInclude(i)]
def log(self) -> None:
mlog.log('flags =', mlog.bold(', '.join(self.flags)))
mlog.log('defines =', mlog.bold(', '.join(self.defines)))
- mlog.log('includes =', mlog.bold(', '.join(self.includes)))
+ mlog.log('includes =', mlog.bold(', '.join([str(x) for x in self.includes])))
mlog.log('is_generated =', mlog.bold('true' if self.is_generated else 'false'))
mlog.log('language =', mlog.bold(self.language))
mlog.log('sources:')
@@ -111,22 +119,22 @@ class CMakeFileGroup:
mlog.log(i)
class CMakeTarget:
- def __init__(self, data: dict):
- self.artifacts = data.get('artifacts', [])
- self.src_dir = data.get('sourceDirectory', '')
- self.build_dir = data.get('buildDirectory', '')
- self.name = data.get('name', '')
- self.full_name = data.get('fullName', '')
- self.install = data.get('hasInstallRule', False)
- self.install_paths = list(set(data.get('installPaths', [])))
- self.link_lang = data.get('linkerLanguage', '')
- self.link_libraries = _flags_to_list(data.get('linkLibraries', ''))
- self.link_flags = _flags_to_list(data.get('linkFlags', ''))
- self.link_lang_flags = _flags_to_list(data.get('linkLanguageFlags', ''))
- # self.link_path = data.get('linkPath', '')
- self.type = data.get('type', 'EXECUTABLE')
- # self.is_generator_provided = data.get('isGeneratorProvided', False)
- self.files = []
+ def __init__(self, data: T.Dict[str, T.Any]) -> None:
+ self.artifacts = data.get('artifacts', []) # type: T.List[str]
+ self.src_dir = data.get('sourceDirectory', '') # type: str
+ self.build_dir = data.get('buildDirectory', '') # type: str
+ self.name = data.get('name', '') # type: str
+ self.full_name = data.get('fullName', '') # type: str
+ self.install = data.get('hasInstallRule', False) # type: bool
+ self.install_paths = list(set(data.get('installPaths', []))) # type: T.List[str]
+ self.link_lang = data.get('linkerLanguage', '') # type: str
+ self.link_libraries = _flags_to_list(data.get('linkLibraries', '')) # type: T.List[str]
+ self.link_flags = _flags_to_list(data.get('linkFlags', '')) # type: T.List[str]
+ self.link_lang_flags = _flags_to_list(data.get('linkLanguageFlags', '')) # type: T.List[str]
+ # self.link_path = data.get('linkPath', '') # type: str
+ self.type = data.get('type', 'EXECUTABLE') # type: str
+ # self.is_generator_provided = data.get('isGeneratorProvided', False) # type: bool
+ self.files = [] # type: T.List[CMakeFileGroup]
for i in data.get('fileGroups', []):
self.files += [CMakeFileGroup(i)]
@@ -152,11 +160,11 @@ class CMakeTarget:
i.log()
class CMakeProject:
- def __init__(self, data: dict):
- self.src_dir = data.get('sourceDirectory', '')
- self.build_dir = data.get('buildDirectory', '')
- self.name = data.get('name', '')
- self.targets = []
+ def __init__(self, data: T.Dict[str, T.Any]) -> None:
+ self.src_dir = data.get('sourceDirectory', '') # type: str
+ self.build_dir = data.get('buildDirectory', '') # type: str
+ self.name = data.get('name', '') # type: str
+ self.targets = [] # type: T.List[CMakeTarget]
for i in data.get('targets', []):
self.targets += [CMakeTarget(i)]
@@ -171,9 +179,9 @@ class CMakeProject:
i.log()
class CMakeConfiguration:
- def __init__(self, data: dict):
- self.name = data.get('name', '')
- self.projects = []
+ def __init__(self, data: T.Dict[str, T.Any]) -> None:
+ self.name = data.get('name', '') # type: str
+ self.projects = [] # type: T.List[CMakeProject]
for i in data.get('projects', []):
self.projects += [CMakeProject(i)]
diff --git a/mesonbuild/cmake/executor.py b/mesonbuild/cmake/executor.py
index 2226c02..d3588f0 100644
--- a/mesonbuild/cmake/executor.py
+++ b/mesonbuild/cmake/executor.py
@@ -38,7 +38,8 @@ if T.TYPE_CHECKING:
from ..dependencies.base import ExternalProgram
from ..compilers import Compiler
-TYPE_result = T.Tuple[int, T.Optional[str], T.Optional[str]]
+TYPE_result = T.Tuple[int, T.Optional[str], T.Optional[str]]
+TYPE_cache_key = T.Tuple[str, T.Tuple[str, ...], str, T.FrozenSet[T.Tuple[str, str]]]
_MESON_TO_CMAKE_MAPPING = {
'arm': 'ARMCC',
@@ -76,8 +77,8 @@ def meson_compiler_to_cmake_id(cobj: 'Compiler') -> str:
class CMakeExecutor:
# The class's copy of the CMake path. Avoids having to search for it
# multiple times in the same Meson invocation.
- class_cmakebin = PerMachine(None, None)
- class_cmakevers = PerMachine(None, None)
+ class_cmakebin = PerMachine(None, None) # type: PerMachine[T.Optional[ExternalProgram]]
+ class_cmakevers = PerMachine(None, None) # type: PerMachine[T.Optional[str]]
class_cmake_cache = {} # type: T.Dict[T.Any, TYPE_result]
def __init__(self, environment: Environment, version: str, for_machine: MachineChoice, silent: bool = False):
@@ -89,8 +90,8 @@ class CMakeExecutor:
self.print_cmout = False
self.prefix_paths = [] # type: T.List[str]
self.extra_cmake_args = [] # type: T.List[str]
- if self.cmakebin is False:
- self.cmakebin = None
+
+ if self.cmakebin is None:
return
if not version_compare(self.cmakevers, self.min_version):
@@ -102,17 +103,18 @@ class CMakeExecutor:
return
self.prefix_paths = self.environment.coredata.builtins_per_machine[self.for_machine]['cmake_prefix_path'].value
- env_pref_path = get_env_var(
+ env_pref_path_raw = get_env_var(
self.for_machine,
self.environment.is_cross_build(),
'CMAKE_PREFIX_PATH')
- if env_pref_path is not None:
+ if env_pref_path_raw is not None:
+ env_pref_path = [] # type: T.List[str]
if mesonlib.is_windows():
# Cannot split on ':' on Windows because its in the drive letter
- env_pref_path = env_pref_path.split(os.pathsep)
+ env_pref_path = env_pref_path_raw.split(os.pathsep)
else:
# https://github.com/mesonbuild/meson/issues/7294
- env_pref_path = re.split(r':|;', env_pref_path)
+ env_pref_path = re.split(r':|;', env_pref_path_raw)
env_pref_path = [x for x in env_pref_path if x] # Filter out empty strings
if not self.prefix_paths:
self.prefix_paths = []
@@ -121,13 +123,14 @@ class CMakeExecutor:
if self.prefix_paths:
self.extra_cmake_args += ['-DCMAKE_PREFIX_PATH={}'.format(';'.join(self.prefix_paths))]
- def find_cmake_binary(self, environment: Environment, silent: bool = False) -> T.Tuple['ExternalProgram', str]:
- from ..dependencies.base import find_external_program
+ def find_cmake_binary(self, environment: Environment, silent: bool = False) -> T.Tuple[T.Optional['ExternalProgram'], T.Optional[str]]:
+ from ..dependencies.base import find_external_program, NonExistingExternalProgram
# Only search for CMake the first time and store the result in the class
# definition
- if CMakeExecutor.class_cmakebin[self.for_machine] is False:
+ if isinstance(CMakeExecutor.class_cmakebin[self.for_machine], NonExistingExternalProgram):
mlog.debug('CMake binary for %s is cached as not found' % self.for_machine)
+ return None, None
elif CMakeExecutor.class_cmakebin[self.for_machine] is not None:
mlog.debug('CMake binary for %s is cached.' % self.for_machine)
else:
@@ -142,7 +145,7 @@ class CMakeExecutor:
continue
if not silent:
mlog.log('Found CMake:', mlog.bold(potential_cmakebin.get_path()),
- '(%s)' % version_if_ok)
+ '({})'.format(version_if_ok))
CMakeExecutor.class_cmakebin[self.for_machine] = potential_cmakebin
CMakeExecutor.class_cmakevers[self.for_machine] = version_if_ok
break
@@ -151,8 +154,9 @@ class CMakeExecutor:
mlog.log('Found CMake:', mlog.red('NO'))
# Set to False instead of None to signify that we've already
# searched for it and not found it
- CMakeExecutor.class_cmakebin[self.for_machine] = False
+ CMakeExecutor.class_cmakebin[self.for_machine] = NonExistingExternalProgram()
CMakeExecutor.class_cmakevers[self.for_machine] = None
+ return None, None
return CMakeExecutor.class_cmakebin[self.for_machine], CMakeExecutor.class_cmakevers[self.for_machine]
@@ -185,19 +189,19 @@ class CMakeExecutor:
if always_capture_stderr is not None:
self.always_capture_stderr = always_capture_stderr
- def _cache_key(self, args: T.List[str], build_dir: str, env):
- fenv = frozenset(env.items()) if env is not None else None
+ def _cache_key(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_cache_key:
+ fenv = frozenset(env.items()) if env is not None else frozenset()
targs = tuple(args)
- return (self.cmakebin, targs, build_dir, fenv)
+ return (self.cmakebin.get_path(), targs, build_dir, fenv)
- def _call_cmout_stderr(self, args: T.List[str], build_dir: str, env) -> TYPE_result:
+ def _call_cmout_stderr(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
cmd = self.cmakebin.get_command() + args
proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.PIPE, cwd=build_dir, env=env)
# stdout and stderr MUST be read at the same time to avoid pipe
# blocking issues. The easiest way to do this is with a separate
# thread for one of the pipes.
- def print_stdout():
+ def print_stdout() -> None:
while True:
line = proc.stdout.readline()
if not line:
@@ -214,10 +218,10 @@ class CMakeExecutor:
tline_start_reg = re.compile(r'^\s*(.*\.(cmake|txt))\(([0-9]+)\):\s*(\w+)\(.*$')
inside_multiline_trace = False
while True:
- line = proc.stderr.readline()
- if not line:
+ line_raw = proc.stderr.readline()
+ if not line_raw:
break
- line = line.decode(errors='ignore')
+ line = line_raw.decode(errors='ignore')
if tline_start_reg.match(line):
raw_trace += line
inside_multiline_trace = not line.endswith(' )\n')
@@ -233,7 +237,7 @@ class CMakeExecutor:
return proc.returncode, None, raw_trace
- def _call_cmout(self, args: T.List[str], build_dir: str, env) -> TYPE_result:
+ def _call_cmout(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
cmd = self.cmakebin.get_command() + args
proc = S.Popen(cmd, stdout=S.PIPE, stderr=S.STDOUT, cwd=build_dir, env=env)
while True:
@@ -245,7 +249,7 @@ class CMakeExecutor:
proc.wait()
return proc.returncode, None, None
- def _call_quiet(self, args: T.List[str], build_dir: str, env) -> TYPE_result:
+ def _call_quiet(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
os.makedirs(build_dir, exist_ok=True)
cmd = self.cmakebin.get_command() + args
ret = S.run(cmd, env=env, cwd=build_dir, close_fds=False,
@@ -257,7 +261,7 @@ class CMakeExecutor:
mlog.debug("Called `{}` in {} -> {}".format(call, build_dir, rc))
return rc, out, err
- def _call_impl(self, args: T.List[str], build_dir: str, env) -> TYPE_result:
+ def _call_impl(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]]) -> TYPE_result:
if not self.print_cmout:
return self._call_quiet(args, build_dir, env)
else:
@@ -266,9 +270,9 @@ class CMakeExecutor:
else:
return self._call_cmout(args, build_dir, env)
- def call(self, args: T.List[str], build_dir: str, env=None, disable_cache: bool = False) -> TYPE_result:
+ def call(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]] = None, disable_cache: bool = False) -> TYPE_result:
if env is None:
- env = os.environ
+ env = os.environ.copy()
args = args + self.extra_cmake_args
if disable_cache:
@@ -281,7 +285,7 @@ class CMakeExecutor:
cache[key] = self._call_impl(args, build_dir, env)
return cache[key]
- def call_with_fake_build(self, args: T.List[str], build_dir: str, env=None) -> TYPE_result:
+ def call_with_fake_build(self, args: T.List[str], build_dir: str, env: T.Optional[T.Dict[str, str]] = None) -> TYPE_result:
# First check the cache
cache = CMakeExecutor.class_cmake_cache
key = self._cache_key(args, build_dir, env)
@@ -369,7 +373,7 @@ class CMakeExecutor:
set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
set(CMAKE_SIZEOF_VOID_P "{}")
'''.format(c_comp, c_launcher, is_gnu, c_id, c_version,
- ctypes.sizeof(ctypes.c_voidp))))
+ ctypes.sizeof(ctypes.c_void_p))))
if cxx_comp and not cxx_comp_file.is_file():
is_gnu = '1' if cxx_id == 'GNU' else ''
@@ -388,7 +392,7 @@ class CMakeExecutor:
set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS C;M;c++;cc;cpp;cxx;mm;CPP)
set(CMAKE_SIZEOF_VOID_P "{}")
'''.format(cxx_comp, cxx_launcher, is_gnu, cxx_id, cxx_version,
- ctypes.sizeof(ctypes.c_voidp))))
+ ctypes.sizeof(ctypes.c_void_p))))
if fortran_comp and not fortran_comp_file.is_file():
fortran_comp_file.write_text(textwrap.dedent('''\
@@ -403,7 +407,7 @@ class CMakeExecutor:
set(CMAKE_Fortran_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
set(CMAKE_Fortran_SOURCE_FILE_EXTENSIONS f;F;fpp;FPP;f77;F77;f90;F90;for;For;FOR;f95;F95)
set(CMAKE_SIZEOF_VOID_P "{}")
- '''.format(fortran_comp, fortran_launcher, ctypes.sizeof(ctypes.c_voidp))))
+ '''.format(fortran_comp, fortran_launcher, ctypes.sizeof(ctypes.c_void_p))))
return self.call(args, build_dir, env)
diff --git a/mesonbuild/cmake/fileapi.py b/mesonbuild/cmake/fileapi.py
index f219f16..0405145 100644
--- a/mesonbuild/cmake/fileapi.py
+++ b/mesonbuild/cmake/fileapi.py
@@ -27,8 +27,8 @@ class CMakeFileAPI:
self.api_base_dir = os.path.join(self.build_dir, '.cmake', 'api', 'v1')
self.request_dir = os.path.join(self.api_base_dir, 'query', 'client-meson')
self.reply_dir = os.path.join(self.api_base_dir, 'reply')
- self.cmake_sources = []
- self.cmake_configurations = []
+ self.cmake_sources = [] # type: T.List[CMakeBuildFile]
+ self.cmake_configurations = [] # type: T.List[CMakeConfiguration]
self.kind_resolver_map = {
'codemodel': self._parse_codemodel,
'cmakeFiles': self._parse_cmakeFiles,
@@ -87,7 +87,7 @@ class CMakeFileAPI:
self.kind_resolver_map[i['kind']](i)
- def _parse_codemodel(self, data: dict) -> None:
+ def _parse_codemodel(self, data: T.Dict[str, T.Any]) -> None:
assert('configurations' in data)
assert('paths' in data)
@@ -100,7 +100,7 @@ class CMakeFileAPI:
# resolved and the resulting data structure is identical
# to the CMake serve output.
- def helper_parse_dir(dir_entry: dict) -> T.Tuple[str, str]:
+ def helper_parse_dir(dir_entry: T.Dict[str, T.Any]) -> T.Tuple[str, str]:
src_dir = dir_entry.get('source', '.')
bld_dir = dir_entry.get('build', '.')
src_dir = src_dir if os.path.isabs(src_dir) else os.path.join(source_dir, src_dir)
@@ -110,7 +110,7 @@ class CMakeFileAPI:
return src_dir, bld_dir
- def parse_sources(comp_group: dict, tgt: dict) -> T.Tuple[T.List[str], T.List[str], T.List[int]]:
+ def parse_sources(comp_group: T.Dict[str, T.Any], tgt: T.Dict[str, T.Any]) -> T.Tuple[T.List[str], T.List[str], T.List[int]]:
gen = []
src = []
idx = []
@@ -127,7 +127,7 @@ class CMakeFileAPI:
return src, gen, idx
- def parse_target(tgt: dict) -> dict:
+ def parse_target(tgt: T.Dict[str, T.Any]) -> T.Dict[str, T.Any]:
src_dir, bld_dir = helper_parse_dir(cnf.get('paths', {}))
# Parse install paths (if present)
@@ -230,7 +230,7 @@ class CMakeFileAPI:
}]
return tgt_data
- def parse_project(pro: dict) -> dict:
+ def parse_project(pro: T.Dict[str, T.Any]) -> T.Dict[str, T.Any]:
# Only look at the first directory specified in directoryIndexes
# TODO Figure out what the other indexes are there for
p_src_dir = source_dir
@@ -268,7 +268,7 @@ class CMakeFileAPI:
self.cmake_configurations += [CMakeConfiguration(cnf_data)]
- def _parse_cmakeFiles(self, data: dict) -> None:
+ def _parse_cmakeFiles(self, data: T.Dict[str, T.Any]) -> None:
assert('inputs' in data)
assert('paths' in data)
@@ -309,10 +309,14 @@ class CMakeFileAPI:
return data
- def _reply_file_content(self, filename: str) -> dict:
+ def _reply_file_content(self, filename: str) -> T.Dict[str, T.Any]:
real_path = os.path.join(self.reply_dir, filename)
if not os.path.exists(real_path):
raise CMakeException('File "{}" does not exist'.format(real_path))
with open(real_path, 'r') as fp:
- return json.load(fp)
+ data = json.load(fp)
+ assert isinstance(data, dict)
+ for i in data.keys():
+ assert isinstance(i, str)
+ return data
diff --git a/mesonbuild/cmake/generator.py b/mesonbuild/cmake/generator.py
index a30d2de..02d56cc 100644
--- a/mesonbuild/cmake/generator.py
+++ b/mesonbuild/cmake/generator.py
@@ -13,6 +13,7 @@
# limitations under the License.
from .. import mesonlib
+import typing as T
def parse_generator_expressions(raw: str) -> str:
'''Parse CMake generator expressions
@@ -73,7 +74,7 @@ def parse_generator_expressions(raw: str) -> str:
'ANGLE-R': lambda x: '>',
'COMMA': lambda x: ',',
'SEMICOLON': lambda x: ';',
- }
+ } # type: T.Dict[str, T.Callable[[str], str]]
# Recursively evaluate generator expressions
def eval_generator_expressions() -> str:
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 2fdb328..1a7fe7a 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -15,13 +15,12 @@
# This class contains the basic functionality needed to run any interpreter
# or an interpreter-based tool.
-from .common import CMakeException, CMakeTarget, TargetOptions
-from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel
+from .common import CMakeException, CMakeTarget, TargetOptions, CMakeConfiguration
+from .client import CMakeClient, RequestCMakeInputs, RequestConfigure, RequestCompute, RequestCodeModel, ReplyCMakeInputs, ReplyCodeModel
from .fileapi import CMakeFileAPI
from .executor import CMakeExecutor
from .traceparser import CMakeTraceParser, CMakeGeneratorTarget
from .. import mlog, mesonlib
-from ..environment import Environment
from ..mesonlib import MachineChoice, OrderedSet, version_compare
from ..mesondata import mesondata
from ..compilers.compilers import lang_suffixes, header_suffixes, obj_suffixes, lib_suffixes, is_header
@@ -51,6 +50,11 @@ from ..mparser import (
if T.TYPE_CHECKING:
from ..build import Build
from ..backend.backends import Backend
+ from ..environment import Environment
+
+TYPE_mixed = T.Union[str, int, bool, BaseNode]
+TYPE_mixed_list = T.Union[TYPE_mixed, T.Sequence[TYPE_mixed]]
+TYPE_mixed_kwargs = T.Dict[str, TYPE_mixed_list]
# Disable all warnings automaticall enabled with --trace and friends
# See https://cmake.org/cmake/help/latest/variable/CMAKE_POLICY_WARNING_CMPNNNN.html
@@ -138,7 +142,7 @@ class OutputTargetMap:
rm_so_version = re.compile(r'(\.[0-9]+)+$')
def __init__(self, build_dir: str):
- self.tgt_map = {}
+ self.tgt_map = {} # type: T.Dict[str, T.Union['ConverterTarget', 'ConverterCustomTarget']]
self.build_dir = build_dir
def add(self, tgt: T.Union['ConverterTarget', 'ConverterCustomTarget']) -> None:
@@ -185,8 +189,10 @@ class OutputTargetMap:
keys += [self._rel_artifact_key(i), os.path.basename(i), self._base_artifact_key(i)]
return self._return_first_valid_key(keys)
- def generated(self, name: str) -> T.Optional[T.Union['ConverterTarget', 'ConverterCustomTarget']]:
- return self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)])
+ def generated(self, name: str) -> T.Optional['ConverterCustomTarget']:
+ res = self._return_first_valid_key([self._rel_generated_file_key(name), self._base_generated_file_key(name)])
+ assert res is None or isinstance(res, ConverterCustomTarget)
+ return res
# Utility functions to generate local keys
def _rel_path(self, fname: str) -> T.Optional[str]:
@@ -213,42 +219,44 @@ class OutputTargetMap:
return '__art_{}__'.format(os.path.basename(fname))
class ConverterTarget:
- def __init__(self, target: CMakeTarget, env: Environment):
- self.env = env
- self.artifacts = target.artifacts
- self.src_dir = target.src_dir
- self.build_dir = target.build_dir
- self.name = target.name
- self.cmake_name = target.name
- self.full_name = target.full_name
- self.type = target.type
- self.install = target.install
- self.install_dir = ''
+ def __init__(self, target: CMakeTarget, env: 'Environment') -> None:
+ self.env = env
+ self.artifacts = target.artifacts
+ self.src_dir = target.src_dir
+ self.build_dir = target.build_dir
+ self.name = target.name
+ self.cmake_name = target.name
+ self.full_name = target.full_name
+ self.type = target.type
+ self.install = target.install
+ self.install_dir = ''
self.link_libraries = target.link_libraries
- self.link_flags = target.link_flags + target.link_lang_flags
- self.depends_raw = []
- self.depends = []
+ self.link_flags = target.link_flags + target.link_lang_flags
+ self.depends_raw = [] # type: T.List[str]
+ self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
if target.install_paths:
self.install_dir = target.install_paths[0]
- self.languages = []
- self.sources = []
- self.generated = []
- self.includes = []
- self.sys_includes = []
- self.link_with = []
- self.object_libs = []
- self.compile_opts = {}
- self.public_compile_opts = []
- self.pie = False
+ self.languages = [] # type: T.List[str]
+ self.sources = [] # type: T.List[str]
+ self.generated = [] # type: T.List[str]
+ self.generated_ctgt = [] # type: T.List[CustomTargetReference]
+ self.includes = [] # type: T.List[str]
+ self.sys_includes = [] # type: T.List[str]
+ self.link_with = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
+ self.object_libs = [] # type: T.List[ConverterTarget]
+ self.compile_opts = {} # type: T.Dict[str, T.List[str]]
+ self.public_compile_opts = [] # type: T.List[str]
+ self.pie = False
# Project default override options (c_std, cpp_std, etc.)
- self.override_options = []
+ self.override_options = [] # type: T.List[str]
# Convert the target name to a valid meson target name
self.name = _sanitize_cmake_name(self.name)
+ self.generated_raw = [] # type: T.List[str]
for i in target.files:
# Determine the meson language
lang_cmake_to_meson = {val.lower(): key for key, val in language_map.items()}
@@ -264,12 +272,12 @@ class ConverterTarget:
self.compile_opts[lang] += [x for x in args if x not in self.compile_opts[lang]]
# Handle include directories
- self.includes += [x['path'] for x in i.includes if x not in self.includes and not x['isSystem']]
- self.sys_includes += [x['path'] for x in i.includes if x not in self.sys_includes and x['isSystem']]
+ self.includes += [x.path for x in i.includes if x.path not in self.includes and not x.isSystem]
+ self.sys_includes += [x.path for x in i.includes if x.path not in self.sys_includes and x.isSystem]
# Add sources to the right array
if i.is_generated:
- self.generated += i.sources
+ self.generated_raw += i.sources
else:
self.sources += i.sources
@@ -306,7 +314,7 @@ class ConverterTarget:
# Sometimes projects pass generated source files as compiler
# flags. Add these as generated sources to ensure that the
# corresponding custom target is run.2
- self.generated += [j]
+ self.generated_raw += [j]
temp += [j]
elif j in blacklist_compiler_flags:
pass
@@ -339,7 +347,7 @@ class ConverterTarget:
cfg = ''
otherDeps = []
libraries = []
- mlog.debug(tgt)
+ mlog.debug(str(tgt))
if 'INTERFACE_INCLUDE_DIRECTORIES' in tgt.properties:
self.includes += [x for x in tgt.properties['INTERFACE_INCLUDE_DIRECTORIES'] if x]
@@ -424,8 +432,8 @@ class ConverterTarget:
for i in self.languages:
supported += list(lang_suffixes[i])
supported = ['.{}'.format(x) for x in supported]
- self.sources = [x for x in self.sources if any([x.endswith(y) for y in supported])]
- self.generated = [x for x in self.generated if any([x.endswith(y) for y in supported])]
+ self.sources = [x for x in self.sources if any([x.endswith(y) for y in supported])]
+ self.generated_raw = [x for x in self.generated_raw if any([x.endswith(y) for y in supported])]
# Make paths relative
def rel_path(x: str, is_header: bool, is_generated: bool) -> T.Optional[str]:
@@ -434,7 +442,7 @@ class ConverterTarget:
x = os.path.normpath(x)
if not os.path.exists(x) and not any([x.endswith(y) for y in obj_suffixes]) and not is_generated:
if (
- any([os.path.commonpath([x, os.path.normpath(os.path.join(root_src_dir, y))]) == x for y in self.generated])
+ any([os.path.commonpath([x, os.path.normpath(os.path.join(root_src_dir, y))]) == x for y in self.generated_raw])
and os.path.commonpath([x, self.env.get_build_dir()]) == self.env.get_build_dir()
):
os.makedirs(x)
@@ -465,29 +473,27 @@ class ConverterTarget:
return os.path.relpath(x, root_src_dir)
return x
- def custom_target(x: str):
- ctgt = output_target_map.generated(x)
- if ctgt:
- assert(isinstance(ctgt, ConverterCustomTarget))
- ref = ctgt.get_ref(x)
- assert(isinstance(ref, CustomTargetReference) and ref.valid())
- return ref
- return x
-
build_dir_rel = os.path.relpath(self.build_dir, os.path.join(self.env.get_build_dir(), subdir))
- self.generated = [rel_path(x, False, True) for x in self.generated]
+ self.generated_raw = [rel_path(x, False, True) for x in self.generated_raw]
self.includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.includes)] + [build_dir_rel]))
self.sys_includes = list(OrderedSet([rel_path(x, True, False) for x in OrderedSet(self.sys_includes)]))
self.sources = [rel_path(x, False, False) for x in self.sources]
# Resolve custom targets
- self.generated = [custom_target(x) for x in self.generated]
+ for gen_file in self.generated_raw:
+ ctgt = output_target_map.generated(gen_file)
+ if ctgt:
+ assert isinstance(ctgt, ConverterCustomTarget)
+ ref = ctgt.get_ref(gen_file)
+ assert isinstance(ref, CustomTargetReference) and ref.valid()
+ self.generated_ctgt += [ref]
+ elif gen_file is not None:
+ self.generated += [gen_file]
# Remove delete entries
- self.includes = [x for x in self.includes if x is not None]
+ self.includes = [x for x in self.includes if x is not None]
self.sys_includes = [x for x in self.sys_includes if x is not None]
- self.sources = [x for x in self.sources if x is not None]
- self.generated = [x for x in self.generated if x is not None]
+ self.sources = [x for x in self.sources if x is not None]
# Make sure '.' is always in the include directories
if '.' not in self.includes:
@@ -511,20 +517,20 @@ class ConverterTarget:
# Handle explicit CMake add_dependency() calls
for i in self.depends_raw:
- tgt = output_target_map.target(i)
- if tgt:
- self.depends.append(tgt)
+ dep_tgt = output_target_map.target(i)
+ if dep_tgt:
+ self.depends.append(dep_tgt)
- def process_object_libs(self, obj_target_list: T.List['ConverterTarget'], linker_workaround: bool):
+ def process_object_libs(self, obj_target_list: T.List['ConverterTarget'], linker_workaround: bool) -> None:
# Try to detect the object library(s) from the generated input sources
- temp = [x for x in self.generated if isinstance(x, str)]
+ temp = [x for x in self.generated]
temp = [os.path.basename(x) for x in temp]
temp = [x for x in temp if any([x.endswith('.' + y) for y in obj_suffixes])]
temp = [os.path.splitext(x)[0] for x in temp]
exts = self._all_source_suffixes()
# Temp now stores the source filenames of the object files
for i in obj_target_list:
- source_files = [x for x in i.sources + i.generated if isinstance(x, str)]
+ source_files = [x for x in i.sources + i.generated]
source_files = [os.path.basename(x) for x in source_files]
for j in temp:
# On some platforms (specifically looking at you Windows with vs20xy backend) CMake does
@@ -546,15 +552,17 @@ class ConverterTarget:
break
# Filter out object files from the sources
- self.generated = [x for x in self.generated if not isinstance(x, str) or not any([x.endswith('.' + y) for y in obj_suffixes])]
+ self.generated = [x for x in self.generated if not any([x.endswith('.' + y) for y in obj_suffixes])]
def _append_objlib_sources(self, tgt: 'ConverterTarget') -> None:
- self.includes += tgt.includes
- self.sources += tgt.sources
- self.generated += tgt.generated
- self.sources = list(OrderedSet(self.sources))
- self.generated = list(OrderedSet(self.generated))
- self.includes = list(OrderedSet(self.includes))
+ self.includes += tgt.includes
+ self.sources += tgt.sources
+ self.generated += tgt.generated
+ self.generated_ctgt += tgt.generated_ctgt
+ self.includes = list(OrderedSet(self.includes))
+ self.sources = list(OrderedSet(self.sources))
+ self.generated = list(OrderedSet(self.generated))
+ self.generated_ctgt = list(OrderedSet(self.generated_ctgt))
# Inherit compiler arguments since they may be required for building
for lang, opts in tgt.compile_opts.items():
@@ -574,9 +582,16 @@ class ConverterTarget:
lang_opts = self.env.coredata.compiler_options.build.get(lang, None)
if not lang_opts or 'std' not in lang_opts:
return []
- return lang_opts['std'].choices
+ res = lang_opts['std'].choices
+
+ # TODO: Get rid of this once we have propper typing for options
+ assert isinstance(res, list)
+ for i in res:
+ assert isinstance(i, str)
- def process_inter_target_dependencies(self):
+ return res
+
+ def process_inter_target_dependencies(self) -> None:
# Move the dependencies from all transfer_dependencies_from to the target
to_process = list(self.depends)
processed = []
@@ -589,7 +604,7 @@ class ConverterTarget:
new_deps += [i]
self.depends = list(OrderedSet(new_deps))
- def cleanup_dependencies(self):
+ def cleanup_dependencies(self) -> None:
# Clear the dependencies from targets that where moved from
if self.meson_func() in transfer_dependencies_from:
self.depends = []
@@ -613,6 +628,7 @@ class ConverterTarget:
mlog.log(' -- sys_includes: ', mlog.bold(str(self.sys_includes)))
mlog.log(' -- sources: ', mlog.bold(str(self.sources)))
mlog.log(' -- generated: ', mlog.bold(str(self.generated)))
+ mlog.log(' -- generated_ctgt: ', mlog.bold(str(self.generated_ctgt)))
mlog.log(' -- pie: ', mlog.bold('true' if self.pie else 'false'))
mlog.log(' -- override_opts: ', mlog.bold(str(self.override_options)))
mlog.log(' -- depends: ', mlog.bold(str(self.depends)))
@@ -621,7 +637,7 @@ class ConverterTarget:
mlog.log(' -', key, '=', mlog.bold(str(val)))
class CustomTargetReference:
- def __init__(self, ctgt: 'ConverterCustomTarget', index: int):
+ def __init__(self, ctgt: 'ConverterCustomTarget', index: int) -> None:
self.ctgt = ctgt # type: ConverterCustomTarget
self.index = index # type: int
@@ -641,24 +657,25 @@ class ConverterCustomTarget:
tgt_counter = 0 # type: int
out_counter = 0 # type: int
- def __init__(self, target: CMakeGeneratorTarget):
- assert(target.current_bin_dir is not None)
- assert(target.current_src_dir is not None)
+ def __init__(self, target: CMakeGeneratorTarget) -> None:
+ assert target.current_bin_dir is not None
+ assert target.current_src_dir is not None
self.name = target.name
if not self.name:
self.name = 'custom_tgt_{}'.format(ConverterCustomTarget.tgt_counter)
ConverterCustomTarget.tgt_counter += 1
- self.cmake_name = str(self.name)
+ self.cmake_name = str(self.name)
self.original_outputs = list(target.outputs)
- self.outputs = [os.path.basename(x) for x in self.original_outputs]
- self.conflict_map = {}
- self.command = target.command
- self.working_dir = target.working_dir
- self.depends_raw = target.depends
- self.inputs = []
- self.depends = []
- self.current_bin_dir = Path(target.current_bin_dir)
- self.current_src_dir = Path(target.current_src_dir)
+ self.outputs = [os.path.basename(x) for x in self.original_outputs]
+ self.conflict_map = {} # type: T.Dict[str, str]
+ self.command = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]]
+ self.working_dir = target.working_dir
+ self.depends_raw = target.depends
+ self.inputs = [] # type: T.List[T.Union[str, CustomTargetReference]]
+ self.depends = [] # type: T.List[T.Union[ConverterTarget, ConverterCustomTarget]]
+ self.current_bin_dir = Path(target.current_bin_dir)
+ self.current_src_dir = Path(target.current_src_dir)
+ self._raw_target = target
# Convert the target name to a valid meson target name
self.name = _sanitize_cmake_name(self.name)
@@ -700,12 +717,12 @@ class ConverterCustomTarget:
self.outputs = temp_outputs
# Check if the command is a build target
- commands = []
- for i in self.command:
- assert(isinstance(i, list))
- cmd = []
+ commands = [] # type: T.List[T.List[T.Union[str, ConverterTarget]]]
+ for curr_cmd in self._raw_target.command:
+ assert(isinstance(curr_cmd, list))
+ cmd = [] # type: T.List[T.Union[str, ConverterTarget]]
- for j in i:
+ for j in curr_cmd:
if not j:
continue
target = output_target_map.executable(j)
@@ -759,9 +776,11 @@ class ConverterCustomTarget:
elif tgt:
self.depends += [tgt]
elif gen:
- self.inputs += [gen.get_ref(i)]
+ ctgt_ref = gen.get_ref(i)
+ assert ctgt_ref is not None
+ self.inputs += [ctgt_ref]
- def process_inter_target_dependencies(self):
+ def process_inter_target_dependencies(self) -> None:
# Move the dependencies from all transfer_dependencies_from to the target
to_process = list(self.depends)
processed = []
@@ -799,7 +818,7 @@ class CMakeAPI(Enum):
FILE = 2
class CMakeInterpreter:
- def __init__(self, build: 'Build', subdir: str, src_dir: str, install_prefix: str, env: Environment, backend: 'Backend'):
+ def __init__(self, build: 'Build', subdir: str, src_dir: str, install_prefix: str, env: 'Environment', backend: 'Backend'):
assert(hasattr(backend, 'name'))
self.build = build
self.subdir = subdir
@@ -815,21 +834,21 @@ class CMakeInterpreter:
self.fileapi = CMakeFileAPI(self.build_dir)
# Raw CMake results
- self.bs_files = []
- self.codemodel_configs = None
- self.raw_trace = None
+ self.bs_files = [] # type: T.List[str]
+ self.codemodel_configs = None # type: T.Optional[T.List[CMakeConfiguration]]
+ self.raw_trace = None # type: T.Optional[str]
# Analysed data
- self.project_name = ''
- self.languages = []
- self.targets = []
- self.custom_targets = [] # type: T.List[ConverterCustomTarget]
- self.trace = CMakeTraceParser('', '') # Will be replaced in analyse
+ self.project_name = ''
+ self.languages = [] # type: T.List[str]
+ self.targets = [] # type: T.List[ConverterTarget]
+ self.custom_targets = [] # type: T.List[ConverterCustomTarget]
+ self.trace = CMakeTraceParser('', '') # Will be replaced in analyse
self.output_target_map = OutputTargetMap(self.build_dir)
# Generated meson data
- self.generated_targets = {}
- self.internal_name_map = {}
+ self.generated_targets = {} # type: T.Dict[str, T.Dict[str, T.Optional[str]]]
+ self.internal_name_map = {} # type: T.Dict[str, str]
def configure(self, extra_cmake_options: T.List[str]) -> None:
for_machine = MachineChoice.HOST # TODO make parameter
@@ -933,9 +952,11 @@ class CMakeInterpreter:
# Get CMake build system files
bs_reply = self.client.query_checked(RequestCMakeInputs(), 'Querying build system files')
+ assert isinstance(bs_reply, ReplyCMakeInputs)
# Now get the CMake code model
cm_reply = self.client.query_checked(RequestCodeModel(), 'Querying the CMake code model')
+ assert isinstance(cm_reply, ReplyCodeModel)
src_dir = bs_reply.src_dir
self.bs_files = [x.file for x in bs_reply.build_files if not x.is_cmake and not x.is_temp]
@@ -958,63 +979,64 @@ class CMakeInterpreter:
# Find all targets
added_target_names = [] # type: T.List[str]
- for i in self.codemodel_configs:
- for j in i.projects:
+ for i_0 in self.codemodel_configs:
+ for j_0 in i_0.projects:
if not self.project_name:
- self.project_name = j.name
- for k in j.targets:
+ self.project_name = j_0.name
+ for k_0 in j_0.targets:
# Avoid duplicate targets from different configurations and known
# dummy CMake internal target types
- if k.type not in skip_targets and k.name not in added_target_names:
- added_target_names += [k.name]
- self.targets += [ConverterTarget(k, self.env)]
+ if k_0.type not in skip_targets and k_0.name not in added_target_names:
+ added_target_names += [k_0.name]
+ self.targets += [ConverterTarget(k_0, self.env)]
# Add interface targets from trace, if not already present.
# This step is required because interface targets were removed from
# the CMake file API output.
api_target_name_list = [x.name for x in self.targets]
- for i in self.trace.targets.values():
- if i.type != 'INTERFACE' or i.name in api_target_name_list or i.imported:
+ for i_1 in self.trace.targets.values():
+ if i_1.type != 'INTERFACE' or i_1.name in api_target_name_list or i_1.imported:
continue
dummy = CMakeTarget({
- 'name': i.name,
+ 'name': i_1.name,
'type': 'INTERFACE_LIBRARY',
'sourceDirectory': self.src_dir,
'buildDirectory': self.build_dir,
})
self.targets += [ConverterTarget(dummy, self.env)]
- for i in self.trace.custom_targets:
- self.custom_targets += [ConverterCustomTarget(i)]
+ for i_2 in self.trace.custom_targets:
+ self.custom_targets += [ConverterCustomTarget(i_2)]
# generate the output_target_map
- for i in [*self.targets, *self.custom_targets]:
- self.output_target_map.add(i)
+ for i_3 in [*self.targets, *self.custom_targets]:
+ assert isinstance(i_3, (ConverterTarget, ConverterCustomTarget))
+ self.output_target_map.add(i_3)
# First pass: Basic target cleanup
object_libs = []
custom_target_outputs = [] # type: T.List[str]
- for i in self.custom_targets:
- i.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace)
- for i in self.targets:
- i.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace)
- if i.type == 'OBJECT_LIBRARY':
- object_libs += [i]
- self.languages += [x for x in i.languages if x not in self.languages]
+ for ctgt in self.custom_targets:
+ ctgt.postprocess(self.output_target_map, self.src_dir, self.subdir, custom_target_outputs, self.trace)
+ for tgt in self.targets:
+ tgt.postprocess(self.output_target_map, self.src_dir, self.subdir, self.install_prefix, self.trace)
+ if tgt.type == 'OBJECT_LIBRARY':
+ object_libs += [tgt]
+ self.languages += [x for x in tgt.languages if x not in self.languages]
# Second pass: Detect object library dependencies
- for i in self.targets:
- i.process_object_libs(object_libs, self._object_lib_workaround())
+ for tgt in self.targets:
+ tgt.process_object_libs(object_libs, self._object_lib_workaround())
# Third pass: Reassign dependencies to avoid some loops
- for i in self.targets:
- i.process_inter_target_dependencies()
- for i in self.custom_targets:
- i.process_inter_target_dependencies()
+ for tgt in self.targets:
+ tgt.process_inter_target_dependencies()
+ for ctgt in self.custom_targets:
+ ctgt.process_inter_target_dependencies()
# Fourth pass: Remove rassigned dependencies
- for i in self.targets:
- i.cleanup_dependencies()
+ for tgt in self.targets:
+ tgt.cleanup_dependencies()
mlog.log('CMake project', mlog.bold(self.project_name), 'has', mlog.bold(str(len(self.targets) + len(self.custom_targets))), 'build targets.')
@@ -1022,7 +1044,7 @@ class CMakeInterpreter:
if not self.project_name:
raise CMakeException('CMakeInterpreter was not analysed')
- def token(tid: str = 'string', val='') -> Token:
+ def token(tid: str = 'string', val: TYPE_mixed = '') -> Token:
return Token(tid, self.subdir, 0, 0, 0, None, val)
def string(value: str) -> StringNode:
@@ -1034,7 +1056,7 @@ class CMakeInterpreter:
def number(value: int) -> NumberNode:
return NumberNode(token(val=value))
- def nodeify(value):
+ def nodeify(value: TYPE_mixed_list) -> BaseNode:
if isinstance(value, str):
return string(value)
elif isinstance(value, bool):
@@ -1043,34 +1065,38 @@ class CMakeInterpreter:
return number(value)
elif isinstance(value, list):
return array(value)
- return value
+ elif isinstance(value, BaseNode):
+ return value
+ raise RuntimeError('invalid type of value: {} ({})'.format(type(value).__name__, str(value)))
def indexed(node: BaseNode, index: int) -> IndexNode:
return IndexNode(node, nodeify(index))
- def array(elements) -> ArrayNode:
+ def array(elements: TYPE_mixed_list) -> ArrayNode:
args = ArgumentNode(token())
if not isinstance(elements, list):
elements = [args]
args.arguments += [nodeify(x) for x in elements if x is not None]
return ArrayNode(args, 0, 0, 0, 0)
- def function(name: str, args=None, kwargs=None) -> FunctionNode:
+ def function(name: str, args: T.Optional[TYPE_mixed_list] = None, kwargs: T.Optional[TYPE_mixed_kwargs] = None) -> FunctionNode:
args = [] if args is None else args
kwargs = {} if kwargs is None else kwargs
args_n = ArgumentNode(token())
if not isinstance(args, list):
+ assert isinstance(args, (str, int, bool, BaseNode))
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
func_n = FunctionNode(self.subdir, 0, 0, 0, 0, name, args_n)
return func_n
- def method(obj: BaseNode, name: str, args=None, kwargs=None) -> MethodNode:
+ def method(obj: BaseNode, name: str, args: T.Optional[TYPE_mixed_list] = None, kwargs: T.Optional[TYPE_mixed_kwargs] = None) -> MethodNode:
args = [] if args is None else args
kwargs = {} if kwargs is None else kwargs
args_n = ArgumentNode(token())
if not isinstance(args, list):
+ assert isinstance(args, (str, int, bool, BaseNode))
args = [args]
args_n.arguments = [nodeify(x) for x in args if x is not None]
args_n.kwargs = {id_node(k): nodeify(v) for k, v in kwargs.items() if v is not None}
@@ -1086,9 +1112,9 @@ class CMakeInterpreter:
# Add the run script for custom commands
# Add the targets
- processing = []
- processed = {}
- name_map = {}
+ processing = [] # type: T.List[str]
+ processed = {} # type: T.Dict[str, T.Dict[str, T.Optional[str]]]
+ name_map = {} # type: T.Dict[str, str]
def extract_tgt(tgt: T.Union[ConverterTarget, ConverterCustomTarget, CustomTargetReference]) -> IdNode:
tgt_name = None
@@ -1105,24 +1131,24 @@ class CMakeInterpreter:
raise CMakeException('Cycle in CMake inputs/dependencies detected')
processing.append(tgt.name)
- def resolve_ctgt_ref(ref: CustomTargetReference) -> BaseNode:
+ def resolve_ctgt_ref(ref: CustomTargetReference) -> T.Union[IdNode, IndexNode]:
tgt_var = extract_tgt(ref)
if len(ref.ctgt.outputs) == 1:
return tgt_var
else:
return indexed(tgt_var, ref.index)
- def process_target(tgt: ConverterTarget):
+ def process_target(tgt: ConverterTarget) -> None:
detect_cycle(tgt)
# First handle inter target dependencies
- link_with = []
- objec_libs = [] # type: T.List[IdNode]
- sources = []
- generated = []
- generated_filenames = []
- custom_targets = []
- dependencies = []
+ link_with = [] # type: T.List[IdNode]
+ objec_libs = [] # type: T.List[IdNode]
+ sources = [] # type: T.List[str]
+ generated = [] # type: T.List[T.Union[IdNode, IndexNode]]
+ generated_filenames = [] # type: T.List[str]
+ custom_targets = [] # type: T.List[ConverterCustomTarget]
+ dependencies = [] # type: T.List[IdNode]
for i in tgt.link_with:
assert(isinstance(i, ConverterTarget))
if i.name not in processed:
@@ -1141,16 +1167,17 @@ class CMakeInterpreter:
dependencies += [extract_tgt(i)]
# Generate the source list and handle generated sources
- for i in tgt.sources + tgt.generated:
- if isinstance(i, CustomTargetReference):
- if i.ctgt.name not in processed:
- process_custom_target(i.ctgt)
- generated += [resolve_ctgt_ref(i)]
- generated_filenames += [i.filename()]
- if i.ctgt not in custom_targets:
- custom_targets += [i.ctgt]
- else:
- sources += [i]
+ sources += tgt.sources
+ sources += tgt.generated
+
+ for ctgt_ref in tgt.generated_ctgt:
+ ctgt = ctgt_ref.ctgt
+ if ctgt.name not in processed:
+ process_custom_target(ctgt)
+ generated += [resolve_ctgt_ref(ctgt_ref)]
+ generated_filenames += [ctgt_ref.filename()]
+ if ctgt not in custom_targets:
+ custom_targets += [ctgt]
# Add all header files from all used custom targets. This
# ensures that all custom targets are built before any
@@ -1158,12 +1185,12 @@ class CMakeInterpreter:
# header files are present. This step is necessary because
# CMake always ensures that a custom target is executed
# before another target if at least one output is used.
- for i in custom_targets:
- for j in i.outputs:
+ for ctgt in custom_targets:
+ for j in ctgt.outputs:
if not is_header(j) or j in generated_filenames:
continue
- generated += [resolve_ctgt_ref(i.get_ref(j))]
+ generated += [resolve_ctgt_ref(ctgt.get_ref(j))]
generated_filenames += [j]
# Determine the meson function to use for the build target
@@ -1190,7 +1217,7 @@ class CMakeInterpreter:
'install': install_tgt,
'override_options': options.get_override_options(tgt.cmake_name, tgt.override_options),
'objects': [method(x, 'extract_all_objects') for x in objec_libs],
- }
+ } # type: TYPE_mixed_kwargs
# Only set if installed and only override if it is set
if install_tgt and tgt.install_dir:
@@ -1212,7 +1239,7 @@ class CMakeInterpreter:
'link_with': id_node(tgt_var),
'compile_args': tgt.public_compile_opts,
'include_directories': id_node(inc_var),
- }
+ } # type: TYPE_mixed_kwargs
if dependencies:
generated += dependencies
@@ -1230,7 +1257,7 @@ class CMakeInterpreter:
tgt_var = None
else:
src_node = assign(src_var, function('files', sources))
- tgt_node = assign(tgt_var, function(tgt_func, [tgt_var, [id_node(src_var)] + generated], tgt_kwargs))
+ tgt_node = assign(tgt_var, function(tgt_func, [tgt_var, id_node(src_var), *generated], tgt_kwargs))
node_list += [src_node, tgt_node]
if tgt_func in ['static_library', 'shared_library']:
dep_node = assign(dep_var, function('declare_dependency', kwargs=dep_kwargs))
@@ -1290,19 +1317,19 @@ class CMakeInterpreter:
'output': tgt.outputs,
'command': command,
'depends': [resolve_source(x) for x in tgt.depends],
- }
+ } # type: TYPE_mixed_kwargs
root_cb.lines += [assign(tgt_var, function('custom_target', [tgt.name], tgt_kwargs))]
processed[tgt.name] = {'inc': None, 'src': None, 'dep': None, 'tgt': tgt_var, 'func': 'custom_target'}
name_map[tgt.cmake_name] = tgt.name
# Now generate the target function calls
- for i in self.custom_targets:
- if i.name not in processed:
- process_custom_target(i)
- for i in self.targets:
- if i.name not in processed:
- process_target(i)
+ for ctgt in self.custom_targets:
+ if ctgt.name not in processed:
+ process_custom_target(ctgt)
+ for tgt in self.targets:
+ if tgt.name not in processed:
+ process_target(tgt)
self.generated_targets = processed
self.internal_name_map = name_map
diff --git a/mesonbuild/cmake/traceparser.py b/mesonbuild/cmake/traceparser.py
index 98b56f5..5c41196 100644
--- a/mesonbuild/cmake/traceparser.py
+++ b/mesonbuild/cmake/traceparser.py
@@ -28,30 +28,37 @@ import json
import textwrap
class CMakeTraceLine:
- def __init__(self, file, line, func, args):
+ def __init__(self, file: str, line: int, func: str, args: T.List[str]) -> None:
self.file = file
self.line = line
self.func = func.lower()
self.args = args
- def __repr__(self):
+ def __repr__(self) -> str:
s = 'CMake TRACE: {0}:{1} {2}({3})'
return s.format(self.file, self.line, self.func, self.args)
class CMakeTarget:
- def __init__(self, name, target_type, properties=None, imported: bool = False, tline: T.Optional[CMakeTraceLine] = None):
+ def __init__(
+ self,
+ name: str,
+ target_type: str,
+ properties: T.Optional[T.Dict[str, T.List[str]]] = None,
+ imported: bool = False,
+ tline: T.Optional[CMakeTraceLine] = None
+ ):
if properties is None:
properties = {}
- self.name = name
- self.type = target_type
- self.properties = properties
- self.imported = imported
- self.tline = tline
- self.depends = []
- self.current_bin_dir = None
- self.current_src_dir = None
-
- def __repr__(self):
+ self.name = name
+ self.type = target_type
+ self.properties = properties
+ self.imported = imported
+ self.tline = tline
+ self.depends = [] # type: T.List[str]
+ self.current_bin_dir = None # type: T.Optional[str]
+ self.current_src_dir = None # type: T.Optional[str]
+
+ def __repr__(self) -> str:
s = 'CMake TARGET:\n -- name: {}\n -- type: {}\n -- imported: {}\n -- properties: {{\n{} }}\n -- tline: {}'
propSTR = ''
for i in self.properties:
@@ -67,14 +74,14 @@ class CMakeTarget:
assert all([';' not in x for x in self.properties[key]])
class CMakeGeneratorTarget(CMakeTarget):
- def __init__(self, name):
+ def __init__(self, name: str) -> None:
super().__init__(name, 'CUSTOM', {})
self.outputs = [] # type: T.List[str]
self.command = [] # type: T.List[T.List[str]]
self.working_dir = None # type: T.Optional[str]
class CMakeTraceParser:
- def __init__(self, cmake_version: str, build_dir: str, permissive: bool = True):
+ def __init__(self, cmake_version: str, build_dir: str, permissive: bool = True) -> None:
self.vars = {} # type: T.Dict[str, T.List[str]]
self.targets = {} # type: T.Dict[str, CMakeTarget]
@@ -117,7 +124,7 @@ class CMakeTraceParser:
# meaning here in the trace parser.
'meson_ps_execute_delayed_calls': self._meson_ps_execute_delayed_calls,
'meson_ps_reload_vars': self._meson_ps_reload_vars,
- }
+ } # type: T.Dict[str, T.Callable[[CMakeTraceLine], None]]
def trace_args(self) -> T.List[str]:
arg_map = {
@@ -256,7 +263,7 @@ class CMakeTraceParser:
else:
self.vars[identifier] = value.split(';')
- def _cmake_unset(self, tline: CMakeTraceLine):
+ def _cmake_unset(self, tline: CMakeTraceLine) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/unset.html
if len(tline.args) < 1:
return self._gen_exception('unset', 'requires at least one argument', tline)
@@ -264,7 +271,7 @@ class CMakeTraceParser:
if tline.args[0] in self.vars:
del self.vars[tline.args[0]]
- def _cmake_add_executable(self, tline: CMakeTraceLine):
+ def _cmake_add_executable(self, tline: CMakeTraceLine) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/add_executable.html
args = list(tline.args) # Make a working copy
@@ -280,7 +287,7 @@ class CMakeTraceParser:
self.targets[args[0]] = CMakeTarget(args[0], 'EXECUTABLE', {}, tline=tline, imported=is_imported)
- def _cmake_add_library(self, tline: CMakeTraceLine):
+ def _cmake_add_library(self, tline: CMakeTraceLine) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/add_library.html
args = list(tline.args) # Make a working copy
@@ -314,7 +321,7 @@ class CMakeTraceParser:
else:
self.targets[args[0]] = CMakeTarget(args[0], 'NORMAL', {}, tline=tline)
- def _cmake_add_custom_command(self, tline: CMakeTraceLine, name=None):
+ def _cmake_add_custom_command(self, tline: CMakeTraceLine, name: T.Optional[str] = None) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/add_custom_command.html
args = self._flatten_args(list(tline.args)) # Commands can be passed as ';' seperated lists
@@ -379,7 +386,7 @@ class CMakeTraceParser:
if name:
self.targets[name] = target
- def _cmake_add_custom_target(self, tline: CMakeTraceLine):
+ def _cmake_add_custom_target(self, tline: CMakeTraceLine) -> None:
# DOC: https://cmake.org/cmake/help/latest/command/add_custom_target.html
# We only the first parameter (the target name) is interesting
if len(tline.args) < 1:
@@ -494,7 +501,7 @@ class CMakeTraceParser:
arglist = [] # type: T.List[T.Tuple[str, T.List[str]]]
if self.trace_format == 'human':
name = args.pop(0)
- values = []
+ values = [] # type: T.List[str]
prop_regex = re.compile(r'^[A-Z_]+$')
for a in args:
if prop_regex.match(a):
@@ -550,7 +557,7 @@ class CMakeTraceParser:
# DOC: https://cmake.org/cmake/help/latest/command/target_link_libraries.html
self._parse_common_target_options('target_link_options', 'LINK_LIBRARIES', 'INTERFACE_LINK_LIBRARIES', tline)
- def _parse_common_target_options(self, func: str, private_prop: str, interface_prop: str, tline: CMakeTraceLine, ignore: T.Optional[T.List[str]] = None, paths: bool = False):
+ def _parse_common_target_options(self, func: str, private_prop: str, interface_prop: str, tline: CMakeTraceLine, ignore: T.Optional[T.List[str]] = None, paths: bool = False) -> None:
if ignore is None:
ignore = ['BEFORE']
@@ -588,11 +595,11 @@ class CMakeTraceParser:
interface = [x for x in interface if x]
private = [x for x in private if x]
- for i in [(private_prop, private), (interface_prop, interface)]:
- if not i[0] in self.targets[target].properties:
- self.targets[target].properties[i[0]] = []
+ for j in [(private_prop, private), (interface_prop, interface)]:
+ if not j[0] in self.targets[target].properties:
+ self.targets[target].properties[j[0]] = []
- self.targets[target].properties[i[0]] += i[1]
+ self.targets[target].properties[j[0]] += j[1]
def _meson_ps_execute_delayed_calls(self, tline: CMakeTraceLine) -> None:
for l in self.stored_commands:
@@ -606,7 +613,7 @@ class CMakeTraceParser:
def _meson_ps_reload_vars(self, tline: CMakeTraceLine) -> None:
self.delayed_commands = self.get_cmake_var('MESON_PS_DELAYED_CALLS')
- def _lex_trace_human(self, trace):
+ def _lex_trace_human(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]:
# The trace format is: '<file>(<line>): <func>(<args -- can contain \n> )\n'
reg_tline = re.compile(r'\s*(.*\.(cmake|txt))\(([0-9]+)\):\s*(\w+)\(([\s\S]*?) ?\)\s*\n', re.MULTILINE)
reg_other = re.compile(r'[^\n]*\n')
@@ -629,17 +636,23 @@ class CMakeTraceParser:
func = mo_file_line.group(4)
args = mo_file_line.group(5)
args = parse_generator_expressions(args)
- args = args.split(' ')
- args = list(map(lambda x: x.strip(), args))
+ argl = args.split(' ')
+ argl = list(map(lambda x: x.strip(), argl))
- yield CMakeTraceLine(file, line, func, args)
+ yield CMakeTraceLine(file, int(line), func, argl)
- def _lex_trace_json(self, trace: str):
+ def _lex_trace_json(self, trace: str) -> T.Generator[CMakeTraceLine, None, None]:
lines = trace.splitlines(keepends=False)
lines.pop(0) # The first line is the version
for i in lines:
data = json.loads(i)
+ assert isinstance(data['file'], str)
+ assert isinstance(data['line'], int)
+ assert isinstance(data['cmd'], str)
+ assert isinstance(data['args'], list)
args = data['args']
+ for j in args:
+ assert isinstance(j, str)
args = [parse_generator_expressions(x) for x in args]
yield CMakeTraceLine(data['file'], data['line'], data['cmd'], args)
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index 5cfa06c..091a000 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -56,15 +56,15 @@ class CCompiler(CLikeCompiler, Compiler):
def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool,
info: 'MachineInfo', exe_wrapper: T.Optional[str] = None, **kwargs):
# If a child ObjC or CPP class has already set it, don't set it ourselves
- Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
- CLikeCompiler.__init__(self, is_cross, exe_wrapper)
+ Compiler.__init__(self, exelist, version, for_machine, info, is_cross=is_cross, **kwargs)
+ CLikeCompiler.__init__(self, exe_wrapper)
def get_no_stdinc_args(self):
return ['-nostdinc']
def sanity_check(self, work_dir, environment):
code = 'int main(void) { int class=0; return class; }\n'
- return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def has_header_symbol(self, hname, symbol, prefix, env, *, extra_args=None, dependencies=None):
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py
index c4bd7c2..12643b0 100644
--- a/mesonbuild/compilers/compilers.py
+++ b/mesonbuild/compilers/compilers.py
@@ -24,28 +24,32 @@ from .. import mesonlib
from ..linkers import LinkerEnvVarsMixin
from ..mesonlib import (
EnvironmentException, MachineChoice, MesonException,
- Popen_safe, split_args
+ Popen_safe, split_args, LibType
)
from ..envconfig import (
Properties, get_env_var
)
+
from ..arglist import CompilerArgs
if T.TYPE_CHECKING:
+ from ..build import BuildTarget
from ..coredata import OptionDictType
from ..envconfig import MachineInfo
from ..environment import Environment
from ..linkers import DynamicLinker # noqa: F401
+ from ..dependencies import Dependency
CompilerType = T.TypeVar('CompilerType', bound=Compiler)
+ _T = T.TypeVar('_T')
"""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', 'dll.a', 'dylib', 'so')
+header_suffixes = ('h', 'hh', 'hpp', 'hxx', 'H', 'ipp', 'moc', 'vapi', 'di') # type: T.Tuple[str, ...]
+obj_suffixes = ('o', 'obj', 'res') # type: T.Tuple[str, ...]
+lib_suffixes = ('a', 'lib', 'dll', 'dll.a', 'dylib', 'so') # type: T.Tuple[str, ...]
# 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 = {
@@ -63,26 +67,26 @@ lang_suffixes = {
'cs': ('cs',),
'swift': ('swift',),
'java': ('java',),
-}
+} # type: T.Dict[str, T.Tuple[str, ...]]
all_languages = lang_suffixes.keys()
-cpp_suffixes = lang_suffixes['cpp'] + ('h',)
-c_suffixes = lang_suffixes['c'] + ('h',)
+cpp_suffixes = lang_suffixes['cpp'] + ('h',) # type: T.Tuple[str, ...]
+c_suffixes = lang_suffixes['c'] + ('h',) # type: T.Tuple[str, ...]
# List of languages that by default consume and output libraries following the
# C ABI; these can generally be used interchangebly
-clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',)
+clib_langs = ('objcpp', 'cpp', 'objc', 'c', 'fortran',) # type: T.Tuple[str, ...]
# 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()
-clink_langs = ('d', 'cuda') + clib_langs
-clink_suffixes = ()
+clink_langs = ('d', 'cuda') + clib_langs # type: T.Tuple[str, ...]
+clink_suffixes = tuple() # type: T.Tuple[str, ...]
for _l in clink_langs + ('vala',):
clink_suffixes += lang_suffixes[_l]
clink_suffixes += ('h', 'll', 's')
-all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes))
+all_suffixes = set(itertools.chain(*lang_suffixes.values(), clink_suffixes)) # type: T.Set[str]
# Languages that should use LDFLAGS arguments when linking.
-languages_using_ldflags = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'}
+languages_using_ldflags = {'objcpp', 'cpp', 'objc', 'c', 'fortran', 'd', 'cuda'} # type: T.Set[str]
# Languages that should use CPPFLAGS arguments when linking.
-languages_using_cppflags = {'c', 'cpp', 'objc', 'objcpp'}
+languages_using_cppflags = {'c', 'cpp', 'objc', 'objcpp'} # type: T.Set[str]
soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$')
# Environment variables that each lang uses.
@@ -94,14 +98,14 @@ cflags_mapping = {'c': 'CFLAGS',
'fortran': 'FFLAGS',
'd': 'DFLAGS',
'vala': 'VALAFLAGS',
- 'rust': 'RUSTFLAGS'}
+ 'rust': 'RUSTFLAGS'} # type: T.Dict[str, str]
cexe_mapping = {'c': 'CC',
'cpp': 'CXX'}
# All these are only for C-linkable languages; see `clink_langs` above.
-def sort_clink(lang):
+def sort_clink(lang: str) -> int:
'''
Sorting function to sort the list of languages according to
reversed(compilers.clink_langs) and append the unknown langs in the end.
@@ -112,40 +116,40 @@ def sort_clink(lang):
return 1
return -clink_langs.index(lang)
-def is_header(fname):
- if hasattr(fname, 'fname'):
+def is_header(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
suffix = fname.split('.')[-1]
return suffix in header_suffixes
-def is_source(fname):
- if hasattr(fname, 'fname'):
+def is_source(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
suffix = fname.split('.')[-1].lower()
return suffix in clink_suffixes
-def is_assembly(fname):
- if hasattr(fname, 'fname'):
+def is_assembly(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
return fname.split('.')[-1].lower() == 's'
-def is_llvm_ir(fname):
- if hasattr(fname, 'fname'):
+def is_llvm_ir(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
return fname.split('.')[-1] == 'll'
@lru_cache(maxsize=None)
-def cached_by_name(fname):
+def cached_by_name(fname: 'mesonlib.FileOrString') -> bool:
suffix = fname.split('.')[-1]
return suffix in obj_suffixes
-def is_object(fname):
- if hasattr(fname, 'fname'):
+def is_object(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
return cached_by_name(fname)
-def is_library(fname):
- if hasattr(fname, 'fname'):
+def is_library(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
if soregex.match(fname):
@@ -154,8 +158,8 @@ def is_library(fname):
suffix = fname.split('.')[-1]
return suffix in lib_suffixes
-def is_known_suffix(fname):
- if hasattr(fname, 'fname'):
+def is_known_suffix(fname: 'mesonlib.FileOrString') -> bool:
+ if isinstance(fname, mesonlib.File):
fname = fname.fname
suffix = fname.split('.')[-1]
@@ -166,14 +170,14 @@ cuda_buildtype_args = {'plain': [],
'debugoptimized': [],
'release': [],
'minsize': [],
- }
+ } # type: T.Dict[str, T.List[str]]
java_buildtype_args = {'plain': [],
'debug': ['-g'],
'debugoptimized': ['-g'],
'release': [],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
rust_buildtype_args = {'plain': [],
'debug': [],
@@ -181,7 +185,7 @@ rust_buildtype_args = {'plain': [],
'release': [],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
d_gdc_buildtype_args = {'plain': [],
'debug': [],
@@ -189,7 +193,7 @@ d_gdc_buildtype_args = {'plain': [],
'release': ['-finline-functions'],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
d_ldc_buildtype_args = {'plain': [],
'debug': [],
@@ -197,7 +201,7 @@ d_ldc_buildtype_args = {'plain': [],
'release': ['-enable-inlining', '-Hkeep-all-bodies'],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
d_dmd_buildtype_args = {'plain': [],
'debug': [],
@@ -205,7 +209,7 @@ d_dmd_buildtype_args = {'plain': [],
'release': ['-inline'],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
mono_buildtype_args = {'plain': [],
'debug': [],
@@ -213,7 +217,7 @@ mono_buildtype_args = {'plain': [],
'release': ['-optimize+'],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
swift_buildtype_args = {'plain': [],
'debug': [],
@@ -221,14 +225,14 @@ swift_buildtype_args = {'plain': [],
'release': [],
'minsize': [],
'custom': [],
- }
+ } # type: T.Dict[str, T.List[str]]
gnu_winlibs = ['-lkernel32', '-luser32', '-lgdi32', '-lwinspool', '-lshell32',
- '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32']
+ '-lole32', '-loleaut32', '-luuid', '-lcomdlg32', '-ladvapi32'] # type: T.List[str]
msvc_winlibs = ['kernel32.lib', 'user32.lib', 'gdi32.lib',
'winspool.lib', 'shell32.lib', 'ole32.lib', 'oleaut32.lib',
- 'uuid.lib', 'comdlg32.lib', 'advapi32.lib']
+ 'uuid.lib', 'comdlg32.lib', 'advapi32.lib'] # type: T.List[str]
clike_optimization_args = {'0': [],
'g': [],
@@ -236,7 +240,7 @@ clike_optimization_args = {'0': [],
'2': ['-O2'],
'3': ['-O3'],
's': ['-Os'],
- }
+ } # type: T.Dict[str, T.List[str]]
cuda_optimization_args = {'0': [],
'g': ['-O0'],
@@ -244,13 +248,13 @@ cuda_optimization_args = {'0': [],
'2': ['-O2'],
'3': ['-O3'],
's': ['-O3']
- }
+ } # type: T.Dict[str, T.List[str]]
cuda_debug_args = {False: [],
- True: ['-g']}
+ True: ['-g']} # type: T.Dict[bool, T.List[str]]
clike_debug_args = {False: [],
- True: ['-g']}
+ True: ['-g']} # type: T.Dict[bool, T.List[str]]
base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', True),
'b_lto': coredata.UserBooleanOption('Use link time optimization', False),
@@ -278,18 +282,21 @@ base_options = {'b_pch': coredata.UserBooleanOption('Use precompiled headers', T
'b_vscrt': coredata.UserComboOption('VS run-time library type to use.',
['none', 'md', 'mdd', 'mt', 'mtd', 'from_buildtype'],
'from_buildtype'),
- }
+ } # type: OptionDictType
-def option_enabled(boptions, options, option):
+def option_enabled(boptions: T.List[str], options: 'OptionDictType',
+ option: str) -> bool:
try:
if option not in boptions:
return False
- return options[option].value
+ ret = options[option].value
+ assert isinstance(ret, bool), 'must return bool' # could also be str
+ return ret
except KeyError:
return False
-def get_base_compile_args(options, compiler):
- args = []
+def get_base_compile_args(options: 'OptionDictType', compiler: 'Compiler') -> T.List[str]:
+ args = [] # type T.List[str]
try:
if options['b_lto'].value:
args.extend(compiler.get_lto_compile_args())
@@ -337,8 +344,9 @@ def get_base_compile_args(options, compiler):
pass
return args
-def get_base_link_args(options, linker, is_shared_module):
- args = []
+def get_base_link_args(options: 'OptionDictType', linker: 'Compiler',
+ is_shared_module: bool) -> T.List[str]:
+ args = [] # type: T.List[str]
try:
if options['b_lto'].value:
args.extend(linker.get_lto_link_args())
@@ -398,32 +406,58 @@ class CrossNoRunException(MesonException):
pass
class RunResult:
- def __init__(self, compiled, returncode=999, stdout='UNDEFINED', stderr='UNDEFINED'):
+ def __init__(self, compiled: bool, returncode: int = 999,
+ stdout: str = 'UNDEFINED', stderr: str = 'UNDEFINED'):
self.compiled = compiled
self.returncode = returncode
self.stdout = stdout
self.stderr = stderr
+class CompileResult:
+
+ """The result of Compiler.compiles (and friends)."""
+
+ def __init__(self, stdo: T.Optional[str] = None, stde: T.Optional[str] = None,
+ args: T.Optional[T.List[str]] = None,
+ returncode: int = 999, pid: int = -1,
+ text_mode: bool = True,
+ input_name: T.Optional[str] = None,
+ output_name: T.Optional[str] = None,
+ command: T.Optional[T.List[str]] = None, cached: bool = False):
+ self.stdout = stdo
+ self.stderr = stde
+ self.input_name = input_name
+ self.output_name = output_name
+ self.command = command or []
+ self.args = args or []
+ self.cached = cached
+ self.returncode = returncode
+ self.pid = pid
+ self.text_mode = text_mode
+
+
class Compiler(metaclass=abc.ABCMeta):
# Libraries to ignore in find_library() since they are provided by the
# compiler or the C library. Currently only used for MSVC.
- ignore_libs = ()
+ ignore_libs = [] # type: T.List[str]
# Libraries that are internal compiler implementations, and must not be
# manually searched.
- internal_libs = ()
+ internal_libs = [] # type: T.List[str]
LINKER_PREFIX = None # type: T.Union[None, str, T.List[str]]
INVOKES_LINKER = True
- def __init__(self, exelist, version, for_machine: MachineChoice, info: 'MachineInfo',
- linker: T.Optional['DynamicLinker'] = None, **kwargs):
- if isinstance(exelist, str):
- self.exelist = [exelist]
- elif isinstance(exelist, list):
- self.exelist = exelist
- else:
- raise TypeError('Unknown argument to Compiler')
+ # TODO: these could be forward declarations once we drop 3.5 support
+ if T.TYPE_CHECKING:
+ language = 'unset'
+ id = ''
+
+ def __init__(self, exelist: T.List[str], version: str,
+ for_machine: MachineChoice, info: 'MachineInfo',
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None, is_cross: bool = False):
+ self.exelist = exelist
# In case it's been overridden by a child class already
if not hasattr(self, 'file_suffixes'):
self.file_suffixes = lang_suffixes[self.language]
@@ -431,28 +465,24 @@ class Compiler(metaclass=abc.ABCMeta):
self.can_compile_suffixes = set(self.file_suffixes)
self.default_suffix = self.file_suffixes[0]
self.version = version
- if 'full_version' in kwargs:
- self.full_version = kwargs['full_version']
- else:
- self.full_version = None
+ self.full_version = full_version
self.for_machine = for_machine
- self.base_options = []
+ self.base_options = [] # type: T.List[str]
self.linker = linker
self.info = info
+ self.is_cross = is_cross
- def __repr__(self):
+ def __repr__(self) -> str:
repr_str = "<{0}: v{1} `{2}`>"
return repr_str.format(self.__class__.__name__, self.version,
' '.join(self.exelist))
@lru_cache(maxsize=None)
- def can_compile(self, src) -> bool:
- if hasattr(src, 'fname'):
+ def can_compile(self, src: 'mesonlib.FileOrString') -> bool:
+ if isinstance(src, mesonlib.File):
src = src.fname
suffix = os.path.splitext(src)[1].lower()
- if suffix and suffix[1:] in self.can_compile_suffixes:
- return True
- return False
+ return bool(suffix) and suffix[1:] in self.can_compile_suffixes
def get_id(self) -> str:
return self.id
@@ -482,42 +512,54 @@ class Compiler(metaclass=abc.ABCMeta):
def get_default_suffix(self) -> str:
return self.default_suffix
- def get_define(self, dname, prefix, env, extra_args, dependencies) -> T.Tuple[str, bool]:
+ def get_define(self, dname: str, prefix: str, env: 'Environment',
+ extra_args: T.List[str], dependencies: T.List['Dependency'],
+ disable_cache: bool = False) -> T.Tuple[str, bool]:
raise EnvironmentException('%s does not support get_define ' % self.get_id())
- def compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies) -> int:
+ def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]], dependencies: T.Optional[T.List['Dependency']]) -> int:
raise EnvironmentException('%s does not support compute_int ' % self.get_id())
- def compute_parameters_with_absolute_paths(self, parameter_list, build_dir):
+ def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
+ build_dir: str) -> T.List[str]:
raise EnvironmentException('%s does not support compute_parameters_with_absolute_paths ' % self.get_id())
- def has_members(self, typename, membernames, prefix, env, *,
- extra_args=None, dependencies=None) -> T.Tuple[bool, bool]:
+ def has_members(self, typename: str, membernames: T.List[str],
+ prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_member(s) ' % self.get_id())
- def has_type(self, typename, prefix, env, extra_args, *,
- dependencies=None) -> T.Tuple[bool, bool]:
+ def has_type(self, typename: str, prefix: str, env: 'Environment',
+ extra_args: T.List[str], *,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('%s does not support has_type ' % self.get_id())
- def symbols_have_underscore_prefix(self, env) -> bool:
+ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
raise EnvironmentException('%s does not support symbols_have_underscore_prefix ' % self.get_id())
- def get_exelist(self):
- return self.exelist[:]
+ def get_exelist(self) -> T.List[str]:
+ return self.exelist.copy()
def get_linker_exelist(self) -> T.List[str]:
return self.linker.get_exelist()
+ @abc.abstractmethod
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ pass
+
def get_linker_output_args(self, outputname: str) -> T.List[str]:
return self.linker.get_output_args(outputname)
- def get_builtin_define(self, *args, **kwargs):
+ def get_builtin_define(self, define: str) -> T.Optional[str]:
raise EnvironmentException('%s does not support get_builtin_define.' % self.id)
- def has_builtin_define(self, *args, **kwargs):
+ def has_builtin_define(self, define: str) -> bool:
raise EnvironmentException('%s does not support has_builtin_define.' % self.id)
- def get_always_args(self):
+ def get_always_args(self) -> T.List[str]:
return []
def can_linker_accept_rsp(self) -> bool:
@@ -529,10 +571,10 @@ class Compiler(metaclass=abc.ABCMeta):
def get_linker_always_args(self) -> T.List[str]:
return self.linker.get_always_args()
- def get_linker_lib_prefix(self):
+ def get_linker_lib_prefix(self) -> str:
return self.linker.get_lib_prefix()
- def gen_import_library_args(self, implibname):
+ def gen_import_library_args(self, implibname: str) -> T.List[str]:
"""
Used only on Windows for libraries that need an import library.
This currently means C, C++, Fortran.
@@ -544,62 +586,115 @@ class Compiler(metaclass=abc.ABCMeta):
is_cross: bool) -> T.List[str]:
return self.linker.get_args_from_envvars(for_machine, is_cross)
- def get_options(self) -> T.Dict[str, coredata.UserOption]:
+ def get_options(self) -> 'OptionDictType':
return {}
- def get_option_compile_args(self, options):
+ def get_option_compile_args(self, options: 'OptionDictType') -> T.List[str]:
return []
def get_option_link_args(self, options: 'OptionDictType') -> T.List[str]:
return self.linker.get_option_args(options)
- def check_header(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def check_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ """Check that header is usable.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
- def has_header(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
+ """Check that header is exists.
+
+ This check will return true if the file exists, even if it contains:
+
+ ```c
+ # error "You thought you could use this, LOLZ!"
+ ```
+
+ Use check_header if your header only works in some cases.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
raise EnvironmentException('Language %s does not support header checks.' % self.get_display_language())
- def has_header_symbol(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str,
+ env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
raise EnvironmentException('Language %s does not support header symbol checks.' % self.get_display_language())
- def compiles(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def compiles(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
raise EnvironmentException('Language %s does not support compile checks.' % self.get_display_language())
- def links(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def links(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
raise EnvironmentException('Language %s does not support link checks.' % self.get_display_language())
- def run(self, *args, **kwargs) -> RunResult:
+ def run(self, code: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> RunResult:
raise EnvironmentException('Language %s does not support run checks.' % self.get_display_language())
- def sizeof(self, *args, **kwargs) -> int:
+ def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
raise EnvironmentException('Language %s does not support sizeof checks.' % self.get_display_language())
- def alignment(self, *args, **kwargs) -> int:
+ def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
raise EnvironmentException('Language %s does not support alignment checks.' % self.get_display_language())
- def has_function(self, *args, **kwargs) -> T.Tuple[bool, bool]:
+ def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ """See if a function exists.
+
+ Returns a two item tuple of bools. The first bool is whether the
+ check succeeded, the second is whether the result was cached (True)
+ or run fresh (False).
+ """
raise EnvironmentException('Language %s does not support function checks.' % self.get_display_language())
- @classmethod
- def unix_args_to_native(cls, args: T.List[str]) -> T.List[str]:
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
"Always returns a copy that can be independently mutated"
- return args[:]
+ return args.copy()
@classmethod
def native_args_to_unix(cls, args: T.List[str]) -> T.List[str]:
"Always returns a copy that can be independently mutated"
- return args[:]
+ return args.copy()
- def find_library(self, *args, **kwargs):
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
raise EnvironmentException('Language {} does not support library finding.'.format(self.get_display_language()))
- def get_library_dirs(self, *args, **kwargs):
- return ()
+ def get_library_naming(self, env: 'Environment', libtype: LibType,
+ strict: bool = False) -> T.Optional[T.Tuple[str, ...]]:
+ raise EnvironmentException(
+ 'Language {} does not support get_library_naming.'.format(
+ self.get_display_language()))
- def get_program_dirs(self, *args, **kwargs):
+ def get_program_dirs(self, env: 'Environment') -> T.List[str]:
return []
- def has_multi_arguments(self, args, env) -> T.Tuple[bool, bool]:
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
raise EnvironmentException(
'Language {} does not support has_multi_arguments.'.format(
self.get_display_language()))
@@ -607,7 +702,8 @@ class Compiler(metaclass=abc.ABCMeta):
def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
return self.linker.has_multi_arguments(args, env)
- def _get_compile_output(self, dirname, mode):
+ def _get_compile_output(self, dirname: str, mode: str) -> str:
+ # TODO: mode should really be an enum
# In pre-processor mode, the output is sent to stdout and discarded
if mode == 'preprocess':
return None
@@ -619,8 +715,9 @@ class Compiler(metaclass=abc.ABCMeta):
suffix = 'obj'
return os.path.join(dirname, 'output.' + suffix)
- def get_compiler_args_for_mode(self, mode):
- args = []
+ def get_compiler_args_for_mode(self, mode: str) -> T.List[str]:
+ # TODO: mode should really be an enum
+ args = [] # type: T.List[str]
args += self.get_always_args()
if mode == 'compile':
args += self.get_compile_only_args()
@@ -633,7 +730,11 @@ class Compiler(metaclass=abc.ABCMeta):
return CompilerArgs(self, args)
@contextlib.contextmanager
- def compile(self, code: str, extra_args: list = None, *, mode: str = 'link', want_output: bool = False, temp_dir: str = None):
+ def compile(self, code: 'mesonlib.FileOrString',
+ extra_args: T.Union[None, CompilerArgs, T.List[str]] = None,
+ *, mode: str = 'link', want_output: bool = False,
+ temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]:
+ # TODO: there isn't really any reason for this to be a contextmanager
if extra_args is None:
extra_args = []
try:
@@ -646,8 +747,11 @@ class Compiler(metaclass=abc.ABCMeta):
ofile.write(code)
# ccache would result in a cache miss
no_ccache = True
+ contents = code
elif isinstance(code, mesonlib.File):
srcname = code.fname
+ with open(code.fname, 'r') as f:
+ contents = f.read()
# Construct the compiler command-line
commands = self.compiler_args()
@@ -662,69 +766,62 @@ class Compiler(metaclass=abc.ABCMeta):
# in the command line after '/link' is given to the linker.
commands += extra_args
# Generate full command-line with the exelist
- commands = self.get_exelist() + commands.to_native()
+ command_list = 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)
+ mlog.debug('Command line: ', ' '.join(command_list), '\n')
+ mlog.debug('Code:\n', contents)
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
if no_ccache:
os_env['CCACHE_DISABLE'] = '1'
- p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname, env=os_env)
- mlog.debug('Compiler stdout:\n', p.stdo)
- mlog.debug('Compiler stderr:\n', p.stde)
- p.commands = commands
- p.input_name = srcname
+ p, stdo, stde = Popen_safe(command_list, cwd=tmpdirname, env=os_env)
+ mlog.debug('Compiler stdout:\n', stdo)
+ mlog.debug('Compiler stderr:\n', stde)
+
+ result = CompileResult(stdo, stde, list(commands), p.returncode, p.pid, input_name=srcname)
if want_output:
- p.output_name = output
- p.cached = False # Make sure that the cached attribute always exists
- yield p
+ result.output_name = output
+ yield result
except 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
+ yield None
@contextlib.contextmanager
- def cached_compile(self, code, cdata: coredata.CoreData, *, extra_args=None, mode: str = 'link', temp_dir=None):
- assert(isinstance(cdata, coredata.CoreData))
+ def cached_compile(self, code: str, cdata: coredata.CoreData, *,
+ extra_args: T.Union[None, T.List[str], CompilerArgs] = None,
+ mode: str = 'link',
+ temp_dir: T.Optional[str] = None) -> T.Iterator[T.Optional[CompileResult]]:
+ # TODO: There's isn't really any reason for this to be a context manager
# Calculate the key
- textra_args = tuple(extra_args) if extra_args is not None else None
- key = (tuple(self.exelist), self.version, code, textra_args, mode)
-
- # Check if not cached
- if key not in cdata.compiler_check_cache:
+ textra_args = tuple(extra_args) if extra_args is not None else tuple() # type: T.Tuple[str, ...]
+ key = (tuple(self.exelist), self.version, code, textra_args, mode) # type: coredata.CompilerCheckCacheKey
+
+ # Check if not cached, and generate, otherwise get from the cache
+ if key in cdata.compiler_check_cache:
+ p = cdata.compiler_check_cache[key] # type: CompileResult
+ p.cached = True
+ mlog.debug('Using cached compile:')
+ mlog.debug('Cached command line: ', ' '.join(p.command), '\n')
+ mlog.debug('Code:\n', code)
+ mlog.debug('Cached compiler stdout:\n', p.stdout)
+ mlog.debug('Cached compiler stderr:\n', p.stderr)
+ yield p
+ else:
with self.compile(code, extra_args=extra_args, mode=mode, want_output=False, temp_dir=temp_dir) as p:
- # Remove all attributes except the following
- # This way the object can be serialized
- tokeep = ['args', 'commands', 'input_name', 'output_name',
- 'pid', 'returncode', 'stdo', 'stde', 'text_mode']
- todel = [x for x in vars(p).keys() if x not in tokeep]
- for i in todel:
- delattr(p, i)
- p.cached = False
cdata.compiler_check_cache[key] = p
yield p
- return
-
- # Return cached
- p = cdata.compiler_check_cache[key]
- p.cached = True
- mlog.debug('Using cached compile:')
- mlog.debug('Cached command line: ', ' '.join(p.commands), '\n')
- mlog.debug('Code:\n', code)
- mlog.debug('Cached compiler stdout:\n', p.stdo)
- mlog.debug('Cached compiler stderr:\n', p.stde)
- yield p
-
- def get_colorout_args(self, colortype):
+
+ def get_colorout_args(self, colortype: str) -> T.List[str]:
+ # TODO: colortype can probably be an emum
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):
+ def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
return []
def get_link_debugfile_name(self, targetfile: str) -> str:
@@ -748,10 +845,12 @@ class Compiler(metaclass=abc.ABCMeta):
def no_undefined_link_args(self) -> T.List[str]:
return self.linker.no_undefined_args()
- # Compiler arguments needed to enable the given instruction set.
- # May be [] meaning nothing needed or None meaning the given set
- # is not supported.
- def get_instruction_set_args(self, instruction_set):
+ def get_instruction_set_args(self, instruction_set: str) -> T.Optional[T.List[str]]:
+ """Compiler arguments needed to enable the given instruction set.
+
+ Return type ay be an empty list meaning nothing needed or None
+ meaning the given set is not supported.
+ """
return None
def build_rpath_args(self, env: 'Environment', build_dir: str, from_dir: str,
@@ -760,40 +859,40 @@ class Compiler(metaclass=abc.ABCMeta):
return self.linker.build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
- def thread_flags(self, env):
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
return []
- def openmp_flags(self):
+ def openmp_flags(self) -> T.List[str]:
raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language())
- def openmp_link_flags(self):
+ def openmp_link_flags(self) -> T.List[str]:
return self.openmp_flags()
- def language_stdlib_only_link_flags(self):
+ def language_stdlib_only_link_flags(self) -> T.List[str]:
return []
- def gnu_symbol_visibility_args(self, vistype):
+ def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
return []
- def get_gui_app_args(self, value):
+ def get_gui_app_args(self, value: bool) -> T.List[str]:
return []
- def has_func_attribute(self, name, env):
+ def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
raise EnvironmentException(
'Language {} does not support function attributes.'.format(self.get_display_language()))
- def get_pic_args(self):
+ def get_pic_args(self) -> T.List[str]:
m = 'Language {} does not support position-independent code'
raise EnvironmentException(m.format(self.get_display_language()))
- def get_pie_args(self):
+ def get_pie_args(self) -> T.List[str]:
m = 'Language {} does not support position-independent executable'
raise EnvironmentException(m.format(self.get_display_language()))
def get_pie_link_args(self) -> T.List[str]:
return self.linker.get_pie_args()
- def get_argument_syntax(self):
+ def get_argument_syntax(self) -> str:
"""Returns the argument family type.
Compilers fall into families if they try to emulate the command line
@@ -804,22 +903,19 @@ class Compiler(metaclass=abc.ABCMeta):
"""
return 'other'
- def get_profile_generate_args(self):
+ def get_profile_generate_args(self) -> T.List[str]:
raise EnvironmentException(
'%s does not support get_profile_generate_args ' % self.get_id())
- def get_profile_use_args(self):
+ def get_profile_use_args(self) -> T.List[str]:
raise EnvironmentException(
'%s does not support get_profile_use_args ' % self.get_id())
- def get_undefined_link_args(self) -> T.List[str]:
- return self.linker.get_undefined_link_args()
-
- def remove_linkerlike_args(self, args):
+ def remove_linkerlike_args(self, args: T.List[str]) -> T.List[str]:
rm_exact = ('-headerpad_max_install_names',)
rm_prefixes = ('-Wl,', '-L',)
rm_next = ('-L', '-framework',)
- ret = []
+ ret = [] # T.List[str]
iargs = iter(args)
for arg in iargs:
# Remove this argument
@@ -868,13 +964,13 @@ class Compiler(metaclass=abc.ABCMeta):
env, prefix, shlib_name, suffix, soversion,
darwin_versions, is_shared_module)
- def get_target_link_args(self, target):
+ def get_target_link_args(self, target: 'BuildTarget') -> T.List[str]:
return target.link_args
- def get_dependency_compile_args(self, dep):
+ def get_dependency_compile_args(self, dep: 'Dependency') -> T.List[str]:
return dep.get_compile_args()
- def get_dependency_link_args(self, dep):
+ def get_dependency_link_args(self, dep: 'Dependency') -> T.List[str]:
return dep.get_link_args()
@classmethod
@@ -883,33 +979,73 @@ class Compiler(metaclass=abc.ABCMeta):
"""
return []
+ def get_coverage_args(self) -> T.List[str]:
+ return []
+
def get_coverage_link_args(self) -> T.List[str]:
return self.linker.get_coverage_args()
def get_disable_assert_args(self) -> T.List[str]:
return []
+ def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ raise EnvironmentError('This compiler does not support Windows CRT selection')
-def get_largefile_args(compiler):
- '''
- Enable transparent large-file-support for 32-bit UNIX systems
- '''
- if not (compiler.get_argument_syntax() == 'msvc' or compiler.info.is_darwin()):
- # Enable large-file support unconditionally on all platforms other
- # than macOS and MSVC. macOS is now 64-bit-only so it doesn't
- # need anything special, and MSVC 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 []
+ def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ raise EnvironmentError('This compiler does not support Windows CRT selection')
+
+ def get_compile_only_args(self) -> T.List[str]:
+ return []
+
+ def get_preprocess_only_args(self) -> T.List[str]:
+ raise EnvironmentError('This compiler does not have a preprocessor')
+
+ def get_default_include_dirs(self) -> T.List[str]:
+ return []
+
+ def get_largefile_args(self) -> T.List[str]:
+ '''Enable transparent large-file-support for 32-bit UNIX systems'''
+ if not (self.get_argument_syntax() == 'msvc' or self.info.is_darwin()):
+ # Enable large-file support unconditionally on all platforms other
+ # than macOS and MSVC. macOS is now 64-bit-only so it doesn't
+ # need anything special, and MSVC 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 []
+
+ def get_library_dirs(self, env: 'Environment',
+ elf_class: T.Optional[int] = None) -> T.List[str]:
+ return []
+
+ def find_framework_paths(self, env: 'Environment') -> T.List[str]:
+ raise EnvironmentException('{} does not support find_framework_paths'.format(self.id))
+
+ def attribute_check_func(self, name: str) -> str:
+ raise EnvironmentException('{} does not support attribute checks'.format(self.id))
+
+ def get_pch_suffix(self) -> str:
+ raise EnvironmentException('{} does not support pre compiled headers'.format(self.id))
+
+ def get_pch_name(self, name: str) -> str:
+ raise EnvironmentException('{} does not support pre compiled headers'.format(self.id))
+
+ def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
+ raise EnvironmentException('{} does not support pre compiled headers'.format(self.id))
+
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ raise EnvironmentException('{} does not support function attributes'.format(self.id))
+
+ def name_string(self) -> str:
+ return ' '.join(self.exelist)
def get_args_from_envvars(lang: str,
@@ -953,7 +1089,7 @@ def get_global_options(lang: str,
comp: T.Type[Compiler],
for_machine: MachineChoice,
is_cross: bool,
- properties: Properties) -> T.Dict[str, coredata.UserOption]:
+ properties: Properties) -> 'OptionDictType':
"""Retreive options that apply to all compilers for a given language."""
description = 'Extra arguments passed to the {}'.format(lang)
opts = {
@@ -963,7 +1099,7 @@ def get_global_options(lang: str,
'link_args': coredata.UserArrayOption(
description + ' linker',
[], split_args=True, user_input=True, allow_dups=True),
- }
+ } # type: OptionDictType
# Get from env vars.
compile_args, link_args = get_args_from_envvars(
diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py
index 698c71a..b5dbdda 100644
--- a/mesonbuild/compilers/cpp.py
+++ b/mesonbuild/compilers/cpp.py
@@ -65,8 +65,8 @@ class CPPCompiler(CLikeCompiler, Compiler):
def __init__(self, exelist, version, for_machine: MachineChoice, is_cross: bool,
info: 'MachineInfo', exe_wrap: T.Optional[str] = None, **kwargs):
# If a child ObjCPP class has already set it, don't set it ourselves
- Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
- CLikeCompiler.__init__(self, is_cross, exe_wrap)
+ Compiler.__init__(self, exelist, version, for_machine, info, is_cross=is_cross, **kwargs)
+ CLikeCompiler.__init__(self, exe_wrap)
@staticmethod
def get_display_language():
@@ -77,7 +77,7 @@ class CPPCompiler(CLikeCompiler, Compiler):
def sanity_check(self, work_dir, environment):
code = 'class breakCCompiler;int main(void) { return 0; }\n'
- return self.sanity_check_impl(work_dir, environment, 'sanitycheckcpp.cc', code)
+ 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
diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py
index 843348e..b269aec 100644
--- a/mesonbuild/compilers/cs.py
+++ b/mesonbuild/compilers/cs.py
@@ -40,7 +40,6 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
info: 'MachineInfo', comp_id, runner=None):
super().__init__(exelist, version, for_machine, info)
self.id = comp_id
- self.is_cross = False
self.runner = runner
@classmethod
@@ -95,9 +94,6 @@ class CsCompiler(BasicLinkerIsCompilerMixin, Compiler):
return parameter_list
- def name_string(self):
- return ' '.join(self.exelist)
-
def get_pch_use_args(self, pch_dir, header):
return []
diff --git a/mesonbuild/compilers/cuda.py b/mesonbuild/compilers/cuda.py
index 934ad12..482d504 100644
--- a/mesonbuild/compilers/cuda.py
+++ b/mesonbuild/compilers/cuda.py
@@ -161,7 +161,7 @@ class CudaCompiler(Compiler):
mlog.debug('cudaGetDeviceCount() returned ' + stde)
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
- result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args, dependencies)
+ result, cached = super().has_header_symbol(hname, symbol, prefix, env, extra_args=extra_args, dependencies=dependencies)
if result:
return True, cached
if extra_args is None:
@@ -171,7 +171,7 @@ class CudaCompiler(Compiler):
#include <{header}>
using {symbol};
int main(void) {{ return 0; }}'''
- return self.compiles(t.format(**fargs), env, extra_args, dependencies)
+ return self.compiles(t.format(**fargs), env, extra_args=extra_args, dependencies=dependencies)
def get_options(self):
opts = super().get_options()
@@ -210,9 +210,6 @@ class CudaCompiler(Compiler):
def get_option_link_args(self, options):
return self._cook_link_args(self.host_compiler.get_option_link_args(self._to_host_compiler_options(options)))
- def name_string(self):
- return ' '.join(self.exelist)
-
def get_soname_args(self, *args):
return self._cook_link_args(self.host_compiler.get_soname_args(*args))
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
index a74dc95..ca7f80d 100644
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -30,6 +30,7 @@ from .compilers import (
from .mixins.gnu import GnuCompiler
if T.TYPE_CHECKING:
+ from ..dependencies import ExternalProgram
from ..envconfig import MachineInfo
d_feature_args = {'gcc': {'unittest': '-funittest',
@@ -442,13 +443,13 @@ class DCompiler(Compiler):
language = 'd'
- def __init__(self, exelist, version, for_machine: MachineChoice,
- info: 'MachineInfo', arch, is_cross, exe_wrapper, **kwargs):
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, exe_wrapper: T.Optional['ExternalProgram'] = None,
+ **kwargs):
super().__init__(exelist, version, for_machine, info, **kwargs)
self.id = 'unknown'
self.arch = arch
self.exe_wrapper = exe_wrapper
- self.is_cross = is_cross
def sanity_check(self, work_dir, environment):
source_name = os.path.join(work_dir, 'sanity.d')
@@ -633,18 +634,16 @@ class DCompiler(Compiler):
def thread_link_flags(self, env):
return self.linker.thread_flags(env)
- def name_string(self):
- return ' '.join(self.exelist)
-
class GnuDCompiler(GnuCompiler, DCompiler):
# we mostly want DCompiler, but that gives us the Compiler.LINKER_PREFIX instead
LINKER_PREFIX = GnuCompiler.LINKER_PREFIX
- def __init__(self, exelist, version, for_machine: MachineChoice,
- info: 'MachineInfo', is_cross, exe_wrapper, arch, **kwargs):
- DCompiler.__init__(self, exelist, version, for_machine, info, is_cross, exe_wrapper, arch, **kwargs)
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, *, exe_wrapper: T.Optional['ExternalProgram'] = None,
+ **kwargs):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch, exe_wrapper=exe_wrapper, **kwargs)
GnuCompiler.__init__(self, {})
self.id = 'gcc'
default_warn_args = ['-Wall', '-Wdeprecated']
@@ -698,9 +697,9 @@ class GnuDCompiler(GnuCompiler, DCompiler):
class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- info: 'MachineInfo', arch, **kwargs):
- DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs)
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, **kwargs):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch, **kwargs)
self.id = 'llvm'
self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
@@ -750,9 +749,9 @@ class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- info: 'MachineInfo', arch, **kwargs):
- DCompiler.__init__(self, exelist, version, for_machine, info, arch, False, None, **kwargs)
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ info: 'MachineInfo', arch: str, **kwargs):
+ DCompiler.__init__(self, exelist, version, for_machine, info, arch, **kwargs)
self.id = 'dmd'
self.base_options = ['b_coverage', 'b_colorout', 'b_vscrt', 'b_ndebug']
diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py
index 7ca3073..9c2f5bf 100644
--- a/mesonbuild/compilers/fortran.py
+++ b/mesonbuild/compilers/fortran.py
@@ -44,9 +44,9 @@ class FortranCompiler(CLikeCompiler, Compiler):
language = 'fortran'
def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs):
- Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
- CLikeCompiler.__init__(self, is_cross, exe_wrapper)
+ is_cross: bool, info: 'MachineInfo', exe_wrapper=None, **kwargs):
+ Compiler.__init__(self, exelist, version, for_machine, info, is_cross=is_cross, **kwargs)
+ CLikeCompiler.__init__(self, exe_wrapper)
self.id = 'unknown'
def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
@@ -143,7 +143,7 @@ class FortranCompiler(CLikeCompiler, Compiler):
def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
code = 'stop; end program'
- return self.find_library_impl(libname, env, extra_dirs, code, libtype)
+ return self._find_library_impl(libname, env, extra_dirs, code, libtype)
def has_multi_arguments(self, args: T.Sequence[str], env):
for arg in args[:]:
diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py
index 5aeb250..8405b43 100644
--- a/mesonbuild/compilers/java.py
+++ b/mesonbuild/compilers/java.py
@@ -32,7 +32,6 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
info: 'MachineInfo'):
super().__init__(exelist, version, for_machine, info)
self.id = 'unknown'
- self.is_cross = False
self.javarunner = 'java'
def get_werror_args(self):
@@ -64,9 +63,6 @@ class JavaCompiler(BasicLinkerIsCompilerMixin, Compiler):
def get_pic_args(self):
return []
- def name_string(self):
- return ' '.join(self.exelist)
-
def get_pch_use_args(self, pch_dir, header):
return []
diff --git a/mesonbuild/compilers/mixins/arm.py b/mesonbuild/compilers/mixins/arm.py
index b331d8f..25fb545 100644
--- a/mesonbuild/compilers/mixins/arm.py
+++ b/mesonbuild/compilers/mixins/arm.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2019 The Meson development team
+# Copyright 2012-2020 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.
@@ -15,15 +15,22 @@
"""Representations specific to the arm family of compilers."""
import os
-import re
import typing as T
from ... import mesonlib
+from ...linkers import ArmClangDynamicLinker
from ..compilers import clike_debug_args
from .clang import clang_color_args
if T.TYPE_CHECKING:
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
arm_buildtype_args = {
'plain': [],
@@ -62,9 +69,11 @@ armclang_optimization_args = {
} # type: T.Dict[str, T.List[str]]
-class ArmCompiler:
- # Functionality that is common to all ARM family compilers.
- def __init__(self):
+class ArmCompiler(Compiler):
+
+ """Functionality that is common to all ARM family compilers."""
+
+ def __init__(self) -> None:
if not self.is_cross:
raise mesonlib.EnvironmentException('armcc supports only cross-compilation.')
self.id = 'arm'
@@ -126,32 +135,15 @@ class ArmCompiler:
return parameter_list
-class ArmclangCompiler:
- def __init__(self):
+class ArmclangCompiler(Compiler):
+
+ def __init__(self) -> None:
if not self.is_cross:
raise mesonlib.EnvironmentException('armclang supports only cross-compilation.')
# Check whether 'armlink' is available in path
- self.linker_exe = 'armlink'
- args = '--vsn'
- try:
- p, stdo, stderr = mesonlib.Popen_safe(self.linker_exe, args)
- except OSError as e:
- err_msg = 'Unknown linker\nRunning "{0}" gave \n"{1}"'.format(' '.join([self.linker_exe] + [args]), e)
- raise mesonlib.EnvironmentException(err_msg)
- # Verify the armlink version
- ver_str = re.search('.*Component.*', stdo)
- if ver_str:
- ver_str = ver_str.group(0)
- else:
- raise mesonlib.EnvironmentException('armlink version string not found')
- assert ver_str # makes mypy happy
- # Using the regular expression from environment.search_version,
- # which is used for searching compiler version
- version_regex = r'(?<!(\d|\.))(\d{1,2}(\.\d+)+(-[a-zA-Z0-9]+)?)'
- linker_ver = re.search(version_regex, ver_str)
- if linker_ver:
- linker_ver = linker_ver.group(0)
- if not mesonlib.version_compare(self.version, '==' + linker_ver):
+ if not isinstance(self.linker, ArmClangDynamicLinker):
+ raise mesonlib.EnvironmentException('Unsupported Linker {}, must be armlink'.format(self.linker.exelist))
+ if not mesonlib.version_compare(self.version, '==' + self.linker.version):
raise mesonlib.EnvironmentException('armlink version does not match with compiler version')
self.id = 'armclang'
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_sanitize', 'b_coverage',
diff --git a/mesonbuild/compilers/mixins/c2000.py b/mesonbuild/compilers/mixins/c2000.py
index 65a2cea..aca1ee8 100644
--- a/mesonbuild/compilers/mixins/c2000.py
+++ b/mesonbuild/compilers/mixins/c2000.py
@@ -21,6 +21,13 @@ from ...mesonlib import EnvironmentException
if T.TYPE_CHECKING:
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
c2000_buildtype_args = {
'plain': [],
@@ -46,8 +53,9 @@ c2000_debug_args = {
} # type: T.Dict[bool, T.List[str]]
-class C2000Compiler:
- def __init__(self):
+class C2000Compiler(Compiler):
+
+ def __init__(self) -> None:
if not self.is_cross:
raise EnvironmentException('c2000 supports only cross-compilation.')
self.id = 'c2000'
diff --git a/mesonbuild/compilers/mixins/ccrx.py b/mesonbuild/compilers/mixins/ccrx.py
index b859215..fb82797 100644
--- a/mesonbuild/compilers/mixins/ccrx.py
+++ b/mesonbuild/compilers/mixins/ccrx.py
@@ -21,6 +21,13 @@ from ...mesonlib import EnvironmentException
if T.TYPE_CHECKING:
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
ccrx_buildtype_args = {
'plain': [],
@@ -46,8 +53,13 @@ ccrx_debug_args = {
} # type: T.Dict[bool, T.List[str]]
-class CcrxCompiler:
- def __init__(self):
+class CcrxCompiler(Compiler):
+
+ if T.TYPE_CHECKING:
+ is_cross = True
+ can_compile_suffixes = set() # type: T.Set[str]
+
+ def __init__(self) -> None:
if not self.is_cross:
raise EnvironmentException('ccrx supports only cross-compilation.')
self.id = 'ccrx'
diff --git a/mesonbuild/compilers/mixins/clang.py b/mesonbuild/compilers/mixins/clang.py
index 7525c12..8c85944 100644
--- a/mesonbuild/compilers/mixins/clang.py
+++ b/mesonbuild/compilers/mixins/clang.py
@@ -42,6 +42,7 @@ clang_optimization_args = {
} # type: T.Dict[str, T.List[str]]
class ClangCompiler(GnuLikeCompiler):
+
def __init__(self, defines: T.Optional[T.Dict[str, str]]):
super().__init__()
self.id = 'clang'
@@ -75,17 +76,15 @@ class ClangCompiler(GnuLikeCompiler):
# so it might change semantics at any time.
return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))]
- def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.List[str]:
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
myargs = ['-Werror=unknown-warning-option', '-Werror=unused-command-line-argument']
if mesonlib.version_compare(self.version, '>=3.6.0'):
myargs.append('-Werror=ignored-optimization-argument')
- return super().has_multi_arguments(
- myargs + args,
- env)
+ return super().has_multi_arguments(myargs + args, env)
def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
extra_args: T.Optional[T.List[str]] = None,
- dependencies: T.Optional[T.List['Dependency']] = None) -> bool:
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
extra_args = []
# Starting with XCode 8, we need to pass this to force linker
@@ -96,7 +95,7 @@ class ClangCompiler(GnuLikeCompiler):
if isinstance(self.linker, AppleDynamicLinker) and mesonlib.version_compare(self.version, '>=8.0'):
extra_args.append('-Wl,-no_weak_imports')
return super().has_function(funcname, prefix, env, extra_args=extra_args,
- dependencies=dependencies)
+ dependencies=dependencies)
def openmp_flags(self) -> T.List[str]:
if mesonlib.version_compare(self.version, '>=3.8.0'):
@@ -125,7 +124,7 @@ class ClangCompiler(GnuLikeCompiler):
return ['-fuse-ld={}'.format(linker)]
return super().use_linker_args(linker)
- def get_has_func_attribute_extra_args(self, name):
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
# Clang only warns about unknown or ignored attributes, so force an
# error.
return ['-Werror=attributes']
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index b2942d3..e146f5f 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -20,6 +20,8 @@ of this is to have mixin's, which are classes that are designed *not* to be
standalone, they only work through inheritance.
"""
+import contextlib
+import collections
import functools
import glob
import itertools
@@ -38,7 +40,15 @@ from .. import compilers
from .visualstudio import VisualStudioLikeCompiler
if T.TYPE_CHECKING:
+ from ...dependencies import Dependency, ExternalProgram
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
GROUP_FLAGS = re.compile(r'''\.so (?:\.[0-9]+)? (?:\.[0-9]+)? (?:\.[0-9]+)?$ |
^(?:-Wl,)?-l |
@@ -55,6 +65,9 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')
def to_native(self, copy: bool = False) -> T.List[str]:
+ # This seems to be allowed, but could never work?
+ assert isinstance(self.compiler, compilers.Compiler), 'How did you get here'
+
# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
# needed by static libraries that are provided by object files or
@@ -82,8 +95,8 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
new.insert(group_end + 1, '-Wl,--end-group')
new.insert(group_start, '-Wl,--start-group')
# Remove system/default include paths added with -isystem
- if hasattr(self.compiler, 'get_default_include_dirs'):
- default_dirs = self.compiler.get_default_include_dirs()
+ default_dirs = self.compiler.get_default_include_dirs()
+ if default_dirs:
bad_idx_list = [] # type: T.List[int]
for i, each in enumerate(new):
if not each.startswith('-isystem'):
@@ -107,20 +120,20 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
return 'CLikeCompilerArgs({!r}, {!r})'.format(self.compiler, self._container)
-class CLikeCompiler:
+class CLikeCompiler(Compiler):
"""Shared bits for the C and CPP Compilers."""
+ if T.TYPE_CHECKING:
+ warn_args = {} # type: T.Dict[str, T.List[str]]
+
# TODO: Replace this manual cache with functools.lru_cache
- library_dirs_cache = {}
- program_dirs_cache = {}
- find_library_cache = {}
- find_framework_cache = {}
+ find_library_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], str, LibType], T.Optional[T.List[str]]]
+ find_framework_cache = {} # type: T.Dict[T.Tuple[T.Tuple[str, ...], str, T.Tuple[str, ...], bool], T.Optional[T.List[str]]]
internal_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS
- def __init__(self, is_cross: bool, exe_wrapper: T.Optional[str] = None):
+ def __init__(self, exe_wrapper: T.Optional['ExternalProgram'] = None):
# If a child ObjC or CPP class has already set it, don't set it ourselves
- self.is_cross = is_cross
self.can_compile_suffixes.add('h')
# If the exe wrapper was not found, pretend it wasn't set so that the
# sanity check is skipped and compiler checks use fallbacks.
@@ -130,69 +143,71 @@ class CLikeCompiler:
self.exe_wrapper = exe_wrapper.get_command()
def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CLikeCompilerArgs:
+ # This is correct, mypy just doesn't understand co-operative inheritance
return CLikeCompilerArgs(self, args)
- def needs_static_linker(self):
+ def needs_static_linker(self) -> bool:
return True # When compiling static libraries, so yes.
- def get_always_args(self):
+ def get_always_args(self) -> T.List[str]:
'''
Args that are always-on for all C compilers other than MSVC
'''
- return ['-pipe'] + compilers.get_largefile_args(self)
+ return ['-pipe'] + self.get_largefile_args()
- def get_no_stdinc_args(self):
+ def get_no_stdinc_args(self) -> T.List[str]:
return ['-nostdinc']
- def get_no_stdlib_link_args(self):
+ def get_no_stdlib_link_args(self) -> T.List[str]:
return ['-nostdlib']
- def get_warn_args(self, level):
+ def get_warn_args(self, level: str) -> T.List[str]:
+ # TODO: this should be an enum
return self.warn_args[level]
- def get_no_warn_args(self):
+ def get_no_warn_args(self) -> T.List[str]:
# Almost every compiler uses this for disabling warnings
return ['-w']
- def split_shlib_to_parts(self, fname):
+ def split_shlib_to_parts(self, fname: str) -> T.Tuple[T.Optional[str], str]:
return None, fname
- def depfile_for_object(self, objfile):
+ def depfile_for_object(self, objfile: str) -> str:
return objfile + '.' + self.get_depfile_suffix()
- def get_depfile_suffix(self):
+ def get_depfile_suffix(self) -> str:
return 'd'
- def get_exelist(self):
- return self.exelist[:]
+ def get_exelist(self) -> T.List[str]:
+ return self.exelist.copy()
- def get_preprocess_only_args(self):
+ def get_preprocess_only_args(self) -> T.List[str]:
return ['-E', '-P']
- def get_compile_only_args(self):
+ def get_compile_only_args(self) -> T.List[str]:
return ['-c']
- def get_no_optimization_args(self):
+ def get_no_optimization_args(self) -> T.List[str]:
return ['-O0']
- def get_compiler_check_args(self):
+ def get_compiler_check_args(self) -> T.List[str]:
'''
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):
+ def get_output_args(self, target: str) -> T.List[str]:
return ['-o', target]
- def get_werror_args(self):
+ def get_werror_args(self) -> T.List[str]:
return ['-Werror']
- def get_std_exe_link_args(self):
+ def get_std_exe_link_args(self) -> T.List[str]:
# TODO: is this a linker property?
return []
- def get_include_args(self, path, is_system):
+ def get_include_args(self, path: str, is_system: bool) -> T.List[str]:
if path == '':
path = '.'
if is_system:
@@ -206,7 +221,9 @@ class CLikeCompiler:
return []
@functools.lru_cache()
- def get_library_dirs(self, env, elf_class = None):
+ def _get_library_dirs(self, env: 'Environment',
+ elf_class: T.Optional[int] = None) -> T.List[str]:
+ # TODO: replace elf_class with enum
dirs = self.get_compiler_dirs(env, 'libraries')
if elf_class is None or elf_class == 0:
return dirs
@@ -241,22 +258,29 @@ class CLikeCompiler:
# Skip the file if we can't read it
pass
- return tuple(retval)
+ return retval
+
+ def get_library_dirs(self, env: 'Environment',
+ elf_class: T.Optional[int] = None) -> T.List[str]:
+ """Wrap the lru_cache so that we return a new copy and don't allow
+ mutation of the cached value.
+ """
+ return self._get_library_dirs(env, elf_class).copy()
@functools.lru_cache()
- def get_program_dirs(self, env):
+ def _get_program_dirs(self, env: 'Environment') -> T.List[str]:
'''
Programs used by the compiler. Also where toolchain DLLs such as
libstdc++-6.dll are found with MinGW.
'''
return self.get_compiler_dirs(env, 'programs')
+ def get_program_dirs(self, env: 'Environment') -> T.List[str]:
+ return self._get_program_dirs(env).copy()
+
def get_pic_args(self) -> T.List[str]:
return ['-fPIC']
- def name_string(self) -> str:
- return ' '.join(self.exelist)
-
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
return ['-include', os.path.basename(header)]
@@ -266,7 +290,7 @@ class CLikeCompiler:
def get_linker_search_args(self, dirname: str) -> T.List[str]:
return self.linker.get_search_args(dirname)
- def get_default_include_dirs(self):
+ def get_default_include_dirs(self) -> T.List[str]:
return []
def gen_export_dynamic_link_args(self, env: 'Environment') -> T.List[str]:
@@ -275,7 +299,8 @@ class CLikeCompiler:
def gen_import_library_args(self, implibname: str) -> T.List[str]:
return self.linker.import_library_args(implibname)
- def sanity_check_impl(self, work_dir, environment, sname, code):
+ def _sanity_check_impl(self, work_dir: str, environment: 'Environment',
+ sname: str, code: str) -> None:
mlog.debug('Sanity testing ' + self.get_display_language() + ' compiler:', ' '.join(self.exelist))
mlog.debug('Is cross compiler: %s.' % str(self.is_cross))
@@ -330,18 +355,23 @@ class CLikeCompiler:
if pe.returncode != 0:
raise mesonlib.EnvironmentException('Executables created by {0} compiler {1} are not runnable.'.format(self.language, self.name_string()))
- def sanity_check(self, work_dir, environment):
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
code = 'int main(void) { int class=0; return class; }\n'
- return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
+ return self._sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
- def check_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None):
+ def check_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#include <{header}>'''
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def has_header(self, hname: str, prefix: str, env, *, extra_args=None, dependencies=None, disable_cache: bool = False):
+ def has_header(self, hname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix}
#ifdef __has_include
@@ -354,7 +384,10 @@ class CLikeCompiler:
return self.compiles(code.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies, mode='preprocess', disable_cache=disable_cache)
- def has_header_symbol(self, hname: str, symbol: str, prefix: str, env, *, extra_args=None, dependencies=None):
+ def has_header_symbol(self, hname: str, symbol: str, prefix: str,
+ env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix}
#include <{header}>
@@ -368,8 +401,9 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def _get_basic_compiler_args(self, env, mode: str):
- cargs, largs = [], []
+ def _get_basic_compiler_args(self, env: 'Environment', mode: str) -> T.Tuple[T.List[str], T.List[str]]:
+ cargs = [] # type: T.List[str]
+ largs = [] # type: T.List[str]
if mode == 'link':
# Sometimes we need to manually select the CRT to use with MSVC.
# One example is when trying to do a compiler check that involves
@@ -405,20 +439,26 @@ class CLikeCompiler:
cargs += self.get_compiler_args_for_mode(mode)
return cargs, largs
- def _get_compiler_check_args(self, env, extra_args: list, dependencies, mode: str = 'compile') -> T.List[str]:
+ def _get_compiler_check_args(self, env: 'Environment',
+ extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]],
+ dependencies: T.Optional[T.List['Dependency']],
+ mode: str = 'compile') -> arglist.CompilerArgs:
+ # TODO: the caller should handle the listfing of these arguments
if extra_args is None:
extra_args = []
else:
+ # TODO: we want to do this in the caller
extra_args = mesonlib.listify(extra_args)
extra_args = mesonlib.listify([e(mode) if callable(e) else e for e in extra_args])
if dependencies is None:
dependencies = []
- elif not isinstance(dependencies, list):
- dependencies = [dependencies]
+ elif not isinstance(dependencies, collections.abc.Iterable):
+ # TODO: we want to ensure the front end does the listifing here
+ dependencies = [dependencies] # type: ignore
# Collect compiler arguments
- cargs = self.compiler_args()
- largs = []
+ cargs = self.compiler_args() # type: arglist.CompilerArgs
+ largs = [] # type: T.List[str]
for d in dependencies:
# Add compile flags needed by dependencies
cargs += d.get_compile_args()
@@ -440,23 +480,40 @@ class CLikeCompiler:
args = cargs + extra_args + largs
return args
- def compiles(self, code: str, env, *,
- extra_args: T.Sequence[T.Union[T.Sequence[str], str]] = None,
- dependencies=None, mode: str = 'compile', disable_cache: bool = False) -> T.Tuple[bool, bool]:
+ def compiles(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], arglist.CompilerArgs] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
with self._build_wrapper(code, env, extra_args, dependencies, mode, disable_cache=disable_cache) as p:
return p.returncode == 0, p.cached
- def _build_wrapper(self, code: str, env, extra_args, dependencies=None, mode: str = 'compile', want_output: bool = False, disable_cache: bool = False, temp_dir: str = None) -> T.Tuple[bool, bool]:
+ @contextlib.contextmanager
+ def _build_wrapper(self, code: str, env: 'Environment',
+ extra_args: T.Union[None, arglist.CompilerArgs, T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile', want_output: bool = False,
+ disable_cache: bool = False,
+ temp_dir: str = None) -> T.Iterator[T.Optional[compilers.CompileResult]]:
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
if disable_cache or want_output:
- return self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir)
- return self.cached_compile(code, env.coredata, extra_args=args, mode=mode, temp_dir=env.scratch_dir)
-
- def links(self, code, env, *, extra_args=None, dependencies=None, disable_cache=False):
+ with self.compile(code, extra_args=args, mode=mode, want_output=want_output, temp_dir=env.scratch_dir) as r:
+ yield r
+ else:
+ with self.cached_compile(code, env.coredata, extra_args=args, mode=mode, temp_dir=env.scratch_dir) as r:
+ yield r
+
+ def links(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], arglist.CompilerArgs] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
return self.compiles(code, env, extra_args=extra_args,
dependencies=dependencies, mode='link', disable_cache=disable_cache)
- def run(self, code: str, env, *, extra_args=None, dependencies=None):
+ def run(self, code: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> compilers.RunResult:
need_exe_wrapper = env.need_exe_wrapper(self.for_machine)
if need_exe_wrapper and self.exe_wrapper is None:
raise compilers.CrossNoRunException('Can not run test applications in this cross environment.')
@@ -469,7 +526,7 @@ class CLikeCompiler:
if need_exe_wrapper:
cmdlist = self.exe_wrapper + [p.output_name]
else:
- cmdlist = p.output_name
+ cmdlist = [p.output_name]
try:
pe, so, se = mesonlib.Popen_safe(cmdlist)
except Exception as e:
@@ -482,7 +539,9 @@ class CLikeCompiler:
mlog.debug(se)
return compilers.RunResult(True, pe.returncode, so, se)
- def _compile_int(self, expression, prefix, env, extra_args, dependencies):
+ def _compile_int(self, expression: str, prefix: str, env: 'Environment',
+ extra_args: T.Optional[T.List[str]],
+ dependencies: T.Optional[T.List['Dependency']]) -> bool:
fargs = {'prefix': prefix, 'expression': expression}
t = '''#include <stdio.h>
{prefix}
@@ -490,7 +549,10 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)[0]
- def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies):
+ def cross_compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str, env: 'Environment',
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
# Try user's guess first
if isinstance(guess, int):
if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies):
@@ -538,7 +600,10 @@ class CLikeCompiler:
return low
- def compute_int(self, expression, low, high, guess, prefix, env, *, extra_args=None, dependencies=None):
+ def compute_int(self, expression: str, low: T.Optional[int], high: T.Optional[int],
+ guess: T.Optional[int], prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
@@ -558,7 +623,9 @@ class CLikeCompiler:
raise mesonlib.EnvironmentException('Could not run compute_int test binary.')
return int(res.stdout)
- def cross_sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
+ def cross_sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@@ -573,7 +640,9 @@ class CLikeCompiler:
return -1
return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies)
- def sizeof(self, typename, prefix, env, *, extra_args=None, dependencies=None):
+ def sizeof(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@@ -594,7 +663,9 @@ class CLikeCompiler:
raise mesonlib.EnvironmentException('Could not run sizeof test binary.')
return int(res.stdout)
- def cross_alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
+ def cross_alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename}
@@ -615,7 +686,9 @@ class CLikeCompiler:
}};'''
return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t.format(**fargs), env, extra_args, dependencies)
- def alignment(self, typename, prefix, env, *, extra_args=None, dependencies=None):
+ def alignment(self, typename: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> int:
if extra_args is None:
extra_args = []
if self.is_cross:
@@ -644,7 +717,10 @@ class CLikeCompiler:
raise mesonlib.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, disable_cache=False):
+ def get_define(self, dname: str, prefix: str, env: 'Environment',
+ extra_args: T.Optional[T.List[str]],
+ dependencies: T.Optional[T.List['Dependency']],
+ disable_cache: bool = False) -> T.Tuple[str, bool]:
delim = '"MESON_GET_DEFINE_DELIMITER"'
fargs = {'prefix': prefix, 'define': dname, 'delim': delim}
code = '''
@@ -655,9 +731,9 @@ class CLikeCompiler:
{delim}\n{define}'''
args = self._get_compiler_check_args(env, extra_args, dependencies,
mode='preprocess').to_native()
- func = lambda: self.cached_compile(code.format(**fargs), env.coredata, extra_args=args, mode='preprocess')
+ func = functools.partial(self.cached_compile, code.format(**fargs), env.coredata, extra_args=args, mode='preprocess')
if disable_cache:
- func = lambda: self.compile(code.format(**fargs), extra_args=args, mode='preprocess', temp_dir=env.scratch_dir)
+ func = functools.partial(self.compile, code.format(**fargs), extra_args=args, mode='preprocess', temp_dir=env.scratch_dir)
with func() as p:
cached = p.cached
if p.returncode != 0:
@@ -665,9 +741,13 @@ class CLikeCompiler:
# Get the preprocessed value after the delimiter,
# minus the extra newline at the end and
# merge string literals.
- return self.concatenate_string_literals(p.stdo.split(delim + '\n')[-1][:-1]), cached
+ return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]), cached
- def get_return_value(self, fname, rtype, prefix, env, extra_args, dependencies):
+ def get_return_value(self, fname: str, rtype: str, prefix: str,
+ env: 'Environment', extra_args: T.Optional[T.List[str]],
+ dependencies: T.Optional[T.List['Dependency']]) -> T.Union[str, int]:
+ # TODO: rtype should be an enum.
+ # TODO: maybe we can use overload to tell mypy when this will return int vs str?
if rtype == 'string':
fmt = '%s'
cast = '(char*)'
@@ -695,9 +775,10 @@ class CLikeCompiler:
except ValueError:
m = 'Return value of {}() is not an int'
raise mesonlib.EnvironmentException(m.format(fname))
+ assert False, 'Unreachable'
@staticmethod
- def _no_prototype_templ():
+ def _no_prototype_templ() -> T.Tuple[str, str]:
"""
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
@@ -732,7 +813,7 @@ class CLikeCompiler:
return head, main
@staticmethod
- def _have_prototype_templ():
+ def _have_prototype_templ() -> T.Tuple[str, str]:
"""
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.
@@ -752,8 +833,11 @@ class CLikeCompiler:
}}'''
return head, main
- def has_function(self, funcname, prefix, env, *, extra_args=None, dependencies=None):
- """
+ def has_function(self, funcname: str, prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
+ """Determine if a function exists.
+
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
@@ -773,7 +857,11 @@ class CLikeCompiler:
return val, False
raise mesonlib.EnvironmentException('Cross variable {0} is not a boolean.'.format(varname))
- fargs = {'prefix': prefix, 'func': funcname}
+ # TODO: we really need a protocol for this,
+ #
+ # class StrProto(typing.Protocol):
+ # def __str__(self) -> str: ...
+ fargs = {'prefix': prefix, 'func': funcname} # type: T.Dict[str, T.Union[str, bool, int]]
# 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
@@ -845,7 +933,10 @@ class CLikeCompiler:
return self.links(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def has_members(self, typename, membernames, prefix, env, *, extra_args=None, dependencies=None):
+ def has_members(self, typename: str, membernames: T.List[str],
+ prefix: str, env: 'Environment', *,
+ extra_args: T.Optional[T.List[str]] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'type': typename, 'name': 'foo'}
@@ -862,7 +953,8 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def has_type(self, typename, prefix, env, extra_args, dependencies=None):
+ def has_type(self, typename: str, prefix: str, env: 'Environment', extra_args: T.List[str],
+ dependencies: T.Optional[T.List['Dependency']] = None) -> T.Tuple[bool, bool]:
fargs = {'prefix': prefix, 'type': typename}
t = '''{prefix}
void bar(void) {{
@@ -871,7 +963,7 @@ class CLikeCompiler:
return self.compiles(t.format(**fargs), env, extra_args=extra_args,
dependencies=dependencies)
- def symbols_have_underscore_prefix(self, env):
+ def symbols_have_underscore_prefix(self, env: 'Environment') -> bool:
'''
Check if the compiler prefixes an underscore to global C symbols
'''
@@ -889,7 +981,7 @@ class CLikeCompiler:
with self._build_wrapper(code, env, extra_args=args, mode='compile', want_output=True, temp_dir=env.scratch_dir) as p:
if p.returncode != 0:
m = 'BUG: Unable to compile {!r} check: {}'
- raise RuntimeError(m.format(n, p.stdo))
+ raise RuntimeError(m.format(n, p.stdout))
if not os.path.isfile(p.output_name):
m = 'BUG: Can\'t find compiled test code for {!r} check'
raise RuntimeError(m.format(n))
@@ -906,8 +998,8 @@ class CLikeCompiler:
return False
raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n))
- def _get_patterns(self, env, prefixes, suffixes, shared=False):
- patterns = []
+ def _get_patterns(self, env: 'Environment', prefixes: T.List[str], suffixes: T.List[str], shared: bool = False) -> T.List[str]:
+ patterns = [] # type: T.List[str]
for p in prefixes:
for s in suffixes:
patterns.append(p + '{}.' + s)
@@ -923,7 +1015,7 @@ class CLikeCompiler:
patterns.append(p + '{}.so.[0-9]*.[0-9]*')
return patterns
- def get_library_naming(self, env, libtype: LibType, strict=False):
+ def get_library_naming(self, env: 'Environment', libtype: LibType, strict: bool = False) -> T.Tuple[str, ...]:
'''
Get library prefixes and suffixes for the target platform ordered by
priority
@@ -970,8 +1062,8 @@ class CLikeCompiler:
return tuple(patterns)
@staticmethod
- def _sort_shlibs_openbsd(libs):
- filtered = []
+ def _sort_shlibs_openbsd(libs: T.List[str]) -> T.List[str]:
+ filtered = [] # type: T.List[str]
for lib in libs:
# Validate file as a shared library of type libfoo.so.X.Y
ret = lib.rsplit('.so.', maxsplit=1)
@@ -986,7 +1078,7 @@ class CLikeCompiler:
return sorted(filtered, key=float_cmp, reverse=True)
@classmethod
- def _get_trials_from_pattern(cls, pattern, directory, libname):
+ def _get_trials_from_pattern(cls, pattern: str, directory: str, libname: str) -> T.List[Path]:
f = Path(directory) / pattern.format(libname)
# Globbing for OpenBSD
if '*' in pattern:
@@ -996,7 +1088,7 @@ class CLikeCompiler:
return [f]
@staticmethod
- def _get_file_from_list(env, files: T.List[str]) -> Path:
+ def _get_file_from_list(env: 'Environment', paths: T.List[Path]) -> Path:
'''
We just check whether the library exists. We can't do a link check
because the library might have unresolved symbols that require other
@@ -1004,7 +1096,6 @@ class CLikeCompiler:
architecture.
'''
# If not building on macOS for Darwin, do a simple file check
- paths = [Path(f) for f in files]
if not env.machines.host.is_darwin() or not env.machines.build.is_darwin():
for p in paths:
if p.is_file():
@@ -1022,13 +1113,13 @@ class CLikeCompiler:
return None
@functools.lru_cache()
- def output_is_64bit(self, env):
+ def output_is_64bit(self, env: 'Environment') -> bool:
'''
returns true if the output produced is 64-bit, false if 32-bit
'''
return self.sizeof('void *', '', env) == 8
- def find_library_real(self, libname, env, extra_dirs, code, libtype: LibType):
+ def _find_library_real(self, libname: str, env: 'Environment', extra_dirs: T.List[str], code: str, libtype: LibType) -> T.Optional[T.List[str]]:
# First try if we can just add the library as -l.
# Gcc + co seem to prefer builtin lib dirs to -L dirs.
# Only try to find std libs if no extra dirs specified.
@@ -1061,16 +1152,17 @@ class CLikeCompiler:
# Search in the specified dirs, and then in the system libraries
for d in itertools.chain(extra_dirs, self.get_library_dirs(env, elf_class)):
for p in patterns:
- trial = self._get_trials_from_pattern(p, d, libname)
- if not trial:
+ trials = self._get_trials_from_pattern(p, d, libname)
+ if not trials:
continue
- trial = self._get_file_from_list(env, trial)
+ trial = self._get_file_from_list(env, trials)
if not trial:
continue
return [trial.as_posix()]
return None
- def find_library_impl(self, libname, env, extra_dirs, code, libtype: LibType):
+ def _find_library_impl(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ code: str, libtype: LibType) -> T.Optional[T.List[str]]:
# These libraries are either built-in or invalid
if libname in self.ignore_libs:
return []
@@ -1078,24 +1170,26 @@ class CLikeCompiler:
extra_dirs = [extra_dirs]
key = (tuple(self.exelist), libname, tuple(extra_dirs), code, libtype)
if key not in self.find_library_cache:
- value = self.find_library_real(libname, env, extra_dirs, code, libtype)
+ value = self._find_library_real(libname, env, extra_dirs, code, libtype)
self.find_library_cache[key] = value
else:
value = self.find_library_cache[key]
if value is None:
return None
- return value[:]
+ return value.copy()
- def find_library(self, libname, env, extra_dirs, libtype: LibType = LibType.PREFER_SHARED):
+ def find_library(self, libname: str, env: 'Environment', extra_dirs: T.List[str],
+ libtype: LibType = LibType.PREFER_SHARED) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }\n'
- return self.find_library_impl(libname, env, extra_dirs, code, libtype)
+ return self._find_library_impl(libname, env, extra_dirs, code, libtype)
- def find_framework_paths(self, env):
+ def find_framework_paths(self, env: 'Environment') -> T.List[str]:
'''
These are usually /Library/Frameworks and /System/Library/Frameworks,
unless you select a particular macOS SDK with the -isysroot flag.
You can also add to this by setting -F in CFLAGS.
'''
+ # TODO: this really needs to be *AppleClang*, not just any clang.
if self.id != 'clang':
raise mesonlib.MesonException('Cannot find framework path with non-clang compiler')
# Construct the compiler command-line
@@ -1107,7 +1201,7 @@ class CLikeCompiler:
os_env = os.environ.copy()
os_env['LC_ALL'] = 'C'
_, _, stde = mesonlib.Popen_safe(commands, env=os_env, stdin=subprocess.PIPE)
- paths = []
+ paths = [] # T.List[str]
for line in stde.split('\n'):
if '(framework directory)' not in line:
continue
@@ -1116,7 +1210,7 @@ class CLikeCompiler:
paths.append(line[:-21].strip())
return paths
- def find_framework_real(self, name, env, extra_dirs, allow_system):
+ def _find_framework_real(self, name: str, env: 'Environment', extra_dirs: T.List[str], allow_system: bool) -> T.Optional[T.List[str]]:
code = 'int main(void) { return 0; }'
link_args = []
for d in extra_dirs:
@@ -1127,36 +1221,43 @@ class CLikeCompiler:
link_args += ['-framework', name]
if self.links(code, env, extra_args=(extra_args + link_args), disable_cache=True)[0]:
return link_args
+ return None
- def find_framework_impl(self, name, env, extra_dirs, allow_system):
+ def _find_framework_impl(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ allow_system: bool) -> T.Optional[T.List[str]]:
if isinstance(extra_dirs, str):
extra_dirs = [extra_dirs]
key = (tuple(self.exelist), name, tuple(extra_dirs), allow_system)
if key in self.find_framework_cache:
value = self.find_framework_cache[key]
else:
- value = self.find_framework_real(name, env, extra_dirs, allow_system)
+ value = self._find_framework_real(name, env, extra_dirs, allow_system)
self.find_framework_cache[key] = value
if value is None:
return None
- return value[:]
+ return value.copy()
- def find_framework(self, name, env, extra_dirs, allow_system=True):
+ def find_framework(self, name: str, env: 'Environment', extra_dirs: T.List[str],
+ allow_system: bool = True) -> T.Optional[T.List[str]]:
'''
Finds the framework with the specified name, and returns link args for
the same or returns None when the framework is not found.
'''
+ # TODO: maybe this belongs in clang? also, should probably check for macOS?
if self.id != 'clang':
raise mesonlib.MesonException('Cannot find frameworks with non-clang compiler')
- return self.find_framework_impl(name, env, extra_dirs, allow_system)
+ return self._find_framework_impl(name, env, extra_dirs, allow_system)
def get_crt_compile_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
return []
def get_crt_link_args(self, crt_val: str, buildtype: str) -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
return []
- def thread_flags(self, env):
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
+ # TODO: does this belong here or in GnuLike or maybe PosixLike?
host_m = env.machines[self.for_machine]
if host_m.is_haiku() or host_m.is_darwin():
return []
@@ -1165,19 +1266,21 @@ class CLikeCompiler:
def thread_link_flags(self, env: 'Environment') -> T.List[str]:
return self.linker.thread_flags(env)
- def linker_to_compiler_args(self, args):
- return args
+ def linker_to_compiler_args(self, args: T.List[str]) -> T.List[str]:
+ return args.copy()
- def has_arguments(self, args: T.Sequence[str], env, code: str, mode: str) -> T.Tuple[bool, bool]:
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ mode: str) -> T.Tuple[bool, bool]:
return self.compiles(code, env, extra_args=args, mode=mode)
- def has_multi_arguments(self, args, env):
- for arg in args[:]:
+ def has_multi_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
+ new_args = [] # type: T.List[str]
+ for arg in args:
# some compilers, e.g. GCC, don't warn for unsupported warning-disable
# flags, so when we are testing a flag like "-Wno-forgotten-towel", also
# check the equivalent enable flag too "-Wforgotten-towel"
if arg.startswith('-Wno-'):
- args.append('-W' + arg[5:])
+ new_args.append('-W' + arg[5:])
if arg.startswith('-Wl,'):
mlog.warning('{} looks like a linker argument, '
'but has_argument and other similar methods only '
@@ -1187,10 +1290,11 @@ class CLikeCompiler:
'the compiler you are using. has_link_argument or '
'other similar method can be used instead.'
.format(arg))
+ new_args.append(arg)
code = 'extern int i;\nint i;\n'
- return self.has_arguments(args, env, code, mode='compile')
+ return self.has_arguments(new_args, env, code, mode='compile')
- def has_multi_link_arguments(self, args, env):
+ def has_multi_link_arguments(self, args: T.List[str], env: 'Environment') -> T.Tuple[bool, bool]:
# First time we check for link flags we need to first check if we have
# --fatal-warnings, otherwise some linker checks could give some
# false positive.
@@ -1200,7 +1304,7 @@ class CLikeCompiler:
return self.has_arguments(args, env, code, mode='link')
@staticmethod
- def concatenate_string_literals(s):
+ def _concatenate_string_literals(s: str) -> str:
pattern = re.compile(r'(?P<pre>.*([^\\]")|^")(?P<str1>([^\\"]|\\.)*)"\s+"(?P<str2>([^\\"]|\\.)*)(?P<post>".*)')
ret = s
m = pattern.match(ret)
@@ -1209,13 +1313,13 @@ class CLikeCompiler:
m = pattern.match(ret)
return ret
- def get_has_func_attribute_extra_args(self, name):
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
# Most compilers (such as GCC and Clang) only warn about unknown or
# ignored attributes, so force an error. Overriden in GCC and Clang
# mixins.
return ['-Werror']
- def has_func_attribute(self, name, env):
+ def has_func_attribute(self, name: str, env: 'Environment') -> T.Tuple[bool, bool]:
# Just assume that if we're not on windows that dllimport and dllexport
# don't work
m = env.machines[self.for_machine]
diff --git a/mesonbuild/compilers/mixins/compcert.py b/mesonbuild/compilers/mixins/compcert.py
index ebcccf5..0f816a8 100644
--- a/mesonbuild/compilers/mixins/compcert.py
+++ b/mesonbuild/compilers/mixins/compcert.py
@@ -20,6 +20,13 @@ import typing as T
if T.TYPE_CHECKING:
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
ccomp_buildtype_args = {
'plain': [''],
@@ -51,7 +58,8 @@ ccomp_args_to_wul = [
r"^-r$"
] # type: T.List[str]
-class CompCertCompiler:
+class CompCertCompiler(Compiler):
+
def __init__(self) -> None:
self.id = 'ccomp'
# Assembly
@@ -78,9 +86,9 @@ class CompCertCompiler:
def get_pch_use_args(self, pch_dir: str, header: str) -> T.List[str]:
return []
- def unix_args_to_native(self, args):
+ def unix_args_to_native(self, args: T.List[str]) -> T.List[str]:
"Always returns a copy that can be independently mutated"
- patched_args = []
+ patched_args = [] # type: T.List[str]
for arg in args:
added = 0
for ptrn in ccomp_args_to_wul:
diff --git a/mesonbuild/compilers/mixins/elbrus.py b/mesonbuild/compilers/mixins/elbrus.py
index db743d8..2ea3599 100644
--- a/mesonbuild/compilers/mixins/elbrus.py
+++ b/mesonbuild/compilers/mixins/elbrus.py
@@ -30,7 +30,8 @@ if T.TYPE_CHECKING:
class ElbrusCompiler(GnuLikeCompiler):
# Elbrus compiler is nearly like GCC, but does not support
# PCH, LTO, sanitizers and color output as of version 1.21.x.
- def __init__(self):
+
+ def __init__(self) -> None:
super().__init__()
self.id = 'lcc'
self.base_options = ['b_pgo', 'b_coverage',
diff --git a/mesonbuild/compilers/mixins/emscripten.py b/mesonbuild/compilers/mixins/emscripten.py
index 08b2205..87bc40c 100644
--- a/mesonbuild/compilers/mixins/emscripten.py
+++ b/mesonbuild/compilers/mixins/emscripten.py
@@ -20,12 +20,19 @@ import typing as T
from ... import coredata
if T.TYPE_CHECKING:
- from ..environment import Environment
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
-class EmscriptenMixin:
+class EmscriptenMixin(Compiler):
- def _get_compile_output(self, dirname, mode):
+ def _get_compile_output(self, dirname: str, mode: str) -> str:
# In pre-processor mode, the output is sent to stdout and discarded
if mode == 'preprocess':
return None
@@ -48,7 +55,7 @@ class EmscriptenMixin:
args.extend(['-s', 'PTHREAD_POOL_SIZE={}'.format(count)])
return args
- def get_options(self):
+ def get_options(self) -> 'coredata.OptionDictType':
opts = super().get_options()
opts.update({
'{}_thread_count'.format(self.language): coredata.UserIntegerOption(
diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py
index 83f7047..9c60fcb 100644
--- a/mesonbuild/compilers/mixins/gnu.py
+++ b/mesonbuild/compilers/mixins/gnu.py
@@ -26,8 +26,14 @@ from ... import mesonlib
from ... import mlog
if T.TYPE_CHECKING:
- from ...coredata import UserOption # noqa: F401
from ...environment import Environment
+ from .clike import CLikeCompiler as Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
# XXX: prevent circular references.
# FIXME: this really is a posix interface not a c-like interface
@@ -106,7 +112,7 @@ def gnulike_default_include_dirs(compiler: T.Tuple[str], lang: str) -> T.List[st
)
stdout = p.stdout.read().decode('utf-8', errors='replace')
parse_state = 0
- paths = []
+ paths = [] # type: T.List[str]
for line in stdout.split('\n'):
line = line.strip(' \n\r\t')
if parse_state == 0:
@@ -129,7 +135,7 @@ def gnulike_default_include_dirs(compiler: T.Tuple[str], lang: str) -> T.List[st
return paths
-class GnuLikeCompiler(metaclass=abc.ABCMeta):
+class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta):
"""
GnuLikeCompiler is a common interface to all compilers implementing
the GNU-style commandline interface. This includes GCC, Clang
@@ -139,7 +145,7 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
LINKER_PREFIX = '-Wl,'
- def __init__(self):
+ def __init__(self) -> None:
self.base_options = ['b_pch', 'b_lto', 'b_pgo', 'b_coverage',
'b_ndebug', 'b_staticpic', 'b_pie']
if not (self.info.is_windows() or self.info.is_cygwin() or self.info.is_openbsd()):
@@ -164,14 +170,14 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
- raise NotImplementedError("get_optimization_args not implemented")
+ pass
def get_debug_args(self, is_debug: bool) -> T.List[str]:
return clike_debug_args[is_debug]
@abc.abstractmethod
def get_pch_suffix(self) -> str:
- raise NotImplementedError("get_pch_suffix not implemented")
+ pass
def split_shlib_to_parts(self, fname: str) -> T.Tuple[str, str]:
return os.path.dirname(fname), fname
@@ -184,7 +190,7 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
@abc.abstractmethod
def openmp_flags(self) -> T.List[str]:
- raise NotImplementedError("openmp_flags not implemented")
+ pass
def gnu_symbol_visibility_args(self, vistype: str) -> T.List[str]:
return gnu_symbol_visibility_args[vistype]
@@ -223,12 +229,10 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
@functools.lru_cache()
def _get_search_dirs(self, env: 'Environment') -> str:
extra_args = ['--print-search-dirs']
- stdo = None
with self._build_wrapper('', env, extra_args=extra_args,
dependencies=None, mode='compile',
want_output=True) as p:
- stdo = p.stdo
- return stdo
+ return p.stdout
def _split_fetch_real_dirs(self, pathstr: str) -> T.List[str]:
# We need to use the path separator used by the compiler for printing
@@ -291,7 +295,7 @@ class GnuLikeCompiler(metaclass=abc.ABCMeta):
def get_output_args(self, target: str) -> T.List[str]:
return ['-o', target]
- def get_dependency_gen_args(self, outtarget, outfile):
+ def get_dependency_gen_args(self, outtarget: str, outfile: str) -> T.List[str]:
return ['-MD', '-MQ', outtarget, '-MF', outfile]
def get_compile_only_args(self) -> T.List[str]:
@@ -322,7 +326,7 @@ class GnuCompiler(GnuLikeCompiler):
Compilers imitating GCC (Clang/Intel) should use the GnuLikeCompiler ABC.
"""
- def __init__(self, defines: T.Dict[str, str]):
+ def __init__(self, defines: T.Optional[T.Dict[str, str]]):
super().__init__()
self.id = 'gcc'
self.defines = defines or {}
@@ -334,6 +338,7 @@ class GnuCompiler(GnuLikeCompiler):
return []
def get_warn_args(self, level: str) -> T.List[str]:
+ # Mypy doesn't understand cooperative inheritance
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
@@ -358,19 +363,20 @@ class GnuCompiler(GnuLikeCompiler):
def openmp_flags(self) -> T.List[str]:
return ['-fopenmp']
- def has_arguments(self, args, env, code, mode):
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str,
+ mode: str) -> T.Tuple[bool, bool]:
# For some compiler command line arguments, the GNU compilers will
# emit a warning on stderr indicating that an option is valid for a
# another language, but still complete with exit_success
with self._build_wrapper(code, env, args, None, mode) as p:
result = p.returncode == 0
- if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stde:
+ if self.language in {'cpp', 'objcpp'} and 'is valid for C/ObjC' in p.stderr:
result = False
- if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stde:
+ if self.language in {'c', 'objc'} and 'is valid for C++/ObjC++' in p.stderr:
result = False
return result, p.cached
- def get_has_func_attribute_extra_args(self, name):
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
# GCC only warns about unknown or ignored attributes, so force an
# error.
return ['-Werror=attributes']
diff --git a/mesonbuild/compilers/mixins/intel.py b/mesonbuild/compilers/mixins/intel.py
index eb4b10d..b83e5c4 100644
--- a/mesonbuild/compilers/mixins/intel.py
+++ b/mesonbuild/compilers/mixins/intel.py
@@ -28,7 +28,9 @@ from .gnu import GnuLikeCompiler
from .visualstudio import VisualStudioLikeCompiler
if T.TYPE_CHECKING:
- import subprocess # noqa: F401
+ from ...arglist import CompilerArgs
+ from ...dependencies import Dependency
+ from ...environment import Environment
# XXX: avoid circular dependencies
# TODO: this belongs in a posix compiler class
@@ -97,21 +99,21 @@ class IntelGnuLikeCompiler(GnuLikeCompiler):
else:
return ['-openmp']
- def compiles(self, *args, **kwargs) -> T.Tuple[bool, bool]:
- # This covers a case that .get('foo', []) doesn't, that extra_args is
- # defined and is None
- extra_args = kwargs.get('extra_args') or []
- kwargs['extra_args'] = [
- extra_args,
+ def compiles(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], 'CompilerArgs'] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
+ extra_args = extra_args.copy() if extra_args is not None else []
+ extra_args += [
'-diag-error', '10006', # ignoring unknown option
'-diag-error', '10148', # Option not supported
'-diag-error', '10155', # ignoring argument required
'-diag-error', '10156', # ignoring not argument allowed
'-diag-error', '10157', # Ignoring argument of the wrong type
'-diag-error', '10158', # Argument must be separate. Can be hit by trying an option like -foo-bar=foo when -foo=bar is a valid option but -foo-bar isn't
- '-diag-error', '1292', # unknown __attribute__
]
- return super().compiles(*args, **kwargs)
+ return super().compiles(code, env, extra_args=extra_args, dependencies=dependencies, mode=mode, disable_cache=disable_cache)
def get_profile_generate_args(self) -> T.List[str]:
return ['-prof-gen=threadsafe']
@@ -125,6 +127,9 @@ class IntelGnuLikeCompiler(GnuLikeCompiler):
def get_optimization_args(self, optimization_level: str) -> T.List[str]:
return self.OPTIM_ARGS[optimization_level]
+ def get_has_func_attribute_extra_args(self, name: str) -> T.List[str]:
+ return ['-diag-error', '1292']
+
class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
@@ -148,13 +153,17 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
's': ['/Os'],
}
- def __init__(self, target: str):
+ def __init__(self, target: str) -> None:
super().__init__(target)
self.id = 'intel-cl'
- def compile(self, code: str, *, extra_args: T.Optional[T.List[str]] = None, **kwargs) -> T.Iterator['subprocess.Popen']:
+ def compiles(self, code: str, env: 'Environment', *,
+ extra_args: T.Union[None, T.List[str], 'CompilerArgs'] = None,
+ dependencies: T.Optional[T.List['Dependency']] = None,
+ mode: str = 'compile',
+ disable_cache: bool = False) -> T.Tuple[bool, bool]:
# This covers a case that .get('foo', []) doesn't, that extra_args is
- if kwargs.get('mode', 'compile') != 'link':
+ if mode != 'link':
extra_args = extra_args.copy() if extra_args is not None else []
extra_args.extend([
'/Qdiag-error:10006', # ignoring unknown option
@@ -164,7 +173,7 @@ class IntelVisualStudioLikeCompiler(VisualStudioLikeCompiler):
'/Qdiag-error:10157', # Ignoring argument of the wrong type
'/Qdiag-error:10158', # Argument must be separate. Can be hit by trying an option like -foo-bar=foo when -foo=bar is a valid option but -foo-bar isn't
])
- return super().compile(code, extra_args, **kwargs)
+ return super().compiles(code, env, extra_args=extra_args, dependencies=dependencies, mode=mode, disable_cache=disable_cache)
def get_toolset_version(self) -> T.Optional[str]:
# Avoid circular dependencies....
diff --git a/mesonbuild/compilers/mixins/islinker.py b/mesonbuild/compilers/mixins/islinker.py
index a9967d6..ce7a8af 100644
--- a/mesonbuild/compilers/mixins/islinker.py
+++ b/mesonbuild/compilers/mixins/islinker.py
@@ -27,9 +27,16 @@ from ... import mesonlib
if T.TYPE_CHECKING:
from ...coredata import OptionDictType
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
-class BasicLinkerIsCompilerMixin:
+class BasicLinkerIsCompilerMixin(Compiler):
"""Provides a baseline of methods that a linker would implement.
@@ -99,8 +106,8 @@ class BasicLinkerIsCompilerMixin:
def bitcode_args(self) -> T.List[str]:
raise mesonlib.MesonException("This linker doesn't support bitcode bundles")
- def get_soname_args(self, for_machine: 'mesonlib.MachineChoice',
- prefix: str, shlib_name: str, suffix: str, soversion: str,
+ def get_soname_args(self, env: 'Environment', prefix: str, shlib_name: str,
+ suffix: str, soversion: str,
darwin_versions: T.Tuple[str, str],
is_shared_module: bool) -> T.List[str]:
raise mesonlib.MesonException("This linker doesn't support soname args")
diff --git a/mesonbuild/compilers/mixins/pgi.py b/mesonbuild/compilers/mixins/pgi.py
index 77a7a28..f6ad279 100644
--- a/mesonbuild/compilers/mixins/pgi.py
+++ b/mesonbuild/compilers/mixins/pgi.py
@@ -20,6 +20,16 @@ from pathlib import Path
from ..compilers import clike_debug_args, clike_optimization_args
+if T.TYPE_CHECKING:
+ from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
+
pgi_buildtype_args = {
'plain': [],
'debug': [],
@@ -30,8 +40,9 @@ pgi_buildtype_args = {
} # type: T.Dict[str, T.List[str]]
-class PGICompiler:
- def __init__(self):
+class PGICompiler(Compiler):
+
+ def __init__(self) -> None:
self.base_options = ['b_pch']
self.id = 'pgi'
@@ -94,6 +105,6 @@ class PGICompiler:
else:
return []
- def thread_flags(self, env):
+ def thread_flags(self, env: 'Environment') -> T.List[str]:
# PGI cannot accept -pthread, it's already threaded
return []
diff --git a/mesonbuild/compilers/mixins/visualstudio.py b/mesonbuild/compilers/mixins/visualstudio.py
index 0ea03f0..d9abb95 100644
--- a/mesonbuild/compilers/mixins/visualstudio.py
+++ b/mesonbuild/compilers/mixins/visualstudio.py
@@ -20,11 +20,19 @@ import abc
import os
import typing as T
+from ... import arglist
from ... import mesonlib
from ... import mlog
if T.TYPE_CHECKING:
from ...environment import Environment
+ from .clike import CLikeCompiler as Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
vs32_instruction_set_args = {
'mmx': ['/arch:SSE'], # There does not seem to be a flag just for MMX
@@ -85,7 +93,7 @@ msvc_debug_args = {
} # type: T.Dict[bool, T.List[str]]
-class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
+class VisualStudioLikeCompiler(Compiler, metaclass=abc.ABCMeta):
"""A common interface for all compilers implementing an MSVC-style
interface.
@@ -97,10 +105,8 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
std_warn_args = ['/W3']
std_opt_args = ['/O2']
- # XXX: this is copied in this patch only to avoid circular dependencies
- #ignore_libs = unixy_compiler_internal_libs
- ignore_libs = ('m', 'c', 'pthread', 'dl', 'rt', 'execinfo')
- internal_libs = ()
+ ignore_libs = arglist.UNIXY_COMPILER_INTERNAL_LIBS + ['execinfo']
+ internal_libs = [] # type: T.List[str]
crt_args = {
'none': [],
@@ -137,6 +143,7 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
self.machine = 'arm'
else:
self.machine = target
+ assert self.linker is not None
self.linker.machine = self.machine
# Override CCompiler.get_always_args
@@ -291,12 +298,12 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
# 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_arguments(self, args: T.List[str], env: 'Environment', code, mode: str) -> T.Tuple[bool, bool]:
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: str) -> T.Tuple[bool, bool]:
warning_text = '4044' if mode == 'link' else '9002'
with self._build_wrapper(code, env, extra_args=args, mode=mode) as p:
if p.returncode != 0:
return False, p.cached
- return not(warning_text in p.stde or warning_text in p.stdo), p.cached
+ return not(warning_text in p.stderr or warning_text in p.stdout), p.cached
def get_compile_debugfile_args(self, rel_obj: str, pch: bool = False) -> T.List[str]:
pdbarr = rel_obj.split('.')[:-1]
@@ -420,7 +427,7 @@ class ClangClCompiler(VisualStudioLikeCompiler):
super().__init__(target)
self.id = 'clang-cl'
- def has_arguments(self, args: T.List[str], env: 'Environment', code, mode: str) -> T.Tuple[bool, bool]:
+ def has_arguments(self, args: T.List[str], env: 'Environment', code: str, mode: str) -> T.Tuple[bool, bool]:
if mode != 'link':
args = args + ['-Werror=unknown-argument']
return super().has_arguments(args, env, code, mode)
diff --git a/mesonbuild/compilers/mixins/xc16.py b/mesonbuild/compilers/mixins/xc16.py
index f12cccc..edc5f2c 100644
--- a/mesonbuild/compilers/mixins/xc16.py
+++ b/mesonbuild/compilers/mixins/xc16.py
@@ -21,6 +21,13 @@ from ...mesonlib import EnvironmentException
if T.TYPE_CHECKING:
from ...environment import Environment
+ from ...compilers.compilers import Compiler
+else:
+ # This is a bit clever, for mypy we pretend that these mixins descend from
+ # Compiler, so we get all of the methods and attributes defined for us, but
+ # for runtime we make them descend from object (which all classes normally
+ # do). This gives up DRYer type checking, with no runtime impact
+ Compiler = object
xc16_buildtype_args = {
'plain': [],
@@ -46,8 +53,9 @@ xc16_debug_args = {
} # type: T.Dict[bool, T.List[str]]
-class Xc16Compiler:
- def __init__(self):
+class Xc16Compiler(Compiler):
+
+ def __init__(self) -> None:
if not self.is_cross:
raise EnvironmentException('xc16 supports only cross-compilation.')
self.id = 'xc16'
@@ -85,7 +93,7 @@ class Xc16Compiler:
def get_coverage_args(self) -> T.List[str]:
return []
-
+
def get_no_stdinc_args(self) -> T.List[str]:
return ['-nostdinc']
diff --git a/mesonbuild/compilers/objc.py b/mesonbuild/compilers/objc.py
index 254a609..1b280eb 100644
--- a/mesonbuild/compilers/objc.py
+++ b/mesonbuild/compilers/objc.py
@@ -23,24 +23,31 @@ from .mixins.gnu import GnuCompiler
from .mixins.clang import ClangCompiler
if T.TYPE_CHECKING:
+ from ..dependencies import ExternalProgram
from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
class ObjCCompiler(CLikeCompiler, Compiler):
language = 'objc'
- def __init__(self, exelist, version, for_machine: MachineChoice,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
is_cross: bool, info: 'MachineInfo',
- exe_wrap: T.Optional[str], **kwargs):
- Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
- CLikeCompiler.__init__(self, is_cross, exe_wrap)
+ exe_wrap: T.Optional['ExternalProgram'],
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ Compiler.__init__(self, exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ CLikeCompiler.__init__(self, exe_wrap)
@staticmethod
- def get_display_language():
+ def get_display_language() -> str:
return 'Objective-C'
- def sanity_check(self, work_dir, environment):
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
# 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')
@@ -67,11 +74,14 @@ class ObjCCompiler(CLikeCompiler, Compiler):
class GnuObjCCompiler(GnuCompiler, ObjCCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None,
- defines=None, **kwargs):
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross,
- info, exe_wrapper, **kwargs)
+ info, exe_wrapper, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
@@ -81,12 +91,15 @@ class GnuObjCCompiler(GnuCompiler, ObjCCompiler):
class ClangObjCCompiler(ClangCompiler, ObjCCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None,
- **kwargs):
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
ObjCCompiler.__init__(self, exelist, version, for_machine, is_cross,
- info, exe_wrapper, **kwargs)
- ClangCompiler.__init__(self, [])
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch']
self.warn_args = {'0': [],
'1': default_warn_args,
diff --git a/mesonbuild/compilers/objcpp.py b/mesonbuild/compilers/objcpp.py
index 3197abc..16ba77e 100644
--- a/mesonbuild/compilers/objcpp.py
+++ b/mesonbuild/compilers/objcpp.py
@@ -23,23 +23,30 @@ from .mixins.gnu import GnuCompiler
from .mixins.clang import ClangCompiler
if T.TYPE_CHECKING:
+ from ..dependencies import ExternalProgram
from ..envconfig import MachineInfo
+ from ..environment import Environment
+ from ..linkers import DynamicLinker
class ObjCPPCompiler(CLikeCompiler, Compiler):
language = 'objcpp'
- def __init__(self, exelist, version, for_machine: MachineChoice,
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
is_cross: bool, info: 'MachineInfo',
- exe_wrap: T.Optional[str], **kwargs):
- Compiler.__init__(self, exelist, version, for_machine, info, **kwargs)
- CLikeCompiler.__init__(self, is_cross, exe_wrap)
+ exe_wrap: T.Optional['ExternalProgram'],
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ Compiler.__init__(self, exelist, version, for_machine, info,
+ is_cross=is_cross, full_version=full_version,
+ linker=linker)
+ CLikeCompiler.__init__(self, exe_wrap)
@staticmethod
- def get_display_language():
+ def get_display_language() -> str:
return 'Objective-C++'
- def sanity_check(self, work_dir, environment):
+ def sanity_check(self, work_dir: str, environment: 'Environment') -> None:
# 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')
@@ -67,10 +74,14 @@ class ObjCPPCompiler(CLikeCompiler, Compiler):
class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None,
- defines=None, **kwargs):
- ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs)
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
GnuCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor']
self.warn_args = {'0': [],
@@ -80,11 +91,16 @@ class GnuObjCPPCompiler(GnuCompiler, ObjCPPCompiler):
class ClangObjCPPCompiler(ClangCompiler, ObjCPPCompiler):
- def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None,
- **kwargs):
- ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross, info, exe_wrapper, **kwargs)
- ClangCompiler.__init__(self, [])
+
+ def __init__(self, exelist: T.List[str], version: str, for_machine: MachineChoice,
+ is_cross: bool, info: 'MachineInfo',
+ exe_wrapper: T.Optional['ExternalProgram'] = None,
+ defines: T.Optional[T.Dict[str, str]] = None,
+ linker: T.Optional['DynamicLinker'] = None,
+ full_version: T.Optional[str] = None):
+ ObjCPPCompiler.__init__(self, exelist, version, for_machine, is_cross,
+ info, exe_wrapper, linker=linker, full_version=full_version)
+ ClangCompiler.__init__(self, defines)
default_warn_args = ['-Wall', '-Winvalid-pch', '-Wnon-virtual-dtor']
self.warn_args = {'0': [],
'1': default_warn_args,
diff --git a/mesonbuild/compilers/rust.py b/mesonbuild/compilers/rust.py
index c2e21c4..baa7272 100644
--- a/mesonbuild/compilers/rust.py
+++ b/mesonbuild/compilers/rust.py
@@ -36,18 +36,14 @@ class RustCompiler(Compiler):
language = 'rust'
def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', exe_wrapper=None, **kwargs):
- super().__init__(exelist, version, for_machine, info, **kwargs)
+ is_cross: bool, info: 'MachineInfo', exe_wrapper=None, **kwargs):
+ super().__init__(exelist, version, for_machine, info, is_cross=is_cross, **kwargs)
self.exe_wrapper = exe_wrapper
self.id = 'rustc'
- self.is_cross = is_cross
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')
@@ -110,6 +106,9 @@ class RustCompiler(Compiler):
def get_std_exe_link_args(self):
return []
+ def get_output_args(self, outputname: str) -> T.List[str]:
+ return ['-o', outputname]
+
# Rust does not have a use_linker_args because it dispatches to a gcc-like
# C compiler for dynamic linking, as such we invoke the C compiler's
# use_linker_args method instead.
diff --git a/mesonbuild/compilers/swift.py b/mesonbuild/compilers/swift.py
index 1942120..55f2761 100644
--- a/mesonbuild/compilers/swift.py
+++ b/mesonbuild/compilers/swift.py
@@ -36,14 +36,10 @@ class SwiftCompiler(Compiler):
language = 'swift'
def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo', **kwargs):
- super().__init__(exelist, version, for_machine, info, **kwargs)
+ is_cross: bool, info: 'MachineInfo', **kwargs):
+ super().__init__(exelist, version, for_machine, info, is_cross=is_cross, **kwargs)
self.version = version
self.id = 'llvm'
- self.is_cross = is_cross
-
- def name_string(self):
- return ' '.join(self.exelist)
def needs_static_linker(self):
return True
diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py
index a5d49b6..f31a294 100644
--- a/mesonbuild/compilers/vala.py
+++ b/mesonbuild/compilers/vala.py
@@ -28,16 +28,12 @@ class ValaCompiler(Compiler):
language = 'vala'
def __init__(self, exelist, version, for_machine: MachineChoice,
- is_cross, info: 'MachineInfo'):
- super().__init__(exelist, version, for_machine, info)
+ is_cross: bool, info: 'MachineInfo'):
+ super().__init__(exelist, version, for_machine, info, is_cross=is_cross)
self.version = version
- self.is_cross = is_cross
self.id = 'valac'
self.base_options = ['b_colorout']
- def name_string(self):
- return ' '.join(self.exelist)
-
def needs_static_linker(self):
return False # Because compiles into C.
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index a36bbe9..5827a4e 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -32,10 +32,11 @@ import typing as T
if T.TYPE_CHECKING:
from . import dependencies
- from .compilers import Compiler # noqa: F401
+ from .compilers.compilers import Compiler, CompileResult # noqa: F401
from .environment import Environment
OptionDictType = T.Dict[str, 'UserOption[T.Any]']
+ CompilerCheckCacheKey = T.Tuple[T.Tuple[str, ...], str, str, T.Tuple[str, ...], str]
version = '0.55.999'
backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'vs2019', 'xcode']
@@ -394,7 +395,7 @@ class CoreData:
build_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
host_cache = DependencyCache(self.builtins_per_machine, MachineChoice.BUILD)
self.deps = PerMachine(build_cache, host_cache) # type: PerMachine[DependencyCache]
- self.compiler_check_cache = OrderedDict()
+ self.compiler_check_cache = OrderedDict() # type: T.Dict[CompilerCheckCacheKey, compiler.CompileResult]
# Only to print a warning if it changes between Meson invocations.
self.config_files = self.__load_config_files(options, scratch_dir, 'native')
diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py
index aa513ba..99030e8 100644
--- a/mesonbuild/dependencies/base.py
+++ b/mesonbuild/dependencies/base.py
@@ -2052,26 +2052,26 @@ class ExternalProgram:
def get_command(self) -> T.List[str]:
return self.command[:]
- def get_path(self):
+ def get_path(self) -> str:
return self.path
- def get_name(self):
+ def get_name(self) -> str:
return self.name
class NonExistingExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-init]
"A program that will never exist"
- def __init__(self, name='nonexistingprogram'):
+ def __init__(self, name: str = 'nonexistingprogram') -> None:
self.name = name
self.command = [None]
self.path = None
- def __repr__(self):
+ def __repr__(self) -> str:
r = '<{} {!r} -> {!r}>'
return r.format(self.__class__.__name__, self.name, self.command)
- def found(self):
+ def found(self) -> bool:
return False
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 4623049..513a03c 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -1588,6 +1588,10 @@ class Environment:
exe_wrap, defines, linker=linker)
if 'clang' in out:
linker = None
+ defines = self.get_clang_compiler_defines(compiler)
+ if not defines:
+ popen_exceptions[' '.join(compiler)] = 'no pre-processor defines'
+ continue
if 'Apple' in out:
comp = AppleClangObjCCompiler if objc else AppleClangObjCPPCompiler
else:
@@ -1604,7 +1608,7 @@ class Environment:
compiler, comp, for_machine)
return comp(
ccache + compiler, version, for_machine,
- is_cross, info, exe_wrap, linker=linker)
+ is_cross, info, exe_wrap, linker=linker, defines=defines)
self._handle_exceptions(popen_exceptions, compilers)
def detect_java_compiler(self, for_machine):
@@ -1821,7 +1825,8 @@ class Environment:
elif 'gdc' in out:
linker = self._guess_nix_linker(exelist, compilers.GnuDCompiler, for_machine)
return compilers.GnuDCompiler(
- exelist, version, for_machine, info, arch, is_cross, exe_wrap,
+ exelist, version, for_machine, info, arch,
+ exe_wrapper=exe_wrap, is_cross=is_cross,
full_version=full_version, linker=linker)
elif 'The D Language Foundation' in out or 'Digital Mars' in out:
# DMD seems to require a file
diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py
index d85c88e..ed28fa3 100644
--- a/mesonbuild/linkers.py
+++ b/mesonbuild/linkers.py
@@ -351,6 +351,7 @@ class DynamicLinker(LinkerEnvVarsMixin, metaclass=abc.ABCMeta):
self.version = version
self.prefix_arg = prefix_arg
self.always_args = always_args
+ self.machine = None # type: T.Optional[str]
def __repr__(self) -> str:
return '<{}: v{} `{}`>'.format(type(self).__name__, self.version, ' '.join(self.exelist))
diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py
index fbda240..6985ca9 100644
--- a/mesonbuild/mdist.py
+++ b/mesonbuild/mdist.py
@@ -35,7 +35,7 @@ archive_extension = {'gztar': '.tar.gz',
def add_arguments(parser):
parser.add_argument('-C', default='.', dest='wd',
help='directory to cd into before running')
- parser.add_argument('--formats', default='xztar',
+ parser.add_argument('--formats', default='xztar', choices=archive_choices,
help='Comma separated list of archive types to create.')
parser.add_argument('--include-subprojects', action='store_true',
help='Include source code of subprojects that have been used for the build.')
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index dd2fe8e..17d2733 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -34,6 +34,8 @@ if T.TYPE_CHECKING:
from .compilers.compilers import CompilerType
from .interpreterbase import ObjectHolder
+ FileOrString = T.Union['File', str]
+
_T = T.TypeVar('_T')
_U = T.TypeVar('_U')
diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py
index 6050705..a5faa32 100644
--- a/mesonbuild/modules/windows.py
+++ b/mesonbuild/modules/windows.py
@@ -119,22 +119,23 @@ class WindowsModule(ExtensionModule):
src = unholder(src)
if isinstance(src, str):
- name_format = 'file {!r}'
+ name_formatted = src
name = os.path.join(state.subdir, src)
elif isinstance(src, mesonlib.File):
- name_format = 'file {!r}'
+ name_formatted = src.fname
name = src.relative_name()
elif isinstance(src, build.CustomTarget):
if len(src.get_outputs()) > 1:
raise MesonException('windows.compile_resources does not accept custom targets with more than 1 output.')
- name_format = 'target {!r}'
+ name_formatted = src.get_filename()
name = src.get_id()
else:
raise MesonException('Unexpected source type {!r}. windows.compile_resources accepts only strings, files, custom targets, and lists thereof.'.format(src))
# Path separators are not allowed in target names
name = name.replace('/', '_').replace('\\', '_')
+ name_formatted = name_formatted.replace('/', '_').replace('\\', '_')
res_kwargs = {
'output': name + '_@BASENAME@.' + suffix,
@@ -149,7 +150,7 @@ class WindowsModule(ExtensionModule):
res_kwargs['depfile'] = res_kwargs['output'] + '.d'
res_kwargs['command'] += ['--preprocessor-arg=-MD', '--preprocessor-arg=-MQ@OUTPUT@', '--preprocessor-arg=-MF@DEPFILE@']
- res_targets.append(build.CustomTarget('Windows resource for ' + name_format.format(name), state.subdir, state.subproject, res_kwargs))
+ res_targets.append(build.CustomTarget(name_formatted, state.subdir, state.subproject, res_kwargs))
add_target(args)
diff --git a/run_mypy.py b/run_mypy.py
index 953c20d..04f563a 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -7,60 +7,70 @@ from pathlib import Path
import typing as T
modules = [
- # fully typed submodules
- 'mesonbuild/ast',
- 'mesonbuild/scripts',
- 'mesonbuild/wrap',
+ # fully typed submodules
+ 'mesonbuild/ast',
+ 'mesonbuild/cmake',
+ 'mesonbuild/compilers/mixins',
+ 'mesonbuild/scripts',
+ 'mesonbuild/wrap',
- # specific files
- 'mesonbuild/arglist.py',
- # 'mesonbuild/compilers/mixins/intel.py',
- # 'mesonbuild/coredata.py',
- 'mesonbuild/dependencies/boost.py',
- 'mesonbuild/dependencies/hdf5.py',
- 'mesonbuild/dependencies/mpi.py',
- 'mesonbuild/envconfig.py',
- 'mesonbuild/interpreterbase.py',
- 'mesonbuild/linkers.py',
- 'mesonbuild/mcompile.py',
- 'mesonbuild/mesonlib.py',
- 'mesonbuild/minit.py',
- 'mesonbuild/mintro.py',
- 'mesonbuild/mlog.py',
- 'mesonbuild/modules/fs.py',
- 'mesonbuild/mparser.py',
- 'mesonbuild/msetup.py',
- 'mesonbuild/mtest.py',
+ # specific files
+ 'mesonbuild/arglist.py',
+ 'mesonbuild/compilers/compilers.py',
+ 'mesonbuild/compilers/c_function_attributes.py',
+ 'mesonbuild/compilers/objc.py',
+ 'mesonbuild/compilers/objcpp.py',
+ # 'mesonbuild/coredata.py',
+ 'mesonbuild/dependencies/boost.py',
+ 'mesonbuild/dependencies/hdf5.py',
+ 'mesonbuild/dependencies/mpi.py',
+ 'mesonbuild/envconfig.py',
+ 'mesonbuild/interpreterbase.py',
+ 'mesonbuild/linkers.py',
+ 'mesonbuild/mcompile.py',
+ 'mesonbuild/mesonlib.py',
+ 'mesonbuild/minit.py',
+ 'mesonbuild/mintro.py',
+ 'mesonbuild/mlog.py',
+ 'mesonbuild/modules/fs.py',
+ 'mesonbuild/mparser.py',
+ 'mesonbuild/msetup.py',
+ 'mesonbuild/mtest.py',
- 'run_mypy.py',
- 'tools'
+ 'run_mypy.py',
+ 'tools'
]
def check_mypy() -> None:
- try:
- import mypy
- except ImportError:
- print('Failed import mypy')
- sys.exit(1)
+ try:
+ import mypy
+ except ImportError:
+ print('Failed import mypy')
+ sys.exit(1)
def main() -> int:
- check_mypy()
+ check_mypy()
- root = Path(__file__).absolute().parent
- args = [] # type: T.List[str]
+ root = Path(__file__).absolute().parent
+ args = [] # type: T.List[str]
- parser = argparse.ArgumentParser(description='Process some integers.')
- parser.add_argument('-p', '--pretty', action='store_true', help='pretty print mypy errors')
+ parser = argparse.ArgumentParser(description='Process some integers.')
+ parser.add_argument('-p', '--pretty', action='store_true', help='pretty print mypy errors')
+ parser.add_argument('-C', '--clear', action='store_true', help='clear the terminal before running mypy')
- opts = parser.parse_args()
- if opts.pretty:
- args.append('--pretty')
+ opts = parser.parse_args()
+ if opts.pretty:
+ args.append('--pretty')
- p = subprocess.run(
- [sys.executable, '-m', 'mypy'] + args + modules,
- cwd=root,
- )
- return p.returncode
+ if opts.clear:
+ print('\x1bc', end='', flush=True)
+
+ print('Running mypy (this can take some time) ...')
+ p = subprocess.run(
+ [sys.executable, '-m', 'mypy'] + args + modules,
+ cwd=root,
+ )
+ return p.returncode
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(main())
diff --git a/run_tests.py b/run_tests.py
index 6e9c6ce..0f02636 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -263,8 +263,6 @@ def run_mtest_inprocess(commandlist):
return returncode, stdout.getvalue(), stderr.getvalue()
def clear_meson_configure_class_caches():
- compilers.CCompiler.library_dirs_cache = {}
- compilers.CCompiler.program_dirs_cache = {}
compilers.CCompiler.find_library_cache = {}
compilers.CCompiler.find_framework_cache = {}
dependencies.PkgConfigDependency.pkgbin_cache = {}
diff --git a/run_unittests.py b/run_unittests.py
index 6b817bb..db61ca6 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -377,7 +377,7 @@ class InternalTests(unittest.TestCase):
self.assertEqual(a, ['-I.', '-I./tests2/', '-I./tests/', '-I..'])
def test_compiler_args_class_d(self):
- d = mesonbuild.compilers.DCompiler([], 'fake', MachineChoice.HOST, 'info', 'arch', False, None)
+ d = mesonbuild.compilers.DmdDCompiler([], 'fake', MachineChoice.HOST, 'info', 'arch')
# check include order is kept when deduplicating
a = d.compiler_args(['-Ifirst', '-Isecond', '-Ithird'])
a += ['-Ifirst']
@@ -792,7 +792,7 @@ class InternalTests(unittest.TestCase):
f.write('')
with open(os.path.join(tmpdir, 'libfoo.so.70.0.so.1'), 'w') as f:
f.write('')
- found = cc.find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED)
+ found = cc._find_library_real('foo', env, [tmpdir], '', LibType.PREFER_SHARED)
self.assertEqual(os.path.basename(found[0]), 'libfoo.so.54.0')
def test_find_library_patterns(self):
diff --git a/test cases/frameworks/1 boost/meson.build b/test cases/frameworks/1 boost/meson.build
index 5a2e1a1..83570f0 100644
--- a/test cases/frameworks/1 boost/meson.build
+++ b/test cases/frameworks/1 boost/meson.build
@@ -51,8 +51,8 @@ extralibexe = executable('extralibexe', 'extralib.cpp', dependencies : extralibd
python2module = shared_library('python2_module', ['python_module.cpp'], dependencies: [python2dep, bpython2dep], name_prefix: '', cpp_args: ['-DMOD_NAME=python2_module'])
python3module = shared_library('python3_module', ['python_module.cpp'], dependencies: [python3dep, bpython3dep], name_prefix: '', cpp_args: ['-DMOD_NAME=python3_module'])
-test('Boost linktest', linkexe)
-test('Boost UTF test', unitexe)
+test('Boost linktest', linkexe, timeout: 60)
+test('Boost UTF test', unitexe, timeout: 60)
test('Boost nomod', nomodexe)
if host_machine.system() != 'darwin' or s
# Segfaults on macOS with dynamic linking since Boost 1.73