aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Reference-manual.md22
-rw-r--r--docs/markdown/snippets/configure_file_output_format.md14
-rwxr-xr-xmeson.py17
-rw-r--r--mesonbuild/environment.py6
-rw-r--r--mesonbuild/interpreter.py14
-rw-r--r--mesonbuild/mesonlib.py84
-rw-r--r--mesonbuild/mesonmain.py34
-rw-r--r--mesonbuild/scripts/regen_checker.py6
-rwxr-xr-xrun_meson_command_tests.py186
-rwxr-xr-xrun_project_tests.py21
-rwxr-xr-xrun_tests.py43
-rwxr-xr-xrun_unittests.py64
-rw-r--r--test cases/nasm/1 configure file/hello.asm27
-rw-r--r--test cases/nasm/1 configure file/meson.build44
14 files changed, 434 insertions, 148 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 3f1bb50..0c54de0 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -208,6 +208,10 @@ the `@variable@` syntax.
`@PLAINNAME@` or `@BASENAME@` substitutions). In configuration mode,
the permissions of the input file (if it is specified) are copied to
the output file.
+- `output_format` *(added 0.47.0)* the format of the output to generate when no input
+ was specified. It defaults to `c`, in which case preprocessor directives
+ will be prefixed with `#`, you can also use `nasm`, in which case the
+ prefix will be `%`.
### custom_target()
@@ -1423,7 +1427,7 @@ the following methods.
the string to an array if needed.
- `override_find_program(progname, program)` [*(Added
- 0.46.0)*](Release-notes-for-0-46-0.html#Can-override-find_program)
+ 0.46.0)*](Release-notes-for-0-46-0.html#can-override-find_program)
specifies that whenever `find_program` is used to find a program
named `progname`, Meson should not not look it up on the system but
instead return `program`, which may either be the result of
@@ -1444,17 +1448,15 @@ doing the actual compilation. See
[Cross-compilation](Cross-compilation.md). It has the following
methods:
-- `cpu_family()` returns the CPU family name. Guaranteed to return
- `x86` for 32-bit userland on x86 CPUs, `x86_64` for 64-bit userland
- on x86 CPUs, `arm` for 32-bit userland on all ARM CPUs, etc.
+- `cpu_family()` returns the CPU family name. [This table](Reference-tables.md#cpu-families)
+ contains all known CPU families. These are guaranteed to continue working.
- `cpu()` returns a more specific CPU name, such as `i686`, `amd64`,
etc.
-- `system()` returns the operating system name, such as `windows` (all
- versions of Windows), `linux` (all Linux distros), `darwin` (all
- versions of OS X/macOS), `cygwin` (for Cygwin), and `bsd` (all *BSD
- OSes).
+- `system()` returns the operating system name.
+ [This table](Reference-tables.html#operating-system-names) Lists all of the
+ currently known Operating System names, these are guaranteed to continue working.
- `endian()` returns `big` on big-endian systems and `little` on
little-endian systems.
@@ -1464,7 +1466,7 @@ Currently, these values are populated using
and
[`platform.machine()`](https://docs.python.org/3.4/library/platform.html#platform.machine). If
you think the returned values for any of these are incorrect for your
-system or CPU, or if your OS is not in the above list, please file [a
+system or CPU, or if your OS is not in the linked table, please file [a
bug report](https://github.com/mesonbuild/meson/issues/new) with
details and we'll look into it.
@@ -1544,7 +1546,7 @@ the following methods:
value as a string or empty string if it is not defined.
- `get_id()` returns a string identifying the compiler. For example,
- `gcc`, `msvc`, [and more](Compiler-properties.md#compiler-id).
+ `gcc`, `msvc`, [and more](Reference-tables.html#compiler-ids).
- `get_supported_arguments(list_of_string)` *(added 0.43.0)* returns
an array containing only the arguments supported by the compiler,
diff --git a/docs/markdown/snippets/configure_file_output_format.md b/docs/markdown/snippets/configure_file_output_format.md
new file mode 100644
index 0000000..e522885
--- /dev/null
+++ b/docs/markdown/snippets/configure_file_output_format.md
@@ -0,0 +1,14 @@
+## New keyword argument `output_format` for configure_file()
+
+When called without an input file, `configure_file` generates a
+C header file by default. A keyword argument was added to allow
+specifying the output format, for example for use with nasm or yasm:
+
+```
+conf = configuration_data()
+conf.set('FOO', 1)
+
+configure_file('config.asm',
+ configuration: conf,
+ output_format: 'nasm')
+```
diff --git a/meson.py b/meson.py
index abbac6f..dc84b51 100755
--- a/meson.py
+++ b/meson.py
@@ -14,13 +14,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from mesonbuild import mesonmain
-import sys, os
+import sys
+from pathlib import Path
+
+# If we're run uninstalled, add the script directory to sys.path to ensure that
+# we always import the correct mesonbuild modules even if PYTHONPATH is mangled
+meson_exe = Path(sys.argv[0]).resolve()
+if (meson_exe.parent / 'mesonbuild').is_dir():
+ sys.path.insert(0, meson_exe.parent)
-def main():
- # Always resolve the command path so Ninja can find it for regen, tests, etc.
- launcher = os.path.realpath(sys.argv[0])
- return mesonmain.run(sys.argv[1:], launcher)
+from mesonbuild import mesonmain
if __name__ == '__main__':
- sys.exit(main())
+ sys.exit(mesonmain.main())
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index fc837d6..4bcffbd 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -262,10 +262,9 @@ class Environment:
private_dir = 'meson-private'
log_dir = 'meson-logs'
- def __init__(self, source_dir, build_dir, main_script_launcher, options, original_cmd_line_args):
+ def __init__(self, source_dir, build_dir, options, original_cmd_line_args):
self.source_dir = source_dir
self.build_dir = build_dir
- self.meson_script_launcher = main_script_launcher
self.scratch_dir = os.path.join(build_dir, Environment.private_dir)
self.log_dir = os.path.join(build_dir, Environment.log_dir)
os.makedirs(self.scratch_dir, exist_ok=True)
@@ -278,7 +277,8 @@ class Environment:
# re-initialized with project options by the interpreter during
# build file parsing.
self.coredata = coredata.CoreData(options)
- self.coredata.meson_script_launcher = self.meson_script_launcher
+ # Used by the regenchecker script, which runs meson
+ self.coredata.meson_command = mesonlib.meson_command
self.first_invocation = True
if self.coredata.cross_file:
self.cross_info = CrossBuildInfo(self.coredata.cross_file)
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index 79b66c1..93fd237 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -1716,7 +1716,7 @@ permitted_kwargs = {'add_global_arguments': {'language'},
'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'},
'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'},
'build_target': known_build_target_kwargs,
- 'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'capture', 'install', 'format'},
+ 'configure_file': {'input', 'output', 'configuration', 'command', 'copy', 'install_dir', 'capture', 'install', 'format', 'output_format'},
'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'},
'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version', 'private_headers'},
'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'},
@@ -3239,6 +3239,16 @@ root and issuing %s.
if fmt not in ('meson', 'cmake', 'cmake@'):
raise InterpreterException('"format" possible values are "meson", "cmake" or "cmake@".')
+ if 'output_format' in kwargs:
+ output_format = kwargs['output_format']
+ if not isinstance(output_format, str):
+ raise InterpreterException('"output_format" keyword must be a string.')
+ else:
+ output_format = 'c'
+
+ if output_format not in ('c', 'nasm'):
+ raise InterpreterException('"format" possible values are "c" or "nasm".')
+
# Validate input
inputfile = None
ifile_abs = None
@@ -3301,7 +3311,7 @@ root and issuing %s.
"present in the given configuration data." % (
var_list, inputfile), location=node)
else:
- mesonlib.dump_conf_header(ofile_abs, conf.held_object)
+ mesonlib.dump_conf_header(ofile_abs, conf.held_object, output_format)
conf.mark_used()
elif 'command' in kwargs:
# We use absolute paths for input and output here because the cwd
diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py
index fde7ed5..eaa5359 100644
--- a/mesonbuild/mesonlib.py
+++ b/mesonbuild/mesonlib.py
@@ -40,58 +40,12 @@ except Exception:
from glob import glob
-def detect_meson_py_location():
- c = sys.argv[0]
- c_dir, c_fname = os.path.split(c)
-
- # get the absolute path to the <mesontool> folder
- m_dir = None
- if os.path.isabs(c):
- # $ /foo/<mesontool>.py <args>
- m_dir = c_dir
- elif c_dir == '':
- # $ <mesontool> <args> (gets run from /usr/bin/<mesontool>)
- in_path_exe = shutil.which(c_fname)
- if in_path_exe:
- if not os.path.isabs(in_path_exe):
- m_dir = os.getcwd()
- c_fname = in_path_exe
- else:
- m_dir, c_fname = os.path.split(in_path_exe)
- else:
- m_dir = os.path.abspath(c_dir)
-
- # find meson in m_dir
- if m_dir is not None:
- for fname in ['meson', 'meson.py']:
- m_path = os.path.join(m_dir, fname)
- if os.path.exists(m_path):
- return m_path
-
- # No meson found, which means that either:
- # a) meson is not installed
- # b) meson is installed to a non-standard location
- # c) the script that invoked mesonlib is not the one of meson tools (e.g. run_unittests.py)
- fname = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'meson.py'))
- if os.path.exists(fname):
- return fname
- # If meson is still not found, we might be imported by out-of-source tests
- # https://github.com/mesonbuild/meson/issues/3015
- exe = shutil.which('meson')
- if exe is None:
- exe = shutil.which('meson.py')
- if exe is not None:
- return exe
- # Give up.
- raise RuntimeError('Could not determine how to run Meson. Please file a bug with details.')
-
if os.path.basename(sys.executable) == 'meson.exe':
# In Windows and using the MSI installed executable.
- meson_command = [sys.executable]
python_command = [sys.executable, 'runpython']
else:
python_command = [sys.executable]
- meson_command = python_command + [detect_meson_py_location()]
+meson_command = None
def is_ascii_string(astring):
try:
@@ -704,28 +658,46 @@ def do_conf_file(src, dst, confdata, format):
replace_if_different(dst, dst_tmp)
return missing_variables
-def dump_conf_header(ofilename, cdata):
- ofilename_tmp = ofilename + '~'
- with open(ofilename_tmp, 'w', encoding='utf-8') as ofile:
- ofile.write('''/*
+CONF_C_PRELUDE = '''/*
* Autogenerated by the Meson build system.
* Do not edit, your changes will be lost.
*/
#pragma once
-''')
+'''
+
+CONF_NASM_PRELUDE = '''; Autogenerated by the Meson build system.
+; Do not edit, your changes will be lost.
+
+'''
+
+def dump_conf_header(ofilename, cdata, output_format):
+ if output_format == 'c':
+ prelude = CONF_C_PRELUDE
+ prefix = '#'
+ elif output_format == 'nasm':
+ prelude = CONF_NASM_PRELUDE
+ prefix = '%'
+
+ ofilename_tmp = ofilename + '~'
+ with open(ofilename_tmp, 'w', encoding='utf-8') as ofile:
+ ofile.write(prelude)
for k in sorted(cdata.keys()):
(v, desc) = cdata.get(k)
if desc:
- ofile.write('/* %s */\n' % desc)
+ if output_format == 'c':
+ ofile.write('/* %s */\n' % desc)
+ elif output_format == 'nasm':
+ for line in desc.split('\n'):
+ ofile.write('; %s\n' % line)
if isinstance(v, bool):
if v:
- ofile.write('#define %s\n\n' % k)
+ ofile.write('%sdefine %s\n\n' % (prefix, k))
else:
- ofile.write('#undef %s\n\n' % k)
+ ofile.write('%sundef %s\n\n' % (prefix, k))
elif isinstance(v, (int, str)):
- ofile.write('#define %s %s\n\n' % (k, v))
+ ofile.write('%sdefine %s %s\n\n' % (prefix, k, v))
else:
raise MesonException('Unknown data type in configuration file entry: ' + k)
replace_if_different(ofilename, ofilename_tmp)
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 4a977a1..c03ef9b 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -73,9 +73,8 @@ def filter_builtin_options(args, original_args):
class MesonApp:
- def __init__(self, dir1, dir2, script_launcher, handshake, options, original_cmd_line_args):
+ def __init__(self, dir1, dir2, handshake, options, original_cmd_line_args):
(self.source_dir, self.build_dir) = self.validate_dirs(dir1, dir2, handshake)
- self.meson_script_launcher = script_launcher
self.options = options
self.original_cmd_line_args = original_cmd_line_args
@@ -129,7 +128,7 @@ class MesonApp:
env.coredata.pkgconf_envvar = curvar
def generate(self):
- env = environment.Environment(self.source_dir, self.build_dir, self.meson_script_launcher, self.options, self.original_cmd_line_args)
+ env = environment.Environment(self.source_dir, self.build_dir, self.options, self.original_cmd_line_args)
mlog.initialize(env.get_log_dir())
with mesonlib.BuildDirLock(self.build_dir):
self._generate(env)
@@ -269,12 +268,27 @@ def run_script_command(args):
raise MesonException('Unknown internal command {}.'.format(cmdname))
return cmdfunc(cmdargs)
-def run(original_args, mainfile=None):
+def set_meson_command(mainfile):
+ if mainfile.endswith('.exe'):
+ mesonlib.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
+ mesonlib.meson_command = mesonlib.python_command + ['-m', 'mesonbuild.mesonmain']
+ else:
+ mesonlib.meson_command = mesonlib.python_command + [mainfile]
+ # This won't go into the log file because it's not initialized yet, and we
+ # need this value for unit tests.
+ if 'MESON_COMMAND_TESTS' in os.environ:
+ mlog.log('meson_command is {!r}'.format(mesonlib.meson_command))
+
+def run(original_args, mainfile):
if sys.version_info < (3, 5):
print('Meson works correctly only with python 3.5+.')
print('You have python %s.' % sys.version)
print('Please update your environment')
return 1
+ # Set the meson command that will be used to run scripts and so on
+ set_meson_command(mainfile)
args = original_args[:]
if len(args) > 0:
# First check if we want to run a subcommand.
@@ -352,9 +366,7 @@ def run(original_args, mainfile=None):
else:
dir2 = '.'
try:
- if mainfile is None:
- raise AssertionError('I iz broken. Sorry.')
- app = MesonApp(dir1, dir2, mainfile, handshake, options, original_args)
+ app = MesonApp(dir1, dir2, handshake, options, original_args)
except Exception as e:
# Log directory does not exist, so just print
# to stdout.
@@ -382,3 +394,11 @@ def run(original_args, mainfile=None):
mlog.shutdown()
return 0
+
+def main():
+ # Always resolve the command path so Ninja can find it for regen, tests, etc.
+ launcher = os.path.realpath(sys.argv[0])
+ return run(sys.argv[1:], launcher)
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py
index a9b00c7..80d9242 100644
--- a/mesonbuild/scripts/regen_checker.py
+++ b/mesonbuild/scripts/regen_checker.py
@@ -14,7 +14,6 @@
import sys, os
import pickle, subprocess
-from mesonbuild.mesonlib import meson_command
# This could also be used for XCode.
@@ -32,7 +31,7 @@ def need_regen(regeninfo, regen_timestamp):
Vs2010Backend.touch_regen_timestamp(regeninfo.build_dir)
return False
-def regen(regeninfo, mesonscript, backend):
+def regen(regeninfo, meson_command, backend):
cmd = meson_command + ['--internal',
'regenerate',
regeninfo.build_dir,
@@ -48,11 +47,10 @@ def run(args):
regeninfo = pickle.load(f)
with open(coredata, 'rb') as f:
coredata = pickle.load(f)
- mesonscript = coredata.meson_script_launcher
backend = coredata.get_builtin_option('backend')
regen_timestamp = os.stat(dumpfile).st_mtime
if need_regen(regeninfo, regen_timestamp):
- regen(regeninfo, mesonscript, backend)
+ regen(regeninfo, coredata.meson_command, backend)
sys.exit(0)
if __name__ == '__main__':
diff --git a/run_meson_command_tests.py b/run_meson_command_tests.py
new file mode 100755
index 0000000..6efd911
--- /dev/null
+++ b/run_meson_command_tests.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+
+# Copyright 2018 The Meson development team
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import tempfile
+import unittest
+import subprocess
+from pathlib import Path
+
+from mesonbuild.mesonlib import windows_proof_rmtree, python_command, is_windows
+
+# Find the meson.py adjacent to us
+meson_py = Path(__file__).resolve().parent / 'meson.py'
+if not meson_py.is_file():
+ raise RuntimeError("meson.py not found: test must only run from git")
+
+def get_pypath():
+ import sysconfig
+ pypath = sysconfig.get_path('purelib', vars={'base': ''})
+ # Ensure that / is the path separator and not \, then strip /
+ return Path(pypath).as_posix().strip('/')
+
+def get_pybindir():
+ import sysconfig
+ # 'Scripts' on Windows and 'bin' on other platforms including MSYS
+ return sysconfig.get_path('scripts', vars={'base': ''}).strip('\\/')
+
+class CommandTests(unittest.TestCase):
+ '''
+ Test that running meson in various ways works as expected by checking the
+ value of mesonlib.meson_command that was set during configuration.
+ '''
+
+ def setUp(self):
+ super().setUp()
+ self.orig_env = os.environ.copy()
+ self.orig_dir = os.getcwd()
+ os.environ['MESON_COMMAND_TESTS'] = '1'
+ self.tmpdir = Path(tempfile.mkdtemp()).resolve()
+ self.src_root = Path(__file__).resolve().parent
+ self.testdir = str(self.src_root / 'test cases/common/1 trivial')
+ self.meson_args = ['--backend=ninja']
+
+ def tearDown(self):
+ try:
+ windows_proof_rmtree(str(self.tmpdir))
+ except FileNotFoundError:
+ pass
+ os.environ.clear()
+ os.environ.update(self.orig_env)
+ os.chdir(str(self.orig_dir))
+ super().tearDown()
+
+ def _run(self, command, workdir=None):
+ '''
+ Run a command while printing the stdout and stderr to stdout,
+ and also return a copy of it
+ '''
+ # If this call hangs CI will just abort. It is very hard to distinguish
+ # between CI issue and test bug in that case. Set timeout and fail loud
+ # instead.
+ p = subprocess.run(command, stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, env=os.environ.copy(),
+ universal_newlines=True, cwd=workdir, timeout=60 * 5)
+ print(p.stdout)
+ if p.returncode != 0:
+ raise subprocess.CalledProcessError(p.returncode, command)
+ return p.stdout
+
+ def assertMesonCommandIs(self, line, cmd):
+ self.assertTrue(line.startswith('meson_command '), msg=line)
+ self.assertEqual(line, 'meson_command is {!r}'.format(cmd))
+
+ def test_meson_uninstalled(self):
+ # This is what the meson command must be for all these cases
+ resolved_meson_command = python_command + [str(self.src_root / 'meson.py')]
+ # Absolute path to meson.py
+ os.chdir('/')
+ builddir = str(self.tmpdir / 'build1')
+ meson_py = str(self.src_root / 'meson.py')
+ meson_setup = [meson_py, 'setup']
+ meson_command = python_command + meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+ # ./meson.py
+ os.chdir(str(self.src_root))
+ builddir = str(self.tmpdir / 'build2')
+ meson_py = './meson.py'
+ meson_setup = [meson_py, 'setup']
+ meson_command = python_command + meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+ # Symlink to meson.py
+ if is_windows():
+ # Symlinks require admin perms
+ return
+ os.chdir(str(self.src_root))
+ builddir = str(self.tmpdir / 'build3')
+ # Create a symlink to meson.py in bindir, and add it to PATH
+ bindir = (self.tmpdir / 'bin')
+ bindir.mkdir()
+ (bindir / 'meson').symlink_to(self.src_root / 'meson.py')
+ os.environ['PATH'] = str(bindir) + os.pathsep + os.environ['PATH']
+ # See if it works!
+ meson_py = 'meson'
+ meson_setup = [meson_py, 'setup']
+ meson_command = meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+
+ def test_meson_installed(self):
+ # Install meson
+ prefix = self.tmpdir / 'prefix'
+ pylibdir = prefix / get_pypath()
+ bindir = prefix / get_pybindir()
+ pylibdir.mkdir(parents=True)
+ os.environ['PYTHONPATH'] = str(pylibdir)
+ os.environ['PATH'] = str(bindir) + os.pathsep + os.environ['PATH']
+ self._run(python_command + ['setup.py', 'install', '--prefix', str(prefix)])
+ self.assertTrue(pylibdir.is_dir())
+ self.assertTrue(bindir.is_dir())
+ # Run `meson`
+ os.chdir('/')
+ if is_windows():
+ resolved_meson_command = python_command + [str(bindir / 'meson.py')]
+ else:
+ resolved_meson_command = python_command + [str(bindir / 'meson')]
+ # The python configuration on appveyor does not register .py as
+ # a valid extension, so we cannot run `meson` on Windows.
+ builddir = str(self.tmpdir / 'build1')
+ meson_setup = ['meson', 'setup']
+ meson_command = meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+ # Run `/path/to/meson`
+ builddir = str(self.tmpdir / 'build2')
+ if is_windows():
+ # Cannot run .py directly because of the appveyor configuration,
+ # and the script is named meson.py, not meson
+ meson_setup = python_command + [str(bindir / 'meson.py'), 'setup']
+ else:
+ meson_setup = [str(bindir / 'meson'), 'setup']
+ meson_command = meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+ # Run `python3 -m mesonbuild.mesonmain`
+ resolved_meson_command = python_command + ['-m', 'mesonbuild.mesonmain']
+ builddir = str(self.tmpdir / 'build3')
+ meson_setup = ['-m', 'mesonbuild.mesonmain', 'setup']
+ meson_command = python_command + meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+ if is_windows():
+ # Next part requires a shell
+ return
+ # `meson` is a wrapper to `meson.real`
+ resolved_meson_command = python_command + [str(bindir / 'meson.real')]
+ builddir = str(self.tmpdir / 'build4')
+ (bindir / 'meson').rename(bindir / 'meson.real')
+ wrapper = (bindir / 'meson')
+ with open(wrapper, 'w') as f:
+ f.write('#!/bin/sh\n\nmeson.real "$@"')
+ wrapper.chmod(0o755)
+ meson_setup = [str(wrapper), 'setup']
+ meson_command = meson_setup + self.meson_args
+ stdo = self._run(meson_command + [self.testdir, builddir])
+ self.assertMesonCommandIs(stdo.split('\n')[0], resolved_meson_command)
+
+ def test_meson_exe_windows(self):
+ raise unittest.SkipTest('NOT IMPLEMENTED')
+
+if __name__ == '__main__':
+ unittest.main(buffer=True)
diff --git a/run_project_tests.py b/run_project_tests.py
index 3801432..0b77a37 100755
--- a/run_project_tests.py
+++ b/run_project_tests.py
@@ -38,8 +38,7 @@ import time
import multiprocessing
from concurrent.futures import ProcessPoolExecutor
import re
-from run_unittests import get_fake_options, run_configure
-
+from run_tests import get_fake_options, run_configure, get_meson_script
from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
from run_tests import ensure_backend_detects_changes
@@ -88,12 +87,6 @@ no_meson_log_msg = 'No meson-log.txt found.'
system_compiler = None
-meson_command = os.path.join(os.getcwd(), 'meson')
-if not os.path.exists(meson_command):
- meson_command += '.py'
- if not os.path.exists(meson_command):
- raise RuntimeError('Could not find main Meson script to run.')
-
class StopException(Exception):
def __init__(self):
super().__init__('Stopped by user')
@@ -324,7 +317,7 @@ def _run_test(testdir, test_build_dir, install_dir, extra_args, compiler, backen
if pass_libdir_to_test(testdir):
gen_args += ['--libdir', 'lib']
gen_args += [testdir, test_build_dir] + flags + test_args + extra_args
- (returncode, stdo, stde) = run_configure(meson_command, gen_args)
+ (returncode, stdo, stde) = run_configure(gen_args)
try:
logfile = Path(test_build_dir, 'meson-logs', 'meson-log.txt')
mesonlog = logfile.open(errors='ignore', encoding='utf-8').read()
@@ -417,7 +410,7 @@ def have_d_compiler():
def have_objc_compiler():
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
- env = environment.Environment(None, build_dir, None, get_fake_options('/'), [])
+ env = environment.Environment(None, build_dir, get_fake_options('/'), [])
try:
objc_comp = env.detect_objc_compiler(False)
except mesonlib.MesonException:
@@ -432,7 +425,7 @@ def have_objc_compiler():
def have_objcpp_compiler():
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
- env = environment.Environment(None, build_dir, None, get_fake_options('/'), [])
+ env = environment.Environment(None, build_dir, get_fake_options('/'), [])
try:
objcpp_comp = env.detect_objcpp_compiler(False)
except mesonlib.MesonException:
@@ -523,6 +516,7 @@ def detect_tests_to_run():
('python3', 'python3', backend is not Backend.ninja),
('fpga', 'fpga', shutil.which('yosys') is None),
('frameworks', 'frameworks', False),
+ ('nasm', 'nasm', False),
]
gathered_tests = [(name, gather_tests(Path('test cases', subdir)), skip) for name, subdir, skip in all_tests]
return gathered_tests
@@ -647,11 +641,12 @@ def check_format():
check_file(fullname)
def check_meson_commands_work():
- global backend, meson_command, compile_commands, test_commands, install_commands
+ global backend, compile_commands, test_commands, install_commands
testdir = PurePath('test cases', 'common', '1 trivial').as_posix()
+ meson_commands = mesonlib.python_command + [get_meson_script()]
with AutoDeletedDir(tempfile.mkdtemp(prefix='b ', dir='.')) as build_dir:
print('Checking that configuring works...')
- gen_cmd = mesonlib.meson_command + [testdir, build_dir] + backend_flags
+ gen_cmd = meson_commands + [testdir, build_dir] + backend_flags
pc, o, e = Popen_safe(gen_cmd)
if pc.returncode != 0:
raise RuntimeError('Failed to configure {!r}:\n{}\n{}'.format(testdir, e, o))
diff --git a/run_tests.py b/run_tests.py
index 648e6ce..736cdc0 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -21,13 +21,16 @@ import shutil
import subprocess
import tempfile
import platform
+from io import StringIO
+from enum import Enum
+from glob import glob
+from pathlib import Path
+
+import mesonbuild
from mesonbuild import mesonlib
from mesonbuild import mesonmain
from mesonbuild import mlog
from mesonbuild.environment import detect_ninja
-from io import StringIO
-from enum import Enum
-from glob import glob
Backend = Enum('Backend', 'ninja vs xcode')
@@ -42,6 +45,28 @@ if mesonlib.is_windows() or mesonlib.is_cygwin():
else:
exe_suffix = ''
+def get_meson_script():
+ '''
+ Guess the meson that corresponds to the `mesonbuild` that has been imported
+ so we can run configure and other commands in-process, since mesonmain.run
+ needs to know the meson_command to use.
+
+ Also used by run_unittests.py to determine what meson to run when not
+ running in-process (which is the default).
+ '''
+ # Is there a meson.py next to the mesonbuild currently in use?
+ mesonbuild_dir = Path(mesonbuild.__file__).resolve().parent.parent
+ meson_script = mesonbuild_dir / 'meson.py'
+ if meson_script.is_file():
+ return str(meson_script)
+ # Then if mesonbuild is in PYTHONPATH, meson must be in PATH
+ mlog.warning('Could not find meson.py next to the mesonbuild module. '
+ 'Trying system meson...')
+ meson_cmd = shutil.which('meson')
+ if meson_cmd:
+ return meson_cmd
+ raise RuntimeError('Could not find {!r} or a meson in PATH'.format(meson_script))
+
def get_backend_args_for_dir(backend, builddir):
'''
Visual Studio backend needs to be given the solution to build
@@ -133,13 +158,13 @@ def get_fake_options(prefix):
def should_run_linux_cross_tests():
return shutil.which('arm-linux-gnueabihf-gcc') and not platform.machine().lower().startswith('arm')
-def run_configure_inprocess(meson_command, commandlist):
+def run_configure_inprocess(commandlist):
old_stdout = sys.stdout
sys.stdout = mystdout = StringIO()
old_stderr = sys.stderr
sys.stderr = mystderr = StringIO()
try:
- returncode = mesonmain.run(commandlist, meson_command)
+ returncode = mesonmain.run(commandlist, get_meson_script())
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr
@@ -149,11 +174,11 @@ def run_configure_external(full_command):
pc, o, e = mesonlib.Popen_safe(full_command)
return pc.returncode, o, e
-def run_configure(meson_command, commandlist):
+def run_configure(commandlist):
global meson_exe
if meson_exe:
return run_configure_external(meson_exe + commandlist)
- return run_configure_inprocess(meson_command, commandlist)
+ return run_configure_inprocess(commandlist)
def print_system_info():
print(mlog.bold('System information.').get_text(mlog.colorize_console))
@@ -214,6 +239,9 @@ if __name__ == '__main__':
'coverage.process_startup()\n')
env['COVERAGE_PROCESS_START'] = '.coveragerc'
env['PYTHONPATH'] = os.pathsep.join([td] + env.get('PYTHONPATH', []))
+ # Meson command tests
+ returncode += subprocess.call(mesonlib.python_command + ['run_meson_command_tests.py', '-v'], env=env)
+ # Unit tests
returncode += subprocess.call(mesonlib.python_command + ['run_unittests.py', '-v'], env=env)
# Ubuntu packages do not have a binary without -6 suffix.
if should_run_linux_cross_tests():
@@ -221,5 +249,6 @@ if __name__ == '__main__':
print()
returncode += subprocess.call(mesonlib.python_command + ['run_cross_test.py', 'cross/ubuntu-armhf.txt'],
env=env)
+ # Project tests
returncode += subprocess.call(mesonlib.python_command + ['run_project_tests.py'] + sys.argv[1:], env=env)
sys.exit(returncode)
diff --git a/run_unittests.py b/run_unittests.py
index 552769b..d6b2619 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -36,7 +36,7 @@ import mesonbuild.modules.gnome
from mesonbuild.interpreter import ObjectHolder
from mesonbuild.mesonlib import (
is_windows, is_osx, is_cygwin, is_dragonflybsd,
- windows_proof_rmtree, python_command, meson_command, version_compare,
+ windows_proof_rmtree, python_command, version_compare,
BuildDirLock
)
from mesonbuild.environment import Environment, detect_ninja
@@ -44,9 +44,9 @@ from mesonbuild.mesonlib import MesonException, EnvironmentException
from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram
import mesonbuild.modules.pkgconfig
-from run_tests import exe_suffix, get_fake_options
+from run_tests import exe_suffix, get_fake_options, get_meson_script
from run_tests import get_builddir_target_args, get_backend_commands, Backend
-from run_tests import ensure_backend_detects_changes, run_configure, meson_exe
+from run_tests import ensure_backend_detects_changes, run_configure_inprocess
from run_tests import should_run_linux_cross_tests
@@ -488,13 +488,13 @@ class BasePlatformTests(unittest.TestCase):
# Get the backend
# FIXME: Extract this from argv?
self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja'))
- self.meson_mainfile = os.path.join(src_root, 'meson.py')
self.meson_args = ['--backend=' + self.backend.name]
self.meson_cross_file = None
- self.meson_command = meson_command + self.meson_args
- self.mconf_command = meson_command + ['configure']
- self.mintro_command = meson_command + ['introspect']
- self.wrap_command = meson_command + ['wrap']
+ self.meson_command = python_command + [get_meson_script()]
+ self.setup_command = self.meson_command + self.meson_args
+ self.mconf_command = self.meson_command + ['configure']
+ self.mintro_command = self.meson_command + ['introspect']
+ self.wrap_command = self.meson_command + ['wrap']
# Backend-specific build commands
self.build_command, self.clean_command, self.test_command, self.install_command, \
self.uninstall_command = get_backend_commands(self.backend)
@@ -521,7 +521,7 @@ class BasePlatformTests(unittest.TestCase):
self.logdir = os.path.join(self.builddir, 'meson-logs')
self.installdir = os.path.join(self.builddir, 'install')
self.distdir = os.path.join(self.builddir, 'meson-dist')
- self.mtest_command = meson_command + ['test', '-C', self.builddir]
+ self.mtest_command = self.meson_command + ['test', '-C', self.builddir]
self.builddirs.append(self.builddir)
def new_builddir(self):
@@ -581,7 +581,7 @@ class BasePlatformTests(unittest.TestCase):
self.privatedir = os.path.join(self.builddir, 'meson-private')
if inprocess:
try:
- (returncode, out, err) = run_configure(self.meson_mainfile, self.meson_args + args + extra_args)
+ (returncode, out, err) = run_configure_inprocess(self.meson_args + args + extra_args)
if 'MESON_SKIP_TEST' in out:
raise unittest.SkipTest('Project requested skipping.')
if returncode != 0:
@@ -601,7 +601,7 @@ class BasePlatformTests(unittest.TestCase):
mesonbuild.mlog.log_file = None
else:
try:
- out = self._run(self.meson_command + args + extra_args)
+ out = self._run(self.setup_command + args + extra_args)
except unittest.SkipTest:
raise unittest.SkipTest('Project requested skipping: ' + srcdir)
except:
@@ -902,8 +902,7 @@ class AllPlatformTests(BasePlatformTests):
https://github.com/mesonbuild/meson/issues/1355
'''
testdir = os.path.join(self.common_test_dir, '3 static')
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
static_linker = env.detect_static_linker(cc)
if is_windows():
@@ -1184,8 +1183,7 @@ class AllPlatformTests(BasePlatformTests):
if not is_windows():
langs += [('objc', 'OBJC'), ('objcpp', 'OBJCXX')]
testdir = os.path.join(self.unit_test_dir, '5 compiler detection')
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
for lang, evar in langs:
# Detect with evar and do sanity checks on that
if evar in os.environ:
@@ -1287,8 +1285,7 @@ class AllPlatformTests(BasePlatformTests):
def test_always_prefer_c_compiler_for_asm(self):
testdir = os.path.join(self.common_test_dir, '141 c cpp and asm')
# Skip if building with MSVC
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
if env.detect_c_compiler(False).get_id() == 'msvc':
raise unittest.SkipTest('MSVC can\'t compile assembly')
self.init(testdir)
@@ -1546,8 +1543,7 @@ int main(int argc, char **argv) {
self.assertPathExists(os.path.join(testdir, i))
def detect_prebuild_env(self):
- env = Environment('', self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment('', self.builddir, get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
stlinker = env.detect_static_linker(cc)
if mesonbuild.mesonlib.is_windows():
@@ -1715,8 +1711,7 @@ int main(int argc, char **argv) {
'--libdir=' + libdir])
# Find foo dependency
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True}
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
# Ensure link_args are properly quoted
@@ -1849,16 +1844,16 @@ int main(int argc, char **argv) {
for lang in ('c', 'cpp'):
for type in ('executable', 'library'):
with tempfile.TemporaryDirectory() as tmpdir:
- self._run(meson_command + ['init', '--language', lang, '--type', type],
+ self._run(self.meson_command + ['init', '--language', lang, '--type', type],
workdir=tmpdir)
- self._run(self.meson_command + ['--backend=ninja', 'builddir'],
+ self._run(self.setup_command + ['--backend=ninja', 'builddir'],
workdir=tmpdir)
self._run(ninja,
workdir=os.path.join(tmpdir, 'builddir'))
with tempfile.TemporaryDirectory() as tmpdir:
with open(os.path.join(tmpdir, 'foo.' + lang), 'w') as f:
f.write('int main() {}')
- self._run(meson_command + ['init', '-b'], workdir=tmpdir)
+ self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
# The test uses mocking and thus requires that
# the current process is the one to run the Meson steps.
@@ -1983,8 +1978,7 @@ recommended as it can lead to undefined behaviour on some platforms''')
testdirbase = os.path.join(self.unit_test_dir, '26 guessed linker dependencies')
testdirlib = os.path.join(testdirbase, 'lib')
extra_args = None
- env = Environment(testdirlib, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdirlib, self.builddir, get_fake_options(self.prefix), [])
if env.detect_c_compiler(False).get_id() != 'msvc':
# static libraries are not linkable with -l with msvc because meson installs them
# as .a files which unix_args_to_native will not know as it expects libraries to use
@@ -2146,9 +2140,6 @@ class FailureTests(BasePlatformTests):
Assert that running meson configure on the specified @contents raises
a error message matching regex @match.
'''
- if meson_exe is not None:
- # Because the exception happens in a different process.
- raise unittest.SkipTest('Can not test assert raise tests with an external Meson command.')
if langs is None:
langs = []
with open(self.mbuild, 'w') as f:
@@ -2283,8 +2274,7 @@ class FailureTests(BasePlatformTests):
'''
Test that when we can't detect objc or objcpp, we fail gracefully.
'''
- env = Environment('', self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment('', self.builddir, get_fake_options(self.prefix), [])
try:
env.detect_objc_compiler(False)
env.detect_objcpp_compiler(False)
@@ -2393,8 +2383,7 @@ class WindowsTests(BasePlatformTests):
ExternalLibraryHolder from build files.
'''
testdir = os.path.join(self.platform_test_dir, '1 basic')
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
if cc.id != 'msvc':
raise unittest.SkipTest('Not using MSVC')
@@ -2464,8 +2453,7 @@ class LinuxlikeTests(BasePlatformTests):
'''
testdir = os.path.join(self.common_test_dir, '51 pkgconfig-gen')
self.init(testdir)
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
kwargs = {'required': True, 'silent': True}
os.environ['PKG_CONFIG_LIBDIR'] = self.privatedir
foo_dep = PkgConfigDependency('libfoo', env, kwargs)
@@ -2715,8 +2703,7 @@ class LinuxlikeTests(BasePlatformTests):
an ordinary test because it requires passing options to meson.
'''
testdir = os.path.join(self.common_test_dir, '1 trivial')
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
cc = env.detect_c_compiler(False)
self._test_stds_impl(testdir, cc, 'c')
@@ -2726,8 +2713,7 @@ class LinuxlikeTests(BasePlatformTests):
be an ordinary test because it requires passing options to meson.
'''
testdir = os.path.join(self.common_test_dir, '2 cpp')
- env = Environment(testdir, self.builddir, self.meson_command,
- get_fake_options(self.prefix), [])
+ env = Environment(testdir, self.builddir, get_fake_options(self.prefix), [])
cpp = env.detect_cpp_compiler(False)
self._test_stds_impl(testdir, cpp, 'cpp')
diff --git a/test cases/nasm/1 configure file/hello.asm b/test cases/nasm/1 configure file/hello.asm
new file mode 100644
index 0000000..4188b8d
--- /dev/null
+++ b/test cases/nasm/1 configure file/hello.asm
@@ -0,0 +1,27 @@
+; hello.asm a first program for nasm for Linux, Intel, gcc
+;
+; assemble: nasm -f elf -l hello.lst hello.asm
+; link: gcc -o hello hello.o
+; run: hello
+; output is: Hello World
+
+%include "config.asm"
+
+ SECTION .data ; data section
+msg: db "Hello World",10 ; the string to print, 10=cr
+len: equ $-msg ; "$" means "here"
+ ; len is a value, not an address
+
+ SECTION .text ; code section
+ global main ; make label available to linker
+main: ; standard gcc entry point
+
+ mov edx,len ; arg3, length of string to print
+ mov ecx,msg ; arg2, pointer to string
+ mov ebx,1 ; arg1, where to write, screen
+ mov eax,4 ; write sysout command to int 80 hex
+ int 0x80 ; interrupt 80 hex, call kernel
+
+ mov ebx,HELLO ; exit code, 0=normal
+ mov eax,1 ; exit command to kernel
+ int 0x80 ; interrupt 80 hex, call kernel
diff --git a/test cases/nasm/1 configure file/meson.build b/test cases/nasm/1 configure file/meson.build
new file mode 100644
index 0000000..213e114
--- /dev/null
+++ b/test cases/nasm/1 configure file/meson.build
@@ -0,0 +1,44 @@
+project('nasm config file', 'c')
+
+if host_machine.cpu_family() == 'x86' and host_machine.system() == 'windows'
+ asm_format = 'win32'
+elif host_machine.cpu_family() == 'x86_64' and host_machine.system() == 'windows'
+ asm_format = 'win64'
+elif host_machine.cpu_family() == 'x86' and host_machine.system() == 'linux'
+ asm_format = 'elf32'
+elif host_machine.cpu_family() == 'x86_64' and host_machine.system() == 'linux'
+ asm_format = 'elf64'
+else
+ error('MESON_SKIP_TEST: skipping test on this platform')
+endif
+
+nasm = find_program('nasm', required: false)
+
+if not nasm.found()
+ error('MESON_SKIP_TEST: nasm not available')
+endif
+
+conf = configuration_data()
+
+conf.set('HELLO', 0)
+
+asm_gen = generator(nasm,
+ output : '@BASENAME@.o',
+ arguments : [
+ '-f', asm_format,
+ '-i', meson.current_source_dir() + '/',
+ '-i', join_paths(meson.current_source_dir(), ''),
+ '-P', join_paths(meson.current_build_dir(), 'config.asm'),
+ '@INPUT@',
+ '-o', '@OUTPUT@'])
+
+
+config_file = configure_file(
+ output: 'config.asm',
+ configuration: conf,
+ output_format: 'nasm',
+)
+
+exe = executable('hello', asm_gen.process('hello.asm'))
+
+test('test-nasm-configure-file', exe)