diff options
-rw-r--r-- | docs/markdown/Python-module.md | 4 | ||||
-rw-r--r-- | docs/markdown/Reference-manual.md | 6 | ||||
-rw-r--r-- | docs/markdown/Running-Meson.md | 257 | ||||
-rw-r--r-- | docs/markdown/snippets/introspect_meson_info.md | 6 | ||||
-rw-r--r-- | mesonbuild/backend/backends.py | 9 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 8 | ||||
-rw-r--r-- | mesonbuild/compilers/c.py | 3 | ||||
-rw-r--r-- | mesonbuild/environment.py | 7 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 2 | ||||
-rw-r--r-- | mesonbuild/mconf.py | 4 | ||||
-rw-r--r-- | mesonbuild/mesonmain.py | 4 | ||||
-rw-r--r-- | mesonbuild/mintro.py | 191 | ||||
-rw-r--r-- | mesonbuild/modules/rpm.py | 104 | ||||
-rw-r--r-- | mesonbuild/msetup.py | 18 | ||||
-rw-r--r-- | mesonbuild/munstable_coredata.py | 126 | ||||
-rwxr-xr-x | run_unittests.py | 14 | ||||
-rw-r--r-- | test cases/common/97 test workdir/meson.build | 2 | ||||
-rwxr-xr-x | test cases/common/97 test workdir/subdir/checker.py | 5 | ||||
-rw-r--r-- | test cases/common/97 test workdir/subdir/meson.build | 4 |
19 files changed, 519 insertions, 255 deletions
diff --git a/docs/markdown/Python-module.md b/docs/markdown/Python-module.md index a50a33d..a963a32 100644 --- a/docs/markdown/Python-module.md +++ b/docs/markdown/Python-module.md @@ -220,7 +220,7 @@ It exposes the same methods as its parent class. [shared_module]: Reference-manual.md#shared_module [external program]: Reference-manual.md#external-program-object [dependency]: Reference-manual.md#dependency -[install_data]: Reference-manual.md#install-data -[configure_file]: Reference-manual.md#configure-file +[install_data]: Reference-manual.md#install_data +[configure_file]: Reference-manual.md#configure_file [dependency object]: Reference-manual.md#dependency-object [buildtarget object]: Reference-manual.md#build-target-object diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 0ddd4a9..3d4379a 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -565,6 +565,8 @@ be passed to [shared and static libraries](#library). - `d_module_versions` list of module version identifiers set when compiling D sources - `d_debug` list of module debug identifiers set when compiling D sources - `pie` *(added 0.49.0)* build a position-independent executable +- `native`, is a boolean controlling whether the target is compiled for the + build or host machines. Defaults to false, building for the host machine. The list of `sources`, `objects`, and `dependencies` is always flattened, which means you can freely nest and add lists while @@ -1107,8 +1109,8 @@ This function prints its argument to stdout prefixed with WARNING:. The first argument to this function must be a string defining the name of this project. It is followed by programming languages that the project uses. Supported values for languages are `c`, `cpp` (for -`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and -`vala`. In versions before `0.40.0` you must have at least one +`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`), +`vala` and `rust`. In versions before `0.40.0` you must have at least one language listed. The project name can be any string you want, it's not used for diff --git a/docs/markdown/Running-Meson.md b/docs/markdown/Running-Meson.md index 426e87d..910513c 100644 --- a/docs/markdown/Running-Meson.md +++ b/docs/markdown/Running-Meson.md @@ -4,49 +4,45 @@ short-description: Building a project with Meson # Running Meson -There are two different ways of invoking Meson. First, you can run it -directly from the source tree with the command -`/path/to/source/meson.py`. Meson may also be installed in which case -the command is simply `meson`. In this manual we only use the latter -format for simplicity. +There are two different ways of invoking Meson. First, you can run it directly +from the source tree with the command `/path/to/source/meson.py`. Meson may +also be installed in which case the command is simply `meson`. In this manual +we only use the latter format for simplicity. -Additionally, the invocation can pass options to meson. -The list of options is documented [here](Builtin-options.md). +Additionally, the invocation can pass options to meson. The list of options is +documented [here](Builtin-options.md). -At the time of writing only a command line version of Meson is -available. This means that Meson must be invoked using the -terminal. If you wish to use the MSVC compiler, you need to run Meson -under "Visual Studio command prompt". +At the time of writing only a command line version of Meson is available. This +means that Meson must be invoked using the terminal. If you wish to use the +MSVC compiler, you need to run Meson under "Visual Studio command prompt". -Configuring the source -== +## Configuring the build directory -Let us assume that we have a source tree that has a Meson build -system. This means that at the topmost directory has a file called -`meson.build`. We run the following commands to get the build started. +Let us assume that we have a source tree that has a Meson build system. This +means that at the topmost directory has a file called `meson.build`. We run the +following commands to get the build started. +```sh +cd /path/to/source/root +meson setup builddir +``` - cd /path/to/source/root - mkdir builddir - cd builddir - meson .. +We invoke Meson with the `setup` command, giving it the location of the build +directory. Meson uses [out of source +builds](http://voices.canonical.com/jussi.pakkanen/2013/04/16/why-you-should-consider-using-separate-build-directories/). -First we create a directory to hold all files generated during the -build. Then we go into it and invoke Meson, giving it the location of -the source root. +Hint: The syntax of meson is `meson [command] [arguments] [options]`. The +`setup` command takes a `builddir` and a `srcdir` argument. If no `srcdir` is +given Meson will deduce the `srcdir` based on `pwd` and the location of +`meson.build`. -Hint: The syntax of meson is `meson [options] [srcdir] [builddir]`, -but you may omit either `srcdir` or `builddir`. Meson will deduce the -`srcdir` by the location of `meson.build`. The other one will be your -`pwd`. +Meson then loads the build configuration file and writes the corresponding +build backend in the build directory. By default Meson generates a *debug +build*, which turns on basic warnings and debug information and disables +compiler optimizations. -Meson then loads the build configuration file and writes the -corresponding build backend in the build directory. By default Meson -generates a *debug build*, which turns on basic warnings and debug -information and disables compiler optimizations. - -You can specify a different type of build with the `--buildtype` -command line argument. It can have one of the following values. +You can specify a different type of build with the `--buildtype` command line +argument. It can have one of the following values. | value | meaning | | ------ | -------- | @@ -55,122 +51,123 @@ command line argument. It can have one of the following values. | `debugoptimized` | debug info is generated and the code is optimized (on most compilers this means `-g -O2`) | | `release` | full optimization, no debug info | -The build directory is mandatory. The reason for this is that it -simplifies the build process immensely. Meson will not under any -circumstances write files inside the source directory (if it does, it -is a bug and should be fixed). This means that the user does not need -to add a bunch of files to their revision control's ignore list. It -also means that you can create arbitrarily many build directories for -any given source tree. If we wanted to test building the source code -with the Clang compiler instead of the system default, we could just -type the following commands. - - cd /path/to/source/root - mkdir buildclang - cd buildclang - CC=clang CXX=clang++ meson .. - -This separation is even more powerful if your code has multiple -configuration options (such as multiple data backends). You can create -a separate subdirectory for each of them. You can also have build -directories for optimized builds, code coverage, static analysis and -so on. They are all neatly separated and use the same source -tree. Changing between different configurations is just a question of -changing to the corresponding directory. - -Unless otherwise mentioned, all following command line invocations are -meant to be run in the build directory. - -By default Meson will use the Ninja backend to build your project. If -you wish to use any of the other backends, you need to pass the -corresponding argument during configuration time. As an example, here -is how you would use Meson to generate a Visual studio solution. - - meson <source dir> <build dir> --backend=vs2010 - -You can then open the generated solution with Visual Studio and -compile it in the usual way. A list of backends can be obtained with -`meson --help`. - -Environment Variables --- - -Sometimes you want to add extra compiler flags, this can be done by -passing them in environment variables when calling meson. See [the -reference -tables](Reference-tables.md#compiler-and-linker-flag-envrionment-variables) -for a list of all the environment variables. Be aware however these -environment variables are only used for the native compiler and will -not affect the compiler used for cross-compiling, where the flags -specified in the cross file will be used. - -Furthermore it is possible to stop meson from adding flags itself by -using the `--buildtype=plain` option, in this case you must provide -the full compiler and linker arguments needed. - -Building the source -== +The build directory is mandatory. The reason for this is that it simplifies the +build process immensely. Meson will not under any circumstances write files +inside the source directory (if it does, it is a bug and should be fixed). This +means that the user does not need to add a bunch of files to their revision +control's ignore list. It also means that you can create arbitrarily many build +directories for any given source tree. + +For example, if we wanted to test building the source code with the Clang +compiler instead of the system default, we could just type the following +commands: + +```sh +cd /path/to/source/root +CC=clang CXX=clang++ meson setup buildclang +``` + +This separation is even more powerful if your code has multiple configuration +options (such as multiple data backends). You can create a separate +subdirectory for each of them. You can also have build directories for +optimized builds, code coverage, static analysis and so on. They are all neatly +separated and use the same source tree. Changing between different +configurations is just a question of changing to the corresponding directory. + +Unless otherwise mentioned, all following command line invocations are meant to +be run in the source directory. + +By default Meson will use the Ninja backend to build your project. If you wish +to use any of the other backends, you need to pass the corresponding argument +during configuration time. As an example, here is how you would use Meson to +generate a Visual studio solution. + +```sh +meson setup <build dir> --backend=vs2010 +``` + +You can then open the generated solution with Visual Studio and compile it in +the usual way. A list of backends can be obtained with `meson setup --help`. + +## Environment variables + +Sometimes you want to add extra compiler flags, this can be done by passing +them in environment variables when calling meson. See [the reference +tables](Reference-tables.md#compiler-and-linker-flag-envrionment-variables) for +a list of all the environment variables. Be aware however these environment +variables are only used for the native compiler and will not affect the +compiler used for cross-compiling, where the flags specified in the cross file +will be used. + +Furthermore it is possible to stop meson from adding flags itself by using the +`--buildtype=plain` option, in this case you must provide the full compiler and +linker arguments needed. + +## Building from the source If you are not using an IDE, Meson uses the [Ninja build -system](https://ninja-build.org/) to actually build the code. To start -the build, simply type the following command. +system](https://ninja-build.org/) to actually build the code. To start the +build, simply type the following command. - ninja +```sh +ninja -C builddir +``` -The main usability difference between Ninja and Make is that Ninja -will automatically detect the number of CPUs in your computer and -parallelize itself accordingly. You can override the amount of -parallel processes used with the command line argument `-j <num -processes>`. +The main usability difference between Ninja and Make is that Ninja will +automatically detect the number of CPUs in your computer and parallelize itself +accordingly. You can override the amount of parallel processes used with the +command line argument `-j <num processes>`. -It should be noted that after the initial configure step `ninja` is -the only command you ever need to type to compile. No matter how you -alter your source tree (short of moving it to a completely new -location), Meson will detect the changes and regenerate itself -accordingly. This is especially handy if you have multiple build -directories. Often one of them is used for development (the "debug" -build) and others only every now and then (such as a "static analysis" -build). Any configuration can be built just by `cd`'ing to the -corresponding directory and running Ninja. +It should be noted that after the initial configure step `ninja` is the only +command you ever need to type to compile. No matter how you alter your source +tree (short of moving it to a completely new location), Meson will detect the +changes and regenerate itself accordingly. This is especially handy if you have +multiple build directories. Often one of them is used for development (the +"debug" build) and others only every now and then (such as a "static analysis" +build). Any configuration can be built just by `cd`'ing to the corresponding +directory and running Ninja. -Running tests -== +## Running tests -Meson provides native support for running tests. The command to do -that is simple. +Meson provides native support for running tests. The command to do that is +simple. - ninja test +```sh +ninja -C builddir test +``` -Meson does not force the use of any particular testing framework. You -are free to use GTest, Boost Test, Check or even custom executables. +Meson does not force the use of any particular testing framework. You are free +to use GTest, Boost Test, Check or even custom executables. -Installing -== +## Installing Installing the built software is just as simple. - ninja install +```sh +ninja -C builddir install +``` Note that Meson will only install build targets explicitly tagged as -installable, as detailed in the [installing targets documentation](Installing.md). +installable, as detailed in the [installing targets +documentation](Installing.md). -By default Meson installs to `/usr/local`. This can be changed by -passing the command line argument `--prefix /your/prefix` to Meson -during configure time. Meson also supports the `DESTDIR` variable used -in e.g. building packages. It is used like this: +By default Meson installs to `/usr/local`. This can be changed by passing the +command line argument `--prefix /your/prefix` to Meson during configure time. +Meson also supports the `DESTDIR` variable used in e.g. building packages. It +is used like this: - DESTDIR=/path/to/staging ninja install +```sh +DESTDIR=/path/to/staging ninja -C builddir install +``` -Command line help -== +## Command line help -Meson has a standard command line help feature. It can be accessed -with the following command. +Meson has a standard command line help feature. It can be accessed with the +following command. meson --help -Exit status -== +## Exit status -Meson exits with status 0 if successful, 1 for problems with the command line or -meson.build file, and 2 for internal errors. +Meson exits with status 0 if successful, 1 for problems with the command line +or meson.build file, and 2 for internal errors. diff --git a/docs/markdown/snippets/introspect_meson_info.md b/docs/markdown/snippets/introspect_meson_info.md new file mode 100644 index 0000000..42f2fda --- /dev/null +++ b/docs/markdown/snippets/introspect_meson_info.md @@ -0,0 +1,6 @@ +## Added the `meson-info.json` introspection file + +Meson now generates a `meson-info.json` file in the `meson-info` directory +to provide introspection information about the latest meson run. This file +is updated when the build configuration is changed and the build files are +(re)generated. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 39aa365..0637905 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -725,7 +725,7 @@ class Backend: elif isinstance(a, str): cmd_args.append(a) elif isinstance(a, build.Target): - cmd_args.append(self.get_target_filename(a)) + cmd_args.append(self.construct_target_rel_path(a, t.workdir)) else: raise MesonException('Bad object in test command.') ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross, @@ -737,6 +737,13 @@ class Backend: def write_test_serialisation(self, tests, datafile): pickle.dump(self.create_test_serialisation(tests), datafile) + def construct_target_rel_path(self, a, workdir): + if workdir is None: + return self.get_target_filename(a) + assert(os.path.isabs(workdir)) + abs_path = self.get_target_filename_abs(a) + return os.path.relpath(abs_path, workdir) + def generate_depmf_install(self, d): if self.build.dep_manifest_name is None: return diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 3688f29..debb4fb 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1473,7 +1473,7 @@ int dummy; command_template = ' command = {executable} $ARGS {output_args} $in $LINK_ARGS {cross_args} $aliasing\n' command = command_template.format( executable=' '.join(compiler.get_linker_exelist()), - cross_args=' '.join(cross_args), + cross_args=' '.join([quote_func(i) for i in cross_args]), output_args=' '.join(compiler.get_linker_output_args('$out')) ) description = ' description = Linking target $out.\n' @@ -1601,7 +1601,7 @@ rule FORTRAN_DEP_HACK%s command_template = ' command = {executable} $ARGS {cross_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), - cross_args=' '.join(compiler.get_cross_extra_flags(self.environment, False)) if is_cross else '', + cross_args=' '.join([quote_func(i) for i in compiler.get_cross_extra_flags(self.environment, False)]) if is_cross else '', output_args=' '.join(compiler.get_output_args('$out')), compile_only_args=' '.join(compiler.get_compile_only_args()) ) @@ -1659,7 +1659,7 @@ rule FORTRAN_DEP_HACK%s command_template = ' command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), - cross_args=' '.join(cross_args), + cross_args=' '.join([quote_func(i) for i in cross_args]), dep_args=' '.join(quoted_depargs), output_args=' '.join(compiler.get_output_args('$out')), compile_only_args=' '.join(compiler.get_compile_only_args()) @@ -1703,7 +1703,7 @@ rule FORTRAN_DEP_HACK%s output = ' '.join(compiler.get_output_args('$out')) command = " command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n".format( executable=' '.join(compiler.get_exelist()), - cross_args=' '.join(cross_args), + cross_args=' '.join([quote_func(i) for i in cross_args]), dep_args=' '.join(quoted_depargs), output_args=output, compile_only_args=' '.join(compiler.get_compile_only_args()) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 6350eee..f7864c0 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -1659,6 +1659,9 @@ class CcrxCCompiler(CcrxCompiler, CCompiler): def get_linker_output_args(self, outputname): return ['-output=%s' % outputname] + def get_werror_args(self): + return ['-change_message=error'] + def get_include_args(self, path, is_system): if path == '': path = '.' diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index e99174c..4bf77ac 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -246,6 +246,10 @@ def detect_cpu_family(compilers): trial = 'arm' # Add more quirks here as bugs are reported. Keep in sync with detect_cpu() # below. + elif trial == 'parisc64': + # ATM there is no 64 bit userland for PA-RISC. Thus always + # report it as 32 bit for simplicity. + trial = 'parisc' if trial not in known_cpu_families: mlog.warning('Unknown CPU family {!r}, please report this at ' @@ -1019,7 +1023,8 @@ class Environment: return comp, cross_comp def detect_static_linker(self, compiler): - linker = self.binaries.host.lookup_entry('ar') + for_machine = MachineChoice.HOST if compiler.is_cross else MachineChoice.BUILD + linker = self.binaries[for_machine].lookup_entry('ar') if linker is not None: linkers = [linker] else: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 9ebce70..83ca336 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2713,8 +2713,6 @@ external dependencies (including libraries) must go to "dependencies".''') mlog.log('Cross', cross_comp.get_display_language(), 'compiler:', mlog.bold(' '.join(cross_comp.get_exelist())), version_string) self.build.add_cross_compiler(cross_comp) - if self.environment.is_cross_build() and not need_cross_compiler: - self.build.add_cross_compiler(comp) self.add_base_options(comp) return success diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 2863b0c..b8fb3c6 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -148,6 +148,7 @@ class Conf: def run(options): coredata.parse_cmd_line_options(options) builddir = os.path.abspath(os.path.realpath(options.builddir)) + c = None try: c = Conf(builddir) save = False @@ -163,7 +164,10 @@ def run(options): if save: c.save() mintro.update_build_options(c.coredata, c.build.environment.info_dir) + mintro.write_meson_info_file(c.build, []) except ConfException as e: print('Meson configurator encountered an error:') + if c is not None and c.build is not None: + mintro.write_meson_info_file(c.build, [e]) raise e return 0 diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index c11d044..7236d1a 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -20,7 +20,7 @@ import argparse from . import mesonlib from . import mlog -from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects +from . import mconf, minit, minstall, mintro, msetup, mtest, rewriter, msubprojects, munstable_coredata from .mesonlib import MesonException from .environment import detect_msys2_arch from .wrap import wraptool @@ -57,6 +57,8 @@ class CommandLineParser: help=argparse.SUPPRESS) self.add_command('runpython', self.add_runpython_arguments, self.run_runpython_command, help=argparse.SUPPRESS) + self.add_command('unstable-coredata', munstable_coredata.add_arguments, munstable_coredata.run, + help=argparse.SUPPRESS) def add_command(self, name, add_arguments_func, run_func, help): # FIXME: Cannot have hidden subparser: diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 3382e0d..df9fe73 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -33,25 +33,64 @@ from .backend import backends import sys, os import pathlib +def get_meson_info_file(info_dir: str): + return os.path.join(info_dir, 'meson-info.json') + +def get_meson_introspection_version(): + return '1.0.0' + +def get_meson_introspection_types(coredata: cdata.CoreData = None, builddata: build.Build = None, backend: backends.Backend = None): + if backend and builddata: + benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) + testdata = backend.create_test_serialisation(builddata.get_tests()) + installdata = backend.create_install_data() + else: + benchmarkdata = testdata = installdata = None + + return { + 'benchmarks': { + 'func': lambda: list_benchmarks(benchmarkdata), + 'desc': 'List all benchmarks.', + }, + 'buildoptions': { + 'func': lambda: list_buildoptions(coredata), + 'desc': 'List all build options.', + }, + 'buildsystem_files': { + 'func': lambda: list_buildsystem_files(builddata), + 'desc': 'List files that make up the build system.', + 'key': 'buildsystem-files', + }, + 'dependencies': { + 'func': lambda: list_deps(coredata), + 'desc': 'List external dependencies.', + }, + 'installed': { + 'func': lambda: list_installed(installdata), + 'desc': 'List all installed files and directories.', + }, + 'projectinfo': { + 'func': lambda: list_projinfo(builddata), + 'desc': 'Information about projects.', + }, + 'targets': { + 'func': lambda: list_targets(builddata, installdata, backend), + 'desc': 'List top level targets.', + }, + 'tests': { + 'func': lambda: list_tests(testdata), + 'desc': 'List all unit tests.', + } + } + def add_arguments(parser): - parser.add_argument('--targets', action='store_true', dest='list_targets', default=False, - help='List top level targets.') - parser.add_argument('--installed', action='store_true', dest='list_installed', default=False, - help='List all installed files and directories.') + intro_types = get_meson_introspection_types() + for key, val in intro_types.items(): + flag = '--' + val.get('key', key) + parser.add_argument(flag, action='store_true', dest=key, default=False, help=val['desc']) + parser.add_argument('--target-files', action='store', dest='target_files', default=None, help='List source files for a given target.') - parser.add_argument('--buildsystem-files', action='store_true', dest='buildsystem_files', default=False, - help='List files that make up the build system.') - parser.add_argument('--buildoptions', action='store_true', dest='buildoptions', default=False, - help='List all build options.') - parser.add_argument('--tests', action='store_true', dest='tests', default=False, - help='List all unit tests.') - parser.add_argument('--benchmarks', action='store_true', dest='benchmarks', default=False, - help='List all benchmarks.') - parser.add_argument('--dependencies', action='store_true', dest='dependencies', default=False, - help='List external dependencies.') - parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False, - help='Information about projects.') parser.add_argument('--backend', choices=cdata.backendlist, dest='backend', default='ninja', help='The backend to use for the --buildoptions introspection.') parser.add_argument('-a', '--all', action='store_true', dest='all', default=False, @@ -74,7 +113,7 @@ def list_installed(installdata): res[path] = os.path.join(installdata.prefix, installdir, os.path.basename(path)) for path, installpath, unused_custom_install_mode in installdata.man: res[path] = os.path.join(installdata.prefix, installpath) - return ('installed', res) + return res def list_targets(builddata: build.Build, installdata, backend: backends.Backend): tlist = [] @@ -110,7 +149,7 @@ def list_targets(builddata: build.Build, installdata, backend: backends.Backend) else: t['installed'] = False tlist.append(t) - return ('targets', tlist) + return tlist class BuildoptionsOptionHelper: # mimic an argparse namespace @@ -178,7 +217,7 @@ class BuildoptionsInterperter(astinterpreter.AstInterpreter): return flattend_args def add_languages(self, args): - need_cross_compiler = self.environment.is_cross_build() and self.environment.cross_info.need_cross_compiler() + need_cross_compiler = self.environment.is_cross_build() for lang in sorted(args, key=compilers.sort_clink): lang = lang.lower() if lang not in self.coredata.compilers: @@ -257,8 +296,7 @@ def list_buildoptions_from_source(sourcedir, backend, indent): intr.analyze() # Reenable logging just in case mlog.enable() - buildoptions = list_buildoptions(intr.coredata)[1] - print(json.dumps(buildoptions, indent=indent)) + print(json.dumps(list_buildoptions(intr.coredata), indent=indent)) def list_target_files(target_name, targets, builddata: build.Build): result = [] @@ -279,7 +317,7 @@ def list_target_files(target_name, targets, builddata: build.Build): # TODO Remove this line in a future PR with other breaking changes result = list(map(lambda x: os.path.relpath(x, builddata.environment.get_source_dir()), result)) - return ('target_files', result) + return result def list_buildoptions(coredata: cdata.CoreData): optlist = [] @@ -312,7 +350,7 @@ def list_buildoptions(coredata: cdata.CoreData): add_keys(optlist, dir_options, 'directory') add_keys(optlist, coredata.user_options, 'user') add_keys(optlist, test_options, 'test') - return ('buildoptions', optlist) + return optlist def add_keys(optlist, options, section): keys = list(options.keys()) @@ -349,7 +387,7 @@ def find_buildsystem_files_list(src_dir): def list_buildsystem_files(builddata: build.Build): src_dir = builddata.environment.get_source_dir() filelist = find_buildsystem_files_list(src_dir) - return ('buildsystem_files', filelist) + return filelist def list_deps(coredata: cdata.CoreData): result = [] @@ -358,7 +396,7 @@ def list_deps(coredata: cdata.CoreData): result += [{'name': d.name, 'compile_args': d.get_compile_args(), 'link_args': d.get_link_args()}] - return ('dependencies', result) + return result def get_test_list(testdata): result = [] @@ -382,10 +420,10 @@ def get_test_list(testdata): return result def list_tests(testdata): - return ('tests', get_test_list(testdata)) + return get_test_list(testdata) def list_benchmarks(benchdata): - return ('benchmarks', get_test_list(benchdata)) + return get_test_list(benchdata) def list_projinfo(builddata: build.Build): result = {'version': builddata.project_version, @@ -397,7 +435,7 @@ def list_projinfo(builddata: build.Build): 'descriptive_name': builddata.projects.get(k)} subprojects.append(c) result['subprojects'] = subprojects - return ('projectinfo', result) + return result class ProjectInfoInterperter(astinterpreter.AstInterpreter): def __init__(self, source_root, subdir): @@ -482,30 +520,21 @@ def run(options): results = [] toextract = [] + intro_types = get_meson_introspection_types() + + for i in intro_types.keys(): + if options.all or getattr(options, i, False): + toextract += [i] - if options.all or options.benchmarks: - toextract += ['benchmarks'] - if options.all or options.buildoptions: - toextract += ['buildoptions'] - if options.all or options.buildsystem_files: - toextract += ['buildsystem_files'] - if options.all or options.dependencies: - toextract += ['dependencies'] - if options.all or options.list_installed: - toextract += ['installed'] - if options.all or options.projectinfo: - toextract += ['projectinfo'] - if options.all or options.list_targets: - toextract += ['targets'] + # Handle the one option that does not have its own JSON file (meybe deprecate / remove this?) if options.target_files is not None: targets_file = os.path.join(infodir, 'intro-targets.json') with open(targets_file, 'r') as fp: targets = json.load(fp) builddata = build.load(options.builddir) # TODO remove this in a breaking changes PR - results += [list_target_files(options.target_files, targets, builddata)] - if options.all or options.tests: - toextract += ['tests'] + results += [('target_files', list_target_files(options.target_files, targets, builddata))] + # Extract introspection information from JSON for i in toextract: curr = os.path.join(infodir, 'intro-{}.json'.format(i)) if not os.path.isfile(curr): @@ -527,7 +556,10 @@ def run(options): print(json.dumps(out, indent=indent)) return 0 +updated_introspection_files = [] + def write_intro_info(intro_info, info_dir): + global updated_introspection_files for i in intro_info: out_file = os.path.join(info_dir, 'intro-{}.json'.format(i[0])) tmp_file = os.path.join(info_dir, 'tmp_dump.json') @@ -535,29 +567,70 @@ def write_intro_info(intro_info, info_dir): json.dump(i[1], fp) fp.flush() # Not sure if this is needed os.replace(tmp_file, out_file) + updated_introspection_files += [i[0]] def generate_introspection_file(builddata: build.Build, backend: backends.Backend): coredata = builddata.environment.get_coredata() - benchmarkdata = backend.create_test_serialisation(builddata.get_benchmarks()) - testdata = backend.create_test_serialisation(builddata.get_tests()) - installdata = backend.create_install_data() + intro_types = get_meson_introspection_types(coredata=coredata, builddata=builddata, backend=backend) + intro_info = [] - intro_info = [ - list_benchmarks(benchmarkdata), - list_buildoptions(coredata), - list_buildsystem_files(builddata), - list_deps(coredata), - list_installed(installdata), - list_projinfo(builddata), - list_targets(builddata, installdata, backend), - list_tests(testdata) - ] + for key, val in intro_types.items(): + intro_info += [(key, val['func']())] write_intro_info(intro_info, builddata.environment.info_dir) def update_build_options(coredata: cdata.CoreData, info_dir): intro_info = [ - list_buildoptions(coredata) + ('buildoptions', list_buildoptions(coredata)) ] write_intro_info(intro_info, info_dir) + +def split_version_string(version: str): + vers_list = version.split('.') + return { + 'full': version, + 'major': int(vers_list[0] if len(vers_list) > 0 else 0), + 'minor': int(vers_list[1] if len(vers_list) > 1 else 0), + 'patch': int(vers_list[2] if len(vers_list) > 2 else 0) + } + +def write_meson_info_file(builddata: build.Build, errors: list, build_files_updated: bool = False): + global updated_introspection_files + info_dir = builddata.environment.info_dir + info_file = get_meson_info_file(info_dir) + intro_types = get_meson_introspection_types() + intro_info = {} + + for i in intro_types.keys(): + intro_info[i] = { + 'file': 'intro-{}.json'.format(i), + 'updated': i in updated_introspection_files + } + + info_data = { + 'meson_version': split_version_string(cdata.version), + 'directories': { + 'source': builddata.environment.get_source_dir(), + 'build': builddata.environment.get_build_dir(), + 'info': info_dir, + }, + 'introspection': { + 'version': split_version_string(get_meson_introspection_version()), + 'information': intro_info, + }, + 'build_files_updated': build_files_updated, + } + + if len(errors) > 0: + info_data['error'] = True + info_data['error_list'] = [x if isinstance(x, str) else str(x) for x in errors] + else: + info_data['error'] = False + + # Write the data to disc + tmp_file = os.path.join(info_dir, 'tmp_dump.json') + with open(tmp_file, 'w') as fp: + json.dump(info_data, fp) + fp.flush() + os.replace(tmp_file, info_file) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index ba5bcaa..9774286 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -29,39 +29,16 @@ import os class RPMModule(ExtensionModule): @noKwargs - def generate_spec_template(self, state, args, kwargs): - compiler_deps = set() - for compiler in state.compilers.values(): - # Elbrus has one 'lcc' package for every compiler - if isinstance(compiler, compilers.GnuCCompiler): - compiler_deps.add('gcc') - elif isinstance(compiler, compilers.GnuCPPCompiler): - compiler_deps.add('gcc-c++') - elif isinstance(compiler, compilers.ElbrusCCompiler): - compiler_deps.add('lcc') - elif isinstance(compiler, compilers.ElbrusCPPCompiler): - compiler_deps.add('lcc') - elif isinstance(compiler, compilers.ElbrusFortranCompiler): - compiler_deps.add('lcc') - elif isinstance(compiler, compilers.ValaCompiler): - compiler_deps.add('vala') - elif isinstance(compiler, compilers.GnuFortranCompiler): - compiler_deps.add('gcc-gfortran') - elif isinstance(compiler, compilers.GnuObjCCompiler): - compiler_deps.add('gcc-objc') - elif compiler == compilers.GnuObjCPPCompiler: - compiler_deps.add('gcc-objc++') - else: - mlog.log('RPM spec file will not created, generating not allowed for:', - mlog.bold(compiler.get_id())) - return - proj = state.project_name.replace(' ', '_').replace('\t', '_') + def generate_spec_template(self, coredata, args, kwargs): + self.coredata = coredata + required_compilers = self.__get_required_compilers() + proj = coredata.project_name.replace(' ', '_').replace('\t', '_') so_installed = False devel_subpkg = False files = set() files_devel = set() to_delete = set() - for target in state.targets.values(): + for target in coredata.targets.values(): if isinstance(target, build.Executable) and target.need_install: files.add('%%{_bindir}/%s' % target.get_filename()) elif isinstance(target, build.SharedLibrary) and target.need_install: @@ -80,18 +57,19 @@ class RPMModule(ExtensionModule): files_devel.add('%%{_datadir}/gir-1.0/%s' % target.get_filename()[0]) elif isinstance(target, TypelibTarget) and target.should_install(): files.add('%%{_libdir}/girepository-1.0/%s' % target.get_filename()[0]) - for header in state.headers: + for header in coredata.headers: if len(header.get_install_subdir()) > 0: files_devel.add('%%{_includedir}/%s/' % header.get_install_subdir()) else: for hdr_src in header.get_sources(): files_devel.add('%%{_includedir}/%s' % hdr_src) - for man in state.man: + for man in coredata.man: for man_file in man.get_sources(): files.add('%%{_mandir}/man%u/%s.*' % (int(man_file.split('.')[-1]), man_file)) if len(files_devel) > 0: devel_subpkg = True - filename = os.path.join(state.environment.get_build_dir(), + + filename = os.path.join(coredata.environment.get_build_dir(), '%s.spec' % proj) with open(filename, 'w+') as fn: fn.write('Name: %s\n' % proj) @@ -102,24 +80,28 @@ class RPMModule(ExtensionModule): fn.write('\n') fn.write('Source0: %{name}-%{version}.tar.xz # FIXME\n') fn.write('\n') - for compiler in compiler_deps: + fn.write('BuildRequires: meson\n') + for compiler in required_compilers: fn.write('BuildRequires: %s\n' % compiler) - for dep in state.environment.coredata.deps: + for dep in coredata.environment.coredata.deps: fn.write('BuildRequires: pkgconfig(%s)\n' % dep[0]) - for lib in state.environment.coredata.ext_libs.values(): - name = lib.get_name() - fn.write('BuildRequires: {} # FIXME\n'.format(name)) - mlog.warning('replace', mlog.bold(name), 'with the real package.', - 'You can use following command to find package which ' - 'contains this lib:', - mlog.bold("dnf provides '*/lib{}.so'".format(name))) - for prog in state.environment.coredata.ext_progs.values(): - if not prog.found(): - fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' % - prog.get_name()) - else: - fn.write('BuildRequires: {}\n'.format(prog.get_path())) - fn.write('BuildRequires: meson\n') +# ext_libs and ext_progs have been removed from coredata so the following code +# no longer works. It is kept as a reminder of the idea should anyone wish +# to re-implement it. +# +# for lib in state.environment.coredata.ext_libs.values(): +# name = lib.get_name() +# fn.write('BuildRequires: {} # FIXME\n'.format(name)) +# mlog.warning('replace', mlog.bold(name), 'with the real package.', +# 'You can use following command to find package which ' +# 'contains this lib:', +# mlog.bold("dnf provides '*/lib{}.so'".format(name))) +# for prog in state.environment.coredata.ext_progs.values(): +# if not prog.found(): +# fn.write('BuildRequires: %%{_bindir}/%s # FIXME\n' % +# prog.get_name()) +# else: +# fn.write('BuildRequires: {}\n'.format(prog.get_path())) fn.write('\n') fn.write('%description\n') fn.write('\n') @@ -167,5 +149,33 @@ class RPMModule(ExtensionModule): mlog.log('RPM spec template written to %s.spec.\n' % proj) return ModuleReturnValue(None, []) + def __get_required_compilers(self): + required_compilers = set() + for compiler in self.coredata.compilers.values(): + # Elbrus has one 'lcc' package for every compiler + if isinstance(compiler, compilers.GnuCCompiler): + required_compilers.add('gcc') + elif isinstance(compiler, compilers.GnuCPPCompiler): + required_compilers.add('gcc-c++') + elif isinstance(compiler, compilers.ElbrusCCompiler): + required_compilers.add('lcc') + elif isinstance(compiler, compilers.ElbrusCPPCompiler): + required_compilers.add('lcc') + elif isinstance(compiler, compilers.ElbrusFortranCompiler): + required_compilers.add('lcc') + elif isinstance(compiler, compilers.ValaCompiler): + required_compilers.add('vala') + elif isinstance(compiler, compilers.GnuFortranCompiler): + required_compilers.add('gcc-gfortran') + elif isinstance(compiler, compilers.GnuObjCCompiler): + required_compilers.add('gcc-objc') + elif compiler == compilers.GnuObjCPPCompiler: + required_compilers.add('gcc-objc++') + else: + mlog.log('RPM spec file not created, generation not allowed for:', + mlog.bold(compiler.get_id())) + return required_compilers + + def initialize(*args, **kwargs): return RPMModule(*args, **kwargs) diff --git a/mesonbuild/msetup.py b/mesonbuild/msetup.py index 67559a1..023afdb 100644 --- a/mesonbuild/msetup.py +++ b/mesonbuild/msetup.py @@ -185,11 +185,15 @@ class MesonApp: mlog.log('Target machine cpu:', mlog.bold(intr.builtin['target_machine'].cpu_method([], {}))) mlog.log('Build machine cpu family:', mlog.bold(intr.builtin['build_machine'].cpu_family_method([], {}))) mlog.log('Build machine cpu:', mlog.bold(intr.builtin['build_machine'].cpu_method([], {}))) - if self.options.profile: - fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log') - profile.runctx('intr.run()', globals(), locals(), filename=fname) - else: - intr.run() + try: + if self.options.profile: + fname = os.path.join(self.build_dir, 'meson-private', 'profile-interpreter.log') + profile.runctx('intr.run()', globals(), locals(), filename=fname) + else: + intr.run() + except Exception as e: + mintro.write_meson_info_file(b, [e]) + raise # Print all default option values that don't match the current value for def_opt_name, def_opt_value, cur_opt_value in intr.get_non_matching_default_options(): mlog.log('Option', mlog.bold(def_opt_name), 'is:', @@ -224,7 +228,9 @@ class MesonApp: profile.runctx('mintro.generate_introspection_file(b, intr.backend)', globals(), locals(), filename=fname) else: mintro.generate_introspection_file(b, intr.backend) - except: + mintro.write_meson_info_file(b, [], True) + except Exception as e: + mintro.write_meson_info_file(b, [e]) if 'cdf' in locals(): old_cdf = cdf + '.prev' if os.path.exists(old_cdf): diff --git a/mesonbuild/munstable_coredata.py b/mesonbuild/munstable_coredata.py new file mode 100644 index 0000000..78f3f34 --- /dev/null +++ b/mesonbuild/munstable_coredata.py @@ -0,0 +1,126 @@ +# Copyright 2019 The Meson development team + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from . import coredata as cdata + +import os.path +import pprint +import textwrap + +def add_arguments(parser): + parser.add_argument('--all', action='store_true', dest='all', default=False, + help='Show data not used by current backend.') + + parser.add_argument('builddir', nargs='?', default='.', help='The build directory') + + +def dump_compilers(compilers): + for lang, compiler in compilers.items(): + print(' ' + lang + ':') + print(' Id: ' + compiler.id) + print(' Command: ' + ' '.join(compiler.exelist)) + print(' Full version: ' + compiler.full_version) + print(' Detected version: ' + compiler.version) + print(' Detected type: ' + repr(compiler.compiler_type)) + #pprint.pprint(compiler.__dict__) + + +def dump_guids(d): + for name, value in d.items(): + print(' ' + name + ': ' + value) + + +def run(options): + datadir = 'meson-private' + if options.builddir is not None: + datadir = os.path.join(options.builddir, datadir) + if not os.path.isdir(datadir): + print('Current directory is not a build dir. Please specify it or ' + 'change the working directory to it.') + return 1 + + all = options.all + + print('This is a dump of the internal unstable cache of meson. This is for debugging only.') + print('Do NOT parse, this will change from version to version in incompatible ways') + print('') + + coredata = cdata.load(options.builddir) + backend = coredata.get_builtin_option('backend') + for k, v in sorted(coredata.__dict__.items()): + if k in ('backend_options', 'base_options', 'builtins', 'compiler_options', 'user_options'): + # use `meson configure` to view these + pass + elif k in ['install_guid', 'test_guid', 'regen_guid']: + if all or backend.startswith('vs'): + print(k + ': ' + v) + elif k == 'target_guids': + if all or backend.startswith('vs'): + print(k + ':') + dump_guids(v) + elif k in ['lang_guids']: + if all or backend.startswith('vs') or backend == 'xcode': + print(k + ':') + dump_guids(v) + elif k == 'meson_command': + if all or backend.startswith('vs'): + print('Meson command used in build file regeneration: ' + ' '.join(v)) + elif k == 'pkgconf_envvar': + print('Last seen PKGCONFIG enviroment variable value: ' + v) + elif k == 'version': + print('Meson version: ' + v) + elif k == 'cross_file': + print('Cross File: ' + (v or 'None')) + elif k == 'config_files': + if v: + print('Native File: ' + ' '.join(v)) + elif k == 'compilers': + print('Cached native compilers:') + dump_compilers(v) + elif k == 'cross_compilers': + print('Cached cross compilers:') + dump_compilers(v) + elif k == 'deps': + native = [] + cross = [] + for dep_key, dep in sorted(v.items()): + if dep_key[2]: + cross.append((dep_key, dep)) + else: + native.append((dep_key, dep)) + + def print_dep(dep_key, dep): + print(' ' + dep_key[0] + ": ") + print(' compile args: ' + repr(dep.get_compile_args())) + print(' link args: ' + repr(dep.get_link_args())) + if dep.get_sources(): + print(' sources: ' + repr(dep.get_sources())) + print(' version: ' + repr(dep.get_version())) + + if native: + print('Cached native dependencies:') + for dep_key, dep in native: + print_dep(dep_key, dep) + if cross: + print('Cached dependencies:') + for dep_key, dep in cross: + print_dep(dep_key, dep) + elif k == 'external_preprocess_args': + for lang, opts in v.items(): + if opts: + print('Preprocessor args for ' + lang + ': ' + ' '.join(opts)) + else: + print(k + ':') + print(textwrap.indent(pprint.pformat(v), ' ')) diff --git a/run_unittests.py b/run_unittests.py index f7737ab..55a5bd6 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -3291,6 +3291,20 @@ recommended as it is not supported on some platforms''') self.assertEqual(res_all, res_file) + def test_introspect_meson_info(self): + testdir = os.path.join(self.unit_test_dir, '49 introspection') + introfile = os.path.join(self.builddir, 'meson-info', 'meson-info.json') + self.init(testdir) + self.assertPathExists(introfile) + with open(introfile, 'r') as fp: + res1 = json.load(fp) + + for i in ['meson_version', 'directories', 'introspection', 'build_files_updated', 'error']: + self.assertIn(i, res1) + + self.assertEqual(res1['error'], False) + self.assertEqual(res1['build_files_updated'], True) + def test_introspect_config_update(self): testdir = os.path.join(self.unit_test_dir, '49 introspection') introfile = os.path.join(self.builddir, 'meson-info', 'intro-buildoptions.json') diff --git a/test cases/common/97 test workdir/meson.build b/test cases/common/97 test workdir/meson.build index 1323a17..a8290f7 100644 --- a/test cases/common/97 test workdir/meson.build +++ b/test cases/common/97 test workdir/meson.build @@ -4,3 +4,5 @@ exe = executable('opener', 'opener.c') test('basic', exe, workdir : meson.source_root()) test('shouldfail', exe, should_fail : true) + +subdir('subdir') diff --git a/test cases/common/97 test workdir/subdir/checker.py b/test cases/common/97 test workdir/subdir/checker.py new file mode 100755 index 0000000..66e287d --- /dev/null +++ b/test cases/common/97 test workdir/subdir/checker.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python3 + +import sys + +data = open(sys.argv[1], 'rb').read() diff --git a/test cases/common/97 test workdir/subdir/meson.build b/test cases/common/97 test workdir/subdir/meson.build new file mode 100644 index 0000000..687a1cf --- /dev/null +++ b/test cases/common/97 test workdir/subdir/meson.build @@ -0,0 +1,4 @@ +exe2 = executable('dummy', '../opener.c') +test('subdir', find_program('checker.py'), + workdir : meson.source_root(), + args: [exe2]) |