diff options
86 files changed, 1045 insertions, 569 deletions
diff --git a/.appveyor.yml b/.appveyor.yml index d8a4b12..60a6fd3 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -49,17 +49,23 @@ branches: only: - master -skip_commits: - files: - - docs/**/* +init: + - ps: | + If($Env:compiler -like 'msvc2010') { + Set-WinSystemLocale de-DE + Start-Sleep -s 5 + Restart-Computer + } install: - cmd: set "ORIG_PATH=%PATH%" # Boost 1.56.0: https://www.appveyor.com/docs/build-environment/#boost #- cmd: set "BOOST_ROOT=C:\Libraries\boost" + # Use a Ninja with QuLogic's patch: https://github.com/ninja-build/ninja/issues/1219 + - cmd: set "MESON_FIXED_NINJA=1" + - ps: (new-object net.webclient).DownloadFile('http://nirbheek.in/files/binaries/ninja/win32/ninja.exe', 'C:\projects\meson\ninja.exe') # Use the x86 python only when building for x86 for the cpython tests. # For all other archs (including, say, arm), use the x64 python. - - ps: (new-object net.webclient).DownloadFile('https://www.dropbox.com/s/bbzvepq85hv47x1/ninja.exe?dl=1', 'C:\projects\meson\ninja.exe') - cmd: if %arch%==x86 (set MESON_PYTHON_PATH=C:\python34) else (set MESON_PYTHON_PATH=C:\python34-x64) # Set paths and config for each build type. diff --git a/.travis.yml b/.travis.yml index 5b6e022..559b39c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,7 +30,9 @@ matrix: before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install ninja python3; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install python3; 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:zesty; fi # We need to copy the current checkout inside the Docker container, @@ -45,6 +47,6 @@ script: ci_env=`bash <(curl -s https://codecov.io/env)` docker run $ci_env -v ${PWD}/.coverage:/root/.coverage \ withgit \ - /bin/sh -c "cd /root && CC=$CC CXX=$CXX OBJC=$CC OBJCXX=$CXX ./run_tests.py -- $MESON_ARGS && chmod -R a+rwX .coverage" + /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) OBJC=$CC OBJCXX=$CXX ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then SDKROOT=$(xcodebuild -version -sdk macosx Path) OBJC=$CC OBJCXX=$CXX PATH=$HOME/tools:$PATH MESON_FIXED_NINJA=1 ./run_tests.py --backend=ninja -- $MESON_ARGS ; fi diff --git a/ci/appveyor-install.bat b/ci/appveyor-install.bat index becc80a..9eddeac 100644 --- a/ci/appveyor-install.bat +++ b/ci/appveyor-install.bat @@ -1,5 +1,7 @@ set CACHE=C:\cache set CYGWIN_MIRROR="http://cygwin.mirror.constant.com" +set CYGWIN_ADDITIONAL_REPO="http://www.dronecode.org.uk/cygwin/" +set CYGWIN_ADDITIONAL_REPO_KEY="http://www.dronecode.org.uk/cygwin/dronecode.gpg" if _%arch%_ == _x64_ set SETUP=setup-x86_64.exe && set CYGWIN_ROOT=C:\cygwin64 if _%arch%_ == _x86_ set SETUP=setup-x86.exe && set CYGWIN_ROOT=C:\cygwin @@ -7,5 +9,5 @@ if _%arch%_ == _x86_ set SETUP=setup-x86.exe && set CYGWIN_ROOT=C:\cygwin if not exist %CACHE% mkdir %CACHE% echo Updating Cygwin and installing ninja and test prerequisites -%CYGWIN_ROOT%\%SETUP% -qnNdO -R "%CYGWIN_ROOT%" -s "%CYGWIN_MIRROR%" -l "%CACHE%" -g -P "ninja,gcc-objc,gcc-objc++,libglib2.0-devel,zlib-devel,python3-pip" +%CYGWIN_ROOT%\%SETUP% -qnNdO -R "%CYGWIN_ROOT%" -s "%CYGWIN_MIRROR%" -s "%CYGWIN_ADDITIONAL_REPO%" -K "%CYGWIN_ADDITIONAL_REPO_KEY%" -l "%CACHE%" -g -P "ninja,gcc-objc,gcc-objc++,libglib2.0-devel,zlib-devel,python3-pip" echo Install done diff --git a/contributing.txt b/contributing.txt index c4809ec..094c5e6 100644 --- a/contributing.txt +++ b/contributing.txt @@ -58,7 +58,7 @@ These are bad because they would make Meson's DSL Turing complete. The second feature is a Make backend. The FAQ has specific information why these two features will not be -added to Meson: https://github.com/mesonbuild/meson/wiki/FAQ +added to Meson: http://mesonbuild.com/FAQ.html Merge requests adding either of these two features will be automatically rejected. Please save everyone's time (especially your own) and don't start diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md index 54905d5..cb7b19e 100644 --- a/docs/markdown/Build-options.md +++ b/docs/markdown/Build-options.md @@ -42,11 +42,16 @@ prefix = get_option('prefix') ``` It should be noted that you can not set option values in your Meson -scripts. They have to be set externally with the `mesonconf` command -line tool. Running `mesonconf` without arguments in a build dir shows -you all options you can set. To change their values use the `-D` +scripts. They have to be set externally with the `meson configure` command +line tool. Running `meson configure` without arguments in a build dir shows +you all options you can set. + +To change their values use the `-D` option: ```console -$ mesonconf -Doption=newvalue +$ meson configure -Doption=newvalue ``` + + +**NOTE:** If you cannot call `meson configure` you likely have a old version of Meson. In that case you can call `mesonconf` instead, but that is deprecated in newer versions diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md index 5978b5d..4def628 100644 --- a/docs/markdown/Compiler-properties.md +++ b/docs/markdown/Compiler-properties.md @@ -29,26 +29,9 @@ Compiler id == The compiler object has a method called `get_id`, which returns a -lower case string describing the "family" of the compiler. It has one -of the following values. - -| Value | Compiler family | -| ----- | ---------------- | -| gcc | The GNU Compiler Collection | -| clang | The Clang compiler | -| msvc | Microsoft Visual Studio | -| intel | Intel compiler | -| llvm | LLVM-based compiler (Swift, D) | -| mono | Xamarin C# compiler | -| dmd | D lang reference compiler | -| rustc | Rust compiler | -| valac | Vala compiler | -| pathscale | The Pathscale Fortran compiler | -| pgi | The Portland Fortran compiler | -| sun | Sun Fortran compiler | -| g95 | The G95 Fortran compiler | -| open64 | The Open64 Fortran Compiler | -| nagfor | The NAG Fortran compiler | +lower case string describing the "family" of the compiler. See +[reference tables](Reference-tables.md) for a list of supported +compiler ids. Does code compile? == @@ -120,12 +103,12 @@ The `result` variable encapsulates the state of the test, which can be extracted with the following methods. The `name` keyword argument works the same as with `compiles`. -| Method | Return value -| ------ | ------------ -| compiled | `True` if compilation succeeded. If `false` then all other methods return undefined values. -| returncode | The return code of the application as an integer -| stdout | Program's standard out as text. -| stderr | Program's standard error as text. +| Method | Return value | +| ------ | ------------ | +| compiled | `True` if compilation succeeded. If `false` then all other methods return undefined values. | +| returncode | The return code of the application as an integer | +| stdout | Program's standard out as text. | +| stderr | Program's standard error as text. | Here is an example usage: diff --git a/docs/markdown/D.md b/docs/markdown/D.md new file mode 100644 index 0000000..7b0d485 --- /dev/null +++ b/docs/markdown/D.md @@ -0,0 +1,89 @@ +--- +title: D +short-description: Compiling D sources +... + +# Compiling D applications + +Meson has support for compiling D programs. A minimal `meson.build` file for D looks like this: + +```meson +project('myapp', 'd') + +executable('myapp', 'app.d') +``` + +## Compiling different versions + +If you are using the [version()](https://dlang.org/spec/version.html) feature for conditional compilation, you can use it using the `d_module_versions` +target property: +```meson +project('myapp', 'd') +executable('myapp', 'app.d', d_module_versions: ['Demo', 'FeatureA']) +``` + +## Using embedded unittests + +If you are using embedded [unittest functions](https://dlang.org/spec/unittest.html), your source code needs to be compiled twice, once in regular +mode, and once with unittests active. This is done by setting the `d_unittest` target property to `true`. +Meson will only ever pass the respective compiler's `-unittest` flag, and never have the compiler generate an empty main function. +If you need that feature in a portable way, create an empty `main()` function for unittests yourself, since the GNU D compiler +does not have this feature. + +This is an example for using D unittests with Meson: +```meson +project('myapp_tested', 'd') + +myapp_src = ['app.d', 'alpha.d', 'beta.d'] +executable('myapp', myapp_src) + +test_exe = executable('myapp_test', myapp_src, d_unittest: true) +test('myapptest', test_exe) +``` + +# Compiling D libraries and installing them + +Building D libraries is a straightforward process, not different from how C libraries are built in Meson. You should generate a pkg-config file +and install it, in order to make other software on the system find the dependency once it is installed. + +This is an example on how to build a D shared library: +```meson +project('mylib', 'd', version: '1.2.0') + +project_soversion = 0 +glib_dep = dependency('glib-2.0') + +my_lib = library('mylib', + ['src/mylib/libfunctions.d'], + dependencies: [glib_dep], + install: true, + version: meson.project_version(), + soversion: project_soversion, + d_module_versions: ['FeatureA', 'featureB'] +) +pkgc.generate(name: 'mylib', + libraries: my_lib, + subdirs: 'd/mylib', + version: meson.project_version(), + description: 'A simple example D library.', + d_module_versions: ['FeatureA'] +) +install_subdir('src/mylib/', install_dir: 'include/d/mylib/') +``` + +It is important to make the D sources install in a subdirectory in the include path, in this case `/usr/include/d/mylib/mylib`. +All D compilers include the `/usr/include/d` directory by default, and if your library would be installed into `/usr/include/d/mylib`, there +is a high chance that, when you compile your project again on a machine where you installed it, the compiler will prefer the old installed include over +the new version in the source tree, leading to very confusing errors. + +This is an example of how to use the D library we just built and installed in an application: +```meson +project('myapp', 'd') + +mylib_dep = dependency('mylib', version: '>= 1.2.0') +myapp_src = ['app.d', 'alpha.d', 'beta.d'] +executable('myapp', myapp_src, dependencies: [mylib_dep]) +``` + +Please keep in mind that the library and executable would both need to be built with the exact same D compiler and D compiler version. The D ABI is not +stable across compilers and their versions, and mixing compilers will lead to problems. diff --git a/docs/markdown/Getting-meson.md b/docs/markdown/Getting-meson.md index d654ff3..8664d61 100644 --- a/docs/markdown/Getting-meson.md +++ b/docs/markdown/Getting-meson.md @@ -5,7 +5,7 @@ Meson releases can be downloaded from the [GitHub release page]. Meson is also available in the [Python Package Index] and can be -installed with <tt>pip3 install meson</tt>. +installed with `pip3 install meson`. The newest development code can be obtained directly from [Git] diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index 3672761..99a9c8e 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -226,6 +226,7 @@ files and the second specifies the XML file name. * `namespace`: namespace of the interface * `object_manager`: *(Added 0.40.0)* if true generates object manager code * `annotations`: *(Added 0.43.0)* list of lists of 3 strings for the annotation for `'ELEMENT', 'KEY', 'VALUE'` +* `docbook`: *(Added 0.43.0)* prefix to generate `'PREFIX'-NAME.xml` docbooks Returns an opaque object containing the source files. Add it to a top level target's source list. @@ -241,7 +242,8 @@ gdbus_src = gnome.gdbus_codegen('example-interface', 'com.example.Sample.xml', namespace : 'Sample', annotations : [ ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'] - ] + ], + docbook : 'example-interface-doc' ) ``` diff --git a/docs/markdown/Pkgconfig-module.md b/docs/markdown/Pkgconfig-module.md index 5a660fd..7f767f1 100644 --- a/docs/markdown/Pkgconfig-module.md +++ b/docs/markdown/Pkgconfig-module.md @@ -41,3 +41,5 @@ keyword arguments. e.g. `datadir=${prefix}/share`. The names `prefix`, `libdir` and `installdir` are reserved and may not be used. - `version` a string describing the version of this library +- `d_module_versions` a list of module version flags used when compiling + D sources referred to by this pkg-config file diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 301c693..f3640f0 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -201,10 +201,15 @@ following. to the target file. Note that your command argument list may not contain `@OUTPUT@` when capture mode is active. - `command` command to run to create outputs from inputs. The command - may be strings or the return of `find_program()` or `executable()` - (note: always specify commands in array form `['commandname', + may be strings or the return value of functions that return file-like + objects such as [`find_program()`](#find_program), + [`executable()`](#executable), [`configure_file()`](#configure_file), + [`files()`](#files), [`custom_target()`](#custom_target), etc. + Meson will automatically insert the appropriate dependencies on + targets and files listed in this keyword argument. + Note: always specify commands in array form `['commandname', '-arg1', '-arg2']` rather than as a string `'commandname -arg1 - -arg2'` as the latter will *not* work) + -arg2'` as the latter will *not* work. - `depend_files` files ([`string`](#string-object), [`files()`](#files), or [`configure_file()`](#configure_file)) that this target depends on but are not listed in the `command` keyword @@ -424,6 +429,10 @@ be passed to [shared and static libraries](#library). - `override_options` takes an array of strings in the same format as `project`'s `default_options` overriding the values of these options for this target only, since 0.40.0 +- `d_import_dirs` list of directories to look in for string imports used + in the D programmling language +- `d_unittest`, when set to true, the D modules are compiled in debug mode +- `d_module_versions` list of module versions set when compiling D sources The list of `sources`, `objects`, and `dependencies` is always flattened, which means you can freely nest and add lists while @@ -835,7 +844,7 @@ This function prints its argument to stdout. The first argument to this function must be a string defining the name of this project. It is followed by programming languages that the project uses. Supported values for languages are `c`, `cpp` (for -`C++`), `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and +`C++`), `d`, `objc`, `objcpp`, `fortran`, `java`, `cs` (for `C#`) and `vala`. In versions before `0.40.0` you must have at least one language listed. @@ -1176,11 +1185,13 @@ the following methods. Python](https://docs.python.org/3/library/os.path.html#os.path.join) special-case absolute paths. - `MESONINTROSPECT` contains the path to the `mesonintrospect` - executable that corresponds to the `meson` executable that was used - to configure the build. (This might be a different path then the - first `mesonintrospect` executable found in `PATH`.) It can be used - to query build configuration. + `MESONINTROSPECT` contains the path to the introspect command that + corresponds to the `meson` executable that was used to configure the + build. (This might be a different path then the first executable + found in `PATH`.) It can be used to query build configuration. Note + that the value may contain many parts, i.e. it may be `python3 + /path/to/meson.py introspect`. The user is responsible for splitting + the string to an array if needed. - `source_root()` returns a string with the absolute path to the source root directory. Note: you should use the `files()` function @@ -1502,7 +1513,10 @@ A build target is either an [executable](#executable), files with custom flags. To use the object file(s) in another build target, use the `objects:` keyword argument. -- `full_path()` returns a full path pointing to the result target file +- `full_path()` returns a full path pointing to the result target file. + NOTE: In most cases using the object itself will do the same job as + this and will also allow Meson to setup inter-target dependencies + correctly. Please file a bug if that doesn't work for you. - `private_dir_include()` returns a opaque value that works like `include_directories` but points to the private directory of this @@ -1548,7 +1562,10 @@ cause a syntax error. This object is returned by [`custom_target`](#custom_target) and contains a target with the following methods: -- `full_path()` returns a full path pointing to the result target file +- `full_path()` returns a full path pointing to the result target file + NOTE: In most cases using the object itself will do the same job as + this and will also allow Meson to setup inter-target dependencies + correctly. Please file a bug if that doesn't work for you. ### `dependency` object diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md new file mode 100644 index 0000000..b604fb6 --- /dev/null +++ b/docs/markdown/Reference-tables.md @@ -0,0 +1,60 @@ +# Reference tables + +## Compiler ids + +These are return values of the `get_id` method in a compiler object. + +| Value | Compiler family | +| ----- | ---------------- | +| gcc | The GNU Compiler Collection | +| clang | The Clang compiler | +| msvc | Microsoft Visual Studio | +| intel | Intel compiler | +| llvm | LLVM-based compiler (Swift, D) | +| mono | Xamarin C# compiler | +| dmd | D lang reference compiler | +| rustc | Rust compiler | +| valac | Vala compiler | +| pathscale | The Pathscale Fortran compiler | +| pgi | The Portland Fortran compiler | +| sun | Sun Fortran compiler | +| g95 | The G95 Fortran compiler | +| open64 | The Open64 Fortran Compiler | +| nagfor | The NAG Fortran compiler | + +## Script environment variables + +| Value | Comment | +| ----- | ------- | +| MESON_SOURCE_ROOT | Absolute path to the source dir | +| MESON_BUILD_ROOT | Absolute path to the build dir | +| MESONINTROSPECT | Command to run to run the introspection command, may be of the form `python /path/to/meson introspect`, user is responsible for splitting the path if necessary. | +| MESON_SUBDIR | Current subdirectory, only set for `run_command` | + +## CPU families + +These are returned by the `cpu_family` method of `build_machine`, +`host_machine` and `target_machine`. For cross compilation they are +set in the cross file. + +| Value | Comment | +| ----- | ------- | +| x86 | 32 bit x86 processor | +| x86_64 | 64 bit x86 processor | +| arm | 32 bit ARM processor | + +Any cpu family not listed in the above list is not guaranteed to +remain stable in future releases. + +## Operating system names + +These are provided by the `.system()` method call. + +| Value | Comment | +| ----- | ------- | +| linux | | +| darwin | Either OSX or iOS | +| windows | Any version of Windows | + +Any string not listed above is not guaranteed to remain stable in +future releases.
\ No newline at end of file diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 7949522..6ded714 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -57,42 +57,42 @@ Sometimes a test can only determine at runtime that it can not be run. The GNU s ## Testing tool -In version 0.37.0 a new tool called `mesontest` was added. The goal of this tool is to provide a simple way to run tests in a variety of different ways. The tool is designed to be run in the build directory. +The goal of the meson test tool is to provide a simple way to run tests in a variety of different ways. The tool is designed to be run in the build directory. The simplest thing to do is just to run all tests, which is equivalent to running `ninja test`. ```console -$ mesontest +$ meson test ``` You can also run only a single test by giving its name: ```console -$ mesontest testname +$ meson test testname ``` Sometimes you need to run the tests multiple times, which is done like this: ```console -$ mesontest --repeat=10 +$ meson test --repeat=10 ``` Invoking tests via a helper executable such as Valgrind can be done with the `--wrap` argument ```console -$ mesontest --wrap=valgrind testname +$ meson test --wrap=valgrind testname ``` Arguments to the wrapper binary can be given like this: ```console -$ mesontest --wrap='valgrind --tool=helgrind' testname +$ meson test --wrap='valgrind --tool=helgrind' testname ``` Meson also supports running the tests under GDB. Just doing this: ```console -$ mesontest --gdb testname +$ meson test --gdb testname ``` Mesontest will launch `gdb` all set up to run the test. Just type `run` in the GDB command prompt to start the program. @@ -100,9 +100,11 @@ Mesontest will launch `gdb` all set up to run the test. Just type `run` in the G The second use case is a test that segfaults only rarely. In this case you can invoke the following command: ```console -$ mesontest --gdb --repeat=10000 testname +$ meson test --gdb --repeat=10000 testname ``` This runs the test up to 10 000 times under GDB automatically. If the program crashes, GDB will halt and the user can debug the application. Note that testing timeouts are disabled in this case so mesontest will not kill `gdb` while the developer is still debugging it. The downside is that if the test binary freezes, the test runner will wait forever. For further information see the command line help of Mesontest by running `mesontest -h`. + +**NOTE:** If `meson test` does not work for you, you likely have a old version of Meson. In that case you should call `mesontest` instead. If `mesontest` doesn't work either you have a very old version prior to 0.37.0 and should upgrade. diff --git a/docs/markdown/Videos.md b/docs/markdown/Videos.md index ab128c6..5abfbe4 100644 --- a/docs/markdown/Videos.md +++ b/docs/markdown/Videos.md @@ -9,7 +9,7 @@ short-description: Videos about Meson (from Linux.conf.au 2015 -- Auckland, New Zealand) <div class="video-container"> -<iframe width="854" height="480" src="https://www.youtube.com/embed/KPi0AuVpxLI" frameborder="0" allowfullscreen></iframe> +<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/KPi0AuVpxLI" allowfullscreen></iframe> </div> ## Talks about design, goals behind Meson's multiplatform dependency system was held @@ -17,7 +17,7 @@ short-description: Videos about Meson (From Linux.conf.au 2016 -- Geelong, Australia) <div class="video-container"> -<iframe width="854" height="480" src="https://www.youtube.com/embed/CTJtKtQ8R5k" frameborder="0" allowfullscreen></iframe> +<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/CTJtKtQ8R5k" allowfullscreen></iframe> </div> ## Features and benefits of Meson's multiplatform support for building and dependencies] @@ -25,7 +25,7 @@ short-description: Videos about Meson (Libre Application Summit 2016 talk _New world, new tools_ explored further) <div class="video-container"> -<iframe width="854" height="480" src="https://www.youtube.com/embed/0-gx1qU2pPo" frameborder="0" allowfullscreen></iframe> +<iframe width="854" height="480" style="border:0;" src="https://www.youtube.com/embed/0-gx1qU2pPo" allowfullscreen></iframe> </div> ## The first ever public presentation on Meson @@ -33,5 +33,5 @@ short-description: Videos about Meson (lightning talk at FOSDEM 2014) <video width="854" height="480" controls> - < <source src=http://mirror.onet.pl/pub/mirrors/video.fosdem.org/2014/H2215_Ferrer/Sunday/Introducing_the_Meson_build_system.webm> + <source src=http://mirror.onet.pl/pub/mirrors/video.fosdem.org/2014/H2215_Ferrer/Sunday/Introducing_the_Meson_build_system.webm> </video> diff --git a/docs/markdown/i18n-module.md b/docs/markdown/i18n-module.md index 5e6004a..1144e29 100644 --- a/docs/markdown/i18n-module.md +++ b/docs/markdown/i18n-module.md @@ -32,6 +32,8 @@ argument which is the name of the gettext module. [source](https://github.com/mesonbuild/meson/blob/master/mesonbuild/modules/i18n.py) for for their value +* `install`: (*Added 0.43.0*) if false, do not install the built translations. + This function also defines targets for maintainers to use: **Note**: These output to the source directory diff --git a/docs/markdown/index.md b/docs/markdown/index.md index 8e35a4d..81c17ff 100644 --- a/docs/markdown/index.md +++ b/docs/markdown/index.md @@ -13,7 +13,7 @@ The main design point of Meson is that every moment a developer spends writing o ## Features * multiplatform support for Linux, OSX, Windows, GCC, Clang, Visual Studio and others -* supported languages include C, C++, Fortran, Java, Rust +* supported languages include C, C++, D, Fortran, Java, Rust * build definitions in a very readable and user friendly non-Turing complete DSL * cross compilation for many operating systems as well as bare metal * optimized for extremely fast full and incremental builds without sacrificing correctness @@ -24,10 +24,10 @@ The main design point of Meson is that every moment a developer spends writing o There are two main methods of connecting with other Meson developers. The first one is the mailing list, which is hosted at [Google Groups](https://groups.google.com/forum/#!forum/mesonbuild). -The second way is via IRC. The channel to use is <tt>#mesonbuild</tt> at [Freenode](https://freenode.net/). +The second way is via IRC. The channel to use is `#mesonbuild` at [Freenode](https://freenode.net/). ## Development -All development on Meson is done on [GitHub project](https://github.com/mesonbuild/meson). For further info look into the <tt>contributing.txt</tt> file that comes with Meson's source checkout. +All development on Meson is done on [GitHub project](https://github.com/mesonbuild/meson). For further info look into the `contributing.txt` file that comes with Meson's source checkout. You do not need to sign a CLA to contribute to Meson. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index af8aede..89dfdbd 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -38,6 +38,7 @@ index.md Windows-module.md Java.md Vala.md + D.md IDE-integration.md Custom-build-targets.md Build-system-converters.md @@ -47,6 +48,7 @@ index.md Creating-OSX-packages.md Creating-Linux-binaries.md Reference-manual.md + Reference-tables.md FAQ.md Reproducible-builds.md howtox.md @@ -14,6 +14,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +# ghwt - GitHub WrapTool +# +# An emergency wraptool(1) replacement downloader that downloads +# directly from GitHub in case wrapdb.mesonbuild.com is down. + import urllib.request, json, sys, os, shutil, subprocess import configparser, hashlib @@ -24,7 +24,7 @@ def main(): # encoding, so we can just warn about it. e = locale.getpreferredencoding() if e.upper() != 'UTF-8' and not mesonlib.is_windows(): - print('Warning: You are using {!r} which is not a a Unicode-compatible ' + print('Warning: You are using {!r} which is not a Unicode-compatible ' 'locale.'.format(e), file=sys.stderr) print('You might see errors if you use UTF-8 strings as ' 'filenames, as strings, or as file contents.', file=sys.stderr) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 4dbf41c..97959b6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -65,12 +65,12 @@ class ExecutableSerialisation: self.capture = capture class TestSerialisation: - def __init__(self, name, suite, fname, is_cross, exe_wrapper, is_parallel, cmd_args, env, + def __init__(self, name, suite, fname, is_cross_built, exe_wrapper, is_parallel, cmd_args, env, should_fail, timeout, workdir, extra_paths): self.name = name self.suite = suite self.fname = fname - self.is_cross = is_cross + self.is_cross_built = is_cross_built self.exe_runner = exe_wrapper self.is_parallel = is_parallel self.cmd_args = cmd_args @@ -180,8 +180,9 @@ class Backend: # target that the GeneratedList is used in return os.path.join(self.get_target_private_dir(target), src) - def get_unity_source_filename(self, target, suffix): - return target.name + '-unity.' + suffix + def get_unity_source_file(self, target, suffix): + osrc = target.name + '-unity.' + suffix + return mesonlib.File.from_built_file(self.get_target_private_dir(target), osrc) def generate_unity_files(self, target, unity_src): abs_files = [] @@ -189,18 +190,15 @@ class Backend: compsrcs = classify_unity_sources(target.compilers.values(), unity_src) def init_language_file(suffix): - unity_src_name = self.get_unity_source_filename(target, suffix) - unity_src_subdir = self.get_target_private_dir_abs(target) - outfilename = os.path.join(unity_src_subdir, - unity_src_name) - outfileabs = os.path.join(self.environment.get_build_dir(), - outfilename) + unity_src = self.get_unity_source_file(target, suffix) + outfileabs = unity_src.absolute_path(self.environment.get_source_dir(), + self.environment.get_build_dir()) outfileabs_tmp = outfileabs + '.tmp' abs_files.append(outfileabs) outfileabs_tmp_dir = os.path.dirname(outfileabs_tmp) if not os.path.exists(outfileabs_tmp_dir): os.makedirs(outfileabs_tmp_dir) - result.append(mesonlib.File(True, unity_src_subdir, unity_src_name)) + result.append(unity_src) return open(outfileabs_tmp, 'w') # For each language, generate a unity source file and return the list @@ -257,11 +255,11 @@ class Backend: else: exe_cmd = [exe] exe_needs_wrapper = False - is_cross = exe_needs_wrapper and \ + is_cross_built = exe_needs_wrapper and \ self.environment.is_cross_build() and \ self.environment.cross_info.need_cross_compiler() and \ self.environment.cross_info.need_exe_wrapper() - if is_cross: + if is_cross_built: exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) else: exe_wrapper = None @@ -270,7 +268,7 @@ class Backend: else: extra_paths = [] es = ExecutableSerialisation(basename, exe_cmd, cmd_args, env, - is_cross, exe_wrapper, workdir, + is_cross_built, exe_wrapper, workdir, extra_paths, capture) pickle.dump(es, f) return exe_data @@ -310,13 +308,35 @@ class Backend: return result def object_filename_from_source(self, target, source, is_unity): - if isinstance(source, mesonlib.File): - source = source.fname + assert isinstance(source, mesonlib.File) + build_dir = self.environment.get_build_dir() + rel_src = source.rel_to_builddir(self.build_to_src) # foo.vala files compile down to foo.c and then foo.c.o, not foo.vala.o - if source.endswith(('.vala', '.gs')): + if rel_src.endswith(('.vala', '.gs')): + # See description in generate_vala_compile for this logic. + if source.is_built: + if os.path.isabs(rel_src): + rel_src = rel_src[len(build_dir) + 1:] + rel_src = os.path.relpath(rel_src, self.get_target_private_dir(target)) + else: + rel_src = os.path.basename(rel_src) if is_unity: - return source[:-5] + '.c.' + self.environment.get_object_suffix() - source = os.path.join(self.get_target_private_dir(target), source[:-5] + '.c') + return 'meson-generated_' + rel_src[:-5] + '.c.' + self.environment.get_object_suffix() + # A meson- prefixed directory is reserved; hopefully no-one creates a file name with such a weird prefix. + source = 'meson-generated_' + rel_src[:-5] + '.c' + elif source.is_built: + if os.path.isabs(rel_src): + rel_src = rel_src[len(build_dir) + 1:] + targetdir = self.get_target_private_dir(target) + # A meson- prefixed directory is reserved; hopefully no-one creates a file name with such a weird prefix. + source = 'meson-generated_' + os.path.relpath(rel_src, targetdir) + else: + if os.path.isabs(rel_src): + # Not from the source directory; hopefully this doesn't conflict with user's source files. + source = os.path.basename(rel_src) + else: + source = os.path.relpath(os.path.join(build_dir, rel_src), + os.path.join(self.environment.get_source_dir(), target.get_subdir())) return source.replace('/', '_').replace('\\', '_') + '.' + self.environment.get_object_suffix() def determine_ext_objs(self, target, extobj, proj_dir_to_build_root): @@ -330,9 +350,8 @@ class Backend: extobj.srclist[0]) # There is a potential conflict here, but it is unlikely that # anyone both enables unity builds and has a file called foo-unity.cpp. - osrc = self.get_unity_source_filename(extobj.target, - comp.get_default_suffix()) - osrc = os.path.join(self.get_target_private_dir(extobj.target), osrc) + osrc = self.get_unity_source_file(extobj.target, + comp.get_default_suffix()) objname = self.object_filename_from_source(extobj.target, osrc, True) objname = objname.replace('/', '_').replace('\\', '_') objpath = os.path.join(proj_dir_to_build_root, targetdir, objname) @@ -515,6 +534,10 @@ class Backend: self.environment.cross_info.need_exe_wrapper() if isinstance(exe, build.BuildTarget): is_cross = is_cross and exe.is_cross + if isinstance(exe, dependencies.ExternalProgram): + # E.g. an external verificator or simulator program run on a generated executable. + # Can always be run. + is_cross = False if is_cross: exe_wrapper = self.environment.cross_info.config['binaries'].get('exe_wrapper', None) else: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index bff173a..41b93cb 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, pickle, re, shlex, shutil, subprocess, sys +import os, pickle, re, shlex, subprocess, sys from collections import OrderedDict from . import backends @@ -83,10 +83,9 @@ class NinjaBuildElement: def write(self, outfile): self.check_outputs() - line = 'build %s: %s %s' % ( - ' '.join([ninja_quote(i) for i in self.outfilenames]), - self.rule, - ' '.join([ninja_quote(i) for i in self.infilenames])) + line = 'build %s: %s %s' % (' '.join([ninja_quote(i) for i in self.outfilenames]), + self.rule, + ' '.join([ninja_quote(i) for i in self.infilenames])) if len(self.deps) > 0: line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps]) if len(self.orderdeps) > 0: @@ -192,6 +191,9 @@ int dummy; def generate(self, interp): self.interpreter = interp + self.ninja_command = environment.detect_ninja(log=True) + if self.ninja_command is None: + raise MesonException('Could not detect Ninja v1.5 or newer') outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename) tempfilename = outfilename + '~' with open(tempfilename, 'w') as outfile: @@ -225,11 +227,10 @@ int dummy; # http://clang.llvm.org/docs/JSONCompilationDatabase.html def generate_compdb(self): - ninja_exe = environment.detect_ninja() pch_compilers = ['%s_PCH' % i for i in self.build.compilers] native_compilers = ['%s_COMPILER' % i for i in self.build.compilers] cross_compilers = ['%s_CROSS_COMPILER' % i for i in self.build.cross_compilers] - ninja_compdb = [ninja_exe, '-t', 'compdb'] + pch_compilers + native_compilers + cross_compilers + ninja_compdb = [self.ninja_command, '-t', 'compdb'] + pch_compilers + native_compilers + cross_compilers builddir = self.environment.get_build_dir() try: jsondb = subprocess.check_output(ninja_compdb, cwd=builddir) @@ -719,8 +720,7 @@ int dummy; # On toolchains/platforms that use an import library for # linking (separate from the shared library with all the # code), we need to install that too (dll.a/.lib). - if (isinstance(t, build.SharedLibrary) or - isinstance(t, build.Executable)) and t.get_import_filename(): + if (isinstance(t, build.SharedLibrary) or isinstance(t, build.Executable)) and t.get_import_filename(): if custom_install_dir: # If the DLL is installed into a custom directory, # install the import library into the same place so @@ -854,8 +854,9 @@ int dummy; self.create_target_alias('meson-test', outfile) # And then benchmarks. - cmd = self.environment.get_build_command(True) + ['test', '--benchmark', '--logbase', - 'benchmarklog', '--num-processes=1', '--no-rebuild'] + cmd = self.environment.get_build_command(True) + [ + 'test', '--benchmark', '--logbase', + 'benchmarklog', '--num-processes=1', '--no-rebuild'] elem = NinjaBuildElement(self.all_outputs, 'meson-benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY']) elem.add_item('COMMAND', cmd) elem.add_item('DESC', 'Running benchmark suite.') @@ -1046,7 +1047,7 @@ int dummy; if hasattr(i, 'fname'): i = i.fname if i.endswith('vala'): - vapiname = dep.name + '.vapi' + vapiname = dep.vala_vapi fullname = os.path.join(self.get_target_dir(dep), vapiname) result.add(fullname) break @@ -1574,9 +1575,10 @@ int dummy; def generate_swift_compile_rules(self, compiler, outfile): rule = 'rule %s_COMPILER\n' % compiler.get_language() full_exe = [ninja_quote(x) for x in self.environment.get_build_command()] + [ - '--internal', - 'dirchanger', - '$RUNDIR'] + '--internal', + 'dirchanger', + '$RUNDIR', + ] invoc = (' '.join(full_exe) + ' ' + ' '.join(ninja_quote(i) for i in compiler.get_exelist())) command = ' command = %s $ARGS $in\n' % invoc @@ -1782,7 +1784,6 @@ rule FORTRAN_DEP_HACK exe_arr = self.exe_object_to_cmd_array(exe) infilelist = genlist.get_inputs() outfilelist = genlist.get_outputs() - base_args = generator.get_arglist() 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)): @@ -1792,6 +1793,7 @@ rule FORTRAN_DEP_HACK sole_output = '' curfile = infilelist[i] infilename = curfile.rel_to_builddir(self.build_to_src) + base_args = generator.get_arglist(infilename) outfiles = genlist.get_outputs_for(curfile) outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles] if generator.depfile is None: @@ -1816,7 +1818,7 @@ rule FORTRAN_DEP_HACK elem.add_item('DEPFILE', depfile) if len(extra_dependencies) > 0: elem.add_dep(extra_dependencies) - elem.add_item('DESC', 'Generating $out') + elem.add_item('DESC', 'Generating {!r}.'.format(sole_output)) if isinstance(exe, build.BuildTarget): elem.add_dep(self.get_target_filename(exe)) elem.add_item('COMMAND', cmdlist) @@ -2116,39 +2118,21 @@ rule FORTRAN_DEP_HACK self.target_arg_cache[key] = commands commands = CompilerArgs(commands.compiler, commands) - if isinstance(src, mesonlib.File) and src.is_built: - rel_src = os.path.join(src.subdir, src.fname) - if os.path.isabs(rel_src): - assert(rel_src.startswith(self.environment.get_build_dir())) - rel_src = rel_src[len(self.environment.get_build_dir()) + 1:] - abs_src = os.path.join(self.environment.get_build_dir(), rel_src) - elif isinstance(src, mesonlib.File): + build_dir = self.environment.get_build_dir() + if isinstance(src, File): rel_src = src.rel_to_builddir(self.build_to_src) - abs_src = src.absolute_path(self.environment.get_source_dir(), - self.environment.get_build_dir()) + if os.path.isabs(rel_src): + # Source files may not be from the source directory if they originate in source-only libraries, + # so we can't assert that the absolute path is anywhere in particular. + if src.is_built: + assert rel_src.startswith(build_dir) + rel_src = rel_src[len(build_dir) + 1:] elif is_generated: raise AssertionError('BUG: broken generated source file handling for {!r}'.format(src)) else: - if isinstance(src, File): - rel_src = src.rel_to_builddir(self.build_to_src) - else: - raise InvalidArguments('Invalid source type: {!r}'.format(src)) - abs_src = os.path.join(self.environment.get_build_dir(), rel_src) - if isinstance(src, File): - if src.is_built: - src_filename = os.path.join(src.subdir, src.fname) - if os.path.isabs(src_filename): - assert(src_filename.startswith(self.environment.get_build_dir())) - src_filename = src_filename[len(self.environment.get_build_dir()) + 1:] - else: - src_filename = src.fname - elif os.path.isabs(src): - src_filename = os.path.basename(src) - else: - src_filename = src - obj_basename = src_filename.replace('/', '_').replace('\\', '_') + raise InvalidArguments('Invalid source type: {!r}'.format(src)) + obj_basename = self.object_filename_from_source(target, src, self.is_unity(target)) rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) - rel_obj += '.' + self.environment.get_object_suffix() dep_file = compiler.depfile_for_object(rel_obj) # Add MSVC debug file generation compile flags: /Fd /FS @@ -2181,6 +2165,7 @@ rule FORTRAN_DEP_HACK # outdir argument instead. # https://github.com/mesonbuild/meson/issues/1348 if not is_generated: + abs_src = os.path.join(build_dir, rel_src) extra_deps += self.get_fortran_deps(compiler, abs_src, target) # Dependency hack. Remove once multiple outputs in Ninja is fixed: # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8 @@ -2542,10 +2527,11 @@ rule FORTRAN_DEP_HACK def generate_dist(self, outfile): elem = NinjaBuildElement(self.all_outputs, 'meson-dist', 'CUSTOM_COMMAND', 'PHONY') elem.add_item('DESC', 'Creating source packages') - elem.add_item('COMMAND', self.environment.get_build_command() + - ['--internal', 'dist', - self.environment.source_dir, - self.environment.build_dir] + self.environment.get_build_command()) + elem.add_item('COMMAND', self.environment.get_build_command() + [ + '--internal', 'dist', + self.environment.source_dir, + self.environment.build_dir, + ] + self.environment.get_build_command()) elem.add_item('pool', 'console') elem.write(outfile) # Alias that runs the target defined above @@ -2583,11 +2569,8 @@ rule FORTRAN_DEP_HACK default = 'default all\n\n' outfile.write(default) - ninja_command = environment.detect_ninja() - if ninja_command is None: - raise MesonException('Could not detect Ninja v1.6 or newer') elem = NinjaBuildElement(self.all_outputs, 'meson-clean', 'CUSTOM_COMMAND', 'PHONY') - elem.add_item('COMMAND', [ninja_command, '-t', 'clean']) + elem.add_item('COMMAND', [self.ninja_command, '-t', 'clean']) elem.add_item('description', 'Cleaning.') # Alias that runs the above-defined meson-clean target self.create_target_alias('meson-clean', outfile) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index ec5ad7d..22c1779 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -22,7 +22,6 @@ from .. import build from .. import dependencies from .. import mlog from .. import compilers -from ..build import BuildTarget from ..compilers import CompilerArgs from ..mesonlib import MesonException, File from ..environment import Environment @@ -80,39 +79,10 @@ class Vs2010Backend(backends.Backend): super().__init__(build) self.name = 'vs2010' self.project_file_version = '10.0.30319.1' - self.sources_conflicts = {} self.platform_toolset = None self.vs_version = '2010' self.windows_target_platform_version = None - def object_filename_from_source(self, target, source, is_unity=False): - basename = os.path.basename(source.fname) - filename_without_extension = '.'.join(basename.split('.')[:-1]) - if basename in self.sources_conflicts[target.get_id()]: - # If there are multiple source files with the same basename, we must resolve the conflict - # by giving each a unique object output file. - filename_without_extension = '.'.join(source.fname.split('.')[:-1]).replace('/', '_').replace('\\', '_') - return filename_without_extension + '.' + self.environment.get_object_suffix() - - def resolve_source_conflicts(self): - for name, target in self.build.targets.items(): - if not isinstance(target, BuildTarget): - continue - conflicts = {} - for s in target.get_sources(): - if hasattr(s, 'held_object'): - s = s.held_object - if not isinstance(s, File): - continue - basename = os.path.basename(s.fname) - conflicting_sources = conflicts.get(basename, None) - if conflicting_sources is None: - conflicting_sources = [] - conflicts[basename] = conflicting_sources - conflicting_sources.append(s) - self.sources_conflicts[target.get_id()] = {name: src_conflicts for name, src_conflicts in conflicts.items() - if len(src_conflicts) > 1} - def generate_custom_generator_commands(self, target, parent_node): generator_output_files = [] custom_target_include_dirs = [] @@ -135,7 +105,6 @@ class Vs2010Backend(backends.Backend): infilelist = genlist.get_inputs() outfilelist = genlist.get_outputs() exe_arr = self.exe_object_to_cmd_array(exe) - base_args = generator.get_arglist() idgroup = ET.SubElement(parent_node, 'ItemGroup') for i in range(len(infilelist)): if len(infilelist) == len(outfilelist): @@ -144,6 +113,7 @@ class Vs2010Backend(backends.Backend): sole_output = '' curfile = infilelist[i] infilename = os.path.join(down, curfile.rel_to_builddir(self.build_to_src)) + base_args = generator.get_arglist(infilename) outfiles_rel = genlist.get_outputs_for(curfile) outfiles = [os.path.join(target_private_dir, of) for of in outfiles_rel] generator_output_files += outfiles @@ -164,7 +134,6 @@ class Vs2010Backend(backends.Backend): return generator_output_files, custom_target_output_files, custom_target_include_dirs def generate(self, interp): - self.resolve_source_conflicts() self.interpreter = interp target_machine = self.interpreter.builtin['target_machine'].cpu_family_method(None, None) if target_machine.endswith('64'): @@ -413,8 +382,7 @@ class Vs2010Backend(backends.Backend): cmd = [sys.executable, os.path.join(self.environment.get_script_dir(), 'commandrunner.py'), self.environment.get_build_dir(), self.environment.get_source_dir(), - self.get_target_dir(target)] + \ - self.environment.get_build_command() + self.get_target_dir(target)] + self.environment.get_build_command() for i in cmd_raw: if isinstance(i, build.BuildTarget): cmd.append(os.path.join(self.environment.get_build_dir(), self.get_target_filename(i))) @@ -956,8 +924,7 @@ class Vs2010Backend(backends.Backend): ofile.text = '$(OutDir)%s' % target.get_filename() subsys = ET.SubElement(link, 'SubSystem') subsys.text = subsystem - if (isinstance(target, build.SharedLibrary) or - isinstance(target, build.Executable)) and target.get_import_filename(): + if (isinstance(target, build.SharedLibrary) or isinstance(target, build.Executable)) and target.get_import_filename(): # DLLs built with MSVC always have an import library except when # they're data-only DLLs, but we don't support those yet. ET.SubElement(link, 'ImportLibrary').text = target.get_import_filename() @@ -1004,9 +971,7 @@ class Vs2010Backend(backends.Backend): self.add_additional_options(lang, inc_cl, file_args) self.add_preprocessor_defines(lang, inc_cl, file_defines) self.add_include_dirs(lang, inc_cl, file_inc_dirs) - basename = os.path.basename(s.fname) - if basename in self.sources_conflicts[target.get_id()]: - ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s) + ET.SubElement(inc_cl, 'ObjectFileName').text = "$(IntDir)" + self.object_filename_from_source(target, s, False) for s in gen_src: inc_cl = ET.SubElement(inc_src, 'CLCompile', Include=s) lang = Vs2010Backend.lang_from_source_file(s) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 89689d7..c54abbd 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -19,7 +19,7 @@ import itertools from . import environment from . import dependencies from . import mlog -from .mesonlib import File, MesonException +from .mesonlib import File, MesonException, listify, extract_as_list from .mesonlib import flatten, typeslistify, stringlistify, classify_unity_sources from .mesonlib import get_filenames_templates_dict, substitute_values from .environment import for_windows, for_darwin, for_cygwin @@ -36,6 +36,9 @@ known_basic_kwargs = {'install': True, 'vala_args': True, 'fortran_args': True, 'd_args': True, + 'd_import_dirs': True, + 'd_unittest': True, + 'd_module_versions': True, 'java_args': True, 'rust_args': True, 'link_args': True, @@ -351,11 +354,12 @@ 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) if not self.sources and not self.generated and not self.objects: raise InvalidArguments('Build target %s has no sources.' % name) - self.process_compilers() + self.process_compilers_late() self.validate_sources() self.validate_cross_install(environment) @@ -411,8 +415,7 @@ class BuildTarget(Target): raise InvalidArguments(msg) def process_sourcelist(self, sources): - if not isinstance(sources, list): - sources = [sources] + sources = listify(sources) added_sources = {} # If the same source is defined multiple times, use it only once. for s in sources: # Holder unpacking. Ugly. @@ -437,6 +440,39 @@ class BuildTarget(Target): removed = True return removed + def process_compilers_late(self): + """Processes additional compilers after kwargs have been evaluated. + + This can add extra compilers that might be required by keyword + arguments, such as link_with or dependencies. It will also try to guess + which compiler to use if one hasn't been selected already. + """ + # Populate list of compilers + if self.is_cross: + compilers = self.environment.coredata.cross_compilers + else: + compilers = self.environment.coredata.compilers + + # If this library is linked against another library we need to consider + # the languages of those libraries as well. + if self.link_targets or self.link_whole_targets: + extra = set() + for t in itertools.chain(self.link_targets, self.link_whole_targets): + for name, compiler in t.compilers.items(): + if name in clike_langs: + extra.add((name, compiler)) + for name, compiler in sorted(extra, key=lambda p: sort_clike(p[0])): + self.compilers[name] = compiler + + if not self.compilers: + # No source files or parent targets, target consists of only object + # files of unknown origin. Just add the first clike compiler + # that we have and hope that it can link these objects + for lang in clike_langs: + if lang in compilers: + self.compilers[lang] = compilers[lang] + break + def process_compilers(self): ''' Populate self.compilers, which is the list of compilers that this @@ -485,14 +521,7 @@ class BuildTarget(Target): # Re-sort according to clike_langs self.compilers = OrderedDict(sorted(self.compilers.items(), key=lambda t: sort_clike(t[0]))) - else: - # No source files, target consists of only object files of unknown - # origin. Just add the first clike compiler that we have and hope - # that it can link these objects - for lang in clike_langs: - if lang in compilers: - self.compilers[lang] = compilers[lang] - break + # If all our sources are Vala, our target also needs the C compiler but # it won't get added above. if 'vala' in self.compilers and 'c' not in self.compilers: @@ -525,8 +554,7 @@ class BuildTarget(Target): generated twice, since the output needs to be passed to the ld_args and link_depends. """ - if not isinstance(sources, list): - sources = [sources] + sources = listify(sources) for s in sources: if hasattr(s, 'held_object'): s = s.held_object @@ -548,8 +576,7 @@ class BuildTarget(Target): return self.kwargs def unpack_holder(self, d): - if not isinstance(d, list): - d = [d] + d = listify(d) newd = [] for i in d: if isinstance(i, list): @@ -607,70 +634,54 @@ class BuildTarget(Target): self.copy_kwargs(kwargs) kwargs.get('modules', []) self.need_install = kwargs.get('install', self.need_install) - llist = kwargs.get('link_with', []) - if not isinstance(llist, list): - llist = [llist] + llist = extract_as_list(kwargs, 'link_with') for linktarget in llist: # 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(linktarget) - lwhole = kwargs.get('link_whole', []) - if not isinstance(lwhole, list): - lwhole = [lwhole] + 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 = kwargs.get('c_pch', []) - if not isinstance(c_pchlist, list): - c_pchlist = [c_pchlist] + + c_pchlist, cpp_pchlist, clist, cpplist, cslist, valalist, objclist, objcpplist, fortranlist, rustlist \ + = extract_as_list(kwargs, 'c_pch', 'cpp_pch', 'c_args', 'cpp_args', 'cs_args', 'vala_args', 'objc_args', + 'objcpp_args', 'fortran_args', 'rust_args') + self.add_pch('c', c_pchlist) - cpp_pchlist = kwargs.get('cpp_pch', []) - if not isinstance(cpp_pchlist, list): - cpp_pchlist = [cpp_pchlist] self.add_pch('cpp', cpp_pchlist) - clist = kwargs.get('c_args', []) - if not isinstance(clist, list): - clist = [clist] - self.add_compiler_args('c', clist) - cpplist = kwargs.get('cpp_args', []) - if not isinstance(cpplist, list): - cpplist = [cpplist] - self.add_compiler_args('cpp', cpplist) - cslist = kwargs.get('cs_args', []) - if not isinstance(cslist, list): - cslist = [cslist] - self.add_compiler_args('cs', cslist) - valalist = kwargs.get('vala_args', []) - if not isinstance(valalist, list): - valalist = [valalist] - self.add_compiler_args('vala', valalist) - objclist = kwargs.get('objc_args', []) - if not isinstance(objclist, list): - objclist = [objclist] - self.add_compiler_args('objc', objclist) - objcpplist = kwargs.get('objcpp_args', []) - if not isinstance(objcpplist, list): - objcpplist = [objcpplist] - self.add_compiler_args('objcpp', objcpplist) - fortranlist = kwargs.get('fortran_args', []) - if not isinstance(fortranlist, list): - fortranlist = [fortranlist] - self.add_compiler_args('fortran', fortranlist) - rustlist = kwargs.get('rust_args', []) - if not isinstance(rustlist, list): - rustlist = [rustlist] - self.add_compiler_args('rust', rustlist) + compiler_args = {'c': clist, 'cpp': cpplist, 'cs': cslist, 'vala': valalist, 'objc': objclist, 'objcpp': objcpplist, + 'fortran': fortranlist, 'rust': rustlist + } + for key, value in compiler_args.items(): + self.add_compiler_args(key, value) + if not isinstance(self, Executable): self.vala_header = kwargs.get('vala_header', self.name + '.h') self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi') self.vala_gir = kwargs.get('vala_gir', None) + dlist = stringlistify(kwargs.get('d_args', [])) self.add_compiler_args('d', dlist) + dfeatures = dict() + dfeature_unittest = kwargs.get('d_unittest', False) + if dfeature_unittest: + dfeatures['unittest'] = dfeature_unittest + 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: + 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.link_args = flatten(kwargs.get('link_args', [])) for i in self.link_args: if not isinstance(i, str): @@ -682,14 +693,10 @@ This will become a hard error in a future Meson release.''') self.process_link_depends(kwargs.get('link_depends', []), environment) # Target-specific include dirs must be added BEFORE include dirs from # internal deps (added inside self.add_deps()) to override them. - inclist = kwargs.get('include_directories', []) - if not isinstance(inclist, list): - inclist = [inclist] + inclist = extract_as_list(kwargs, 'include_directories') self.add_include_dirs(inclist) # Add dependencies (which also have include_directories) - deplist = kwargs.get('dependencies', []) - if not isinstance(deplist, list): - deplist = [deplist] + deplist = extract_as_list(kwargs, 'dependencies') self.add_deps(deplist) # If an item in this list is False, the output corresponding to # the list index of that item will not be installed @@ -705,9 +712,7 @@ This will become a hard error in a future Meson release.''') raise InvalidArguments('Argument gui_app must be boolean.') elif 'gui_app' in kwargs: raise InvalidArguments('Argument gui_app can only be used on executables.') - extra_files = kwargs.get('extra_files', []) - if not isinstance(extra_files, list): - extra_files = [extra_files] + extra_files = extract_as_list(kwargs, 'extra_files') for i in extra_files: assert(isinstance(i, File)) trial = os.path.join(environment.get_source_dir(), i.subdir, i.fname) @@ -720,9 +725,7 @@ This will become a hard error in a future Meson release.''') self.build_rpath = kwargs.get('build_rpath', '') if not isinstance(self.build_rpath, str): raise InvalidArguments('Build_rpath is not a string.') - resources = kwargs.get('resources', []) - if not isinstance(resources, list): - resources = [resources] + resources = extract_as_list(kwargs, 'resources') for r in resources: if not isinstance(r, str): raise InvalidArguments('Resource argument is not a string.') @@ -811,8 +814,7 @@ This will become a hard error in a future Meson release.''') return self.include_dirs def add_deps(self, deps): - if not isinstance(deps, list): - deps = [deps] + deps = listify(deps) for dep in deps: if hasattr(dep, 'held_object'): dep = dep.held_object @@ -1040,9 +1042,7 @@ class Generator: self.arglist = args if 'output' not in kwargs: raise InvalidArguments('Generator must have "output" keyword argument.') - outputs = kwargs['output'] - if not isinstance(outputs, list): - outputs = [outputs] + outputs = listify(kwargs['output']) for rule in outputs: if not isinstance(rule, str): raise InvalidArguments('"output" may only contain strings.') @@ -1075,8 +1075,10 @@ class Generator: basename = os.path.splitext(plainname)[0] return self.depfile.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) - def get_arglist(self): - return self.arglist + def get_arglist(self, inname): + plainname = os.path.split(inname)[1] + basename = os.path.splitext(plainname)[0] + return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist] def process_files(self, name, files, state, extra_args=[]): output = GeneratedList(self, extra_args=extra_args) @@ -1538,8 +1540,7 @@ class CustomTarget(Target): return deps def flatten_command(self, cmd): - if not isinstance(cmd, list): - cmd = [cmd] + cmd = listify(cmd) final_cmd = [] for c in cmd: if hasattr(c, 'held_object'): @@ -1574,9 +1575,7 @@ class CustomTarget(Target): self.sources.append(s) if 'output' not in kwargs: raise InvalidArguments('Missing keyword argument "output".') - self.outputs = kwargs['output'] - if not isinstance(self.outputs, list): - self.outputs = [self.outputs] + self.outputs = listify(kwargs['output']) # This will substitute values from the input into output and return it. inputs = get_sources_string_names(self.sources) values = get_filenames_templates_dict(inputs, []) @@ -1630,18 +1629,13 @@ class CustomTarget(Target): self.build_always = kwargs.get('build_always', False) if not isinstance(self.build_always, bool): raise InvalidArguments('Argument build_always must be a boolean.') - extra_deps = kwargs.get('depends', []) - if not isinstance(extra_deps, list): - extra_deps = [extra_deps] + extra_deps, depend_files = extract_as_list(kwargs, 'depends', 'depend_files', pop = False) for ed in extra_deps: while hasattr(ed, 'held_object'): ed = ed.held_object if not isinstance(ed, (CustomTarget, BuildTarget)): raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target (executable or a library)') self.extra_depends.append(ed) - depend_files = kwargs.get('depend_files', []) - if not isinstance(depend_files, list): - depend_files = [depend_files] for i in depend_files: if isinstance(i, (File, str)): self.depend_files.append(i) @@ -1791,8 +1785,7 @@ class Data: self.sources = sources self.install_dir = install_dir self.install_mode = install_mode - if not isinstance(self.sources, list): - self.sources = [self.sources] + self.sources = listify(self.sources) for s in self.sources: assert(isinstance(s, File)) diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 80a0f36..c17726a 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -16,7 +16,7 @@ import subprocess, os.path, tempfile from .. import mlog from .. import coredata -from ..mesonlib import EnvironmentException, version_compare, Popen_safe +from ..mesonlib import EnvironmentException, version_compare, Popen_safe, listify from .compilers import ( GCC_MINGW, @@ -1041,8 +1041,7 @@ class VisualStudioCCompiler(CCompiler): def get_link_whole_for(self, args): # Only since VS2015 - if not isinstance(args, list): - args = [args] + args = listify(args) return ['/WHOLEARCHIVE:' + x for x in args] def get_instruction_set_args(self, instruction_set): @@ -1054,5 +1053,3 @@ class VisualStudioCCompiler(CCompiler): # and the can not be called. return None return vs32_instruction_set_args.get(instruction_set, None) - - diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index c431194..89208e0 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -252,7 +252,7 @@ vs32_instruction_set_args = {'mmx': ['/arch:SSE'], # There does not seem to be a 'avx': ['/arch:AVX'], 'avx2': ['/arch:AVX2'], 'neon': None, -} + } # The 64 bit compiler defaults to /arch:avx. vs64_instruction_set_args = {'mmx': ['/arch:AVX'], diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index a989704..9739f28 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -27,6 +27,20 @@ from .compilers import ( CompilerArgs, ) +d_feature_args = {'gcc': {'unittest': '-funittest', + 'version': '-fversion', + 'import_dir': '-J' + }, + 'llvm': {'unittest': '-unittest', + 'version': '-d-version', + 'import_dir': '-J' + }, + 'dmd': {'unittest': '-unittest', + 'version': '-version', + 'import_dir': '-J' + } + } + class DCompiler(Compiler): def __init__(self, exelist, version, is_cross): self.language = 'd' @@ -79,8 +93,42 @@ 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_unittest_args(self): - return ['-unittest'] + def get_feature_args(self, kwargs): + res = [] + if 'unittest' in kwargs: + unittest = kwargs.pop('unittest') + unittest_arg = d_feature_args[self.id]['unittest'] + if not unittest_arg: + raise EnvironmentException('D compiler %s does not support the "unittest" feature.' % self.name_string()) + if unittest: + res.append(unittest_arg) + + if 'versions' in kwargs: + versions = kwargs.pop('versions') + if not isinstance(versions, list): + versions = [versions] + + version_arg = d_feature_args[self.id]['version'] + if not version_arg: + raise EnvironmentException('D compiler %s does not support the "feature versions" feature.' % self.name_string()) + for v in versions: + res.append('{0}={1}'.format(version_arg, v)) + + if 'import_dirs' in kwargs: + import_dirs = kwargs.pop('import_dirs') + if not isinstance(import_dirs, list): + import_dirs = [import_dirs] + + 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)) + + if kwargs: + raise EnvironmentException('Unknown D compiler feature(s) selected: %s' % ', '.join(kwargs.keys())) + + return res def get_buildtype_linker_args(self, buildtype): return [] @@ -217,9 +265,6 @@ class GnuDCompiler(DCompiler): def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): return self.build_unix_rpath_args(build_dir, from_dir, rpath_paths, build_rpath, install_rpath) - def get_unittest_args(self): - return ['-funittest'] - class LLVMDCompiler(DCompiler): def __init__(self, exelist, version, is_cross): diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index cc4837a..7c7f986 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -23,7 +23,7 @@ from enum import Enum from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many +from ..mesonlib import MesonException, Popen_safe, flatten, version_compare_many, listify # These must be defined in this file to avoid cyclical references. @@ -374,10 +374,7 @@ class ExternalProgram: def __init__(self, name, command=None, silent=False, search_dir=None): self.name = name if command is not None: - if not isinstance(command, list): - self.command = [command] - else: - self.command = command + self.command = listify(command) else: self.command = self._search(name, search_dir) if not silent: diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 2f0c9fc..c2b6dbd 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -24,7 +24,7 @@ import sysconfig from .. import mlog from .. import mesonlib -from ..mesonlib import Popen_safe +from ..mesonlib import Popen_safe, extract_as_list from ..environment import detect_cpu_family from .base import DependencyException, DependencyMethods @@ -132,9 +132,7 @@ class BoostDependency(ExternalDependency): return args def get_requested(self, kwargs): - candidates = kwargs.get('modules', []) - if not isinstance(candidates, list): - candidates = [candidates] + candidates = extract_as_list(kwargs, 'modules') for c in candidates: if not isinstance(c, str): raise DependencyException('Boost module argument is not a string.') diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 99e017b..8f183e5 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -23,7 +23,7 @@ from collections import OrderedDict from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, version_compare +from ..mesonlib import MesonException, Popen_safe, version_compare, extract_as_list from ..environment import for_windows, detect_cpu from .base import DependencyException, DependencyMethods @@ -468,12 +468,9 @@ class WxDependency(ExternalDependency): self.link_args = out.split() def get_requested(self, kwargs): - modules = 'modules' - if modules not in kwargs: + if 'modules' not in kwargs: return [] - candidates = kwargs[modules] - if not isinstance(candidates, list): - candidates = [candidates] + candidates = extract_as_list(kwargs, 'modules') for c in candidates: if not isinstance(c, str): raise DependencyException('wxwidgets module argument is not a string') diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 0b2a159..2f33a03 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -90,16 +90,19 @@ def find_coverage_tools(): genhtml_exe = None return gcovr_exe, lcov_exe, genhtml_exe -def detect_ninja(version='1.5'): +def detect_ninja(version='1.5', log=False): for n in ['ninja', 'ninja-build']: try: p, found = Popen_safe([n, '--version'])[0:2] except (FileNotFoundError, PermissionError): # Doesn't exist in PATH or isn't executable continue + found = found.strip() # Perhaps we should add a way for the caller to know the failure mode # (not found or too old) if p.returncode == 0 and mesonlib.version_compare(found, '>=' + version): + if log: + mlog.log('Found ninja-{} at {}'.format(found, shlex.quote(shutil.which(n)))) return n def detect_native_windows_arch(): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 47a1501..2cc5a9f 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -21,7 +21,7 @@ from . import optinterpreter from . import compilers from .wrap import wrap, WrapMode from . import mesonlib -from .mesonlib import FileMode, Popen_safe +from .mesonlib import FileMode, Popen_safe, listify, extract_as_list from .dependencies import ExternalProgram from .dependencies import InternalDependency, Dependency, DependencyException from .interpreterbase import InterpreterBase @@ -264,8 +264,7 @@ class DependencyHolder(InterpreterObject): return self.held_object.get_version() def pkgconfig_method(self, args, kwargs): - if not isinstance(args, list): - args = [args] + args = listify(args) if len(args) != 1: raise InterpreterException('get_pkgconfig_variable takes exactly one argument.') varname = args[0] @@ -669,9 +668,7 @@ class CompilerHolder(InterpreterObject): if not isinstance(nobuiltins, bool): raise InterpreterException('Type of no_builtin_args not a boolean.') args = [] - incdirs = kwargs.get('include_directories', []) - if not isinstance(incdirs, list): - incdirs = [incdirs] + incdirs = extract_as_list(kwargs, 'include_directories') for i in incdirs: if not isinstance(i, IncludeDirsHolder): raise InterpreterException('Include directories argument must be an include_directories object.') @@ -688,8 +685,7 @@ class CompilerHolder(InterpreterObject): def determine_dependencies(self, kwargs): deps = kwargs.get('dependencies', None) if deps is not None: - if not isinstance(deps, list): - deps = [deps] + deps = listify(deps) final_deps = [] for d in deps: try: @@ -752,10 +748,13 @@ class CompilerHolder(InterpreterObject): return self.compiler.symbols_have_underscore_prefix(self.environment) def unittest_args_method(self, args, kwargs): - # At time, only D compilers have this feature. - if not hasattr(self.compiler, 'get_unittest_args'): - raise InterpreterException('This {} compiler has no unittest arguments.'.format(self.compiler.get_display_language())) - return self.compiler.get_unittest_args() + ''' + This function is deprecated and should not be used. + It can be removed in a future version of Meson. + ''' + 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'}) def has_member_method(self, args, kwargs): if len(args) != 2: @@ -1029,7 +1028,6 @@ class CompilerHolder(InterpreterObject): h) return result - def first_supported_argument_method(self, args, kwargs): for i in mesonlib.stringlistify(args): if self.compiler.has_argument(i, self.environment): @@ -1246,45 +1244,50 @@ class MesonMain(InterpreterObject): pch_kwargs = set(['c_pch', 'cpp_pch']) -lang_arg_kwargs = set(['c_args', - 'cpp_args', - 'd_args', - 'fortran_args', - 'java_args', - 'objc_args', - 'objcpp_args', - 'rust_args', - 'vala_args', - 'cs_args', - ]) +lang_arg_kwargs = set([ + 'c_args', + 'cpp_args', + 'd_args', + 'd_import_dirs', + 'd_unittest', + 'd_module_versions', + 'fortran_args', + 'java_args', + 'objc_args', + 'objcpp_args', + 'rust_args', + 'vala_args', + 'cs_args', +]) vala_kwargs = set(['vala_header', 'vala_gir', 'vala_vapi']) rust_kwargs = set(['rust_crate_type']) -cs_kwargs = set(['resources']) - -buildtarget_kwargs = set(['build_by_default', - 'build_rpath', - 'dependencies', - 'extra_files', - 'gui_app', - 'link_with', - 'link_whole', - 'link_args', - 'link_depends', - 'implicit_include_directories', - 'include_directories', - 'install', - 'install_rpath', - 'install_dir', - 'name_prefix', - 'name_suffix', - 'native', - 'objects', - 'override_options', - 'pic', - 'sources', - 'vs_module_defs', - ]) +cs_kwargs = set(['resources', 'cs_args']) + +buildtarget_kwargs = set([ + 'build_by_default', + 'build_rpath', + 'dependencies', + 'extra_files', + 'gui_app', + 'link_with', + 'link_whole', + 'link_args', + 'link_depends', + 'implicit_include_directories', + 'include_directories', + 'install', + 'install_rpath', + 'install_dir', + 'name_prefix', + 'name_suffix', + 'native', + 'objects', + 'override_options', + 'pic', + 'sources', + 'vs_module_defs', +]) build_target_common_kwargs = ( buildtarget_kwargs | @@ -1454,8 +1457,7 @@ class Interpreter(InterpreterBase): raise InterpreterException('Module returned a value of unknown type.') def process_new_values(self, invalues): - if not isinstance(invalues, list): - invalues = [invalues] + invalues = listify(invalues) for v in invalues: if isinstance(v, (build.BuildTarget, build.CustomTarget, build.RunTarget)): self.add_target(v.name, v) @@ -1535,19 +1537,12 @@ class Interpreter(InterpreterBase): version = kwargs.get('version', self.project_version) if not isinstance(version, str): raise InterpreterException('Version must be a string.') - incs = kwargs.get('include_directories', []) - if not isinstance(incs, list): - incs = [incs] - libs = kwargs.get('link_with', []) - if not isinstance(libs, list): - libs = [libs] - sources = kwargs.get('sources', []) - if not isinstance(sources, list): - sources = [sources] + incs = extract_as_list(kwargs, 'include_directories') + libs = extract_as_list(kwargs, 'link_with') + sources = extract_as_list(kwargs, 'sources') sources = self.source_strings_to_files(self.flatten(sources)) deps = self.flatten(kwargs.get('dependencies', [])) - if not isinstance(deps, list): - deps = [deps] + deps = listify(deps) compile_args = mesonlib.stringlistify(kwargs.get('compile_args', [])) link_args = mesonlib.stringlistify(kwargs.get('link_args', [])) final_deps = [] @@ -1692,11 +1687,11 @@ class Interpreter(InterpreterBase): raise InterpreterException('Argument required for get_option.') optname = args[0] try: - return compilers.base_options[optname].value + return self.environment.get_coredata().base_options[optname].value except KeyError: pass try: - return self.environment.get_coredata().get_builtin_option(optname) + return self.environment.coredata.get_builtin_option(optname) except RuntimeError: pass try: @@ -1721,6 +1716,11 @@ class Interpreter(InterpreterBase): return self.coredata.external_args[lang] except KeyError: pass + # Some base options are not defined in some environments, return the default value. + try: + return compilers.base_options[optname].value + except KeyError: + pass raise InterpreterException('Tried to access unknown option "%s".' % optname) @noKwargs @@ -1730,8 +1730,7 @@ class Interpreter(InterpreterBase): return ConfigurationDataHolder() def parse_default_options(self, default_options): - if not isinstance(default_options, list): - default_options = [default_options] + default_options = listify(default_options) for option in default_options: if not isinstance(option, str): mlog.debug(option) @@ -2015,11 +2014,11 @@ class Interpreter(InterpreterBase): if not isinstance(use_native, bool): raise InvalidArguments('Argument to "native" must be a boolean.') if not use_native: - progobj = self.program_from_cross_file(args) + progobj = self.program_from_cross_file(args) 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' % exename) + raise InvalidArguments('Program "%s" not found or not executable' % args[0]) if progobj is None: return ExternalProgramHolder(dependencies.ExternalProgram('nonexistingprogram')) return progobj @@ -2277,12 +2276,8 @@ class Interpreter(InterpreterBase): elif len(args) == 1: if 'command' not in kwargs: raise InterpreterException('Missing "command" keyword argument') - all_args = kwargs['command'] - if not isinstance(all_args, list): - all_args = [all_args] - deps = kwargs.get('depends', []) - if not isinstance(deps, list): - deps = [deps] + all_args = extract_as_list(kwargs, 'command') + deps = extract_as_list(kwargs, 'depends') else: raise InterpreterException('Run_target needs at least one positional argument.') @@ -2333,8 +2328,7 @@ class Interpreter(InterpreterBase): if isinstance(envlist, EnvironmentVariablesHolder): env = envlist.held_object else: - if not isinstance(envlist, list): - envlist = [envlist] + envlist = listify(envlist) # Convert from array to environment object env = EnvironmentVariablesHolder() for e in envlist: @@ -2363,9 +2357,7 @@ class Interpreter(InterpreterBase): par = kwargs.get('is_parallel', True) if not isinstance(par, bool): raise InterpreterException('Keyword argument is_parallel must be a boolean.') - cmd_args = kwargs.get('args', []) - if not isinstance(cmd_args, list): - cmd_args = [cmd_args] + cmd_args = extract_as_list(kwargs, 'args') for i in cmd_args: if not isinstance(i, (str, mesonlib.File, TargetHolder)): raise InterpreterException('Command line arguments must be strings, files or targets.') @@ -2504,9 +2496,7 @@ class Interpreter(InterpreterBase): if not isinstance(install_dir, str): raise InvalidArguments('Keyword argument install_dir not a string.') if 'exclude_files' in kwargs: - exclude = kwargs['exclude_files'] - if not isinstance(exclude, list): - exclude = [exclude] + exclude = extract_as_list(kwargs, 'exclude_files') for f in exclude: if not isinstance(f, str): raise InvalidArguments('Exclude argument not a string.') @@ -2516,9 +2506,7 @@ class Interpreter(InterpreterBase): else: exclude_files = set() if 'exclude_directories' in kwargs: - exclude = kwargs['exclude_directories'] - if not isinstance(exclude, list): - exclude = [exclude] + exclude = extract_as_list(kwargs, 'exclude_directories') for d in exclude: if not isinstance(d, str): raise InvalidArguments('Exclude argument not a string.') @@ -2681,9 +2669,7 @@ different subdirectory. if re.fullmatch('[_a-zA-Z][_0-9a-zA-Z]*', setup_name) is None: raise InterpreterException('Setup name may only contain alphanumeric characters.') try: - inp = kwargs.get('exe_wrapper', []) - if not isinstance(inp, list): - inp = [inp] + inp = extract_as_list(kwargs, 'exe_wrapper') exe_wrapper = [] for i in inp: if hasattr(i, 'held_object'): @@ -2825,8 +2811,7 @@ different subdirectory. is_cross = False try: kw_src = self.flatten(kwargs['sources']) - if not isinstance(kw_src, list): - kw_src = [kw_src] + kw_src = listify(kw_src) except KeyError: kw_src = [] sources += kw_src @@ -2834,12 +2819,9 @@ different subdirectory. objs = self.flatten(kwargs.get('objects', [])) kwargs['dependencies'] = self.flatten(kwargs.get('dependencies', [])) if 'extra_files' in kwargs: - ef = kwargs['extra_files'] - if not isinstance(ef, list): - ef = [ef] + ef = extract_as_list(kwargs, 'extra_files') kwargs['extra_files'] = self.source_strings_to_files(ef) - if not isinstance(objs, list): - objs = [objs] + objs = listify(objs) self.check_sources_exist(os.path.join(self.source_root, self.subdir), sources) if targetholder is ExecutableHolder: targetclass = build.Executable diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index 4862e23..71134a8 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -14,6 +14,7 @@ """A library of random helper functionality.""" +import sys import stat import time import platform, subprocess, operator, os, shutil, re @@ -215,6 +216,9 @@ def is_osx(): def is_linux(): return platform.system().lower() == 'linux' +def is_haiku(): + return platform.system().lower() == 'haiku' + def is_windows(): platname = platform.system().lower() return platname == 'windows' or 'mingw' in platname @@ -470,6 +474,26 @@ def replace_if_different(dst, dst_tmp): else: os.unlink(dst_tmp) + +def listify(*args): + ''' + Returns a list with all args embedded in a list if they are not of type list. + This function preserves order. + ''' + if len(args) == 1: # Special case with one single arg + return args[0] if type(args[0]) is list else [args[0]] + return [item if type(item) is list else [item] for item in args] + + +def extract_as_list(dict_object, *keys, pop = False): + ''' + Extracts all values from given dict_object and listifies them. + ''' + if pop: + return listify(*[dict_object.pop(key, []) for key in keys]) + return listify(*[dict_object.get(key, []) for key in keys]) + + def typeslistify(item, types): ''' Ensure that type(@item) is one of @types or a @@ -506,6 +530,8 @@ def expand_arguments(args): return expended_args def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs): + if sys.version_info < (3, 6) or not sys.stdout.encoding: + return Popen_safe_legacy(args, write=write, stderr=stderr, **kwargs) p = subprocess.Popen(args, universal_newlines=True, close_fds=False, stdout=subprocess.PIPE, @@ -513,6 +539,25 @@ def Popen_safe(args, write=None, stderr=subprocess.PIPE, **kwargs): o, e = p.communicate(write) return p, o, e +def Popen_safe_legacy(args, write=None, stderr=subprocess.PIPE, **kwargs): + p = subprocess.Popen(args, universal_newlines=False, + stdout=subprocess.PIPE, + stderr=stderr, **kwargs) + if write is not None: + write = write.encode('utf-8') + o, e = p.communicate(write) + if o is not None: + if sys.stdout.encoding: + o = o.decode(encoding=sys.stdout.encoding, errors='replace').replace('\r\n', '\n') + else: + o = o.decode(errors='replace').replace('\r\n', '\n') + if e is not None: + if sys.stderr.encoding: + e = e.decode(encoding=sys.stderr.encoding, errors='replace').replace('\r\n', '\n') + else: + e = e.decode(errors='replace').replace('\r\n', '\n') + return p, o, e + def commonpath(paths): ''' For use on Python 3.4 where os.path.commonpath is not available. diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index ec2bc58..45e6026 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -121,13 +121,12 @@ class MesonApp: priv_dir = os.path.join(build_dir, 'meson-private/coredata.dat') if os.path.exists(priv_dir): if not handshake: - print('''Trying to run Meson on a build directory that has already been configured. -If you want to build it, just run your build command (e.g. ninja) inside the -build directory. Meson will autodetect any changes in your setup and regenerate -itself as required. Though it shouldn't be necessary, running ninja reconfigure -will force Meson to regenerate the build files. - -If you want to change option values, use meson configure instead.''') + print('Directory already configured, exiting Meson. Just run your build command\n' + '(e.g. ninja) and Meson will regenerate as necessary. If ninja fails, run ninja\n' + 'reconfigure to force Meson to regenerate.\n' + '\nIf build failures persist, manually wipe your build directory to clear any\n' + 'stored system data.\n' + '\nTo change option values, run meson configure instead.') sys.exit(0) else: if handshake: @@ -341,7 +340,7 @@ def run(args, mainfile=None): dir2 = '.' try: if mainfile is None: - sys.exit('I iz broken. Sorry.') + raise AssertionError('I iz broken. Sorry.') app = MesonApp(dir1, dir2, mainfile, handshake, options, sys.argv) except Exception as e: # Log directory does not exist, so just print @@ -357,7 +356,11 @@ def run(args, mainfile=None): 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:')) + # Error message mlog.log(e) + # Path to log file + logfile = os.path.join(app.build_dir, environment.Environment.log_dir, mlog.log_fname) + mlog.log("\nA full log can be found at", mlog.bold(logfile)) if os.environ.get('MESON_FORCE_BACKTRACE'): raise else: diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 659c8f5..18c1e6a 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -22,11 +22,12 @@ colorize_console = platform.system().lower() != 'windows' and os.isatty(sys.stdo os.environ.get('TERM') != 'dumb' log_dir = None log_file = None +log_fname = 'meson-log.txt' def initialize(logdir): global log_dir, log_file log_dir = logdir - log_file = open(os.path.join(logdir, 'meson-log.txt'), 'w', encoding='utf8') + log_file = open(os.path.join(logdir, log_fname), 'w', encoding='utf8') def shutdown(): global log_file diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 8b5b210..364bc45 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -4,7 +4,6 @@ from .. import build from .. import dependencies from .. import mlog from ..mesonlib import MesonException -from ..interpreterbase import permittedKwargs, noKwargs class permittedSnippetKwargs: diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index cb8f560..1ab075b 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -17,7 +17,6 @@ functionality such as gobject-introspection, gresources and gtk-doc''' from .. import build import os -import sys import copy import subprocess from . import ModuleReturnValue @@ -30,7 +29,7 @@ from .. import interpreter from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget from . import find_program, get_include_args from . import ExtensionModule -from . import noKwargs, permittedKwargs +from ..interpreterbase import noKwargs, permittedKwargs # gresource compilation is broken due to the way # the resource compiler and Ninja clash about it @@ -98,17 +97,12 @@ class GnomeModule(ExtensionModule): cmd = ['glib-compile-resources', '@INPUT@'] - source_dirs = kwargs.pop('source_dir', []) - if not isinstance(source_dirs, list): - source_dirs = [source_dirs] + source_dirs, dependencies = mesonlib.extract_as_list(kwargs, 'source_dir', 'dependencies', pop=True) if len(args) < 2: raise MesonException('Not enough arguments; the name of the resource ' 'and the path to the XML file are required') - dependencies = kwargs.pop('dependencies', []) - if not isinstance(dependencies, list): - dependencies = [dependencies] # Validate dependencies for (ii, dep) in enumerate(dependencies): if hasattr(dep, 'held_object'): @@ -328,8 +322,7 @@ class GnomeModule(ExtensionModule): cflags = OrderedSet() ldflags = OrderedSet() gi_includes = OrderedSet() - if not isinstance(deps, list): - deps = [deps] + deps = mesonlib.listify(deps) for dep in deps: if hasattr(dep, 'held_object'): @@ -464,17 +457,14 @@ class GnomeModule(ExtensionModule): scan_command += ['--filelist=' + gir_filelist_filename] if 'link_with' in kwargs: - link_with = kwargs.pop('link_with') - if not isinstance(link_with, list): - link_with = [link_with] + link_with = mesonlib.extract_as_list(kwargs, 'link_with', pop = True) + for link in link_with: scan_command += self._get_link_args(state, link.held_object, depends, use_gir_args=True) if 'includes' in kwargs: - includes = kwargs.pop('includes') - if not isinstance(includes, list): - includes = [includes] + includes = mesonlib.extract_as_list(kwargs, 'includes', pop = True) for inc in includes: if hasattr(inc, 'held_object'): inc = inc.held_object @@ -515,17 +505,17 @@ class GnomeModule(ExtensionModule): # FIXME: Linking directly to libasan is not recommended but g-ir-scanner # does not understand -f LDFLAGS. https://bugzilla.gnome.org/show_bug.cgi?id=783892 # ldflags += compilers.sanitizer_link_args(sanitize) - if kwargs.get('symbol_prefix'): + if 'symbol_prefix' in kwargs: sym_prefix = kwargs.pop('symbol_prefix') if not isinstance(sym_prefix, str): raise MesonException('Gir symbol prefix must be str') scan_command += ['--symbol-prefix=%s' % sym_prefix] - if kwargs.get('identifier_prefix'): + if 'identifier_prefix' in kwargs: identifier_prefix = kwargs.pop('identifier_prefix') if not isinstance(identifier_prefix, str): raise MesonException('Gir identifier prefix must be str') scan_command += ['--identifier-prefix=%s' % identifier_prefix] - if kwargs.get('export_packages'): + if 'export_packages' in kwargs: pkgs = kwargs.pop('export_packages') if isinstance(pkgs, str): scan_command += ['--pkg-export=%s' % pkgs] @@ -534,9 +524,7 @@ class GnomeModule(ExtensionModule): else: raise MesonException('Gir export packages must be str or list') - deps = kwargs.pop('dependencies', []) - if not isinstance(deps, list): - deps = [deps] + deps = mesonlib.extract_as_list(kwargs, 'dependencies', pop = True) deps = (girtarget.get_all_link_deps() + girtarget.get_external_deps() + deps) # Need to recursively add deps on GirTarget sources from our @@ -593,9 +581,7 @@ class GnomeModule(ExtensionModule): for i in gi_includes: scan_command += ['--add-include-path=%s' % i] - inc_dirs = kwargs.pop('include_directories', []) - if not isinstance(inc_dirs, list): - inc_dirs = [inc_dirs] + inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories', pop = True) for incd in inc_dirs: if not isinstance(incd.held_object, (str, build.IncludeDirs)): raise MesonException( @@ -618,7 +604,7 @@ class GnomeModule(ExtensionModule): scankwargs = {'output': girfile, 'command': scan_command, 'depends': depends} - if kwargs.get('install'): + if 'install' in kwargs: scankwargs['install'] = kwargs['install'] scankwargs['install_dir'] = kwargs.get('install_dir_gir', os.path.join(state.environment.get_datadir(), 'gir-1.0')) @@ -636,7 +622,7 @@ class GnomeModule(ExtensionModule): 'output': typelib_output, 'command': typelib_cmd, } - if kwargs.get('install'): + if 'install' in kwargs: typelib_kwargs['install'] = kwargs['install'] typelib_kwargs['install_dir'] = kwargs.get('install_dir_typelib', os.path.join(state.environment.get_libdir(), 'girepository-1.0')) @@ -680,6 +666,11 @@ class GnomeModule(ExtensionModule): source_str = '@@'.join(sources) langs = mesonlib.stringlistify(kwargs.pop('languages', [])) + if langs: + mlog.log(mlog.red('DEPRECATION:'), '''The "languages" argument of gnome.yelp() is deprecated. +Use a LINGUAS file in the sources directory instead. +This will become a hard error in the future.''') + media = mesonlib.stringlistify(kwargs.pop('media', [])) symlinks = kwargs.pop('symlink_media', True) @@ -705,18 +696,22 @@ class GnomeModule(ExtensionModule): args.append('--langs=' + '@@'.join(langs)) inscript = build.RunScript(script, args) - potargs = state.environment.get_build_command() + ['--internal', 'yelphelper', 'pot', - '--subdir=' + state.subdir, - '--id=' + project_id, - '--sources=' + source_str] + potargs = state.environment.get_build_command() + [ + '--internal', 'yelphelper', 'pot', + '--subdir=' + state.subdir, + '--id=' + project_id, + '--sources=' + source_str, + ] pottarget = build.RunTarget('help-' + project_id + '-pot', potargs[0], potargs[1:], [], state.subdir) - poargs = state.environment.get_build_command() + ['--internal', 'yelphelper', 'update-po', - '--subdir=' + state.subdir, - '--id=' + project_id, - '--sources=' + source_str, - '--langs=' + '@@'.join(langs)] + poargs = state.environment.get_build_command() + [ + '--internal', 'yelphelper', 'update-po', + '--subdir=' + state.subdir, + '--id=' + project_id, + '--sources=' + source_str, + '--langs=' + '@@'.join(langs), + ] potarget = build.RunTarget('help-' + project_id + '-update-po', poargs[0], poargs[1:], [], state.subdir) @@ -754,9 +749,7 @@ class GnomeModule(ExtensionModule): if mode not in VALID_MODES: raise MesonException('gtkdoc: Mode {} is not a valid mode: {}'.format(mode, VALID_MODES)) - src_dirs = kwargs['src_dir'] - if not isinstance(src_dirs, list): - src_dirs = [src_dirs] + src_dirs = mesonlib.extract_as_list(kwargs, 'src_dir') header_dirs = [] for src_dir in src_dirs: if hasattr(src_dir, 'held_object'): @@ -801,9 +794,7 @@ class GnomeModule(ExtensionModule): def _get_build_args(self, kwargs, state): args = [] cflags, ldflags, gi_includes = self._get_dependencies_flags(kwargs.get('dependencies', []), state, include_rpath=True) - inc_dirs = kwargs.get('include_directories', []) - if not isinstance(inc_dirs, list): - inc_dirs = [inc_dirs] + inc_dirs = mesonlib.extract_as_list(kwargs, 'include_directories') for incd in inc_dirs: if not isinstance(incd.held_object, (str, build.IncludeDirs)): raise MesonException( @@ -834,9 +825,7 @@ class GnomeModule(ExtensionModule): if kwarg_name not in kwargs: return [] - new_args = kwargs[kwarg_name] - if not isinstance(new_args, list): - new_args = [new_args] + new_args = mesonlib.extract_as_list(kwargs, kwarg_name) args = [] for i in new_args: if expend_file_state and isinstance(i, mesonlib.File): @@ -850,7 +839,8 @@ class GnomeModule(ExtensionModule): return [] - @permittedKwargs({'interface_prefix', 'namespace', 'object_manager', 'build_by_default', 'annotations'}) + @permittedKwargs({'interface_prefix', 'namespace', 'object_manager', 'build_by_default', + 'annotations', 'docbook'}) def gdbus_codegen(self, state, args, kwargs): if len(args) != 2: raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') @@ -864,6 +854,8 @@ class GnomeModule(ExtensionModule): cmd += ['--c-namespace', kwargs.pop('namespace')] if kwargs.get('object_manager', False): cmd += ['--c-generate-object-manager'] + if 'docbook' in kwargs: + cmd += ['--generate-docbook', kwargs.pop('docbook')] # Annotations are a bit ugly in that they are a list of lists of strings... annotations = kwargs.pop('annotations', []) @@ -1192,12 +1184,8 @@ G_END_DECLS''' @staticmethod def _vapi_args_to_command(prefix, variable, kwargs, accept_vapi=False): - arg_list = kwargs.get(variable) - if not arg_list: - return [] + arg_list = mesonlib.extract_as_list(kwargs, variable) ret = [] - if not isinstance(arg_list, list): - arg_list = [arg_list] for arg in arg_list: if not isinstance(arg, str): types = 'strings' + ' or InternalDependencys' if accept_vapi else '' @@ -1216,9 +1204,7 @@ G_END_DECLS''' arg_list = kwargs.get('packages') if not arg_list: return [], [], [], [] - if not isinstance(arg_list, list): - arg_list = [arg_list] - + arg_list = mesonlib.listify(arg_list) vapi_depends = [] vapi_packages = [] vapi_includes = [] @@ -1292,12 +1278,10 @@ G_END_DECLS''' cmd += pkg_cmd cmd += ['--metadatadir=' + source_dir] - inputs = kwargs.get('sources') - if not inputs: + if 'sources' not in kwargs: raise MesonException('sources are required to generate the vapi file') - if not isinstance(inputs, list): - inputs = [inputs] + inputs = mesonlib.extract_as_list(kwargs, 'sources') link_with = [] for i in inputs: diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 2af09de..c1dd837 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import sys import shutil from os import path @@ -20,7 +19,7 @@ from .. import coredata, mesonlib, build from ..mesonlib import MesonException from . import ModuleReturnValue from . import ExtensionModule -from . import permittedKwargs +from ..interpreterbase import permittedKwargs PRESET_ARGS = { 'glib': [ @@ -72,8 +71,10 @@ class I18nModule(ExtensionModule): datadirs = self._get_data_dirs(state, mesonlib.stringlistify(kwargs.pop('data_dirs', []))) datadirs = '--datadirs=' + ':'.join(datadirs) if datadirs else None - command = state.environment.get_build_command() + ['--internal', 'msgfmthelper', - '@INPUT@', '@OUTPUT@', file_type, podir] + command = state.environment.get_build_command() + [ + '--internal', 'msgfmthelper', + '@INPUT@', '@OUTPUT@', file_type, podir + ] if datadirs: command.append(datadirs) @@ -81,7 +82,7 @@ class I18nModule(ExtensionModule): ct = build.CustomTarget(kwargs['output'] + '_merge', state.subdir, kwargs) return ModuleReturnValue(ct, [ct]) - @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages', 'args', 'preset'}) + @permittedKwargs({'po_dir', 'data_dirs', 'type', 'languages', 'args', 'preset', 'install'}) def gettext(self, state, args, kwargs): if len(args) != 1: raise coredata.MesonException('Gettext requires one positional argument (package name).') @@ -126,16 +127,21 @@ class I18nModule(ExtensionModule): updatepoargs.append(extra_args) updatepotarget = build.RunTarget(packagename + '-update-po', updatepoargs[0], updatepoargs[1:], [], state.subdir) - script = state.environment.get_build_command() - args = ['--internal', 'gettext', 'install', - '--subdir=' + state.subdir, - '--localedir=' + state.environment.coredata.get_builtin_option('localedir'), - pkg_arg] - if lang_arg: - args.append(lang_arg) - iscript = build.RunScript(script, args) - - return ModuleReturnValue(None, [pottarget, gmotarget, iscript, updatepotarget]) + targets = [pottarget, gmotarget, updatepotarget] + + install = kwargs.get('install', True) + if install: + script = state.environment.get_build_command() + args = ['--internal', 'gettext', 'install', + '--subdir=' + state.subdir, + '--localedir=' + state.environment.coredata.get_builtin_option('localedir'), + pkg_arg] + if lang_arg: + args.append(lang_arg) + iscript = build.RunScript(script, args) + targets.append(iscript) + + return ModuleReturnValue(None, targets) def initialize(): return I18nModule() diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py index dd2f215..758eeae 100644 --- a/mesonbuild/modules/modtest.py +++ b/mesonbuild/modules/modtest.py @@ -14,7 +14,7 @@ from . import ModuleReturnValue from . import ExtensionModule -from . import noKwargs +from ..interpreterbase import noKwargs class TestModule(ExtensionModule): diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index 0a0498c..aaed820 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -19,7 +19,7 @@ from .. import mesonlib from .. import mlog from . import ModuleReturnValue from . import ExtensionModule -from . import permittedKwargs +from ..interpreterbase import permittedKwargs class PkgConfigModule(ExtensionModule): @@ -108,8 +108,7 @@ class PkgConfigModule(ExtensionModule): ofile.write('\n') def process_libs(self, libs): - if not isinstance(libs, list): - libs = [libs] + libs = mesonlib.listify(libs) processed_libs = [] for l in libs: if hasattr(l, 'held_object'): @@ -121,7 +120,7 @@ class PkgConfigModule(ExtensionModule): @permittedKwargs({'libraries', 'version', 'name', 'description', 'filebase', 'subdirs', 'requires', 'requires_private', 'libraries_private', - 'install_dir', 'extra_cflags', 'variables', 'url'}) + 'install_dir', 'extra_cflags', 'variables', 'url', 'd_module_versions'}) def generate(self, state, args, kwargs): if len(args) > 0: raise mesonlib.MesonException('Pkgconfig_gen takes no positional arguments.') @@ -148,6 +147,12 @@ class PkgConfigModule(ExtensionModule): conflicts = mesonlib.stringlistify(kwargs.get('conflicts', [])) extra_cflags = mesonlib.stringlistify(kwargs.get('extra_cflags', [])) + dversions = kwargs.get('d_module_versions', None) + if dversions: + compiler = state.environment.coredata.compilers.get('d') + if compiler: + extra_cflags.extend(compiler.get_feature_args({'versions': dversions})) + def parse_variable_list(stringlist): reserved = ['prefix', 'libdir', 'includedir'] variables = [] diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index 94db75c..4fae88b 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -18,7 +18,8 @@ from .. import mesonlib, dependencies from . import ExtensionModule from mesonbuild.modules import ModuleReturnValue -from . import noKwargs, permittedSnippetKwargs +from . import permittedSnippetKwargs +from ..interpreterbase import noKwargs from ..interpreter import shlib_kwargs mod_kwargs = set() diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 4056b6d..a63aff8 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -15,12 +15,12 @@ import os from .. import mlog from .. import build -from ..mesonlib import MesonException, Popen_safe +from ..mesonlib import MesonException, Popen_safe, extract_as_list from ..dependencies import Qt4Dependency from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue -from . import permittedKwargs +from ..interpreterbase import permittedKwargs class Qt4Module(ExtensionModule): tools_detected = False @@ -99,21 +99,8 @@ class Qt4Module(ExtensionModule): @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'}) def preprocess(self, state, args, kwargs): - rcc_files = kwargs.pop('qresources', []) - if not isinstance(rcc_files, list): - rcc_files = [rcc_files] - ui_files = kwargs.pop('ui_files', []) - if not isinstance(ui_files, list): - ui_files = [ui_files] - moc_headers = kwargs.pop('moc_headers', []) - if not isinstance(moc_headers, list): - moc_headers = [moc_headers] - moc_sources = kwargs.pop('moc_sources', []) - if not isinstance(moc_sources, list): - moc_sources = [moc_sources] - sources = kwargs.pop('sources', []) - if not isinstance(sources, list): - sources = [sources] + rcc_files, ui_files, moc_headers, moc_sources, sources \ + = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True) sources += args[1:] method = kwargs.get('method', 'auto') self._detect_tools(state.environment, method) diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 6194a23..08ce662 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -15,12 +15,12 @@ import os from .. import mlog from .. import build -from ..mesonlib import MesonException, Popen_safe +from ..mesonlib import MesonException, Popen_safe, extract_as_list from ..dependencies import Qt5Dependency from . import ExtensionModule import xml.etree.ElementTree as ET from . import ModuleReturnValue -from . import permittedKwargs +from ..interpreterbase import permittedKwargs class Qt5Module(ExtensionModule): tools_detected = False @@ -105,21 +105,8 @@ class Qt5Module(ExtensionModule): @permittedKwargs({'moc_headers', 'moc_sources', 'ui_files', 'qresources', 'method'}) def preprocess(self, state, args, kwargs): - rcc_files = kwargs.pop('qresources', []) - if not isinstance(rcc_files, list): - rcc_files = [rcc_files] - ui_files = kwargs.pop('ui_files', []) - if not isinstance(ui_files, list): - ui_files = [ui_files] - moc_headers = kwargs.pop('moc_headers', []) - if not isinstance(moc_headers, list): - moc_headers = [moc_headers] - moc_sources = kwargs.pop('moc_sources', []) - if not isinstance(moc_sources, list): - moc_sources = [moc_sources] - sources = kwargs.pop('sources', []) - if not isinstance(sources, list): - sources = [sources] + rcc_files, ui_files, moc_headers, moc_sources, sources \ + = extract_as_list(kwargs, 'qresources', 'ui_files', 'moc_headers', 'moc_sources', 'sources', pop = True) sources += args[1:] method = kwargs.get('method', 'auto') self._detect_tools(state.environment, method) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index b0a8db9..dbb01f7 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -22,7 +22,7 @@ from .. import mlog from . import GirTarget, TypelibTarget from . import ModuleReturnValue from . import ExtensionModule -from . import noKwargs +from ..interpreterbase import noKwargs import os diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py index 828afec..b774cff 100644 --- a/mesonbuild/modules/unstable_simd.py +++ b/mesonbuild/modules/unstable_simd.py @@ -73,9 +73,7 @@ class SimdModule(ExtensionModule): } lib_kwargs.update(basic_kwargs) langarg_key = compiler.get_language() + '_args' - old_lang_args = lib_kwargs.get(langarg_key, []) - if not isinstance(old_lang_args, list): - old_lang_args = [old_lang_args] + old_lang_args = mesonlib.extract_as_list(lib_kwargs, langarg_key) all_lang_args = old_lang_args + args lib_kwargs[langarg_key] = all_lang_args result.append(interpreter.func_static_lib(None, [libname], lib_kwargs)) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index 6fef5bb..c16d7a8 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -16,11 +16,11 @@ import os from .. import mlog from .. import mesonlib, dependencies, build -from ..mesonlib import MesonException +from ..mesonlib import MesonException, extract_as_list from . import get_include_args from . import ModuleReturnValue from . import ExtensionModule -from . import permittedKwargs +from ..interpreterbase import permittedKwargs class WindowsModule(ExtensionModule): @@ -35,9 +35,7 @@ class WindowsModule(ExtensionModule): comp = self.detect_compiler(state.compilers) extra_args = mesonlib.stringlistify(kwargs.get('args', [])) - inc_dirs = kwargs.pop('include_directories', []) - if not isinstance(inc_dirs, list): - inc_dirs = [inc_dirs] + inc_dirs = extract_as_list(kwargs, 'include_directories', pop = True) for incd in inc_dirs: if not isinstance(incd.held_object, (str, build.IncludeDirs)): raise MesonException('Resource include dirs should be include_directories().') diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 2520ae8..4e049a8 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -195,10 +195,10 @@ class TestHarness: def run_single_test(self, wrap, test): if test.fname[0].endswith('.jar'): cmd = ['java', '-jar'] + test.fname - elif not test.is_cross and run_with_mono(test.fname[0]): + elif not test.is_cross_built and run_with_mono(test.fname[0]): cmd = ['mono'] + test.fname else: - if test.is_cross: + if test.is_cross_built: if test.exe_runner is None: # Can not run test on cross compiled executable # because there is no execute wrapper. @@ -224,7 +224,7 @@ class TestHarness: child_env.update(test.env) if len(test.extra_paths) > 0: - child_env['PATH'] += os.pathsep.join([''] + test.extra_paths) + child_env['PATH'] = os.pathsep.join(test.extra_paths + ['']) + child_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 diff --git a/mesonbuild/rewriter.py b/mesonbuild/rewriter.py index b88c5ef..b88c5ef 100755..100644 --- a/mesonbuild/rewriter.py +++ b/mesonbuild/rewriter.py diff --git a/mesonbuild/scripts/yelphelper.py b/mesonbuild/scripts/yelphelper.py index 978a870..ab99267 100644 --- a/mesonbuild/scripts/yelphelper.py +++ b/mesonbuild/scripts/yelphelper.py @@ -18,6 +18,7 @@ import shutil import argparse from .. import mlog from . import destdir_join +from .gettext import read_linguas parser = argparse.ArgumentParser() parser.add_argument('command') @@ -108,6 +109,9 @@ def run(args): build_subdir = os.path.join(os.environ['MESON_BUILD_ROOT'], options.subdir) abs_sources = [os.path.join(src_subdir, 'C', source) for source in sources] + if not langs: + langs = read_linguas(src_subdir) + if options.command == 'pot': build_pot(src_subdir, options.project_id, sources) elif options.command == 'update-po': diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py index ac84d0e..933bbd5 100644 --- a/mesonbuild/wrap/wrap.py +++ b/mesonbuild/wrap/wrap.py @@ -299,7 +299,7 @@ class Resolver: def extract_package(self, package): if sys.version_info < (3, 5): try: - import lzma + import lzma # noqa: F401 del lzma except ImportError: pass diff --git a/mesonrewriter.py b/mesonrewriter.py index 426d878..e6f2637 100755 --- a/mesonrewriter.py +++ b/mesonrewriter.py @@ -23,11 +23,10 @@ # - move targets # - reindent? -from mesonbuild import mesonmain, mlog +from mesonbuild import mesonmain import sys if __name__ == '__main__': print('Warning: This executable is deprecated. Use "meson rewrite" instead.', file=sys.stderr) sys.exit(mesonmain.run(['rewrite'] + sys.argv[1:])) - diff --git a/msi/License.rtf b/msi/License.rtf index 9b58df9..9b58df9 100755..100644 --- a/msi/License.rtf +++ b/msi/License.rtf diff --git a/msi/createmsi.py b/msi/createmsi.py index 921e2ac..dd92e50 100755 --- a/msi/createmsi.py +++ b/msi/createmsi.py @@ -56,7 +56,7 @@ class PackageGenerator: 'Title': 'Meson', 'Description': 'Meson executables', 'Level': '1', - 'Absent': 'disallow', + 'Absent': 'disallow', }, self.staging_dirs[1]: { 'Id': 'NinjaProgram', @@ -77,8 +77,16 @@ class PackageGenerator: modules = [os.path.splitext(os.path.split(x)[1])[0] for x in glob(os.path.join('mesonbuild/modules/*'))] modules = ['mesonbuild.modules.' + x for x in modules if not x.startswith('_')] modulestr = ','.join(modules) - subprocess.check_call(['c:\\Python\python.exe', - 'c:\\Python\Scripts\\cxfreeze', + python = 'c:\\Python\python.exe' + if sys.executable: + python = sys.executable + cxfreeze = os.path.join(os.path.dirname(python), "Scripts", "cxfreeze") + if not os.path.isfile(cxfreeze): + print("ERROR: This script requires cx_freeze module") + sys.exit(1) + + subprocess.check_call([python, + cxfreeze, '--target-dir', main_stage, '--include-modules', @@ -101,7 +109,7 @@ class PackageGenerator: 'Language': '1033', 'Codepage': '1252', 'Version': self.version, - }) + }) package = ET.SubElement(product, 'Package', { 'Id': '*', @@ -113,7 +121,7 @@ class PackageGenerator: 'Languages': '1033', 'Compressed': 'yes', 'SummaryCodepage': '1252', - }) + }) if self.bytesize == 64: package.set('Platform', 'x64') @@ -121,25 +129,26 @@ class PackageGenerator: 'Id': '1', 'Cabinet': 'meson.cab', 'EmbedCab': 'yes', - }) + }) targetdir = ET.SubElement(product, 'Directory', { 'Id': 'TARGETDIR', 'Name': 'SourceDir', - }) + }) progfiledir = ET.SubElement(targetdir, 'Directory', { - 'Id' : self.progfile_dir, - }) + 'Id': self.progfile_dir, + }) installdir = ET.SubElement(progfiledir, 'Directory', { 'Id': 'INSTALLDIR', - 'Name': 'Meson'}) + 'Name': 'Meson', + }) ET.SubElement(product, 'Property', { 'Id': 'WIXUI_INSTALLDIR', 'Value': 'INSTALLDIR', - }) + }) ET.SubElement(product, 'UIRef', { 'Id': 'WixUI_FeatureTree', - }) + }) for sd in self.staging_dirs: assert(os.path.isdir(sd)) top_feature = ET.SubElement(product, 'Feature', { @@ -149,7 +158,7 @@ class PackageGenerator: 'Display': 'expand', 'Level': '1', 'ConfigurableDirectory': 'INSTALLDIR', - }) + }) for sd in self.staging_dirs: nodes = {} for root, dirs, files in os.walk(sd): @@ -157,7 +166,7 @@ class PackageGenerator: nodes[root] = cur_node self.create_xml(nodes, sd, installdir, sd) self.build_features(nodes, top_feature, sd) - ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8',xml_declaration=True) + ET.ElementTree(self.root).write(self.main_xml, encoding='utf-8', xml_declaration=True) # ElementTree can not do prettyprinting so do it manually import xml.dom.minidom doc = xml.dom.minidom.parse(self.main_xml) @@ -169,8 +178,7 @@ class PackageGenerator: for component_id in self.feature_components[staging_dir]: ET.SubElement(feature, 'ComponentRef', { 'Id': component_id, - }) - + }) def create_xml(self, nodes, current_dir, parent_xml_node, staging_dir): cur_node = nodes[current_dir] @@ -179,7 +187,7 @@ class PackageGenerator: comp_xml_node = ET.SubElement(parent_xml_node, 'Component', { 'Id': component_id, 'Guid': gen_guid(), - }) + }) self.feature_components[staging_dir].append(component_id) if self.bytesize == 64: comp_xml_node.set('Win64', 'yes') @@ -200,20 +208,23 @@ class PackageGenerator: 'Id': file_id, 'Name': f, 'Source': os.path.join(current_dir, f), - }) + }) for dirname in cur_node.dirs: dir_id = os.path.join(current_dir, dirname).replace('\\', '_').replace('/', '_') dir_node = ET.SubElement(parent_xml_node, 'Directory', { 'Id': dir_id, 'Name': dirname, - }) + }) self.create_xml(nodes, os.path.join(current_dir, dirname), dir_node, staging_dir) def build_package(self): wixdir = 'c:\\Program Files\\Wix Toolset v3.11\\bin' if not os.path.isdir(wixdir): wixdir = 'c:\\Program Files (x86)\\Wix Toolset v3.11\\bin' + if not os.path.isdir(wixdir): + print("ERROR: This script requires WIX") + sys.exit(1) subprocess.check_call([os.path.join(wixdir, 'candle'), self.main_xml]) subprocess.check_call([os.path.join(wixdir, 'light'), '-ext', 'WixUIExtension', diff --git a/run_tests.py b/run_tests.py index efbdaaa..f74ff13 100755 --- a/run_tests.py +++ b/run_tests.py @@ -104,6 +104,12 @@ def get_backend_commands(backend, debug=False): return cmd, clean_cmd, test_cmd, install_cmd, uninstall_cmd def ensure_backend_detects_changes(backend): + # We're using a ninja with QuLogic's patch for sub-1s resolution timestamps + # and not running on HFS+ which only stores dates in seconds: + # https://developer.apple.com/legacy/library/technotes/tn/tn1150.html#HFSPlusDates + # FIXME: Upgrade Travis image to Apple FS when that becomes available + if 'MESON_FIXED_NINJA' in os.environ and not mesonlib.is_osx(): + return # This is needed to increase the difference between build.ninja's # timestamp and the timestamp of whatever you changed due to a Ninja # bug: https://github.com/ninja-build/ninja/issues/371 @@ -171,7 +177,7 @@ if __name__ == '__main__': backend = Backend.xcode break # Running on a developer machine? Be nice! - if not mesonlib.is_windows() and 'TRAVIS' not in os.environ: + if not mesonlib.is_windows() and not mesonlib.is_haiku() and 'TRAVIS' not in os.environ: os.nice(20) # Appveyor sets the `platform` environment variable which completely messes # up building with the vs2010 and vs2015 backends. diff --git a/run_unittests.py b/run_unittests.py index 6487496..b217714 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -1946,7 +1946,12 @@ cpu = 'armv7' # Not sure if correct. endian = 'little' ''' % os.path.join(testdir, 'some_cross_tool.py')) crossfile.flush() - self.init(testdir, ['--cross-file='+crossfile.name]) + self.init(testdir, ['--cross-file=' + crossfile.name]) + + def test_reconfigure(self): + testdir = os.path.join(self.unit_test_dir, '13 reconfigure') + self.init(testdir, ['-Db_lto=true'], default_args=False) + self.build('reconfigure') class LinuxArmCrossCompileTests(BasePlatformTests): @@ -14,8 +14,10 @@ ignore = E305, # E401: multiple imports on one line E401, - # too many leading '#' for block comment + # E266: too many leading '#' for block comment E266, - # module level import not at top of file - E402 + # E402: module level import not at top of file + E402, + # E731: do not assign a lambda expression, use a def (too many false positives) + E731 max-line-length = 120 diff --git a/test cases/common/146 C and CPP link/dummy.c b/test cases/common/146 C and CPP link/dummy.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/common/146 C and CPP link/dummy.c diff --git a/test cases/common/146 C and CPP link/meson.build b/test cases/common/146 C and CPP link/meson.build index db84445..2dd3364 100644 --- a/test cases/common/146 C and CPP link/meson.build +++ b/test cases/common/146 C and CPP link/meson.build @@ -65,10 +65,44 @@ libfoo = shared_library( # # VS2010 lacks the /WHOLEARCHIVE option that later versions of MSVC support, so # don't run this tests on that backend. -if meson.backend() != 'vs2010' +if not (cxx.get_id() == 'msvc' and cxx.version().version_compare('<19')) libfoowhole = shared_library( 'foowhole', ['foobar.c', 'foobar.h'], link_whole : [libc, libcpp], ) endif + +# Test sublinking (linking C and C++, then linking that to C) +libfoo_static = static_library( + 'foo_static', + ['foobar.c', 'foobar.h'], + link_with : [libc, libcpp], +) + +libsub = shared_library( + 'sub', + ['sub.c', 'sub.h'], + link_with : libfoo_static, +) + +if not (cxx.get_id() == 'msvc' and cxx.version().version_compare('<19')) + libsubwhole = shared_library( + 'subwhole', + ['sub.c', 'sub.h'], + link_whole : libfoo_static, + ) +endif + +# Test that it really is recursive +libsub_static = static_library( + 'sub_static', + ['sub.c', 'sub.h'], + link_with : libfoo_static, +) + +libsubsub = shared_library( + 'subsub', + ['dummy.c'], + link_with : libsub_static, +) diff --git a/test cases/common/146 C and CPP link/sub.c b/test cases/common/146 C and CPP link/sub.c new file mode 100644 index 0000000..7c078f8 --- /dev/null +++ b/test cases/common/146 C and CPP link/sub.c @@ -0,0 +1,19 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "sub.h" + +float a_half(void) { + return .5; +} diff --git a/test cases/common/146 C and CPP link/sub.h b/test cases/common/146 C and CPP link/sub.h new file mode 100644 index 0000000..5b02e17 --- /dev/null +++ b/test cases/common/146 C and CPP link/sub.h @@ -0,0 +1,16 @@ +/* Copyright © 2017 Dylan Baker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +float a_half(void); diff --git a/test cases/common/147 mesonintrospect from scripts/check_env.py b/test cases/common/147 mesonintrospect from scripts/check_env.py index 9bd64d7..2d46d88 100644 --- a/test cases/common/147 mesonintrospect from scripts/check_env.py +++ b/test cases/common/147 mesonintrospect from scripts/check_env.py @@ -16,8 +16,8 @@ mesonintrospect = os.environ['MESONINTROSPECT'] introspect_arr = shlex.split(mesonintrospect) -#print(mesonintrospect) -#print(introspect_arr) +# print(mesonintrospect) +# print(introspect_arr) some_executable = introspect_arr[0] diff --git a/test cases/common/159 reserved targets/meson.build b/test cases/common/159 reserved targets/meson.build index 26572c0..24fd937 100644 --- a/test cases/common/159 reserved targets/meson.build +++ b/test cases/common/159 reserved targets/meson.build @@ -26,6 +26,9 @@ subdir('uninstall') subdir('runtarget') +py3 = import('python3').find_python() + custom_target('ctlist-test', output : 'out.txt', - command : ['echo'], capture : true, + command : [py3, '-c', 'print("")'], + capture : true, build_by_default : true) diff --git a/test cases/common/160 duplicate source names/dir1/file.c b/test cases/common/160 duplicate source names/dir1/file.c new file mode 100644 index 0000000..094e187 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir1/file.c @@ -0,0 +1,16 @@ +extern int dir2; +extern int dir2_dir1; +extern int dir3; +extern int dir3_dir1; + +int main() { + if (dir2 != 20) + return 1; + if (dir2_dir1 != 21) + return 1; + if (dir3 != 30) + return 1; + if (dir3_dir1 != 31) + return 1; + return 0; +} diff --git a/test cases/common/160 duplicate source names/dir1/meson.build b/test cases/common/160 duplicate source names/dir1/meson.build new file mode 100644 index 0000000..00bc85d --- /dev/null +++ b/test cases/common/160 duplicate source names/dir1/meson.build @@ -0,0 +1 @@ +sources += files('file.c') diff --git a/test cases/common/160 duplicate source names/dir2/dir1/file.c b/test cases/common/160 duplicate source names/dir2/dir1/file.c new file mode 100644 index 0000000..5aac8e5 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir2/dir1/file.c @@ -0,0 +1 @@ +int dir2_dir1 = 21; diff --git a/test cases/common/160 duplicate source names/dir2/file.c b/test cases/common/160 duplicate source names/dir2/file.c new file mode 100644 index 0000000..6cf8d66 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir2/file.c @@ -0,0 +1 @@ +int dir2 = 20; diff --git a/test cases/common/160 duplicate source names/dir2/meson.build b/test cases/common/160 duplicate source names/dir2/meson.build new file mode 100644 index 0000000..f116a02 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir2/meson.build @@ -0,0 +1 @@ +sources += files('file.c', 'dir1/file.c') diff --git a/test cases/common/160 duplicate source names/dir3/dir1/file.c b/test cases/common/160 duplicate source names/dir3/dir1/file.c new file mode 100644 index 0000000..04667c2 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir3/dir1/file.c @@ -0,0 +1 @@ +int dir3_dir1 = 31; diff --git a/test cases/common/160 duplicate source names/dir3/file.c b/test cases/common/160 duplicate source names/dir3/file.c new file mode 100644 index 0000000..d16d0a8 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir3/file.c @@ -0,0 +1 @@ +int dir3 = 30; diff --git a/test cases/common/160 duplicate source names/dir3/meson.build b/test cases/common/160 duplicate source names/dir3/meson.build new file mode 100644 index 0000000..70ddbf2 --- /dev/null +++ b/test cases/common/160 duplicate source names/dir3/meson.build @@ -0,0 +1 @@ +lib = static_library('lib', 'file.c', 'dir1/file.c') diff --git a/test cases/common/160 duplicate source names/meson.build b/test cases/common/160 duplicate source names/meson.build new file mode 100644 index 0000000..cac5194 --- /dev/null +++ b/test cases/common/160 duplicate source names/meson.build @@ -0,0 +1,7 @@ +project('proj', 'c') + +sources = [] +subdir('dir1') +subdir('dir2') +subdir('dir3') +executable('a.out', sources : sources, objects : lib.extract_all_objects()) diff --git a/test cases/common/47 options/meson.build b/test cases/common/47 options/meson.build index 4058748..2a764f0 100644 --- a/test cases/common/47 options/meson.build +++ b/test cases/common/47 options/meson.build @@ -12,6 +12,7 @@ if get_option('combo_opt') != 'combo' error('Incorrect value to combo option.') endif +# If the default changes, update test cases/unit/13 reconfigure if get_option('b_lto') != false error('Incorrect value in base option.') endif diff --git a/test cases/common/98 gen extra/meson.build b/test cases/common/98 gen extra/meson.build index 52ed847..772f52e 100644 --- a/test cases/common/98 gen extra/meson.build +++ b/test cases/common/98 gen extra/meson.build @@ -11,3 +11,20 @@ g2 = gen.process('name.dat', extra_args: '--upper') test('basic', executable('basic', 'plain.c', g1)) test('upper', executable('upper', 'upper.c', g2)) + +prog2 = find_program('srcgen2.py') +basename_gen = generator(prog2, + output : ['@BASENAME@.tab.c', '@BASENAME@.tab.h'], + arguments : ['@BUILD_DIR@', '@BASENAME@', '@INPUT@']) + +basename_src = basename_gen.process('name.l') + +test('basename', executable('basename', basename_src)) + +plainname_gen = generator(prog2, + output : ['@PLAINNAME@.tab.c', '@PLAINNAME@.tab.h'], + arguments : ['@BUILD_DIR@', '@PLAINNAME@', '@INPUT@']) + +plainname_src = plainname_gen.process('name.l') + +test('plainname', executable('plainname', plainname_src)) diff --git a/test cases/common/98 gen extra/name.l b/test cases/common/98 gen extra/name.l new file mode 100644 index 0000000..3adda4f --- /dev/null +++ b/test cases/common/98 gen extra/name.l @@ -0,0 +1,3 @@ +int main() { +return 0; +} diff --git a/test cases/common/98 gen extra/srcgen2.py b/test cases/common/98 gen extra/srcgen2.py new file mode 100644 index 0000000..9cdf12d --- /dev/null +++ b/test cases/common/98 gen extra/srcgen2.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 + +import os +import sys +import argparse + +parser = argparse.ArgumentParser() +parser.add_argument('target_dir', + help='the target dir') +parser.add_argument('stem', + help='the stem') +parser.add_argument('input', + help='the input file') + +options = parser.parse_args(sys.argv[1:]) + +with open(options.input) as f: + content = f.read() + + +output_c = os.path.join(options.target_dir, options.stem + ".tab.c") +with open(output_c, 'w') as f: + f.write(content) + + +output_h = os.path.join(options.target_dir, options.stem + ".tab.h") +h_content = '''#pragma once + +int myfun(void); +''' +with open(output_h, 'w') as f: + f.write(h_content) diff --git a/test cases/d/9 features/app.d b/test cases/d/9 features/app.d new file mode 100644 index 0000000..37cc1dd --- /dev/null +++ b/test cases/d/9 features/app.d @@ -0,0 +1,51 @@ + +import std.stdio; +import std.array : split; +import std.string : strip; + +auto getMenu () +{ + auto foods = import ("food.txt").strip.split ("\n"); + return foods; +} + +auto getPeople () +{ + return import ("people.txt").strip.split ("\n"); +} + +void main (string[] args) +{ + import std.array : join; + import core.stdc.stdlib : exit; + + immutable request = args[1]; + if (request == "menu") { + version (No_Menu) { + } else { + writeln ("On the menu: ", getMenu.join (", ")); + exit (0); + } + } + + version (With_People) { + if (request == "people") { + writeln ("People: ", getPeople.join (", ")); + exit (0); + } + } + + // we fail here + exit (1); +} + +unittest +{ + writeln ("TEST"); + import core.stdc.stdlib : exit; + + writeln(getMenu); + assert (getMenu () == ["Spam", "Eggs", "Spam", "Baked Beans", "Spam", "Spam"]); + + exit (0); +} diff --git a/test cases/d/9 features/data/food.txt b/test cases/d/9 features/data/food.txt new file mode 100644 index 0000000..8275dd0 --- /dev/null +++ b/test cases/d/9 features/data/food.txt @@ -0,0 +1,6 @@ +Spam +Eggs +Spam +Baked Beans +Spam +Spam diff --git a/test cases/d/9 features/data/people.txt b/test cases/d/9 features/data/people.txt new file mode 100644 index 0000000..abbae06 --- /dev/null +++ b/test cases/d/9 features/data/people.txt @@ -0,0 +1,5 @@ +Rick +Morty +Summer +Beth +Jerry diff --git a/test cases/d/9 features/meson.build b/test cases/d/9 features/meson.build new file mode 100644 index 0000000..9e63710 --- /dev/null +++ b/test cases/d/9 features/meson.build @@ -0,0 +1,29 @@ +project('D Features', 'd') + +# directory for data +data_dir = join_paths(meson.current_source_dir(), 'data') + +e_plain = executable('dapp_menu', + 'app.d', + d_import_dirs: [data_dir] +) +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', + d_import_dirs: [data_dir], + d_module_versions: ['No_Menu', 'With_People'] +) +test('dapp_versions_t_fail', e_versions, args: ['menu'], should_fail: true) +test('dapp_versions_t', e_versions, args: ['people']) + +# test everything and unittests +e_test = executable('dapp_test', + 'app.d', + d_import_dirs: [data_dir], + d_module_versions: ['No_Menu', 'With_People'], + d_unittest: true +) +test('dapp_test', e_test) diff --git a/test cases/frameworks/13 yelp/help/LINGUAS b/test cases/frameworks/13 yelp/help/LINGUAS new file mode 100644 index 0000000..173f978 --- /dev/null +++ b/test cases/frameworks/13 yelp/help/LINGUAS @@ -0,0 +1,2 @@ +de +es diff --git a/test cases/frameworks/13 yelp/help/meson.build b/test cases/frameworks/13 yelp/help/meson.build index 85bc980..c8edd61 100644 --- a/test cases/frameworks/13 yelp/help/meson.build +++ b/test cases/frameworks/13 yelp/help/meson.build @@ -13,3 +13,9 @@ gnome.yelp('meson-symlink', symlink_media: true, languages: ['de', 'es'], ) + +gnome.yelp('meson-linguas', + sources: 'index.page', + media: 'media/test.txt', + symlink_media: false, +) diff --git a/test cases/frameworks/13 yelp/installed_files.txt b/test cases/frameworks/13 yelp/installed_files.txt index 9fc097d..1f6522f 100644 --- a/test cases/frameworks/13 yelp/installed_files.txt +++ b/test cases/frameworks/13 yelp/installed_files.txt @@ -10,3 +10,9 @@ usr/share/help/es/meson-symlink/media/test.txt usr/share/help/es/meson-symlink/index.page usr/share/help/de/meson-symlink/index.page usr/share/help/de/meson-symlink/media/test.txt +usr/share/help/C/meson-linguas/index.page +usr/share/help/C/meson-linguas/media/test.txt +usr/share/help/es/meson-linguas/media/test.txt +usr/share/help/es/meson-linguas/index.page +usr/share/help/de/meson-linguas/index.page +usr/share/help/de/meson-linguas/media/test.txt diff --git a/test cases/frameworks/7 gnome/gdbus/meson.build b/test cases/frameworks/7 gnome/gdbus/meson.build index f8a8eba..ea91caa 100644 --- a/test cases/frameworks/7 gnome/gdbus/meson.build +++ b/test cases/frameworks/7 gnome/gdbus/meson.build @@ -3,7 +3,8 @@ gdbus_src = gnome.gdbus_codegen('generated-gdbus', 'com.example.Sample.xml', namespace : 'Sample', annotations : [ ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'] - ] + ], + docbook : 'generated-gdbus-doc' ) gdbus_exe = executable('gdbus-test', 'gdbusprog.c', diff --git a/test cases/unit/13 reconfigure/meson.build b/test cases/unit/13 reconfigure/meson.build new file mode 100644 index 0000000..102180e --- /dev/null +++ b/test cases/unit/13 reconfigure/meson.build @@ -0,0 +1,5 @@ +project('reconfigure test', ['c']) + +if get_option('b_lto') != true + error('b_lto not set') +endif diff --git a/test cases/vala/12 custom output/bar.vala b/test cases/vala/12 custom output/bar.vala new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test cases/vala/12 custom output/bar.vala diff --git a/test cases/vala/12 custom output/meson.build b/test cases/vala/12 custom output/meson.build index ef6dbb5..c328959 100644 --- a/test cases/vala/12 custom output/meson.build +++ b/test cases/vala/12 custom output/meson.build @@ -3,7 +3,11 @@ project('valatest', 'c', 'vala') glib = dependency('glib-2.0') gobject = dependency('gobject-2.0') -library('foo-1.0', 'foo.vala', - vala_header: 'foo.h', - vala_vapi: 'foo.vapi', +foo_lib = library('foo-1.0', 'foo.vala', + vala_header: 'foo.h', + vala_vapi: 'foo.vapi', + dependencies: [glib, gobject]) + +library('bar', 'bar.vala', + link_with: [foo_lib], dependencies: [glib, gobject]) diff --git a/tools/cmake2meson.py b/tools/cmake2meson.py index 0789a0c..5fe433c 100755 --- a/tools/cmake2meson.py +++ b/tools/cmake2meson.py @@ -26,7 +26,7 @@ class Token: class Statement: def __init__(self, name, args): - self.name = name + self.name = name.lower() self.args = args class Lexer: @@ -120,7 +120,10 @@ class Parser: args.append(self.arguments()) self.expect('rparen') arg = self.current - if self.accept('string') \ + if self.accept('comment'): + rest = self.arguments() + args += rest + elif self.accept('string') \ or self.accept('varexp') \ or self.accept('id'): args.append(arg) @@ -155,7 +158,7 @@ class Converter: if i.tid == 'id': res.append("'%s'" % i.value) elif i.tid == 'varexp': - res.append('%s' % i.value) + res.append('%s' % i.value.lower()) elif i.tid == 'string': res.append("'%s'" % i.value) else: |