Merge pull request #8200 from bonzini/mtest-asyncio-logs
mtest: improvements to logging
diff --git a/.github/workflows/images.yml b/.github/workflows/images.yml
index 0cf9156..87fae3b 100644
--- a/.github/workflows/images.yml
+++ b/.github/workflows/images.yml
@@ -32,7 +32,7 @@
- { name: Fedora, id: fedora }
- { name: OpenSUSE, id: opensuse }
- { name: Ubuntu Bionic, id: bionic }
- - { name: Ubuntu Eoan, id: eoan }
+ - { name: Ubuntu Rolling, id: ubuntu-rolling }
steps:
- uses: actions/checkout@v2
diff --git a/.github/workflows/os_comp.yml b/.github/workflows/os_comp.yml
index a5be499..3cdcccd 100644
--- a/.github/workflows/os_comp.yml
+++ b/.github/workflows/os_comp.yml
@@ -6,10 +6,21 @@
- master
# Stable branches such as 0.56 or the eventual 1.0
- '[0-9]+.[0-9]+'
+ paths:
+ - "mesonbuild/**"
+ - "test cases/**"
+ - ".github/workflows/images.yml"
+ - ".github/workflows/os_comp.yml"
+ - "run_unittests.py"
pull_request:
+ paths:
+ - "mesonbuild/**"
+ - "test cases/**"
+ - ".github/workflows/images.yml"
+ - ".github/workflows/os_comp.yml"
+ - "run_unittests.py"
jobs:
- arch:
name: ${{ matrix.cfg.name }}
runs-on: ubuntu-latest
strategy:
@@ -30,8 +41,8 @@
# via the `args` array ub the image.json
run: bash -c 'source /ci/env_vars.sh; cd $GITHUB_WORKSPACE; ./run_tests.py $CI_ARGS'
- eoan:
- name: 'Ubuntu Eoan'
+ ubuntu-rolling:
+ name: 'Ubuntu Rolling'
runs-on: ubuntu-latest
strategy:
@@ -60,7 +71,7 @@
CXX: 'g++'
container:
- image: mesonbuild/eoan
+ image: mesonbuild/ubuntu-rolling
env:
MESON_RSP_THRESHOLD: ${{ matrix.cfg.MESON_RSP_THRESHOLD }}
MESON_ARGS: ${{ matrix.cfg.MESON_ARGS }}
@@ -94,4 +105,4 @@
update-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix
update-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix
- ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS
\ No newline at end of file
+ ./run_tests.py $RUN_TESTS_ARGS -- $MESON_ARGS
diff --git a/ci/ciimage/arch/install.sh b/ci/ciimage/arch/install.sh
index ccbbab0..683986f 100755
--- a/ci/ciimage/arch/install.sh
+++ b/ci/ciimage/arch/install.sh
@@ -12,12 +12,12 @@
itstool gtk3 java-environment=8 gtk-doc llvm clang sdl2 graphviz
doxygen vulkan-validation-layers openssh mercurial gtk-sharp-2 qt5-tools
libwmf valgrind cmake netcdf-fortran openmpi nasm gnustep-base gettext
- python-jsonschema python-lxml
+ python-lxml hotdoc
# cuda
)
aur_pkgs=(scalapack)
-pip_pkgs=(hotdoc gcovr)
+pip_pkgs=(gcovr)
cleanup_pkgs=(go)
AUR_USER=docker
diff --git a/ci/ciimage/eoan/image.json b/ci/ciimage/eoan/image.json
deleted file mode 100644
index cbf86c3..0000000
--- a/ci/ciimage/eoan/image.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "base_image": "ubuntu:eoan",
- "env": {
- "CI": "1",
- "DC": "gdc"
- }
-}
diff --git a/ci/ciimage/ubuntu-rolling/image.json b/ci/ciimage/ubuntu-rolling/image.json
new file mode 100644
index 0000000..038b945
--- /dev/null
+++ b/ci/ciimage/ubuntu-rolling/image.json
@@ -0,0 +1,7 @@
+{
+ "base_image": "ubuntu:rolling",
+ "env": {
+ "CI": "1",
+ "DC": "gdc"
+ }
+}
diff --git a/ci/ciimage/eoan/install.sh b/ci/ciimage/ubuntu-rolling/install.sh
similarity index 89%
rename from ci/ciimage/eoan/install.sh
rename to ci/ciimage/ubuntu-rolling/install.sh
index 36dec72..507113b 100755
--- a/ci/ciimage/eoan/install.sh
+++ b/ci/ciimage/ubuntu-rolling/install.sh
@@ -9,14 +9,12 @@
export DC=gdc
pkgs=(
- python3-pytest-xdist
+ python3-pytest-xdist python3-jsonschema
python3-pip libxml2-dev libxslt1-dev libyaml-dev libjson-glib-dev
python3-lxml
wget unzip
qt5-default clang
pkg-config-arm-linux-gnueabihf
- qt4-linguist-tools
- python-dev
libomp-dev
llvm lcov
dub ldc
@@ -43,7 +41,7 @@
eatmydata apt-get -y install "${pkgs[@]}"
eatmydata apt-get -y install --no-install-recommends wine-stable # Wine is special
-eatmydata python3 -m pip install hotdoc codecov gcovr jsonschema
+eatmydata python3 -m pip install hotdoc codecov gcovr
# dub stuff
dub_fetch urld
diff --git a/ci/ciimage/eoan/test.sh b/ci/ciimage/ubuntu-rolling/test.sh
similarity index 100%
rename from ci/ciimage/eoan/test.sh
rename to ci/ciimage/ubuntu-rolling/test.sh
diff --git a/docs/markdown/Creating-releases.md b/docs/markdown/Creating-releases.md
index 040fb53..55242a6 100644
--- a/docs/markdown/Creating-releases.md
+++ b/docs/markdown/Creating-releases.md
@@ -58,3 +58,21 @@
So with `--no-tests` you can tell Meson "Do not build and test generated
packages.".
+## Release a subproject separately
+
+*Since 0.57.0* the `meson dist` command can now create a distribution tarball
+for a subproject in the same git repository as the main project. This can be
+useful if parts of the project (e.g. libraries) can be built and distributed
+separately. In that case they can be moved into `subprojects/mysub` and running
+`meson dist` in that directory will now create a tarball containing only the
+source code from that subdir and not the rest of the main project or other
+subprojects.
+
+For example:
+```sh
+git clone https://github.com/myproject
+cd myproject/subprojects/mysubproject
+meson builddir
+meson dist -C builddir
+```
+This produces `builddir/meson-dist/mysubproject-1.0.tar.xz` tarball.
diff --git a/docs/markdown/Machine-files.md b/docs/markdown/Machine-files.md
index e703988..e553720 100644
--- a/docs/markdown/Machine-files.md
+++ b/docs/markdown/Machine-files.md
@@ -34,7 +34,7 @@
option = false
```
-An integer must be either an unquoted numeric constant;
+An integer must be an unquoted numeric constant.
```ini
option = 42
```
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index cb8347a..3a844da 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -119,7 +119,9 @@
environment juggling. *(since 0.52.0)* A dictionary is also accepted.
- `exe_wrapper`: a list containing the wrapper command or script followed by the arguments to it
- `gdb`: if `true`, the tests are also run under `gdb`
-- `timeout_multiplier`: a number to multiply the test timeout with
+- `timeout_multiplier`: a number to multiply the test timeout with.
+ *Since 0.57* if timeout_multiplier is `<= 0` the test has infinite duration,
+ in previous versions of Meson the test would fail with a timeout immediately.
- `is_default` *(since 0.49.0)*: a bool to set whether this is the default test setup.
If `true`, the setup will be used whenever `meson test` is run
without the `--setup` option.
@@ -372,6 +374,10 @@
- `install_mode` *(since 0.47.0)*: the file mode and optionally the
owner/uid and group/gid
- `output`: list of output files
+- `env` *(since 0.57.0)*: environment variables to set, such as
+ `{'NAME1': 'value1', 'NAME2': 'value2'}` or `['NAME1=value1', 'NAME2=value2']`,
+ or an [`environment()` object](#environment-object) which allows more
+ sophisticated environment juggling.
The list of strings passed to the `command` keyword argument accept
the following special string substitutions:
@@ -1426,7 +1432,9 @@
- `version`: which is a free form string describing the version of
this project. You can access the value in your Meson build files
- with `meson.project_version()`.
+ with `meson.project_version()`. Since 0.57.0 this can also be a
+ `File` object pointing to a file that contains exactly one line of
+ text.
### run_command()
@@ -1730,7 +1738,8 @@
- `timeout`: the amount of seconds the test is allowed to run, a test
that exceeds its time limit is always considered failed, defaults to
- 30 seconds
+ 30 seconds. *Since 0.57* if timeout is `<= 0` the test has infinite duration,
+ in previous versions of Meson the test would fail with a timeout immediately.
- `workdir`: absolute path that will be used as the working directory
for the test
@@ -1817,21 +1826,29 @@
it from a subproject is a hard error. *(since 0.49.0)* Accepts multiple arguments
for the fscript. *(since 0.54.0)* The `MESON_SOURCE_ROOT` and `MESON_BUILD_ROOT`
environment variables are set when dist scripts are run.
+
*(since 0.55.0)* The output of `configure_file`, `files`, and `find_program`
as well as strings.
+ *(since 0.57.0)* `file` objects and the output of `configure_file` may be
+ *used as the `script_name` parameter.
+
- `add_install_script(script_name, arg1, arg2, ...)`: causes the script
given as an argument to be run during the install step, this script
will have the environment variables `MESON_SOURCE_ROOT`,
`MESON_BUILD_ROOT`, `MESON_INSTALL_PREFIX`,
`MESON_INSTALL_DESTDIR_PREFIX`, and `MESONINTROSPECT` set.
All positional arguments are passed as parameters.
+
+ *(since 0.54.0)* If `meson install` is called with the `--quiet` option, the
+ environment variable `MESON_INSTALL_QUIET` will be set.
+
*(since 0.55.0)* The output of `configure_file`, `files`, `find_program`,
`custom_target`, indexes of `custom_target`, `executable`, `library`, and
other built targets as well as strings.
- *(since 0.54.0)* If `meson install` is called with the `--quiet` option, the
- environment variable `MESON_INSTALL_QUIET` will be set.
+ *(since 0.57.0)* `file` objects and the output of `configure_file` may be
+ *used as the `script_name` parameter.
Meson uses the `DESTDIR` environment variable as set by the
inherited environment to determine the (temporary) installation
@@ -1859,9 +1876,13 @@
executable given as an argument after all project files have been
generated. This script will have the environment variables
`MESON_SOURCE_ROOT` and `MESON_BUILD_ROOT` set.
+
*(since 0.55.0)* The output of `configure_file`, `files`, and `find_program`
as well as strings.
+ *(since 0.57.0)* `file` objects and the output of `configure_file` may be
+ *used as the `script_name` parameter.
+
- `backend()` *(since 0.37.0)*: returns a string representing the
current backend: `ninja`, `vs2010`, `vs2015`, `vs2017`, `vs2019`,
or `xcode`.
diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md
index 65cb70f..4f17ac8 100644
--- a/docs/markdown/Syntax.md
+++ b/docs/markdown/Syntax.md
@@ -657,7 +657,7 @@
equality_operator: "==" | "!="
expression: assignment_expression
expression_list: expression ("," expression)*
-expression_statememt: expression
+expression_statement: expression
function_expression: id_expression "(" [argument_list] ")"
hex_literal: "0x" HEX_NUMBER
HEX_NUMBER: /[a-fA-F0-9]+/
diff --git a/docs/markdown/snippets/customtarget_env.md b/docs/markdown/snippets/customtarget_env.md
new file mode 100644
index 0000000..93687ab
--- /dev/null
+++ b/docs/markdown/snippets/customtarget_env.md
@@ -0,0 +1,11 @@
+## custom_target() now accepts `env` keyword argument
+
+Environment variables can now be passed to `custom_target()` command.
+
+```meson
+env = environment()
+env.append('PATH', '/foo')
+custom_target(..., env: env)
+custom_target(..., env: {'MY_ENV': 'value'})
+custom_target(..., env: ['MY_ENV=value'])
+```
diff --git a/docs/markdown/snippets/dist_subproject.md b/docs/markdown/snippets/dist_subproject.md
new file mode 100644
index 0000000..2520ccd
--- /dev/null
+++ b/docs/markdown/snippets/dist_subproject.md
@@ -0,0 +1,16 @@
+## Package a subproject
+
+The `meson dist` command can now create a distribution tarball for a subproject
+in the same git repository as the main project. This can be useful if parts of
+the project (e.g. libraries) can be built and distributed separately. In that
+case they can be moved into `subprojects/mysub` and running `meson dist` in that
+directory will now create a tarball containing only the source code from that
+subdir and not the rest of the main project or other subprojects.
+
+For example:
+```sh
+git clone https://github.com/myproject
+cd myproject/subprojects/mysubproject
+meson builddir
+meson dist -C builddir
+```
diff --git a/docs/markdown/snippets/pass_file_to_add_script.md b/docs/markdown/snippets/pass_file_to_add_script.md
new file mode 100644
index 0000000..10d08e0
--- /dev/null
+++ b/docs/markdown/snippets/pass_file_to_add_script.md
@@ -0,0 +1,15 @@
+## The `add_*_script` methods now accept a File as the first argument
+
+Meson now accepts `file` objects, including those produced by
+`configure_file` as the `prog` (first) parameter of the various
+`add_*_script` methods
+
+```meson
+install_script = configure_file(
+ configuration : conf,
+ input : 'myscript.py.in',
+ output : 'myscript.py',
+)
+
+meson.add_install_script(install_script, other, params)
+```
diff --git a/docs/markdown/snippets/test_timeout.md b/docs/markdown/snippets/test_timeout.md
new file mode 100644
index 0000000..e436d2e
--- /dev/null
+++ b/docs/markdown/snippets/test_timeout.md
@@ -0,0 +1,9 @@
+## `test()` timeout and timeout_multiplier value <= 0
+
+`test(..., timeout: 0)`, or negative value, used to abort the test immediately
+but now instead allow infinite duration. Note that omitting the `timeout`
+keyword argument still defaults to 30s timeout.
+
+Likewise, `add_test_setup(..., timeout_multiplier: 0)`, or
+`meson test --timeout-multiplier 0`, or negative value, disable tests timeout.
+
diff --git a/docs/markdown/snippets/versionfile.md b/docs/markdown/snippets/versionfile.md
new file mode 100644
index 0000000..6071f2c
--- /dev/null
+++ b/docs/markdown/snippets/versionfile.md
@@ -0,0 +1,12 @@
+## Project version can be specified with a file
+
+Meson can be instructed to load project's version string from an
+external file like this:
+
+```meson
+project('foo', 'c' version: files('VERSION'))
+```
+
+The version file must contain exactly one line of text and that will
+be set as the project's version. If the line ends in a newline
+character, it is removed.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index ced1d7d..caec761 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -14,6 +14,7 @@
from collections import OrderedDict
from functools import lru_cache
+from itertools import chain
from pathlib import Path
import enum
import json
@@ -21,9 +22,10 @@
import pickle
import re
import shlex
-import subprocess
import textwrap
import typing as T
+import hashlib
+import copy
from .. import build
from .. import dependencies
@@ -44,6 +46,11 @@
InstallType = T.List[T.Tuple[str, str, T.Optional['FileMode']]]
InstallSubdirsType = T.List[T.Tuple[str, str, T.Optional['FileMode'], T.Tuple[T.Set[str], T.Set[str]]]]
+# Languages that can mix with C or C++ but don't support unity builds yet
+# because the syntax we use for unity builds is specific to C/++/ObjC/++.
+# Assembly files cannot be unitified and neither can LLVM IR files
+LANGS_CANT_UNITY = ('d', 'fortran')
+
class TestProtocol(enum.Enum):
EXITCODE = 0
@@ -99,7 +106,7 @@
self.data: 'InstallType' = []
self.po_package_name: str = ''
self.po = []
- self.install_scripts: T.List[build.RunScript] = []
+ self.install_scripts: T.List[ExecutableSerialisation] = []
self.install_subdirs: 'InstallSubdirsType' = []
self.mesonintrospect = mesonintrospect
self.version = version
@@ -119,10 +126,10 @@
self.optional = optional
class ExecutableSerialisation:
- def __init__(self, cmd_args, env=None, exe_wrapper=None,
+ def __init__(self, cmd_args, env: T.Optional[build.EnvironmentVariables] = None, exe_wrapper=None,
workdir=None, extra_paths=None, capture=None) -> None:
self.cmd_args = cmd_args
- self.env = env or {}
+ self.env = env
if exe_wrapper is not None:
assert(isinstance(exe_wrapper, dependencies.ExternalProgram))
self.exe_runner = exe_wrapper
@@ -371,12 +378,11 @@
raise MesonException('Unknown data type in object list.')
return obj_list
- def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None,
- extra_bdeps=None, capture=None, force_serialize=False):
- '''
- Serialize an executable for running with a generator or a custom target
- '''
- import hashlib
+ def get_executable_serialisation(self, cmd, workdir=None,
+ extra_bdeps=None, capture=None,
+ env: T.Optional[build.EnvironmentVariables] = None):
+ exe = cmd[0]
+ cmd_args = cmd[1:]
if isinstance(exe, dependencies.ExternalProgram):
exe_cmd = exe.get_command()
exe_for_machine = exe.for_machine
@@ -396,11 +402,10 @@
is_cross_built = not self.environment.machines.matches_build_machine(exe_for_machine)
if is_cross_built and self.environment.need_exe_wrapper():
exe_wrapper = self.environment.get_exe_wrapper()
- if not exe_wrapper.found():
- msg = 'The exe_wrapper {!r} defined in the cross file is ' \
- 'needed by target {!r}, but was not found. Please ' \
- 'check the command and/or add it to PATH.'
- raise MesonException(msg.format(exe_wrapper.name, tname))
+ if not exe_wrapper or not exe_wrapper.found():
+ msg = 'An exe_wrapper is needed but was not found. Please define one ' \
+ 'in cross file and check the command and/or add it to PATH.'
+ raise MesonException(msg)
else:
if exe_cmd[0].endswith('.jar'):
exe_cmd = ['java', '-jar'] + exe_cmd
@@ -408,11 +413,24 @@
exe_cmd = ['mono'] + exe_cmd
exe_wrapper = None
+ workdir = workdir or self.environment.get_build_dir()
+ return ExecutableSerialisation(exe_cmd + cmd_args, env,
+ exe_wrapper, workdir,
+ extra_paths, capture)
+
+ def as_meson_exe_cmdline(self, tname, exe, cmd_args, workdir=None,
+ extra_bdeps=None, capture=None, force_serialize=False,
+ env: T.Optional[build.EnvironmentVariables] = None):
+ '''
+ Serialize an executable for running with a generator or a custom target
+ '''
+ cmd = [exe] + cmd_args
+ es = self.get_executable_serialisation(cmd, workdir, extra_bdeps, capture, env)
reasons = []
- if extra_paths:
+ if es.extra_paths:
reasons.append('to set PATH')
- if exe_wrapper:
+ if es.exe_runner:
reasons.append('to use exe_wrapper')
if workdir:
@@ -421,6 +439,9 @@
if any('\n' in c for c in cmd_args):
reasons.append('because command contains newlines')
+ if env and env.varnames:
+ reasons.append('to set env')
+
force_serialize = force_serialize or bool(reasons)
if capture:
@@ -430,11 +451,9 @@
if not capture:
return None, ''
return ((self.environment.get_build_command() +
- ['--internal', 'exe', '--capture', capture, '--'] + exe_cmd + cmd_args),
+ ['--internal', 'exe', '--capture', capture, '--'] + es.cmd_args),
', '.join(reasons))
- workdir = workdir or self.environment.get_build_dir()
- env = {}
if isinstance(exe, (dependencies.ExternalProgram,
build.BuildTarget, build.CustomTarget)):
basename = exe.name
@@ -445,15 +464,12 @@
# Take a digest of the cmd args, env, workdir, and capture. This avoids
# collisions and also makes the name deterministic over regenerations
# which avoids a rebuild by Ninja because the cmdline stays the same.
- data = bytes(str(sorted(env.items())) + str(cmd_args) + str(workdir) + str(capture),
+ data = bytes(str(env) + str(cmd_args) + str(es.workdir) + str(capture),
encoding='utf-8')
digest = hashlib.sha1(data).hexdigest()
scratch_file = 'meson_exe_{0}_{1}.dat'.format(basename, digest)
exe_data = os.path.join(self.environment.get_scratch_dir(), scratch_file)
with open(exe_data, 'wb') as f:
- es = ExecutableSerialisation(exe_cmd + cmd_args, env,
- exe_wrapper, workdir,
- extra_paths, capture)
pickle.dump(es, f)
return (self.environment.get_build_command() + ['--internal', 'exe', '--unpickle', exe_data],
', '.join(reasons))
@@ -636,6 +652,9 @@
unity_size = self.get_option_for_target(OptionKey('unity_size'), extobj.target)
for comp, srcs in compsrcs.items():
+ if comp.language in LANGS_CANT_UNITY:
+ sources += srcs
+ continue
for i in range(len(srcs) // unity_size + 1):
osrc = self.get_unity_source_file(extobj.target,
comp.get_default_suffix(), i)
@@ -767,7 +786,7 @@
# pkg-config puts the thread flags itself via `Cflags:`
# Fortran requires extra include directives.
if compiler.language == 'fortran':
- for lt in target.link_targets:
+ for lt in chain(target.link_targets, target.link_whole_targets):
priv_dir = self.get_target_private_dir(lt)
commands += compiler.get_include_args(priv_dir, False)
return commands
@@ -1174,16 +1193,16 @@
return inputs, outputs, cmd
def run_postconf_scripts(self) -> None:
+ from ..scripts.meson_exe import run_exe
env = {'MESON_SOURCE_ROOT': self.environment.get_source_dir(),
'MESON_BUILD_ROOT': self.environment.get_build_dir(),
'MESONINTROSPECT': ' '.join([shlex.quote(x) for x in self.environment.get_build_command() + ['introspect']]),
}
- child_env = os.environ.copy()
- child_env.update(env)
for s in self.build.postconf_scripts:
- cmd = s['exe'] + s['args']
- subprocess.check_call(cmd, env=child_env)
+ name = ' '.join(s.cmd_args)
+ mlog.log('Running postconf script {!r}'.format(name))
+ run_exe(s, env)
def create_install_data(self) -> InstallData:
strip_bin = self.environment.lookup_binary_entry(MachineChoice.HOST, 'strip')
@@ -1312,18 +1331,18 @@
d.targets.append(i)
def generate_custom_install_script(self, d: InstallData) -> None:
- result: T.List[build.RunScript] = []
+ result: T.List[ExecutableSerialisation] = []
srcdir = self.environment.get_source_dir()
builddir = self.environment.get_build_dir()
for i in self.build.install_scripts:
- exe = i['exe']
- args = i['args']
fixed_args = []
- for a in args:
+ for a in i.cmd_args:
a = a.replace('@SOURCE_ROOT@', srcdir)
a = a.replace('@BUILD_ROOT@', builddir)
fixed_args.append(a)
- result.append(build.RunScript(exe, fixed_args))
+ es = copy.copy(i)
+ es.cmd_args = fixed_args
+ result.append(es)
d.install_scripts = result
def generate_header_install(self, d: InstallData) -> None:
diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py
index 400433f..36f1fd2 100644
--- a/mesonbuild/backend/ninjabackend.py
+++ b/mesonbuild/backend/ninjabackend.py
@@ -626,19 +626,14 @@
srcs[f] = s
return srcs
- # Languages that can mix with C or C++ but don't support unity builds yet
- # because the syntax we use for unity builds is specific to C/++/ObjC/++.
- # Assembly files cannot be unitified and neither can LLVM IR files
- langs_cant_unity = ('d', 'fortran')
-
def get_target_source_can_unity(self, target, source):
if isinstance(source, File):
source = source.fname
if self.environment.is_llvm_ir(source) or \
self.environment.is_assembly(source):
return False
- suffix = os.path.splitext(source)[1][1:]
- for lang in self.langs_cant_unity:
+ suffix = os.path.splitext(source)[1][1:].lower()
+ for lang in backends.LANGS_CANT_UNITY:
if lang not in target.compilers:
continue
if suffix in target.compilers[lang].file_suffixes:
@@ -762,7 +757,7 @@
if is_unity:
# Warn about incompatible sources if a unity build is enabled
langs = set(target.compilers.keys())
- langs_cant = langs.intersection(self.langs_cant_unity)
+ langs_cant = langs.intersection(backends.LANGS_CANT_UNITY)
if langs_cant:
langs_are = langs = ', '.join(langs_cant).upper()
langs_are += ' are' if len(langs_cant) > 1 else ' is'
@@ -918,7 +913,7 @@
all_suffixes = set(compilers.lang_suffixes['cpp']) | set(compilers.lang_suffixes['fortran'])
selected_sources = []
for source in compiled_sources:
- ext = os.path.splitext(source)[1][1:]
+ ext = os.path.splitext(source)[1][1:].lower()
if ext in all_suffixes:
selected_sources.append(source)
return selected_sources
@@ -965,7 +960,8 @@
meson_exe_cmd, reason = self.as_meson_exe_cmdline(target.name, target.command[0], cmd[1:],
extra_bdeps=target.get_transitive_build_target_deps(),
- capture=ofilenames[0] if target.capture else None)
+ capture=ofilenames[0] if target.capture else None,
+ env=target.env)
if meson_exe_cmd:
cmd = meson_exe_cmd
cmd_type = ' (wrapped by meson {})'.format(reason)
@@ -1598,7 +1594,7 @@
if isinstance(g, GeneratedList):
fname = os.path.join(self.get_target_private_dir(target), i)
else:
- fname = i
+ fname = os.path.join(g.get_subdir(), i)
if main_rust_file is None:
main_rust_file = fname
orderdeps.append(fname)
@@ -2602,7 +2598,10 @@
def get_fortran_orderdeps(self, target, compiler):
if compiler.language != 'fortran':
return []
- return [os.path.join(self.get_target_dir(lt), lt.get_filename()) for lt in target.link_targets]
+ return [
+ os.path.join(self.get_target_dir(lt), lt.get_filename())
+ for lt in itertools.chain(target.link_targets, target.link_whole_targets)
+ ]
def generate_msvc_pch_command(self, target, compiler, pch):
header = pch[0]
@@ -3026,14 +3025,14 @@
def generate_gcov_clean(self):
gcno_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
- gcno_elem.add_item('COMMAND', mesonlib.meson_command + ['--internal', 'delwithsuffix', '.', 'gcno'])
+ gcno_elem.add_item('COMMAND', mesonlib.get_meson_command() + ['--internal', 'delwithsuffix', '.', 'gcno'])
gcno_elem.add_item('description', 'Deleting gcno files')
self.add_build(gcno_elem)
# Alias that runs the target defined above
self.create_target_alias('meson-clean-gcno')
gcda_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
- gcda_elem.add_item('COMMAND', mesonlib.meson_command + ['--internal', 'delwithsuffix', '.', 'gcda'])
+ gcda_elem.add_item('COMMAND', mesonlib.get_meson_command() + ['--internal', 'delwithsuffix', '.', 'gcda'])
gcda_elem.add_item('description', 'Deleting gcda files')
self.add_build(gcda_elem)
# Alias that runs the target defined above
diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py
index 6e070a7..c47fb4a 100644
--- a/mesonbuild/backend/vs2010backend.py
+++ b/mesonbuild/backend/vs2010backend.py
@@ -176,7 +176,11 @@
# x86
self.platform = 'Win32'
elif target_machine == 'aarch64' or target_machine == 'arm64':
- self.platform = 'arm64'
+ target_cpu = self.interpreter.builtin['target_machine'].cpu_method(None, None)
+ if target_cpu == 'arm64ec':
+ self.platform = 'arm64ec'
+ else:
+ self.platform = 'arm64'
elif 'arm' in target_machine.lower():
self.platform = 'ARM'
else:
@@ -572,7 +576,8 @@
workdir=tdir_abs,
extra_bdeps=extra_bdeps,
capture=ofilenames[0] if target.capture else None,
- force_serialize=True)
+ force_serialize=True,
+ env=target.env)
if target.build_always_stale:
# Use a nonexistent file to always consider the target out-of-date.
ofilenames += [self.nonexistent_file(os.path.join(self.environment.get_scratch_dir(),
@@ -1218,6 +1223,8 @@
targetmachine.text = 'MachineARM'
elif targetplatform == 'arm64':
targetmachine.text = 'MachineARM64'
+ elif targetplatform == 'arm64ec':
+ targetmachine.text = 'MachineARM64EC'
else:
raise MesonException('Unsupported Visual Studio target machine: ' + targetplatform)
# /nologo
diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py
index 0e39c65..230b684 100644
--- a/mesonbuild/backend/xcodebackend.py
+++ b/mesonbuild/backend/xcodebackend.py
@@ -578,7 +578,7 @@
self.write_line(');')
self.write_line('runOnlyForDeploymentPostprocessing = 0;')
self.write_line('shellPath = /bin/sh;')
- cmd = mesonlib.meson_command + ['test', test_data, '-C', self.environment.get_build_dir()]
+ cmd = mesonlib.get_meson_command() + ['test', test_data, '-C', self.environment.get_build_dir()]
cmdstr = ' '.join(["'%s'" % i for i in cmd])
self.write_line('shellScript = "%s";' % cmdstr)
self.write_line('showEnvVarsInLog = 0;')
@@ -734,7 +734,9 @@
else:
product_name = target.get_basename()
ldargs += target.link_args
+ cargs = []
for dep in target.get_external_deps():
+ cargs += dep.get_compile_args()
ldargs += dep.get_link_args()
ldstr = ' '.join(ldargs)
valid = self.buildconfmap[target_name][buildtype]
@@ -751,6 +753,7 @@
args = pargs + gargs + targs
if args:
langargs[langnamemap[lang]] = args
+ langargs['C'] += cargs
symroot = os.path.join(self.environment.get_build_dir(), target.subdir)
self.write_line('%s /* %s */ = {' % (valid, buildtype))
self.indent_level += 1
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 13783d1..32daf54 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -2153,6 +2153,7 @@
'build_by_default',
'override_options',
'console',
+ 'env',
])
def __init__(self, name, subdir, subproject, kwargs, absolute_paths=False, backend=None):
@@ -2325,6 +2326,7 @@
else:
mlog.debug(i)
raise InvalidArguments('Unknown type {!r} in depend_files.'.format(type(i).__name__))
+ self.env = kwargs.get('env')
def get_dependencies(self):
return self.dependencies
@@ -2607,14 +2609,6 @@
else:
self.rename = rename
-class RunScript(dict):
- def __init__(self, script, args):
- super().__init__()
- assert(isinstance(script, list))
- assert(isinstance(args, list))
- self['exe'] = script
- self['args'] = args
-
class TestSetup:
def __init__(self, exe_wrapper: T.Optional[T.List[str]], gdb: bool,
timeout_multiplier: int, env: EnvironmentVariables):
diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py
index 782b7c2..abb4983 100644
--- a/mesonbuild/cmake/interpreter.py
+++ b/mesonbuild/cmake/interpreter.py
@@ -1322,7 +1322,7 @@
# Generate the command list
command = [] # type: T.List[T.Union[str, IdNode, IndexNode]]
- command += mesonlib.meson_command
+ command += mesonlib.get_meson_command()
command += ['--internal', 'cmake_run_ctgt']
command += ['-o', '@OUTPUT@']
if tgt.original_outputs:
diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py
index c11319b..0a2d478 100644
--- a/mesonbuild/compilers/c.py
+++ b/mesonbuild/compilers/c.py
@@ -16,7 +16,8 @@
import typing as T
from .. import coredata
-from ..mesonlib import MachineChoice, MesonException, mlog, version_compare, OptionKey
+from .. import mlog
+from ..mesonlib import MachineChoice, MesonException, version_compare, OptionKey
from .c_function_attributes import C_FUNC_ATTRIBUTES
from .mixins.clike import CLikeCompiler
from .mixins.ccrx import CcrxCompiler
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 1a7f3f1..219b0f2 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -867,7 +867,7 @@
# re-initialized with project options by the interpreter during
# build file parsing.
# meson_command is used by the regenchecker script, which runs meson
- self.coredata = coredata.CoreData(options, self.scratch_dir, mesonlib.meson_command)
+ self.coredata = coredata.CoreData(options, self.scratch_dir, mesonlib.get_meson_command())
self.first_invocation = True
def is_cross_build(self, when_building_for: MachineChoice = MachineChoice.HOST) -> bool:
@@ -887,7 +887,7 @@
return self.coredata
def get_build_command(self, unbuffered=False):
- cmd = mesonlib.meson_command[:]
+ cmd = mesonlib.get_meson_command().copy()
if unbuffered and 'python' in os.path.basename(cmd[0]):
cmd.insert(1, '-u')
return cmd
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index a3fa050..ceaa29a 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2019 The Meson development team
+# Copyright 2012-2021 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
@@ -34,7 +34,7 @@
from .interpreterbase import TYPE_var, TYPE_nkwargs
from .modules import ModuleReturnValue, ExtensionModule
from .cmake import CMakeInterpreter
-from .backend.backends import TestProtocol, Backend
+from .backend.backends import TestProtocol, Backend, ExecutableSerialisation
from pathlib import Path, PurePath
import os
@@ -1947,27 +1947,27 @@
'backend': self.backend_method,
})
- def _find_source_script(self, prog: T.Union[str, ExecutableHolder], args):
- if isinstance(prog, ExecutableHolder):
- prog_path = self.interpreter.backend.get_target_filename(prog.held_object)
- return build.RunScript([prog_path], args)
- elif isinstance(prog, ExternalProgramHolder):
- return build.RunScript(prog.get_command(), args)
-
+ def _find_source_script(self, prog: T.Union[str, mesonlib.File, ExecutableHolder], args):
+ if isinstance(prog, (ExecutableHolder, ExternalProgramHolder)):
+ return self.interpreter.backend.get_executable_serialisation([unholder(prog)] + args)
# Prefer scripts in the current source directory
search_dir = os.path.join(self.interpreter.environment.source_dir,
self.interpreter.subdir)
key = (prog, search_dir)
if key in self._found_source_scripts:
found = self._found_source_scripts[key]
+ elif isinstance(prog, mesonlib.File):
+ prog = prog.rel_to_builddir(self.interpreter.environment.source_dir)
+ found = dependencies.ExternalProgram(prog, search_dir=self.interpreter.environment.build_dir)
else:
found = dependencies.ExternalProgram(prog, search_dir=search_dir)
- if found.found():
- self._found_source_scripts[key] = found
- else:
- m = 'Script or command {!r} not found or not executable'
- raise InterpreterException(m.format(prog))
- return build.RunScript(found.get_command(), args)
+
+ if found.found():
+ self._found_source_scripts[key] = found
+ else:
+ m = 'Script or command {!r} not found or not executable'
+ raise InterpreterException(m.format(prog))
+ return self.interpreter.backend.get_executable_serialisation([found] + args)
def _process_script_args(
self, name: str, args: T.List[T.Union[
@@ -2016,9 +2016,12 @@
return script_args
@permittedKwargs(set())
- def add_install_script_method(self, args: 'T.Tuple[T.Union[str, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, ConfigureFileHolder], ...]', kwargs):
+ def add_install_script_method(self, args: 'T.Tuple[T.Union[str, mesonlib.File, ExecutableHolder], T.Union[str, mesonlib.File, CustomTargetHolder, CustomTargetIndexHolder, ConfigureFileHolder], ...]', kwargs):
if len(args) < 1:
raise InterpreterException('add_install_script takes one or more arguments')
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_install_script',
+ '0.57.0', self.interpreter.subproject)
script_args = self._process_script_args('add_install_script', args[1:], allow_built=True)
script = self._find_source_script(args[0], script_args)
self.build.install_scripts.append(script)
@@ -2027,6 +2030,9 @@
def add_postconf_script_method(self, args, kwargs):
if len(args) < 1:
raise InterpreterException('add_postconf_script takes one or more arguments')
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_postconf_script',
+ '0.57.0', self.interpreter.subproject)
script_args = self._process_script_args('add_postconf_script', args[1:], allow_built=True)
script = self._find_source_script(args[0], script_args)
self.build.postconf_scripts.append(script)
@@ -2038,6 +2044,9 @@
if len(args) > 1:
FeatureNew.single_use('Calling "add_dist_script" with multiple arguments',
'0.49.0', self.interpreter.subproject)
+ if isinstance(args[0], mesonlib.File):
+ FeatureNew.single_use('Passing file object to script parameter of add_dist_script',
+ '0.57.0', self.interpreter.subproject)
if self.interpreter.subproject != '':
raise InterpreterException('add_dist_script may not be used in a subproject.')
script_args = self._process_script_args('add_dist_script', args[1:], allow_built=True)
@@ -2315,7 +2324,8 @@
'depfile',
'build_by_default',
'build_always_stale',
- 'console'},
+ 'console',
+ 'env'},
'dependency': {'default_options',
'embed',
'fallback',
@@ -2544,7 +2554,7 @@
return GeneratedListHolder(item)
elif isinstance(item, build.RunTarget):
raise RuntimeError('This is not a pipe.')
- elif isinstance(item, build.RunScript):
+ elif isinstance(item, ExecutableSerialisation):
raise RuntimeError('Do not do this.')
elif isinstance(item, build.Data):
return DataHolder(item)
@@ -2571,7 +2581,7 @@
self.module_method_callback(v)
elif isinstance(v, build.GeneratedList):
pass
- elif isinstance(v, build.RunScript):
+ elif isinstance(v, ExecutableSerialisation):
self.build.install_scripts.append(v)
elif isinstance(v, build.Data):
self.build.data.append(v)
@@ -3168,9 +3178,29 @@
if not self.is_subproject():
self.build.project_name = proj_name
self.active_projectname = proj_name
- self.project_version = kwargs.get('version', 'undefined')
- if not isinstance(self.project_version, str):
- raise InvalidCode('The version keyword argument must be a string.')
+ version = kwargs.get('version', 'undefined')
+ if isinstance(version, list):
+ if len(version) != 1:
+ raise InvalidCode('Version argument is an array with more than one entry.')
+ version = version[0]
+ if isinstance(version, mesonlib.File):
+ FeatureNew.single_use('version from file', '0.57.0', self.subproject)
+ self.add_build_def_file(version)
+ ifname = version.absolute_path(self.environment.source_dir,
+ self.environment.build_dir)
+ try:
+ ver_data = Path(ifname).read_text(encoding='utf-8').split('\n')
+ except FileNotFoundError:
+ raise InterpreterException('Version file not found.')
+ if len(ver_data) == 2 and ver_data[1] == '':
+ ver_data = ver_data[0:1]
+ if len(ver_data) != 1:
+ raise InterpreterException('Version file must contain exactly one line of text.')
+ self.project_version = ver_data[0]
+ elif isinstance(version, str):
+ self.project_version = version
+ else:
+ raise InvalidCode('The version keyword argument must be a string or a file.')
if self.build.project_version is None:
self.build.project_version = self.project_version
proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
@@ -3997,6 +4027,7 @@
raise SubdirDoneRequest()
@stringArgs
+ @FeatureNewKwargs('custom_target', '0.57.0', ['env'])
@FeatureNewKwargs('custom_target', '0.48.0', ['console'])
@FeatureNewKwargs('custom_target', '0.47.0', ['install_mode', 'build_always_stale'])
@FeatureNewKwargs('custom_target', '0.40.0', ['build_by_default'])
@@ -4018,6 +4049,7 @@
except mesonlib.MesonException:
mlog.warning('''Custom target input \'%s\' can\'t be converted to File object(s).
This will become a hard error in the future.''' % kwargs['input'], location=self.current_node)
+ kwargs['env'] = self.unpack_env_kwarg(kwargs)
tg = CustomTargetHolder(build.CustomTarget(name, self.subdir, self.subproject, kwargs, backend=self.backend), self)
self.add_target(name, tg.held_object)
return tg
@@ -4140,6 +4172,10 @@
if not isinstance(should_fail, bool):
raise InterpreterException('Keyword argument should_fail must be a boolean.')
timeout = kwargs.get('timeout', 30)
+ if not isinstance(timeout, int):
+ raise InterpreterException('Timeout must be an integer.')
+ if timeout <= 0:
+ FeatureNew('test() timeout <= 0', '0.57.0').use(self.subproject)
if 'workdir' in kwargs:
workdir = kwargs['workdir']
if not isinstance(workdir, str):
@@ -4148,8 +4184,6 @@
raise InterpreterException('Workdir keyword argument must be an absolute path.')
else:
workdir = None
- if not isinstance(timeout, int):
- raise InterpreterException('Timeout must be an integer.')
protocol = kwargs.get('protocol', 'exitcode')
if protocol not in {'exitcode', 'tap', 'gtest', 'rust'}:
raise InterpreterException('Protocol must be one of "exitcode", "tap", "gtest", or "rust".')
@@ -4638,6 +4672,8 @@
timeout_multiplier = kwargs.get('timeout_multiplier', 1)
if not isinstance(timeout_multiplier, int):
raise InterpreterException('Timeout multiplier must be a number.')
+ if timeout_multiplier <= 0:
+ FeatureNew('add_test_setup() timeout_multiplier <= 0', '0.57.0').use(self.subproject)
is_default = kwargs.get('is_default', False)
if not isinstance(is_default, bool):
raise InterpreterException('is_default option must be a boolean')
diff --git a/mesonbuild/mdist.py b/mesonbuild/mdist.py
index 3ca13e5..4547b38 100644
--- a/mesonbuild/mdist.py
+++ b/mesonbuild/mdist.py
@@ -23,9 +23,10 @@
from glob import glob
from pathlib import Path
from mesonbuild.environment import detect_ninja
-from mesonbuild.mesonlib import windows_proof_rmtree, MesonException
+from mesonbuild.mesonlib import windows_proof_rmtree, MesonException, quiet_git
from mesonbuild.wrap import wrap
from mesonbuild import mlog, build
+from .scripts.meson_exe import run_exe
archive_choices = ['gztar', 'xztar', 'zip']
archive_extension = {'gztar': '.tar.gz',
@@ -79,26 +80,36 @@
def run_dist_scripts(src_root, bld_root, dist_root, dist_scripts):
assert(os.path.isabs(dist_root))
- env = os.environ.copy()
+ env = {}
env['MESON_DIST_ROOT'] = dist_root
env['MESON_SOURCE_ROOT'] = src_root
env['MESON_BUILD_ROOT'] = bld_root
for d in dist_scripts:
- script = d['exe']
- args = d['args']
- name = ' '.join(script + args)
+ name = ' '.join(d.cmd_args)
print('Running custom dist script {!r}'.format(name))
try:
- rc = subprocess.call(script + args, env=env)
+ rc = run_exe(d, env)
if rc != 0:
sys.exit('Dist script errored out')
except OSError:
print('Failed to run dist script {!r}'.format(name))
sys.exit(1)
+def git_root(src_root):
+ # Cannot use --show-toplevel here because git in our CI prints cygwin paths
+ # that python cannot resolve. Workaround this by taking parent of src_root.
+ prefix = quiet_git(['rev-parse', '--show-prefix'], src_root, check=True)[1].strip()
+ if not prefix:
+ return Path(src_root)
+ prefix_level = len(Path(prefix).parents)
+ return Path(src_root).parents[prefix_level - 1]
+
def is_git(src_root):
- _git = os.path.join(src_root, '.git')
- return os.path.isdir(_git) or os.path.isfile(_git)
+ '''
+ Checks if meson.build file at the root source directory is tracked by git.
+ It could be a subproject part of the parent project git repository.
+ '''
+ return quiet_git(['ls-files', '--error-unmatch', 'meson.build'], src_root)[0]
def git_have_dirty_index(src_root):
'''Check whether there are uncommitted changes in git'''
@@ -109,9 +120,21 @@
if git_have_dirty_index(src_root):
mlog.warning('Repository has uncommitted changes that will not be included in the dist tarball')
if os.path.exists(distdir):
- shutil.rmtree(distdir)
- os.makedirs(distdir)
- subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])
+ windows_proof_rmtree(distdir)
+ repo_root = git_root(src_root)
+ if repo_root.samefile(src_root):
+ os.makedirs(distdir)
+ subprocess.check_call(['git', 'clone', '--shared', src_root, distdir])
+ else:
+ subdir = Path(src_root).relative_to(repo_root)
+ tmp_distdir = distdir + '-tmp'
+ if os.path.exists(tmp_distdir):
+ windows_proof_rmtree(tmp_distdir)
+ os.makedirs(tmp_distdir)
+ subprocess.check_call(['git', 'clone', '--shared', '--no-checkout', str(repo_root), tmp_distdir])
+ subprocess.check_call(['git', 'checkout', 'HEAD', '--', str(subdir)], cwd=tmp_distdir)
+ Path(tmp_distdir, subdir).rename(distdir)
+ windows_proof_rmtree(tmp_distdir)
process_submodules(distdir)
del_gitfiles(distdir)
@@ -133,7 +156,7 @@
compressed_name = distdir + archive_extension[a]
shutil.make_archive(distdir, a, root_dir=dist_sub, base_dir=dist_name)
output_names.append(compressed_name)
- shutil.rmtree(distdir)
+ windows_proof_rmtree(distdir)
return output_names
def is_hg(src_root):
@@ -245,7 +268,7 @@
b = build.load(options.wd)
# This import must be load delayed, otherwise it will get the default
# value of None.
- from mesonbuild.mesonlib import meson_command
+ from mesonbuild.mesonlib import get_meson_command
src_root = b.environment.source_dir
bld_root = b.environment.build_dir
priv_dir = os.path.join(bld_root, 'meson-private')
@@ -279,7 +302,7 @@
rc = 0
if not options.no_tests:
# Check only one.
- rc = check_dist(names[0], meson_command, extra_meson_args, bld_root, priv_dir)
+ rc = check_dist(names[0], get_meson_command(), extra_meson_args, bld_root, priv_dir)
if rc == 0:
for name in names:
create_hash(name)
diff --git a/mesonbuild/mesonlib/__init__.py b/mesonbuild/mesonlib/__init__.py
new file mode 100644
index 0000000..5b646b5
--- /dev/null
+++ b/mesonbuild/mesonlib/__init__.py
@@ -0,0 +1,30 @@
+# SPDX-license-identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Helper functions and classes."""
+
+import os
+
+from .universal import *
+
+# Here we import either the posix implementations, the windows implementations,
+# or a generic no-op implementation
+if os.name == 'posix':
+ from .posix import *
+elif os.name == 'nt':
+ from .win32 import *
+else:
+ from .platform import *
diff --git a/mesonbuild/mesonlib/platform.py b/mesonbuild/mesonlib/platform.py
new file mode 100644
index 0000000..cdd42b1
--- /dev/null
+++ b/mesonbuild/mesonlib/platform.py
@@ -0,0 +1,37 @@
+# SPDX-license-identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""base classes providing no-op functionality.."""
+
+import os
+import typing as T
+
+from .. import mlog
+
+__all__ = ['BuildDirLock']
+
+# This needs to be inheritted by the specific implementations to make type
+# checking happy
+class BuildDirLock:
+
+ def __init__(self, builddir: str) -> None:
+ self.lockfilename = os.path.join(builddir, 'meson-private/meson.lock')
+
+ def __enter__(self) -> None:
+ mlog.debug('Calling ther no-op version of BuildDirLock')
+
+ def __exit__(self, *args: T.Any) -> None:
+ pass
diff --git a/mesonbuild/mesonlib/posix.py b/mesonbuild/mesonlib/posix.py
new file mode 100644
index 0000000..1d8ba8c
--- /dev/null
+++ b/mesonbuild/mesonlib/posix.py
@@ -0,0 +1,39 @@
+# SPDX-license-identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Posix specific implementations of mesonlib functionality."""
+
+import fcntl
+import typing as T
+
+from .universal import MesonException
+from .platform import BuildDirLock as BuildDirLockBase
+
+__all__ = ['BuildDirLock']
+
+class BuildDirLock(BuildDirLockBase):
+
+ def __enter__(self) -> None:
+ self.lockfile = open(self.lockfilename, 'w')
+ try:
+ fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except (BlockingIOError, PermissionError):
+ self.lockfile.close()
+ raise MesonException('Some other Meson process is already using this build directory. Exiting.')
+
+ def __exit__(self, *args: T.Any) -> None:
+ fcntl.flock(self.lockfile, fcntl.LOCK_UN)
+ self.lockfile.close()
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib/universal.py
similarity index 95%
rename from mesonbuild/mesonlib.py
rename to mesonbuild/mesonlib/universal.py
index ef48ec2..dfcec8e 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib/universal.py
@@ -30,34 +30,117 @@
from mesonbuild import mlog
if T.TYPE_CHECKING:
- from .build import ConfigurationData
- from .coredata import KeyedOptionDictType, UserOption
- from .compilers.compilers import CompilerType
- from .interpreterbase import ObjectHolder
+ from ..build import ConfigurationData
+ from ..coredata import KeyedOptionDictType, UserOption
+ from ..compilers.compilers import CompilerType
+ from ..interpreterbase import ObjectHolder
- FileOrString = T.Union['File', str]
+FileOrString = T.Union['File', str]
_T = T.TypeVar('_T')
_U = T.TypeVar('_U')
-have_fcntl = False
-have_msvcrt = False
+__all__ = [
+ 'GIT',
+ 'an_unpicklable_object',
+ 'python_command',
+ 'project_meson_versions',
+ 'File',
+ 'FileMode',
+ 'GitException',
+ 'LibType',
+ 'MachineChoice',
+ 'MesonException',
+ 'EnvironmentException',
+ 'FileOrString',
+ 'GitException',
+ 'OptionKey',
+ 'dump_conf_header',
+ 'OptionOverrideProxy',
+ 'OptionProxy',
+ 'OptionType',
+ 'OrderedSet',
+ 'PerMachine',
+ 'PerMachineDefaultable',
+ 'PerThreeMachine',
+ 'PerThreeMachineDefaultable',
+ 'ProgressBar',
+ 'TemporaryDirectoryWinProof',
+ 'Version',
+ 'check_direntry_issues',
+ 'classify_unity_sources',
+ 'current_vs_supports_modules',
+ 'darwin_get_object_archs',
+ 'default_libdir',
+ 'default_libexecdir',
+ 'default_prefix',
+ 'detect_subprojects',
+ 'detect_vcs',
+ 'do_conf_file',
+ 'do_conf_str',
+ 'do_define',
+ 'do_replacement',
+ 'exe_exists',
+ 'expand_arguments',
+ 'extract_as_list',
+ 'get_compiler_for_source',
+ 'get_filenames_templates_dict',
+ 'get_library_dirs',
+ 'get_variable_regex',
+ 'get_wine_shortpath',
+ 'git',
+ 'has_path_sep',
+ 'is_aix',
+ 'is_android',
+ 'is_ascii_string',
+ 'is_cygwin',
+ 'is_debianlike',
+ 'is_dragonflybsd',
+ 'is_freebsd',
+ 'is_haiku',
+ 'is_hurd',
+ 'is_irix',
+ 'is_linux',
+ 'is_netbsd',
+ 'is_openbsd',
+ 'is_osx',
+ 'is_qnx',
+ 'is_sunos',
+ 'is_windows',
+ 'iter_regexin_iter',
+ 'join_args',
+ 'listify',
+ 'partition',
+ 'path_is_in_root',
+ 'Popen_safe',
+ 'quiet_git',
+ 'quote_arg',
+ 'relative_to_if_possible',
+ 'relpath',
+ 'replace_if_different',
+ 'run_once',
+ 'get_meson_command',
+ 'set_meson_command',
+ 'split_args',
+ 'stringlistify',
+ 'substitute_values',
+ 'substring_is_in_list',
+ 'typeslistify',
+ 'unholder',
+ 'verbose_git',
+ 'version_compare',
+ 'version_compare_condition_with_min',
+ 'version_compare_many',
+ 'windows_proof_rm',
+ 'windows_proof_rmtree',
+]
+
+
# TODO: this is such a hack, this really should be either in coredata or in the
# interpreter
# {subproject: project_meson_version}
project_meson_versions = collections.defaultdict(str) # type: T.DefaultDict[str, str]
-try:
- import fcntl
- have_fcntl = True
-except Exception:
- pass
-
-try:
- import msvcrt
- have_msvcrt = True
-except Exception:
- pass
from glob import glob
@@ -66,7 +149,7 @@
python_command = [sys.executable, 'runpython']
else:
python_command = [sys.executable]
-meson_command = None
+_meson_command = None
class MesonException(Exception):
'''Exceptions thrown by Meson'''
@@ -117,20 +200,24 @@
def set_meson_command(mainfile: str) -> None:
global python_command
- global meson_command
+ global _meson_command
# On UNIX-like systems `meson` is a Python script
# On Windows `meson` and `meson.exe` are wrapper exes
if not mainfile.endswith('.py'):
- meson_command = [mainfile]
+ _meson_command = [mainfile]
elif os.path.isabs(mainfile) and mainfile.endswith('mesonmain.py'):
# Can't actually run meson with an absolute path to mesonmain.py, it must be run as -m mesonbuild.mesonmain
- meson_command = python_command + ['-m', 'mesonbuild.mesonmain']
+ _meson_command = python_command + ['-m', 'mesonbuild.mesonmain']
else:
# Either run uninstalled, or full path to meson-script.py
- meson_command = python_command + [mainfile]
+ _meson_command = python_command + [mainfile]
# We print this value for unit tests.
if 'MESON_COMMAND_TESTS' in os.environ:
- mlog.log('meson_command is {!r}'.format(meson_command))
+ mlog.log('meson_command is {!r}'.format(_meson_command))
+
+
+def get_meson_command() -> T.Optional[T.List[str]]:
+ return _meson_command
def is_ascii_string(astring: T.Union[str, bytes]) -> bool:
@@ -261,8 +348,15 @@
perms |= stat.S_ISVTX
return perms
+dot_C_dot_H_warning = """You are using .C or .H files in your project. This is deprecated.
+ Currently, Meson treats this as C code, but this
+ might change in the future, breaking your build.
+ You code also might be already broken on gcc and clang.
+ See https://github.com/mesonbuild/meson/pull/8239 for the discussions."""
class File:
def __init__(self, is_built: bool, subdir: str, fname: str):
+ if fname.endswith(".C") or fname.endswith(".H"):
+ mlog.warning(dot_C_dot_H_warning, once=True)
self.is_built = is_built
self.subdir = subdir
self.fname = fname
@@ -1564,29 +1658,6 @@
def difference(self, set_: T.Union[T.Set[_T], 'OrderedSet[_T]']) -> 'OrderedSet[_T]':
return type(self)(e for e in self if e not in set_)
-class BuildDirLock:
-
- def __init__(self, builddir: str) -> None:
- self.lockfilename = os.path.join(builddir, 'meson-private/meson.lock')
-
- def __enter__(self) -> None:
- self.lockfile = open(self.lockfilename, 'w')
- try:
- if have_fcntl:
- fcntl.flock(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
- elif have_msvcrt:
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
- except (BlockingIOError, PermissionError):
- self.lockfile.close()
- raise MesonException('Some other Meson process is already using this build directory. Exiting.')
-
- def __exit__(self, *args: T.Any) -> None:
- if have_fcntl:
- fcntl.flock(self.lockfile, fcntl.LOCK_UN)
- elif have_msvcrt:
- msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
- self.lockfile.close()
-
def relpath(path: str, start: str) -> str:
# On Windows a relative path can't be evaluated for paths on two different
# drives (i.e. c:\foo and f:\bar). The only thing left to do is to use the
@@ -1965,7 +2036,7 @@
raw3 = raw2
for_machine = MachineChoice.HOST
- from .compilers import all_languages
+ from ..compilers import all_languages
if any(raw3.startswith(f'{l}_') for l in all_languages):
lang, opt = raw3.split('_', 1)
else:
@@ -2024,4 +2095,4 @@
def is_base(self) -> bool:
"""Convenience method to check if this is a base option."""
- return self.type is OptionType.BASE
\ No newline at end of file
+ return self.type is OptionType.BASE
diff --git a/mesonbuild/mesonlib/win32.py b/mesonbuild/mesonlib/win32.py
new file mode 100644
index 0000000..0919ef7
--- /dev/null
+++ b/mesonbuild/mesonlib/win32.py
@@ -0,0 +1,39 @@
+# SPDX-license-identifier: Apache-2.0
+# Copyright 2012-2021 The Meson development team
+# Copyright © 2021 Intel Corporation
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Windows specific implementations of mesonlib functionality."""
+
+import msvcrt
+import typing as T
+
+from .universal import MesonException
+from .platform import BuildDirLock as BuildDirLockBase
+
+__all__ = ['BuildDirLock']
+
+class BuildDirLock(BuildDirLockBase):
+
+ def __enter__(self) -> None:
+ self.lockfile = open(self.lockfilename, 'w')
+ try:
+ msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_NBLCK, 1)
+ except (BlockingIOError, PermissionError):
+ self.lockfile.close()
+ raise MesonException('Some other Meson process is already using this build directory. Exiting.')
+
+ def __exit__(self, *args: T.Any) -> None:
+ msvcrt.locking(self.lockfile.fileno(), msvcrt.LK_UNLCK, 1)
+ self.lockfile.close()
diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py
index 4a38313..55e716c 100644
--- a/mesonbuild/minit.py
+++ b/mesonbuild/minit.py
@@ -174,7 +174,7 @@
print('Build directory already exists, deleting it.')
shutil.rmtree(options.builddir)
print('Building...')
- cmd = mesonlib.meson_command + [options.builddir]
+ cmd = mesonlib.get_meson_command() + [options.builddir]
ret = subprocess.run(cmd)
if ret.returncode:
raise SystemExit
diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py
index 3e425eb..98608c7 100644
--- a/mesonbuild/minstall.py
+++ b/mesonbuild/minstall.py
@@ -30,6 +30,7 @@
from .coredata import version as coredata_version
from .mesonlib import is_windows, Popen_safe
from .scripts import depfixer, destdir_join
+from .scripts.meson_exe import run_exe
try:
from __main__ import __file__ as main_file
except ImportError:
@@ -485,17 +486,12 @@
if self.options.quiet:
env['MESON_INSTALL_QUIET'] = '1'
- child_env = os.environ.copy()
- child_env.update(env)
-
for i in d.install_scripts:
self.did_install_something = True # Custom script must report itself if it does nothing.
- script = i['exe']
- args = i['args']
- name = ' '.join(script + args)
+ name = ' '.join(i.cmd_args)
self.log('Running custom install script {!r}'.format(name))
try:
- rc = subprocess.call(script + args, env=child_env)
+ rc = run_exe(i, env)
except OSError:
print('FAILED: install script \'{}\' could not be run, stopped'.format(name))
# POSIX shells return 127 when a command could not be found
diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py
index 5cad9f5..f564eb4 100644
--- a/mesonbuild/modules/gnome.py
+++ b/mesonbuild/modules/gnome.py
@@ -899,7 +899,7 @@
args.append('--media=' + '@@'.join(media))
if langs:
args.append('--langs=' + '@@'.join(langs))
- inscript = build.RunScript(script, args)
+ inscript = state.backend.get_executable_serialisation(script + args)
potargs = state.environment.get_build_command() + [
'--internal', 'yelphelper', 'pot',
@@ -1051,7 +1051,7 @@
self.interpreter.add_test(state.current_node, check_args, check_kwargs, True)
res = [custom_target, alias_target]
if kwargs.get('install', True):
- res.append(build.RunScript(command, args))
+ res.append(state.backend.get_executable_serialisation(command + args))
return ModuleReturnValue(custom_target, res)
def _get_build_args(self, kwargs, state, depends):
diff --git a/mesonbuild/modules/hotdoc.py b/mesonbuild/modules/hotdoc.py
index 931db12..eda411c 100644
--- a/mesonbuild/modules/hotdoc.py
+++ b/mesonbuild/modules/hotdoc.py
@@ -350,7 +350,7 @@
install_script = None
if install is True:
- install_script = HotdocRunScript(self.build_command, [
+ install_script = self.state.backend.get_executable_serialisation(self.build_command + [
"--internal", "hotdoc",
"--install", os.path.join(fullname, 'html'),
'--name', self.name,
@@ -391,11 +391,6 @@
return res
-class HotdocRunScript(build.RunScript):
- def __init__(self, script, args):
- super().__init__(script, args)
-
-
class HotDocModule(ExtensionModule):
@FeatureNew('Hotdoc Module', '0.48.0')
def __init__(self, interpreter):
diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py
index d48f83b..ae24e6e 100644
--- a/mesonbuild/modules/i18n.py
+++ b/mesonbuild/modules/i18n.py
@@ -180,7 +180,7 @@
pkg_arg]
if lang_arg:
args.append(lang_arg)
- iscript = build.RunScript(script, args)
+ iscript = state.backend.get_executable_serialisation(script + args)
targets.append(iscript)
return ModuleReturnValue(None, targets)
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index b1d43bd..24db5ce 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -130,7 +130,7 @@
parser.add_argument('-t', '--timeout-multiplier', type=float, default=None,
help='Define a multiplier for test timeout, for example '
' when running tests in particular conditions they might take'
- ' more time to execute.')
+ ' more time to execute. (<= 0 to disable timeout)')
parser.add_argument('--setup', default=None, dest='setup',
help='Which test setup to use.')
parser.add_argument('--test-args', default=[], type=split_args,
@@ -541,7 +541,7 @@
spaces=' ' * TestResult.maxlen(),
dur=int(time.time() - self.progress_test.starttime),
durlen=harness.duration_max_len,
- timeout=int(self.progress_test.timeout))
+ timeout=int(self.progress_test.timeout or -1))
detail = self.progress_test.detail
if detail:
right += ' ' + detail
@@ -1270,12 +1270,14 @@
if ('MALLOC_PERTURB_' not in env or not env['MALLOC_PERTURB_']) and not options.benchmark:
env['MALLOC_PERTURB_'] = str(random.randint(1, 255))
- if self.options.gdb or self.test.timeout is None:
+ if self.options.gdb or self.test.timeout is None or self.test.timeout <= 0:
timeout = None
- elif self.options.timeout_multiplier is not None:
- timeout = self.test.timeout * self.options.timeout_multiplier
- else:
+ elif self.options.timeout_multiplier is None:
timeout = self.test.timeout
+ elif self.options.timeout_multiplier <= 0:
+ timeout = None
+ else:
+ timeout = self.test.timeout * self.options.timeout_multiplier
self.runobj = TestRun(test, env, name, timeout)
diff --git a/mesonbuild/scripts/depscan.py b/mesonbuild/scripts/depscan.py
index df7df48..c85f8e7 100644
--- a/mesonbuild/scripts/depscan.py
+++ b/mesonbuild/scripts/depscan.py
@@ -46,7 +46,7 @@
self.sources_with_exports = [] # type: T.List[str]
def scan_file(self, fname: str) -> None:
- suffix = os.path.splitext(fname)[1][1:]
+ suffix = os.path.splitext(fname)[1][1:].lower()
if suffix in lang_suffixes['fortran']:
self.scan_fortran_file(fname)
elif suffix in lang_suffixes['cpp']:
@@ -131,7 +131,7 @@
return objname
def module_name_for(self, src: str) -> str:
- suffix= os.path.splitext(src)[1][1:]
+ suffix = os.path.splitext(src)[1][1:].lower()
if suffix in lang_suffixes['fortran']:
exported = self.exports[src]
# Module foo:bar goes to a file name foo@bar.smod
diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py
index 50ad2f5..620f579 100644
--- a/mesonbuild/scripts/meson_exe.py
+++ b/mesonbuild/scripts/meson_exe.py
@@ -30,7 +30,7 @@
parser.add_argument('--capture')
return parser
-def run_exe(exe: ExecutableSerialisation) -> int:
+def run_exe(exe: ExecutableSerialisation, extra_env: T.Optional[dict] = None) -> int:
if exe.exe_runner:
if not exe.exe_runner.found():
raise AssertionError('BUG: Can\'t run cross-compiled exe {!r} with not-found '
@@ -39,7 +39,10 @@
else:
cmd_args = exe.cmd_args
child_env = os.environ.copy()
- child_env.update(exe.env)
+ if extra_env:
+ child_env.update(extra_env)
+ if exe.env:
+ child_env = exe.env.get_env(child_env)
if exe.extra_paths:
child_env['PATH'] = (os.pathsep.join(exe.extra_paths + ['']) +
child_env['PATH'])
@@ -55,14 +58,21 @@
stderr=subprocess.PIPE)
stdout, stderr = p.communicate()
- if exe.pickled and p.returncode != 0:
- print('while executing {!r}'.format(cmd_args))
-
if p.returncode == 0xc0000135:
# STATUS_DLL_NOT_FOUND on Windows indicating a common problem that is otherwise hard to diagnose
raise FileNotFoundError('due to missing DLLs')
- if exe.capture and p.returncode == 0:
+ if p.returncode != 0:
+ if exe.pickled:
+ print('while executing {!r}'.format(cmd_args))
+ if not exe.capture:
+ print('--- stdout ---')
+ print(stdout.decode())
+ print('--- stderr ---')
+ print(stderr.decode())
+ return p.returncode
+
+ if exe.capture:
skip_write = False
try:
with open(exe.capture, 'rb') as cur:
@@ -72,11 +82,8 @@
if not skip_write:
with open(exe.capture, 'wb') as output:
output.write(stdout)
- else:
- sys.stdout.buffer.write(stdout)
- if stderr:
- sys.stderr.buffer.write(stderr)
- return p.returncode
+
+ return 0
def run(args: T.List[str]) -> int:
global options
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
index 34e58e3..cce44a8 100644
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -353,8 +353,10 @@
raise WrapException(m)
def resolve_git_submodule(self) -> bool:
+ # Is git installed? If not, we're probably not in a git repository and
+ # definitely cannot try to conveniently set up a submodule.
if not GIT:
- raise WrapException('Git program not found.')
+ return False
# Are we in a git repository?
ret, out = quiet_git(['rev-parse'], self.subdir_root)
if not ret:
diff --git a/run_mypy.py b/run_mypy.py
index daf7431..e6900c7 100755
--- a/run_mypy.py
+++ b/run_mypy.py
@@ -1,9 +1,10 @@
#!/usr/bin/env python3
-import sys
-import subprocess
-import argparse
from pathlib import Path
+import argparse
+import os
+import subprocess
+import sys
import typing as T
modules = [
@@ -24,7 +25,8 @@
'mesonbuild/interpreterbase.py',
'mesonbuild/linkers.py',
'mesonbuild/mcompile.py',
- 'mesonbuild/mesonlib.py',
+ 'mesonbuild/mesonlib/platform.py',
+ 'mesonbuild/mesonlib/universal.py',
'mesonbuild/minit.py',
'mesonbuild/minstall.py',
'mesonbuild/mintro.py',
@@ -40,6 +42,11 @@
'tools'
]
+if os.name == 'posix':
+ modules.append('mesonbuild/mesonlib/posix.py')
+elif os.name == 'nt':
+ modules.append('mesonbuild/mesonlib/win32.py')
+
def check_mypy() -> None:
try:
import mypy
diff --git a/run_unittests.py b/run_unittests.py
index 8762b9c..2a14f78 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -30,7 +30,7 @@
import io
import operator
import threading
-import zipfile
+import zipfile, tarfile
import hashlib
from itertools import chain
from unittest import mock
@@ -146,6 +146,9 @@
'user.name', 'Author Person'], cwd=project_dir)
subprocess.check_call(['git', 'config',
'user.email', 'teh_coderz@example.com'], cwd=project_dir)
+ _git_add_all(project_dir)
+
+def _git_add_all(project_dir):
subprocess.check_call('git add *', cwd=project_dir, shell=True,
stdout=subprocess.DEVNULL)
subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir,
@@ -2847,7 +2850,7 @@
raise unittest.SkipTest('Dist is only supported with Ninja')
try:
- self.dist_impl(_git_init)
+ self.dist_impl(_git_init, _git_add_all)
except PermissionError:
# When run under Windows CI, something (virus scanner?)
# holds on to the git files so cleaning up the dir
@@ -2916,10 +2919,10 @@
path = os.path.join(project_dir, 'subprojects', name)
os.makedirs(path)
with open(os.path.join(path, 'meson.build'), 'w') as ofile:
- ofile.write("project('{}')".format(name))
+ ofile.write("project('{}', version: '1.0')".format(name))
return path
- def dist_impl(self, vcs_init, include_subprojects=True):
+ def dist_impl(self, vcs_init, vcs_add_all=None, include_subprojects=True):
# Create this on the fly because having rogue .git directories inside
# the source tree leads to all kinds of trouble.
with tempfile.TemporaryDirectory() as project_dir:
@@ -2930,6 +2933,7 @@
test('dist test', e)
subproject('vcssub', required : false)
subproject('tarballsub', required : false)
+ subproject('samerepo', required : false)
'''))
with open(os.path.join(project_dir, 'distexe.c'), 'w') as ofile:
ofile.write(textwrap.dedent('''\
@@ -2949,6 +2953,8 @@
vcs_init(self.create_dummy_subproject(project_dir, 'vcssub'))
self.create_dummy_subproject(project_dir, 'tarballsub')
self.create_dummy_subproject(project_dir, 'unusedsub')
+ if vcs_add_all:
+ vcs_add_all(self.create_dummy_subproject(project_dir, 'samerepo'))
self.init(project_dir)
self.build('dist')
self.assertPathExists(xz_distfile)
@@ -2961,24 +2967,46 @@
self.assertPathExists(zip_checksumfile)
if include_subprojects:
+ # Verify that without --include-subprojects we have files from
+ # the main project and also files from subprojects part of the
+ # main vcs repository.
z = zipfile.ZipFile(zip_distfile)
- self.assertEqual(sorted(['disttest-1.4.3/',
- 'disttest-1.4.3/meson.build',
- 'disttest-1.4.3/distexe.c']),
+ expected = ['disttest-1.4.3/',
+ 'disttest-1.4.3/meson.build',
+ 'disttest-1.4.3/distexe.c']
+ if vcs_add_all:
+ expected += ['disttest-1.4.3/subprojects/',
+ 'disttest-1.4.3/subprojects/samerepo/',
+ 'disttest-1.4.3/subprojects/samerepo/meson.build']
+ self.assertEqual(sorted(expected),
sorted(z.namelist()))
-
+ # Verify that with --include-subprojects we now also have files
+ # from tarball and separate vcs subprojects. But not files from
+ # unused subprojects.
self._run(self.meson_command + ['dist', '--formats', 'zip', '--include-subprojects'],
workdir=self.builddir)
z = zipfile.ZipFile(zip_distfile)
- self.assertEqual(sorted(['disttest-1.4.3/',
- 'disttest-1.4.3/subprojects/',
- 'disttest-1.4.3/meson.build',
- 'disttest-1.4.3/distexe.c',
- 'disttest-1.4.3/subprojects/tarballsub/',
- 'disttest-1.4.3/subprojects/vcssub/',
- 'disttest-1.4.3/subprojects/tarballsub/meson.build',
- 'disttest-1.4.3/subprojects/vcssub/meson.build']),
+ expected += ['disttest-1.4.3/subprojects/tarballsub/',
+ 'disttest-1.4.3/subprojects/tarballsub/meson.build',
+ 'disttest-1.4.3/subprojects/vcssub/',
+ 'disttest-1.4.3/subprojects/vcssub/meson.build']
+ self.assertEqual(sorted(expected),
sorted(z.namelist()))
+ if vcs_add_all:
+ # Verify we can distribute separately subprojects in the same vcs
+ # repository as the main project.
+ subproject_dir = os.path.join(project_dir, 'subprojects', 'samerepo')
+ self.new_builddir()
+ self.init(subproject_dir)
+ self.build('dist')
+ xz_distfile = os.path.join(self.distdir, 'samerepo-1.0.tar.xz')
+ xz_checksumfile = xz_distfile + '.sha256sum'
+ self.assertPathExists(xz_distfile)
+ self.assertPathExists(xz_checksumfile)
+ tar = tarfile.open(xz_distfile, "r:xz")
+ self.assertEqual(sorted(['samerepo-1.0',
+ 'samerepo-1.0/meson.build']),
+ sorted([i.name for i in tar]))
def test_rpath_uses_ORIGIN(self):
'''
@@ -5185,6 +5213,12 @@
os.utime(str(cmakefile))
self.assertReconfiguredBuildIsNoop()
+ def test_version_file(self):
+ srcdir = os.path.join(self.common_test_dir, '2 cpp')
+ self.init(srcdir)
+ projinfo = self.introspect('--projectinfo')
+ self.assertEqual(projinfo['version'], '1.0.0')
+
class FailureTests(BasePlatformTests):
'''
diff --git a/setup.py b/setup.py
index 70a76e5..17a00b3 100644
--- a/setup.py
+++ b/setup.py
@@ -33,6 +33,7 @@
'mesonbuild.compilers',
'mesonbuild.compilers.mixins',
'mesonbuild.dependencies',
+ 'mesonbuild.mesonlib',
'mesonbuild.modules',
'mesonbuild.scripts',
'mesonbuild.templates',
diff --git a/test cases/common/2 cpp/VERSION b/test cases/common/2 cpp/VERSION
new file mode 100644
index 0000000..3eefcb9
--- /dev/null
+++ b/test cases/common/2 cpp/VERSION
@@ -0,0 +1 @@
+1.0.0
diff --git a/test cases/common/2 cpp/meson.build b/test cases/common/2 cpp/meson.build
index de8b98e..47cb7c5 100644
--- a/test cases/common/2 cpp/meson.build
+++ b/test cases/common/2 cpp/meson.build
@@ -1,4 +1,4 @@
-project('c++ test', 'cpp')
+project('c++ test', 'cpp', version: files('VERSION'))
cpp = meson.get_compiler('cpp')
if cpp.get_id() == 'intel'
diff --git a/test cases/common/50 custom target/meson.build b/test cases/common/50 custom target/meson.build
index 5c7cfae..52e8630 100644
--- a/test cases/common/50 custom target/meson.build
+++ b/test cases/common/50 custom target/meson.build
@@ -15,6 +15,7 @@
output : 'data.dat',
input : 'data_source.txt',
command : [python, comp, '--input=@INPUT@', '--output=@OUTPUT@', useless],
+env: {'MY_COMPILER_ENV': 'value'},
install : true,
install_dir : 'subdir'
)
diff --git a/test cases/common/50 custom target/my_compiler.py b/test cases/common/50 custom target/my_compiler.py
index f46d23a..9869111 100755
--- a/test cases/common/50 custom target/my_compiler.py
+++ b/test cases/common/50 custom target/my_compiler.py
@@ -8,6 +8,7 @@
args = sys.argv[:-1]
if __name__ == '__main__':
+ assert os.environ['MY_COMPILER_ENV'] == 'value'
if len(args) != 3 or not args[1].startswith('--input') or \
not args[2].startswith('--output'):
print(args[0], '--input=input_file --output=output_file')
diff --git a/test cases/common/54 install script/meson.build b/test cases/common/54 install script/meson.build
index 696e3f6..24d5dc8 100644
--- a/test cases/common/54 install script/meson.build
+++ b/test cases/common/54 install script/meson.build
@@ -1,6 +1,5 @@
project('custom install script', 'c')
-executable('prog', 'prog.c', install : true)
meson.add_install_script('myinstall.py', 'diiba/daaba', 'file.dat')
meson.add_install_script('myinstall.py', 'this/should', 'also-work.dat')
@@ -25,3 +24,22 @@
meson.add_install_script('myinstall.py', 'customtarget', t, '--mode=copy')
meson.add_install_script('myinstall.py', 'customtargetindex', t[0], '--mode=copy')
+
+installer = configure_file(
+ input : 'myinstall.py',
+ output : 'myinstall_copy.py',
+ copy : true,
+)
+
+meson.add_install_script(installer, 'otherdir', afile, '--mode=copy')
+
+# This executable links on a library built in src/ directory. On Windows this
+# means meson must add src/ into $PATH to find the DLL when running it as
+# install script.
+myexe = executable('prog', 'prog.c',
+ link_with: mylib,
+ install : true,
+)
+if meson.can_run_host_binaries()
+ meson.add_install_script(myexe)
+endif
diff --git a/test cases/common/54 install script/myinstall.py b/test cases/common/54 install script/myinstall.py
old mode 100644
new mode 100755
diff --git a/test cases/common/54 install script/prog.c b/test cases/common/54 install script/prog.c
index 3bbf08e..85f8df9 100644
--- a/test cases/common/54 install script/prog.c
+++ b/test cases/common/54 install script/prog.c
@@ -1,6 +1,14 @@
#include<stdio.h>
+#ifdef _WIN32
+ #define DO_IMPORT __declspec(dllimport)
+#else
+ #define DO_IMPORT
+#endif
+
+DO_IMPORT int foo(void);
+
int main(void) {
printf("This is text.\n");
- return 0;
+ return foo();
}
diff --git a/test cases/common/54 install script/src/foo.c b/test cases/common/54 install script/src/foo.c
new file mode 100644
index 0000000..46cb845
--- /dev/null
+++ b/test cases/common/54 install script/src/foo.c
@@ -0,0 +1,10 @@
+#ifdef _WIN32
+ #define DO_EXPORT __declspec(dllexport)
+#else
+ #define DO_EXPORT
+#endif
+
+DO_EXPORT int foo(void)
+{
+ return 0;
+}
diff --git a/test cases/common/54 install script/src/meson.build b/test cases/common/54 install script/src/meson.build
index 123fe18..72de346 100644
--- a/test cases/common/54 install script/src/meson.build
+++ b/test cases/common/54 install script/src/meson.build
@@ -1,3 +1,5 @@
meson.add_install_script('myinstall.py', 'this/does', 'something-different.dat')
afile = files('a file.txt')
+
+mylib = shared_library('mylib', 'foo.c')
diff --git a/test cases/common/54 install script/test.json b/test cases/common/54 install script/test.json
index 3804fe1..7ac2607 100644
--- a/test cases/common/54 install script/test.json
+++ b/test cases/common/54 install script/test.json
@@ -7,6 +7,7 @@
{"type": "file", "file": "usr/this/does/something-different.dat.in"},
{"type": "file", "file": "usr/dir/a file.txt"},
{"type": "file", "file": "usr/dir/conf.txt"},
+ {"type": "file", "file": "usr/otherdir/a file.txt"},
{"type": "file", "file": "usr/customtarget/1.txt"},
{"type": "file", "file": "usr/customtarget/2.txt"},
{"type": "file", "file": "usr/customtargetindex/1.txt"}
diff --git a/test cases/fortran/21 install static/main.f90 b/test cases/fortran/21 install static/main.f90
new file mode 100644
index 0000000..c83a6a0
--- /dev/null
+++ b/test cases/fortran/21 install static/main.f90
@@ -0,0 +1,4 @@
+use main_lib
+implicit none
+call main_hello()
+end program
\ No newline at end of file
diff --git a/test cases/fortran/21 install static/main_lib.f90 b/test cases/fortran/21 install static/main_lib.f90
new file mode 100644
index 0000000..5f3cb45
--- /dev/null
+++ b/test cases/fortran/21 install static/main_lib.f90
@@ -0,0 +1,16 @@
+module main_lib
+
+ use static_hello
+ implicit none
+
+ private
+ public :: main_hello
+
+ contains
+
+ subroutine main_hello
+ call static_say_hello()
+ print *, "Main hello routine finished."
+ end subroutine main_hello
+
+end module main_lib
diff --git a/test cases/fortran/21 install static/meson.build b/test cases/fortran/21 install static/meson.build
new file mode 100644
index 0000000..a91613f
--- /dev/null
+++ b/test cases/fortran/21 install static/meson.build
@@ -0,0 +1,20 @@
+# Based on 'fortran/5 static', but:
+# - Uses a subproject dependency
+# - Is an install:true static library to trigger certain codepath (promotion to link_whole)
+# - Does fortran code 'generation' with configure_file
+# - Uses .F90 ext (capital F typically denotes a dependence on preprocessor treatment, which however is not used)
+project('try-static-subproject-dependency', 'fortran', default_options: ['default_library=static'])
+
+static_dep = dependency('static_hello', fallback: ['static_hello', 'static_hello_dep'])
+
+mainsrc = 'main_lib.f90'
+mainsrc = configure_file(
+ command: [find_program('cp'), '@INPUT@', '@OUTPUT@'],
+ input: mainsrc,
+ output: 'main_lib_output.F90'
+)
+main_lib = library('mainstatic', mainsrc, dependencies: static_dep, install: true)
+main_dep = declare_dependency(link_with: main_lib)
+
+main_exe = executable('main_exe', 'main.f90', dependencies: main_dep)
+test('static_subproject_test', main_exe)
diff --git a/test cases/fortran/21 install static/subprojects/static_hello/meson.build b/test cases/fortran/21 install static/subprojects/static_hello/meson.build
new file mode 100644
index 0000000..7edca39
--- /dev/null
+++ b/test cases/fortran/21 install static/subprojects/static_hello/meson.build
@@ -0,0 +1,12 @@
+project('static-hello', 'fortran')
+
+# staticlibsource = 'static_hello.f90'
+staticlibsource = configure_file(
+ command: [find_program('cp'), '@INPUT@', '@OUTPUT@'],
+ input: 'static_hello.f90',
+ output: 'static_hello_output.F90'
+)
+
+static_hello_lib = static_library('static_hello', staticlibsource, install: false)
+
+static_hello_dep = declare_dependency(link_with: static_hello_lib)
diff --git a/test cases/fortran/21 install static/subprojects/static_hello/static_hello.f90 b/test cases/fortran/21 install static/subprojects/static_hello/static_hello.f90
new file mode 100644
index 0000000..5407560
--- /dev/null
+++ b/test cases/fortran/21 install static/subprojects/static_hello/static_hello.f90
@@ -0,0 +1,17 @@
+module static_hello
+implicit none
+
+private
+public :: static_say_hello
+
+interface static_say_hello
+ module procedure say_hello
+end interface static_say_hello
+
+contains
+
+subroutine say_hello
+ print *, "Static library called."
+end subroutine say_hello
+
+end module static_hello
diff --git a/test cases/fortran/21 install static/test.json b/test cases/fortran/21 install static/test.json
new file mode 100644
index 0000000..b31e91d
--- /dev/null
+++ b/test cases/fortran/21 install static/test.json
@@ -0,0 +1,5 @@
+{
+ "installed": [
+ {"file": "usr/lib/libmainstatic.a", "type": "file"}
+ ]
+}
\ No newline at end of file
diff --git a/test cases/rust/11 generated main/gen.py b/test cases/rust/11 generated main/gen.py
index ebbc2a7..c8cfe76 100644
--- a/test cases/rust/11 generated main/gen.py
+++ b/test cases/rust/11 generated main/gen.py
@@ -6,10 +6,14 @@
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument('out')
+ parser.add_argument('--mode', choices=['main', 'lib'], default='main')
args = parser.parse_args()
with open(args.out, 'w') as f:
- f.write('fn main() { println!("I prefer tarnish, actually.") }')
+ if args.mode == 'main':
+ f.write('fn main() { println!("I prefer tarnish, actually.") }')
+ elif args.mode == 'lib':
+ f.write('pub fn libfun() { println!("I prefer tarnish, actually.") }')
if __name__ == "__main__":
diff --git a/test cases/rust/11 generated main/generated_lib_main.rs b/test cases/rust/11 generated main/generated_lib_main.rs
new file mode 100644
index 0000000..d9b373e
--- /dev/null
+++ b/test cases/rust/11 generated main/generated_lib_main.rs
@@ -0,0 +1,5 @@
+extern crate static_lib_generated as lib;
+
+fn main() {
+ lib::libfun();
+}
diff --git a/test cases/rust/11 generated main/meson.build b/test cases/rust/11 generated main/meson.build
index 4749816..695124c 100644
--- a/test cases/rust/11 generated main/meson.build
+++ b/test cases/rust/11 generated main/meson.build
@@ -11,6 +11,11 @@
executable('custom_target_main', c)
executable('custom_target_index_main', c[0])
-gen = generator(gen, arguments : ['@OUTPUT@'], output : '@BASENAME@.rs')
+gener = generator(gen, arguments : ['@OUTPUT@'], output : '@BASENAME@.rs')
# Doesn't actually use gen.py as input, just a limitation of generators
-executable('generator_main', gen.process(['gen.py']))
+executable('generator_main', gener.process(['gen.py']))
+
+subdir('sub')
+executable('custom_target_subdir_main', s)
+
+executable('link_with_generated_lib', 'generated_lib_main.rs', link_with : lib)
diff --git a/test cases/rust/11 generated main/sub/meson.build b/test cases/rust/11 generated main/sub/meson.build
new file mode 100644
index 0000000..3ce96e5
--- /dev/null
+++ b/test cases/rust/11 generated main/sub/meson.build
@@ -0,0 +1,13 @@
+s = custom_target(
+ 'subdir_target',
+ command : [gen, '@OUTPUT@'],
+ output : ['main.rs'],
+)
+
+l = custom_target(
+ 'lib_target',
+ command : [gen, '@OUTPUT@', '--mode', 'lib'],
+ output : ['lib.rs'],
+)
+
+lib = static_library('static_lib_generated', l)