diff options
133 files changed, 1774 insertions, 725 deletions
diff --git a/.travis.yml b/.travis.yml index f077c9c..48cfb38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,8 @@ matrix: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install python3; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew uninstall python mercurial; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install python@2 python@3 mercurial qt; fi # Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir -p $HOME/tools; curl -L http://nirbheek.in/files/binaries/ninja/macos/ninja -o $HOME/tools/ninja; chmod +x $HOME/tools/ninja; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:artful; fi @@ -49,4 +50,4 @@ script: withgit \ /bin/sh -c "cd /root && mkdir -p tools; wget -c http://nirbheek.in/files/binaries/ninja/linux-amd64/ninja -O /root/tools/ninja; chmod +x /root/tools/ninja; CC=$CC CXX=$CXX OBJC=$CC OBJCXX=$CXX PATH=/root/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py -- $MESON_ARGS && chmod -R a+rwX .coverage" fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib OBJC=$CC OBJCXX=$CXX PATH=$HOME/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib OBJC=$CC OBJCXX=$CXX PATH=$HOME/tools:/usr/local/opt/qt/bin:$PATH MESON_FIXED_NINJA=1 ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi diff --git a/ciimage/Dockerfile b/ciimage/Dockerfile index 05e679e..72788c3 100644 --- a/ciimage/Dockerfile +++ b/ciimage/Dockerfile @@ -14,3 +14,5 @@ RUN apt-get -y update && apt-get -y upgrade \ && apt-get -y install libwmf-dev \ && apt-get -y install qt4-linguist-tools qttools5-dev-tools \ && python3 -m pip install hotdoc codecov + +ENV LANG='C.UTF-8' diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..3d4dc34 --- /dev/null +++ b/contributing.md @@ -0,0 +1,8 @@ +## Contributing to the Meson build system + +Thank you for your interest in participating to the development! +A large fraction of Meson is contributed by people outside +the core team and we are **excited** to see what you do. + +**Contribution instructions can be found on the website** + @ http://mesonbuild.com/Contributing.html diff --git a/contributing.txt b/contributing.txt deleted file mode 100644 index b1c015c..0000000 --- a/contributing.txt +++ /dev/null @@ -1,4 +0,0 @@ -Contributing to the Meson build system - -Contribution instructions can be found [on the -website](http://mesonbuild.com/Contributing.html). diff --git a/docs/markdown/Adding-arguments.md b/docs/markdown/Adding-arguments.md index e314102..117622b 100644 --- a/docs/markdown/Adding-arguments.md +++ b/docs/markdown/Adding-arguments.md @@ -49,6 +49,8 @@ executable('prog', 'prog.cc', cpp_args : '-DCPPTHING') Here we create a C++ executable with an extra argument that is used during compilation but not for linking. +You can find the parameter name for other languages in the [reference tables](Reference-tables.md). + Specifying extra linker arguments is done in the same way: ```meson diff --git a/docs/markdown/Configuring-a-build-directory.md b/docs/markdown/Configuring-a-build-directory.md index 774addf..8e016e2 100644 --- a/docs/markdown/Configuring-a-build-directory.md +++ b/docs/markdown/Configuring-a-build-directory.md @@ -9,9 +9,7 @@ generated. For example you might want to change from a debug build into a release build, set custom compiler flags, change the build options provided in your `meson_options.txt` file and so on. -The main tool for this is the `meson configure` command. You may also use the -`mesongui` graphical application if you want. However this document -describes the use of the command line client. +The main tool for this is the `meson configure` command. You invoke `meson configure` by giving it the location of your build dir. If omitted, the current working directory is used instead. Here's a diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index c1ad317..520a081 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -261,7 +261,7 @@ myvar = meson.get_cross_property('somekey') ## Cross file locations As of version 0.44.0 meson supports loading cross files from system locations -on Linux and the BSDs. This will be $XDG_DATA_DIRS/meson/cross, or if +(except on Windows). This will be $XDG_DATA_DIRS/meson/cross, or if XDG_DATA_DIRS is undefined, then /usr/local/share/meson/cross and /usr/share/meson/cross will be tried in that order, for system wide cross files. User local files can be put in $XDG_DATA_HOME/meson/cross, or @@ -272,7 +272,7 @@ The order of locations tried is as follows: - The user local location - The system wide locations in order -Linux and BSD distributions are encouraged to ship cross files either with +Distributions are encouraged to ship cross files either with their cross compiler toolchain packages or as a standalone package, and put them in one of the system paths referenced above. diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 74a918a..12e1b1f 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -61,7 +61,7 @@ zdep_prefix = zdep.get_pkgconfig_variable('libdir', define_variable: ['prefix', The dependency detector works with all libraries that provide a `pkg-config` file. Unfortunately several packages don't provide pkg-config files. Meson has autodetection support for some of these, -and they are described later on this page. +and they are described [later in this page](#dependencies-with-custom-lookup-functionality). # Declaring your own @@ -111,6 +111,14 @@ of all the work behind the scenes to make this work. # Dependencies with custom lookup functionality +## AppleFrameworks + +Use the `modules` keyword to list frameworks required, e.g. + +```meson +dep = find_dep('appleframeworks', modules : 'foundation') +``` + ## Boost Boost is not a single dependency but rather a group of different @@ -138,7 +146,11 @@ can set the BOOST_ROOT, BOOST_INCLUDEDIR, and/or BOOST_LIBRARYDIR environment variables. You can set the argument `threading` to `single` to use boost libraries that -has been compiled for single-threaded use instead. +have been compiled for single-threaded use instead. + +## GL + +This finds the OpenGL library in a way appropriate to the platform. ## GTest and GMock @@ -160,9 +172,9 @@ test('gtest test', e) MPI is supported for C, C++ and Fortran. Because dependencies are language-specific, you must specify the requested language using the `language` keyword argument, i.e., - * `dependency('mpi', language='c')` for the C MPI headers and libraries - * `dependency('mpi', language='cpp')` for the C++ MPI headers and libraries - * `dependency('mpi', language='fortran')` for the Fortran MPI headers and libraries + * `dependency('mpi', language: 'c')` for the C MPI headers and libraries + * `dependency('mpi', language: 'cpp')` for the C++ MPI headers and libraries + * `dependency('mpi', language: 'fortran')` for the Fortran MPI headers and libraries Meson prefers pkg-config for MPI, but if your MPI implementation does not provide them, it will search for the standard wrapper executables, @@ -171,9 +183,9 @@ are not in your path, they can be specified by setting the standard environment variables `MPICC`, `MPICXX`, `MPIFC`, `MPIF90`, or `MPIF77`, during configuration. -## Qt5 +## Qt4 & Qt5 -Meson has native Qt5 support. Its usage is best demonstrated with an +Meson has native Qt support. Its usage is best demonstrated with an example. ```meson @@ -204,12 +216,26 @@ the list of sources for the target. The `modules` keyword of `dependency` works just like it does with Boost. It tells which subparts of Qt the program uses. +## SDL2 + +SDL2 can be located using `pkg-confg`, the `sdl2-config` config tool, or as an +OSX framework. + +## Valgrind + +Meson will find valgrind using `pkg-config`, but only uses the compilation flags +and avoids trying to link with it's non-PIC static libs. + +## Vulkan + +Vulkan can be located using `pkg-config`, or the `VULKAN_SDK` environment variable. + ## Dependencies using config tools -CUPS, LLVM, PCAP, WxWidgets, libwmf, and GnuStep either do not provide -pkg-config modules or additionally can be detected via a config tool +CUPS, LLVM, PCAP, [WxWidgets](#wxwidgets), libwmf, and GnuStep either do not +provide pkg-config modules or additionally can be detected via a config tool (cups-config, llvm-config, etc). Meson has native support for these tools, and -then can be found like other dependencies: +they can be found like other dependencies: ```meson pcap_dep = dependency('pcap', version : '>=1.0') @@ -224,6 +250,30 @@ tools support. You can force one or another via the method keyword: wmf_dep = dependency('wmf', method : 'config-tool') ``` +## WxWidgets + +Similar to [Boost](#boost), WxWidgets is not a single library but rather +a collection of modules. WxWidgets is supported via `wx-config`. +Meson substitutes `modules` to `wx-config` invocation, it generates +- `compile_args` using `wx-config --cxxflags $modules...` +- `link_args` using `wx-config --libs $modules...` + +### Example + +```meson +wx_dep = dependency( + 'wxwidgets', version : '>=3.0.0', modules : ['std', 'stc'], +) +``` + +```shell +# compile_args: +$ wx-config --cxxflags std stc + +# link_args: +$ wx-config --libs std stc +``` + ## LLVM Meson has native support for LLVM going back to version LLVM version 3.5. @@ -257,10 +307,10 @@ llvm_dep = dependency( Python3 is handled specially by meson: 1. Meson tries to use `pkg-config`. -1. If `pkg-config` fails meson uses fallback: - - On Windows fallback is current `python3` interpreter. - - On OSX fallback is framework dependency from `/Library/Frameworks`. +1. If `pkg-config` fails meson uses a fallback: + - On Windows the fallback is the current `python3` interpreter. + - On OSX the fallback is a framework dependency from `/Library/Frameworks`. Note that `python3` found by this dependency might differ from the one used in -`python3` module because modules uses current interpreter but dependency tries +`python3` module because modules uses the current interpreter, but dependency tries `pkg-config` first. diff --git a/docs/markdown/FAQ.md b/docs/markdown/FAQ.md index 441cd69..1dbc80a 100644 --- a/docs/markdown/FAQ.md +++ b/docs/markdown/FAQ.md @@ -7,9 +7,16 @@ See also [How do I do X in Meson](howtox.md). ## Why is it called Meson? -When the name was originally chosen, there were two main limitations: there must not exist either a Debian package or a Sourceforge project of the given name. This ruled out tens of potential project names. At some point the name Gluon was considered. Gluons are elementary particles that hold protons and neutrons together, much like a build system's job is to take pieces of source code and a compiler and bind them to a complete whole. +When the name was originally chosen, there were two main limitations: +there must not exist either a Debian package or a Sourceforge project +of the given name. This ruled out tens of potential project names. At +some point the name Gluon was considered. Gluons are elementary +particles that hold protons and neutrons together, much like a build +system's job is to take pieces of source code and a compiler and bind +them to a complete whole. -Unfortunately this name was taken, too. Then the rest of subatomic particles were examined and Meson was found to be available. +Unfortunately this name was taken, too. Then the rest of subatomic +particles were examined and Meson was found to be available. ## What is the correct way to use threads (such as pthreads)? @@ -17,23 +24,34 @@ Unfortunately this name was taken, too. Then the rest of subatomic particles wer thread_dep = dependency('threads') ``` -This will set up everything on your behalf. People coming from Autotools or CMake want to do this by looking for `libpthread.so` manually. Don't do that, it has tricky corner cases especially when cross compiling. +This will set up everything on your behalf. People coming from +Autotools or CMake want to do this by looking for `libpthread.so` +manually. Don't do that, it has tricky corner cases especially when +cross compiling. ## How to use Meson on a host where it is not available in system packages? -Starting from version 0.29.0, Meson is available from the [Python Package Index](https://pypi.python.org/pypi/meson/), so installing it simply a matter of running this command: +Starting from version 0.29.0, Meson is available from the [Python +Package Index](https://pypi.python.org/pypi/meson/), so installing it +simply a matter of running this command: ```console $ pip3 install <your options here> meson ``` -If you don't have access to PyPI, that is not a problem either. Meson has been designed to be easily runnable from an extracted source tarball or even a git checkout. First you need to download Meson. Then use this command to set up you build instead of plain `meson`. +If you don't have access to PyPI, that is not a problem either. Meson +has been designed to be easily runnable from an extracted source +tarball or even a git checkout. First you need to download Meson. Then +use this command to set up you build instead of plain `meson`. ```console $ /path/to/meson.py <options> ``` -After this you don't have to care about invoking Meson any more. It remembers where it was originally invoked from and calls itself appropriately. As a user the only thing you need to do is to `cd` into your build directory and invoke `ninja`. +After this you don't have to care about invoking Meson any more. It +remembers where it was originally invoked from and calls itself +appropriately. As a user the only thing you need to do is to `cd` into +your build directory and invoke `ninja`. ## Why can't I specify target files with a wildcard? @@ -43,17 +61,34 @@ Instead of specifying files explicitly, people seem to want to do this: executable('myprog', sources : '*.cpp') # This does NOT work! ``` -Meson does not support this syntax and the reason for this is simple. This can not be made both reliable and fast. By reliable we mean that if the user adds a new source file to the subdirectory, Meson should detect that and make it part of the build automatically. +Meson does not support this syntax and the reason for this is +simple. This can not be made both reliable and fast. By reliable we +mean that if the user adds a new source file to the subdirectory, +Meson should detect that and make it part of the build automatically. -One of the main requirements of Meson is that it must be fast. This means that a no-op build in a tree of 10 000 source files must take no more than a fraction of a second. This is only possible because Meson knows the exact list of files to check. If any target is specified as a wildcard glob, this is no longer possible. Meson would need to re-evaluate the glob every time and compare the list of files produced against the previous list. This means inspecting the entire source tree (because the glob pattern could be `src/\*/\*/\*/\*.cpp` or something like that). This is impossible to do efficiently. +One of the main requirements of Meson is that it must be fast. This +means that a no-op build in a tree of 10 000 source files must take no +more than a fraction of a second. This is only possible because Meson +knows the exact list of files to check. If any target is specified as +a wildcard glob, this is no longer possible. Meson would need to +re-evaluate the glob every time and compare the list of files produced +against the previous list. This means inspecting the entire source +tree (because the glob pattern could be `src/\*/\*/\*/\*.cpp` or +something like that). This is impossible to do efficiently. -The main backend of Meson is Ninja, which does not support wildcard matches either, and for the same reasons. +The main backend of Meson is Ninja, which does not support wildcard +matches either, and for the same reasons. Because of this, all source files must be specified explicitly. ## But I really want to use wildcards! -If the tradeoff between reliability and convenience is acceptable to you, then Meson gives you all the tools necessary to do wildcard globbing. You are allowed to run arbitrary commands during configuration. First you need to write a script that locates the files to compile. Here's a simple shell script that writes all `.c` files in the current directory, one per line. +If the tradeoff between reliability and convenience is acceptable to +you, then Meson gives you all the tools necessary to do wildcard +globbing. You are allowed to run arbitrary commands during +configuration. First you need to write a script that locates the files +to compile. Here's a simple shell script that writes all `.c` files in +the current directory, one per line. ```bash @@ -72,17 +107,37 @@ sources = c.stdout().strip().split('\n') e = executable('prog', sources) ``` -The script can be any executable, so it can be written in shell, Python, Lua, Perl or whatever you wish. +The script can be any executable, so it can be written in shell, +Python, Lua, Perl or whatever you wish. -As mentioned above, the tradeoff is that just adding new files to the source directory does *not* add them to the build automatically. To add them you need to tell Meson to reinitialize itself. The simplest way is to touch the `meson.build` file in your source root. Then Meson will reconfigure itself next time the build command is run. Advanced users can even write a small background script that utilizes a filesystem event queue, such as [inotify](https://en.wikipedia.org/wiki/Inotify), to do this automatically. +As mentioned above, the tradeoff is that just adding new files to the +source directory does *not* add them to the build automatically. To +add them you need to tell Meson to reinitialize itself. The simplest +way is to touch the `meson.build` file in your source root. Then Meson +will reconfigure itself next time the build command is run. Advanced +users can even write a small background script that utilizes a +filesystem event queue, such as +[inotify](https://en.wikipedia.org/wiki/Inotify), to do this +automatically. ## Should I use `subdir` or `subproject`? -The answer is almost always `subdir`. Subproject exists for a very specific use case: embedding external dependencies into your build process. As an example, suppose we are writing a game and wish to use SDL. Let us further suppose that SDL comes with a Meson build definition. Let us suppose even further that we don't want to use prebuilt binaries but want to compile SDL for ourselves. +The answer is almost always `subdir`. Subproject exists for a very +specific use case: embedding external dependencies into your build +process. As an example, suppose we are writing a game and wish to use +SDL. Let us further suppose that SDL comes with a Meson build +definition. Let us suppose even further that we don't want to use +prebuilt binaries but want to compile SDL for ourselves. -In this case you would use `subproject`. The way to do it would be to grab the source code of SDL and put it inside your own source tree. Then you would do `sdl = subproject('sdl')`, which would cause Meson to build SDL as part of your build and would then allow you to link against it or do whatever else you may prefer. +In this case you would use `subproject`. The way to do it would be to +grab the source code of SDL and put it inside your own source +tree. Then you would do `sdl = subproject('sdl')`, which would cause +Meson to build SDL as part of your build and would then allow you to +link against it or do whatever else you may prefer. -For every other use you would use `subdir`. As an example, if you wanted to build a shared library in one dir and link tests against it in another dir, you would do something like this: +For every other use you would use `subdir`. As an example, if you +wanted to build a shared library in one dir and link tests against it +in another dir, you would do something like this: ```meson project('simple', 'c') @@ -92,27 +147,53 @@ subdir('tests') # test binaries would link against the library here ## Why is there not a Make backend? -Because Make is slow. This is not an implementation issue, Make simply can not be made fast. For further info we recommend you read [this post](http://neugierig.org/software/chromium/notes/2011/02/ninja.html) by Evan Martin, the author of Ninja. Makefiles also have a syntax that is very unpleasant to write which makes them a big maintenance burden. +Because Make is slow. This is not an implementation issue, Make simply +can not be made fast. For further info we recommend you read [this +post](http://neugierig.org/software/chromium/notes/2011/02/ninja.html) +by Evan Martin, the author of Ninja. Makefiles also have a syntax that +is very unpleasant to write which makes them a big maintenance burden. -The only reason why one would use Make instead of Ninja is working on a platform that does not have a Ninja port. Even in this case it is an order of magnitude less work to port Ninja than it is to write a Make backend for Meson. +The only reason why one would use Make instead of Ninja is working on +a platform that does not have a Ninja port. Even in this case it is an +order of magnitude less work to port Ninja than it is to write a Make +backend for Meson. Just use Ninja, you'll be happier that way. I guarantee it. ## Why is Meson not just a Python module so I could code my build setup in Python? -A related question to this is *Why is Meson's configuration language not Turing-complete?* +A related question to this is *Why is Meson's configuration language +not Turing-complete?* -There are many good reasons for this, most of which are summarized on this web page: [Against The Use Of Programming Languages in Configuration Files](https://taint.org/2011/02/18/001527a.html). +There are many good reasons for this, most of which are summarized on +this web page: [Against The Use Of Programming Languages in +Configuration Files](https://taint.org/2011/02/18/001527a.html). -In addition to those reasons, not exposing Python or any other "real" programming language makes it possible to port Meson's implementation to a different language. This might become necessary if, for example, Python turns out to be a performance bottleneck. This is an actual problem that has caused complications for GNU Autotools and SCons. +In addition to those reasons, not exposing Python or any other "real" +programming language makes it possible to port Meson's implementation +to a different language. This might become necessary if, for example, +Python turns out to be a performance bottleneck. This is an actual +problem that has caused complications for GNU Autotools and SCons. ## How do I do the equivalent of Libtools export-symbol and export-regex? -Either by using [GCC symbol visibility](https://gcc.gnu.org/wiki/Visibility) or by writing a [linker script](https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html). This has the added benefit that your symbol definitions are in a standalone file instead of being buried inside your build definitions. An example can be found [here](https://github.com/jpakkane/meson/tree/master/test%20cases/linuxlike/3%20linker%20script). +Either by using [GCC symbol +visibility](https://gcc.gnu.org/wiki/Visibility) or by writing a +[linker +script](https://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html). This +has the added benefit that your symbol definitions are in a standalone +file instead of being buried inside your build definitions. An example +can be found +[here](https://github.com/jpakkane/meson/tree/master/test%20cases/linuxlike/3%20linker%20script). ## My project works fine on Linux and MinGW but fails with MSVC due to a missing .lib file -With GCC, all symbols on shared libraries are exported automatically unless you specify otherwise. With MSVC no symbols are exported by default. If your shared library exports no symbols, MSVC will silently not produce an import library file leading to failures. The solution is to add symbol visibility definitions [as specified in GCC wiki](https://gcc.gnu.org/wiki/Visibility). +With GCC, all symbols on shared libraries are exported automatically +unless you specify otherwise. With MSVC no symbols are exported by +default. If your shared library exports no symbols, MSVC will silently +not produce an import library file leading to failures. The solution +is to add symbol visibility definitions [as specified in GCC +wiki](https://gcc.gnu.org/wiki/Visibility). ## I added some compiler flags and now the build fails with weird errors. What is happening? @@ -123,7 +204,13 @@ executable('foobar', ... c_args : '-some_arg -other_arg') ``` -Meson is *explicit*. In this particular case it will **not** automatically split your strings at whitespaces, instead it will take it as is and work extra hard to pass it to the compiler unchanged, including quoting it properly over shell invocations. This is mandatory to make e.g. files with spaces in them work flawlessly. To pass multiple command line arguments, you need to explicitly put them in an array like this: +Meson is *explicit*. In this particular case it will **not** +automatically split your strings at whitespaces, instead it will take +it as is and work extra hard to pass it to the compiler unchanged, +including quoting it properly over shell invocations. This is +mandatory to make e.g. files with spaces in them work flawlessly. To +pass multiple command line arguments, you need to explicitly put them +in an array like this: ```meson executable('foobar', ... @@ -138,20 +225,66 @@ You probably had a project that looked something like this: project('foobar', 'cpp') ``` -This defaults to `c++11` on GCC compilers. Suppose you want to use `c++14` instead, so you change the definition to this: +This defaults to `c++11` on GCC compilers. Suppose you want to use +`c++14` instead, so you change the definition to this: ```meson project('foobar', 'cpp', default_options : ['cpp_std=c++14']) ``` -But when you recompile, it still uses `c++11`. The reason for this is that default options are only looked at when you are setting up a build directory for the very first time. After that the setting is considered to have a value and thus the default value is ignored. To change an existing build dir to `c++14`, either reconfigure your build dir with `meson configure` or delete the build dir and recreate it from scratch. +But when you recompile, it still uses `c++11`. The reason for this is +that default options are only looked at when you are setting up a +build directory for the very first time. After that the setting is +considered to have a value and thus the default value is ignored. To +change an existing build dir to `c++14`, either reconfigure your build +dir with `meson configure` or delete the build dir and recreate it +from scratch. + +The reason we don't automatically change the option value when the +default is changed is that it is impossible to know to do that +reliably. The actual question that we need to solve is "if the +option's value is foo and the default value is bar, should we change +the option value to bar also". There are many choices: + + - if the user has changed the value themselves from the default, then + we must not change it back + + - if the user has not changed the value, but changes the default + value, then this section's premise would seem to indicate that the + value should be changed + + - suppose the user changes the value from the default to foo, then + back to bar and then changes the default value to bar, the correct + step to take is ambiguous by itself + +In order to solve the latter question we would need to remember not +only the current and old value, but also all the times the user has +changed the value and from which value to which other value. Since +people don't remember their own actions that far back, toggling +between states based on long history would be confusing. + +Because of this we do the simple an understandable thing: default +values are only defaults and will never affect the value of an option +once set. ## Does wrap download sources behind my back? -It does not. In order for Meson to download anything from the net while building, two conditions must be met. - -First of all there needs to be a `.wrap` file with a download URL in the `subprojects` directory. If one does not exist, Meson will not download anything. - -The second requirement is that there needs to be an explicit subproject invocation in your `meson.build` files. Either `subproject('foobar')` or `dependency('foobar', fallback : ['foobar', 'foo_dep'])`. If these declarations either are not in any build file or they are not called (due to e.g. `if/else`) then nothing is downloaded. - -If this is not sufficient for you, starting from release 0.40.0 Meson has a option called `wrap-mode` which can be used to disable wrap downloads altogether with `--wrap-mode=nodownload`. You can also disable dependency fallbacks altogether with `--wrap-mode=nofallback`, which also implies the `nodownload` option. +It does not. In order for Meson to download anything from the net +while building, two conditions must be met. + +First of all there needs to be a `.wrap` file with a download URL in +the `subprojects` directory. If one does not exist, Meson will not +download anything. + +The second requirement is that there needs to be an explicit +subproject invocation in your `meson.build` files. Either +`subproject('foobar')` or `dependency('foobar', fallback : ['foobar', +'foo_dep'])`. If these declarations either are not in any build file +or they are not called (due to e.g. `if/else`) then nothing is +downloaded. + +If this is not sufficient for you, starting from release 0.40.0 Meson +has a option called `wrap-mode` which can be used to disable wrap +downloads altogether with `--wrap-mode=nodownload`. You can also +disable dependency fallbacks altogether with `--wrap-mode=nofallback`, +which also implies the `nodownload` option. diff --git a/docs/markdown/Generating-sources.md b/docs/markdown/Generating-sources.md index a160095..cbe6c0d 100644 --- a/docs/markdown/Generating-sources.md +++ b/docs/markdown/Generating-sources.md @@ -4,23 +4,32 @@ short-description: Generation of source files before compilation # Generating sources - Sometimes source files need to be preprocessed before they are passed to the actual compiler. As an example you might want build an IDL compiler and then run some files through that to generate actual source files. In Meson this is done with [`generator()`](Reference-manual.md#generator) or [`custom_target()`](Reference-manual.md#custom_target). +Sometimes source files need to be preprocessed before they are passed +to the actual compiler. As an example you might want build an IDL +compiler and then run some files through that to generate actual +source files. In Meson this is done with +[`generator()`](Reference-manual.md#generator) or +[`custom_target()`](Reference-manual.md#custom_target). ## Using custom_target() -Let's say you have a build target that must be built using sources generated by a compiler. The compiler can either be a built target: +Let's say you have a build target that must be built using sources +generated by a compiler. The compiler can either be a built target: ```meson mycomp = executable('mycompiler', 'compiler.c') ``` -Or an external program provided by the system, or script inside the source tree: +Or an external program provided by the system, or script inside the +source tree: ```meson mycomp = find_program('mycompiler') ``` -Custom targets can take zero or more input files and use them to generate one or more output files. Using a custom target, you can run this compiler at build time to generate the sources: +Custom targets can take zero or more input files and use them to +generate one or more output files. Using a custom target, you can run +this compiler at build time to generate the sources: ```meson gen_src = custom_target('gen-output', @@ -31,7 +40,9 @@ gen_src = custom_target('gen-output', '--h-out', '@OUTPUT1@']) ``` -The `@INPUT@` there will be transformed to `'somefile1.c' 'file2.c'`. Just like the output, you can also refer to each input file individually by index. +The `@INPUT@` there will be transformed to `'somefile1.c' +'file2.c'`. Just like the output, you can also refer to each input +file individually by index. Then you just put that in your program and you're done. @@ -41,11 +52,21 @@ executable('program', 'main.c', gen_src) ## Using generator() -Generators are similar to custom targets, except that we define a *generator*, which defines how to transform an input file into one or more output files, and then use that on as many input files as we want. +Generators are similar to custom targets, except that we define a +*generator*, which defines how to transform an input file into one or +more output files, and then use that on as many input files as we +want. -Note that generators should only be used for outputs that will only be used as inputs for a build target or a custom target. When you use the processed output of a generator in multiple targets, the generator will be run multiple times to create outputs for each target. Each output will be created in a target-private directory `@BUILD_DIR@`. +Note that generators should only be used for outputs that will only be +used as inputs for a build target or a custom target. When you use the +processed output of a generator in multiple targets, the generator +will be run multiple times to create outputs for each target. Each +output will be created in a target-private directory `@BUILD_DIR@`. -If you want to generate files for general purposes such as for generating headers to be used by several sources, or data that will be installed, and so on, use a [`custom_target()`](Reference-manual.md#custom_target) instead. +If you want to generate files for general purposes such as for +generating headers to be used by several sources, or data that will be +installed, and so on, use a +[`custom_target()`](Reference-manual.md#custom_target) instead. ```meson @@ -54,9 +75,23 @@ gen = generator(mycomp, arguments : ['@INPUT@', '@OUTPUT@']) ``` -The first argument is the executable file to run. The next file specifies a name generation rule. It specifies how to build the output file name for a given input name. `@BASENAME@` is a placeholder for the input file name without preceding path or suffix (if any). So if the input file name were `some/path/filename.idl`, then the output name would be `filename.c`. You can also use `@PLAINNAME@`, which preserves the suffix which would result in a file called `filename.idl.c`. The last line specifies the command line arguments to pass to the executable. `@INPUT@` and `@OUTPUT@` are placeholders for the input and output files, respectively, and will be automatically filled in by Meson. If your rule produces multiple output files and you need to pass them to the command line, append the location to the output holder like this: `@OUTPUT0@`, `@OUTPUT1@` and so on. - -With this rule specified we can generate source files and add them to a target. +The first argument is the executable file to run. The next file +specifies a name generation rule. It specifies how to build the output +file name for a given input name. `@BASENAME@` is a placeholder for +the input file name without preceding path or suffix (if any). So if +the input file name were `some/path/filename.idl`, then the output +name would be `filename.c`. You can also use `@PLAINNAME@`, which +preserves the suffix which would result in a file called +`filename.idl.c`. The last line specifies the command line arguments +to pass to the executable. `@INPUT@` and `@OUTPUT@` are placeholders +for the input and output files, respectively, and will be +automatically filled in by Meson. If your rule produces multiple +output files and you need to pass them to the command line, append the +location to the output holder like this: `@OUTPUT0@`, `@OUTPUT1@` and +so on. + +With this rule specified we can generate source files and add them to +a target. ```meson gen_src = gen.process('input1.idl', 'input2.idl') @@ -67,19 +102,32 @@ Generators can also generate multiple output files with unknown names: ```meson gen2 = generator(someprog, - outputs : ['@BASENAME@.c', '@BASENAME@.h'], + output : ['@BASENAME@.c', '@BASENAME@.h'], arguments : ['--out_dir=@BUILD_DIR@', '@INPUT@']) ``` -In this case you can not use the plain `@OUTPUT@` variable, as it would be ambiguous. This program only needs to know the output directory, it will generate the file names by itself. - -To make passing different additional arguments to the generator program at each use possible, you can use the `@EXTRA_ARGS@` string in the `arguments` list. Note that this placeholder can only be present as a whole string, and not as a substring. The main reason is that it represents a list of strings, which may be empty, or contain multiple elements; and in either case, interpolating it into the middle of a single string would be troublesome. If there are no extra arguments passed in from a `process()` invocation, the placeholder is entirely omitted from the actual list of arguments, so an empty string won't be passed to the generator program because of this. If there are multiple elements in `extra_args`, they are inserted into to the actual argument list as separate elements. +In this case you can not use the plain `@OUTPUT@` variable, as it +would be ambiguous. This program only needs to know the output +directory, it will generate the file names by itself. + +To make passing different additional arguments to the generator +program at each use possible, you can use the `@EXTRA_ARGS@` string in +the `arguments` list. Note that this placeholder can only be present +as a whole string, and not as a substring. The main reason is that it +represents a list of strings, which may be empty, or contain multiple +elements; and in either case, interpolating it into the middle of a +single string would be troublesome. If there are no extra arguments +passed in from a `process()` invocation, the placeholder is entirely +omitted from the actual list of arguments, so an empty string won't be +passed to the generator program because of this. If there are multiple +elements in `extra_args`, they are inserted into to the actual +argument list as separate elements. ```meson gen3 = generator(genprog, output : '@BASENAME@.cc', arguments : ['@INPUT@', '@EXTRA_ARGS@', '@OUTPUT@']) -gen3_src1 = gen3.process('input1.y) +gen3_src1 = gen3.process('input1.y') gen3_src2 = gen3.process('input2.y', extra_args: '--foo') gen3_src3 = gen3.process('input3.y', extra_args: ['--foo', '--bar']) ``` diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index fbf9530..ad3715e 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -123,7 +123,9 @@ Returns an array of two elements which are: `[c_source, header_file]` ### gnome.mkenums() Generates enum files for GObject using the `glib-mkenums` tool. The -first argument is the base name of the output files. +first argument is the base name of the output files, unless `c_template` +and `h_template` are specified. In this case, the output files will be +the base name of the values passed as templates. This method is essentially a wrapper around the `glib-mkenums` tool's command line API. It is the most featureful method for enum creation. diff --git a/docs/markdown/IDE-integration.md b/docs/markdown/IDE-integration.md index f7939dd..f608c5c 100644 --- a/docs/markdown/IDE-integration.md +++ b/docs/markdown/IDE-integration.md @@ -6,7 +6,7 @@ short-description: Meson's API to integrate Meson support into an IDE Meson has exporters for Visual Studio and XCode, but writing a custom backend for every IDE out there is not a scalable approach. To solve this problem, Meson provides an API that makes it easy for any IDE or build tool to integrate Meson builds and provide an experience comparable to a solution native to the IDE. -The basic tool for this is a script called `mesonintrospect.py`. Some distro packages might not expose this script in the regular path, and in this case you need to execute it from the install directory. +The basic tool for this is `meson introspect`. The first thing to do when setting up a Meson project in an IDE is to select the source and build directories. For this example we assume that the source resides in an Eclipse-like directory called `workspace/project` and the build tree is nested inside it as `workspace/project/build`. First we initialise Meson by running the following command in the source directory. @@ -16,13 +16,13 @@ For the remainder of the document we assume that all commands are executed insid The first thing you probably want is to get a list of top level targets. For that we use the introspection tool. It comes with extensive command line help so we recommend using that in case problems appear. - mesonintrospect.py --targets + meson introspect --targets The JSON formats will not be specified in this document. The easiest way of learning them is to look at sample output from the tool. Once you have a list of targets, you probably need the list of source files that comprise the target. To get this list for a target, say `exampletarget`, issue the following command. - mesonintrospect.py --target-files exampletarget + meson introspect --target-files exampletarget In order to make code completion work, you need the compiler flags for each compilation step. Meson does not provide this itself, but the Ninja tool Meson uses to build does provide it. To find out the compile steps necessary to build target foo, issue the following command. @@ -32,7 +32,7 @@ Note that if the target has dependencies (such as generated sources), then the c The next thing to display is the list of options that can be set. These include build type and so on. Here's how to extract them. - mesonintrospect.py --buildoptions + meson introspect --buildoptions To set the options, use the `meson configure` command. @@ -40,6 +40,6 @@ Compilation and unit tests are done as usual by running the `ninja` and `ninja t When these tests fail, the user probably wants to run the failing test in a debugger. To make this as integrated as possible, extract the test test setups with this command. - mesonintrospect.py --tests + meson introspect --tests This provides you with all the information needed to run the test: what command to execute, command line arguments and environment variable settings. diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 4dc87c9..589baf1 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -267,6 +267,8 @@ keyword arguments. - `include_directories`, the directories to add to header search path - `link_args`, link arguments to use - `link_with`, libraries to link against + - `link_whole`, libraries to link fully, same as [`executable`](#executable) + Since 0.46.0 - `sources`, sources to add to targets (or generated header files that should be built before sources including them are built) - `version`, the version of this dependency, such as `1.2.3` @@ -433,7 +435,7 @@ be passed to [shared and static libraries](#library). - `install_dir` override install directory for this file. The value is relative to the `prefix` specified. F.ex, if you want to install plugins into a subdir, you'd use something like this: `install_dir : - get_option('libdir') + '/projectname-1.0'`. + join_paths(get_option('libdir'), 'projectname-1.0'`). - `install_rpath` a string to set the target's rpath to after install (but *not* before that) - `objects` list of prebuilt object files (usually for third party @@ -580,7 +582,7 @@ the following special substitutions: - `@PLAINNAME@`: the complete input file name, e.g: `foo.c` becomes `foo.c` (unchanged) - `@BASENAME@`: the base of the input filename, e.g.: `foo.c.y` becomes `foo.c` (extension is removed) -Each string passed to the `outputs` keyword argument *must* be +Each string passed to the `output` keyword argument *must* be constructed using one or both of these two substitutions. In addition to the above substitutions, the `arguments` keyword @@ -613,8 +615,13 @@ Obtains the value of the [project build option](Build-options.md) specified in t Note that the value returned for built-in options that end in `dir` such as `bindir` and `libdir` is always a path relative to (and inside) the `prefix`. + The only exceptions are: `sysconfdir`, `localstatedir`, and `sharedstatedir` -which will return the value passed during configuration as-is. +which will return the value passed during configuration as-is, which may be +absolute, or relative to `prefix`. [`install_dir` arguments](Installing.md) +handles that as expected, but if you need the absolute path to one of these +e.g. to use in a define etc., you should use `join_paths(get_option('prefix'), +get_option('localstatedir')))` ### get_variable() @@ -661,6 +668,10 @@ Note that this function call itself does not add the directories into the search path, since there is no global search path. For something like that, see [`add_project_arguments()`](#add_project_arguments). +See also `implicit_include_directories` parameter of +[executable()](#executable), which adds current source and build directories +to include path. + Each directory given is converted to two include paths: one that is relative to the source root and one relative to the build root. @@ -976,14 +987,20 @@ Project supports the following keyword arguments. runresult run_command(command, list_of_args) ``` -Runs the command specified in positional arguments. Returns [an opaque -object](#run-result-object) containing the result of the -invocation. The script is run from an *unspecified* directory, and +Runs the command specified in positional arguments. +`command` can be a string, or the output of [`find_program()`](#find_program), +[`files()`](#files) or [`configure_file()`](#configure_file), or +[a compiler object](#compiler-object). + +Returns [an opaque object](#run-result-object) containing the result of the +invocation. The command is run from an *unspecified* directory, and Meson will set three environment variables `MESON_SOURCE_ROOT`, `MESON_BUILD_ROOT` and `MESON_SUBDIR` that specify the source directory, build directory and subdirectory the target was defined in, respectively. +See also [External commands](External-commands.md). + ### run_target ``` meson @@ -1134,6 +1151,8 @@ subproject. However, if you want to use a dependency object from inside a subproject, an easier way is to use the `fallback:` keyword argument to [`dependency()`](#dependency). +[See additional documentation](Subprojects.md). + ### test() ``` meson @@ -1191,10 +1210,18 @@ be up to date on every build. Keywords are similar to `custom_target`. Meson will read the contents of `input`, substitute the `replace_string` with the detected revision number, and write the -result to `output`. This method returns an opaque -[`custom_target`](#custom_target) object that can be used as -source. If you desire more specific behavior than what this command -provides, you should use `custom_target`. +result to `output`. This method returns a +[`custom_target`](#custom_target) object that (as usual) should be +used to signal dependencies if other targets use the file outputted +by this. + +For example, if you generate a header with this and want to use that in +a build target, you must add the return value to the sources of that +build target. Without that, Meson will not know the order in which to +build the targets. + +If you desire more specific behavior than what this command provides, +you should use `custom_target`. ## Built-in objects @@ -1780,7 +1807,7 @@ opaque object representing it. - `get_variable(name)` fetches the specified variable from inside the subproject. This is useful to, for instance, get a [declared - dependency](#declare_dependency) from the subproject. + dependency](#declare_dependency) from the [subproject](Subprojects.md). ### `run result` object diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 5ee0db1..7611232 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -63,3 +63,21 @@ These are provided by the `.system()` method call. Any string not listed above is not guaranteed to remain stable in future releases. + + +## Language arguments parameter names + +These are the parameter names for passing language specific arguments to your build target. + +| Language | Parameter name | +| ----- | ----- | +| C | c_args | +| C++ | cpp_args | +| C# | cs_args | +| D | d_args | +| Fortran | fortran_args | +| Java | java_args | +| Objective C | objc_args | +| Objective C++ | objcpp_args | +| Rust | rust_args | +| Vala | vala_args | diff --git a/docs/markdown/Release-notes-for-0.45.0.md b/docs/markdown/Release-notes-for-0.45.0.md index b3df71c..6b24183 100644 --- a/docs/markdown/Release-notes-for-0.45.0.md +++ b/docs/markdown/Release-notes-for-0.45.0.md @@ -1,16 +1,194 @@ --- title: Release 0.45 -short-description: Release notes for 0.45 (preliminary) +short-description: Release notes for 0.45 ... # New features -This page is a placeholder for the eventual release notes. +## Python minimum version is now 3.5 -Notable new features should come with release note updates. This is -done by creating a file snippet called `snippets/featurename.md` and -whose contents should look like this: +Meson will from this version on require Python version 3.5 or newer. - ## Feature name +## Config-Tool based dependencies can be specified in a cross file - A short description explaining the new feature and how it should be used. +Tools like LLVM and pcap use a config tool for dependencies, this is a +script or binary that is run to get configuration information (cflags, +ldflags, etc) from. + +These binaries may now be specified in the `binaries` section of a +cross file. + +```dosini +[binaries] +cc = ... +llvm-config = '/usr/bin/llvm-config32' +``` + +## Visual Studio C# compiler support + +In addition to the Mono C# compiler we also support Visual Studio's C# +compiler. Currently this is only supported on the Ninja backend. + +## Removed two deprecated features + +The standalone `find_library` function has been a no-op for a long +time. Starting with this version it becomes a hard error. + +There used to be a keywordless version of `run_target` which looked +like this: + + run_target('targetname', 'command', 'arg1', 'arg2') + +This is now an error. The correct format for this is now: + + run_target('targetname', + command : ['command', 'arg1', 'arg2']) + +## Experimental FPGA support + +This version adds support for generating, analysing and uploading FPGA +programs using the [IceStorm +toolchain](http://www.clifford.at/icestorm/). This support is +experimental and is currently limited to the `iCE 40` series of FPGA +chips. + +FPGA generation integrates with other parts of Meson seamlessly. As an +example, [here](https://github.com/jpakkane/lm32) is an example +project that compiles a simple firmware into Verilog and combines that +with an lm32 softcore processor. + +## Generator outputs can preserve directory structure + +Normally when generating files with a generator, Meson flattens the +input files so they all go in the same directory. Some code +generators, such as Protocol Buffers, require that the generated files +have the same directory layout as the input files used to generate +them. This can now be achieved like this: + +```meson +g = generator(...) # Compiles protobuf sources +generated = gen.process('com/mesonbuild/one.proto', + 'com/mesonbuild/two.proto', + preserve_path_from : meson.current_source_dir()) +``` + +This would cause the following files to be generated inside the target +private directory: + + com/mesonbuild/one.pb.h + com/mesonbuild/one.pb.cc + com/mesonbuild/two.pb.h + com/mesonbuild/two.pb.cc + +## Hexadecimal string literals + +Hexadecimal integer literals can now be used in build and option files. + + int_255 = 0xFF + +## b_ndebug : if-release + +The value `if-release` can be given for the `b_ndebug` project option. + +This will make the `NDEBUG` pre-compiler macro to be defined for release +type builds as if the `b_ndebug` project option had had the value `true` +defined for it. + +## `install_data()` defaults to `{datadir}/{projectname}` + +If `install_data()` is not given an `install_dir` keyword argument, the +target directory defaults to `{datadir}/{projectname}` (e.g. +`/usr/share/myproj`). + +## install_subdir() supports strip_directory + +If strip_directory=true install_subdir() installs directory contents +instead of directory itself, stripping basename of the source directory. + +## Integer options + +There is a new integer option type with optional minimum and maximum +values. It can be specified like this in the `meson_options.txt` file: + + option('integer_option', type : 'integer', min : 0, max : 5, value : 3) + +## New method meson.project_license() + +The `meson` builtin object now has a `project_license()` method that +returns a list of all licenses for the project. + +## Rust cross-compilation + +Cross-compilation is now supported for Rust targets. Like other +cross-compilers, the Rust binary must be specified in your cross +file. It should specify a `--target` (as installed by `rustup target`) +and a custom linker pointing to your C cross-compiler. For example: + +``` +[binaries] +c = '/usr/bin/arm-linux-gnueabihf-gcc-7' +rust = [ + 'rustc', + '--target', 'arm-unknown-linux-gnueabihf', + '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7', +] +``` + +## Rust compiler-private library disambiguation + +When building a Rust target with Rust library dependencies, an +`--extern` argument is now specified to avoid ambiguity between the +dependency library, and any crates of the same name in `rustc`'s +private sysroot. + +## Project templates + +Meson ships with predefined project templates. To start a new project from +scratch, simply go to an empty directory and type: + +```meson +meson init --name=myproject --type=executable --language=c +``` + +## Improve test setup selection + +Test setups are now identified (also) by the project they belong to +and it is possible to select the used test setup from a specific +project. E.g. to use a test setup `some_setup` from project +`some_project` for all executed tests one can use + + meson test --setup some_project:some_setup + +Should one rather want test setups to be used from the same project as +where the current test itself has been defined, one can use just + + meson test --setup some_setup + +In the latter case every (sub)project must have a test setup `some_setup` +defined in it. + +## Can use custom targets as Windows resource files + +The `compile_resources()` function of the `windows` module can now be used on custom targets as well as regular files. +# Can promote dependencies with wrap command + +The `promote` command makes it easy to copy nested dependencies to the top level. + + meson wrap promote scommon + +This will search the project tree for a subproject called `scommon` +and copy it to the top level. + +If there are many embedded subprojects with the same name, you have to +specify which one to promote manually like this: + + meson wrap promote subprojects/s1/subprojects/scommon + +## Yielding subproject option to superproject + +Normally project options are specific to the current project. However +sometimes you want to have an option whose value is the same over all +projects. This can be achieved with the new `yield` keyword for +options. When set to `true`, getting the value of this option in +`meson.build` files gets the value from the option with the same name +in the master project (if such an option exists). diff --git a/docs/markdown/Release-notes-for-0.46.0.md b/docs/markdown/Release-notes-for-0.46.0.md new file mode 100644 index 0000000..395a94d --- /dev/null +++ b/docs/markdown/Release-notes-for-0.46.0.md @@ -0,0 +1,16 @@ +--- +title: Release 0.46 +short-description: Release notes for 0.46 (preliminary) +... + +# New features + +This page is a placeholder for the eventual release notes. + +Notable new features should come with release note updates. This is +done by creating a file snippet called `snippets/featurename.md` and +whose contents should look like this: + + ## Feature name + + A short description explaining the new feature and how it should be used. diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md index 0d1442e..ad2aae2 100644 --- a/docs/markdown/Subprojects.md +++ b/docs/markdown/Subprojects.md @@ -77,3 +77,27 @@ subproject `b` and have `b` also use `a`. Meson ships with a dependency system to automatically obtain dependency subprojects. It is documented in the [Wrap dependency system manual](Wrap-dependency-system-manual.md). + +# Why must all subprojects be inside a single directory? + +There are several reasons. + +First of all, to maintain any sort of sanity, the system must prevent going +inside other subprojects with `subdir()` or variations thereof. Having the +subprojects in well defined places makes this easy. If subprojects could be +anywhere at all, it would be a lot harder. + +Second of all it is extremely important that end users can easily see what +subprojects any project has. Because they are in one, and only one, place, +reviewing them becomes easy. + +This is also a question of convention. Since all Meson projects have the same +layout w.r.t subprojects, switching between projects becomes easier. You don't +have to spend time on a new project traipsing through the source tree looking +for subprojects. They are always in the same place. + +Finally if you can have subprojects anywhere, this increases the possibility of +having many different (possibly incompatible) versions of a dependency in your +source tree. Then changing some code (such as changing the order you traverse +directories) may cause a completely different version of the subproject to be +used by accident. diff --git a/docs/markdown/index.md b/docs/markdown/index.md index 6893564..cffd488 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -33,6 +33,16 @@ developers. The first one is the mailing list, which is hosted at The second way is via IRC. The channel to use is `#mesonbuild` at [Freenode](https://freenode.net/). +### [Projects using Meson](http://mesonbuild.com/Users.html) + +Many projects out there are using Meson and their communities are also +a great resource for learning about what (and what not too!) do when +trying to convert to using Meson. + +[A short list of Meson users can be found here](http://mesonbuild.com/Users.html) +but there are many more. We would love to hear about your success +stories too and how things could be improved too! + ## Development All development on Meson is done on the [GitHub diff --git a/docs/markdown/snippets/altered-logging.md b/docs/markdown/snippets/altered-logging.md new file mode 100644 index 0000000..4ff9bb0 --- /dev/null +++ b/docs/markdown/snippets/altered-logging.md @@ -0,0 +1,5 @@ +## Log output slightly changed + +The format of some human-readable diagnostic messages has changed in +minor ways. In case you are parsing these messages, you may need to +adjust your code. diff --git a/docs/markdown/snippets/compiler-object-run_command.md b/docs/markdown/snippets/compiler-object-run_command.md new file mode 100644 index 0000000..0308416 --- /dev/null +++ b/docs/markdown/snippets/compiler-object-run_command.md @@ -0,0 +1,10 @@ +## Compiler object can now be passed to run_command() + +This can be used to run the current compiler with the specified arguments +to obtain additional information from it. +One of the use cases is to get the location of development files for the +GCC plugins: + + cc = meson.get_compiler('c') + result = run_command(cc, '-print-file-name=plugin') + plugin_dev_path = result.stdout().strip() diff --git a/docs/markdown/snippets/config-tool-cross.md b/docs/markdown/snippets/config-tool-cross.md deleted file mode 100644 index 1102481..0000000 --- a/docs/markdown/snippets/config-tool-cross.md +++ /dev/null @@ -1,13 +0,0 @@ -# Config-Tool based dependencies can be specified in a cross file - -Tools like LLVM and pcap use a config tool for dependencies, this is a script -or binary that is run to get configuration information (cflags, ldflags, etc) -from. - -These binaries may now be specified in the `binaries` section of a cross file. - -```dosini -[binaries] -cc = ... -llvm-config = '/usr/bin/llvm-config32' -``` diff --git a/docs/markdown/snippets/declare_dependency-link_whole.md b/docs/markdown/snippets/declare_dependency-link_whole.md new file mode 100644 index 0000000..827b1f6 --- /dev/null +++ b/docs/markdown/snippets/declare_dependency-link_whole.md @@ -0,0 +1,4 @@ +## declare_dependency() supports link_whole + +`declare_dependency()` supports `link_whole` parameter. +`link_whole` propagates to build target that uses dependency. diff --git a/docs/markdown/snippets/deprecations.md b/docs/markdown/snippets/deprecations.md deleted file mode 100644 index adab2e6..0000000 --- a/docs/markdown/snippets/deprecations.md +++ /dev/null @@ -1,14 +0,0 @@ -## Removed two deprecated features - -The standalone `find_library` function has been a no-op for a long -time. Starting with this version it becomes a hard error. - -There used to be a keywordless version of `run_target` which looked -like this: - - run_target('targetname', 'command', 'arg1', 'arg2') - -This is now an error. The correct format for this is now: - - run_target('targetname', - command : ['command', 'arg1', 'arg2']) diff --git a/docs/markdown/snippets/fpga.md b/docs/markdown/snippets/fpga.md deleted file mode 100644 index b5e4938..0000000 --- a/docs/markdown/snippets/fpga.md +++ /dev/null @@ -1,12 +0,0 @@ -## Experimental FPGA support - -This version adds support for generating, analysing and uploading FPGA -programs using the [IceStorm -toolchain](http://www.clifford.at/icestorm/). This support is -experimental and is currently limited to the `iCE 40` series of FPGA -chips. - -FPGA generation integrates with other parts of Meson seamlessly. As an -example, [here](https://github.com/jpakkane/lm32) is an example -project that compiles a simple firmware into Verilog and combines that -with an lm32 softcore processor. diff --git a/docs/markdown/snippets/gen-subdirs.md b/docs/markdown/snippets/gen-subdirs.md deleted file mode 100644 index fdb5945..0000000 --- a/docs/markdown/snippets/gen-subdirs.md +++ /dev/null @@ -1,21 +0,0 @@ -## Generator outputs can preserve directory structure - -Normally when generating files with a generator, Meson flattens the -input files so they all go in the same directory. Some code -generators, such as Protocol Buffers, require that the generated files -have the same directory layout as the input files used to generate -them. This can now be achieved like this: - -```meson -g = generator(...) # Compiles protobuf sources -generated = gen.process('com/mesonbuild/one.proto', - 'com/mesonbuild/two.proto', - preserve_path_from : meson.current_source_dir()) - -This would cause the following files to be generated inside the target -private directory: - - com/mesonbuild/one.pb.h - com/mesonbuild/one.pb.cc - com/mesonbuild/two.pb.h - com/mesonbuild/two.pb.cc diff --git a/docs/markdown/snippets/hexnumbers.md b/docs/markdown/snippets/hexnumbers.md deleted file mode 100644 index 840c0cb..0000000 --- a/docs/markdown/snippets/hexnumbers.md +++ /dev/null @@ -1,5 +0,0 @@ -## Hexadecimal string literals - -Hexadecimal integer literals can now be used in build and option files. - - int_255 = 0xFF diff --git a/docs/markdown/snippets/if-release.md b/docs/markdown/snippets/if-release.md deleted file mode 100644 index 96e12ef..0000000 --- a/docs/markdown/snippets/if-release.md +++ /dev/null @@ -1,7 +0,0 @@ -## b_ndebug : if-release - -The value `if-release` can be given for the `b_ndebug` project option. - -This will make the `NDEBUG` pre-compiler macro to be defined for release -type builds as if the `b_ndebug` project option had had the value `true` -defined for it. diff --git a/docs/markdown/snippets/improved-help.md b/docs/markdown/snippets/improved-help.md new file mode 100644 index 0000000..db7e852 --- /dev/null +++ b/docs/markdown/snippets/improved-help.md @@ -0,0 +1,6 @@ +## "meson help" now shows command line help + +Command line parsing is now less surprising. "meson help" is now +equivalent to "meson --help" and "meson help <subcommand>" is +equivalent to "meson <subcommand> --help", instead of creating a build +directory called "help" in these cases. diff --git a/docs/markdown/snippets/improved-meson-init.md b/docs/markdown/snippets/improved-meson-init.md new file mode 100644 index 0000000..ec17bc4 --- /dev/null +++ b/docs/markdown/snippets/improved-meson-init.md @@ -0,0 +1,19 @@ +## Autogeneration of simple meson.build files + +A feature to generate a meson.build file compiling given C/C++ source +files into a single executable has been added to "meson init". By +default, it will take all recognizable source files in the current +directory. You can also specify a list of dependencies with the -d +flag and automatically invoke a build with the -b flag to check if the +code builds with those dependencies. + +For example, + +```meson +meson init -fbd sdl2,gl +``` + +will look for C or C++ files in the current directory, generate a +meson.build for them with the dependencies of sdl2 and gl and +immediately try to build it, overwriting any previous meson.build and +build directory. diff --git a/docs/markdown/snippets/install_subdir-strip_directory.md b/docs/markdown/snippets/install_subdir-strip_directory.md deleted file mode 100644 index 9ddb4a4..0000000 --- a/docs/markdown/snippets/install_subdir-strip_directory.md +++ /dev/null @@ -1,4 +0,0 @@ -## install_subdir() supports strip_directory - -If strip_directory=true install_subdir() installs directory contents -instead of directory itself, stripping basename of the source directory. diff --git a/docs/markdown/snippets/installdir.md b/docs/markdown/snippets/installdir.md deleted file mode 100644 index c709ffe..0000000 --- a/docs/markdown/snippets/installdir.md +++ /dev/null @@ -1,5 +0,0 @@ -## `install_data()` defaults to `{datadir}/{projectname}` - -If `install_data()` is not given an `install_dir` keyword argument, the -target directory defaults to `{datadir}/{projectname}` (e.g. -`/usr/share/myproj`). diff --git a/docs/markdown/snippets/intopt.md b/docs/markdown/snippets/intopt.md deleted file mode 100644 index daf660b..0000000 --- a/docs/markdown/snippets/intopt.md +++ /dev/null @@ -1,6 +0,0 @@ -## Integer options - -There is a new integer option type with optional minimum and maximum -values. It can be specified like this in the `meson_options.txt` file: - - option('integer_option', type : 'integer', min : 0, max : 5, value : 3) diff --git a/docs/markdown/snippets/project-license.md b/docs/markdown/snippets/project-license.md deleted file mode 100644 index 5da2c6a..0000000 --- a/docs/markdown/snippets/project-license.md +++ /dev/null @@ -1,4 +0,0 @@ -## New method meson.project_license() - -The `meson` builtin object now has a `project_license()` method that returns a -list of all licenses for the project. diff --git a/docs/markdown/snippets/rust-cross.md b/docs/markdown/snippets/rust-cross.md deleted file mode 100644 index 7f18c44..0000000 --- a/docs/markdown/snippets/rust-cross.md +++ /dev/null @@ -1,16 +0,0 @@ -## Rust cross-compilation - -Cross-compilation is now supported for Rust targets. Like other -cross-compilers, the Rust binary must be specified in your cross -file. It should specify a `--target` (as installed by `rustup target`) -and a custom linker pointing to your C cross-compiler. For example: - -``` -[binaries] -c = '/usr/bin/arm-linux-gnueabihf-gcc-7' -rust = [ - 'rustc', - '--target', 'arm-unknown-linux-gnueabihf', - '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7', -] -``` diff --git a/docs/markdown/snippets/rust-private-disambiguation.md b/docs/markdown/snippets/rust-private-disambiguation.md deleted file mode 100644 index 6988a7a..0000000 --- a/docs/markdown/snippets/rust-private-disambiguation.md +++ /dev/null @@ -1,6 +0,0 @@ -## Rust compiler-private library disambiguation - -When building a Rust target with Rust library dependencies, an -`--extern` argument is now specified to avoid ambiguity between the -dependency library, and any crates of the same name in `rustc`'s -private sysroot. diff --git a/docs/markdown/snippets/templates.md b/docs/markdown/snippets/templates.md deleted file mode 100644 index 6f0474d..0000000 --- a/docs/markdown/snippets/templates.md +++ /dev/null @@ -1,8 +0,0 @@ -## Project templates - -Meson ships with predefined project templates. To start a new project from -scratch, simply go to an empty directory and type: - -```meson -meson init --name=myproject --type=executable --language=c -``` diff --git a/docs/markdown/snippets/windows-resources-custom-targets.md b/docs/markdown/snippets/windows-resources-custom-targets.md deleted file mode 100644 index a2dce3a..0000000 --- a/docs/markdown/snippets/windows-resources-custom-targets.md +++ /dev/null @@ -1,3 +0,0 @@ -## Can use custom targets as Windows resource files - -The `compile_resources()` function of the `windows` module can now be used on custom targets as well as regular files. diff --git a/docs/markdown/snippets/wrap_promote.md b/docs/markdown/snippets/wrap_promote.md deleted file mode 100644 index 20fee47..0000000 --- a/docs/markdown/snippets/wrap_promote.md +++ /dev/null @@ -1,11 +0,0 @@ -# Can promote dependencies with wrap command - -The `promote` command makes it easy to copy nested dependencies to the top level. - - meson wrap promote scommon - -This will search the project tree for a subproject called `scommon` and copy it to the top level. - -If there are many embedded subprojects with the same name, you have to specify which one to promote manually like this: - - meson wrap promote subprojects/s1/subprojects/scommon diff --git a/docs/markdown/snippets/yield.md b/docs/markdown/snippets/yield.md deleted file mode 100644 index 3880e67..0000000 --- a/docs/markdown/snippets/yield.md +++ /dev/null @@ -1,8 +0,0 @@ -## Yielding subproject option to superproject - -Normally project options are specific to the current project. However -sometimes you want to have an option whose value is the same over all -projects. This can be achieved with the new `yield` keyword for -options. When set to `true`, getting the value of this option in -`meson.build` files gets the value from the option with the same name -in the master project (if such an option exists). diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 144ca4a..844b600 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -65,6 +65,7 @@ index.md Shipping-prebuilt-binaries-as-wraps.md fallback-wraptool.md Release-notes.md + Release-notes-for-0.46.0.md Release-notes-for-0.45.0.md Release-notes-for-0.44.0.md Release-notes-for-0.43.0.md diff --git a/man/meson.1 b/man/meson.1 index 4429fa2..19ad737 100644 --- a/man/meson.1 +++ b/man/meson.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "December 2017" "meson 0.44.0" "User Commands" +.TH MESON "1" "March 2018" "meson 0.45.0" "User Commands" .SH NAME meson - a high productivity build system .SH DESCRIPTION diff --git a/man/mesonconf.1 b/man/mesonconf.1 index 3a83473..b189663 100644 --- a/man/mesonconf.1 +++ b/man/mesonconf.1 @@ -1,4 +1,4 @@ -.TH MESONCONF "1" "December 2017" "mesonconf 0.44.0" "User Commands" +.TH MESONCONF "1" "March 2018" "mesonconf 0.45.0" "User Commands" .SH NAME mesonconf - a tool to configure Meson builds .SH DESCRIPTION diff --git a/man/mesonintrospect.1 b/man/mesonintrospect.1 index 27f39c0..61aa381 100644 --- a/man/mesonintrospect.1 +++ b/man/mesonintrospect.1 @@ -1,4 +1,4 @@ -.TH MESONINTROSPECT "1" "December 2017" "mesonintrospect 0.44.0" "User Commands" +.TH MESONINTROSPECT "1" "March 2017" "mesonintrospect 0.45.0" "User Commands" .SH NAME mesonintrospect - a tool to extract information about a Meson build .SH DESCRIPTION diff --git a/man/mesontest.1 b/man/mesontest.1 index d2b2743..9a9f743 100644 --- a/man/mesontest.1 +++ b/man/mesontest.1 @@ -1,4 +1,4 @@ -.TH MESON "1" "December 2017" "meson 0.44.0" "User Commands" +.TH MESON "1" "March 2018" "meson 0.45.0" "User Commands" .SH NAME mesontest - test tool for the Meson build system .SH DESCRIPTION diff --git a/man/wraptool.1 b/man/wraptool.1 index 113b33c..93ec457 100644 --- a/man/wraptool.1 +++ b/man/wraptool.1 @@ -1,4 +1,4 @@ -.TH WRAPTOOL "1" "December 2017" "meson 0.44.0" "User Commands" +.TH WRAPTOOL "1" "March 2018" "meson 0.45.0" "User Commands" .SH NAME wraptool - source dependency downloader .SH DESCRIPTION @@ -14,8 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -from mesonbuild import mesonmain, mesonlib -import sys, os, locale +from mesonbuild import mesonmain +import sys, os def main(): # Always resolve the command path so Ninja can find it for regen, tests, etc. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index b547bc3..a8e8164 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -65,9 +65,10 @@ class ExecutableSerialisation: self.capture = capture class TestSerialisation: - def __init__(self, name, suite, fname, is_cross_built, exe_wrapper, is_parallel, cmd_args, env, - should_fail, timeout, workdir, extra_paths): + def __init__(self, name, project, suite, fname, is_cross_built, exe_wrapper, is_parallel, + cmd_args, env, should_fail, timeout, workdir, extra_paths): self.name = name + self.project_name = project self.suite = suite self.fname = fname self.is_cross_built = is_cross_built @@ -603,9 +604,9 @@ class Backend: cmd_args.append(self.get_target_filename(a)) else: raise MesonException('Bad object in test command.') - ts = TestSerialisation(t.get_name(), t.suite, cmd, is_cross, exe_wrapper, - t.is_parallel, cmd_args, t.env, t.should_fail, - t.timeout, t.workdir, extra_paths) + ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross, + exe_wrapper, t.is_parallel, cmd_args, t.env, + t.should_fail, t.timeout, t.workdir, extra_paths) arr.append(ts) pickle.dump(arr, datafile) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index e8c8b39..c13720f 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -12,8 +12,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, pickle, re, shlex, subprocess, sys +import os, pickle, re, shlex, subprocess from collections import OrderedDict +import itertools from pathlib import PurePath from . import backends @@ -103,7 +104,8 @@ class NinjaBuildElement: # This is the only way I could find to make this work on all # platforms including Windows command shell. Slash is a dir separator # on Windows, too, so all characters are unambiguous and, more importantly, - # do not require quoting. + # do not require quoting, unless explicitely specified, which is necessary for + # the csc compiler. line = line.replace('\\', '/') outfile.write(line) @@ -114,7 +116,6 @@ class NinjaBuildElement: (name, elems) = e should_quote = name not in raw_names line = ' %s = ' % name - noq_templ = "%s" newelems = [] for i in elems: if not should_quote or i == '&&': # Hackety hack hack @@ -263,7 +264,7 @@ int dummy; vala_header = File.from_built_file(self.get_target_dir(target), target.vala_header) header_deps.append(vala_header) # Recurse and find generated headers - for dep in target.link_targets: + for dep in itertools.chain(target.link_targets, target.link_whole_targets): if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)): header_deps += self.get_generated_headers(dep) return header_deps @@ -654,11 +655,30 @@ int dummy; # Alias that runs the target defined above self.create_target_alias('meson-coverage-html', outfile) elem = NinjaBuildElement(self.all_outputs, os.path.join(htmloutdir, 'index.html'), 'CUSTOM_COMMAND', '') - command = [lcov_exe, '--directory', self.environment.get_build_dir(), - '--capture', '--output-file', covinfo, '--no-checksum', - '&&', genhtml_exe, '--prefix', self.environment.get_build_dir(), - '--output-directory', htmloutdir, '--title', 'Code coverage', - '--legend', '--show-details', covinfo] + + subproject_dir = self.build.get_subproject_dir() + command = [lcov_exe, + '--directory', self.environment.get_build_dir(), + '--capture', + '--output-file', covinfo, + '--no-checksum', + '&&', lcov_exe, + '--extract', + covinfo, + os.path.join(self.environment.get_source_dir(), '*'), + '--output-file', covinfo, + '&&', lcov_exe, + '--remove', + covinfo, + os.path.join(self.environment.get_source_dir(), subproject_dir, '*'), + '--output-file', covinfo, + '&&', genhtml_exe, + '--prefix', self.environment.get_build_dir(), + '--output-directory', htmloutdir, + '--title', 'Code coverage', + '--legend', + '--show-details', + covinfo] elem.add_item('COMMAND', command) elem.add_item('DESC', 'Generating HTML coverage report.') elem.write(outfile) @@ -988,7 +1008,7 @@ int dummy; outname_rel = os.path.join(self.get_target_dir(target), fname) src_list = target.get_sources() compiler = target.compilers['cs'] - rel_srcs = [s.rel_to_builddir(self.build_to_src) for s in src_list] + rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list] deps = [] commands = CompilerArgs(compiler, target.extra_args.get('cs', [])) commands += compiler.get_buildtype_args(buildtype) @@ -1014,8 +1034,8 @@ int dummy; for rel_src in generated_sources.keys(): dirpart, fnamepart = os.path.split(rel_src) if rel_src.lower().endswith('.cs'): - rel_srcs.append(rel_src) - deps.append(rel_src) + rel_srcs.append(os.path.normpath(rel_src)) + deps.append(os.path.normpath(rel_src)) for dep in target.get_external_deps(): commands.extend_direct(dep.get_link_args()) @@ -1588,7 +1608,15 @@ int dummy; def generate_cs_compile_rule(self, compiler, outfile): rule = 'rule %s_COMPILER\n' % compiler.get_language() invoc = ' '.join([ninja_quote(i) for i in compiler.get_exelist()]) - command = ' command = %s $ARGS $in\n' % invoc + + if mesonlib.is_windows(): + command = ''' command = {executable} @$out.rsp + rspfile = $out.rsp + rspfile_content = $ARGS $in +'''.format(executable=invoc) + else: + command = ' command = %s $ARGS $in\n' % invoc + description = ' description = Compiling C Sharp target $out.\n' outfile.write(rule) outfile.write(command) @@ -1843,7 +1871,6 @@ rule FORTRAN_DEP_HACK infilelist = genlist.get_inputs() outfilelist = genlist.get_outputs() extra_dependencies = [os.path.join(self.build_to_src, i) for i in genlist.extra_depends] - source_target_dir = self.get_target_source_dir(target) for i in range(len(infilelist)): if len(generator.outputs) == 1: sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i]) @@ -1868,7 +1895,6 @@ rule FORTRAN_DEP_HACK # We have consumed output files, so drop them from the list of remaining outputs. if sole_output == '': outfilelist = outfilelist[len(generator.outputs):] - relout = self.get_target_private_dir(target) args = self.replace_paths(target, args, override_subdir=subdir) cmdlist = exe_arr + self.replace_extra_args(args, genlist) if generator.capture: @@ -2248,6 +2274,9 @@ rule FORTRAN_DEP_HACK depelem.write(outfile) commands += compiler.get_module_outdir_args(self.get_target_private_dir(target)) + if compiler.language == 'd': + commands += compiler.get_feature_args(target.d_features, self.build_to_src) + element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) for d in header_deps: if isinstance(d, File): @@ -2682,3 +2711,9 @@ rule FORTRAN_DEP_HACK elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '') elem.write(outfile) + +def load(build_dir): + filename = os.path.join(build_dir, 'meson-private', 'install.dat') + with open(filename, 'rb') as f: + obj = pickle.load(f) + return obj diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 057e7c9..7f4c2ef 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, sys +import os import pickle import xml.dom.minidom import xml.etree.ElementTree as ET diff --git a/mesonbuild/backend/xcodebackend.py b/mesonbuild/backend/xcodebackend.py index 3ae31e4..e2204fe 100644 --- a/mesonbuild/backend/xcodebackend.py +++ b/mesonbuild/backend/xcodebackend.py @@ -16,7 +16,7 @@ from . import backends from .. import build from .. import dependencies from .. import mesonlib -import uuid, os, sys +import uuid, os from ..mesonlib import MesonException @@ -565,9 +565,7 @@ class XCodeBackend(backends.Backend): self.write_line(');') self.write_line('runOnlyForDeploymentPostprocessing = 0;') self.write_line('shellPath = /bin/sh;') - script_root = self.environment.get_script_dir() - test_script = os.path.join(script_root, 'meson_test.py') - cmd = mesonlib.python_command + [test_script, test_data, '--wd', self.environment.get_build_dir()] + cmd = mesonlib.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;') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 400b9e5..f4a5e2c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -15,6 +15,7 @@ import copy, os, re from collections import OrderedDict import itertools, pathlib +import pickle from . import environment from . import dependencies @@ -113,6 +114,7 @@ class Build: self.static_linker = None self.static_cross_linker = None self.subprojects = {} + self.subproject_dir = '' self.install_scripts = [] self.postconf_scripts = [] self.install_dirs = [] @@ -138,6 +140,9 @@ class Build: def get_project(self): return self.projects[''] + def get_subproject_dir(self): + return self.subproject_dir + def get_targets(self): return self.targets @@ -355,6 +360,7 @@ class BuildTarget(Target): self.extra_args = {} self.generated = [] self.extra_files = [] + self.d_features = {} # Sources can be: # 1. Pre-existing source files in the source tree # 2. Pre-existing sources generated by configure_file in the build tree @@ -364,9 +370,9 @@ class BuildTarget(Target): # 1. Pre-existing objects provided by the user with the `objects:` kwarg # 2. Compiled objects created by and extracted from another target self.process_objectlist(objects) - self.process_compilers() self.process_kwargs(kwargs, environment) self.check_unknown_kwargs(kwargs) + self.process_compilers() if not any([self.sources, self.generated, self.objects, self.link_whole]): raise InvalidArguments('Build target %s has no sources.' % name) self.process_compilers_late() @@ -499,6 +505,13 @@ class BuildTarget(Target): # which is what we need. if not is_object(s): sources.append(s) + for d in self.external_deps: + if hasattr(d, 'held_object'): + d = d.held_object + for s in d.sources: + if isinstance(s, (str, File)): + sources.append(s) + # Sources that were used to create our extracted objects for o in self.objects: if not isinstance(o, ExtractedObjects): @@ -650,10 +663,6 @@ just like those detected with the dependency() function.''') self.link(linktarget) lwhole = extract_as_list(kwargs, 'link_whole') for linktarget in lwhole: - # Sorry for this hack. Keyword targets are kept in holders - # in kwargs. Unpack here without looking at the exact type. - if hasattr(linktarget, "held_object"): - linktarget = linktarget.held_object self.link_whole(linktarget) c_pchlist, cpp_pchlist, clist, cpplist, cslist, valalist, objclist, objcpplist, fortranlist, rustlist \ @@ -682,12 +691,14 @@ just like those detected with the dependency() function.''') dfeature_versions = kwargs.get('d_module_versions', None) if dfeature_versions: dfeatures['versions'] = dfeature_versions - dfeature_import_dirs = kwargs.get('d_import_dirs', None) - if dfeature_import_dirs: + if 'd_import_dirs' in kwargs: + dfeature_import_dirs = extract_as_list(kwargs, 'd_import_dirs', unholder=True) + for d in dfeature_import_dirs: + if not isinstance(d, IncludeDirs): + raise InvalidArguments('Arguments to d_import_dirs must be include_directories.') dfeatures['import_dirs'] = dfeature_import_dirs if dfeatures: - if 'd' in self.compilers: - self.add_compiler_args('d', self.compilers['d'].get_feature_args(dfeatures)) + self.d_features = dfeatures self.link_args = extract_as_list(kwargs, 'link_args') for i in self.link_args: @@ -831,12 +842,14 @@ This will become a hard error in a future Meson release.''') self.add_include_dirs(dep.include_directories) for l in dep.libraries: self.link(l) + for l in dep.whole_libraries: + self.link_whole(l) # Those parts that are external. extpart = dependencies.InternalDependency('undefined', [], dep.compile_args, dep.link_args, - [], [], []) + [], [], [], []) self.external_deps.append(extpart) # Deps of deps. self.add_deps(dep.ext_deps) @@ -1922,3 +1935,22 @@ def get_sources_string_names(sources): else: raise AssertionError('Unknown source type: {!r}'.format(s)) return names + +def load(build_dir): + filename = os.path.join(build_dir, 'meson-private', 'build.dat') + load_fail_msg = 'Build data file {!r} is corrupted. Try with a fresh build tree.'.format(filename) + nonexisting_fail_msg = 'No such build data file as "{!r}".'.format(filename) + try: + with open(filename, 'rb') as f: + obj = pickle.load(f) + except FileNotFoundError: + raise MesonException(nonexisting_fail_msg) + except pickle.UnpicklingError: + raise MesonException(load_fail_msg) + if not isinstance(obj, Build): + raise MesonException(load_fail_msg) + return obj + +def save(obj, filename): + with open(filename, 'wb') as f: + pickle.dump(obj, f) diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index f09f252..84c87fb 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -67,6 +67,7 @@ __all__ = [ 'JavaCompiler', 'LLVMDCompiler', 'MonoCompiler', + 'VisualStudioCsCompiler', 'NAGFortranCompiler', 'ObjCCompiler', 'ObjCPPCompiler', @@ -127,7 +128,7 @@ from .cpp import ( IntelCPPCompiler, VisualStudioCPPCompiler, ) -from .cs import MonoCompiler +from .cs import MonoCompiler, VisualStudioCsCompiler from .d import ( DCompiler, DmdDCompiler, diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index c10f38e..1fa6f15 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -112,7 +112,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): if self.gcc_type == GCC_MINGW: opts.update({ 'cpp_winlibs': coredata.UserArrayOption('cpp_winlibs', 'Standard Win libraries to link against', - gnu_winlibs), }) + gnu_winlibs), }) return opts def get_option_compile_args(self, options): diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index dd7a433..f78e364 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -15,19 +15,26 @@ import os.path, subprocess from ..mesonlib import EnvironmentException +from ..mesonlib import is_windows from .compilers import Compiler, mono_buildtype_args -class MonoCompiler(Compiler): - def __init__(self, exelist, version, **kwargs): +class CsCompiler(Compiler): + def __init__(self, exelist, version, id, runner=None): self.language = 'cs' - super().__init__(exelist, version, **kwargs) - self.id = 'mono' - self.monorunner = 'mono' + super().__init__(exelist, version) + self.id = id + self.runner = runner def get_display_language(self): return 'C sharp' + def get_always_args(self): + return ['/nologo'] + + def get_linker_always_args(self): + return ['/nologo'] + def get_output_args(self, fname): return ['-out:' + fname] @@ -92,11 +99,14 @@ class MonoCompiler(Compiler): } } ''') - pc = subprocess.Popen(self.exelist + [src], cwd=work_dir) + pc = subprocess.Popen(self.exelist + self.get_always_args() + [src], cwd=work_dir) pc.wait() if pc.returncode != 0: raise EnvironmentException('Mono compiler %s can not compile programs.' % self.name_string()) - cmdlist = [self.monorunner, obj] + if self.runner: + cmdlist = [self.runner, obj] + else: + cmdlist = [os.path.join(work_dir, obj)] pe = subprocess.Popen(cmdlist, cwd=work_dir) pe.wait() if pe.returncode != 0: @@ -107,3 +117,25 @@ class MonoCompiler(Compiler): def get_buildtype_args(self, buildtype): return mono_buildtype_args[buildtype] + + +class MonoCompiler(CsCompiler): + def __init__(self, exelist, version): + super().__init__(exelist, version, 'mono', + 'mono') + + +class VisualStudioCsCompiler(CsCompiler): + def __init__(self, exelist, version): + super().__init__(exelist, version, 'csc') + + def get_buildtype_args(self, buildtype): + res = mono_buildtype_args[buildtype] + if not is_windows(): + tmp = [] + for flag in res: + if flag == '-debug': + flag = '-debug:portable' + tmp.append(flag) + res = tmp + return res diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 3320736..474e1bd 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -93,7 +93,7 @@ class DCompiler(Compiler): # FIXME: Make this work for Windows, MacOS and cross-compiling return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, is_shared_module) - def get_feature_args(self, kwargs): + def get_feature_args(self, kwargs, build_to_src): res = [] if 'unittest' in kwargs: unittest = kwargs.pop('unittest') @@ -122,8 +122,16 @@ class DCompiler(Compiler): import_dir_arg = d_feature_args[self.id]['import_dir'] if not import_dir_arg: raise EnvironmentException('D compiler %s does not support the "string import directories" feature.' % self.name_string()) - for d in import_dirs: - res.append('{0}{1}'.format(import_dir_arg, d)) + for idir_obj in import_dirs: + basedir = idir_obj.get_curdir() + for idir in idir_obj.get_incdirs(): + # Avoid superfluous '/.' at the end of paths when d is '.' + if idir not in ('', '.'): + expdir = os.path.join(basedir, idir) + else: + expdir = basedir + srctreedir = os.path.join(build_to_src, expdir) + res.append('{0}{1}'.format(import_dir_arg, srctreedir)) if kwargs: raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index bae207b..e2a0b48 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -21,7 +21,7 @@ from .mesonlib import MesonException from .mesonlib import default_libdir, default_libexecdir, default_prefix import ast -version = '0.45.0.dev1' +version = '0.46.0.dev1' backendlist = ['ninja', 'vs', 'vs2010', 'vs2015', 'vs2017', 'xcode'] default_yielding = False @@ -340,7 +340,8 @@ class CoreData: return opt.validate_value(override_value) raise MesonException('Tried to validate unknown option %s.' % option_name) -def load(filename): +def load(build_dir): + filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') load_fail_msg = 'Coredata file {!r} is corrupted. Try with a fresh build tree.'.format(filename) try: with open(filename, 'rb') as f: @@ -354,7 +355,8 @@ def load(filename): (obj.version, version)) return obj -def save(obj, filename): +def save(obj, build_dir): + filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') if obj.version != version: raise MesonException('Fatal version mismatch corruption.') with open(filename, 'wb') as f: diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 7a652a4..0375102 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -145,7 +145,7 @@ class Dependency: class InternalDependency(Dependency): - def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps): + def __init__(self, version, incdirs, compile_args, link_args, libraries, whole_libraries, sources, ext_deps): super().__init__('internal', {}) self.version = version self.is_found = True @@ -153,6 +153,7 @@ class InternalDependency(Dependency): self.compile_args = compile_args self.link_args = link_args self.libraries = libraries + self.whole_libraries = whole_libraries self.sources = sources self.ext_deps = ext_deps @@ -380,9 +381,7 @@ class PkgConfigDependency(ExternalDependency): pkgname = environment.cross_info.config['binaries']['pkgconfig'] potential_pkgbin = ExternalProgram(pkgname, silent=True) if potential_pkgbin.found(): - # FIXME, we should store all pkg-configs in ExternalPrograms. - # However that is too destabilizing a change to do just before release. - self.pkgbin = potential_pkgbin.get_command()[0] + self.pkgbin = potential_pkgbin PkgConfigDependency.class_pkgbin = self.pkgbin else: mlog.debug('Cross pkg-config %s not found.' % potential_pkgbin.name) @@ -404,7 +403,7 @@ class PkgConfigDependency(ExternalDependency): self.type_string = 'Native' mlog.debug('Determining dependency {!r} with pkg-config executable ' - '{!r}'.format(name, self.pkgbin)) + '{!r}'.format(name, self.pkgbin.get_path())) ret, self.version = self._call_pkgbin(['--modversion', name]) if ret != 0: if self.required: @@ -463,7 +462,7 @@ class PkgConfigDependency(ExternalDependency): def _call_pkgbin(self, args, env=None): if not env: env = os.environ - p, out = Popen_safe([self.pkgbin] + args, env=env)[0:2] + p, out = Popen_safe(self.pkgbin.get_command() + args, env=env)[0:2] return p.returncode, out.strip() def _convert_mingw_paths(self, args): @@ -608,21 +607,23 @@ class PkgConfigDependency(ExternalDependency): pkgbin = os.environ[evar].strip() else: pkgbin = 'pkg-config' - try: - p, out = Popen_safe([pkgbin, '--version'])[0:2] - if p.returncode != 0: - # Set to False instead of None to signify that we've already - # searched for it and not found it + pkgbin = ExternalProgram(pkgbin, silent=True) + if pkgbin.found(): + try: + p, out = Popen_safe(pkgbin.get_command() + ['--version'])[0:2] + if p.returncode != 0: + mlog.warning('Found pkg-config {!r} but couldn\'t run it' + ''.format(' '.join(pkgbin.get_command()))) + # Set to False instead of None to signify that we've already + # searched for it and not found it + pkgbin = False + except (FileNotFoundError, PermissionError): pkgbin = False - except (FileNotFoundError, PermissionError): + else: pkgbin = False - if pkgbin and not os.path.isabs(pkgbin) and shutil.which(pkgbin): - # Sometimes shutil.which fails where Popen succeeds, so - # only find the abs path if it can be found by shutil.which - pkgbin = shutil.which(pkgbin) if not self.silent: if pkgbin: - mlog.log('Found pkg-config:', mlog.bold(pkgbin), + mlog.log('Found pkg-config:', mlog.bold(pkgbin.get_path()), '(%s)' % out.strip()) else: mlog.log('Found Pkg-config:', mlog.red('NO')) diff --git a/mesonbuild/dependencies/boost.py b/mesonbuild/dependencies/boost.py index 03cc7b8..a17fb58 100644 --- a/mesonbuild/dependencies/boost.py +++ b/mesonbuild/dependencies/boost.py @@ -167,7 +167,7 @@ class BoostDependency(ExternalDependency): invalid_modules = [x for x in invalid_modules if x not in remove] if invalid_modules: - mlog.log(mlog.red('ERROR:'), 'Invalid Boost modules: ' + ', '.join(invalid_modules)) + mlog.error('Invalid Boost modules: ' + ', '.join(invalid_modules)) return True else: return False diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 3e2d170..2f31196 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -17,14 +17,13 @@ import os import re -import shutil import subprocess from collections import OrderedDict from .. import mlog from .. import mesonlib from ..mesonlib import ( - MesonException, Popen_safe, extract_as_list, for_windows, + MesonException, Popen_safe, extract_as_list, for_windows, for_cygwin, version_compare_many ) from ..environment import detect_cpu @@ -282,10 +281,15 @@ class QtBaseDependency(ExternalDependency): (k, v) = tuple(line.split(':', 1)) qvars[k] = v if mesonlib.is_osx(): - return self._framework_detect(qvars, mods, kwargs) + self._framework_detect(qvars, mods, kwargs) + return qmake incdir = qvars['QT_INSTALL_HEADERS'] self.compile_args.append('-I' + incdir) libdir = qvars['QT_INSTALL_LIBS'] + if for_cygwin(self.env.is_cross_build(), self.env): + shlibext = '.dll.a' + else: + shlibext = '.so' # Used by self.compilers_detect() self.bindir = self.get_qmake_host_bins(qvars) self.is_found = True @@ -307,7 +311,7 @@ class QtBaseDependency(ExternalDependency): self.is_found = False break else: - libfile = os.path.join(libdir, 'lib{}{}.so'.format(self.qtpkgname, module)) + libfile = os.path.join(libdir, 'lib{}{}{}'.format(self.qtpkgname, module, shlibext)) if not os.path.isfile(libfile): self.is_found = False break @@ -316,15 +320,23 @@ class QtBaseDependency(ExternalDependency): def _framework_detect(self, qvars, modules, kwargs): libdir = qvars['QT_INSTALL_LIBS'] + + # ExtraFrameworkDependency doesn't support any methods + fw_kwargs = kwargs.copy() + fw_kwargs.pop('method', None) + for m in modules: fname = 'Qt' + m fwdep = ExtraFrameworkDependency(fname, False, libdir, self.env, - self.language, kwargs) + self.language, fw_kwargs) self.compile_args.append('-F' + libdir) if fwdep.found(): - self.is_found = True self.compile_args += fwdep.get_compile_args() self.link_args += fwdep.get_link_args() + else: + break + else: + self.is_found = True # Used by self.compilers_detect() self.bindir = self.get_qmake_host_bins(qvars) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index e553423..31ca2a2 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -19,7 +19,6 @@ from .linkers import ArLinker, VisualStudioLinker from . import mesonlib from .mesonlib import EnvironmentException, Popen_safe from . import mlog -import sys from . import compilers from .compilers import ( @@ -54,6 +53,7 @@ from .compilers import ( IntelFortranCompiler, JavaCompiler, MonoCompiler, + VisualStudioCsCompiler, NAGFortranCompiler, Open64FortranCompiler, PathScaleFortranCompiler, @@ -251,8 +251,7 @@ class Environment: os.makedirs(self.scratch_dir, exist_ok=True) os.makedirs(self.log_dir, exist_ok=True) try: - cdf = os.path.join(self.get_build_dir(), Environment.coredata_file) - self.coredata = coredata.load(cdf) + self.coredata = coredata.load(self.get_build_dir()) self.first_invocation = False except FileNotFoundError: # WARNING: Don't use any values from coredata in __init__. It gets @@ -275,6 +274,10 @@ class Environment: else: self.default_c = ['cc', 'gcc', 'clang'] self.default_cpp = ['c++', 'g++', 'clang++'] + if mesonlib.is_windows(): + self.default_cs = ['csc', 'mcs'] + else: + self.default_cs = ['mcs', 'csc'] self.default_objc = ['cc'] self.default_objcpp = ['c++'] self.default_fortran = ['gfortran', 'g95', 'f95', 'f90', 'f77', 'ifort'] @@ -311,9 +314,8 @@ class Environment: return self.cross_info is not None def dump_coredata(self): - cdf = os.path.join(self.get_build_dir(), Environment.coredata_file) - coredata.save(self.coredata, cdf) - return cdf + coredata.save(self.coredata, self.get_build_dir()) + return os.path.join(self.get_build_dir(), Environment.coredata_file) def get_script_dir(self): import mesonbuild.scripts @@ -419,7 +421,7 @@ class Environment: def _get_compilers(self, lang, evar, want_cross): ''' The list of compilers is detected in the exact same way for - C, C++, ObjC, ObjC++, Fortran so consolidate it here. + C, C++, ObjC, ObjC++, Fortran, CS so consolidate it here. ''' if self.is_cross_build() and want_cross: compilers = mesonlib.stringlistify(self.cross_info.config['binaries'][lang]) @@ -664,16 +666,24 @@ class Environment: raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') def detect_cs_compiler(self): - exelist = ['mcs'] - try: - p, out, err = Popen_safe(exelist + ['--version']) - except OSError: - raise EnvironmentException('Could not execute C# compiler "%s"' % ' '.join(exelist)) - version = search_version(out) - full_version = out.split('\n', 1)[0] - if 'Mono' in out: - return MonoCompiler(exelist, version, full_version=full_version) - raise EnvironmentException('Unknown compiler "' + ' '.join(exelist) + '"') + compilers, ccache, is_cross, exe_wrap = self._get_compilers('cs', 'CSC', False) + popen_exceptions = {} + for comp in compilers: + if not isinstance(comp, list): + comp = [comp] + try: + p, out, err = Popen_safe(comp + ['--version']) + except OSError as e: + popen_exceptions[' '.join(comp + ['--version'])] = e + continue + + version = search_version(out) + if 'Mono' in out: + return MonoCompiler(comp, version) + elif "Visual C#" in out: + return VisualStudioCsCompiler(comp, version) + + self._handle_exceptions(popen_exceptions, compilers) def detect_vala_compiler(self): if 'VALAC' in os.environ: diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 8041526..1819db4 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -37,6 +37,7 @@ from pathlib import PurePath import importlib + def stringifyUserArguments(args): if isinstance(args, list): return '[%s]' % ', '.join([stringifyUserArguments(x) for x in args]) @@ -247,7 +248,7 @@ class ConfigurationDataHolder(MutableInterpreterObject, ObjectHolder): return val def get(self, name): - return self.held_object.values[name] # (val, desc) + return self.held_object.values[name] # (val, desc) def keys(self): return self.held_object.values.keys() @@ -652,10 +653,11 @@ class RunTargetHolder(InterpreterObject, ObjectHolder): return r.format(self.__class__.__name__, h.get_id(), h.command) class Test(InterpreterObject): - def __init__(self, name, suite, exe, is_parallel, cmd_args, env, should_fail, timeout, workdir): + def __init__(self, name, project, suite, exe, is_parallel, cmd_args, env, should_fail, timeout, workdir): InterpreterObject.__init__(self) self.name = name self.suite = suite + self.project_name = project self.exe = exe self.is_parallel = is_parallel self.cmd_args = cmd_args @@ -816,7 +818,8 @@ class CompilerHolder(InterpreterObject): ''' if not hasattr(self.compiler, 'get_feature_args'): raise InterpreterException('This {} compiler has no feature arguments.'.format(self.compiler.get_display_language())) - return self.compiler.get_feature_args({'unittest': 'true'}) + build_to_src = os.path.relpath(self.environment.get_source_dir(), self.environment.get_build_dir()) + return self.compiler.get_feature_args({'unittest': 'true'}, build_to_src) def has_member_method(self, args, kwargs): if len(args) != 2: @@ -1309,6 +1312,7 @@ class MesonMain(InterpreterObject): return args[1] raise InterpreterException('Unknown cross property: %s.' % propname) + pch_kwargs = set(['c_pch', 'cpp_pch']) lang_arg_kwargs = set([ @@ -1386,7 +1390,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'}, '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'}, - 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'}, + 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, 'executable': exe_kwargs, 'find_program': {'required', 'native'}, 'generator': {'arguments', 'output', 'depfile', 'capture', 'preserve_path_from'}, @@ -1618,6 +1622,7 @@ class Interpreter(InterpreterBase): raise InterpreterException('Version must be a string.') incs = extract_as_list(kwargs, 'include_directories', unholder=True) libs = extract_as_list(kwargs, 'link_with', unholder=True) + libs_whole = extract_as_list(kwargs, 'link_whole', unholder=True) sources = extract_as_list(kwargs, 'sources') sources = listify(self.source_strings_to_files(sources), unholder=True) deps = extract_as_list(kwargs, 'dependencies', unholder=True) @@ -1637,7 +1642,7 @@ class Interpreter(InterpreterBase): raise InterpreterException('''Entries in "link_with" may only be self-built targets, external dependencies (including libraries) must go to "dependencies".''') dep = dependencies.InternalDependency(version, incs, compile_args, - link_args, libs, sources, final_deps) + link_args, libs, libs_whole, sources, final_deps) return DependencyHolder(dep) @noKwargs @@ -1675,10 +1680,17 @@ external dependencies (including libraries) must go to "dependencies".''') cargs = args[1:] srcdir = self.environment.get_source_dir() builddir = self.environment.get_build_dir() - m = 'must be a string, or the output of find_program(), files(), or ' \ - 'configure_file(); not {!r}' + m = 'must be a string, or the output of find_program(), files() '\ + 'or configure_file(), or a compiler object; not {!r}' if isinstance(cmd, ExternalProgramHolder): cmd = cmd.held_object + elif isinstance(cmd, CompilerHolder): + cmd = cmd.compiler.get_exelist()[0] + prog = ExternalProgram(cmd, silent=True) + if not prog.found(): + raise InterpreterException('Program {!r} not found ' + 'or not executable'.format(cmd)) + cmd = prog else: if isinstance(cmd, mesonlib.File): cmd = cmd.absolute_path(srcdir, builddir) @@ -1759,6 +1771,12 @@ external dependencies (including libraries) must go to "dependencies".''') try: resolved = r.resolve(dirname) except RuntimeError as e: + # if the reason subproject execution failed was because + # the directory doesn't exist, try to give some helpful + # advice if it's a nested subproject that needs + # promotion... + self.print_nested_info(dirname) + msg = 'Subproject directory {!r} does not exist and cannot be downloaded:\n{}' raise InterpreterException(msg.format(os.path.join(self.subproject_dir, dirname), e)) subdir = os.path.join(self.subproject_dir, resolved) @@ -1933,6 +1951,8 @@ to directly access options of other subprojects.''') raise InterpreterException('Subproject_dir must not contain a ".." segment.') self.subproject_dir = spdirname + self.build.subproject_dir = self.subproject_dir + if 'meson_version' in kwargs: cv = coredata.version pv = kwargs['meson_version'] @@ -2084,6 +2104,19 @@ to directly access options of other subprojects.''') else: version_string = ' (%s %s)' % (comp.id, comp.version) mlog.log('Native %s compiler: ' % comp.get_display_language(), mlog.bold(' '.join(comp.get_exelist())), version_string, sep='') + + # If <language>_args/_link_args settings are given on the + # command line, use them. + for optspec in self.build.environment.cmd_line_options.projectoptions: + (optname, optvalue) = optspec.split('=', maxsplit=1) + if optname.endswith('_link_args'): + lang = optname[:-10] + self.coredata.external_link_args.setdefault(lang, []).append(optvalue) + elif optname.endswith('_args'): + lang = optname[:-5] + self.coredata.external_args.setdefault(lang, []).append(optvalue) + # Otherwise, look for definitions from environment + # variables such as CFLAGS. if not comp.get_language() in self.coredata.external_args: (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp) self.coredata.external_preprocess_args[comp.get_language()] = preproc_args @@ -2166,7 +2199,7 @@ to directly access options of other subprojects.''') if progobj is None: progobj = self.program_from_system(args) if required and (progobj is None or not progobj.found()): - raise InvalidArguments('Program "%s" not found or not executable' % args[0]) + raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args)) if progobj is None: return ExternalProgramHolder(dependencies.NonExistingExternalProgram()) return progobj @@ -2303,7 +2336,6 @@ to directly access options of other subprojects.''') # we won't actually read all the build files. return fallback_dep if not dep: - self.print_nested_info(name) assert(exception is not None) raise exception @@ -2335,7 +2367,7 @@ root and issuing %s. cmds = [] command_templ = 'meson wrap promote ' for l in found: - cmds.append(command_templ + l[len(self.source_root)+1:]) + cmds.append(command_templ + l[len(self.source_root) + 1:]) final_message = message + '\n'.join(cmds) print(final_message) @@ -2583,14 +2615,12 @@ root and issuing %s. if not isinstance(timeout, int): raise InterpreterException('Timeout must be an integer.') suite = [] + prj = self.subproject if self.is_subproject() else self.build.project_name for s in mesonlib.stringlistify(kwargs.get('suite', '')): if len(s) > 0: s = ':' + s - if self.is_subproject(): - suite.append(self.subproject.replace(' ', '_').replace(':', '_') + s) - else: - suite.append(self.build.project_name.replace(' ', '_').replace(':', '_') + s) - t = Test(args[0], suite, exe.held_object, par, cmd_args, env, should_fail, timeout, workdir) + suite.append(prj.replace(' ', '_').replace(':', '_') + s) + t = Test(args[0], prj, suite, exe.held_object, par, cmd_args, env, should_fail, timeout, workdir) if is_base_test: self.build.tests.append(t) mlog.debug('Adding test "', mlog.bold(args[0]), '".', sep='') @@ -2847,12 +2877,17 @@ root and issuing %s. @permittedKwargs(permitted_kwargs['include_directories']) @stringArgs def func_include_directories(self, node, args, kwargs): + return self.build_incdir_object(args, kwargs.get('is_system', False)) + + def build_incdir_object(self, incdir_strings, is_system=False): + if not isinstance(is_system, bool): + raise InvalidArguments('Is_system must be boolean.') src_root = self.environment.get_source_dir() build_root = self.environment.get_build_dir() absbase_src = os.path.join(src_root, self.subdir) absbase_build = os.path.join(build_root, self.subdir) - for a in args: + for a in incdir_strings: if a.startswith(src_root): raise InvalidArguments('''Tried to form an absolute path to a source dir. You should not do that but use relative paths instead. @@ -2875,10 +2910,7 @@ different subdirectory. absdir_build = os.path.join(absbase_build, a) if not os.path.isdir(absdir_src) and not os.path.isdir(absdir_build): raise InvalidArguments('Include dir %s does not exist.' % a) - is_system = kwargs.get('is_system', False) - if not isinstance(is_system, bool): - raise InvalidArguments('Is_system must be boolean.') - i = IncludeDirsHolder(build.IncludeDirs(self.subdir, args, is_system)) + i = IncludeDirsHolder(build.IncludeDirs(self.subdir, incdir_strings, is_system)) return i @permittedKwargs(permitted_kwargs['add_test_setup']) @@ -2887,8 +2919,10 @@ different subdirectory. if len(args) != 1: raise InterpreterException('Add_test_setup needs one argument for the setup name.') setup_name = args[0] - if re.fullmatch('[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None: + if re.fullmatch('([_a-zA-Z][_0-9a-zA-Z]*:)?[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None: raise InterpreterException('Setup name may only contain alphanumeric characters.') + if ":" not in setup_name: + setup_name = (self.subproject if self.subproject else self.build.project_name) + ":" + setup_name try: inp = extract_as_list(kwargs, 'exe_wrapper') exe_wrapper = [] @@ -2912,14 +2946,10 @@ different subdirectory. if not isinstance(timeout_multiplier, int): raise InterpreterException('Timeout multiplier must be a number.') env = self.unpack_env_kwarg(kwargs) - setupobj = build.TestSetup(exe_wrapper=exe_wrapper, - gdb=gdb, - timeout_multiplier=timeout_multiplier, - env=env) - if self.subproject == '': - # Dunno what we should do with subprojects really. Let's start simple - # and just use the master project ones. - self.build.test_setups[setup_name] = setupobj + self.build.test_setups[setup_name] = build.TestSetup(exe_wrapper=exe_wrapper, + gdb=gdb, + timeout_multiplier=timeout_multiplier, + env=env) @permittedKwargs(permitted_kwargs['add_global_arguments']) @stringArgs @@ -3106,6 +3136,7 @@ different subdirectory. else: mlog.debug('Unknown target type:', str(targetholder)) raise RuntimeError('Unreachable code') + self.kwarg_strings_to_includedirs(kwargs) target = targetclass(name, self.subdir, self.subproject, is_cross, sources, objs, self.environment, kwargs) if is_cross: self.add_cross_stdlib_info(target) @@ -3114,6 +3145,23 @@ different subdirectory. self.project_args_frozen = True return l + def kwarg_strings_to_includedirs(self, kwargs): + if 'd_import_dirs' in kwargs: + items = mesonlib.extract_as_list(kwargs, 'd_import_dirs') + cleaned_items = [] + for i in items: + if isinstance(i, str): + # BW compatibility. This was permitted so we must support it + # for a few releases so people can transition to "correct" + # path declarations. + if i.startswith(self.environment.get_source_dir()): + mlog.warning('''Building a path to the source dir is not supported. Use a relative path instead. +This will become a hard error in the future.''') + i = os.path.relpath(i, os.path.join(self.environment.get_source_dir(), self.subdir)) + i = self.build_incdir_object([i]) + cleaned_items.append(i) + kwargs['d_import_dirs'] = cleaned_items + def get_used_languages(self, target): result = {} for i in target.sources: @@ -3152,6 +3200,7 @@ different subdirectory. if idx >= len(arg_strings): raise InterpreterException('Format placeholder @{}@ out of range.'.format(idx)) return arg_strings[idx] + return re.sub(r'@(\d+)@', arg_replace, templ) # Only permit object extraction from the same subproject diff --git a/mesonbuild/interpreterbase.py b/mesonbuild/interpreterbase.py index 0539b14..170df29 100644 --- a/mesonbuild/interpreterbase.py +++ b/mesonbuild/interpreterbase.py @@ -408,7 +408,7 @@ The result of this is undefined and will become a hard error in a future Meson r varname = node.var_name addition = self.evaluate_statement(node.value) if is_disabler(addition): - set_variable(varname, addition) + self.set_variable(varname, addition) return # Remember that all variables are immutable. We must always create a # full new variable and then assign it. diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 771e9ee..b409615 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -12,10 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys, os -import pickle +import os +import sys import argparse -from . import coredata, mesonlib +from . import (coredata, mesonlib, build) parser = argparse.ArgumentParser(prog='meson configure') @@ -25,95 +25,73 @@ parser.add_argument('directory', nargs='*') parser.add_argument('--clearcache', action='store_true', default=False, help='Clear cached state (e.g. found dependencies)') + class ConfException(mesonlib.MesonException): pass + class Conf: def __init__(self, build_dir): self.build_dir = build_dir - self.coredata_file = os.path.join(build_dir, 'meson-private/coredata.dat') - self.build_file = os.path.join(build_dir, 'meson-private/build.dat') - if not os.path.isfile(self.coredata_file) or not os.path.isfile(self.build_file): + if not os.path.isdir(os.path.join(build_dir, 'meson-private')): raise ConfException('Directory %s does not seem to be a Meson build directory.' % build_dir) - with open(self.coredata_file, 'rb') as f: - self.coredata = pickle.load(f) - with open(self.build_file, 'rb') as f: - self.build = pickle.load(f) - if self.coredata.version != coredata.version: - raise ConfException('Version mismatch (%s vs %s)' % - (coredata.version, self.coredata.version)) + self.build = build.load(self.build_dir) + self.coredata = coredata.load(self.build_dir) def clear_cache(self): self.coredata.deps = {} def save(self): # Only called if something has changed so overwrite unconditionally. - with open(self.coredata_file, 'wb') as f: - pickle.dump(self.coredata, f) + coredata.save(self.coredata, self.build_dir) # We don't write the build file because any changes to it # are erased when Meson is executed the next time, i.e. when # Ninja is run. - def print_aligned(self, arr): + @staticmethod + def print_aligned(arr): + def make_lower_case(val): + if isinstance(val, bool): + return str(val).lower() + elif isinstance(val, list): + return [make_lower_case(i) for i in val] + else: + return str(val) + if not arr: return - titles = {'name': 'Option', 'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'} - len_name = longest_name = len(titles['name']) - len_descr = longest_descr = len(titles['descr']) - len_value = longest_value = len(titles['value']) - longest_choices = 0 # not printed if we don't get any optional values - - # calculate the max length of each - for x in arr: - name = x['name'] - descr = x['descr'] - value = x['value'] if isinstance(x['value'], str) else str(x['value']).lower() - choices = '' - if isinstance(x['choices'], list): - if x['choices']: - x['choices'] = [s if isinstance(s, str) else str(s).lower() for s in x['choices']] - choices = '[%s]' % ', '.join(map(str, x['choices'])) - elif x['choices']: - choices = x['choices'] if isinstance(x['choices'], str) else str(x['choices']).lower() - longest_name = max(longest_name, len(name)) - longest_descr = max(longest_descr, len(descr)) - longest_value = max(longest_value, len(value)) - longest_choices = max(longest_choices, len(choices)) + titles = {'name': 'Option', 'descr': 'Description', 'value': 'Current Value', 'choices': 'Possible Values'} - # update possible non strings - x['value'] = value - x['choices'] = choices + name_col = [titles['name'], '-' * len(titles['name'])] + value_col = [titles['value'], '-' * len(titles['value'])] + choices_col = [titles['choices'], '-' * len(titles['choices'])] + descr_col = [titles['descr'], '-' * len(titles['descr'])] - # prints header - namepad = ' ' * (longest_name - len_name) - valuepad = ' ' * (longest_value - len_value) - if longest_choices: - len_choices = len(titles['choices']) - longest_choices = max(longest_choices, len_choices) - choicepad = ' ' * (longest_choices - len_choices) - print(' %s%s %s%s %s%s %s' % (titles['name'], namepad, titles['value'], valuepad, titles['choices'], choicepad, titles['descr'])) - print(' %s%s %s%s %s%s %s' % ('-' * len_name, namepad, '-' * len_value, valuepad, '-' * len_choices, choicepad, '-' * len_descr)) - else: - print(' %s%s %s%s %s' % (titles['name'], namepad, titles['value'], valuepad, titles['descr'])) - print(' %s%s %s%s %s' % ('-' * len_name, namepad, '-' * len_value, valuepad, '-' * len_descr)) + choices_found = False + for opt in arr: + name_col.append(opt['name']) + descr_col.append(opt['descr']) + if isinstance(opt['value'], list): + value_col.append('[{0}]'.format(', '.join(make_lower_case(opt['value'])))) + else: + value_col.append(make_lower_case(opt['value'])) + if opt['choices']: + choices_found = True + choices_col.append('[{0}]'.format(', '.join(make_lower_case(opt['choices'])))) + else: + choices_col.append('') - # print values - for i in arr: - name = i['name'] - descr = i['descr'] - value = i['value'] - choices = i['choices'] + col_widths = (max([len(i) for i in name_col], default=0), + max([len(i) for i in value_col], default=0), + max([len(i) for i in choices_col], default=0), + max([len(i) for i in descr_col], default=0)) - namepad = ' ' * (longest_name - len(name)) - valuepad = ' ' * (longest_value - len(value)) - if longest_choices: - choicespad = ' ' * (longest_choices - len(choices)) - f = ' %s%s %s%s %s%s %s' % (name, namepad, value, valuepad, choices, choicespad, descr) + for line in zip(name_col, value_col, choices_col, descr_col): + if choices_found: + print(' {0:{width[0]}} {1:{width[1]}} {2:{width[2]}} {3:{width[3]}}'.format(*line, width=col_widths)) else: - f = ' %s%s %s%s %s' % (name, namepad, value, valuepad, descr) - - print(f) + print(' {0:{width[0]}} {1:{width[1]}} {3:{width[3]}}'.format(*line, width=col_widths)) def set_options(self, options): for o in options: @@ -156,8 +134,7 @@ class Conf: print('Core properties:') print(' Source dir', self.build.environment.source_dir) print(' Build dir ', self.build.environment.build_dir) - print('') - print('Core options:') + print('\nCore options:\n') carr = [] for key in ['buildtype', 'warning_level', 'werror', 'strip', 'unity', 'default_library']: carr.append({'name': key, @@ -165,48 +142,39 @@ class Conf: 'value': self.coredata.get_builtin_option(key), 'choices': coredata.get_builtin_option_choices(key)}) self.print_aligned(carr) - print('') - bekeys = sorted(self.coredata.backend_options.keys()) - if not bekeys: + if not self.coredata.backend_options: print(' No backend options\n') else: bearr = [] - for k in bekeys: + for k in sorted(self.coredata.backend_options): o = self.coredata.backend_options[k] bearr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''}) self.print_aligned(bearr) - print('') - print('Base options:') - okeys = sorted(self.coredata.base_options.keys()) - if not okeys: + print('\nBase options:') + if not self.coredata.base_options: print(' No base options\n') else: coarr = [] - for k in okeys: + for k in sorted(self.coredata.base_options): o = self.coredata.base_options[k] coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': o.choices}) self.print_aligned(coarr) - print('') - print('Compiler arguments:') + print('\nCompiler arguments:') for (lang, args) in self.coredata.external_args.items(): print(' ' + lang + '_args', str(args)) - print('') - print('Linker args:') + print('\nLinker args:') for (lang, args) in self.coredata.external_link_args.items(): print(' ' + lang + '_link_args', str(args)) - print('') - print('Compiler options:') - okeys = sorted(self.coredata.compiler_options.keys()) - if not okeys: + print('\nCompiler options:') + if not self.coredata.compiler_options: print(' No compiler options\n') else: coarr = [] - for k in okeys: + for k in self.coredata.compiler_options: o = self.coredata.compiler_options[k] coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''}) self.print_aligned(coarr) - print('') - print('Directories:') + print('\nDirectories:') parr = [] for key in ['prefix', 'libdir', @@ -227,30 +195,24 @@ class Conf: 'value': self.coredata.get_builtin_option(key), 'choices': coredata.get_builtin_option_choices(key)}) self.print_aligned(parr) - print('') - print('Project options:') + print('\nProject options:') if not self.coredata.user_options: print(' This project does not have any options') else: - options = self.coredata.user_options - keys = list(options.keys()) - keys.sort() optarr = [] - for key in keys: - opt = options[key] + for key in sorted(self.coredata.user_options): + opt = self.coredata.user_options[key] if (opt.choices is None) or (not opt.choices): # Zero length list or string choices = '' else: - # A non zero length list or string, convert to string - choices = str(opt.choices) + choices = opt.choices optarr.append({'name': key, 'descr': opt.description, 'value': opt.value, 'choices': choices}) self.print_aligned(optarr) - print('') - print('Testing options:') + print('\nTesting options:') tarr = [] for key in ['stdsplit', 'errorlogs']: tarr.append({'name': key, @@ -259,6 +221,7 @@ class Conf: 'choices': coredata.get_builtin_option_choices(key)}) self.print_aligned(tarr) + def run(args): args = mesonlib.expand_arguments(args) if not args: @@ -286,10 +249,10 @@ def run(args): if save: c.save() except ConfException as e: - print('Meson configurator encountered an error:\n') - print(e) - return 1 + print('Meson configurator encountered an error:') + raise e return 0 + if __name__ == '__main__': sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 9e0508b..4e72600 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -36,12 +36,11 @@ def detect_meson_py_location(): # $ <mesontool> <args> (gets run from /usr/bin/<mesontool>) in_path_exe = shutil.which(c_fname) if in_path_exe: - m_dir, c_fname = os.path.split(in_path_exe) - # Special case: when run like "./meson.py <opts>", - # we need to expand it out, because, for example, - # "ninja test" will be run from a different directory. - if m_dir == '.': + 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) diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 7966d70..9c4498c 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys, stat, traceback, pickle, argparse -import time, datetime +import sys, stat, traceback, argparse +import datetime import os.path from . import environment, interpreter, mesonlib from . import build @@ -196,6 +196,7 @@ class MesonApp: mlog.log('Build machine cpu:', mlog.bold(intr.builtin['build_machine'].cpu_method([], {}))) intr.run() try: + dumpfile = os.path.join(env.get_scratch_dir(), 'build.dat') # We would like to write coredata as late as possible since we use the existence of # this file to check if we generated the build file successfully. Since coredata # includes settings, the build files must depend on it and appear newer. However, due @@ -204,16 +205,13 @@ class MesonApp: # possible, but before build files, and if any error occurs, delete it. cdf = env.dump_coredata() g.generate(intr) - dumpfile = os.path.join(env.get_scratch_dir(), 'build.dat') - with open(dumpfile, 'wb') as f: - pickle.dump(b, f) + build.save(b, dumpfile) # Post-conf scripts must be run after writing coredata or else introspection fails. g.run_postconf_scripts() except: os.unlink(cdf) raise - def run_script_command(args): cmdname = args[0] cmdargs = args[1:] @@ -286,6 +284,13 @@ def run(original_args, mainfile=None): # First check if we want to run a subcommand. cmd_name = args[0] remaining_args = args[1:] + # "help" is a special case: Since printing of the help may be + # delegated to a subcommand, we edit cmd_name before executing + # the rest of the logic here. + if cmd_name == 'help': + remaining_args += ['--help'] + args = remaining_args + cmd_name = args[0] if cmd_name == 'test': return mtest.run(remaining_args) elif cmd_name == 'setup': @@ -299,7 +304,7 @@ def run(original_args, mainfile=None): try: return mconf.run(remaining_args) except MesonException as e: - mlog.log(mlog.red('\nError configuring project:'), e) + mlog.exception(e) sys.exit(1) elif cmd_name == 'wrap': return wraptool.run(remaining_args) @@ -319,8 +324,8 @@ def run(original_args, mainfile=None): try: sys.exit(run_script_command(args[1:])) except MesonException as e: - mlog.log(mlog.red('\nError in {} helper script:'.format(script))) - mlog.log(e) + mlog.error('\nError in {} helper script:'.format(script)) + mlog.exception(e) sys.exit(1) args = args[2:] handshake = True @@ -363,13 +368,7 @@ def run(original_args, mainfile=None): app.generate() except Exception as e: if isinstance(e, MesonException): - mlog.log() - if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'): - mlog.log('%s:%d:%d:' % (e.file, e.lineno, e.colno), mlog.red('ERROR: '), end='') - else: - mlog.log(mlog.red('ERROR: '), end='') - # Error message - mlog.log(e) + mlog.exception(e) # Path to log file mlog.shutdown() logfile = os.path.join(app.build_dir, environment.Environment.log_dir, mlog.log_fname) diff --git a/mesonbuild/minit.py b/mesonbuild/minit.py index 98817cb..0461cd9 100644 --- a/mesonbuild/minit.py +++ b/mesonbuild/minit.py @@ -1,5 +1,4 @@ # Copyright 2017 The Meson development team -from pyclbr import Function # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,8 +14,10 @@ from pyclbr import Function """Code that creates simple startup projects.""" -import os, sys, argparse, re +import os, sys, argparse, re, shutil from glob import glob +from mesonbuild import mesonlib +from mesonbuild.environment import detect_ninja lib_h_template = '''#pragma once #if defined _WIN32 || defined __CYGWIN__ @@ -107,7 +108,7 @@ pkg_mod.generate( ) ''' -hello_c_template = '''#include <stdio.h> +hello_c_template = '''#include <stdio.h> #define PROJECT_NAME "{project_name}" @@ -123,16 +124,15 @@ int main(int argc, char **argv) {{ hello_c_meson_template = '''project('{project_name}', 'c', version : '{version}', - default_options : ['warning_level=3', - 'cpp_std=c++14']) + default_options : ['warning_level=3']) exe = executable('{exe_name}', '{source_name}', install : true) - + test('basic', exe) ''' -hello_cpp_template = '''#include <iostream> +hello_cpp_template = '''#include <iostream> #define PROJECT_NAME "{project_name}" @@ -148,11 +148,12 @@ int main(int argc, char **argv) {{ hello_cpp_meson_template = '''project('{project_name}', 'cpp', version : '{version}', - default_options : ['warning_level=3']) + default_options : ['warning_level=3', + 'cpp_std=c++14']) exe = executable('{exe_name}', '{source_name}', install : true) - + test('basic', exe) ''' @@ -178,9 +179,9 @@ class {utoken}_PUBLIC {class_name} {{ public: {class_name}(); int get_number() const; - + private: - + int number; }}; @@ -270,7 +271,6 @@ ninja -C builddir def create_exe_c_sample(project_name, project_version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) - uppercase_token = lowercase_token.upper() source_name = lowercase_token + '.c' open(source_name, 'w').write(hello_c_template.format(project_name=project_name)) open('meson.build', 'w').write(hello_c_meson_template.format(project_name=project_name, @@ -291,7 +291,7 @@ def create_lib_c_sample(project_name, version): 'function_name': function_name, 'header_file': lib_h_name, 'source_file': lib_c_name, - 'test_source_file': test_c_name, + 'test_source_file': test_c_name, 'test_exe_name': lowercase_token, 'project_name': project_name, 'lib_name': lowercase_token, @@ -305,13 +305,12 @@ def create_lib_c_sample(project_name, version): def create_exe_cpp_sample(project_name, project_version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) - uppercase_token = lowercase_token.upper() source_name = lowercase_token + '.cpp' open(source_name, 'w').write(hello_cpp_template.format(project_name=project_name)) open('meson.build', 'w').write(hello_cpp_meson_template.format(project_name=project_name, - exe_name=lowercase_token, - source_name=source_name, - version=project_version)) + exe_name=lowercase_token, + source_name=source_name, + version=project_version)) def create_lib_cpp_sample(project_name, version): lowercase_token = re.sub(r'[^a-z0-9]', '_', project_name.lower()) @@ -328,7 +327,7 @@ def create_lib_cpp_sample(project_name, version): 'namespace': namespace, 'header_file': lib_h_name, 'source_file': lib_c_name, - 'test_source_file': test_c_name, + 'test_source_file': test_c_name, 'test_exe_name': lowercase_token, 'project_name': project_name, 'lib_name': lowercase_token, @@ -359,15 +358,123 @@ def create_sample(options): raise RuntimeError('Unreachable code') print(info_message) +def autodetect_options(options, sample=False): + if not options.name: + options.name = os.path.basename(os.getcwd()) + if not re.match('[a-zA-Z_][a-zA-Z0-9]*', options.name) and sample: + print('Name of current directory "{}" is not usable as a sample project name.\n' + 'Specify a project name with --name.'.format(options.name)) + sys.exit(1) + print('Using "{}" (name of current directory) as project name.' + .format(options.name)) + if not options.executable: + options.executable = options.name + print('Using "{}" (project name) as name of executable to build.' + .format(options.executable)) + if sample: + # The rest of the autodetection is not applicable to generating sample projects. + return + if not options.srcfiles: + srcfiles = [] + for f in os.listdir(): + if f.endswith('.cc') or f.endswith('.cpp') or f.endswith('.c'): + srcfiles.append(f) + if not srcfiles: + print("No recognizable source files found.\n" + "Run me in an empty directory to create a sample project.") + sys.exit(1) + options.srcfiles = srcfiles + print("Detected source files: " + ' '.join(srcfiles)) + if not options.language: + for f in options.srcfiles: + if f.endswith('.cc') or f.endswith('.cpp'): + options.language = 'cpp' + break + if f.endswith('.c'): + options.language = 'c' + break + if not options.language: + print("Can't autodetect language, please specify it with -l.") + sys.exit(1) + print("Detected language: " + options.language) + +meson_executable_template = '''project('{project_name}', '{language}', + version : '{version}', + default_options : [{default_options}]) + +executable('{executable}', + {sourcespec},{depspec} + install : true) +''' + +def create_meson_build(options): + if options.type != 'executable': + print('\nGenerating a meson.build file from existing sources is\n' + 'supported only for project type "executable".\n' + 'Run me in an empty directory to create a sample project.') + sys.exit(1) + default_options = ['warning_level=3'] + if options.language == 'cpp': + # This shows how to set this very common option. + default_options += ['cpp_std=c++14'] + # If we get a meson.build autoformatter one day, this code could + # be simplified quite a bit. + formatted_default_options = ', '.join("'{}'".format(x) for x in default_options) + sourcespec = ',\n '.join("'{}'".format(x) for x in options.srcfiles) + depspec = '' + if options.deps: + depspec = '\n dependencies : [\n ' + depspec += ',\n '.join("dependency('{}')".format(x) + for x in options.deps.split(',')) + depspec += '],' + content = meson_executable_template.format(project_name=options.name, + language=options.language, + version=options.version, + executable=options.executable, + sourcespec=sourcespec, + depspec=depspec, + default_options=formatted_default_options) + open('meson.build', 'w').write(content) + print('Generated meson.build file:\n\n' + content) + def run(args): parser = argparse.ArgumentParser(prog='meson') - parser.add_argument('--name', default = 'mesonsample') + parser.add_argument("srcfiles", metavar="sourcefile", nargs="*", + help="source files. default: all recognized files in current directory") + parser.add_argument("-n", "--name", help="project name. default: name of current directory") + parser.add_argument("-e", "--executable", help="executable name. default: project name") + parser.add_argument("-d", "--deps", help="dependencies, comma-separated") + parser.add_argument("-l", "--language", choices=['c', 'cpp'], + help="project language. default: autodetected based on source files") + parser.add_argument("-b", "--build", help="build after generation", action='store_true') + parser.add_argument("--builddir", help="directory for build", default='build') + parser.add_argument("-f", "--force", action="store_true", + help="force overwrite of existing files and directories.") parser.add_argument('--type', default='executable', choices=['executable', 'library']) - parser.add_argument('--language', default='c', choices=['c', 'cpp']) - parser.add_argument('--version', default='1.0') + parser.add_argument('--version', default='0.1') options = parser.parse_args(args) - if len(glob('*')) != 0: - sys.exit('This command must be run in an empty directory.') - create_sample(options) + if len(glob('*')) == 0: + autodetect_options(options, sample=True) + if not options.language: + print('Defaulting to generating a C language project.') + options.language = 'c' + create_sample(options) + else: + autodetect_options(options) + if os.path.isfile('meson.build') and not options.force: + print('meson.build already exists. Use --force to overwrite.') + sys.exit(1) + create_meson_build(options) + if options.build: + if os.path.isdir(options.builddir) and options.force: + print('Build directory already exists, deleting it.') + shutil.rmtree(options.builddir) + print('Building...') + err = os.system('{} "{}"'.format(' '.join(mesonlib.meson_command), options.builddir)) + if err: + sys.exit(1) + err = os.system('{} -C "{}"'.format(detect_ninja(), options.builddir)) + if err: + sys.exit(1) return 0 diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 8cf66af..1805e6c 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -19,8 +19,9 @@ tests and so on. All output is in JSON for simple parsing. Currently only works for the Ninja backend. Others use generated project files and don't need this info.""" -import json, pickle -from . import coredata, build +import json +from . import build, mtest, coredata as cdata +from .backend import ninjabackend import argparse import sys, os import pathlib @@ -132,16 +133,16 @@ def add_keys(optlist, options): for key in keys: opt = options[key] optdict = {'name': key, 'value': opt.value} - if isinstance(opt, coredata.UserStringOption): + if isinstance(opt, cdata.UserStringOption): typestr = 'string' - elif isinstance(opt, coredata.UserBooleanOption): + elif isinstance(opt, cdata.UserBooleanOption): typestr = 'boolean' - elif isinstance(opt, coredata.UserComboOption): + elif isinstance(opt, cdata.UserComboOption): optdict['choices'] = opt.choices typestr = 'combo' - elif isinstance(opt, coredata.UserIntegerOption): + elif isinstance(opt, cdata.UserIntegerOption): typestr = 'integer' - elif isinstance(opt, coredata.UserArrayOption): + elif isinstance(opt, cdata.UserArrayOption): typestr = 'array' else: raise RuntimeError("Unknown option type") @@ -149,7 +150,7 @@ def add_keys(optlist, options): optdict['description'] = opt.description optlist.append(optdict) -def list_buildsystem_files(coredata, builddata): +def list_buildsystem_files(builddata): src_dir = builddata.environment.get_source_dir() # I feel dirty about this. But only slightly. filelist = [] @@ -208,26 +209,15 @@ def run(args): 'change the working directory to it.') return 1 - corefile = os.path.join(datadir, 'coredata.dat') - buildfile = os.path.join(datadir, 'build.dat') - installfile = os.path.join(datadir, 'install.dat') - testfile = os.path.join(datadir, 'meson_test_setup.dat') - benchmarkfile = os.path.join(datadir, 'meson_benchmark_setup.dat') + coredata = cdata.load(options.builddir) + builddata = build.load(options.builddir) + testdata = mtest.load_tests(options.builddir) + benchmarkdata = mtest.load_benchmarks(options.builddir) - # Load all data files - with open(corefile, 'rb') as f: - coredata = pickle.load(f) - with open(buildfile, 'rb') as f: - builddata = pickle.load(f) - with open(testfile, 'rb') as f: - testdata = pickle.load(f) - with open(benchmarkfile, 'rb') as f: - benchmarkdata = pickle.load(f) # Install data is only available with the Ninja backend - if os.path.isfile(installfile): - with open(installfile, 'rb') as f: - installdata = pickle.load(f) - else: + try: + installdata = ninjabackend.load(options.builddir) + except FileNotFoundError: installdata = None if options.list_targets: @@ -237,7 +227,7 @@ def run(args): elif options.target_files is not None: list_target_files(options.target_files, coredata, builddata) elif options.buildsystem_files: - list_buildsystem_files(coredata, builddata) + list_buildsystem_files(builddata) elif options.buildoptions: list_buildoptions(coredata, builddata) elif options.tests: diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 3c34b85..347cede 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -102,19 +102,38 @@ def log(*args, **kwargs): arr = process_markup(args, True) force_print(*arr, **kwargs) -def warning(*args, **kwargs): +def _log_error(severity, *args, **kwargs): from . import environment - - args = (yellow('WARNING:'),) + args + if severity == 'warning': + args = (yellow('WARNING:'),) + args + elif severity == 'error': + args = (red('ERROR:'),) + args + else: + assert False, 'Invalid severity ' + severity if 'location' in kwargs: location = kwargs['location'] del kwargs['location'] - location = '{}:{}:'.format(os.path.join(location.subdir, environment.build_filename), location.lineno) - args = (location,) + args + location_str = '{}:{}:'.format(os.path.join(location.subdir, + environment.build_filename), + location.lineno) + args = (location_str,) + args log(*args, **kwargs) +def error(*args, **kwargs): + return _log_error('error', *args, **kwargs) + +def warning(*args, **kwargs): + return _log_error('warning', *args, **kwargs) + +def exception(e): + log() + if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'): + log('%s:%d:%d:' % (e.file, e.lineno, e.colno), red('ERROR: '), e) + else: + log(red('ERROR:'), e) + # Format a list for logging purposes as a string. It separates # all but the last item with commas, and the last with 'and'. def format_list(list): diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 569011e..8b6397e 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -1350,7 +1350,7 @@ G_END_DECLS''' # - add relevant directories to include dirs incs = [build.IncludeDirs(state.subdir, ['.'] + vapi_includes, False)] sources = [vapi_target] + vapi_depends - rv = InternalDependency(None, incs, [], [], link_with, sources, []) + rv = InternalDependency(None, incs, [], [], link_with, [], sources, []) created_values.append(rv) return ModuleReturnValue(rv, created_values) diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 989e839..9fd9f80 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys import sysconfig from .. import mesonlib, dependencies diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py index 0b7b339..1f548b6 100644 --- a/mesonbuild/modules/unstable_icestorm.py +++ b/mesonbuild/modules/unstable_icestorm.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .. import mesonlib, compilers, mlog +from .. import mesonlib from . import ExtensionModule @@ -33,7 +33,6 @@ class IceStormModule(ExtensionModule): def project(self, interpreter, state, args, kwargs): if not self.yosys_bin: self.detect_binaries(interpreter) - result = [] if not len(args): raise mesonlib.MesonException('Project requires at least one argument, which is the project name.') proj_name = args[0] @@ -46,7 +45,7 @@ class IceStormModule(ExtensionModule): all_sources = interpreter.source_strings_to_files(interpreter.flatten(arg_sources + kwarg_sources)) if 'constraint_file' not in kwargs: raise mesonlib.MesonException('Constraint file not specified.') - + constraint_file = interpreter.source_strings_to_files(kwargs['constraint_file']) if len(constraint_file) != 1: raise mesonlib.MesonException('Constraint file must contain one and only one entry.') @@ -73,13 +72,13 @@ class IceStormModule(ExtensionModule): 'input': asc_target, 'output': bin_fname, 'command': [self.icepack_bin, '@INPUT@', '@OUTPUT@'], - 'build_by_default' : True}) + 'build_by_default': True}) - up_target = interpreter.func_run_target(None, [upload_name], { + interpreter.func_run_target(None, [upload_name], { 'command': [self.iceprog_bin, bin_target]}) - time_target = interpreter.func_run_target(None, [time_name], { - 'command' : [self.icetime_bin, bin_target]}) + interpreter.func_run_target(None, [time_name], { + 'command': [self.icetime_bin, bin_target]}) def initialize(): return IceStormModule() diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index a697106..fbd6e8b 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -28,6 +28,7 @@ import concurrent.futures as conc import platform import signal import random +from copy import deepcopy # GNU autotools interprets a return code of 77 from tests it executes to # mean that the test should be skipped. @@ -164,6 +165,22 @@ def run_with_mono(fname): return True return False +def load_benchmarks(build_dir): + datafile = os.path.join(build_dir, 'meson-private', 'meson_benchmark_setup.dat') + if not os.path.isfile(datafile): + raise TestException('Directory ${!r} does not seem to be a Meson build directory.'.format(build_dir)) + with open(datafile, 'rb') as f: + obj = pickle.load(f) + return obj + +def load_tests(build_dir): + datafile = os.path.join(build_dir, 'meson-private', 'meson_test_setup.dat') + if not os.path.isfile(datafile): + raise TestException('Directory ${!r} does not seem to be a Meson build directory.'.format(build_dir)) + with open(datafile, 'rb') as f: + obj = pickle.load(f) + return obj + class TestHarness: def __init__(self, options): self.options = options @@ -179,12 +196,10 @@ class TestHarness: self.logfile = None self.jsonlogfile = None if self.options.benchmark: - datafile = os.path.join(options.wd, 'meson-private', 'meson_benchmark_setup.dat') + self.tests = load_benchmarks(options.wd) else: - datafile = os.path.join(options.wd, 'meson-private', 'meson_test_setup.dat') - if not os.path.isfile(datafile): - raise TestException('Directory %s does not seem to be a Meson build directory.' % options.wd) - self.load_datafile(datafile) + self.tests = load_tests(options.wd) + self.load_suites() def __del__(self): if self.logfile: @@ -192,7 +207,39 @@ class TestHarness: if self.jsonlogfile: self.jsonlogfile.close() - def run_single_test(self, wrap, test): + def merge_suite_options(self, options, test): + if ":" in options.setup: + if options.setup not in self.build_data.test_setups: + sys.exit("Unknown test setup '%s'." % options.setup) + current = self.build_data.test_setups[options.setup] + else: + full_name = test.project_name + ":" + options.setup + if full_name not in self.build_data.test_setups: + sys.exit("Test setup '%s' not found from project '%s'." % (options.setup, test.project_name)) + current = self.build_data.test_setups[full_name] + if not options.gdb: + options.gdb = current.gdb + if options.timeout_multiplier is None: + options.timeout_multiplier = current.timeout_multiplier + # if options.env is None: + # options.env = current.env # FIXME, should probably merge options here. + if options.wrapper is not None and current.exe_wrapper is not None: + sys.exit('Conflict: both test setup and command line specify an exe wrapper.') + if options.wrapper is None: + options.wrapper = current.exe_wrapper + return current.env.get_env(os.environ.copy()) + + def get_test_env(self, options, test): + if options.setup: + env = self.merge_suite_options(options, test) + else: + env = os.environ.copy() + if isinstance(test.env, build.EnvironmentVariables): + test.env = test.env.get_env(env) + env.update(test.env) + return env + + def run_single_test(self, test): if test.fname[0].endswith('.jar'): cmd = ['java', '-jar'] + test.fname elif not test.is_cross_built and run_with_mono(test.fname[0]): @@ -215,47 +262,64 @@ class TestHarness: stde = None returncode = GNU_SKIP_RETURNCODE else: + test_opts = deepcopy(self.options) + test_env = self.get_test_env(test_opts, test) + wrap = self.get_wrapper(test_opts) + + if test_opts.gdb: + test.timeout = None + cmd = wrap + cmd + test.cmd_args + self.options.test_args starttime = time.time() - child_env = os.environ.copy() - child_env.update(self.options.global_env.get_env(child_env)) - if isinstance(test.env, build.EnvironmentVariables): - test.env = test.env.get_env(child_env) - child_env.update(test.env) if len(test.extra_paths) > 0: - child_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + child_env['PATH'] + test_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + test_env['PATH'] # If MALLOC_PERTURB_ is not set, or if it is set to an empty value, # (i.e., the test or the environment don't explicitly set it), set # it ourselves. We do this unconditionally for regular tests # because it is extremely useful to have. # Setting MALLOC_PERTURB_="0" will completely disable this feature. - if ('MALLOC_PERTURB_' not in child_env or not child_env['MALLOC_PERTURB_']) and not self.options.benchmark: - child_env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) + if ('MALLOC_PERTURB_' not in test_env or not test_env['MALLOC_PERTURB_']) and not self.options.benchmark: + test_env['MALLOC_PERTURB_'] = str(random.randint(1, 255)) - setsid = None stdout = None stderr = None if not self.options.verbose: stdout = subprocess.PIPE stderr = subprocess.PIPE if self.options and self.options.split else subprocess.STDOUT - if not is_windows(): - setsid = os.setsid + # Let gdb handle ^C instead of us + if test_opts.gdb: + previous_sigint_handler = signal.getsignal(signal.SIGINT) + # Make the meson executable ignore SIGINT while gdb is running. + signal.signal(signal.SIGINT, signal.SIG_IGN) + + def preexec_fn(): + if test_opts.gdb: + # Restore the SIGINT handler for the child process to + # ensure it can handle it. + signal.signal(signal.SIGINT, signal.SIG_DFL) + else: + # We don't want setsid() in gdb because gdb needs the + # terminal in order to handle ^C and not show tcsetpgrp() + # errors avoid not being able to use the terminal. + os.setsid() p = subprocess.Popen(cmd, stdout=stdout, stderr=stderr, - env=child_env, + env=test_env, cwd=test.workdir, - preexec_fn=setsid) + preexec_fn=preexec_fn if not is_windows() else None) timed_out = False kill_test = False if test.timeout is None: timeout = None + elif test_opts.timeout_multiplier is not None: + timeout = test.timeout * test_opts.timeout_multiplier else: - timeout = test.timeout * self.options.timeout_multiplier + timeout = test.timeout try: (stdo, stde) = p.communicate(timeout=timeout) except subprocess.TimeoutExpired: @@ -265,6 +329,10 @@ class TestHarness: except KeyboardInterrupt: mlog.warning("CTRL-C detected while running %s" % (test.name)) kill_test = True + finally: + if test_opts.gdb: + # Let us accept ^C again + signal.signal(signal.SIGINT, previous_sigint_handler) if kill_test or timed_out: # Python does not provide multiplatform support for @@ -361,9 +429,6 @@ TIMEOUT: %4d def doit(self): if self.is_run: raise RuntimeError('Test harness object can only be used once.') - if not os.path.isfile(self.datafile): - print('Test data file. Probably this means that you did not run this in the build directory.') - return 1 self.is_run = True tests = self.get_tests() if not tests: @@ -402,15 +467,6 @@ TIMEOUT: %4d ss.add(s) self.suites = list(ss) - def load_tests(self): - with open(self.datafile, 'rb') as f: - self.tests = pickle.load(f) - - def load_datafile(self, datafile): - self.datafile = datafile - self.load_tests() - self.load_suites() - def get_tests(self): if not self.tests: print('No tests defined.') @@ -444,9 +500,9 @@ TIMEOUT: %4d logfile_base = os.path.join(self.options.wd, 'meson-logs', self.options.logbase) if self.options.wrapper: - namebase = os.path.basename(self.get_wrapper()[0]) + namebase = os.path.basename(self.get_wrapper(self.options)[0]) elif self.options.setup: - namebase = self.options.setup + namebase = self.options.setup.replace(":", "_") if namebase: logfile_base += '-' + namebase.replace(' ', '_') @@ -459,16 +515,16 @@ TIMEOUT: %4d self.logfile.write('Log of Meson test suite run on %s\n\n' % datetime.datetime.now().isoformat()) - def get_wrapper(self): + def get_wrapper(self, options): wrap = [] - if self.options.gdb: + if options.gdb: wrap = ['gdb', '--quiet', '--nh'] - if self.options.repeat > 1: + if options.repeat > 1: wrap += ['-ex', 'run', '-ex', 'quit'] # Signal the end of arguments to gdb wrap += ['--args'] - if self.options.wrapper: - wrap += self.options.wrapper + if options.wrapper: + wrap += options.wrapper assert(isinstance(wrap, list)) return wrap @@ -487,28 +543,25 @@ TIMEOUT: %4d futures = [] numlen = len('%d' % len(tests)) self.open_log_files() - wrap = self.get_wrapper() startdir = os.getcwd() if self.options.wd: os.chdir(self.options.wd) + self.build_data = build.load(os.getcwd()) try: for _ in range(self.options.repeat): for i, test in enumerate(tests): visible_name = self.get_pretty_suite(test) - if self.options.gdb: - test.timeout = None - if not test.is_parallel or self.options.gdb: self.drain_futures(futures) futures = [] - res = self.run_single_test(wrap, test) + res = self.run_single_test(test) self.print_stats(numlen, tests, visible_name, res, i) else: if not executor: executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) - f = executor.submit(self.run_single_test, wrap, test) + f = executor.submit(self.run_single_test, test) futures.append((f, numlen, tests, visible_name, i)) if self.options.repeat > 1 and self.fail_count: break @@ -549,26 +602,6 @@ def list_tests(th): for t in tests: print(th.get_pretty_suite(t)) -def merge_suite_options(options): - buildfile = os.path.join(options.wd, 'meson-private/build.dat') - with open(buildfile, 'rb') as f: - build = pickle.load(f) - setups = build.test_setups - if options.setup not in setups: - sys.exit('Unknown test setup: %s' % options.setup) - current = setups[options.setup] - if not options.gdb: - options.gdb = current.gdb - if options.timeout_multiplier is None: - options.timeout_multiplier = current.timeout_multiplier -# if options.env is None: -# options.env = current.env # FIXME, should probably merge options here. - if options.wrapper is not None and current.exe_wrapper is not None: - sys.exit('Conflict: both test setup and command line specify an exe wrapper.') - if options.wrapper is None: - options.wrapper = current.exe_wrapper - return current.env - def rebuild_all(wd): if not os.path.isfile(os.path.join(wd, 'build.ninja')): print("Only ninja backend is supported to rebuild tests before running them.") @@ -594,15 +627,6 @@ def run(args): if options.benchmark: options.num_processes = 1 - if options.setup is not None: - global_env = merge_suite_options(options) - else: - global_env = build.EnvironmentVariables() - if options.timeout_multiplier is None: - options.timeout_multiplier = 1 - - setattr(options, 'global_env', global_env) - if options.verbose and options.quiet: print('Can not be both quiet and verbose at the same time.') return 1 diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index 0191c30..fad7ba0 100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py @@ -54,11 +54,7 @@ def run(args): sys.exit('Unknown command: ' + options.commands[0]) except Exception as e: if isinstance(e, MesonException): - if hasattr(e, 'file') and hasattr(e, 'lineno') and hasattr(e, 'colno'): - mlog.log(mlog.red('\nMeson encountered an error in file %s, line %d, column %d:' % (e.file, e.lineno, e.colno))) - else: - mlog.log(mlog.red('\nMeson encountered an error:')) - mlog.log(e) + mlog.exception(e) else: traceback.print_exc() return 1 diff --git a/msi/createmsi.py b/msi/createmsi.py index 3ea0958..499f4b0 100755 --- a/msi/createmsi.py +++ b/msi/createmsi.py @@ -50,10 +50,14 @@ class PackageGenerator: self.staging_dirs = ['dist', 'dist2'] if self.bytesize == 64: self.progfile_dir = 'ProgramFiles64Folder' - self.redist_path = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\14.11.25325\\MergeModules\\Microsoft_VC141_CRT_x64.msm' + redist_glob = 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\*\\MergeModules\\Microsoft_VC141_CRT_x64.msm' else: self.progfile_dir = 'ProgramFilesFolder' - self.redist_path = 'C:\\Program Files\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\14.11.25325\\MergeModules\\Microsoft_VC141_CRT_x86.msm' + redist_glob = 'C:\\Program Files\\Microsoft Visual Studio\\2017\\Community\\VC\\Redist\\MSVC\\*\\MergeModules\\Microsoft_VC141_CRT_x86.msm' + trials = glob(redist_glob) + if len(trials) != 1: + sys.exit('There are more than one potential redist dirs.') + self.redist_path = trials[0] self.component_num = 0 self.feature_properties = { self.staging_dirs[0]: { @@ -149,7 +153,7 @@ class PackageGenerator: 'SourceFile': self.redist_path, 'DiskId': '1', 'Language': '0', - }) + }) ET.SubElement(product, 'Property', { 'Id': 'WIXUI_INSTALLDIR', @@ -181,7 +185,7 @@ class PackageGenerator: 'AllowAdvertise': 'no', 'Display': 'hidden', 'Level': '1', - }) + }) ET.SubElement(vcredist_feature, 'MergeRef', {'Id': 'VCRedist'}) ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', xml_declaration=True) # ElementTree can not do prettyprinting so do it manually @@ -219,7 +223,6 @@ class PackageGenerator: }) self.component_num += 1 for f in cur_node.files: - file_source = os.path.join(current_dir, f).replace('\\', '\\\\') file_id = os.path.join(current_dir, f).replace('\\', '_').replace('#', '_').replace('-', '_') ET.SubElement(comp_xml_node, 'File', { 'Id': file_id, @@ -253,6 +256,7 @@ class PackageGenerator: if __name__ == '__main__': if not os.path.exists('meson.py'): sys.exit(print('Run me in the top level source dir.')) + subprocess.check_call(['pip', 'install', '--upgrade', 'cx_freeze']) p = PackageGenerator() p.build_dist() diff --git a/run_project_tests.py b/run_project_tests.py index c2c3efe..a1d36ef 100755 --- a/run_project_tests.py +++ b/run_project_tests.py @@ -109,8 +109,6 @@ def setup_commands(optbackend): if backend is None: if msbuild_exe is not None: backend = 'vs' # Meson will auto-detect VS version to use - elif mesonlib.is_osx(): - backend = 'xcode' else: backend = 'ninja' # Set backend arguments for Meson @@ -445,8 +443,8 @@ def skippable(suite, test): if not suite.endswith('frameworks'): return True - # gtk-doc test is always skipped pending upstream fixes for spaces in - # filenames landing in distros + # gtk-doc test may be skipped, pending upstream fixes for spaces in + # filenames landing in the distro used for CI if test.endswith('10 gtk-doc'): return True @@ -465,6 +463,28 @@ def skippable(suite, test): # Other framework tests are allowed to be skipped on other platforms return True +def skip_csharp(backend): + if backend is not Backend.ninja: + return True + if not shutil.which('resgen'): + return True + if shutil.which('mcs'): + return False + if shutil.which('csc'): + # Only support VS2017 for now. Earlier versions fail + # under CI in mysterious ways. + try: + stdo = subprocess.check_output(['csc', '/version']) + except subprocess.CalledProcessError: + return True + # Having incrementing version numbers would be too easy. + # Microsoft reset the versioning back to 1.0 (from 4.x) + # when they got the Roslyn based compiler. Thus there + # is NO WAY to reliably do version number comparisons. + # Only support the version that ships with VS2017. + return not stdo.startswith(b'2.') + return True + def detect_tests_to_run(): # Name, subdirectory, skip condition. all_tests = [ @@ -478,7 +498,7 @@ def detect_tests_to_run(): ('platform-linux', 'linuxlike', mesonlib.is_osx() or mesonlib.is_windows()), ('java', 'java', backend is not Backend.ninja or mesonlib.is_osx() or not have_java()), - ('C#', 'csharp', backend is not Backend.ninja or not shutil.which('mcs')), + ('C#', 'csharp', skip_csharp(backend)), ('vala', 'vala', backend is not Backend.ninja or not shutil.which('valac')), ('rust', 'rust', backend is not Backend.ninja or not shutil.which('rustc')), ('d', 'd', backend is not Backend.ninja or not have_d_compiler()), diff --git a/run_unittests.py b/run_unittests.py index 96d6045..9c7b16b 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -21,12 +21,11 @@ import tempfile import textwrap import os import shutil -import sys import unittest from unittest import mock from configparser import ConfigParser from glob import glob -from pathlib import PurePath +from pathlib import (PurePath, Path) import mesonbuild.mlog import mesonbuild.compilers @@ -36,7 +35,7 @@ import mesonbuild.coredata import mesonbuild.modules.gnome from mesonbuild.interpreter import ObjectHolder from mesonbuild.mesonlib import ( - is_linux, is_windows, is_osx, is_cygwin, is_dragonflybsd, + is_windows, is_osx, is_cygwin, is_dragonflybsd, windows_proof_rmtree, python_command, meson_command, version_compare, ) from mesonbuild.environment import Environment, detect_ninja @@ -50,6 +49,9 @@ from run_tests import should_run_linux_cross_tests def get_dynamic_section_entry(fname, entry): + if is_cygwin() or is_osx(): + raise unittest.SkipTest('Test only applicable to ELF platforms') + try: raw_out = subprocess.check_output(['readelf', '-d', fname], universal_newlines=True) @@ -427,6 +429,20 @@ class InternalTests(unittest.TestCase): kwargs = {'sources': [1, 2, 3], 'pch_sources': [4, 5, 6]} self.assertEqual([[1, 2, 3], [4, 5, 6]], extract(kwargs, 'sources', 'pch_sources')) + def test_snippets(self): + hashcounter = re.compile('^ *(#)+') + snippet_dir = Path('docs/markdown/snippets') + self.assertTrue(snippet_dir.is_dir()) + for f in snippet_dir.glob('*'): + self.assertTrue(f.is_file()) + if f.suffix == '.md': + for line in f.open(): + m = re.match(hashcounter, line) + if m: + self.assertEqual(len(m.group(0)), 2, 'All headings in snippets must have two hash symbols: ' + f.name) + else: + if f.name != 'add_release_note_snippets_here': + self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name) class BasePlatformTests(unittest.TestCase): def setUp(self): @@ -526,9 +542,15 @@ class BasePlatformTests(unittest.TestCase): self.privatedir = os.path.join(self.builddir, 'meson-private') if inprocess: try: - (returncode, out, _) = run_configure(self.meson_mainfile, self.meson_args + args + extra_args) + (returncode, out, err) = run_configure(self.meson_mainfile, self.meson_args + args + extra_args) + if 'MESON_SKIP_TEST' in out: + raise unittest.SkipTest('Project requested skipping.') if returncode != 0: self._print_meson_log() + print('Stdout:\n') + print(out) + print('Stderr:\n') + print(err) raise RuntimeError('Configure failed') except: self._print_meson_log() @@ -949,6 +971,31 @@ class AllPlatformTests(BasePlatformTests): # Setup with only a timeout works self._run(self.mtest_command + ['--setup=timeout']) + def test_testsetup_selection(self): + testdir = os.path.join(self.unit_test_dir, '13 testsetup selection') + self.init(testdir) + self.build() + + # Run tests without setup + self.run_tests() + + self.assertRaises(subprocess.CalledProcessError, self._run, self.mtest_command + ['--setup=missingfromfoo']) + self._run(self.mtest_command + ['--setup=missingfromfoo', '--no-suite=foo:']) + + self._run(self.mtest_command + ['--setup=worksforall']) + self._run(self.mtest_command + ['--setup=main:worksforall']) + + self.assertRaises(subprocess.CalledProcessError, self._run, + self.mtest_command + ['--setup=onlyinbar']) + self.assertRaises(subprocess.CalledProcessError, self._run, + self.mtest_command + ['--setup=onlyinbar', '--no-suite=main:']) + self._run(self.mtest_command + ['--setup=onlyinbar', '--no-suite=main:', '--no-suite=foo:']) + self._run(self.mtest_command + ['--setup=bar:onlyinbar']) + self.assertRaises(subprocess.CalledProcessError, self._run, + self.mtest_command + ['--setup=foo:onlyinbar']) + self.assertRaises(subprocess.CalledProcessError, self._run, + self.mtest_command + ['--setup=main:onlyinbar']) + def assertFailedTestCount(self, failure_count, command): try: self._run(command) @@ -1331,9 +1378,9 @@ class AllPlatformTests(BasePlatformTests): subprocess.check_call(['git', 'config', 'user.email', 'teh_coderz@example.com'], cwd=project_dir) subprocess.check_call(['git', 'add', 'meson.build', 'distexe.c'], cwd=project_dir, - stdout=subprocess.DEVNULL) + stdout=subprocess.DEVNULL) subprocess.check_call(['git', 'commit', '-a', '-m', 'I am a project'], cwd=project_dir, - stdout=subprocess.DEVNULL) + stdout=subprocess.DEVNULL) try: self.dist_impl(git_init) @@ -1474,7 +1521,6 @@ int main(int argc, char **argv) { cmd += ['-c', source, '-o', objectfile] + extra_args subprocess.check_call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - def test_prebuilt_object(self): (compiler, _, object_suffix, _) = self.detect_prebuild_env() tdir = os.path.join(self.unit_test_dir, '14 prebuilt object') @@ -1748,7 +1794,15 @@ int main(int argc, char **argv) { workdir=tmpdir) self._run(ninja, workdir=os.path.join(tmpdir, 'builddir')) - + with tempfile.TemporaryDirectory() as tmpdir: + open(os.path.join(tmpdir, 'foo.' + lang), 'w').write('int main() {}') + self._run(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. + # If we are using an external test executable (most commonly + # in Debian autopkgtests) then the mocking won't work. + @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.') def test_cross_file_system_paths(self): if is_windows(): raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)') @@ -1796,6 +1850,14 @@ int main(int argc, char **argv) { self.init(testdir, ['--cross-file=' + name], inprocess=True) self.wipe() + def test_compiler_run_command(self): + ''' + The test checks that the compiler object can be passed to + run_command(). + ''' + testdir = os.path.join(self.unit_test_dir, '23 compiler run_command') + self.init(testdir) + class FailureTests(BasePlatformTests): ''' @@ -1961,8 +2023,8 @@ class FailureTests(BasePlatformTests): env = Environment('', self.builddir, self.meson_command, get_fake_options(self.prefix), []) try: - objc = env.detect_objc_compiler(False) - objcpp = env.detect_objcpp_compiler(False) + env.detect_objc_compiler(False) + env.detect_objcpp_compiler(False) except EnvironmentException: code = "add_languages('objc')\nadd_languages('objcpp')" self.assertMesonRaises(code, "Unknown compiler") @@ -2103,6 +2165,9 @@ class LinuxlikeTests(BasePlatformTests): is true and not when it is false. This can't be an ordinary test case because we need to inspect the compiler database. ''' + if is_cygwin() or is_osx(): + raise unittest.SkipTest('PIC not relevant') + testdir = os.path.join(self.common_test_dir, '3 static') self.init(testdir) compdb = self.get_compdb() @@ -2190,6 +2255,8 @@ class LinuxlikeTests(BasePlatformTests): database. https://github.com/mesonbuild/meson/issues/864 ''' + if not shutil.which('valac'): + raise unittest.SkipTest('valac not installed.') testdir = os.path.join(self.vala_test_dir, '5 target glib') self.init(testdir) compdb = self.get_compdb() @@ -2246,11 +2313,8 @@ class LinuxlikeTests(BasePlatformTests): if not shutil.which('qmake-qt5'): if not shutil.which('qmake'): raise unittest.SkipTest('QMake not found') - # For some inexplicable reason qmake --version gives different - # results when run from the command line vs invoked by Python. - # Check for both cases in case this behavior changes in the future. - output = subprocess.getoutput(['qmake', '--version']) - if 'Qt version 5' not in output and 'qt5' not in output: + output = subprocess.getoutput('qmake --version') + if 'Qt version 5' not in output: raise unittest.SkipTest('Qmake found, but it is not for Qt 5.') # Disable pkg-config codepath and force searching with qmake/qmake-qt5 testdir = os.path.join(self.framework_test_dir, '4 qt') @@ -2262,6 +2326,9 @@ class LinuxlikeTests(BasePlatformTests): self.assertTrue(msg in mesonlog or msg2 in mesonlog) def _test_soname_impl(self, libpath, install): + if is_cygwin() or is_osx(): + raise unittest.SkipTest('Test only applicable to ELF and linuxlike sonames') + testdir = os.path.join(self.unit_test_dir, '1 soname') self.init(testdir) self.build() @@ -2335,7 +2402,9 @@ class LinuxlikeTests(BasePlatformTests): # Check that all the listed -std=xxx options for this compiler work # just fine when used for v in compiler.get_options()[lang_std].choices: - if compiler.get_id() == 'clang' and version_compare(compiler.version, '<5.0.0') and '17' in v: + if (compiler.get_id() == 'clang' and '17' in v and + (version_compare(compiler.version, '<5.0.0') or + (compiler.clang_type == mesonbuild.compilers.CLANG_OSX and version_compare(compiler.version, '<9.2')))): continue std_opt = '{}={}'.format(lang_std, v) self.init(testdir, ['-D' + std_opt]) @@ -2468,6 +2537,9 @@ class LinuxlikeTests(BasePlatformTests): self.assertNotIn('-Werror', c03_comp) def test_run_installed(self): + if is_cygwin() or is_osx(): + raise unittest.SkipTest('LD_LIBRARY_PATH and RPATH not applicable') + testdir = os.path.join(self.unit_test_dir, '7 run installed') self.init(testdir) self.build() @@ -2537,6 +2609,8 @@ class LinuxlikeTests(BasePlatformTests): self.assertTrue(gobject_found) def test_build_rpath(self): + if is_cygwin(): + raise unittest.SkipTest('Windows PE/COFF binaries do not use RPATH') testdir = os.path.join(self.unit_test_dir, '11 build_rpath') self.init(testdir) self.build() @@ -2554,6 +2628,9 @@ class LinuxlikeTests(BasePlatformTests): self.assertEqual(install_rpath, 'baz') def test_pch_with_address_sanitizer(self): + if is_cygwin(): + raise unittest.SkipTest('asan not available on Cygwin') + testdir = os.path.join(self.common_test_dir, '13 pch') self.init(testdir, ['-Db_sanitize=address']) self.build() @@ -2606,6 +2683,9 @@ endian = 'little' Test that valac outputs generated C files in the expected location when the builddir is a subdir of the source tree. ''' + if not shutil.which('valac'): + raise unittest.SkipTest('valac not installed.') + testdir = os.path.join(self.vala_test_dir, '8 generated sources') newdir = os.path.join(self.builddir, 'srctree') shutil.copytree(testdir, newdir) @@ -2729,7 +2809,7 @@ def unset_envs(): if __name__ == '__main__': unset_envs() cases = ['InternalTests', 'AllPlatformTests', 'FailureTests'] - if is_linux(): + if not is_windows(): cases += ['LinuxlikeTests'] if should_run_linux_cross_tests(): cases += ['LinuxArmCrossCompileTests'] @@ -19,8 +19,9 @@ import sys from mesonbuild.coredata import version -if sys.version_info[0] < 3: - print('Tried to install with Python 2, Meson only supports Python 3.') +if sys.version_info < (3, 5, 0): + print('Tried to install with an unsupported version of Python. ' + 'Meson requires Python 3.5.0 or greater') sys.exit(1) # We need to support Python installations that have nothing but the basic @@ -62,6 +63,7 @@ setup(name='meson', author_email='jpakkane@gmail.com', url='http://mesonbuild.com', license=' Apache License, Version 2.0', + python_requires='>=3.5', packages=['mesonbuild', 'mesonbuild.backend', 'mesonbuild.compilers', diff --git a/test cases/common/145 whole archive/allofme/meson.build b/test cases/common/145 whole archive/allofme/meson.build deleted file mode 100644 index f5c2027..0000000 --- a/test cases/common/145 whole archive/allofme/meson.build +++ /dev/null @@ -1 +0,0 @@ -stlib = static_library('allofme', '../libfile.c') diff --git a/test cases/common/145 whole archive/exe/meson.build b/test cases/common/145 whole archive/exe/meson.build index f47a246..91d298d 100644 --- a/test cases/common/145 whole archive/exe/meson.build +++ b/test cases/common/145 whole archive/exe/meson.build @@ -1,2 +1 @@ -exe = executable('prog', '../prog.c', - link_with : dylib) +exe = executable('prog', '../prog.c', link_with : sh_func2_linked_func1) diff --git a/test cases/common/145 whole archive/exe2/meson.build b/test cases/common/145 whole archive/exe2/meson.build index 5365f03..9184864 100644 --- a/test cases/common/145 whole archive/exe2/meson.build +++ b/test cases/common/145 whole archive/exe2/meson.build @@ -1 +1 @@ -exe2 = executable('prog2', '../prog.c', link_with : dylib2) +exe2 = executable('prog2', '../prog.c', link_with : sh_only_link_whole) diff --git a/test cases/common/145 whole archive/exe3/meson.build b/test cases/common/145 whole archive/exe3/meson.build new file mode 100644 index 0000000..82cf57e --- /dev/null +++ b/test cases/common/145 whole archive/exe3/meson.build @@ -0,0 +1 @@ +exe3 = executable('prog3', '../prog.c', link_with : sh_func2_dep_func1) diff --git a/test cases/common/145 whole archive/exe4/meson.build b/test cases/common/145 whole archive/exe4/meson.build new file mode 100644 index 0000000..0781250 --- /dev/null +++ b/test cases/common/145 whole archive/exe4/meson.build @@ -0,0 +1 @@ +exe4 = executable('prog4', '../prog.c', link_with : sh_func2_transdep_func1) diff --git a/test cases/common/145 whole archive/libfile.c b/test cases/common/145 whole archive/func1.c index b2690a0..b2690a0 100644 --- a/test cases/common/145 whole archive/libfile.c +++ b/test cases/common/145 whole archive/func1.c diff --git a/test cases/common/145 whole archive/dylib.c b/test cases/common/145 whole archive/func2.c index 9e287a4..9e287a4 100644 --- a/test cases/common/145 whole archive/dylib.c +++ b/test cases/common/145 whole archive/func2.c diff --git a/test cases/common/145 whole archive/meson.build b/test cases/common/145 whole archive/meson.build index 617ae03..012df33 100644 --- a/test cases/common/145 whole archive/meson.build +++ b/test cases/common/145 whole archive/meson.build @@ -10,15 +10,41 @@ if cc.get_id() == 'msvc' endif endif -subdir('allofme') -subdir('shlib') +# Test 1: link_whole keeps all symbols +# Make static func1 +subdir('st_func1') +# Make shared func2 linking whole func1 archive +subdir('sh_func2_linked_func1') +# Link exe with shared library only subdir('exe') - +# Test that both func1 and func2 are accessible from shared library test('prog', exe) -# link_whole only -subdir('stlib') -subdir('wholeshlib') +# Test 2: link_whole can be used instead of source list, see #2180 +# Make static func2 +subdir('st_func2') +# Link both func1 and func2 into same shared library +# which does not have any sources other than 2 static libraries +subdir('sh_only_link_whole') +# Link exe2 with shared library only subdir('exe2') - +# Test that both func1 and func2 are accessible from shared library test('prog2', exe2) + +# Test 3: link_whole can be used in declare_dependency() +func1_dep = declare_dependency(link_whole : [st_func1]) +# Use dependency to link func1 into shared library +subdir('sh_func2_dep_func1') +# Link exe3 with shared library +subdir('exe3') +# Test that both func1 and func2 are accessible from shared library +test('prog3', exe3) + +# Test 4: link_whole can be used in transitive declare_dependency() +func1_trans_dep = declare_dependency(dependencies : func1_dep) +# Use transitive dependency to link func1 into shared library +subdir('sh_func2_transdep_func1') +# Link exe4 with shared library +subdir('exe4') +# Test that both func1 and func2 are accessible from shared library +test('prog4', exe4) diff --git a/test cases/common/145 whole archive/sh_func2_dep_func1/meson.build b/test cases/common/145 whole archive/sh_func2_dep_func1/meson.build new file mode 100644 index 0000000..92baca6 --- /dev/null +++ b/test cases/common/145 whole archive/sh_func2_dep_func1/meson.build @@ -0,0 +1,4 @@ +# Same as sh_func2_linked_func1, # func2.c does not depend on func1(), +# so without link_whole compiler would throw func1() away. +# This is the same version of the test with a dependency object instead. +sh_func2_dep_func1 = shared_library('sh_func2_dep_func1', '../func2.c', dependencies : func1_dep) diff --git a/test cases/common/145 whole archive/sh_func2_linked_func1/meson.build b/test cases/common/145 whole archive/sh_func2_linked_func1/meson.build new file mode 100644 index 0000000..2858f65 --- /dev/null +++ b/test cases/common/145 whole archive/sh_func2_linked_func1/meson.build @@ -0,0 +1,3 @@ +# Nothing in func2.c uses func1, so the linker would throw it +# away and thus linking the exe would fail. +sh_func2_linked_func1 = shared_library('sh_func2_linked_func1', '../func2.c', link_whole : st_func1) diff --git a/test cases/common/145 whole archive/sh_func2_transdep_func1/meson.build b/test cases/common/145 whole archive/sh_func2_transdep_func1/meson.build new file mode 100644 index 0000000..0703077 --- /dev/null +++ b/test cases/common/145 whole archive/sh_func2_transdep_func1/meson.build @@ -0,0 +1,6 @@ +# Same as sh_func2_dep_func1 but dependency is transitive. +# func2.c does not have any reference to func1() so without link_whole compiler +# should throw func1() out. +sh_func2_transdep_func1 = shared_library( + 'sh_func2_transdep_func1', '../func2.c', + dependencies : func1_trans_dep) diff --git a/test cases/common/145 whole archive/sh_only_link_whole/meson.build b/test cases/common/145 whole archive/sh_only_link_whole/meson.build new file mode 100644 index 0000000..64baabd --- /dev/null +++ b/test cases/common/145 whole archive/sh_only_link_whole/meson.build @@ -0,0 +1 @@ +sh_only_link_whole = shared_library('sh_only_link_whole', link_whole : [st_func1, st_func2]) diff --git a/test cases/common/145 whole archive/shlib/meson.build b/test cases/common/145 whole archive/shlib/meson.build deleted file mode 100644 index 34a1b78..0000000 --- a/test cases/common/145 whole archive/shlib/meson.build +++ /dev/null @@ -1,4 +0,0 @@ -# Nothing in dylib.c uses func1, so the linker would throw it -# away and thus linking the exe would fail. -dylib = shared_library('shlib', '../dylib.c', - link_whole : stlib) diff --git a/test cases/common/145 whole archive/st_func1/meson.build b/test cases/common/145 whole archive/st_func1/meson.build new file mode 100644 index 0000000..c84d781 --- /dev/null +++ b/test cases/common/145 whole archive/st_func1/meson.build @@ -0,0 +1 @@ +st_func1 = static_library('st_func1', '../func1.c') diff --git a/test cases/common/145 whole archive/st_func2/meson.build b/test cases/common/145 whole archive/st_func2/meson.build new file mode 100644 index 0000000..2732f96 --- /dev/null +++ b/test cases/common/145 whole archive/st_func2/meson.build @@ -0,0 +1 @@ +st_func2 = static_library('st_func2', '../func2.c') diff --git a/test cases/common/145 whole archive/stlib/meson.build b/test cases/common/145 whole archive/stlib/meson.build deleted file mode 100644 index 07a434e..0000000 --- a/test cases/common/145 whole archive/stlib/meson.build +++ /dev/null @@ -1 +0,0 @@ -static = static_library('static', '../dylib.c') diff --git a/test cases/common/145 whole archive/wholeshlib/meson.build b/test cases/common/145 whole archive/wholeshlib/meson.build deleted file mode 100644 index 69a1995..0000000 --- a/test cases/common/145 whole archive/wholeshlib/meson.build +++ /dev/null @@ -1 +0,0 @@ -dylib2 = shared_library('link_whole', link_whole : [stlib, static]) diff --git a/test cases/common/174 preserve gendir/base.inp b/test cases/common/178 preserve gendir/base.inp index df967b9..df967b9 100644 --- a/test cases/common/174 preserve gendir/base.inp +++ b/test cases/common/178 preserve gendir/base.inp diff --git a/test cases/common/174 preserve gendir/com/mesonbuild/subbie.inp b/test cases/common/178 preserve gendir/com/mesonbuild/subbie.inp index df0f4e9..df0f4e9 100644 --- a/test cases/common/174 preserve gendir/com/mesonbuild/subbie.inp +++ b/test cases/common/178 preserve gendir/com/mesonbuild/subbie.inp diff --git a/test cases/common/174 preserve gendir/genprog.py b/test cases/common/178 preserve gendir/genprog.py index 1e10998..1e10998 100755 --- a/test cases/common/174 preserve gendir/genprog.py +++ b/test cases/common/178 preserve gendir/genprog.py diff --git a/test cases/common/174 preserve gendir/meson.build b/test cases/common/178 preserve gendir/meson.build index ce219f0..ce219f0 100644 --- a/test cases/common/174 preserve gendir/meson.build +++ b/test cases/common/178 preserve gendir/meson.build diff --git a/test cases/common/174 preserve gendir/testprog.c b/test cases/common/178 preserve gendir/testprog.c index 46b4602..46b4602 100644 --- a/test cases/common/174 preserve gendir/testprog.c +++ b/test cases/common/178 preserve gendir/testprog.c diff --git a/test cases/common/179 source in dep/bar.cpp b/test cases/common/179 source in dep/bar.cpp new file mode 100644 index 0000000..bda8cb6 --- /dev/null +++ b/test cases/common/179 source in dep/bar.cpp @@ -0,0 +1,5 @@ +extern "C" int foo(); + +int main(int, char**) { + return foo() != 42; +} diff --git a/test cases/common/179 source in dep/foo.c b/test cases/common/179 source in dep/foo.c new file mode 100644 index 0000000..1ecfa8c --- /dev/null +++ b/test cases/common/179 source in dep/foo.c @@ -0,0 +1,3 @@ +int foo() { + return 42; +} diff --git a/test cases/common/179 source in dep/meson.build b/test cases/common/179 source in dep/meson.build new file mode 100644 index 0000000..e2c007e --- /dev/null +++ b/test cases/common/179 source in dep/meson.build @@ -0,0 +1,6 @@ +project('foo', 'c', 'cpp') + +dep = declare_dependency(sources : 'foo.c') + +executable('bar', 'bar.cpp', + dependencies : dep) diff --git a/test cases/common/180 generator link whole/export.h b/test cases/common/180 generator link whole/export.h new file mode 100644 index 0000000..f4f6f45 --- /dev/null +++ b/test cases/common/180 generator link whole/export.h @@ -0,0 +1,18 @@ +#pragma once + +#if defined BUILDING_EMBEDDED + #define DLL_PUBLIC +#elif defined _WIN32 || defined __CYGWIN__ + #if defined BUILDING_DLL + #define DLL_PUBLIC __declspec(dllexport) + #else + #define DLL_PUBLIC __declspec(dllimport) + #endif +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif diff --git a/test cases/common/180 generator link whole/generator.py b/test cases/common/180 generator link whole/generator.py new file mode 100755 index 0000000..0076b74 --- /dev/null +++ b/test cases/common/180 generator link whole/generator.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 + +import os +import os.path +import sys + + +def main(): + name = os.path.splitext(os.path.basename(sys.argv[1]))[0] + out = sys.argv[2] + hname = os.path.join(out, name + '.h') + cname = os.path.join(out, name + '.c') + print(os.getcwd(), hname) + with open(hname, 'w') as hfile: + hfile.write(''' +#pragma once +#include "export.h" +int DLL_PUBLIC {name}(); +'''.format(name=name)) + with open(cname, 'w') as cfile: + cfile.write(''' +#include "{name}.h" +int {name}() {{ + return {size}; +}} +'''.format(name=name, size=len(name))) + + +if __name__ == '__main__': + main() diff --git a/test cases/common/180 generator link whole/main.c b/test cases/common/180 generator link whole/main.c new file mode 100644 index 0000000..acf8717 --- /dev/null +++ b/test cases/common/180 generator link whole/main.c @@ -0,0 +1,11 @@ +#include "meson_test_function.h" + +#include <stdio.h> + +int main() { + if (meson_test_function() != 19) { + printf("Bad meson_test_function()\n"); + return 1; + } + return 0; +} diff --git a/test cases/common/180 generator link whole/meson.build b/test cases/common/180 generator link whole/meson.build new file mode 100644 index 0000000..30ae9c6 --- /dev/null +++ b/test cases/common/180 generator link whole/meson.build @@ -0,0 +1,65 @@ +project('generator link_whole', 'c') + +cc = meson.get_compiler('c') +if cc.get_id() == 'msvc' + if cc.version().version_compare('<19') + error('MESON_SKIP_TEST link_whole only works on VS2015 or newer.') + endif +endif + +# This just generates foo.h and foo.c with int foo() defined. +gen_py = find_program('generator.py') +gen = generator(gen_py, + output: ['@BASENAME@.h', '@BASENAME@.c'], + arguments : ['@INPUT@', '@BUILD_DIR@']) + +# Test 1: link directly into executable +srcs = gen.process('meson_test_function.tmpl') +exe = executable('exe1', [srcs, 'main.c'], c_args : '-DBUILDING_EMBEDDED') +test('test1', exe) + +# Test 2: link into shared library and access from executable +srcs = gen.process('meson_test_function.tmpl') +shlib2 = shared_library('shlib2', [srcs], c_args : '-DBUILDING_DLL') +exe = executable('exe2', 'main.c', + link_with : shlib2, + include_directories : shlib2.private_dir_include(), +) +test('test2', exe) + +# Test 3: link into static library and access from executable +srcs = gen.process('meson_test_function.tmpl') +stlib3 = static_library('stlib3', [srcs], c_args : '-DBUILDING_EMBEDDED') +exe = executable('exe3', 'main.c', + c_args : '-DBUILDING_EMBEDDED', + link_with : stlib3, + include_directories : stlib3.private_dir_include(), +) +test('test3', exe) + +# Test 4: link into static library, link into shared +# and access from executable. To make sure static_library +# is not dropped use pull_meson_test_function helper. +srcs = gen.process('meson_test_function.tmpl') +stlib4 = static_library('stlib4', [srcs], c_args : '-DBUILDING_DLL') +shlib4 = shared_library('shlib4', 'pull_meson_test_function.c', + c_args : '-DBUILDING_DLL', + link_with : stlib4, + include_directories : stlib4.private_dir_include(), +) +exe = executable('exe4', 'main.c', + link_with : shlib4, + include_directories : stlib4.private_dir_include(), +) +test('test4', exe) + +# Test 5: link into static library, link_whole into shared +# and access from executable +srcs = gen.process('meson_test_function.tmpl') +stlib5 = static_library('stlib5', [srcs], c_args : '-DBUILDING_DLL') +shlib5 = shared_library('shlib5', link_whole : stlib5) +exe = executable('exe5', 'main.c', + link_with : shlib5, + include_directories : stlib5.private_dir_include(), +) +test('test5', exe) diff --git a/test cases/common/180 generator link whole/meson_test_function.tmpl b/test cases/common/180 generator link whole/meson_test_function.tmpl new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/common/180 generator link whole/meson_test_function.tmpl diff --git a/test cases/common/180 generator link whole/pull_meson_test_function.c b/test cases/common/180 generator link whole/pull_meson_test_function.c new file mode 100644 index 0000000..c54dda6 --- /dev/null +++ b/test cases/common/180 generator link whole/pull_meson_test_function.c @@ -0,0 +1,6 @@ +#include "export.h" +#include "meson_test_function.h" + +int DLL_PUBLIC function_puller() { + return meson_test_function(); +} diff --git a/test cases/common/181 initial c_args/meson.build b/test cases/common/181 initial c_args/meson.build new file mode 100644 index 0000000..70a6e7a --- /dev/null +++ b/test cases/common/181 initial c_args/meson.build @@ -0,0 +1,7 @@ +project('options', 'c') + +# Test passing c_args and c_link_args options from the command line. +assert(get_option('c_args') == ['-march=native', '-funroll-loops'], + 'Incorrect value for c_args option.') +assert(get_option('c_link_args') == ['-random_linker_option'], + 'Incorrect value for c_link_args option.') diff --git a/test cases/common/181 initial c_args/test_args.txt b/test cases/common/181 initial c_args/test_args.txt new file mode 100644 index 0000000..9a6da06 --- /dev/null +++ b/test cases/common/181 initial c_args/test_args.txt @@ -0,0 +1,4 @@ +# This file is not read by meson itself, but by the test framework. +# It is not possible to pass arguments to meson from a file. +['-Dc_args=-march=native', '-Dc_args=-funroll-loops', + '-Dc_link_args=-random_linker_option'] diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build index bcc9a53..33ba4c5 100644 --- a/test cases/common/64 custom header generator/meson.build +++ b/test cases/common/64 custom header generator/meson.build @@ -3,9 +3,9 @@ project('custom header generator', 'c') gen = find_program('makeheader.py') generated_h = custom_target('makeheader.py', -output : 'myheader.lh', # Suffix not .h to ensure this works with custom suffixes, too. -input : 'input.def', -command : [gen, '@INPUT0@', '@OUTPUT0@', files('somefile.txt')]) + output : 'myheader.lh', # Suffix not .h to ensure this works with custom suffixes, too. + input : 'input.def', + command : [gen, '@INPUT0@', '@OUTPUT0@', files('somefile.txt')]) prog = executable('prog', 'prog.c', generated_h) test('gentest', prog) diff --git a/test cases/common/98 gen extra/srcgen3.py b/test cases/common/98 gen extra/srcgen3.py index ad0a5cb..b737114 100644 --- a/test cases/common/98 gen extra/srcgen3.py +++ b/test cases/common/98 gen extra/srcgen3.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import os import sys import argparse diff --git a/test cases/csharp/1 basic/meson.build b/test cases/csharp/1 basic/meson.build index 2ee6a4a..09e46c2 100644 --- a/test cases/csharp/1 basic/meson.build +++ b/test cases/csharp/1 basic/meson.build @@ -1,4 +1,4 @@ project('simple c#', 'cs') -e = executable('prog', 'prog.cs', install : true) +e = executable('prog', 'prog.cs', 'text.cs', install : true) test('basic', e) diff --git a/test cases/csharp/1 basic/prog.cs b/test cases/csharp/1 basic/prog.cs index dfb2400..6ee47b0 100644 --- a/test cases/csharp/1 basic/prog.cs +++ b/test cases/csharp/1 basic/prog.cs @@ -1,7 +1,8 @@ using System; - + public class Prog { static public void Main () { - Console.WriteLine("C# is working."); + TextGetter tg = new TextGetter(); + Console.WriteLine(tg.getText()); } } diff --git a/test cases/csharp/1 basic/text.cs b/test cases/csharp/1 basic/text.cs new file mode 100644 index 0000000..c83c424 --- /dev/null +++ b/test cases/csharp/1 basic/text.cs @@ -0,0 +1,7 @@ +using System; + +public class TextGetter { + public String getText() { + return "C# is working."; + } +} diff --git a/test cases/csharp/4 external dep/meson.build b/test cases/csharp/4 external dep/meson.build index 004d25f..019d618 100644 --- a/test cases/csharp/4 external dep/meson.build +++ b/test cases/csharp/4 external dep/meson.build @@ -1,4 +1,9 @@ project('C# external library', 'cs') -glib_sharp_2 = dependency('glib-sharp-2.0') +glib_sharp_2 = dependency('glib-sharp-2.0', required : false) + +if not glib_sharp_2.found() + error('MESON_SKIP_TEST glib# not found.') +endif + e = executable('prog', 'prog.cs', dependencies: glib_sharp_2, install : true) test('libtest', e, args: [join_paths(meson.current_source_dir(), 'hello.txt')]) diff --git a/test cases/csharp/4 pkgconfig/meson.build b/test cases/csharp/4 pkgconfig/meson.build deleted file mode 100644 index e2ba035..0000000 --- a/test cases/csharp/4 pkgconfig/meson.build +++ /dev/null @@ -1,7 +0,0 @@ -project('C# pkg-config', 'cs') - -nunit_dep = dependency('nunit') -nunit_runner = find_program('nunit-console') - -test_lib = library('test_lib', 'test-lib.cs', dependencies: nunit_dep) -test('nunit test', nunit_runner, args: test_lib) diff --git a/test cases/csharp/4 pkgconfig/test-lib.cs b/test cases/csharp/4 pkgconfig/test-lib.cs deleted file mode 100644 index 29f6795..0000000 --- a/test cases/csharp/4 pkgconfig/test-lib.cs +++ /dev/null @@ -1,11 +0,0 @@ -using NUnit.Framework; - -[TestFixture] -public class NUnitTest -{ - [Test] - public void Test() - { - Assert.AreEqual(1 + 1, 2); - } -} diff --git a/test cases/d/9 features/meson.build b/test cases/d/9 features/meson.build index 9e63710..356e9f3 100644 --- a/test cases/d/9 features/meson.build +++ b/test cases/d/9 features/meson.build @@ -1,8 +1,22 @@ project('D Features', 'd') -# directory for data +# ONLY FOR BACKWARDS COMPATIBILITY. +# DO NOT DO THIS IN NEW CODE! +# USE include_directories() INSTEAD OF BUILDING +# STRINGS TO PATHS MANUALLY! data_dir = join_paths(meson.current_source_dir(), 'data') +e_plain_bcompat = executable('dapp_menu_bcompat', + 'app.d', + d_import_dirs: [data_dir] +) +test('dapp_menu_t_fail_bcompat', e_plain_bcompat, should_fail: true) +test('dapp_menu_t_bcompat', e_plain_bcompat, args: ['menu']) + +# directory for data +# This is the correct way to do this. +data_dir = include_directories('data') + e_plain = executable('dapp_menu', 'app.d', d_import_dirs: [data_dir] @@ -10,6 +24,7 @@ e_plain = executable('dapp_menu', test('dapp_menu_t_fail', e_plain, should_fail: true) test('dapp_menu_t', e_plain, args: ['menu']) + # test feature versions and string imports e_versions = executable('dapp_versions', 'app.d', diff --git a/test cases/frameworks/10 gtk-doc/include/meson.build b/test cases/frameworks/10 gtk-doc/include/meson.build index f6dd99f..aa32885 100644 --- a/test cases/frameworks/10 gtk-doc/include/meson.build +++ b/test cases/frameworks/10 gtk-doc/include/meson.build @@ -13,4 +13,5 @@ generate_enums_docbook = find_program('generate-enums-docbook.py') docbook = custom_target('enum-docbook', output : 'bar.xml', - command : [generate_enums_docbook, '@OUTPUT@', 'BAR', 'BAR_TYPE', 'BAR_FOO']) + command : [generate_enums_docbook, '@OUTPUT@', 'BAR', 'BAR_TYPE', 'BAR_FOO'], + build_by_default : true) diff --git a/test cases/frameworks/10 gtk-doc/installed_files.txt.bak b/test cases/frameworks/10 gtk-doc/installed_files.txt index 9004af2..6f8ca01 100644 --- a/test cases/frameworks/10 gtk-doc/installed_files.txt.bak +++ b/test cases/frameworks/10 gtk-doc/installed_files.txt @@ -1,13 +1,15 @@ +usr/include/foo-version.h +usr/share/gtk-doc/html/foobar/BAR.html usr/share/gtk-doc/html/foobar/foobar.devhelp2 -usr/share/gtk-doc/html/foobar/foobar-foo.html usr/share/gtk-doc/html/foobar/foobar.html +usr/share/gtk-doc/html/foobar/foobar-foo.html +usr/share/gtk-doc/html/foobar/foobar-foo-version.html usr/share/gtk-doc/html/foobar/home.png usr/share/gtk-doc/html/foobar/index.html -usr/share/gtk-doc/html/foobar/index.sgml -usr/share/gtk-doc/html/foobar/left-insensitive.png usr/share/gtk-doc/html/foobar/left.png -usr/share/gtk-doc/html/foobar/right-insensitive.png +usr/share/gtk-doc/html/foobar/left-insensitive.png usr/share/gtk-doc/html/foobar/right.png +usr/share/gtk-doc/html/foobar/right-insensitive.png usr/share/gtk-doc/html/foobar/style.css -usr/share/gtk-doc/html/foobar/up-insensitive.png usr/share/gtk-doc/html/foobar/up.png +usr/share/gtk-doc/html/foobar/up-insensitive.png diff --git a/test cases/frameworks/10 gtk-doc/meson.build b/test cases/frameworks/10 gtk-doc/meson.build index 71f341c..5c22ad0 100644 --- a/test cases/frameworks/10 gtk-doc/meson.build +++ b/test cases/frameworks/10 gtk-doc/meson.build @@ -13,8 +13,15 @@ inc = include_directories('include') subdir('include') -# We have to disable this test until this bug fix has landed to -# distros https://bugzilla.gnome.org/show_bug.cgi?id=753145 -error('MESON_SKIP_TEST can not enable gtk-doc test until upstream fixes have landed.') +# disable this test unless a bug fix for spaces in pathnames is present +# https://bugzilla.gnome.org/show_bug.cgi?id=753145 +result = run_command(gtkdoc, ['--version']) +gtkdoc_ver = result.stdout().strip() +if gtkdoc_ver == '' + gtkdoc_ver = result.stderr().strip() +endif +if gtkdoc_ver.version_compare('<1.26') + error('MESON_SKIP_TEST gtk-doc test requires gtkdoc >= 1.26.') +endif subdir('doc') diff --git a/test cases/frameworks/14 doxygen/include/comedian.h b/test cases/frameworks/14 doxygen/include/comedian.h index 97b5086..d62b283 100644 --- a/test cases/frameworks/14 doxygen/include/comedian.h +++ b/test cases/frameworks/14 doxygen/include/comedian.h @@ -11,7 +11,7 @@ namespace Comedy { * Do the thing people want to happen. */ virtual void tell_joke() = 0; - virtual ~Comedian(); + virtual ~Comedian(){}; }; } diff --git a/test cases/frameworks/14 doxygen/include/spede.h b/test cases/frameworks/14 doxygen/include/spede.h index 8175465..380708a 100644 --- a/test cases/frameworks/14 doxygen/include/spede.h +++ b/test cases/frameworks/14 doxygen/include/spede.h @@ -29,10 +29,7 @@ namespace Comedy { throw std::runtime_error("Not implemented"); } + private: + int num_movies; ///< How many movies has he done. }; - - -private: - - int num_movies; ///< How many movies has he done. } diff --git a/test cases/frameworks/14 doxygen/meson.build b/test cases/frameworks/14 doxygen/meson.build index 55df316..023aa0e 100644 --- a/test cases/frameworks/14 doxygen/meson.build +++ b/test cases/frameworks/14 doxygen/meson.build @@ -1,5 +1,11 @@ project('doxygen test', 'cpp', version : '0.1.0') +spede_inc = include_directories('include') + +spede_src = [ 'src/spede.cpp' ] + +spede_lib = library('spede', spede_src, include_directories: spede_inc) + doxygen = find_program('doxygen', required : false) if not doxygen.found() error('MESON_SKIP_TEST doxygen not found.') diff --git a/test cases/frameworks/14 doxygen/src/spede.cpp b/test cases/frameworks/14 doxygen/src/spede.cpp index 31c8fb2..d382902 100644 --- a/test cases/frameworks/14 doxygen/src/spede.cpp +++ b/test cases/frameworks/14 doxygen/src/spede.cpp @@ -42,7 +42,7 @@ int gesticulate(int force) { Spede::Spede() : num_movies(100) { } -Spede::slap_forehead() { +void Spede::slap_forehead() { gesticulate(42); } diff --git a/test cases/unit/13 testsetup selection/main.c b/test cases/unit/13 testsetup selection/main.c new file mode 100644 index 0000000..cb3f748 --- /dev/null +++ b/test cases/unit/13 testsetup selection/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/test cases/unit/13 testsetup selection/meson.build b/test cases/unit/13 testsetup selection/meson.build new file mode 100644 index 0000000..ae996c5 --- /dev/null +++ b/test cases/unit/13 testsetup selection/meson.build @@ -0,0 +1,10 @@ +project('main', 'c') + +main = executable('main', 'main.c') +test('Test main', main) + +add_test_setup('worksforall') +add_test_setup('missingfromfoo') + +subproject('foo') +subproject('bar') diff --git a/test cases/unit/13 testsetup selection/subprojects/bar/bar.c b/test cases/unit/13 testsetup selection/subprojects/bar/bar.c new file mode 100644 index 0000000..cb3f748 --- /dev/null +++ b/test cases/unit/13 testsetup selection/subprojects/bar/bar.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/test cases/unit/13 testsetup selection/subprojects/bar/meson.build b/test cases/unit/13 testsetup selection/subprojects/bar/meson.build new file mode 100644 index 0000000..1155a88 --- /dev/null +++ b/test cases/unit/13 testsetup selection/subprojects/bar/meson.build @@ -0,0 +1,6 @@ +project('bar', 'c') +bar = executable('bar', 'bar.c') +test('Test bar', bar) +add_test_setup('onlyinbar') +add_test_setup('worksforall') +add_test_setup('missingfromfoo') diff --git a/test cases/unit/13 testsetup selection/subprojects/foo/foo.c b/test cases/unit/13 testsetup selection/subprojects/foo/foo.c new file mode 100644 index 0000000..cb3f748 --- /dev/null +++ b/test cases/unit/13 testsetup selection/subprojects/foo/foo.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/test cases/unit/13 testsetup selection/subprojects/foo/meson.build b/test cases/unit/13 testsetup selection/subprojects/foo/meson.build new file mode 100644 index 0000000..2eef840 --- /dev/null +++ b/test cases/unit/13 testsetup selection/subprojects/foo/meson.build @@ -0,0 +1,4 @@ +project('foo', 'c') +foo = executable('foo', 'foo.c') +test('Test foo', foo) +add_test_setup('worksforall') diff --git a/test cases/unit/23 compiler run_command/meson.build b/test cases/unit/23 compiler run_command/meson.build new file mode 100644 index 0000000..6d9e0b9 --- /dev/null +++ b/test cases/unit/23 compiler run_command/meson.build @@ -0,0 +1,10 @@ +project('compiler_object_in_run_command', 'c') +cc = meson.get_compiler('c') + +# This test only checks that the compiler object can be passed to +# run_command(). If the compiler has been launched, it is expected +# to output something either to stdout or to stderr. +result = run_command(cc, '--version') +if result.stdout() == '' and result.stderr() == '' + error('No output in stdout and stderr. Did the compiler run at all?') +endif |