diff options
144 files changed, 2236 insertions, 388 deletions
diff --git a/cross/armcc.txt b/cross/armcc.txt new file mode 100644 index 0000000..c884ffa --- /dev/null +++ b/cross/armcc.txt @@ -0,0 +1,20 @@ +# This file assumes that path to the arm compiler toolchain is added +# to the environment(PATH) variable, so that Meson can find +# the armcc, armlink and armar while building. +[binaries] +c = 'armcc' +cpp = 'armcc' +ar = 'armar' +strip = 'armar' + +[properties] +# The '--cpu' option with the appropriate target type should be mentioned +# to cross compile c/c++ code with armcc,. +c_args = ['--cpu=Cortex-M0plus'] +cpp_args = ['--cpu=Cortex-M0plus'] + +[host_machine] +system = 'bare metal' # Update with your system name - bare metal/OS. +cpu_family = 'arm' +cpu = 'Cortex-M0+' +endian = 'little' diff --git a/data/shell-completions/zsh/_meson b/data/shell-completions/zsh/_meson index 877d700..481d04c 100644 --- a/data/shell-completions/zsh/_meson +++ b/data/shell-completions/zsh/_meson @@ -31,7 +31,7 @@ local -i ret local __meson_backends="(ninja xcode ${(j. .)${:-vs{,2010,2015,2017}}})" local __meson_build_types="(plain debug debugoptimized minsize release)" -local __meson_wrap_modes="(WrapMode.{default,nofallback,nodownload})" +local __meson_wrap_modes="(WrapMode.{default,nofallback,nodownload,forcefallback})" local -a meson_commands=( 'setup:set up a build directory' diff --git a/data/syntax-highlighting/vim/syntax/meson.vim b/data/syntax-highlighting/vim/syntax/meson.vim index 83dd66a..d58903e 100644 --- a/data/syntax-highlighting/vim/syntax/meson.vim +++ b/data/syntax-highlighting/vim/syntax/meson.vim @@ -70,6 +70,7 @@ syn keyword mesonBuiltin \ add_project_link_arguments \ add_test_setup \ benchmark + \ both_libraries \ build_machine \ build_target \ configuration_data diff --git a/docs/markdown/Adding-new-projects-to-wrapdb.md b/docs/markdown/Adding-new-projects-to-wrapdb.md index 4420de5..58b27ba 100644 --- a/docs/markdown/Adding-new-projects-to-wrapdb.md +++ b/docs/markdown/Adding-new-projects-to-wrapdb.md @@ -37,11 +37,10 @@ Each project gets its own repo. It is initialized like this: git init git add readme.txt - git commit -a -m 'Start of project foobar.' - git tag commit_zero -a -m 'A tag that helps get revision ids for releases.' + git add LICENSE.build + git commit -a -m 'Create project foobar' git remote add origin <repo url> git push -u origin master - git push --tags Note that this is the *only* commit that will ever be made to master branch. All other commits are done to branches. diff --git a/docs/markdown/Compiler-properties.md b/docs/markdown/Compiler-properties.md index 579417a..1228f42 100644 --- a/docs/markdown/Compiler-properties.md +++ b/docs/markdown/Compiler-properties.md @@ -160,15 +160,30 @@ Does a function exist? Just having a header doesn't say anything about its contents. Sometimes you need to explicitly check if some function -exists. This is how we would check whether the function `somefunc` -exists in header `someheader.h` +exists. This is how we would check whether the function `open_memstream` +exists in header `stdio.h` ```meson -if compiler.has_function('somefunc', prefix : '#include<someheader.h>') +if compiler.has_function('open_memstream', prefix : '#include <stdio.h>') # function exists, do whatever is required. endif ``` +Note that, on macOS programs can be compiled targeting older macOS +versions than the one that the program is compiled on. It can't be +assumed that the OS version that is compiled on matches the OS +version that the binary will run on. + +Therefore when detecting function availability with `has_function`, it +is important to specify the correct header in the prefix argument. + +In the example above, the function `open_memstream` is detected, which +was introduced in macOS 10.13. When the user builds on macOS 10.13, but +targeting macOS 10.11 (`-mmacosx-version-min=10.11`), this will correctly +report the function as missing. Without the header however, it would lack +the necessary availability information and incorrectly report the function +as available. + Does a structure contain a member? == diff --git a/docs/markdown/FAQ.md b/docs/markdown/FAQ.md index f4cf89b..ff93216 100644 --- a/docs/markdown/FAQ.md +++ b/docs/markdown/FAQ.md @@ -288,3 +288,46 @@ has a option called `wrap-mode` which can be used to disable wrap downloads altogether with `--wrap-mode=nodownload`. You can also disable dependency fallbacks altogether with `--wrap-mode=nofallback`, which also implies the `nodownload` option. + +If on the other hand, you want meson to always use the fallback +for dependencies, even when an external dependency exists and could +satisfy the version requirements, for example in order to make +sure your project builds when fallbacks are used, you can use +`--wrap-mode=forcefallback` since 0.46.0. + +## Why is Meson implemented in Python rather than [programming language X]? + +Because build systems are special in ways normal applications aren't. + +Perhaps the biggest limitation is that because Meson is used to build +software at the very lowest levels of the OS, it is part of the core +bootstrap for new systems. Whenever support for a new CPU architecture +is added, Meson must run on the system before software using it can be +compiled natively. This requirement adds two hard limitations. + +The first one is that Meson must have the minimal amount of +dependencies, because they must all be built during the bootstrap to +get Meson to work. + +The second is that Meson must support all CPU architectures, both +existing and future ones. As an example many new programming languages +have only an LLVM based compiler available. LLVM has limited CPU +support compared to, say, GCC, and thus bootstrapping Meson on such +platforms would first require adding new processor support to +LLVM. This is in most cases unfeasible. + +A further limitation is that we want developers on as many platforms +as possible to submit to Meson development using the default tools +provided by their operating system. In practice what this means is +that Windows developers should be able to contribute using nothing but +Visual Studio. + +At the time of writing (April 2018) there are only three languages +that could fullfill these requirements: + + - C + - C++ + - Python + +Out of these we have chosen Python because it is the best fit for our +needs. diff --git a/docs/markdown/Gnome-module.md b/docs/markdown/Gnome-module.md index ad3715e..3db6cc0 100644 --- a/docs/markdown/Gnome-module.md +++ b/docs/markdown/Gnome-module.md @@ -235,9 +235,21 @@ files and the second specifies the XML file name. * `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 +* `build_by_default`: causes, when set to true, to have this target be + built by default, that is, when invoking plain `ninja`, the default + value is true for all built target types +* `install_dir`: (*Added 0.46.0*) location to install the header or + bundle depending on previous options +* `install_header`: (*Added 0.46.0*) if true, install the header file + +Starting *0.46.0*, this function returns a list of at least two custom targets +(in order): one for the source code and one for the header. The list will +contain a third custom target for the generated docbook files if that keyword +argument is passed. -Returns an opaque object containing the source files. Add it to a top -level target's source list. +Earlier versions return a single custom target representing all the outputs. +Generally, you should just add this list of targets to a top level target's +source list. Example: diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index d98fc19..fad6a3c 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -186,7 +186,12 @@ These are all the supported keyword arguments: `output`. Available since v0.41.0. - `command` as explained above, if specified, Meson does not create the file itself but rather runs the specified command, which allows - you to do fully custom file generation + you to do fully custom file generation. +- `format` *(added 0.46.0)* the format of defines. It defaults to `meson`, and so substitutes +`#mesondefine` statements and variables surrounded by `@` characters, you can also use `cmake` +to replace `#cmakedefine` statements and variables with the `${variable}` syntax. Finally you can use +`cmake@` in which case substitutions will apply on `#cmakedefine` statements and variables with +the `@variable@` syntax. - `input` the input file name. If it's not specified in configuration mode, all the variables in the `configuration:` object (see above) are written to the `output:` file. @@ -1238,6 +1243,12 @@ Keyword arguments are the following: - `should_fail` when true the test is considered passed if the executable returns a non-zero return value (i.e. reports an error) +- `suite` `'label'` (or list of labels `['label1', 'label2']`) + attached to this test. The suite name is qualified by a (sub)project + name resulting in `(sub)project_name:label`. In the case of a list + of strings, the suite names will be `(sub)project_name:label1`, + `(sub)project_name:label2`, etc. + - `timeout` the amount of seconds the test is allowed to run, a test that exceeds its time limit is always considered failed, defaults to 30 seconds @@ -1385,6 +1396,13 @@ the following methods. /path/to/meson.py introspect`. The user is responsible for splitting the string to an array if needed. +- `override_find_program(progname, program)` [*(Added + 0.46.0)*](Release-notes-for-0-46-0.html#Can-override-find_program) + specifies that whenever `find_program` is used to find a program + named `progname`, Meson should not not look it up on the system but + instead return `program`, which may either be the result of + `find_program` or `configure_file`. + - `project_version()` returns the version string specified in `project` function call. - `project_license()` returns the array of licenses specified in `project` function call. diff --git a/docs/markdown/Reference-tables.md b/docs/markdown/Reference-tables.md index 7611232..2157e72 100644 --- a/docs/markdown/Reference-tables.md +++ b/docs/markdown/Reference-tables.md @@ -21,6 +21,8 @@ These are return values of the `get_id` method in a compiler object. | g95 | The G95 Fortran compiler | | open64 | The Open64 Fortran Compiler | | nagfor | The NAG Fortran compiler | +| lcc | Elbrus C/C++/Fortran Compiler | +| arm | ARM compiler | ## Script environment variables @@ -42,6 +44,7 @@ set in the cross file. | x86 | 32 bit x86 processor | | x86_64 | 64 bit x86 processor | | arm | 32 bit ARM processor | +| e2k | MCST Elbrus processor | Any cpu family not listed in the above list is not guaranteed to remain stable in future releases. diff --git a/docs/markdown/Release-notes-for-0.45.0.md b/docs/markdown/Release-notes-for-0.45.0.md index 6b24183..19d65b8 100644 --- a/docs/markdown/Release-notes-for-0.45.0.md +++ b/docs/markdown/Release-notes-for-0.45.0.md @@ -18,7 +18,7 @@ ldflags, etc) from. These binaries may now be specified in the `binaries` section of a cross file. -```dosini +```ini [binaries] cc = ... llvm-config = '/usr/bin/llvm-config32' @@ -37,12 +37,16 @@ time. Starting with this version it becomes a hard error. There used to be a keywordless version of `run_target` which looked like this: - run_target('targetname', 'command', 'arg1', 'arg2') +```meson +run_target('targetname', 'command', 'arg1', 'arg2') +``` This is now an error. The correct format for this is now: - run_target('targetname', - command : ['command', 'arg1', 'arg2']) +```meson +run_target('targetname', + command : ['command', 'arg1', 'arg2']) +``` ## Experimental FPGA support @@ -84,7 +88,9 @@ private directory: Hexadecimal integer literals can now be used in build and option files. - int_255 = 0xFF +```meson +int_255 = 0xFF +``` ## b_ndebug : if-release @@ -110,7 +116,9 @@ instead of directory itself, stripping basename of the source directory. There is a new integer option type with optional minimum and maximum values. It can be specified like this in the `meson_options.txt` file: - option('integer_option', type : 'integer', min : 0, max : 5, value : 3) +```meson +option('integer_option', type : 'integer', min : 0, max : 5, value : 3) +``` ## New method meson.project_license() @@ -124,7 +132,7 @@ cross-compilers, the Rust binary must be specified in your cross file. It should specify a `--target` (as installed by `rustup target`) and a custom linker pointing to your C cross-compiler. For example: -``` +```ini [binaries] c = '/usr/bin/arm-linux-gnueabihf-gcc-7' rust = [ @@ -146,9 +154,7 @@ private sysroot. Meson ships with predefined project templates. To start a new project from scratch, simply go to an empty directory and type: -```meson -meson init --name=myproject --type=executable --language=c -``` + meson init --name=myproject --type=executable --language=c ## Improve test setup selection diff --git a/docs/markdown/Syntax.md b/docs/markdown/Syntax.md index 1005100..01c8c6e 100644 --- a/docs/markdown/Syntax.md +++ b/docs/markdown/Syntax.md @@ -90,8 +90,24 @@ single quote do it like this: single quote = 'contains a \' character' ``` -Similarly `\n` gets converted to a newline and `\\` to a single -backslash. +The full list of escape sequences is: + +* `\\` Backslash +* `\'` Single quote +* `\a` Bell +* `\b` Backspace +* `\f` Formfeed +* `\n` Newline +* `\r` Carriage Return +* `\t` Horizontal Tab +* `\v` Vertical Tab +* `\ooo` Character with octal value ooo +* `\xhh` Character with hex value hh +* `\uxxxx` Character with 16-bit hex value xxxx +* `\Uxxxxxxxx` Character with 32-bit hex value xxxxxxxx +* `\N{name}` Character named name in Unicode database + +As in python and C, up to three octal digits are accepted in `\ooo`. #### String concatenation diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md index 53ce9ec..e5e4107 100644 --- a/docs/markdown/Unit-tests.md +++ b/docs/markdown/Unit-tests.md @@ -71,6 +71,14 @@ You can also run only a single test by giving its name: $ meson test testname ``` +Tests belonging to a suite `suite` can be run as follows + +```console +$ meson test --suite (sub)project_name:suite +``` + +Since version *0.46*, `(sub)project_name` can be omitted if it is the top-level project. + Sometimes you need to run the tests multiple times, which is done like this: ```console diff --git a/docs/markdown/snippets/armcc-cross.md b/docs/markdown/snippets/armcc-cross.md new file mode 100644 index 0000000..668f0ab --- /dev/null +++ b/docs/markdown/snippets/armcc-cross.md @@ -0,0 +1,15 @@ +## ARM compiler for C and CPP + +Cross-compilation is now supported for ARM targets using ARM compiler - ARMCC. +The current implementation does not support shareable libraries. +The default extension of the output is .axf. +The environment path should be set properly for the ARM compiler executables. +The '--cpu' option with the appropriate target type should be mentioned +in the cross file as shown in the snippet below. + +``` +[properties] +c_args = ['--cpu=Cortex-M0plus'] +cpp_args = ['--cpu=Cortex-M0plus'] + +``` diff --git a/docs/markdown/snippets/del-old-names.md b/docs/markdown/snippets/del-old-names.md index c4abc9a..5ac5873 100644 --- a/docs/markdown/snippets/del-old-names.md +++ b/docs/markdown/snippets/del-old-names.md @@ -2,6 +2,6 @@ Old executable names `mesonintrospect`, `mesonconf`, `mesonrewriter` and `mesontest` have been deprecated for a long time. Starting from -this versino they no longer do anything but instead always error +this version they no longer do anything but instead always error out. All functionality is available as subcommands in the main `meson` binary. diff --git a/docs/markdown/snippets/find-override.md b/docs/markdown/snippets/find-override.md new file mode 100644 index 0000000..ef3a4a2 --- /dev/null +++ b/docs/markdown/snippets/find-override.md @@ -0,0 +1,37 @@ +## Can override find_program + +It is now possible to override the result of `find_program` to point +to a custom program you want. The overriding is global and applies to +every subproject from there on. Here is how you would use it. + +In master project + +```meson +subproject('mydep') +``` + +In the called subproject: + +```meson +prog = find_program('my_custom_script') +meson.override_find_program('mycodegen', prog) +``` + +In master project (or, in fact, any subproject): + +```meson +genprog = find_program('mycodegen') +``` + +Now `genprog` points to the custom script. If the dependency had come +from the system, then it would point to the system version. + +You can also use the return value of `configure_file()` to override +a program in the same way as above: + +```meson +prog_script = configure_file(input : 'script.sh.in', + output : 'script.sh', + configuration : cdata) +meson.override_find_program('mycodegen', prog_script) +``` diff --git a/docs/markdown/snippets/lcc.md b/docs/markdown/snippets/lcc.md new file mode 100644 index 0000000..2ce300d --- /dev/null +++ b/docs/markdown/snippets/lcc.md @@ -0,0 +1,23 @@ +## Support for lcc compiler for e2k (Elbrus) architecture + +In this version, a support for lcc compiler for Elbrus processors +based on [e2k microarchitecture](https://en.wikipedia.org/wiki/Elbrus_2000) +has been added. + +Examples of such CPUs: +* [Elbrus-8S](https://en.wikipedia.org/wiki/Elbrus-8S); +* Elbrus-4S; +* [Elbrus-2S+](https://en.wikipedia.org/wiki/Elbrus-2S%2B). + +Such compiler have a similar behavior as gcc (basic option compatibility), +but, in is not strictly compatible with gcc as of current version. + +Major differences as of version 1.21.22: +* it does not support LTO and PCH; +* it suffers from the same dependency file creation error as icc; +* it has minor differences in output, especially version output; +* it differently reacts to lchmod() detection; +* some backend messages are produced in ru_RU.KOI8-R even if LANG=C; +* its preprocessor treats some characters differently. + +So every noted difference is properly handled now in meson.
\ No newline at end of file diff --git a/docs/markdown/snippets/more-escape-sequences.md b/docs/markdown/snippets/more-escape-sequences.md new file mode 100644 index 0000000..2894079 --- /dev/null +++ b/docs/markdown/snippets/more-escape-sequences.md @@ -0,0 +1,17 @@ +## String escape character update + +The strings (both single-quoted and triple-quoted) in meson has been taught the +same set of escape sequences as in Python. It is therefore now possible to use +arbitrary bytes in strings, like for example NUL (`\0`) and other ASCII control +characters. See the chapter about *Strings* in *Syntax* for more details. + +Potential backwards compatibility issue: Any valid escape sequence according to +the new rules will be interpreted as an escape sequence instead of the literal +characters. Previously only single-quote strings supported escape sequences and +the supported sequences were `\'`, `\\` and `\n`. + +The most likely breakage is usage of backslash-n in triple-quoted strings. It +is now written in the same way as in single-quoted strings: `\\n` instead of +`\n`. In general it is now recommended to escape any usage of backslash. +However, backslash-c (`\c`), for example, is still backslash-c because it isn't +a valid escape sequence. diff --git a/docs/markdown/snippets/new-wrap-mode.md b/docs/markdown/snippets/new-wrap-mode.md new file mode 100644 index 0000000..e33dd83 --- /dev/null +++ b/docs/markdown/snippets/new-wrap-mode.md @@ -0,0 +1,3 @@ +A new wrap mode was added, `--wrap-mode=forcefallback`. When this is set, +dependencies for which a fallback was provided will always use it, even +if an external dependency exists and satisfies the version requirements. diff --git a/docs/markdown/snippets/non-unique-target-names.md b/docs/markdown/snippets/non-unique-target-names.md new file mode 100644 index 0000000..9b3f917 --- /dev/null +++ b/docs/markdown/snippets/non-unique-target-names.md @@ -0,0 +1,9 @@ +## Relaxing of target name requirements + +In earlier versions of Meson you could only have one target of a given name for each type. +For example you could not have two executables named `foo`. This requirement is now +relaxed so that you can have multiple targets with the same name, as long as they are in +different subdirectories. + +Note that projects that have multiple targets with the same name can not be built with +the `flat` layout or any backend that writes outputs in the same directory. diff --git a/docs/markdown/snippets/openmp-dependency.md b/docs/markdown/snippets/openmp-dependency.md new file mode 100644 index 0000000..ad70011 --- /dev/null +++ b/docs/markdown/snippets/openmp-dependency.md @@ -0,0 +1,6 @@ +## Addition of OpenMP dependency + +An OpenMP dependency (`openmp`) has been added that encapsulates the various +flags used by compilers to enable OpenMP and checks for the existence of the +`omp.h` header. The `language` keyword may be passed to force the use of a +specific compiler for the checks. diff --git a/docs/markdown/snippets/pkg-config-fix-static-only.md b/docs/markdown/snippets/pkg-config-fix-static-only.md new file mode 100644 index 0000000..31cd389 --- /dev/null +++ b/docs/markdown/snippets/pkg-config-fix-static-only.md @@ -0,0 +1,12 @@ +## Improved generation of pkg-config files for static only libraries. + +Previously pkg-config files generated by the pkgconfig modules for static libraries +with dependencies could only be used in a dependencies with `static: true`. + +Now the generated file contains the needed dependencies libraries directly within +`Requires` and `Libs` for build static libraries passed via the `libraries` keyword +argument. + +Projects that install both a static and a shared version of a library should use +the result of `both_libraries` to the pkg config file generator or use +configure_file for more complicated setups. diff --git a/manual tests/2 multiwrap/meson.build b/manual tests/2 multiwrap/meson.build index 741a899..a4c42f4 100644 --- a/manual tests/2 multiwrap/meson.build +++ b/manual tests/2 multiwrap/meson.build @@ -6,7 +6,7 @@ project('multiwrap', 'c', cc = meson.get_compiler('c') luadep = dependency('lua', fallback : ['lua', 'lua_dep']) -pngdep = dependency('libpng', fallback : ['libpng', 'pngdep']) +pngdep = dependency('libpng', fallback : ['libpng', 'png_dep']) executable('prog', 'prog.c', dependencies : [pngdep, luadep]) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 916f680..694700e 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -540,6 +540,8 @@ class Backend: # pkg-config puts the thread flags itself via `Cflags:` if dep.need_threads(): commands += compiler.thread_flags(self.environment) + elif dep.need_openmp(): + commands += compiler.openmp_flags() # Fortran requires extra include directives. if compiler.language == 'fortran': for lt in target.link_targets: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cee1434..bc3a8ef 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -169,7 +169,7 @@ class NinjaBackend(backends.Backend): break else: # None of our compilers are MSVC, we're done. - return open(tempfilename, 'a') + return open(tempfilename, 'a', encoding='utf-8') filename = os.path.join(self.environment.get_scratch_dir(), 'incdetect.c') with open(filename, 'w') as f: @@ -196,7 +196,7 @@ int dummy; if match: with open(tempfilename, 'ab') as binfile: binfile.write(b'msvc_deps_prefix = ' + match.group(1) + b'\n') - return open(tempfilename, 'a') + return open(tempfilename, 'a', encoding='utf-8') raise MesonException('Could not determine vs dep dependency prefix string.') def generate(self, interp): @@ -206,7 +206,7 @@ int dummy; 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: + with open(tempfilename, 'w', encoding='utf-8') as outfile: outfile.write('# This is the build file for project "%s"\n' % self.build.get_project()) outfile.write('# It is autogenerated by the Meson build system.\n') @@ -1156,7 +1156,7 @@ int dummy; abs_vala_file = os.path.join(self.environment.get_build_dir(), vala_file) if PurePath(os.path.commonpath((abs_srcbasedir, abs_vala_file))) == PurePath(abs_srcbasedir): vala_c_subdir = PurePath(abs_vala_file).parent.relative_to(abs_srcbasedir) - vala_c_file = os.path.join(vala_c_subdir, vala_c_file) + vala_c_file = os.path.join(str(vala_c_subdir), vala_c_file) else: path_to_target = os.path.join(self.build_to_src, target.get_subdir()) if vala_file.startswith(path_to_target): @@ -1473,7 +1473,7 @@ int dummy; # gcc-ar blindly pass the --plugin argument to `ar` and you cannot pass # options as arguments while using the @file.rsp syntax. # See: https://github.com/mesonbuild/meson/issues/1646 - if mesonlib.is_windows() and not isinstance(static_linker, ArLinker): + if static_linker.can_linker_accept_rsp(): command_template = ''' command = {executable} @$out.rsp rspfile = $out.rsp rspfile_content = $LINK_ARGS {output_args} $in @@ -1528,7 +1528,7 @@ int dummy; except KeyError: pass rule = 'rule %s%s_LINKER\n' % (langname, crstr) - if mesonlib.is_windows(): + if compiler.can_linker_accept_rsp(): command_template = ''' command = {executable} @$out.rsp rspfile = $out.rsp rspfile_content = $ARGS {output_args} $in $LINK_ARGS {cross_args} $aliasing @@ -1657,12 +1657,12 @@ rule FORTRAN_DEP_HACK if getattr(self, 'created_llvm_ir_rule', False): return rule = 'rule llvm_ir{}_COMPILER\n'.format('_CROSS' if is_cross else '') - if mesonlib.is_windows(): + if compiler.can_linker_accept_rsp(): command_template = ' command = {executable} @$out.rsp\n' \ ' rspfile = $out.rsp\n' \ - ' rspfile_content = {cross_args} $ARGS {output_args} {compile_only_args} $in\n' + ' rspfile_content = $ARGS{cross_args} {output_args} {compile_only_args} $in\n' else: - command_template = ' command = {executable} {cross_args} $ARGS {output_args} {compile_only_args} $in\n' + command_template = ' command = {executable} $ARGS {cross_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), cross_args=' '.join(self.get_cross_info_lang_args(compiler.language, is_cross)), @@ -1718,13 +1718,13 @@ rule FORTRAN_DEP_HACK d = quote_func(d) quoted_depargs.append(d) cross_args = self.get_cross_info_lang_args(langname, is_cross) - if mesonlib.is_windows(): + if compiler.can_linker_accept_rsp(): command_template = ''' command = {executable} @$out.rsp rspfile = $out.rsp - rspfile_content = {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in + rspfile_content = $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in ''' else: - command_template = ' command = {executable} {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in\n' + command_template = ' command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n' command = command_template.format( executable=' '.join([ninja_quote(i) for i in compiler.get_exelist()]), cross_args=' '.join(cross_args), @@ -1769,7 +1769,7 @@ rule FORTRAN_DEP_HACK output = '' else: output = ' '.join(compiler.get_output_args('$out')) - command = " command = {executable} {cross_args} $ARGS {dep_args} {output_args} {compile_only_args} $in\n".format( + command = " command = {executable} $ARGS {cross_args} {dep_args} {output_args} {compile_only_args} $in\n".format( executable=' '.join(compiler.get_exelist()), cross_args=' '.join(cross_args), dep_args=' '.join(quoted_depargs), @@ -2392,7 +2392,7 @@ rule FORTRAN_DEP_HACK commands += linker.get_pic_args() # Add -Wl,-soname arguments on Linux, -install_name on OS X commands += linker.get_soname_args(target.prefix, target.name, target.suffix, - abspath, target.soversion, + abspath, target.soversion, target.ltversion, isinstance(target, build.SharedModule)) # This is only visited when building for Windows using either GCC or Visual Studio if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'): @@ -2410,6 +2410,74 @@ rule FORTRAN_DEP_HACK target_args = self.build_target_link_arguments(linker, target.link_whole_targets) return linker.get_link_whole_for(target_args) if len(target_args) else [] + def guess_library_absolute_path(self, libname, search_dirs, prefixes, suffixes): + for directory in search_dirs: + for suffix in suffixes: + for prefix in prefixes: + trial = os.path.join(directory, prefix + libname + '.' + suffix) + if os.path.isfile(trial): + return trial + + def guess_external_link_dependencies(self, linker, target, commands, internal): + # Ideally the linker would generate dependency information that could be used. + # But that has 2 problems: + # * currently ld can not create dependency information in a way that ninja can use: + # https://sourceware.org/bugzilla/show_bug.cgi?id=22843 + # * Meson optimizes libraries from the same build using the symbol extractor. + # Just letting ninja use ld generated dependencies would undo this optimization. + search_dirs = [] + libs = [] + absolute_libs = [] + + build_dir = self.environment.get_build_dir() + # the following loop sometimes consumes two items from command in one pass + it = iter(commands) + for item in it: + if item in internal and not item.startswith('-'): + continue + + if item.startswith('-L'): + if len(item) > 2: + path = item[2:] + else: + try: + path = next(it) + except StopIteration: + mlog.warning("Generated linker command has -L argument without following path") + break + if not os.path.isabs(path): + path = os.path.join(build_dir, path) + search_dirs.append(path) + elif item.startswith('-l'): + if len(item) > 2: + libs.append(item[2:]) + else: + try: + libs.append(next(it)) + except StopIteration: + mlog.warning("Generated linker command has '-l' argument without following library name") + break + elif os.path.isabs(item) and self.environment.is_library(item) and os.path.isfile(item): + absolute_libs.append(item) + + guessed_dependencies = [] + # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker + if hasattr(linker, 'get_library_naming'): + search_dirs += linker.get_library_dirs() + prefixes_static, suffixes_static = linker.get_library_naming(self.environment, 'static', strict=True) + prefixes_shared, suffixes_shared = linker.get_library_naming(self.environment, 'shared', strict=True) + for libname in libs: + # be conservative and record most likely shared and static resolution, because we don't know exactly + # which one the linker will prefer + static_resolution = self.guess_library_absolute_path(libname, search_dirs, prefixes_static, suffixes_static) + shared_resolution = self.guess_library_absolute_path(libname, search_dirs, prefixes_shared, suffixes_shared) + if static_resolution: + guessed_dependencies.append(os.path.realpath(static_resolution)) + if shared_resolution: + guessed_dependencies.append(os.path.realpath(shared_resolution)) + + return guessed_dependencies + absolute_libs + def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): if isinstance(target, build.StaticLibrary): linker_base = 'STATIC' @@ -2476,12 +2544,15 @@ rule FORTRAN_DEP_HACK dependencies = [] else: dependencies = target.get_dependencies() - commands += self.build_target_link_arguments(linker, dependencies) + internal = self.build_target_link_arguments(linker, dependencies) + commands += internal # For 'automagic' deps: Boost and GTest. Also dependency('threads'). # pkg-config puts the thread flags itself via `Cflags:` for d in target.external_deps: if d.need_threads(): commands += linker.thread_link_flags(self.environment) + elif d.need_openmp(): + commands += linker.openmp_flags() # Only non-static built targets need link args and link dependencies if not isinstance(target, build.StaticLibrary): commands += target.link_args @@ -2500,6 +2571,10 @@ rule FORTRAN_DEP_HACK # symbols from those can be found here. This is needed when the # *_winlibs that we want to link to are static mingw64 libraries. commands += linker.get_option_link_args(self.environment.coredata.compiler_options) + + dep_targets = [] + dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal)) + # Set runtime-paths so we can run executables without needing to set # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. if has_path_sep(target.name): @@ -2523,7 +2598,7 @@ rule FORTRAN_DEP_HACK # Convert from GCC-style link argument naming to the naming used by the # current compiler. commands = commands.to_native() - dep_targets = [self.get_dependency_filename(t) for t in dependencies] + dep_targets.extend([self.get_dependency_filename(t) for t in dependencies]) dep_targets.extend([self.get_dependency_filename(t) for t in target.link_depends]) elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 5e972f2..22383dc 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -227,7 +227,7 @@ class Vs2010Backend(backends.Backend): def generate_solution(self, sln_filename, projlist): default_projlist = self.get_build_by_default_targets() - with open(sln_filename, 'w') as ofile: + with open(sln_filename, 'w', encoding='utf-8') as ofile: ofile.write('Microsoft Visual Studio Solution File, Format ' 'Version 11.00\n') ofile.write('# Visual Studio ' + self.vs_version + '\n') @@ -575,7 +575,7 @@ class Vs2010Backend(backends.Backend): tree.write(ofname, encoding='utf-8', xml_declaration=True) # ElementTree can not do prettyprinting so do it manually doc = xml.dom.minidom.parse(ofname) - with open(ofname, 'w') as of: + with open(ofname, 'w', encoding='utf-8') as of: of.write(doc.toprettyxml()) def gen_vcxproj(self, target, ofname, guid): @@ -824,7 +824,10 @@ class Vs2010Backend(backends.Backend): for d in reversed(target.get_external_deps()): # Cflags required by external deps might have UNIX-specific flags, # so filter them out if needed - d_compile_args = compiler.unix_args_to_native(d.get_compile_args()) + if isinstance(d, dependencies.OpenMPDependency): + d_compile_args = compiler.openmp_flags() + else: + d_compile_args = compiler.unix_args_to_native(d.get_compile_args()) for arg in d_compile_args: if arg.startswith(('-D', '/D')): define = arg[2:] @@ -915,11 +918,17 @@ class Vs2010Backend(backends.Backend): for dep in target.get_external_deps(): # Extend without reordering or de-dup to preserve `-L -l` sets # https://github.com/mesonbuild/meson/issues/1718 - extra_link_args.extend_direct(dep.get_link_args()) + if isinstance(dep, dependencies.OpenMPDependency): + extra_link_args.extend_direct(compiler.openmp_flags()) + else: + extra_link_args.extend_direct(dep.get_link_args()) for d in target.get_dependencies(): if isinstance(d, build.StaticLibrary): for dep in d.get_external_deps(): - extra_link_args.extend_direct(dep.get_link_args()) + if isinstance(dep, dependencies.OpenMPDependency): + extra_link_args.extend_direct(compiler.openmp_flags()) + else: + extra_link_args.extend_direct(dep.get_link_args()) # Add link args for c_* or cpp_* build options. Currently this only # adds c_winlibs and cpp_winlibs when building for Windows. This needs # to be after all internal and external libraries so that unresolved @@ -946,7 +955,8 @@ class Vs2010Backend(backends.Backend): self.add_project_reference(root, tvcxproj, tid) else: # Other libraries go into AdditionalDependencies - additional_links.append(linkname) + if linkname not in additional_links: + additional_links.append(linkname) for lib in self.get_custom_target_provided_libraries(target): additional_links.append(self.relpath(lib, self.get_target_dir(target))) additional_objects = [] @@ -1118,7 +1128,7 @@ if %%errorlevel%% neq 0 goto :VCEnd''' igroup = ET.SubElement(root, 'ItemGroup') rulefile = os.path.join(self.environment.get_scratch_dir(), 'regen.rule') if not os.path.exists(rulefile): - with open(rulefile, 'w') as f: + with open(rulefile, 'w', encoding='utf-8') as f: f.write("# Meson regen file.") custombuild = ET.SubElement(igroup, 'CustomBuild', Include=rulefile) message = ET.SubElement(custombuild, 'Message') diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 08e0c9d..e707053 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -121,6 +121,8 @@ class Build: self.dep_manifest = {} self.cross_stdlibs = {} self.test_setups = {} + self.find_overrides = {} + self.searched_programs = set() # The list of all programs that have been searched for. def add_compiler(self, compiler): if self.static_linker is None and compiler.needs_static_linker(): @@ -308,11 +310,16 @@ a hard error in the future.''' % name) def get_id(self): # This ID must also be a valid file name on all OSs. # It should also avoid shell metacharacters for obvious - # reasons. - base = self.name + self.type_suffix() - if self.subproject == '': - return base - return self.subproject + '@@' + base + # reasons. '@' is not used as often as '_' in source code names. + # In case of collisions consider using checksums. + # FIXME replace with assert when slash in names is prohibited + name_part = self.name.replace('/', '@').replace('\\', '@') + assert not has_path_sep(self.type_suffix()) + myid = name_part + self.type_suffix() + if self.subdir: + subdir_part = self.subdir.replace('/', '@').replace('\\', '@') + myid = subdir_part + '@@' + myid + return myid def process_kwargs(self, kwargs): if 'build_by_default' in kwargs: @@ -803,12 +810,16 @@ This will become a hard error in a future Meson release.''') def get_extra_args(self, language): return self.extra_args.get(language, []) - def get_dependencies(self): + def get_dependencies(self, exclude=None): transitive_deps = [] + if exclude is None: + exclude = [] for t in itertools.chain(self.link_targets, self.link_whole_targets): + if t in transitive_deps or t in exclude: + continue transitive_deps.append(t) if isinstance(t, StaticLibrary): - transitive_deps += t.get_dependencies() + transitive_deps += t.get_dependencies(transitive_deps + exclude) return transitive_deps def get_source_subdir(self): @@ -851,13 +862,14 @@ This will become a hard error in a future Meson release.''') self.link(l) for l in dep.whole_libraries: self.link_whole(l) - # Those parts that are external. - extpart = dependencies.InternalDependency('undefined', - [], - dep.compile_args, - dep.link_args, - [], [], [], []) - self.external_deps.append(extpart) + if dep.compile_args or dep.link_args: + # Those parts that are external. + extpart = dependencies.InternalDependency('undefined', + [], + dep.compile_args, + dep.link_args, + [], [], [], []) + self.external_deps.append(extpart) # Deps of deps. self.add_deps(dep.ext_deps) elif isinstance(dep, dependencies.Dependency): @@ -1202,7 +1214,11 @@ class Executable(BuildTarget): for_cygwin(is_cross, environment) or 'cs' in self.compilers): self.suffix = 'exe' else: - self.suffix = '' + if ('c' in self.compilers and self.compilers['c'].get_id().startswith('arm') or + 'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('arm')): + self.suffix = 'axf' + else: + self.suffix = '' self.filename = self.name if self.suffix: self.filename += '.' + self.suffix diff --git a/mesonbuild/compilers/__init__.py b/mesonbuild/compilers/__init__.py index 84c87fb..288b3f5 100644 --- a/mesonbuild/compilers/__init__.py +++ b/mesonbuild/compilers/__init__.py @@ -54,10 +54,13 @@ __all__ = [ 'FortranCompiler', 'G95FortranCompiler', 'GnuCCompiler', + 'ElbrusCCompiler', 'GnuCompiler', 'GnuCPPCompiler', + 'ElbrusCPPCompiler', 'GnuDCompiler', 'GnuFortranCompiler', + 'ElbrusFortranCompiler', 'GnuObjCCompiler', 'GnuObjCPPCompiler', 'IntelCompiler', @@ -115,16 +118,20 @@ from .compilers import ( IntelCompiler, ) from .c import ( + ArmCCompiler, CCompiler, ClangCCompiler, GnuCCompiler, + ElbrusCCompiler, IntelCCompiler, VisualStudioCCompiler, ) from .cpp import ( + ArmCPPCompiler, CPPCompiler, ClangCPPCompiler, GnuCPPCompiler, + ElbrusCPPCompiler, IntelCPPCompiler, VisualStudioCPPCompiler, ) @@ -139,6 +146,7 @@ from .fortran import ( FortranCompiler, G95FortranCompiler, GnuFortranCompiler, + ElbrusFortranCompiler, IntelFortranCompiler, NAGFortranCompiler, Open64FortranCompiler, diff --git a/mesonbuild/compilers/c.py b/mesonbuild/compilers/c.py index 56b46b4..0e474e7 100644 --- a/mesonbuild/compilers/c.py +++ b/mesonbuild/compilers/c.py @@ -31,11 +31,13 @@ from .compilers import ( msvc_winlibs, vs32_instruction_set_args, vs64_instruction_set_args, + ArmCompiler, ClangCompiler, Compiler, CompilerArgs, CrossNoRunException, GnuCompiler, + ElbrusCompiler, IntelCompiler, RunResult, ) @@ -84,7 +86,7 @@ class CCompiler(Compiler): # Almost every compiler uses this for disabling warnings return ['-w'] - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): return [] def split_shlib_to_parts(self, fname): @@ -294,6 +296,8 @@ class CCompiler(Compiler): args += d.get_compile_args() if d.need_threads(): args += self.thread_flags(env) + elif d.need_openmp(): + args += self.openmp_flags() if mode == 'link': # Add link flags needed to find dependencies args += d.get_link_args() @@ -319,16 +323,16 @@ class CCompiler(Compiler): args += extra_args return args - def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'): + def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile', want_output=False): args = self._get_compiler_check_args(env, extra_args, dependencies, mode) # We only want to compile; not link with self.compile(code, args.to_native(), mode) as p: return p.returncode == 0 - def _links_wrapper(self, code, env, extra_args, dependencies): + def _links_wrapper(self, code, env, extra_args, dependencies, want_output=False): "Shares common code between self.links and self.run" args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link') - return self.compile(code, args) + return self.compile(code, args, want_output=want_output) def links(self, code, env, extra_args=None, dependencies=None): with self._links_wrapper(code, env, extra_args, dependencies) as p: @@ -337,7 +341,7 @@ class CCompiler(Compiler): def run(self, code, env, extra_args=None, dependencies=None): if self.is_cross and self.exe_wrapper is None: raise CrossNoRunException('Can not run test applications in this cross environment.') - with self._links_wrapper(code, env, extra_args, dependencies) as p: + with self._links_wrapper(code, env, extra_args, dependencies, True) as p: if p.returncode != 0: mlog.debug('Could not compile test file %s: %d\n' % ( p.input_name, @@ -367,24 +371,52 @@ class CCompiler(Compiler): return self.compiles(t.format(**fargs), env, extra_args, dependencies) def cross_compute_int(self, expression, low, high, guess, prefix, env, extra_args, dependencies): + # Try user's guess first if isinstance(guess, int): if self._compile_int('%s == %d' % (expression, guess), prefix, env, extra_args, dependencies): return guess - cur = low - while low < high: - cur = int((low + high) / 2) - if cur == low: - break - - if self._compile_int('%s >= %d' % (expression, cur), prefix, env, extra_args, dependencies): - low = cur + # If no bounds are given, compute them in the limit of int32 + maxint = 0x7fffffff + minint = -0x80000000 + if not isinstance(low, int) or not isinstance(high, int): + if self._compile_int('%s >= 0' % (expression), prefix, env, extra_args, dependencies): + low = cur = 0 + while self._compile_int('%s > %d' % (expression, cur), prefix, env, extra_args, dependencies): + low = cur + 1 + if low > maxint: + raise EnvironmentException('Cross-compile check overflowed') + cur = cur * 2 + 1 + if cur > maxint: + cur = maxint + high = cur else: + low = cur = -1 + while self._compile_int('%s < %d' % (expression, cur), prefix, env, extra_args, dependencies): + high = cur - 1 + if high < minint: + raise EnvironmentException('Cross-compile check overflowed') + cur = cur * 2 + if cur < minint: + cur = minint + low = cur + else: + # Sanity check limits given by user + if high < low: + raise EnvironmentException('high limit smaller than low limit') + condition = '%s <= %d && %s >= %d' % (expression, high, expression, low) + if not self._compile_int(condition, prefix, env, extra_args, dependencies): + raise EnvironmentException('Value out of given range') + + # Binary search + while low != high: + cur = low + int((high - low) / 2) + if self._compile_int('%s <= %d' % (expression, cur), prefix, env, extra_args, dependencies): high = cur + else: + low = cur + 1 - if self._compile_int('%s == %d' % (expression, cur), prefix, env, extra_args, dependencies): - return cur - raise EnvironmentException('Cross-compile check overflowed') + return low def compute_int(self, expression, low, high, guess, prefix, env, extra_args=None, dependencies=None): if extra_args is None: @@ -416,7 +448,7 @@ class CCompiler(Compiler): }}''' if not self.compiles(t.format(**fargs), env, extra_args, dependencies): return -1 - return self.cross_compute_int('sizeof(%s)' % typename, 1, 1024, None, prefix, env, extra_args, dependencies) + return self.cross_compute_int('sizeof(%s)' % typename, None, None, None, prefix, env, extra_args, dependencies) def sizeof(self, typename, prefix, env, extra_args=None, dependencies=None): if extra_args is None: @@ -454,7 +486,7 @@ class CCompiler(Compiler): char c; {type} target; }};''' - return self.cross_compute_int('offsetof(struct tmp, target)', 1, 1024, None, t.format(**fargs), env, extra_args, dependencies) + return self.cross_compute_int('offsetof(struct tmp, target)', None, None, None, t.format(**fargs), env, extra_args, dependencies) def alignment(self, typename, prefix, env, extra_args=None, dependencies=None): if extra_args is None: @@ -708,7 +740,7 @@ class CCompiler(Compiler): args = self.get_cross_extra_flags(env, link=False) args += self.get_compiler_check_args() n = 'symbols_have_underscore_prefix' - with self.compile(code, args, 'compile') as p: + with self.compile(code, args, 'compile', want_output=True) as p: if p.returncode != 0: m = 'BUG: Unable to compile {!r} check: {}' raise RuntimeError(m.format(n, p.stdo)) @@ -726,7 +758,7 @@ class CCompiler(Compiler): return False raise RuntimeError('BUG: {!r} check failed unexpectedly'.format(n)) - def get_library_naming(self, env, libtype): + def get_library_naming(self, env, libtype, strict=False): ''' Get library prefixes and suffixes for the target platform ordered by priority @@ -734,7 +766,10 @@ class CCompiler(Compiler): stlibext = ['a'] # We've always allowed libname to be both `foo` and `libfoo`, # and now people depend on it - prefixes = ['lib', ''] + if strict and self.id != 'msvc': # lib prefix is not usually used with msvc + prefixes = ['lib'] + else: + prefixes = ['lib', ''] # Library suffixes and prefixes if for_darwin(env.is_cross_build(), env): shlibext = ['dylib'] @@ -807,7 +842,12 @@ class CCompiler(Compiler): return ['-pthread'] def has_multi_arguments(self, args, env): - for arg in args: + for arg in args[:]: + # some compilers, e.g. GCC, don't warn for unsupported warning-disable + # flags, so when we are testing a flag like "-Wno-forgotten-towel", also + # check the equivalent enable flag too "-Wforgotten-towel" + if arg.startswith('-Wno-'): + args.append('-W' + arg[5:]) if arg.startswith('-Wl,'): mlog.warning('''{} looks like a linker argument, but has_argument and other similar methods only support checking compiler arguments. @@ -888,6 +928,29 @@ class GnuCCompiler(GnuCompiler, CCompiler): return ['-fpch-preprocess', '-include', os.path.basename(header)] +class ElbrusCCompiler(GnuCCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuCCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + + # It does support some various ISO standards and c/gnu 90, 9x, 1x in addition to those which GNU CC supports. + def get_options(self): + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none', 'c89', 'c90', 'c9x', 'c99', 'c1x', 'c11', + 'gnu89', 'gnu90', 'gnu9x', 'gnu99', 'gnu1x', 'gnu11', + 'iso9899:2011', 'iso9899:1990', 'iso9899:199409', 'iso9899:1999'], + 'none')} + return opts + + # Elbrus C compiler does not have lchmod, but there is only linker warning, not compiler error. + # So we should explicitly fail at this case. + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if funcname == 'lchmod': + return False + else: + return super().has_function(funcname, prefix, env, extra_args, dependencies) + + class IntelCCompiler(IntelCompiler, CCompiler): def __init__(self, exelist, version, icc_type, is_cross, exe_wrapper=None, **kwargs): CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs) @@ -1030,6 +1093,9 @@ class VisualStudioCCompiler(CCompiler): def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): return [] + def openmp_flags(self): + return ['/openmp'] + # FIXME, no idea what these should be. def thread_flags(self, env): return [] @@ -1166,3 +1232,22 @@ class VisualStudioCCompiler(CCompiler): if 'INCLUDE' not in os.environ: return [] return os.environ['INCLUDE'].split(os.pathsep) + + +class ArmCCompiler(ArmCompiler, CCompiler): + def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwargs): + CCompiler.__init__(self, exelist, version, is_cross, exe_wrapper, **kwargs) + ArmCompiler.__init__(self) + + def get_options(self): + opts = {'c_std': coredata.UserComboOption('c_std', 'C language standard to use', + ['none', 'c90', 'c99'], + 'none')} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['c_std'] + if std.value != 'none': + args.append('--' + std.value) + return args diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index a28a225..37326d8 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -55,7 +55,6 @@ for _l in clike_langs: clike_suffixes += lang_suffixes[_l] clike_suffixes += ('h', 'll', 's') -# XXX: Use this in is_library()? soregex = re.compile(r'.*\.so(\.[0-9]+)?(\.[0-9]+)?(\.[0-9]+)?$') # All these are only for C-like languages; see `clike_langs` above. @@ -102,6 +101,10 @@ def is_object(fname): def is_library(fname): if hasattr(fname, 'fname'): fname = fname.fname + + if soregex.match(fname): + return True + suffix = fname.split('.')[-1] return suffix in lib_suffixes @@ -113,6 +116,13 @@ gnulike_buildtype_args = {'plain': [], 'release': ['-O3'], 'minsize': ['-Os', '-g']} +arm_buildtype_args = {'plain': [], + 'debug': ['-O0', '--debug'], + 'debugoptimized': ['-O1', '--debug'], + 'release': ['-O3', '-Otime'], + 'minsize': ['-O3', '-Ospace'], + } + msvc_buildtype_args = {'plain': [], 'debug': ["/MDd", "/ZI", "/Ob0", "/Od", "/RTC1"], 'debugoptimized': ["/MD", "/Zi", "/O2", "/Ob1"], @@ -134,6 +144,13 @@ gnulike_buildtype_linker_args = {'plain': [], 'minsize': [], } +arm_buildtype_linker_args = {'plain': [], + 'debug': [], + 'debugoptimized': [], + 'release': [], + 'minsize': [], + } + msvc_buildtype_linker_args = {'plain': [], 'debug': [], 'debugoptimized': [], @@ -526,15 +543,22 @@ class CompilerArgs(list): def append_direct(self, arg): ''' Append the specified argument without any reordering or de-dup + except for absolute paths where the order of include search directories + is not relevant ''' - super().append(arg) + if os.path.isabs(arg): + self.append(arg) + else: + super().append(arg) def extend_direct(self, iterable): ''' Extend using the elements in the specified iterable without any - reordering or de-dup + reordering or de-dup except for absolute paths where the order of + include search directories is not relevant ''' - super().extend(iterable) + for elem in iterable: + self.append_direct(elem) def __add__(self, args): new = CompilerArgs(self, self.compiler) @@ -601,6 +625,8 @@ class Compiler: # Libraries to ignore in find_library() since they are provided by the # compiler or the C library. Currently only used for MSVC. ignore_libs = () + # Cache for the result of compiler checks which can be cached + compiler_check_cache = {} def __init__(self, exelist, version, **kwargs): if isinstance(exelist, str): @@ -659,6 +685,12 @@ class Compiler: def get_always_args(self): return [] + def can_linker_accept_rsp(self): + """ + Determines whether the linker can accept arguments using the @rsp syntax. + """ + return mesonlib.is_windows() + def get_linker_always_args(self): return [] @@ -753,9 +785,23 @@ class Compiler: return os.path.join(dirname, 'output.' + suffix) @contextlib.contextmanager - def compile(self, code, extra_args=None, mode='link'): + def compile(self, code, extra_args=None, mode='link', want_output=False): if extra_args is None: + textra_args = None extra_args = [] + else: + textra_args = tuple(extra_args) + key = (code, textra_args, mode) + if not want_output: + if key in self.compiler_check_cache: + p = self.compiler_check_cache[key] + mlog.debug('Using cached compile:') + mlog.debug('Cached command line: ', ' '.join(p.commands), '\n') + mlog.debug('Code:\n', code) + mlog.debug('Cached compiler stdout:\n', p.stdo) + mlog.debug('Cached compiler stderr:\n', p.stde) + yield p + raise StopIteration try: with tempfile.TemporaryDirectory() as tmpdirname: if isinstance(code, str): @@ -765,7 +811,6 @@ class Compiler: ofile.write(code) elif isinstance(code, mesonlib.File): srcname = code.fname - output = self._get_compile_output(tmpdirname, mode) # Construct the compiler command-line commands = CompilerArgs(self) @@ -778,6 +823,7 @@ class Compiler: if mode == 'preprocess': commands += self.get_preprocess_only_args() else: + output = self._get_compile_output(tmpdirname, mode) commands += self.get_output_args(output) # Generate full command-line with the exelist commands = self.get_exelist() + commands.to_native() @@ -788,8 +834,12 @@ class Compiler: p, p.stdo, p.stde = Popen_safe(commands, cwd=tmpdirname) mlog.debug('Compiler stdout:\n', p.stdo) mlog.debug('Compiler stderr:\n', p.stde) + p.commands = commands p.input_name = srcname - p.output_name = output + if want_output: + p.output_name = output + else: + self.compiler_check_cache[key] = p yield p except (PermissionError, OSError): # On Windows antivirus programs and the like hold on to files so @@ -887,6 +937,9 @@ class Compiler: def thread_flags(self, env): return [] + def openmp_flags(self): + raise EnvironmentException('Language %s does not support OpenMP flags.' % self.get_display_language()) + GCC_STANDARD = 0 GCC_OSX = 1 @@ -909,14 +962,16 @@ ICC_WIN = 2 GNU_LD_AS_NEEDED = '-Wl,--as-needed' APPLE_LD_AS_NEEDED = '-Wl,-dead_strip_dylibs' -def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module): +def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): if soversion is None: sostr = '' else: sostr = '.' + soversion - if gcc_type in (GCC_STANDARD, GCC_MINGW, GCC_CYGWIN): - # Might not be correct for mingw but seems to work. + if gcc_type == GCC_STANDARD: return ['-Wl,-soname,%s%s.%s%s' % (prefix, shlib_name, suffix, sostr)] + elif gcc_type in (GCC_MINGW, GCC_CYGWIN): + # For PE/COFF the soname argument has no effect with GNU LD + return [] elif gcc_type == GCC_OSX: if is_shared_module: return [] @@ -924,7 +979,15 @@ def get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, i if soversion is not None: install_name += '.' + soversion install_name += '.dylib' - return ['-install_name', os.path.join('@rpath', install_name)] + args = ['-install_name', os.path.join('@rpath', install_name)] + if version and len(version.split('.')) == 3: + splitted = version.split('.') + major = int(splitted[0]) + minor = int(splitted[1]) + revision = int(splitted[2]) + args += ['-compatibility_version', '%d' % (major + minor + 1)] + args += ['-current_version', '%d.%d' % (major + minor + 1, revision)] + return args else: raise RuntimeError('Not implemented yet.') @@ -980,7 +1043,7 @@ def gnulike_default_include_dirs(compiler, lang): stdout=subprocess.PIPE, env=env ) - stderr = p.stderr.read().decode('utf-8') + stderr = p.stderr.read().decode('utf-8', errors='replace') parse_state = 0 paths = [] for line in stderr.split('\n'): @@ -1062,8 +1125,8 @@ class GnuCompiler: def split_shlib_to_parts(self, fname): return os.path.dirname(fname), fname - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): + return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, version, is_shared_module) def get_std_shared_lib_link_args(self): return ['-shared'] @@ -1092,6 +1155,32 @@ class GnuCompiler: def get_default_include_dirs(self): return gnulike_default_include_dirs(self.exelist, self.language) + def openmp_flags(self): + return ['-fopenmp'] + + +class ElbrusCompiler(GnuCompiler): + # Elbrus compiler is nearly like GCC, but does not support + # PCH, LTO, sanitizers and color output as of version 1.21.x. + def __init__(self, gcc_type, defines): + GnuCompiler.__init__(self, gcc_type, defines) + self.id = 'lcc' + self.base_options = ['b_pgo', 'b_coverage', + 'b_ndebug', 'b_staticpic', + 'b_lundef', 'b_asneeded'] + + def get_library_dirs(self): + env = os.environ.copy() + env['LC_ALL'] = 'C' + stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1] + for line in stdo.split('\n'): + if line.startswith('libraries:'): + # lcc does not include '=' in --print-search-dirs output. + libstr = line.split(' ', 1)[1] + return libstr.split(':') + return [] + + class ClangCompiler: def __init__(self, clang_type): @@ -1138,7 +1227,7 @@ class ClangCompiler: # so it might change semantics at any time. return ['-include-pch', os.path.join(pch_dir, self.get_pch_name(header))] - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): if self.clang_type == CLANG_STANDARD: gcc_type = GCC_STANDARD elif self.clang_type == CLANG_OSX: @@ -1147,7 +1236,7 @@ class ClangCompiler: gcc_type = GCC_MINGW else: raise MesonException('Unreachable code when converting clang type to gcc type.') - return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, version, is_shared_module) def has_multi_arguments(self, args, env): myargs = ['-Werror=unknown-warning-option', '-Werror=unused-command-line-argument'] @@ -1187,6 +1276,15 @@ class ClangCompiler: def get_default_include_dirs(self): return gnulike_default_include_dirs(self.exelist, self.language) + def openmp_flags(self): + if version_compare(self.version, '>=3.8.0'): + return ['-fopenmp'] + elif version_compare(self.version, '>=3.7.0'): + return ['-fopenmp=libomp'] + else: + # Shouldn't work, but it'll be checked explicitly in the OpenMP dependency. + return [] + # Tested on linux for ICC 14.0.3, 15.0.6, 16.0.4, 17.0.1 class IntelCompiler: @@ -1221,7 +1319,7 @@ class IntelCompiler: def split_shlib_to_parts(self, fname): return os.path.dirname(fname), fname - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): if self.icc_type == ICC_STANDARD: gcc_type = GCC_STANDARD elif self.icc_type == ICC_OSX: @@ -1230,7 +1328,7 @@ class IntelCompiler: gcc_type = GCC_MINGW else: raise MesonException('Unreachable code when converting icc type to gcc type.') - return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + return get_gcc_soname_args(gcc_type, prefix, shlib_name, suffix, path, soversion, version, is_shared_module) # TODO: centralise this policy more globally, instead # of fragmenting it into GnuCompiler and ClangCompiler @@ -1248,3 +1346,79 @@ class IntelCompiler: def get_default_include_dirs(self): return gnulike_default_include_dirs(self.exelist, self.language) + + def openmp_flags(self): + if version_compare(self.version, '>=15.0.0'): + return ['-qopenmp'] + else: + return ['-openmp'] + + +class ArmCompiler: + # Functionality that is common to all ARM family compilers. + def __init__(self): + if not self.is_cross: + raise EnvironmentException('armcc supports only cross-compilation.') + self.id = 'arm' + default_warn_args = [] + self.warn_args = {'1': default_warn_args, + '2': default_warn_args + [], + '3': default_warn_args + []} + # Assembly + self.can_compile_suffixes.add('s') + + def can_linker_accept_rsp(self): + return False + + def get_pic_args(self): + # FIXME: Add /ropi, /rwpi, /fpic etc. qualifiers to --apcs + return [] + + def get_buildtype_args(self, buildtype): + return arm_buildtype_args[buildtype] + + def get_buildtype_linker_args(self, buildtype): + return arm_buildtype_linker_args[buildtype] + + # Override CCompiler.get_always_args + def get_always_args(self): + return [] + + # Override CCompiler.get_dependency_gen_args + def get_dependency_gen_args(self, outtarget, outfile): + return [] + + # Override CCompiler.get_std_shared_lib_link_args + def get_std_shared_lib_link_args(self): + return [] + + def get_pch_use_args(self, pch_dir, header): + # FIXME: Add required arguments + # NOTE from armcc user guide: + # "Support for Precompiled Header (PCH) files is deprecated from ARM Compiler 5.05 + # onwards on all platforms. Note that ARM Compiler on Windows 8 never supported + # PCH files." + return [] + + def get_pch_suffix(self): + # NOTE from armcc user guide: + # "Support for Precompiled Header (PCH) files is deprecated from ARM Compiler 5.05 + # onwards on all platforms. Note that ARM Compiler on Windows 8 never supported + # PCH files." + return 'pch' + + def thread_flags(self, env): + return [] + + def thread_link_flags(self, env): + return [] + + def get_linker_exelist(self): + args = ['armlink'] + return args + + def get_coverage_args(self): + return [] + + def get_coverage_link_args(self): + return [] diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index 1fa6f15..8dd2306 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -25,7 +25,9 @@ from .compilers import ( msvc_winlibs, ClangCompiler, GnuCompiler, + ElbrusCompiler, IntelCompiler, + ArmCompiler, ) class CPPCompiler(CCompiler): @@ -133,6 +135,29 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): return ['-fpch-preprocess', '-include', os.path.basename(header)] +class ElbrusCPPCompiler(GnuCPPCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuCPPCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + + # It does not support c++/gnu++ 17 and 1z, but still does support 0x, 1y, and gnu++98. + def get_options(self): + opts = super().get_options() + opts['cpp_std'] = coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none', 'c++98', 'c++03', 'c++0x', 'c++11', 'c++14', 'c++1y', + 'gnu++98', 'gnu++03', 'gnu++0x', 'gnu++11', 'gnu++14', 'gnu++1y'], + 'none') + return opts + + # Elbrus C++ compiler does not have lchmod, but there is only linker warning, not compiler error. + # So we should explicitly fail at this case. + def has_function(self, funcname, prefix, env, extra_args=None, dependencies=None): + if funcname == 'lchmod': + return False + else: + return super().has_function(funcname, prefix, env, extra_args, dependencies) + + class IntelCPPCompiler(IntelCompiler, CPPCompiler): def __init__(self, exelist, version, icc_type, is_cross, exe_wrap, **kwargs): CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, **kwargs) @@ -215,3 +240,30 @@ class VisualStudioCPPCompiler(VisualStudioCCompiler, CPPCompiler): # Visual Studio C++ compiler doesn't support -fpermissive, # so just use the plain C args. return super(VisualStudioCCompiler, self).get_compiler_check_args() + + +class ArmCPPCompiler(ArmCompiler, CPPCompiler): + def __init__(self, exelist, version, is_cross, exe_wrap=None, **kwargs): + CPPCompiler.__init__(self, exelist, version, is_cross, exe_wrap, **kwargs) + ArmCompiler.__init__(self) + + def get_options(self): + opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', + ['none', 'c++03', 'c++11'], + 'none')} + return opts + + def get_option_compile_args(self, options): + args = [] + std = options['cpp_std'] + if std.value == 'c++11': + args.append('--cpp11') + elif std.value == 'c++03': + args.append('--cpp') + return args + + def get_option_link_args(self, options): + return [] + + def get_compiler_check_args(self): + return [] diff --git a/mesonbuild/compilers/cs.py b/mesonbuild/compilers/cs.py index f78e364..581b458 100644 --- a/mesonbuild/compilers/cs.py +++ b/mesonbuild/compilers/cs.py @@ -41,7 +41,7 @@ class CsCompiler(Compiler): def get_link_args(self, fname): return ['-r:' + fname] - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): return [] def get_werror_args(self): diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py index 474e1bd..b76bfba 100644 --- a/mesonbuild/compilers/d.py +++ b/mesonbuild/compilers/d.py @@ -89,9 +89,9 @@ class DCompiler(Compiler): def get_std_shared_lib_link_args(self): return ['-shared'] - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): # 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) + return get_gcc_soname_args(GCC_STANDARD, prefix, shlib_name, suffix, path, soversion, version, is_shared_module) def get_feature_args(self, kwargs, build_to_src): res = [] diff --git a/mesonbuild/compilers/fortran.py b/mesonbuild/compilers/fortran.py index f9fcc1c..9d3240f 100644 --- a/mesonbuild/compilers/fortran.py +++ b/mesonbuild/compilers/fortran.py @@ -27,6 +27,7 @@ from .compilers import ( gnulike_buildtype_args, gnulike_buildtype_linker_args, Compiler, + ElbrusCompiler, IntelCompiler, ) @@ -93,8 +94,8 @@ end program prog def split_shlib_to_parts(self, fname): return os.path.dirname(fname), fname - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): - return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, is_shared_module) + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): + return get_gcc_soname_args(self.gcc_type, prefix, shlib_name, suffix, path, soversion, version, is_shared_module) def get_dependency_gen_args(self, outtarget, outfile): # Disabled until this is fixed: @@ -179,6 +180,15 @@ class GnuFortranCompiler(FortranCompiler): """ return ['-Wl,--out-implib=' + implibname] + def openmp_flags(self): + return ['-fopenmp'] + + +class ElbrusFortranCompiler(GnuFortranCompiler, ElbrusCompiler): + def __init__(self, exelist, version, gcc_type, is_cross, exe_wrapper=None, defines=None, **kwargs): + GnuFortranCompiler.__init__(self, exelist, version, gcc_type, is_cross, exe_wrapper, defines, **kwargs) + ElbrusCompiler.__init__(self, gcc_type, defines) + class G95FortranCompiler(FortranCompiler): def __init__(self, exelist, version, is_cross, exe_wrapper=None, **kwags): @@ -224,6 +234,9 @@ class SunFortranCompiler(FortranCompiler): def get_module_outdir_args(self, path): return ['-moddir=' + path] + def openmp_flags(self): + return ['-xopenmp'] + class IntelFortranCompiler(IntelCompiler, FortranCompiler): std_warn_args = ['-warn', 'all'] @@ -256,6 +269,10 @@ class PathScaleFortranCompiler(FortranCompiler): def get_std_warn_args(self, level): return PathScaleFortranCompiler.std_warn_args + def openmp_flags(self): + return ['-mp'] + + class PGIFortranCompiler(FortranCompiler): std_warn_args = ['-Minform=inform'] @@ -275,6 +292,9 @@ class PGIFortranCompiler(FortranCompiler): def get_no_warn_args(self): return ['-silent'] + def openmp_flags(self): + return ['-fopenmp'] + class Open64FortranCompiler(FortranCompiler): std_warn_args = ['-fullwarn'] @@ -289,6 +309,9 @@ class Open64FortranCompiler(FortranCompiler): def get_warn_args(self, level): return Open64FortranCompiler.std_warn_args + def openmp_flags(self): + return ['-mp'] + class NAGFortranCompiler(FortranCompiler): std_warn_args = [] @@ -302,3 +325,6 @@ class NAGFortranCompiler(FortranCompiler): def get_warn_args(self, level): return NAGFortranCompiler.std_warn_args + + def openmp_flags(self): + return ['-openmp'] diff --git a/mesonbuild/compilers/java.py b/mesonbuild/compilers/java.py index a8138d7..1213d18 100644 --- a/mesonbuild/compilers/java.py +++ b/mesonbuild/compilers/java.py @@ -25,7 +25,7 @@ class JavaCompiler(Compiler): self.id = 'unknown' self.javarunner = 'java' - def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, is_shared_module): + def get_soname_args(self, prefix, shlib_name, suffix, path, soversion, version, is_shared_module): return [] def get_werror_args(self): diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 9ab5c8a..6194d1a 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -35,10 +35,10 @@ class ValaCompiler(Compiler): return False # Because compiles into C. def get_output_args(self, target): - return ['-o', target] + return [] # Because compiles into C. def get_compile_only_args(self): - return ['-C'] + return [] # Because compiles into C. def get_pic_args(self): return [] diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 88d007a..918671a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -44,21 +44,17 @@ class UserOption: def validate_value(self, value): raise RuntimeError('Derived option class did not override validate_value.') + def set_value(self, newvalue): + self.value = self.validate_value(newvalue) + class UserStringOption(UserOption): def __init__(self, name, description, value, choices=None, yielding=None): super().__init__(name, description, choices, yielding) self.set_value(value) - def validate(self, value): + def validate_value(self, value): if not isinstance(value, str): raise MesonException('Value "%s" for string option "%s" is not a string.' % (str(value), self.name)) - - def set_value(self, newvalue): - self.validate(newvalue) - self.value = newvalue - - def validate_value(self, value): - self.validate(value) return value class UserBooleanOption(UserOption): @@ -66,23 +62,17 @@ class UserBooleanOption(UserOption): super().__init__(name, description, [True, False], yielding) self.set_value(value) - def tobool(self, thing): - if isinstance(thing, bool): - return thing - if thing.lower() == 'true': - return True - if thing.lower() == 'false': - return False - raise MesonException('Value %s is not boolean (true or false).' % thing) - - def set_value(self, newvalue): - self.value = self.tobool(newvalue) - def __bool__(self): return self.value def validate_value(self, value): - return self.tobool(value) + if isinstance(value, bool): + return value + if value.lower() == 'true': + return True + if value.lower() == 'false': + return False + raise MesonException('Value %s is not boolean (true or false).' % value) class UserIntegerOption(UserOption): def __init__(self, name, description, min_value, max_value, value, yielding=None): @@ -97,16 +87,16 @@ class UserIntegerOption(UserOption): c.append('<=' + str(max_value)) self.choices = ', '.join(c) - def set_value(self, newvalue): - if isinstance(newvalue, str): - newvalue = self.toint(newvalue) - if not isinstance(newvalue, int): + def validate_value(self, value): + if isinstance(value, str): + value = self.toint(value) + if not isinstance(value, int): raise MesonException('New value for integer option is not an integer.') - if self.min_value is not None and newvalue < self.min_value: - raise MesonException('New value %d is less than minimum value %d.' % (newvalue, self.min_value)) - if self.max_value is not None and newvalue > self.max_value: - raise MesonException('New value %d is more than maximum value %d.' % (newvalue, self.max_value)) - self.value = newvalue + if self.min_value is not None and value < self.min_value: + raise MesonException('New value %d is less than minimum value %d.' % (value, self.min_value)) + if self.max_value is not None and value > self.max_value: + raise MesonException('New value %d is more than maximum value %d.' % (value, self.max_value)) + return value def toint(self, valuestring): try: @@ -114,9 +104,6 @@ class UserIntegerOption(UserOption): except ValueError: raise MesonException('Value string "%s" is not convertable to an integer.' % valuestring) - def validate_value(self, value): - return self.toint(value) - class UserComboOption(UserOption): def __init__(self, name, description, choices, value, yielding=None): super().__init__(name, description, choices, yielding) @@ -127,23 +114,18 @@ class UserComboOption(UserOption): raise MesonException('Combo choice elements must be strings.') self.set_value(value) - def set_value(self, newvalue): - if newvalue not in self.choices: - optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices]) - raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (newvalue, self.name, optionsstring)) - self.value = newvalue - def validate_value(self, value): if value not in self.choices: - raise MesonException('Value %s not one of accepted values.' % value) + optionsstring = ', '.join(['"%s"' % (item,) for item in self.choices]) + raise MesonException('Value "%s" for combo option "%s" is not one of the choices. Possible choices are: %s.' % (value, self.name, optionsstring)) return value class UserArrayOption(UserOption): def __init__(self, name, description, value, **kwargs): super().__init__(name, description, kwargs.get('choices', []), yielding=kwargs.get('yielding', None)) - self.set_value(value, user_input=False) + self.value = self.validate_value(value, user_input=False) - def validate(self, value, user_input): + def validate_value(self, value, user_input=True): # User input is for options defined on the command line (via -D # options). Users can put their input in as a comma separated # string, but for defining options in meson_options.txt the format @@ -176,13 +158,6 @@ This will become a hard error in the future.''') ', '.join(bad), ', '.join(self.choices))) return newvalue - def set_value(self, newvalue, user_input=True): - self.value = self.validate(newvalue, user_input) - - def validate_value(self, value): - self.validate(value) - return value - # This class contains all data that must persist over multiple # invocations of Meson. It is roughly the same thing as # cmakecache. @@ -210,7 +185,6 @@ class CoreData: self.compilers = OrderedDict() self.cross_compilers = OrderedDict() self.deps = OrderedDict() - self.modules = {} # Only to print a warning if it changes between Meson invocations. self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') diff --git a/mesonbuild/dependencies/__init__.py b/mesonbuild/dependencies/__init__.py index 4796980..1c67311 100644 --- a/mesonbuild/dependencies/__init__.py +++ b/mesonbuild/dependencies/__init__.py @@ -18,7 +18,7 @@ from .base import ( # noqa: F401 ExternalDependency, ExternalLibrary, ExtraFrameworkDependency, InternalDependency, PkgConfigDependency, find_external_dependency, get_dep_identifier, packages, _packages_accept_language) from .dev import GMockDependency, GTestDependency, LLVMDependency, ValgrindDependency -from .misc import (MPIDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency) +from .misc import (MPIDependency, OpenMPDependency, Python3Dependency, ThreadDependency, PcapDependency, CupsDependency, LibWmfDependency) from .platform import AppleFrameworks from .ui import GLDependency, GnuStepDependency, Qt4Dependency, Qt5Dependency, SDL2Dependency, WxDependency, VulkanDependency @@ -33,6 +33,7 @@ packages.update({ # From misc: 'boost': BoostDependency, 'mpi': MPIDependency, + 'openmp': OpenMPDependency, 'python3': Python3Dependency, 'threads': ThreadDependency, 'pcap': PcapDependency, @@ -53,4 +54,5 @@ packages.update({ }) _packages_accept_language.update({ 'mpi', + 'openmp', }) diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 0375102..2a19544 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -134,6 +134,9 @@ class Dependency: def get_exe_args(self, compiler): return [] + def need_openmp(self): + return False + def need_threads(self): return False @@ -362,6 +365,8 @@ class PkgConfigDependency(ExternalDependency): # The class's copy of the pkg-config path. Avoids having to search for it # multiple times in the same Meson invocation. class_pkgbin = None + # We cache all pkg-config subprocess invocations to avoid redundant calls + pkgbin_cache = {} def __init__(self, name, environment, kwargs, language=None): super().__init__('pkgconfig', environment, language, kwargs) @@ -459,12 +464,22 @@ class PkgConfigDependency(ExternalDependency): return s.format(self.__class__.__name__, self.name, self.is_found, self.version_reqs) - def _call_pkgbin(self, args, env=None): - if not env: - env = os.environ + def _call_pkgbin_real(self, args, env): p, out = Popen_safe(self.pkgbin.get_command() + args, env=env)[0:2] return p.returncode, out.strip() + def _call_pkgbin(self, args, env=None): + if env is None: + fenv = env + env = os.environ + else: + fenv = frozenset(env.items()) + targs = tuple(args) + cache = PkgConfigDependency.pkgbin_cache + if (self.pkgbin, targs, fenv) not in cache: + cache[(self.pkgbin, targs, fenv)] = self._call_pkgbin_real(args, env) + return cache[(self.pkgbin, targs, fenv)] + def _convert_mingw_paths(self, args): ''' Both MSVC and native Python on Windows cannot handle MinGW-esque /c/foo diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 2a218be..d4525b1 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -237,6 +237,38 @@ class MPIDependency(ExternalDependency): [os.path.join(libdir, 'msmpi.lib')]) +class OpenMPDependency(ExternalDependency): + # Map date of specification release (which is the macro value) to a version. + VERSIONS = { + '201511': '4.5', + '201307': '4.0', + '201107': '3.1', + '200805': '3.0', + '200505': '2.5', + '200203': '2.0', + '199810': '1.0', + } + + def __init__(self, environment, kwargs): + language = kwargs.get('language') + super().__init__('openmp', environment, language, kwargs) + self.is_found = False + openmp_date = self.compiler.get_define('_OPENMP', '', self.env, [], [self]) + if openmp_date: + self.version = self.VERSIONS[openmp_date] + if self.compiler.has_header('omp.h', '', self.env, dependencies=[self]): + self.is_found = True + else: + mlog.log(mlog.yellow('WARNING:'), 'OpenMP found but omp.h missing.') + if self.is_found: + mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.green('YES'), self.version) + else: + mlog.log('Dependency', mlog.bold(self.name), 'found:', mlog.red('NO')) + + def need_openmp(self): + return True + + class ThreadDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('threads', environment, None, {}) diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 0115fb3..6920b8d 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -38,6 +38,8 @@ from .compilers import ( is_source, ) from .compilers import ( + ArmCCompiler, + ArmCPPCompiler, ClangCCompiler, ClangCPPCompiler, ClangObjCCompiler, @@ -48,6 +50,9 @@ from .compilers import ( GnuFortranCompiler, GnuObjCCompiler, GnuObjCPPCompiler, + ElbrusCCompiler, + ElbrusCPPCompiler, + ElbrusFortranCompiler, IntelCCompiler, IntelCPPCompiler, IntelFortranCompiler, @@ -223,6 +228,9 @@ def detect_cpu(compilers): except mesonlib.MesonException: pass return 'x86_64' + if trial == 'e2k': + # Make more precise CPU detection for Elbrus platform. + trial = platform.processor().lower() # Add fixes here as bugs are reported. return trial @@ -421,6 +429,15 @@ class Environment: return dot.join((major, minor, patch)) @staticmethod + def get_lcc_version_from_defines(defines): + dot = '.' + generation_and_major = defines.get('__LCC__', '100') + generation = generation_and_major[:1] + major = generation_and_major[1:] + minor = defines.get('__LCC_MINOR__', '0') + return dot.join((generation, major, minor)) + + @staticmethod def get_gnu_compiler_type(defines): # Detect GCC type (Apple, MinGW, Cygwin, Unix) if '__APPLE__' in defines: @@ -504,6 +521,8 @@ class Environment: if found_cl in watcom_cls: continue arg = '/?' + elif 'armcc' in compiler[0]: + arg = '--vsn' else: arg = '--version' try: @@ -513,15 +532,27 @@ class Environment: continue version = search_version(out) full_version = out.split('\n', 1)[0] + + guess_gcc_or_lcc = False if 'Free Software Foundation' in out: + guess_gcc_or_lcc = 'gcc' + if 'e2k' in out and 'lcc' in out: + guess_gcc_or_lcc = 'lcc' + + if guess_gcc_or_lcc: defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) - version = self.get_gnu_version_from_defines(defines) - cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler + if guess_gcc_or_lcc == 'lcc': + version = self.get_lcc_version_from_defines(defines) + cls = ElbrusCCompiler if lang == 'c' else ElbrusCPPCompiler + else: + version = self.get_gnu_version_from_defines(defines) + cls = GnuCCompiler if lang == 'c' else GnuCPPCompiler return cls(ccache + compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) + if 'clang' in out: if 'Apple' in out or mesonlib.for_darwin(want_cross, self): cltype = CLANG_OSX @@ -550,6 +581,9 @@ class Environment: inteltype = ICC_STANDARD cls = IntelCCompiler if lang == 'c' else IntelCPPCompiler return cls(ccache + compiler, version, inteltype, is_cross, exe_wrap, full_version=full_version) + if 'ARM' in out: + cls = ArmCCompiler if lang == 'c' else ArmCPPCompiler + return cls(ccache + compiler, version, is_cross, exe_wrap, full_version=full_version) self._handle_exceptions(popen_exceptions, compilers) def detect_c_compiler(self, want_cross): @@ -574,14 +608,25 @@ class Environment: version = search_version(out) full_version = out.split('\n', 1)[0] + guess_gcc_or_lcc = False if 'GNU Fortran' in out: + guess_gcc_or_lcc = 'gcc' + if 'e2k' in out and 'lcc' in out: + guess_gcc_or_lcc = 'lcc' + + if guess_gcc_or_lcc: defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' continue gtype = self.get_gnu_compiler_type(defines) - version = self.get_gnu_version_from_defines(defines) - return GnuFortranCompiler(compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) + if guess_gcc_or_lcc == 'lcc': + version = self.get_lcc_version_from_defines(defines) + cls = ElbrusFortranCompiler + else: + version = self.get_gnu_version_from_defines(defines) + cls = GnuFortranCompiler + return cls(compiler, version, gtype, is_cross, exe_wrap, defines, full_version=full_version) if 'G95' in out: return G95FortranCompiler(compiler, version, is_cross, exe_wrap, full_version=full_version) @@ -626,7 +671,7 @@ class Environment: popen_exceptions[' '.join(compiler + arg)] = e continue version = search_version(out) - if 'Free Software Foundation' in out: + if 'Free Software Foundation' in out or ('e2k' in out and 'lcc' in out): defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' @@ -653,7 +698,7 @@ class Environment: popen_exceptions[' '.join(compiler + arg)] = e continue version = search_version(out) - if 'Free Software Foundation' in out: + if 'Free Software Foundation' in out or ('e2k' in out and 'lcc' in out): defines = self.get_gnu_compiler_defines(compiler) if not defines: popen_exceptions[' '.join(compiler)] = 'no pre-processor defines' diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 9bdc323..2c92895 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -984,20 +984,20 @@ class CompilerHolder(InterpreterObject): check_stringlist(args) expression = args[0] prefix = kwargs.get('prefix', '') - l = kwargs.get('low', -1024) - h = kwargs.get('high', 1024) + low = kwargs.get('low', None) + high = kwargs.get('high', None) guess = kwargs.get('guess', None) if not isinstance(prefix, str): raise InterpreterException('Prefix argument of compute_int must be a string.') - if not isinstance(l, int): + if low is not None and not isinstance(low, int): raise InterpreterException('Low argument of compute_int must be an int.') - if not isinstance(h, int): + if high is not None and not isinstance(high, int): raise InterpreterException('High argument of compute_int must be an int.') if guess is not None and not isinstance(guess, int): raise InterpreterException('Guess argument of compute_int must be an int.') extra_args = self.determine_args(kwargs) deps = self.determine_dependencies(kwargs) - res = self.compiler.compute_int(expression, l, h, guess, prefix, self.environment, extra_args, deps) + res = self.compiler.compute_int(expression, low, high, guess, prefix, self.environment, extra_args, deps) mlog.log('Computing int of "%s": %d' % (expression, res)) return res @@ -1304,6 +1304,7 @@ class MesonMain(InterpreterObject): 'add_install_script': self.add_install_script_method, 'add_postconf_script': self.add_postconf_script_method, 'install_dependency_manifest': self.install_dependency_manifest_method, + 'override_find_program': self.override_find_program_method, 'project_version': self.project_version_method, 'project_license': self.project_license_method, 'version': self.version_method, @@ -1416,6 +1417,26 @@ class MesonMain(InterpreterObject): raise InterpreterException('Argument must be a string.') self.build.dep_manifest_name = args[0] + def override_find_program_method(self, args, kwargs): + if len(args) != 2: + raise InterpreterException('Override needs two arguments') + name = args[0] + exe = args[1] + if not isinstance(name, str): + raise InterpreterException('First argument must be a string') + if hasattr(exe, 'held_object'): + exe = exe.held_object + if isinstance(exe, mesonlib.File): + abspath = exe.absolute_path(self.interpreter.environment.source_dir, + self.interpreter.environment.build_dir) + if not os.path.exists(abspath): + raise InterpreterException('Tried to override %s with a file that does not exist.' % name) + exe = dependencies.ExternalProgram(abspath) + if not isinstance(exe, dependencies.ExternalProgram): + # FIXME, make this work if the exe is an Executable target. + raise InterpreterException('Second argument must be an external program.') + self.interpreter.add_find_program_override(name, exe) + def project_version_method(self, args, kwargs): return self.build.dep_manifest[self.interpreter.active_projectname]['version'] @@ -1463,7 +1484,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'add_test_setup': {'exe_wrapper', 'gdb', 'timeout_multiplier', 'env'}, 'benchmark': {'args', 'env', 'should_fail', 'timeout', 'workdir', 'suite'}, 'build_target': known_build_target_kwargs, - 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'}, + 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install', 'format'}, 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, 'dependency': {'default_options', 'fallback', 'language', 'main', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'link_whole', 'version'}, @@ -1493,7 +1514,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, class Interpreter(InterpreterBase): def __init__(self, build, backend, subproject='', subdir='', subproject_dir='subprojects', - default_project_options=[]): + modules = None, default_project_options=[]): super().__init__(build.environment.get_source_dir(), subdir) self.an_unpicklable_object = mesonlib.an_unpicklable_object self.build = build @@ -1501,6 +1522,10 @@ class Interpreter(InterpreterBase): self.coredata = self.environment.get_coredata() self.backend = backend self.subproject = subproject + if modules is None: + self.modules = {} + else: + self.modules = modules # Subproject directory is usually the name of the subproject, but can # be different for dependencies provided by wrap files. self.subproject_directory_name = subdir.split(os.path.sep)[-1] @@ -1579,14 +1604,14 @@ class Interpreter(InterpreterBase): 'run_command': self.func_run_command, 'set_variable': self.func_set_variable, 'subdir': self.func_subdir, + 'subdir_done': self.func_subdir_done, 'subproject': self.func_subproject, 'shared_library': self.func_shared_lib, 'shared_module': self.func_shared_module, 'static_library': self.func_static_lib, 'both_libraries': self.func_both_lib, 'test': self.func_test, - 'vcs_tag': self.func_vcs_tag, - 'subdir_done': self.func_subdir_done, + 'vcs_tag': self.func_vcs_tag }) if 'MESON_UNIT_TEST' in os.environ: self.funcs.update({'exception': self.func_exception}) @@ -1683,13 +1708,13 @@ class Interpreter(InterpreterBase): plainname = modname.split('-', 1)[1] mlog.warning('Module %s has no backwards or forwards compatibility and might not exist in future releases.' % modname, location=node) modname = 'unstable_' + plainname - if modname not in self.environment.coredata.modules: + if modname not in self.modules: try: module = importlib.import_module('mesonbuild.modules.' + modname) except ImportError: raise InvalidArguments('Module "%s" does not exist' % (modname, )) - self.environment.coredata.modules[modname] = module.initialize() - return ModuleHolder(modname, self.environment.coredata.modules[modname], self) + self.modules[modname] = module.initialize(self) + return ModuleHolder(modname, self.modules[modname], self) @stringArgs @noKwargs @@ -1864,21 +1889,24 @@ external dependencies (including libraries) must go to "dependencies".''') subdir = os.path.join(self.subproject_dir, resolved) os.makedirs(os.path.join(self.build.environment.get_build_dir(), subdir), exist_ok=True) self.global_args_frozen = True - mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='') - subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir, - mesonlib.stringlistify(kwargs.get('default_options', []))) - subi.subprojects = self.subprojects - - subi.subproject_stack = self.subproject_stack + [dirname] - current_active = self.active_projectname - subi.run() + mlog.log() + with mlog.nested(): + mlog.log('\nExecuting subproject ', mlog.bold(dirname), '.\n', sep='') + subi = Interpreter(self.build, self.backend, dirname, subdir, self.subproject_dir, + self.modules, mesonlib.stringlistify(kwargs.get('default_options', []))) + subi.subprojects = self.subprojects + + subi.subproject_stack = self.subproject_stack + [dirname] + current_active = self.active_projectname + subi.run() + mlog.log('\nSubproject', mlog.bold(dirname), 'finished.') + if 'version' in kwargs: pv = subi.project_version wanted = kwargs['version'] if pv == 'undefined' or not mesonlib.version_compare(pv, wanted): raise InterpreterException('Subproject %s version is %s but %s required.' % (dirname, pv, wanted)) self.active_projectname = current_active - mlog.log('\nSubproject', mlog.bold(dirname), 'finished.') self.build.subprojects[dirname] = subi.project_version self.subprojects.update(subi.subprojects) self.subprojects[dirname] = SubprojectHolder(subi) @@ -2225,7 +2253,7 @@ to directly access options of other subprojects.''') break self.coredata.base_options[optname] = oobj - def program_from_cross_file(self, prognames): + def program_from_cross_file(self, prognames, silent=False): bins = self.environment.cross_info.config['binaries'] for p in prognames: if hasattr(p, 'held_object'): @@ -2236,11 +2264,11 @@ to directly access options of other subprojects.''') raise InterpreterException('Executable name must be a string.') if p in bins: exename = bins[p] - extprog = dependencies.ExternalProgram(exename) + extprog = dependencies.ExternalProgram(exename, silent=silent) progobj = ExternalProgramHolder(extprog) return progobj - def program_from_system(self, args): + def program_from_system(self, args, silent=False): # Search for scripts relative to current subdir. # Do not cache found programs because find_program('foobar') # might give different results when run from different source dirs. @@ -2259,11 +2287,55 @@ to directly access options of other subprojects.''') else: raise InvalidArguments('find_program only accepts strings and ' 'files, not {!r}'.format(exename)) - extprog = dependencies.ExternalProgram(exename, search_dir=search_dir) + extprog = dependencies.ExternalProgram(exename, search_dir=search_dir, + silent=silent) progobj = ExternalProgramHolder(extprog) if progobj.found(): return progobj + def program_from_overrides(self, command_names, silent=False): + for name in command_names: + if not isinstance(name, str): + continue + if name in self.build.find_overrides: + exe = self.build.find_overrides[name] + if not silent: + mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), + '(overridden: %s)' % ' '.join(exe.command)) + return ExternalProgramHolder(exe) + return None + + def store_name_lookups(self, command_names): + for name in command_names: + if isinstance(name, str): + self.build.searched_programs.add(name) + + def add_find_program_override(self, name, exe): + if name in self.build.searched_programs: + raise InterpreterException('Tried to override finding of executable "%s" which has already been found.' + % name) + if name in self.build.find_overrides: + raise InterpreterException('Tried to override executable "%s" which has already been overridden.' + % name) + self.build.find_overrides[name] = exe + + def find_program_impl(self, args, native=False, required=True, silent=True): + if not isinstance(args, list): + args = [args] + progobj = self.program_from_overrides(args, silent=silent) + if progobj is None and self.build.environment.is_cross_build(): + if not native: + progobj = self.program_from_cross_file(args, silent=silent) + if progobj is None: + progobj = self.program_from_system(args, silent=silent) + if required and (progobj is None or not progobj.found()): + raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args)) + if progobj is None: + return ExternalProgramHolder(dependencies.NonExistingExternalProgram()) + # Only store successful lookups + self.store_name_lookups(args) + return progobj + @permittedKwargs(permitted_kwargs['find_program']) def func_find_program(self, node, args, kwargs): if not args: @@ -2271,20 +2343,10 @@ to directly access options of other subprojects.''') required = kwargs.get('required', True) if not isinstance(required, bool): raise InvalidArguments('"required" argument must be a boolean.') - progobj = None - if self.build.environment.is_cross_build(): - use_native = kwargs.get('native', False) - 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) - if progobj is None: - progobj = self.program_from_system(args) - if required and (progobj is None or not progobj.found()): - raise InvalidArguments('Program(s) {!r} not found or not executable'.format(args)) - if progobj is None: - return ExternalProgramHolder(dependencies.NonExistingExternalProgram()) - return progobj + use_native = kwargs.get('native', False) + if not isinstance(use_native, bool): + raise InvalidArguments('Argument to "native" must be a boolean.') + return self.find_program_impl(args, native=use_native, required=required, silent=False) def func_find_library(self, node, args, kwargs): raise InvalidCode('find_library() is removed, use meson.get_compiler(\'name\').find_library() instead.\n' @@ -2406,10 +2468,13 @@ to directly access options of other subprojects.''') dep = None # Search for it outside the project - try: - dep = dependencies.find_external_dependency(name, self.environment, kwargs) - except DependencyException as e: - exception = e + if self.coredata.wrap_mode != WrapMode.forcefallback or 'fallback' not in kwargs: + try: + dep = dependencies.find_external_dependency(name, self.environment, kwargs) + except DependencyException as e: + exception = e + else: + exception = DependencyException("fallback for %s not found" % name) # Search inside the projects list if not dep or not dep.found(): @@ -2688,7 +2753,7 @@ root and issuing %s. exe = args[1] if not isinstance(exe, (ExecutableHolder, JarHolder, ExternalProgramHolder)): if isinstance(exe, mesonlib.File): - exe = self.func_find_program(node, (args[1], ), {}) + exe = self.func_find_program(node, args[1], {}) else: raise InterpreterException('Second argument must be executable.') par = kwargs.get('is_parallel', True) @@ -2884,6 +2949,16 @@ root and issuing %s. if 'command' not in kwargs: raise InterpreterException('"capture" keyword requires "command" keyword.') + if 'format' in kwargs: + format = kwargs['format'] + if not isinstance(format, str): + raise InterpreterException('"format" keyword must be a string.') + else: + format = 'meson' + + if format not in ('meson', 'cmake', 'cmake@'): + raise InterpreterException('"format" possible values are "meson", "cmake" or "cmake@".') + # Validate input inputfile = None ifile_abs = None @@ -2923,7 +2998,7 @@ root and issuing %s. if inputfile is not None: os.makedirs(os.path.join(self.environment.build_dir, self.subdir), exist_ok=True) missing_variables = mesonlib.do_conf_file(ifile_abs, ofile_abs, - conf.held_object) + conf.held_object, format) if missing_variables: var_list = ", ".join(map(repr, sorted(missing_variables))) mlog.warning( diff --git a/mesonbuild/linkers.py b/mesonbuild/linkers.py index 2333e27..cb07c5e 100644 --- a/mesonbuild/linkers.py +++ b/mesonbuild/linkers.py @@ -13,9 +13,14 @@ # limitations under the License. from .mesonlib import Popen_safe +from . import mesonlib class StaticLinker: - pass + def can_linker_accept_rsp(self): + """ + Determines whether the linker can accept arguments using the @rsp syntax. + """ + return mesonlib.is_windows() class VisualStudioLinker(StaticLinker): @@ -51,6 +56,9 @@ class VisualStudioLinker(StaticLinker): def thread_link_flags(self, env): return [] + def openmp_flags(self): + return [] + def get_option_link_args(self, options): return [] @@ -75,6 +83,12 @@ class ArLinker(StaticLinker): self.std_args = ['csrD'] else: self.std_args = ['csr'] + # For 'armar' the options should be prefixed with '-'. + if 'armar' in stdo: + self.std_args = ['-csr'] + + def can_linker_accept_rsp(self): + return False def build_rpath_args(self, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): return [] @@ -103,6 +117,9 @@ class ArLinker(StaticLinker): def thread_link_flags(self, env): return [] + def openmp_flags(self): + return [] + def get_option_link_args(self, options): return [] diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index a076e3e..8a2b67c 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -541,8 +541,13 @@ def has_path_sep(name, sep='/\\'): return True return False -def do_replacement(regex, line, confdata): +def do_replacement(regex, line, format, confdata): missing_variables = set() + start_tag = '@' + backslash_tag = '\\@' + if format == 'cmake': + start_tag = '${' + backslash_tag = '\\${' def variable_replace(match): # Pairs of escape characters before '@' or '\@' @@ -550,8 +555,8 @@ def do_replacement(regex, line, confdata): num_escapes = match.end(0) - match.start(0) return '\\' * (num_escapes // 2) # Single escape character and '@' - elif match.group(0) == '\\@': - return '@' + elif match.group(0) == backslash_tag: + return start_tag # Template variable to be replaced else: varname = match.group(1) @@ -591,7 +596,7 @@ def do_mesondefine(line, confdata): raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname) -def do_conf_file(src, dst, confdata): +def do_conf_file(src, dst, confdata, format): try: with open(src, encoding='utf-8') as f: data = f.readlines() @@ -599,14 +604,24 @@ def do_conf_file(src, dst, confdata): raise MesonException('Could not read input file %s: %s' % (src, str(e))) # Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define # Also allow escaping '@' with '\@' - regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@') + if format in ['meson', 'cmake@']: + regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@') + elif format == 'cmake': + regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}') + else: + raise MesonException('Format "{}" not handled'.format(format)) + + search_token = '#mesondefine' + if format != 'meson': + search_token = '#cmakedefine' + result = [] missing_variables = set() for line in data: - if line.startswith('#mesondefine'): + if line.startswith(search_token): line = do_mesondefine(line, confdata) else: - line, missing = do_replacement(regex, line, confdata) + line, missing = do_replacement(regex, line, format, confdata) missing_variables.update(missing) result.append(line) dst_tmp = dst + '~' @@ -734,7 +749,9 @@ 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: + import locale + encoding = locale.getpreferredencoding() + if sys.version_info < (3, 6) or not sys.stdout.encoding or encoding.upper() != 'UTF-8': return Popen_safe_legacy(args, write=write, stderr=stderr, **kwargs) p = subprocess.Popen(args, universal_newlines=True, close_fds=False, diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index 651224e..daf5907 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -210,7 +210,8 @@ class MesonApp: # Post-conf scripts must be run after writing coredata or else introspection fails. g.run_postconf_scripts() except: - os.unlink(cdf) + if 'cdf' in locals(): + os.unlink(cdf) raise def run_script_command(args): diff --git a/mesonbuild/mintro.py b/mesonbuild/mintro.py index 74d26da..5a9d4cf 100644 --- a/mesonbuild/mintro.py +++ b/mesonbuild/mintro.py @@ -46,7 +46,7 @@ def buildparser(): help='List external dependencies.') parser.add_argument('--projectinfo', action='store_true', dest='projectinfo', default=False, help='Information about projects.') - parser.add_argument('builddir', nargs='?', help='The build directory') + parser.add_argument('builddir', nargs='?', default='.', help='The build directory') return parser def determine_installed_path(target, installdata): diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 347cede..6cbaf60 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -13,6 +13,7 @@ # limitations under the License. import sys, os, platform, io +from contextlib import contextmanager """This is (mostly) a standalone module used to write logging information about Meson runs. Some output goes to screen, @@ -25,6 +26,7 @@ else: log_dir = None log_file = None log_fname = 'meson-log.txt' +log_depth = 0 def initialize(logdir): global log_dir, log_file @@ -77,15 +79,21 @@ def process_markup(args, keep): return arr def force_print(*args, **kwargs): + iostr = io.StringIO() + kwargs['file'] = iostr + print(*args, **kwargs) + + raw = iostr.getvalue() + if log_depth > 0: + prepend = '|' * log_depth + raw = prepend + raw.replace('\n', '\n' + prepend, raw.count('\n') - 1) + # _Something_ is going to get printed. try: - print(*args, **kwargs) + print(raw, end='') except UnicodeEncodeError: - iostr = io.StringIO() - kwargs['file'] = iostr - print(*args, **kwargs) - cleaned = iostr.getvalue().encode('ascii', 'replace').decode('ascii') - print(cleaned) + cleaned = raw.encode('ascii', 'replace').decode('ascii') + print(cleaned, end='') def debug(*args, **kwargs): arr = process_markup(args, False) @@ -146,3 +154,12 @@ def format_list(list): return list[0] else: return '' + +@contextmanager +def nested(): + global log_depth + log_depth += 1 + try: + yield + finally: + log_depth -= 1 diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index bf513fd..55bbbd3 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -18,26 +18,15 @@ class permittedSnippetKwargs: return f(s, interpreter, state, args, kwargs) return wrapped -_found_programs = {} - class ExtensionModule: - def __init__(self): + def __init__(self, interpreter): + self.interpreter = interpreter self.snippets = set() # List of methods that operate only on the interpreter. def is_snippet(self, funcname): return funcname in self.snippets -def find_program(program_name, target_name): - if program_name in _found_programs: - return _found_programs[program_name] - program = dependencies.ExternalProgram(program_name) - if not program.found(): - m = "Target {!r} can't be generated as {!r} could not be found" - raise MesonException(m.format(target_name, program_name)) - _found_programs[program_name] = program - return program - def get_include_args(include_dirs, prefix='-I'): ''' diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 30364a6..5455118 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -25,7 +25,7 @@ from .. import mesonlib from .. import compilers from .. import interpreter from . import GResourceTarget, GResourceHeaderTarget, GirTarget, TypelibTarget, VapiTarget -from . import find_program, get_include_args +from . import get_include_args from . import ExtensionModule from . import ModuleReturnValue from ..mesonlib import MesonException, OrderedSet, Popen_safe, extract_as_list @@ -45,14 +45,14 @@ gdbuswarning_printed = False gresource_warning_printed = False _gir_has_extra_lib_arg = None -def gir_has_extra_lib_arg(): +def gir_has_extra_lib_arg(intr_obj): global _gir_has_extra_lib_arg if _gir_has_extra_lib_arg is not None: return _gir_has_extra_lib_arg _gir_has_extra_lib_arg = False try: - g_ir_scanner = find_program('g-ir-scanner', '').get_command() + g_ir_scanner = intr_obj.find_program_impl('g-ir-scanner').get_command() opts = Popen_safe(g_ir_scanner + ['--help'], stderr=subprocess.STDOUT)[1] _gir_has_extra_lib_arg = '--extra-library' in opts except (MesonException, FileNotFoundError, subprocess.CalledProcessError): @@ -302,7 +302,7 @@ class GnomeModule(ExtensionModule): link_command.append('-Wl,-rpath,' + libdir) if depends: depends.append(lib) - if gir_has_extra_lib_arg() and use_gir_args: + if gir_has_extra_lib_arg(self.interpreter) and use_gir_args: link_command.append('--extra-library=' + lib.name) else: link_command.append('-l' + lib.name) @@ -369,7 +369,7 @@ class GnomeModule(ExtensionModule): mlog.log('dependency {!r} not handled to build gir files'.format(dep)) continue - if gir_has_extra_lib_arg() and use_gir_args: + if gir_has_extra_lib_arg(self.interpreter) and use_gir_args: fixed_ldflags = OrderedSet() for ldflag in ldflags: if ldflag.startswith("-l"): @@ -388,8 +388,8 @@ class GnomeModule(ExtensionModule): raise MesonException('Gir takes one argument') if kwargs.get('install_dir'): raise MesonException('install_dir is not supported with generate_gir(), see "install_dir_gir" and "install_dir_typelib"') - giscanner = find_program('g-ir-scanner', 'Gir') - gicompiler = find_program('g-ir-compiler', 'Gir') + giscanner = self.interpreter.find_program_impl('g-ir-scanner') + gicompiler = self.interpreter.find_program_impl('g-ir-compiler') girtarget = args[0] while hasattr(girtarget, 'held_object'): girtarget = girtarget.held_object @@ -637,7 +637,7 @@ class GnomeModule(ExtensionModule): srcdir = os.path.join(state.build_to_src, state.subdir) outdir = state.subdir - cmd = [find_program('glib-compile-schemas', 'gsettings-compile')] + cmd = [self.interpreter.find_program_impl('glib-compile-schemas')] cmd += ['--targetdir', outdir, srcdir] kwargs['command'] = cmd kwargs['input'] = [] @@ -869,22 +869,21 @@ This will become a hard error in the future.''') return [] @permittedKwargs({'interface_prefix', 'namespace', 'object_manager', 'build_by_default', - 'annotations', 'docbook'}) + 'annotations', 'docbook', 'install', 'install_header'}) def gdbus_codegen(self, state, args, kwargs): if len(args) != 2: raise MesonException('Gdbus_codegen takes two arguments, name and xml file.') namebase = args[0] xml_file = args[1] target_name = namebase + '-gdbus' - cmd = [find_program('gdbus-codegen', target_name)] + cmd = [self.interpreter.find_program_impl('gdbus-codegen')] if 'interface_prefix' in kwargs: cmd += ['--interface-prefix', kwargs.pop('interface_prefix')] if 'namespace' in kwargs: 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')] + build_by_default = kwargs.get('build_by_default', False) # Annotations are a bit ugly in that they are a list of lists of strings... annotations = kwargs.pop('annotations', []) @@ -898,21 +897,74 @@ This will become a hard error in the future.''') raise MesonException('Annotations must be made up of 3 strings for ELEMENT, KEY, and VALUE') cmd += ['--annotate'] + annotation - # https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a - if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.51.3'): - cmd += ['--output-directory', '@OUTDIR@', '--generate-c-code', namebase, '@INPUT@'] + # https://git.gnome.org/browse/glib/commit/?id=e4d68c7b3e8b01ab1a4231bf6da21d045cb5a816 + if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.55.2'): + targets = [] + install_header = kwargs.get('install_header', False) + install_dir = kwargs.get('install_dir', state.environment.coredata.get_builtin_option('includedir')) + + output = namebase + '.c' + custom_kwargs = {'input': xml_file, + 'output': output, + 'command': cmd + ['--body', '--output', '@OUTDIR@/' + output, '@INPUT@'], + 'build_by_default': build_by_default + } + targets.append(build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs)) + + output = namebase + '.h' + custom_kwargs = {'input': xml_file, + 'output': output, + 'command': cmd + ['--header', '--output', '@OUTDIR@/' + output, '@INPUT@'], + 'build_by_default': build_by_default, + 'install': install_header, + 'install_dir': install_dir + } + targets.append(build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs)) + + if 'docbook' in kwargs: + docbook = kwargs['docbook'] + if not isinstance(docbook, str): + raise MesonException('docbook value must be a string.') + + docbook_cmd = cmd + ['--output-directory', '@OUTDIR@', '--generate-docbook', docbook, '@INPUT@'] + + output = namebase + '-docbook' + custom_kwargs = {'input': xml_file, + 'output': output, + 'command': docbook_cmd, + 'build_by_default': build_by_default + } + targets.append(build.CustomTarget(output, state.subdir, state.subproject, custom_kwargs)) + + objects = targets else: - self._print_gdbus_warning() - cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] - outputs = [namebase + '.c', namebase + '.h'] - custom_kwargs = {'input': xml_file, - 'output': outputs, - 'command': cmd - } - if 'build_by_default' in kwargs: - custom_kwargs['build_by_default'] = kwargs['build_by_default'] - ct = build.CustomTarget(target_name, state.subdir, state.subproject, custom_kwargs) - return ModuleReturnValue(ct, [ct]) + if 'docbook' in kwargs: + docbook = kwargs['docbook'] + if not isinstance(docbook, str): + raise MesonException('docbook value must be a string.') + + cmd += ['--generate-docbook', docbook] + + # https://git.gnome.org/browse/glib/commit/?id=ee09bb704fe9ccb24d92dd86696a0e6bb8f0dc1a + if mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.51.3'): + cmd += ['--output-directory', '@OUTDIR@', '--generate-c-code', namebase, '@INPUT@'] + else: + self._print_gdbus_warning() + cmd += ['--generate-c-code', '@OUTDIR@/' + namebase, '@INPUT@'] + outputs = [namebase + '.c', namebase + '.h'] + custom_kwargs = {'input': xml_file, + 'output': outputs, + 'command': cmd, + 'build_by_default': build_by_default + } + ct = build.CustomTarget(target_name, state.subdir, state.subproject, custom_kwargs) + # Ensure that the same number (and order) of arguments are returned + # regardless of the gdbus-codegen (glib) version being used + targets = [ct, ct] + if 'docbook' in kwargs: + targets.append(ct) + objects = [ct] + return ModuleReturnValue(targets, objects) @permittedKwargs({'sources', 'c_template', 'h_template', 'install_header', 'install_dir', 'comments', 'identifier_prefix', 'symbol_prefix', 'eprod', 'vprod', @@ -961,7 +1013,7 @@ This will become a hard error in the future.''') elif arg not in known_custom_target_kwargs: raise MesonException( 'Mkenums does not take a %s keyword argument.' % (arg, )) - cmd = [find_program('glib-mkenums', 'mkenums')] + cmd + cmd = [self.interpreter.find_program_impl(['glib-mkenums', 'mkenums'])] + cmd custom_kwargs = {} for arg in known_custom_target_kwargs: if arg in kwargs: @@ -1046,6 +1098,12 @@ This will become a hard error in the future.''') raise MesonException( 'Sources keyword argument must be a string or array.') + # The `install_header` argument will be used by mkenums() when + # not using template files, so we need to forcibly unset it + # when generating the C source file, otherwise we will end up + # installing it + c_file_kwargs['install_header'] = False + header_prefix = kwargs.get('header_prefix', '') decl_decorator = kwargs.get('decorator', '') func_prefix = kwargs.get('function_prefix', '') @@ -1151,7 +1209,7 @@ G_END_DECLS''' new_genmarshal = mesonlib.version_compare(self._get_native_glib_version(state), '>= 2.53.3') - cmd = [find_program('glib-genmarshal', output + '_genmarshal')] + cmd = [self.interpreter.find_program_impl('glib-genmarshal')] known_kwargs = ['internal', 'nostdinc', 'skip_source', 'stdinc', 'valist_marshallers', 'extra_args'] known_custom_target_kwargs = ['build_always', 'depends', @@ -1297,9 +1355,9 @@ G_END_DECLS''' pkg_cmd, vapi_depends, vapi_packages, vapi_includes = self._extract_vapi_packages(state, kwargs) target_name = 'generate_vapi({})'.format(library) if 'VAPIGEN' in os.environ: - cmd = [find_program(os.environ['VAPIGEN'], target_name)] + cmd = [self.interpreter.find_program_impl(os.environ['VAPIGEN'])] else: - cmd = [find_program('vapigen', target_name)] + cmd = [self.interpreter.find_program_impl('vapigen')] cmd += ['--quiet', '--library=' + library, '--directory=' + build_dir] cmd += self._vapi_args_to_command('--vapidir=', 'vapi_dirs', kwargs) cmd += self._vapi_args_to_command('--metadatadir=', 'metadata_dirs', kwargs) @@ -1354,5 +1412,5 @@ G_END_DECLS''' created_values.append(rv) return ModuleReturnValue(rv, created_values) -def initialize(): - return GnomeModule() +def initialize(*args, **kwargs): + return GnomeModule(*args, **kwargs) diff --git a/mesonbuild/modules/i18n.py b/mesonbuild/modules/i18n.py index 6c02fbb..4281200 100644 --- a/mesonbuild/modules/i18n.py +++ b/mesonbuild/modules/i18n.py @@ -143,5 +143,5 @@ class I18nModule(ExtensionModule): return ModuleReturnValue(None, targets) -def initialize(): - return I18nModule() +def initialize(*args, **kwargs): + return I18nModule(*args, **kwargs) diff --git a/mesonbuild/modules/modtest.py b/mesonbuild/modules/modtest.py index 758eeae..533989f 100644 --- a/mesonbuild/modules/modtest.py +++ b/mesonbuild/modules/modtest.py @@ -24,5 +24,5 @@ class TestModule(ExtensionModule): rv = ModuleReturnValue(None, []) return rv -def initialize(): - return TestModule() +def initialize(*args, **kwargs): + return TestModule(*args, **kwargs) diff --git a/mesonbuild/modules/pkgconfig.py b/mesonbuild/modules/pkgconfig.py index c587f84..934e2f4 100644 --- a/mesonbuild/modules/pkgconfig.py +++ b/mesonbuild/modules/pkgconfig.py @@ -87,10 +87,11 @@ class DependenciesHelper: processed_reqs = [] processed_cflags = [] for obj in libs: + shared_library_only = getattr(obj, 'shared_library_only', False) if hasattr(obj, 'pcdep'): pcdeps = mesonlib.listify(obj.pcdep) for d in pcdeps: - processed_reqs += d.name + processed_reqs.append(d.name) self.add_version_reqs(d.name, obj.version_reqs) elif hasattr(obj, 'generated_pc'): processed_reqs.append(obj.generated_pc) @@ -105,7 +106,7 @@ class DependenciesHelper: if obj.found(): processed_libs += obj.get_link_args() processed_cflags += obj.get_compile_args() - elif isinstance(obj, build.SharedLibrary) and obj.shared_library_only: + elif isinstance(obj, build.SharedLibrary) and shared_library_only: # Do not pull dependencies for shared libraries because they are # only required for static linking. Adding private requires has # the side effect of exposing their cflags, which is the @@ -118,11 +119,15 @@ class DependenciesHelper: obj.generated_pc = self.name elif isinstance(obj, (build.SharedLibrary, build.StaticLibrary)): processed_libs.append(obj) - self.add_priv_libs(obj.get_dependencies()) - self.add_priv_libs(obj.get_external_deps()) if public: if not hasattr(obj, 'generated_pc'): obj.generated_pc = self.name + if isinstance(obj, build.StaticLibrary) and public: + self.add_pub_libs(obj.get_dependencies()) + self.add_pub_libs(obj.get_external_deps()) + else: + self.add_priv_libs(obj.get_dependencies()) + self.add_priv_libs(obj.get_external_deps()) elif isinstance(obj, str): processed_libs.append(obj) else: @@ -132,9 +137,11 @@ class DependenciesHelper: def add_version_reqs(self, name, version_reqs): if version_reqs: - vreqs = self.version_reqs.get(name, []) - vreqs += mesonlib.stringlistify(version_reqs) - self.version_reqs[name] = vreqs + if name not in self.version_reqs: + self.version_reqs[name] = set() + # We could have '>=1.0' or '>= 1.0', remove spaces to normalize + new_vreqs = [s.replace(' ', '') for s in mesonlib.stringlistify(version_reqs)] + self.version_reqs[name].update(new_vreqs) def split_version_req(self, s): for op in ['>=', '<=', '!=', '==', '=', '>', '<']: @@ -386,5 +393,5 @@ class PkgConfigModule(ExtensionModule): res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), pcfile), pkgroot) return ModuleReturnValue(res, [res]) -def initialize(): - return PkgConfigModule() +def initialize(*args, **kwargs): + return PkgConfigModule(*args, **kwargs) diff --git a/mesonbuild/modules/python3.py b/mesonbuild/modules/python3.py index d2bf1dc..79da29a 100644 --- a/mesonbuild/modules/python3.py +++ b/mesonbuild/modules/python3.py @@ -23,8 +23,8 @@ from ..build import known_shmod_kwargs class Python3Module(ExtensionModule): - def __init__(self): - super().__init__() + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) self.snippets.add('extension_module') @permittedSnippetKwargs(known_shmod_kwargs) @@ -69,5 +69,5 @@ class Python3Module(ExtensionModule): return ModuleReturnValue(path, []) -def initialize(): - return Python3Module() +def initialize(*args, **kwargs): + return Python3Module(*args, **kwargs) diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index f5ce1ed..39c65ed 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -15,7 +15,7 @@ import os from .. import mlog from .. import build -from ..mesonlib import MesonException, Popen_safe, extract_as_list +from ..mesonlib import MesonException, Popen_safe, extract_as_list, File from ..dependencies import Qt4Dependency, Qt5Dependency import xml.etree.ElementTree as ET from . import ModuleReturnValue, get_include_args @@ -71,19 +71,47 @@ class QtBaseModule: mlog.log(' {}:'.format(compiler_name.lower()), mlog.red('NO')) self.tools_detected = True - def parse_qrc(self, state, fname): - abspath = os.path.join(state.environment.source_dir, state.subdir, fname) - relative_part = os.path.dirname(fname) + def parse_qrc(self, state, rcc_file): + if type(rcc_file) is str: + abspath = os.path.join(state.environment.source_dir, state.subdir, rcc_file) + rcc_dirname = os.path.dirname(abspath) + elif type(rcc_file) is File: + abspath = rcc_file.absolute_path(state.environment.source_dir, state.environment.build_dir) + rcc_dirname = os.path.dirname(abspath) + try: tree = ET.parse(abspath) root = tree.getroot() result = [] for child in root[0]: if child.tag != 'file': - mlog.warning("malformed rcc file: ", os.path.join(state.subdir, fname)) + mlog.warning("malformed rcc file: ", os.path.join(state.subdir, rcc_file)) break else: - result.append(os.path.join(relative_part, child.text)) + resource_path = child.text + # We need to guess if the pointed resource is: + # a) in build directory -> implies a generated file + # b) in source directory + # c) somewhere else external dependency file to bundle + # + # Also from qrc documentation: relative path are always from qrc file + # So relative path must always be computed from qrc file ! + if os.path.isabs(resource_path): + # a) + if resource_path.startswith(os.path.abspath(state.environment.build_dir)): + resource_relpath = os.path.relpath(resource_path, state.environment.build_dir) + result.append(File(is_built=True, subdir='', fname=resource_relpath)) + # either b) or c) + else: + result.append(File(is_built=False, subdir=state.subdir, fname=resource_path)) + else: + path_from_rcc = os.path.normpath(os.path.join(rcc_dirname, resource_path)) + # a) + if path_from_rcc.startswith(state.environment.build_dir): + result.append(File(is_built=True, subdir=state.subdir, fname=resource_path)) + # b) + else: + result.append(File(is_built=False, subdir=state.subdir, fname=path_from_rcc)) return result except Exception: return [] @@ -102,11 +130,11 @@ class QtBaseModule: if len(rcc_files) > 0: if not self.rcc.found(): raise MesonException(err_msg.format('RCC', 'rcc-qt{}'.format(self.qt_version), self.qt_version)) - qrc_deps = [] - for i in rcc_files: - qrc_deps += self.parse_qrc(state, i) # custom output name set? -> one output file, multiple otherwise if len(args) > 0: + qrc_deps = [] + for i in rcc_files: + qrc_deps += self.parse_qrc(state, i) name = args[0] rcc_kwargs = {'input': rcc_files, 'output': name + '.cpp', @@ -116,7 +144,11 @@ class QtBaseModule: sources.append(res_target) else: for rcc_file in rcc_files: - basename = os.path.basename(rcc_file) + qrc_deps = self.parse_qrc(state, rcc_file) + if type(rcc_file) is str: + basename = os.path.basename(rcc_file) + elif type(rcc_file) is File: + basename = os.path.basename(rcc_file.fname) name = 'qt' + str(self.qt_version) + '-' + basename.replace('.', '_') rcc_kwargs = {'input': rcc_file, 'output': name + '.cpp', diff --git a/mesonbuild/modules/qt4.py b/mesonbuild/modules/qt4.py index 3011de7..29992d5 100644 --- a/mesonbuild/modules/qt4.py +++ b/mesonbuild/modules/qt4.py @@ -19,11 +19,11 @@ from . import ExtensionModule class Qt4Module(ExtensionModule, QtBaseModule): - def __init__(self): + def __init__(self, interpreter): QtBaseModule.__init__(self, qt_version=4) - ExtensionModule.__init__(self) + ExtensionModule.__init__(self, interpreter) -def initialize(): +def initialize(*args, **kwargs): mlog.warning('rcc dependencies will not work properly until this upstream issue is fixed:', mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460')) - return Qt4Module() + return Qt4Module(*args, **kwargs) diff --git a/mesonbuild/modules/qt5.py b/mesonbuild/modules/qt5.py index 7b7acbb..19623ac 100644 --- a/mesonbuild/modules/qt5.py +++ b/mesonbuild/modules/qt5.py @@ -19,11 +19,11 @@ from . import ExtensionModule class Qt5Module(ExtensionModule, QtBaseModule): - def __init__(self): + def __init__(self, interpreter): QtBaseModule.__init__(self, qt_version=5) - ExtensionModule.__init__(self) + ExtensionModule.__init__(self, interpreter) -def initialize(): +def initialize(*args, **kwargs): mlog.warning('rcc dependencies will not work reliably until this upstream issue is fixed:', mlog.bold('https://bugreports.qt.io/browse/QTBUG-45460')) - return Qt5Module() + return Qt5Module(*args, **kwargs) diff --git a/mesonbuild/modules/rpm.py b/mesonbuild/modules/rpm.py index dbb01f7..ba5bcaa 100644 --- a/mesonbuild/modules/rpm.py +++ b/mesonbuild/modules/rpm.py @@ -32,10 +32,17 @@ class RPMModule(ExtensionModule): def generate_spec_template(self, state, args, kwargs): compiler_deps = set() for compiler in state.compilers.values(): + # Elbrus has one 'lcc' package for every compiler if isinstance(compiler, compilers.GnuCCompiler): compiler_deps.add('gcc') elif isinstance(compiler, compilers.GnuCPPCompiler): compiler_deps.add('gcc-c++') + elif isinstance(compiler, compilers.ElbrusCCompiler): + compiler_deps.add('lcc') + elif isinstance(compiler, compilers.ElbrusCPPCompiler): + compiler_deps.add('lcc') + elif isinstance(compiler, compilers.ElbrusFortranCompiler): + compiler_deps.add('lcc') elif isinstance(compiler, compilers.ValaCompiler): compiler_deps.add('vala') elif isinstance(compiler, compilers.GnuFortranCompiler): @@ -160,5 +167,5 @@ class RPMModule(ExtensionModule): mlog.log('RPM spec template written to %s.spec.\n' % proj) return ModuleReturnValue(None, []) -def initialize(): - return RPMModule() +def initialize(*args, **kwargs): + return RPMModule(*args, **kwargs) diff --git a/mesonbuild/modules/unstable_icestorm.py b/mesonbuild/modules/unstable_icestorm.py index 1f548b6..55c647f 100644 --- a/mesonbuild/modules/unstable_icestorm.py +++ b/mesonbuild/modules/unstable_icestorm.py @@ -18,17 +18,17 @@ from . import ExtensionModule class IceStormModule(ExtensionModule): - def __init__(self): - super().__init__() + def __init__(self, interpreter): + super().__init__(interpreter) self.snippets.add('project') self.yosys_bin = None def detect_binaries(self, interpreter): - self.yosys_bin = interpreter.func_find_program(None, ['yosys'], {}) - self.arachne_bin = interpreter.func_find_program(None, ['arachne-pnr'], {}) - self.icepack_bin = interpreter.func_find_program(None, ['icepack'], {}) - self.iceprog_bin = interpreter.func_find_program(None, ['iceprog'], {}) - self.icetime_bin = interpreter.func_find_program(None, ['icetime'], {}) + self.yosys_bin = interpreter.find_program_impl(['yosys']) + self.arachne_bin = interpreter.find_program_impl(['arachne-pnr']) + self.icepack_bin = interpreter.find_program_impl(['icepack']) + self.iceprog_bin = interpreter.find_program_impl(['iceprog']) + self.icetime_bin = interpreter.find_program_impl(['icetime']) def project(self, interpreter, state, args, kwargs): if not self.yosys_bin: @@ -80,5 +80,5 @@ class IceStormModule(ExtensionModule): interpreter.func_run_target(None, [time_name], { 'command': [self.icetime_bin, bin_target]}) -def initialize(): - return IceStormModule() +def initialize(*args, **kwargs): + return IceStormModule(*args, **kwargs) diff --git a/mesonbuild/modules/unstable_simd.py b/mesonbuild/modules/unstable_simd.py index b774cff..c41e96c 100644 --- a/mesonbuild/modules/unstable_simd.py +++ b/mesonbuild/modules/unstable_simd.py @@ -18,8 +18,8 @@ from . import ExtensionModule class SimdModule(ExtensionModule): - def __init__(self): - super().__init__() + def __init__(self, interpreter): + super().__init__(interpreter) self.snippets.add('check') # FIXME add Altivec and AVX512. self.isets = ('mmx', @@ -79,5 +79,5 @@ class SimdModule(ExtensionModule): result.append(interpreter.func_static_lib(None, [libname], lib_kwargs)) return [result, cdata] -def initialize(): - return SimdModule() +def initialize(*args, **kwargs): + return SimdModule(*args, **kwargs) diff --git a/mesonbuild/modules/windows.py b/mesonbuild/modules/windows.py index dc6e9d8..62cb9d1 100644 --- a/mesonbuild/modules/windows.py +++ b/mesonbuild/modules/windows.py @@ -104,5 +104,5 @@ class WindowsModule(ExtensionModule): return ModuleReturnValue(res_targets, [res_targets]) -def initialize(): - return WindowsModule() +def initialize(*args, **kwargs): + return WindowsModule(*args, **kwargs) diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 0e7524c..9e43065 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -13,9 +13,44 @@ # limitations under the License. import re +import codecs from .mesonlib import MesonException from . import mlog +# This is the regex for the supported escape sequences of a regular string +# literal, like 'abc\x00' +ESCAPE_SEQUENCE_SINGLE_RE = re.compile(r''' + ( \\U........ # 8-digit hex escapes + | \\u.... # 4-digit hex escapes + | \\x.. # 2-digit hex escapes + | \\[0-7]{1,3} # Octal escapes + | \\N\{[^}]+\} # Unicode characters by name + | \\[\\'abfnrtv] # Single-character escapes + )''', re.UNICODE | re.VERBOSE) + +# This is the regex for the supported escape sequences of a multiline string +# literal, like '''abc\x00'''. The only difference is that single quote (') +# doesn't require escaping. +ESCAPE_SEQUENCE_MULTI_RE = re.compile(r''' + ( \\U........ # 8-digit hex escapes + | \\u.... # 4-digit hex escapes + | \\x.. # 2-digit hex escapes + | \\[0-7]{1,3} # Octal escapes + | \\N\{[^}]+\} # Unicode characters by name + | \\[\\abfnrtv] # Single-character escapes + )''', re.UNICODE | re.VERBOSE) + +class MesonUnicodeDecodeError(MesonException): + def __init__(self, match): + super().__init__("%s" % match) + self.match = match + +def decode_match(match): + try: + return codecs.decode(match.group(0), 'unicode_escape') + except UnicodeDecodeError as err: + raise MesonUnicodeDecodeError(match.group(0)) + class ParseException(MesonException): def __init__(self, text, line, lineno, colno): # Format as error message, followed by the line with the error, followed by a caret to show the error column. @@ -112,7 +147,6 @@ class Lexer: par_count = 0 bracket_count = 0 col = 0 - newline_rx = re.compile(r'(?<!\\)((?:\\\\)*)\\n') while loc < len(self.code): matched = False value = None @@ -145,12 +179,18 @@ class Lexer: if match_text.find("\n") != -1: mlog.warning("""Newline character in a string detected, use ''' (three single quotes) for multiline strings instead. This will become a hard error in a future Meson release.""", self.getline(line_start), lineno, col) - value = match_text[1:-1].replace(r"\'", "'") - value = newline_rx.sub(r'\1\n', value) - value = value.replace(r" \\ ".strip(), r" \ ".strip()) + value = match_text[1:-1] + try: + value = ESCAPE_SEQUENCE_SINGLE_RE.sub(decode_match, value) + except MesonUnicodeDecodeError as err: + raise MesonException("Failed to parse escape sequence: '{}' in string:\n {}".format(err.match, match_text)) elif tid == 'multiline_string': tid = 'string' value = match_text[3:-3] + try: + value = ESCAPE_SEQUENCE_MULTI_RE.sub(decode_match, value) + except MesonUnicodeDecodeError as err: + raise MesonException("Failed to parse escape sequence: '{}' in string:\n{}".format(err.match, match_text)) lines = match_text.split('\n') if len(lines) > 1: lineno += len(lines) - 1 diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 91567f2..110a94e 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -476,6 +476,25 @@ TIMEOUT: %4d (prj_match, st_match) = TestHarness.split_suite_string(suite) for prjst in test.suite: (prj, st) = TestHarness.split_suite_string(prjst) + + # the SUITE can be passed as + # suite_name + # or + # project_name:suite_name + # so we need to select only the test belonging to project_name + + # this if hanlde the first case (i.e., SUITE == suite_name) + + # in this way we can run tests belonging to different + # (sub)projects which share the same suite_name + if not st_match and st == prj_match: + return True + + # these two conditions are needed to handle the second option + # i.e., SUITE == project_name:suite_name + + # in this way we select the only the tests of + # project_name with suite_name if prj_match and prj != prj_match: continue if st_match and st != st_match: diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index ee63147..41ede1d 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -14,6 +14,7 @@ import sys, struct +import shutil, subprocess SHT_STRTAB = 3 DT_NEEDED = 1 @@ -337,20 +338,68 @@ class Elf(DataSizes): entry.write(self.bf) return None +def fix_elf(fname, new_rpath, verbose=True): + with Elf(fname, verbose) as e: + if new_rpath is None: + e.print_rpath() + e.print_runpath() + else: + e.fix_rpath(new_rpath) + +def get_darwin_rpaths_to_remove(fname): + out = subprocess.check_output(['otool', '-l', fname], universal_newlines=True) + result = [] + current_cmd = 'FOOBAR' + for line in out.split('\n'): + line = line.strip() + if ' ' not in line: + continue + key, value = line.strip().split(' ', 1) + if key == 'cmd': + current_cmd = value + if key == 'path' and current_cmd == 'LC_RPATH': + rp = value.split('(', 1)[0].strip() + result.append(rp) + return result + +def fix_darwin(fname, new_rpath): + try: + rpaths = get_darwin_rpaths_to_remove(fname) + except subprocess.CalledProcessError: + # Otool failed, which happens when invoked on a + # non-executable target. Just return. + return + try: + for rp in rpaths: + subprocess.check_call(['install_name_tool', '-delete_rpath', rp, fname]) + if new_rpath != '': + subprocess.check_call(['install_name_tool', '-add_rpath', new_rpath, fname]) + except Exception as e: + raise + sys.exit(0) + +def fix_rpath(fname, new_rpath, verbose=True): + try: + fix_elf(fname, new_rpath, verbose) + return 0 + except SystemExit as e: + if isinstance(e.code, int) and e.code == 0: + pass + else: + raise + if shutil.which('install_name_tool'): + fix_darwin(fname, new_rpath) + return 0 + def run(args): if len(args) < 1 or len(args) > 2: print('This application resets target rpath.') print('Don\'t run this unless you know what you are doing.') print('%s: <binary file> <prefix>' % sys.argv[0]) sys.exit(1) - with Elf(args[0]) as e: - if len(args) == 1: - e.print_rpath() - e.print_runpath() - else: - new_rpath = args[1] - e.fix_rpath(new_rpath) - return 0 + fname = args[0] + new_rpath = None if len(args) == 1 else args[1] + return fix_rpath(fname, new_rpath) if __name__ == '__main__': - run(sys.argv[1:]) + sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/scripts/meson_install.py b/mesonbuild/scripts/meson_install.py index 1414ace..013f2a0 100644 --- a/mesonbuild/scripts/meson_install.py +++ b/mesonbuild/scripts/meson_install.py @@ -291,12 +291,6 @@ def run_install_script(d): print('Failed to run install script {!r}'.format(name)) sys.exit(1) -def is_elf_platform(): - platname = platform.system().lower() - if platname == 'darwin' or platname == 'windows' or platname == 'cygwin': - return False - return True - def check_for_stampfile(fname): '''Some languages e.g. Rust have output files whose names are not known at configure time. @@ -372,10 +366,9 @@ def install_targets(d): print("Symlink creation does not work on this platform. " "Skipping all symlinking.") printed_symlink_error = True - if is_elf_platform() and os.path.isfile(outname): + if os.path.isfile(outname): try: - e = depfixer.Elf(outname, False) - e.fix_rpath(install_rpath) + depfixer.fix_rpath(outname, install_rpath, False) except SystemExit as e: if isinstance(e.code, int) and e.code == 0: pass diff --git a/mesonbuild/wrap/__init__.py b/mesonbuild/wrap/__init__.py index 019634c..6e2bc83 100644 --- a/mesonbuild/wrap/__init__.py +++ b/mesonbuild/wrap/__init__.py @@ -25,7 +25,12 @@ from enum import Enum # to use 'nofallback' so that any 'copylib' wraps will be # download as subprojects. # +# --wrap-mode=forcefallback will ignore external dependencies, +# even if they match the version requirements, and automatically +# use the fallback if one was provided. This is useful for example +# to make sure a project builds when using the fallbacks. +# # Note that these options do not affect subprojects that # are git submodules since those are only usable in git # repositories, and you almost always want to download them. -WrapMode = Enum('WrapMode', 'default nofallback nodownload') +WrapMode = Enum('WrapMode', 'default nofallback nodownload forcefallback') diff --git a/run_unittests.py b/run_unittests.py index 879048b..a6be2e2 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -42,6 +42,7 @@ from mesonbuild.mesonlib import ( from mesonbuild.environment import Environment, detect_ninja from mesonbuild.mesonlib import MesonException, EnvironmentException from mesonbuild.dependencies import PkgConfigDependency, ExternalProgram +import mesonbuild.modules.pkgconfig from run_tests import exe_suffix, get_fake_options from run_tests import get_builddir_target_args, get_backend_commands, Backend @@ -198,6 +199,12 @@ class InternalTests(unittest.TestCase): # Direct-adding the same library again still adds it l.append_direct('-lbar') self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar']) + # Direct-adding with absolute path deduplicates + l.append_direct('/libbaz.a') + self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a']) + # Adding libbaz again does nothing + l.append_direct('/libbaz.a') + self.assertEqual(l, ['-Lfoodir', '-lfoo', '-Lbardir', '-lbar', '-lbar', '/libbaz.a']) def test_string_templates_substitution(self): dictfunc = mesonbuild.mesonlib.get_filenames_templates_dict @@ -448,6 +455,19 @@ class InternalTests(unittest.TestCase): if f.name != 'add_release_note_snippets_here': self.assertTrue(False, 'A file without .md suffix in snippets dir: ' + f.name) + def test_pkgconfig_module(self): + deps = mesonbuild.modules.pkgconfig.DependenciesHelper("thislib") + + class Mock: + pass + + mock = Mock() + mock.pcdep = Mock() + mock.pcdep.name = "some_name" + mock.version_reqs = [] + deps.add_pub_libs([mock]) + self.assertEqual(deps.format_reqs(deps.pub_reqs), "some_name") + class BasePlatformTests(unittest.TestCase): def setUp(self): @@ -462,6 +482,7 @@ class BasePlatformTests(unittest.TestCase): self.backend = getattr(Backend, os.environ.get('MESON_UNIT_TEST_BACKEND', 'ninja')) self.meson_mainfile = os.path.join(src_root, 'meson.py') self.meson_args = ['--backend=' + self.backend.name] + self.meson_cross_file = None self.meson_command = meson_command + self.meson_args self.mconf_command = meson_command + ['configure'] self.mintro_command = meson_command + ['introspect'] @@ -547,6 +568,8 @@ class BasePlatformTests(unittest.TestCase): if default_args: args += ['--prefix', self.prefix, '--libdir', self.libdir] + if self.meson_cross_file: + args += ['--cross-file', self.meson_cross_file] self.privatedir = os.path.join(self.builddir, 'meson-private') if inprocess: try: @@ -950,6 +973,12 @@ class AllPlatformTests(BasePlatformTests): self.uninstall() self.assertPathDoesNotExist(exename) + def test_forcefallback(self): + testdir = os.path.join(self.unit_test_dir, '27 forcefallback') + self.init(testdir, ['--wrap-mode=forcefallback']) + self.build() + self.run_tests() + def test_testsetups(self): if not shutil.which('valgrind'): raise unittest.SkipTest('Valgrind not installed.') @@ -1089,7 +1118,7 @@ class AllPlatformTests(BasePlatformTests): incs = [a for a in shlex.split(execmd) if a.startswith("-I")] self.assertEqual(len(incs), 9) # target private dir - self.assertPathEqual(incs[0], "-Isub4/someexe@exe") + self.assertPathEqual(incs[0], "-Isub4/sub4@@someexe@exe") # target build subdir self.assertPathEqual(incs[1], "-Isub4") # target source subdir @@ -1321,7 +1350,8 @@ class AllPlatformTests(BasePlatformTests): # \n is never substituted by the GNU pre-processor via a -D define # ' and " confuse shlex.split() even when they are escaped # % and # confuse the MSVC preprocessor - value = 'spaces and fun!@$^&*()-=_+{}[]:;<>?,./~`' + # !, ^, *, and < confuse lcc preprocessor + value = 'spaces and fun@$&()-=_+{}[]:;>?,./~`' os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value) os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define) self.init(testdir, ['-D{}={}'.format(define, value)]) @@ -1888,6 +1918,15 @@ int main(int argc, char **argv) { self.init(testdir, extra_args=['--layout=flat']) self.build() + def test_identical_target_name_in_subdir_flat_layout(self): + ''' + Test that identical targets in different subdirs do not collide + if layout is flat. + ''' + testdir = os.path.join(self.common_test_dir, '189 same target name flat layout') + self.init(testdir, extra_args=['--layout=flat']) + self.build() + def test_flock(self): exception_raised = False with tempfile.TemporaryDirectory() as tdir: @@ -1924,6 +1963,55 @@ recommended as it can lead to undefined behaviour on some platforms''') exe = os.path.join(self.builddir, 'main') self.assertEqual(b'NDEBUG=0', subprocess.check_output(exe).strip()) + def test_guessed_linker_dependencies(self): + ''' + Test that meson adds dependencies for libraries based on the final + linker command line. + ''' + # build library + testdirbase = os.path.join(self.unit_test_dir, '26 guessed linker dependencies') + testdirlib = os.path.join(testdirbase, 'lib') + extra_args = None + env = Environment(testdirlib, self.builddir, self.meson_command, + get_fake_options(self.prefix), []) + if env.detect_c_compiler(False).get_id() != 'msvc': + # static libraries are not linkable with -l with msvc because meson installs them + # as .a files which unix_args_to_native will not know as it expects libraries to use + # .lib as extension. For a DLL the import library is installed as .lib. Thus for msvc + # this tests needs to use shared libraries to test the path resolving logic in the + # dependency generation code path. + extra_args = ['--default-library', 'static'] + self.init(testdirlib, extra_args=extra_args) + self.build() + self.install() + libbuilddir = self.builddir + installdir = self.installdir + libdir = os.path.join(self.installdir, self.prefix.lstrip('/').lstrip('\\'), 'lib') + + # build user of library + self.new_builddir() + # replace is needed because meson mangles platform pathes passed via LDFLAGS + os.environ["LDFLAGS"] = '-L{}'.format(libdir.replace('\\', '/')) + self.init(os.path.join(testdirbase, 'exe')) + del os.environ["LDFLAGS"] + self.build() + self.assertBuildIsNoop() + + # rebuild library + exebuilddir = self.builddir + self.installdir = installdir + self.builddir = libbuilddir + # Microsoft's compiler is quite smart about touching import libs on changes, + # so ensure that there is actually a change in symbols. + self.setconf('-Dmore_exports=true') + self.build() + self.install() + # no ensure_backend_detects_changes needed because self.setconf did that already + + # assert user of library will be rebuild + self.builddir = exebuilddir + self.assertRebuiltTarget('app') + class FailureTests(BasePlatformTests): ''' @@ -2523,8 +2611,8 @@ class LinuxlikeTests(BasePlatformTests): def test_unity_subproj(self): testdir = os.path.join(self.common_test_dir, '49 subproject') self.init(testdir, extra_args='--unity=subprojects') - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/sublib@@simpletest@exe/simpletest-unity.c')) - self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/sublib@@sublib@sha/sublib-unity.c')) + self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/subprojects@sublib@@simpletest@exe/simpletest-unity.c')) + self.assertPathExists(os.path.join(self.builddir, 'subprojects/sublib/subprojects@sublib@@sublib@sha/sublib-unity.c')) self.assertPathDoesNotExist(os.path.join(self.builddir, 'user@exe/user-unity.c')) self.build() @@ -2739,11 +2827,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.meson_cross_file = crossfile.name + self.init(testdir) def test_reconfigure(self): testdir = os.path.join(self.unit_test_dir, '13 reconfigure') - self.init(testdir, ['-Db_lto=true'], default_args=False) + self.init(testdir, ['-Db_coverage=true'], default_args=False) self.build('reconfigure') def test_vala_generated_source_buildir_inside_source_tree(self): @@ -2816,6 +2905,27 @@ endian = 'little' self.assertTrue(os.path.isfile(test_exe)) subprocess.check_call(test_exe, env=myenv) + @unittest.skipIf(shutil.which('pkg-config') is None, 'Pkg-config not found.') + def test_pkgconfig_internal_libraries(self): + ''' + ''' + with tempfile.TemporaryDirectory() as tempdirname: + # build library + testdirbase = os.path.join(self.unit_test_dir, '28 pkgconfig use libraries') + testdirlib = os.path.join(testdirbase, 'lib') + self.init(testdirlib, extra_args=['--prefix=' + tempdirname, + '--libdir=lib', + '--default-library=static'], default_args=False) + self.build() + self.install(use_destdir=False) + + # build user of library + pkg_dir = os.path.join(tempdirname, 'lib/pkgconfig') + os.environ['PKG_CONFIG_PATH'] = pkg_dir + self.new_builddir() + self.init(os.path.join(testdirbase, 'app')) + self.build() + class LinuxArmCrossCompileTests(BasePlatformTests): ''' @@ -2824,7 +2934,7 @@ class LinuxArmCrossCompileTests(BasePlatformTests): def setUp(self): super().setUp() src_root = os.path.dirname(__file__) - self.meson_command += ['--cross=' + os.path.join(src_root, 'cross', 'ubuntu-armhf.txt')] + self.meson_cross_file = os.path.join(src_root, 'cross', 'ubuntu-armhf.txt') def test_cflags_cross_environment_pollution(self): ''' @@ -2838,6 +2948,21 @@ class LinuxArmCrossCompileTests(BasePlatformTests): compdb = self.get_compdb() self.assertNotIn('-DBUILD_ENVIRONMENT_ONLY', compdb[0]['command']) + def test_cross_file_overrides_always_args(self): + ''' + Test that $lang_args in cross files always override get_always_args(). + Needed for overriding the default -D_FILE_OFFSET_BITS=64 on some + architectures such as some Android versions and Raspbian. + https://github.com/mesonbuild/meson/issues/3049 + https://github.com/mesonbuild/meson/issues/3089 + ''' + testdir = os.path.join(self.unit_test_dir, '29 cross file overrides always args') + self.meson_cross_file = os.path.join(self.unit_test_dir, 'ubuntu-armhf-overrides.txt') + self.init(testdir) + compdb = self.get_compdb() + self.assertRegex(compdb[0]['command'], '-D_FILE_OFFSET_BITS=64.*-U_FILE_OFFSET_BITS') + self.build() + class PythonTests(BasePlatformTests): ''' diff --git a/test cases/common/112 has arg/meson.build b/test cases/common/112 has arg/meson.build index 27290a1..ba07311 100644 --- a/test cases/common/112 has arg/meson.build +++ b/test cases/common/112 has arg/meson.build @@ -39,11 +39,17 @@ assert(l2.length() == 0, 'First supported did not return empty array.') if cc.get_id() == 'gcc' pre_arg = '-Wformat' - anti_pre_arg = '-Wno-format' + # NOTE: We have special handling for -Wno-foo args because gcc silently + # ignores unknown -Wno-foo args unless you pass -Werror, so for this test, we + # pass it as two separate arguments. + anti_pre_arg = ['-W', 'no-format'] arg = '-Werror=format-security' assert(not cc.has_multi_arguments([anti_pre_arg, arg]), 'Arg that should be broken is not.') assert(cc.has_multi_arguments(pre_arg), 'Arg that should have worked does not work.') assert(cc.has_multi_arguments([pre_arg, arg]), 'Arg that should have worked does not work.') + # Test that gcc correctly errors out on unknown -Wno flags + assert(not cc.has_argument('-Wno-lol-meson-test-flags'), 'should error out on unknown -Wno args') + assert(not cc.has_multi_arguments(['-Wno-pragmas', '-Wno-lol-meson-test-flags']), 'should error out even if some -Wno args are valid') endif if cc.get_id() == 'clang' and cc.version().version_compare('<=4.0.0') diff --git a/test cases/common/13 pch/meson.build b/test cases/common/13 pch/meson.build index 9ed6512..e144aa5 100644 --- a/test cases/common/13 pch/meson.build +++ b/test cases/common/13 pch/meson.build @@ -1,4 +1,10 @@ project('pch test', 'c') +cc = meson.get_compiler('c') +cc_id = cc.get_id() +if cc_id == 'lcc' + error('MESON_SKIP_TEST: Elbrus compiler does not support PCH.') +endif + exe = executable('prog', 'prog.c', c_pch : ['pch/prog_pch.c', 'pch/prog.h']) diff --git a/test cases/common/132 dependency file generation/meson.build b/test cases/common/132 dependency file generation/meson.build index dcfdcd9..cd66cb7 100644 --- a/test cases/common/132 dependency file generation/meson.build +++ b/test cases/common/132 dependency file generation/meson.build @@ -1,11 +1,13 @@ project('dep file gen', 'c') -cc_id = meson.get_compiler('c').get_id() -if cc_id == 'intel' - # ICC does not escape spaces in paths in the dependency file, so Ninja +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja # (correctly) thinks that the rule has multiple outputs and errors out: # 'depfile has multiple output paths' - error('MESON_SKIP_TEST: Skipping test with Intel compiler because it generates broken dependency files') + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') endif e = executable('main file', 'main .c') diff --git a/test cases/common/142 compute int/config.h.in b/test cases/common/142 compute int/config.h.in index ad8d077..0de63ab 100644 --- a/test cases/common/142 compute int/config.h.in +++ b/test cases/common/142 compute int/config.h.in @@ -1,2 +1,4 @@ #define INTSIZE @INTSIZE@ #define FOOBAR_IN_CONFIG_H @FOOBAR@ +#define MAXINT @MAXINT@ +#define MININT @MININT@ diff --git a/test cases/common/142 compute int/meson.build b/test cases/common/142 compute int/meson.build index 43553fe..22bd266 100644 --- a/test cases/common/142 compute int/meson.build +++ b/test cases/common/142 compute int/meson.build @@ -7,11 +7,15 @@ cc = meson.get_compiler('c') intsize = cc.compute_int('sizeof(int)', low : 1, high : 16, guess : 4) foobar = cc.compute_int('FOOBAR_IN_FOOBAR_H', prefix : '#include "foobar.h"', include_directories : inc) +maxint = cc.compute_int('INT_MAX', prefix: '#include <limits.h>') +minint = cc.compute_int('INT_MIN', prefix: '#include <limits.h>') cd = configuration_data() cd.set('INTSIZE', intsize) cd.set('FOOBAR', foobar) cd.set('CONFIG', 'config.h') +cd.set('MAXINT', maxint) +cd.set('MININT', minint) configure_file(input : 'config.h.in', output : 'config.h', configuration : cd) s = configure_file(input : 'prog.c.in', output : 'prog.c', configuration : cd) @@ -23,11 +27,15 @@ cpp = meson.get_compiler('cpp') intsize = cpp.compute_int('sizeof(int)') foobar = cpp.compute_int('FOOBAR_IN_FOOBAR_H', prefix : '#include "foobar.h"', include_directories : inc) +maxint = cpp.compute_int('INT_MAX', prefix: '#include <limits.h>') +minint = cpp.compute_int('INT_MIN', prefix: '#include <limits.h>') cdpp = configuration_data() cdpp.set('INTSIZE', intsize) cdpp.set('FOOBAR', foobar) cdpp.set('CONFIG', 'config.hpp') +cdpp.set('MAXINT', maxint) +cdpp.set('MININT', minint) configure_file(input : 'config.h.in', output : 'config.hpp', configuration : cdpp) spp = configure_file(input : 'prog.c.in', output : 'prog.cc', configuration : cdpp) diff --git a/test cases/common/142 compute int/prog.c.in b/test cases/common/142 compute int/prog.c.in index 3ff1463..ff1ad55 100644 --- a/test cases/common/142 compute int/prog.c.in +++ b/test cases/common/142 compute int/prog.c.in @@ -1,6 +1,7 @@ #include "@CONFIG@" #include <stdio.h> #include <wchar.h> +#include <limits.h> #include "foobar.h" int main(int argc, char **argv) { @@ -12,5 +13,13 @@ int main(int argc, char **argv) { fprintf(stderr, "Mismatch: computed int %d, should be %d.\n", FOOBAR_IN_CONFIG_H, FOOBAR_IN_FOOBAR_H); return 1; } + if(MAXINT != INT_MAX) { + fprintf(stderr, "Mismatch: computed max int %d, should be %d.\n", MAXINT, INT_MAX); + return 1; + } + if(MININT != INT_MIN) { + fprintf(stderr, "Mismatch: computed min int %d, should be %d.\n", MININT, INT_MIN); + return 1; + } return 0; } diff --git a/test cases/common/16 configure file/config7.h.in b/test cases/common/16 configure file/config7.h.in new file mode 100644 index 0000000..edd0bb3 --- /dev/null +++ b/test cases/common/16 configure file/config7.h.in @@ -0,0 +1,16 @@ +/* No escape */ +#define MESSAGE1 "${var1}" + +/* Single escape means no replace */ +#define MESSAGE2 "\${var1}" + +/* Replace pairs of escapes before '@' or '\@' with escape characters + * (note we have to double number of pairs due to C string escaping) + */ +#define MESSAGE3 "\\\\${var1}" + +/* Pairs of escapes and then single escape to avoid replace */ +#define MESSAGE4 "\\\\\${var1}" + +/* Check escape character outside variables */ +#define MESSAGE5 "\\ ${ \${ \\\\${ \\\\\${" diff --git a/test cases/common/16 configure file/meson.build b/test cases/common/16 configure file/meson.build index 71a2563..5c3a1a5 100644 --- a/test cases/common/16 configure file/meson.build +++ b/test cases/common/16 configure file/meson.build @@ -137,3 +137,15 @@ cfile = configure_file(input : 'config.h.in', output : 'do_not_get_installed.h', install_dir : '', configuration : conf) + +# Test escaping with cmake format +conf7 = configuration_data() +conf7.set('var1', 'foo') +conf7.set('var2', 'bar') +configure_file( + input : 'config7.h.in', + output : '@BASENAME@', + format : 'cmake', + configuration : conf7 +) +test('test7', executable('prog7', 'prog7.c')) diff --git a/test cases/common/16 configure file/prog7.c b/test cases/common/16 configure file/prog7.c new file mode 100644 index 0000000..0bb7d13 --- /dev/null +++ b/test cases/common/16 configure file/prog7.c @@ -0,0 +1,10 @@ +#include <string.h> +#include <config7.h> + +int main(int argc, char **argv) { + return strcmp(MESSAGE1, "foo") + || strcmp(MESSAGE2, "${var1}") + || strcmp(MESSAGE3, "\\foo") + || strcmp(MESSAGE4, "\\${var1}") + || strcmp(MESSAGE5, "\\ ${ ${ \\${ \\${"); +} diff --git a/test cases/common/168 disabler/meson.build b/test cases/common/168 disabler/meson.build index 7ca82b7..1956cd3 100644 --- a/test cases/common/168 disabler/meson.build +++ b/test cases/common/168 disabler/meson.build @@ -21,7 +21,7 @@ else number = 2 endif -assert(d == 0, 'Plain if handled incorrectly, value should be 0 but is @0@'.format(number)) +assert(number == 0, 'Plain if handled incorrectly, value should be 0 but is @0@'.format(number)) if d.found() number = 1 @@ -29,6 +29,6 @@ else number = 2 endif -assert(d == 1, 'If found handled incorrectly, value should be 1 but is @0@'.format(number)) +assert(number == 2, 'If found handled incorrectly, value should be 2 but is @0@'.format(number)) diff --git a/test cases/failing/17 same name/file.c b/test cases/common/183 same target name/file.c index 6f1c172..6f1c172 100644 --- a/test cases/failing/17 same name/file.c +++ b/test cases/common/183 same target name/file.c diff --git a/test cases/failing/17 same name/meson.build b/test cases/common/183 same target name/meson.build index 4e585d5..4e585d5 100644 --- a/test cases/failing/17 same name/meson.build +++ b/test cases/common/183 same target name/meson.build diff --git a/test cases/failing/17 same name/sub/file2.c b/test cases/common/183 same target name/sub/file2.c index a5e453d..a5e453d 100644 --- a/test cases/failing/17 same name/sub/file2.c +++ b/test cases/common/183 same target name/sub/file2.c diff --git a/test cases/failing/17 same name/sub/meson.build b/test cases/common/183 same target name/sub/meson.build index 610a4a3..610a4a3 100644 --- a/test cases/failing/17 same name/sub/meson.build +++ b/test cases/common/183 same target name/sub/meson.build diff --git a/test cases/common/189 same target name flat layout/foo.c b/test cases/common/189 same target name flat layout/foo.c new file mode 100644 index 0000000..ed42789 --- /dev/null +++ b/test cases/common/189 same target name flat layout/foo.c @@ -0,0 +1 @@ +int meson_test_main_foo(void) { return 10; } diff --git a/test cases/common/189 same target name flat layout/main.c b/test cases/common/189 same target name flat layout/main.c new file mode 100644 index 0000000..6f02aeb --- /dev/null +++ b/test cases/common/189 same target name flat layout/main.c @@ -0,0 +1,16 @@ +#include <stdio.h> + +int meson_test_main_foo(void); +int meson_test_subproj_foo(void); + +int main(void) { + if (meson_test_main_foo() != 10) { + printf("Failed meson_test_main_foo\n"); + return 1; + } + if (meson_test_subproj_foo() != 20) { + printf("Failed meson_test_subproj_foo\n"); + return 1; + } + return 0; +} diff --git a/test cases/common/189 same target name flat layout/meson.build b/test cases/common/189 same target name flat layout/meson.build new file mode 100644 index 0000000..a3c95fa --- /dev/null +++ b/test cases/common/189 same target name flat layout/meson.build @@ -0,0 +1,11 @@ +project('subdir targets', 'c') + +# Idea behind this test is to create targets with identical name +# but different output files. We can do this by choosing different +# name_prefix of libraries. Target id does not depend on name_prefix. + +main_foo = static_library('foo', 'foo.c', name_prefix : 'main') +subdir('subdir') # defines subdir_foo + +exe = executable('prog', 'main.c', link_with : [main_foo, subdir_foo]) +test('main test', exe) diff --git a/test cases/common/189 same target name flat layout/subdir/foo.c b/test cases/common/189 same target name flat layout/subdir/foo.c new file mode 100644 index 0000000..f334292 --- /dev/null +++ b/test cases/common/189 same target name flat layout/subdir/foo.c @@ -0,0 +1 @@ +int meson_test_subproj_foo(void) { return 20; } diff --git a/test cases/common/189 same target name flat layout/subdir/meson.build b/test cases/common/189 same target name flat layout/subdir/meson.build new file mode 100644 index 0000000..223a5ef --- /dev/null +++ b/test cases/common/189 same target name flat layout/subdir/meson.build @@ -0,0 +1 @@ +subdir_foo = static_library('foo', 'foo.c', name_prefix : 'subdir') diff --git a/test cases/common/190 escape and unicode/file.c.in b/test cases/common/190 escape and unicode/file.c.in new file mode 100644 index 0000000..413ed42 --- /dev/null +++ b/test cases/common/190 escape and unicode/file.c.in @@ -0,0 +1,5 @@ +#include<stdio.h> +const char* does_it_work() { + printf("{NAME}\n"); + return "yes it does"; +} diff --git a/test cases/common/190 escape and unicode/file.py b/test cases/common/190 escape and unicode/file.py new file mode 100644 index 0000000..af67a09 --- /dev/null +++ b/test cases/common/190 escape and unicode/file.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import sys +import os + +with open(sys.argv[1]) as fh: + content = fh.read().replace("{NAME}", sys.argv[2]) + +with open(os.path.join(sys.argv[3]), 'w') as fh: + fh.write(content) diff --git a/test cases/common/190 escape and unicode/find.py b/test cases/common/190 escape and unicode/find.py new file mode 100644 index 0000000..34a3eb8 --- /dev/null +++ b/test cases/common/190 escape and unicode/find.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python3 + +import os +import sys + +for fh in os.listdir('.'): + if os.path.isfile(fh): + if fh.endswith('.c'): + sys.stdout.write(fh + '\0') diff --git a/test cases/common/190 escape and unicode/fun.c b/test cases/common/190 escape and unicode/fun.c new file mode 100644 index 0000000..8eeb8ea --- /dev/null +++ b/test cases/common/190 escape and unicode/fun.c @@ -0,0 +1,3 @@ +int a_fun() { + return 1; +} diff --git a/test cases/common/190 escape and unicode/main.c b/test cases/common/190 escape and unicode/main.c new file mode 100644 index 0000000..0bcde16 --- /dev/null +++ b/test cases/common/190 escape and unicode/main.c @@ -0,0 +1,12 @@ +#include <string.h> + +const char* does_it_work(); + +int a_fun(); + +int main() { + if(strcmp(does_it_work(), "yes it does") != 0) { + return -a_fun(); + } + return 0; +} diff --git a/test cases/common/190 escape and unicode/meson.build b/test cases/common/190 escape and unicode/meson.build new file mode 100644 index 0000000..65377b6 --- /dev/null +++ b/test cases/common/190 escape and unicode/meson.build @@ -0,0 +1,24 @@ +project('180 escape', 'c') + +gen = generator(find_program('file.py'), arguments:['@INPUT@', 'erd\u0151', '@OUTPUT@'], output: '@BASENAME@') + +gen_file = gen.process('file.c.in') + +find_file_list = run_command(find_program('find.py')) +assert(find_file_list.returncode() == 0, 'Didn\'t find any files.') + +# Strings should support both octal \ooo and hex \xhh encodings + +found_files_oct = [] +foreach l : find_file_list.stdout().strip('\0').split('\000') + found_files_oct += [files(l)] +endforeach + +test('first', executable('first', found_files_oct + [gen_file])) + +found_files_hex = [] +foreach l : find_file_list.stdout().strip('\x00').split('\x00') + found_files_hex += [files(l)] +endforeach + +test('second', executable('second', found_files_hex + [gen_file])) diff --git a/test cases/common/190 find override/meson.build b/test cases/common/190 find override/meson.build new file mode 100644 index 0000000..3b8af80 --- /dev/null +++ b/test cases/common/190 find override/meson.build @@ -0,0 +1,12 @@ +project('find program override', 'c') + +gencodegen = find_program('gencodegen', required : false) + +assert(not gencodegen.found(), 'gencodegen is an internal program, should not be found') + +# Test the check-if-found-else-override workflow +if not gencodegen.found() + subdir('subdir') +endif + +subdir('otherdir') diff --git a/test cases/common/190 find override/otherdir/main.c b/test cases/common/190 find override/otherdir/main.c new file mode 100644 index 0000000..2cef67c --- /dev/null +++ b/test cases/common/190 find override/otherdir/main.c @@ -0,0 +1,5 @@ +int be_seeing_you(); + +int main(int argc, char **argv) { + return be_seeing_you() == 6 ? 0 : 1; +} diff --git a/test cases/common/190 find override/otherdir/main2.c b/test cases/common/190 find override/otherdir/main2.c new file mode 100644 index 0000000..6d71688 --- /dev/null +++ b/test cases/common/190 find override/otherdir/main2.c @@ -0,0 +1,5 @@ +int number_returner(); + +int main(int argc, char **argv) { + return number_returner() == 100 ? 0 : 1; +} diff --git a/test cases/common/190 find override/otherdir/meson.build b/test cases/common/190 find override/otherdir/meson.build new file mode 100644 index 0000000..dc41f5b --- /dev/null +++ b/test cases/common/190 find override/otherdir/meson.build @@ -0,0 +1,26 @@ +gen = find_program('codegen') # Should use overridden value set in "subdir". + +src = custom_target('arrival', + input : 'source.desc', + output : 'file.c', + command : [gen, '@INPUT@', '@OUTPUT@'] + ) + +e = executable('six', 'main.c', src) + +test('six', e) + +# The same again, but this time with a program that was genererated +# with configure_file. + +gen = find_program('gencodegen') + +src = custom_target('hundred', + input : 'source2.desc', + output : 'file2.c', + command : [gen, '@INPUT@', '@OUTPUT@'] + ) + +e = executable('hundred', 'main2.c', src) + +test('hundred', e) diff --git a/test cases/common/190 find override/otherdir/source.desc b/test cases/common/190 find override/otherdir/source.desc new file mode 100644 index 0000000..8b19c9c --- /dev/null +++ b/test cases/common/190 find override/otherdir/source.desc @@ -0,0 +1 @@ +be_seeing_you diff --git a/test cases/common/190 find override/otherdir/source2.desc b/test cases/common/190 find override/otherdir/source2.desc new file mode 100644 index 0000000..965f868 --- /dev/null +++ b/test cases/common/190 find override/otherdir/source2.desc @@ -0,0 +1 @@ +number_returner diff --git a/test cases/common/190 find override/subdir/converter.py b/test cases/common/190 find override/subdir/converter.py new file mode 100755 index 0000000..ee2ff85 --- /dev/null +++ b/test cases/common/190 find override/subdir/converter.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import sys +import pathlib + +[ifilename, ofilename] = sys.argv[1:3] + +ftempl = '''int %s() { + return 6; +} +''' + +d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() + +pathlib.Path(ofilename).write_text(ftempl % d) diff --git a/test cases/common/190 find override/subdir/gencodegen.py.in b/test cases/common/190 find override/subdir/gencodegen.py.in new file mode 100755 index 0000000..57d9c40 --- /dev/null +++ b/test cases/common/190 find override/subdir/gencodegen.py.in @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +import sys +import pathlib + +[ifilename, ofilename] = sys.argv[1:3] + +ftempl = '''int %s() { + return @NUMBER@; +} +''' + +d = pathlib.Path(ifilename).read_text().split('\n')[0].strip() + +pathlib.Path(ofilename).write_text(ftempl % d) diff --git a/test cases/common/190 find override/subdir/meson.build b/test cases/common/190 find override/subdir/meson.build new file mode 100644 index 0000000..e5de34d --- /dev/null +++ b/test cases/common/190 find override/subdir/meson.build @@ -0,0 +1,14 @@ +x = find_program('converter.py') + +meson.override_find_program('codegen', x) + +# Override a command with a generated script + +cdata = configuration_data() + +cdata.set('NUMBER', 100) +numprog = configure_file(input : 'gencodegen.py.in', + output : 'gencodegen.py', + configuration : cdata) + +meson.override_find_program('gencodegen', numprog) diff --git a/test cases/common/190 openmp/main.c b/test cases/common/190 openmp/main.c new file mode 100644 index 0000000..cc81f48 --- /dev/null +++ b/test cases/common/190 openmp/main.c @@ -0,0 +1,16 @@ +#include <stdio.h> +#include <omp.h> + +int main(void) { +#ifdef _OPENMP + if (omp_get_max_threads() == 2) { + return 0; + } else { + printf("Max threads is %d not 2.\n", omp_get_max_threads()); + return 1; + } +#else + printf("_OPENMP is not defined; is OpenMP compilation working?\n"); + return 1; +#endif +} diff --git a/test cases/common/190 openmp/main.cpp b/test cases/common/190 openmp/main.cpp new file mode 100644 index 0000000..b12be3f --- /dev/null +++ b/test cases/common/190 openmp/main.cpp @@ -0,0 +1,16 @@ +#include <iostream> +#include <omp.h> + +int main(void) { +#ifdef _OPENMP + if (omp_get_max_threads() == 2) { + return 0; + } else { + std::cout << "Max threads is " << omp_get_max_threads() << " not 2." << std::endl; + return 1; + } +#else + printf("_OPENMP is not defined; is OpenMP compilation working?\n"); + return 1; +#endif +} diff --git a/test cases/common/190 openmp/main.f90 b/test cases/common/190 openmp/main.f90 new file mode 100644 index 0000000..c062d86 --- /dev/null +++ b/test cases/common/190 openmp/main.f90 @@ -0,0 +1,8 @@ +program main + if (omp_get_max_threads() .eq. 2) then + stop 0 + else + print *, 'Max threads is', omp_get_max_threads(), 'not 2.' + stop 1 + endif +end program main diff --git a/test cases/common/190 openmp/meson.build b/test cases/common/190 openmp/meson.build new file mode 100644 index 0000000..a05ca59 --- /dev/null +++ b/test cases/common/190 openmp/meson.build @@ -0,0 +1,40 @@ +project('openmp', 'c', 'cpp') + +cc = meson.get_compiler('c') +if cc.get_id() == 'gcc' and cc.version().version_compare('<4.2.0') + error('MESON_SKIP_TEST gcc is too old to support OpenMP.') +endif +if cc.get_id() == 'clang' and cc.version().version_compare('<3.7.0') + error('MESON_SKIP_TEST clang is too old to support OpenMP.') +endif +if cc.get_id() == 'msvc' and cc.version().version_compare('<17') + error('MESON_SKIP_TEST msvc is too old to support OpenMP.') +endif +if host_machine.system() == 'darwin' + error('MESON_SKIP_TEST macOS does not support OpenMP.') +endif + +openmp = dependency('openmp') + +exec = executable('exec', + 'main.c', + dependencies : [openmp]) + +execpp = executable('execpp', + 'main.cpp', + dependencies : [openmp]) + +env = environment() +env.set('OMP_NUM_THREADS', '2') + +test('OpenMP C', exec, env : env) +test('OpenMP C++', execpp, env : env) + + +if add_languages('fortran', required : false) + exef = executable('exef', + 'main.f90', + dependencies : [openmp]) + + test('OpenMP Fortran', execpp, env : env) +endif diff --git a/test cases/common/22 header in file list/meson.build b/test cases/common/22 header in file list/meson.build index cc30c71..ff42cc4 100644 --- a/test cases/common/22 header in file list/meson.build +++ b/test cases/common/22 header in file list/meson.build @@ -1,4 +1,14 @@ project('header in file list', 'c') +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja + # (correctly) thinks that the rule has multiple outputs and errors out: + # 'depfile has multiple output paths' + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') +endif + exe = executable('prog', 'prog.c', 'header.h') test('basic', exe) diff --git a/test cases/common/33 try compile/meson.build b/test cases/common/33 try compile/meson.build index 09ca395..cb1037d 100644 --- a/test cases/common/33 try compile/meson.build +++ b/test cases/common/33 try compile/meson.build @@ -1,11 +1,11 @@ project('try compile', 'c', 'cpp') code = '''#include<stdio.h> -void func() { printf("Something.\n"); } +void func() { printf("Something.\\n"); } ''' breakcode = '''#include<nonexisting.h> -void func() { printf("This won't work.\n"); } +void func() { printf("This won't work.\\n"); } ''' foreach compiler : [meson.get_compiler('c'), meson.get_compiler('cpp')] diff --git a/test cases/common/39 tryrun/meson.build b/test cases/common/39 tryrun/meson.build index c64446f..daf5be7 100644 --- a/test cases/common/39 tryrun/meson.build +++ b/test cases/common/39 tryrun/meson.build @@ -13,8 +13,8 @@ endif ok_code = '''#include<stdio.h> int main(int argc, char **argv) { - printf("%s\n", "stdout"); - fprintf(stderr, "%s\n", "stderr"); + printf("%s\\n", "stdout"); + fprintf(stderr, "%s\\n", "stderr"); return 0; } ''' diff --git a/test cases/common/42 string operations/meson.build b/test cases/common/42 string operations/meson.build index a43de70..1c289eb 100644 --- a/test cases/common/42 string operations/meson.build +++ b/test cases/common/42 string operations/meson.build @@ -77,21 +77,21 @@ assert('"1.1.20"'.strip('"') == '1.1.20', '" badly stripped') assert('"1.1.20"'.strip('".') == '1.1.20', '". badly stripped') assert('"1.1.20" '.strip('" ') == '1.1.20', '". badly stripped') -bs_b = '''\b''' -bs_bs_b = '''\\b''' +bs_c = '''\c''' +bs_bs_c = '''\\\c''' nl = ''' ''' -bs_n = '''\n''' +bs_n = '''\\n''' bs_nl = '''\ ''' -bs_bs_n = '''\\n''' -bs_bs_nl = '''\\ +bs_bs_n = '''\\\\n''' +bs_bs_nl = '''\\\\ ''' -assert('\b' == bs_b, 'Single backslash broken') -assert('\\b' == bs_b, 'Double backslash broken') -assert('\\\b' == bs_bs_b, 'Three backslash broken') -assert('\\\\b' == bs_bs_b, 'Four backslash broken') +assert('\c' == bs_c, 'Single backslash broken') +assert('\\c' == bs_c, 'Double backslash broken') +assert('\\\c' == bs_bs_c, 'Three backslash broken') +assert('\\\\c' == bs_bs_c, 'Four backslash broken') assert('\n' == nl, 'Newline escape broken') assert('\\n' == bs_n, 'Double backslash broken before n') assert('\\\n' == bs_nl, 'Three backslash broken before n') diff --git a/test cases/common/51 pkgconfig-gen/dependencies/meson.build b/test cases/common/51 pkgconfig-gen/dependencies/meson.build index d13f009..047e7e7 100644 --- a/test cases/common/51 pkgconfig-gen/dependencies/meson.build +++ b/test cases/common/51 pkgconfig-gen/dependencies/meson.build @@ -5,12 +5,13 @@ pkgg = import('pkgconfig') # libmain internally use libinternal and expose libexpose in its API exposed_lib = shared_library('libexposed', 'exposed.c') internal_lib = shared_library('libinternal', 'internal.c') -main_lib = static_library('libmain', link_with : [exposed_lib, internal_lib]) +main_lib = both_libraries('libmain', link_with : [exposed_lib, internal_lib]) pkgg.generate(exposed_lib) # Declare a few different Dependency objects pc_dep = dependency('libfoo', version : '>=1.0') +pc_dep_dup = dependency('libfoo', version : '>= 1.0') notfound_dep = dependency('notfound', required : false) threads_dep = dependency('threads') custom_dep = declare_dependency(link_args : ['-lcustom'], compile_args : ['-DCUSTOM']) @@ -24,9 +25,10 @@ custom2_dep = declare_dependency(link_args : ['-lcustom2'], compile_args : ['-DC # - Having custom_dep in libraries and libraries_private should only add it in Libs # - Having custom2_dep in libraries_private should not add its Cflags # - Having pc_dep in libraries_private should add it in Requires.private +# - pc_dep_dup is the same library and same version, should be ignored # - notfound_dep is not required so it shouldn't appear in the pc file. pkgg.generate(libraries : [main_lib, exposed_lib, threads_dep , custom_dep], - libraries_private : [custom_dep, custom2_dep, pc_dep, notfound_dep], + libraries_private : [custom_dep, custom2_dep, pc_dep, pc_dep_dup, notfound_dep], version : '1.0', name : 'dependency-test', filebase : 'dependency-test', diff --git a/test cases/common/64 custom header generator/meson.build b/test cases/common/64 custom header generator/meson.build index 33ba4c5..2279513 100644 --- a/test cases/common/64 custom header generator/meson.build +++ b/test cases/common/64 custom header generator/meson.build @@ -1,5 +1,15 @@ project('custom header generator', 'c') +cc_id = meson.get_compiler('c').get_id() +cc_ver = meson.get_compiler('c').version() + +if cc_id == 'intel' or (cc_id == 'lcc' and cc_ver.version_compare('<=1.23.08') + # ICC and LCC <= 1.23.08 do not escape spaces in paths in the dependency file, so Ninja + # (correctly) thinks that the rule has multiple outputs and errors out: + # 'depfile has multiple output paths' + error('MESON_SKIP_TEST: Skipping test because your compiler is known to generate broken dependency files') +endif + gen = find_program('makeheader.py') generated_h = custom_target('makeheader.py', diff --git a/test cases/failing/17 same target/file.c b/test cases/failing/17 same target/file.c new file mode 100644 index 0000000..7412372 --- /dev/null +++ b/test cases/failing/17 same target/file.c @@ -0,0 +1 @@ +int func() { return 0; } diff --git a/test cases/failing/17 same target/meson.build b/test cases/failing/17 same target/meson.build new file mode 100644 index 0000000..ee586d0 --- /dev/null +++ b/test cases/failing/17 same target/meson.build @@ -0,0 +1,4 @@ +project('same target', 'c') + +static_library('foo', 'file.c') +static_library('foo', 'file.c') diff --git a/test cases/failing/72 invalid escape char/meson.build b/test cases/failing/72 invalid escape char/meson.build new file mode 100644 index 0000000..b4e9196 --- /dev/null +++ b/test cases/failing/72 invalid escape char/meson.build @@ -0,0 +1,4 @@ +# Make sure meson exits on invalid string +# The string below contains an invalid unicode code point + +'my name is what \uxyzo who are you' diff --git a/test cases/failing/72 override used/meson.build b/test cases/failing/72 override used/meson.build new file mode 100644 index 0000000..61885bb --- /dev/null +++ b/test cases/failing/72 override used/meson.build @@ -0,0 +1,5 @@ +project('overridde an already found exe', 'c') + +old = find_program('something.py') +replacement = find_program('other.py') +meson.override_find_program('something.py', replacement) diff --git a/test cases/failing/72 override used/other.py b/test cases/failing/72 override used/other.py new file mode 100755 index 0000000..f62ba96 --- /dev/null +++ b/test cases/failing/72 override used/other.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +print('Doing something else.') diff --git a/test cases/failing/72 override used/something.py b/test cases/failing/72 override used/something.py new file mode 100755 index 0000000..64c9454 --- /dev/null +++ b/test cases/failing/72 override used/something.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +print('Doing something.') diff --git a/test cases/failing/73 dual override/meson.build b/test cases/failing/73 dual override/meson.build new file mode 100644 index 0000000..e5f86ba --- /dev/null +++ b/test cases/failing/73 dual override/meson.build @@ -0,0 +1,5 @@ +project('yo dawg', 'c') + +p = find_program('overrides.py') +meson.override_find_program('override', p) +meson.override_find_program('override', p) diff --git a/test cases/failing/73 dual override/overrides.py b/test cases/failing/73 dual override/overrides.py new file mode 100644 index 0000000..49e9b7a --- /dev/null +++ b/test cases/failing/73 dual override/overrides.py @@ -0,0 +1,4 @@ +#!/usr/bin/env python3 + +print('Yo dawg, we put overrides in your overrides,') +print('so now you can override when you override.') diff --git a/test cases/frameworks/4 qt/meson.build b/test cases/frameworks/4 qt/meson.build index b508df3..e8d12b6 100644 --- a/test cases/frameworks/4 qt/meson.build +++ b/test cases/frameworks/4 qt/meson.build @@ -56,7 +56,7 @@ foreach qt : ['qt4', 'qt5'] endif # Test that setting a unique name with a positional argument works - qtmodule.preprocess(qt + 'teststuff', qresources : ['stuff.qrc', 'stuff2.qrc'], method : get_option('method')) + qtmodule.preprocess(qt + 'teststuff', qresources : files(['stuff.qrc', 'stuff2.qrc']), method : get_option('method')) qexe = executable(qt + 'app', sources : ['main.cpp', 'mainWindow.cpp', # Sources that don't need preprocessing. diff --git a/test cases/frameworks/4 qt/subfolder/generator.py b/test cases/frameworks/4 qt/subfolder/generator.py new file mode 100644 index 0000000..045d99a --- /dev/null +++ b/test cases/frameworks/4 qt/subfolder/generator.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python3 +import sys + +if len(sys.argv) > 1: + with open(sys.argv[1], "w") as output: + output.write("Hello World") diff --git a/test cases/frameworks/4 qt/subfolder/main.cpp b/test cases/frameworks/4 qt/subfolder/main.cpp index 61cc9d4..9661811 100644 --- a/test cases/frameworks/4 qt/subfolder/main.cpp +++ b/test cases/frameworks/4 qt/subfolder/main.cpp @@ -1,9 +1,28 @@ #include <QImage> +#include <QFile> +#include <QString> int main(int argc, char **argv) { + #ifndef UNITY_BUILD Q_INIT_RESOURCE(stuff3); - QImage qi(":/thing.png"); - if(qi.width() != 640) { + Q_INIT_RESOURCE(stuff4); + #endif + + for(auto fname:{":/thing.png", ":/thing4.png"}) + { + QImage img1(fname); + if(img1.width() != 640) { + return 1; + } + } + + for(auto fname:{":/txt_resource.txt",":/txt_resource2.txt"}) + { + QFile file(fname); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return 1; + QString line = file.readLine(); + if(line.compare("Hello World")) return 1; } return 0; diff --git a/test cases/frameworks/4 qt/subfolder/meson.build b/test cases/frameworks/4 qt/subfolder/meson.build index d3ff609..f1b84e6 100644 --- a/test cases/frameworks/4 qt/subfolder/meson.build +++ b/test cases/frameworks/4 qt/subfolder/meson.build @@ -1,4 +1,32 @@ -qresources = qtmodule.preprocess(qresources : 'resources/stuff3.qrc') +simple_gen = find_program('generator.py', required : true) -app = executable('subfolder', 'main.cpp', qresources, dependencies : qtdep) +txt_resource = custom_target('txt_resource', + output : 'txt_resource.txt', + command : [simple_gen, '@OUTPUT@'], +) + +cfg = configuration_data() + +cfg.set('filepath', meson.current_source_dir()+'/../thing2.png') +cfg.set('txt_resource', txt_resource.full_path()) +# here we abuse the system by guessing build dir layout +cfg.set('txt_resource2', 'txt_resource.txt') + + +rc_file = configure_file( + configuration : cfg, + input : 'resources/stuff4.qrc.in', + output : 'stuff4.qrc', +) + +extra_cpp_args = [] +if meson.is_unity() + extra_cpp_args += '-DUNITY_BUILD' + qresources = qtmodule.preprocess(qt + '_subfolder_unity_ressource',qresources : ['resources/stuff3.qrc', rc_file]) +else + qresources = qtmodule.preprocess(qresources : ['resources/stuff3.qrc', rc_file]) +endif + +app = executable('subfolder', 'main.cpp', qresources, dependencies : qtdep, cpp_args: extra_cpp_args) +test(qt + 'subfolder', app) diff --git a/test cases/frameworks/4 qt/subfolder/resources/stuff4.qrc.in b/test cases/frameworks/4 qt/subfolder/resources/stuff4.qrc.in new file mode 100644 index 0000000..c30a358 --- /dev/null +++ b/test cases/frameworks/4 qt/subfolder/resources/stuff4.qrc.in @@ -0,0 +1,8 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file alias="thing4.png">@filepath@</file> + <file alias="txt_resource.txt">@txt_resource@</file> + <file alias="txt_resource2.txt">@txt_resource2@</file> + </qresource> +</RCC> diff --git a/test cases/frameworks/7 gnome/gdbus/meson.build b/test cases/frameworks/7 gnome/gdbus/meson.build index ea91caa..57d7f23 100644 --- a/test cases/frameworks/7 gnome/gdbus/meson.build +++ b/test cases/frameworks/7 gnome/gdbus/meson.build @@ -1,3 +1,12 @@ +gdbus_src = gnome.gdbus_codegen('generated-gdbus-no-docbook', 'com.example.Sample.xml', + interface_prefix : 'com.example.', + namespace : 'Sample', + annotations : [ + ['com.example.Hello()', 'org.freedesktop.DBus.Deprecated', 'true'] + ], +) +assert(gdbus_src.length() == 2, 'expected 2 targets') + gdbus_src = gnome.gdbus_codegen('generated-gdbus', 'com.example.Sample.xml', interface_prefix : 'com.example.', namespace : 'Sample', @@ -6,6 +15,7 @@ gdbus_src = gnome.gdbus_codegen('generated-gdbus', 'com.example.Sample.xml', ], docbook : 'generated-gdbus-doc' ) +assert(gdbus_src.length() == 3, 'expected 3 targets') gdbus_exe = executable('gdbus-test', 'gdbusprog.c', gdbus_src, diff --git a/test cases/frameworks/7 gnome/installed_files.txt b/test cases/frameworks/7 gnome/installed_files.txt index c7c704f..ac132ef 100644 --- a/test cases/frameworks/7 gnome/installed_files.txt +++ b/test cases/frameworks/7 gnome/installed_files.txt @@ -1,6 +1,7 @@ usr/include/enums.h usr/include/enums2.h usr/include/enums3.h +usr/include/enums5.h usr/include/marshaller.h usr/lib/?libgir_lib.so usr/lib/?libdep1lib.so diff --git a/test cases/frameworks/7 gnome/mkenums/meson.build b/test cases/frameworks/7 gnome/mkenums/meson.build index 9963455..44c21cb 100644 --- a/test cases/frameworks/7 gnome/mkenums/meson.build +++ b/test cases/frameworks/7 gnome/mkenums/meson.build @@ -123,6 +123,7 @@ enums4 = gnome.mkenums_simple('enums4', sources : 'meson-sample.h', enumexe4 = executable('enumprog4', 'main4.c', enums4, dependencies : gobj) enums5 = gnome.mkenums_simple('enums5', sources : 'meson-sample.h', + install_header : true, decorator : 'MESON_EXPORT', header_prefix : '#include "meson-decls.h"') enumexe5 = executable('enumprog5', main, enums5, dependencies : gobj) diff --git a/test cases/unit/13 reconfigure/meson.build b/test cases/unit/13 reconfigure/meson.build index 102180e..453644a 100644 --- a/test cases/unit/13 reconfigure/meson.build +++ b/test cases/unit/13 reconfigure/meson.build @@ -1,5 +1,5 @@ project('reconfigure test', ['c']) -if get_option('b_lto') != true - error('b_lto not set') +if get_option('b_coverage') != true + error('b_coverage not set') endif diff --git a/test cases/unit/26 guessed linker dependencies/exe/app.c b/test cases/unit/26 guessed linker dependencies/exe/app.c new file mode 100644 index 0000000..1031a42 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/exe/app.c @@ -0,0 +1,6 @@ +void liba_func(); + +int main() { + liba_func(); + return 0; +} diff --git a/test cases/unit/26 guessed linker dependencies/exe/meson.build b/test cases/unit/26 guessed linker dependencies/exe/meson.build new file mode 100644 index 0000000..8bb1bd7 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/exe/meson.build @@ -0,0 +1,7 @@ +project('exe', ['c']) + +executable('app', + 'app.c', + # Use uninterpreted strings to avoid path finding by dependency or compiler.find_library + link_args: ['-ltest-lib'] + ) diff --git a/test cases/unit/26 guessed linker dependencies/lib/lib.c b/test cases/unit/26 guessed linker dependencies/lib/lib.c new file mode 100644 index 0000000..1a8f94d --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/lib.c @@ -0,0 +1,20 @@ +#if defined _WIN32 + #define DLL_PUBLIC __declspec(dllexport) +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +void DLL_PUBLIC liba_func() { +} + +#ifdef MORE_EXPORTS + +void DLL_PUBLIC libb_func() { +} + +#endif diff --git a/test cases/unit/26 guessed linker dependencies/lib/meson.build b/test cases/unit/26 guessed linker dependencies/lib/meson.build new file mode 100644 index 0000000..36df112 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/meson.build @@ -0,0 +1,11 @@ +project('lib1', ['c']) + +c_args = [] + +# Microsoft's compiler is quite smart about touching import libs on changes, +# so ensure that there is actually a change in symbols. +if get_option('more_exports') + c_args += '-DMORE_EXPORTS' +endif + +a = library('test-lib', 'lib.c', c_args: c_args, install: true) diff --git a/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt b/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt new file mode 100644 index 0000000..2123e45 --- /dev/null +++ b/test cases/unit/26 guessed linker dependencies/lib/meson_options.txt @@ -0,0 +1 @@ +option('more_exports', type : 'boolean', value : false) diff --git a/test cases/unit/27 forcefallback/meson.build b/test cases/unit/27 forcefallback/meson.build new file mode 100644 index 0000000..e6a90ea --- /dev/null +++ b/test cases/unit/27 forcefallback/meson.build @@ -0,0 +1,8 @@ +project('mainproj', 'c', + default_options : ['wrap_mode=forcefallback']) + +zlib_dep = dependency('zlib', fallback: ['notzlib', 'zlib_dep']) + +test_not_zlib = executable('test_not_zlib', ['test_not_zlib.c'], dependencies: [zlib_dep]) + +test('test_not_zlib', test_not_zlib) diff --git a/test cases/unit/27 forcefallback/subprojects/notzlib/meson.build b/test cases/unit/27 forcefallback/subprojects/notzlib/meson.build new file mode 100644 index 0000000..254a136 --- /dev/null +++ b/test cases/unit/27 forcefallback/subprojects/notzlib/meson.build @@ -0,0 +1,7 @@ +project('notzlib', 'c') + +notzlib_sources = ['notzlib.c'] + +notzlib = library('notzlib', notzlib_sources) + +zlib_dep = declare_dependency(link_with: notzlib, include_directories: include_directories(['.'])) diff --git a/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.c b/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.c new file mode 100644 index 0000000..c3b6bf9 --- /dev/null +++ b/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.c @@ -0,0 +1,6 @@ +#include "notzlib.h" + +int not_a_zlib_function (void) +{ + return 42; +} diff --git a/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.h b/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.h new file mode 100644 index 0000000..695921d --- /dev/null +++ b/test cases/unit/27 forcefallback/subprojects/notzlib/notzlib.h @@ -0,0 +1,18 @@ +#pragma once + +#if defined _WIN32 || defined __CYGWIN__ +#if defined BUILDING_DLL + #define DLL_PUBLIC __declspec(dllexport) +#else + #define DLL_PUBLIC __declspec(dllimport) +#endif +#else + #if defined __GNUC__ + #define DLL_PUBLIC __attribute__ ((visibility("default"))) + #else + #pragma message ("Compiler does not support symbol visibility.") + #define DLL_PUBLIC + #endif +#endif + +int DLL_PUBLIC not_a_zlib_function (void); diff --git a/test cases/unit/27 forcefallback/test_not_zlib.c b/test cases/unit/27 forcefallback/test_not_zlib.c new file mode 100644 index 0000000..36256af --- /dev/null +++ b/test cases/unit/27 forcefallback/test_not_zlib.c @@ -0,0 +1,8 @@ +#include <notzlib.h> + +int main (int ac, char **av) +{ + if (not_a_zlib_function () != 42) + return 1; + return 0; +} diff --git a/test cases/unit/28 pkgconfig use libraries/app/app.c b/test cases/unit/28 pkgconfig use libraries/app/app.c new file mode 100644 index 0000000..b271a9e --- /dev/null +++ b/test cases/unit/28 pkgconfig use libraries/app/app.c @@ -0,0 +1,6 @@ +void libb_func(); + +int main() { + libb_func(); + return 0; +} diff --git a/test cases/unit/28 pkgconfig use libraries/app/meson.build b/test cases/unit/28 pkgconfig use libraries/app/meson.build new file mode 100644 index 0000000..3d85a32 --- /dev/null +++ b/test cases/unit/28 pkgconfig use libraries/app/meson.build @@ -0,0 +1,5 @@ +project('app', ['c']) + +b = dependency('test-b') + +executable('app', 'app.c', dependencies : [b]) diff --git a/test cases/unit/28 pkgconfig use libraries/lib/liba.c b/test cases/unit/28 pkgconfig use libraries/lib/liba.c new file mode 100644 index 0000000..e98906b --- /dev/null +++ b/test cases/unit/28 pkgconfig use libraries/lib/liba.c @@ -0,0 +1,2 @@ +void liba_func() { +} diff --git a/test cases/unit/28 pkgconfig use libraries/lib/libb.c b/test cases/unit/28 pkgconfig use libraries/lib/libb.c new file mode 100644 index 0000000..3160e5f --- /dev/null +++ b/test cases/unit/28 pkgconfig use libraries/lib/libb.c @@ -0,0 +1,5 @@ +void liba_func(); + +void libb_func() { + liba_func(); +} diff --git a/test cases/unit/28 pkgconfig use libraries/lib/meson.build b/test cases/unit/28 pkgconfig use libraries/lib/meson.build new file mode 100644 index 0000000..748adf4 --- /dev/null +++ b/test cases/unit/28 pkgconfig use libraries/lib/meson.build @@ -0,0 +1,16 @@ +project('lib', ['c']) + +a = library('test-a', 'liba.c', install: true) + +b = library('test-b', 'libb.c', link_with: a, install: true) + +import('pkgconfig').generate( + version: '0.0', + description: 'test library', + filebase: 'test-b', + name: 'test library', + libraries: [b], + subdirs: ['.'] +) + + diff --git a/test cases/unit/29 cross file overrides always args/meson.build b/test cases/unit/29 cross file overrides always args/meson.build new file mode 100644 index 0000000..ef6556e --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/meson.build @@ -0,0 +1,3 @@ +project('cross compile args override always args', 'c') + +executable('no-file-offset-bits', 'test.c') diff --git a/test cases/unit/29 cross file overrides always args/test.c b/test cases/unit/29 cross file overrides always args/test.c new file mode 100644 index 0000000..315f92e --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/test.c @@ -0,0 +1,8 @@ +#ifdef _FILE_OFFSET_BITS + #error "_FILE_OFFSET_BITS should not be set" +#endif + +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt b/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt new file mode 100644 index 0000000..a00a7d1 --- /dev/null +++ b/test cases/unit/29 cross file overrides always args/ubuntu-armhf-overrides.txt @@ -0,0 +1,19 @@ +[binaries] +# we could set exe_wrapper = qemu-arm-static but to test the case +# when cross compiled binaries can't be run we don't do that +c = '/usr/bin/arm-linux-gnueabihf-gcc' +cpp = '/usr/bin/arm-linux-gnueabihf-g++' +rust = ['rustc', '--target', 'arm-unknown-linux-gnueabihf', '-C', 'linker=/usr/bin/arm-linux-gnueabihf-gcc-7'] +ar = '/usr/arm-linux-gnueabihf/bin/ar' +strip = '/usr/arm-linux-gnueabihf/bin/strip' +pkgconfig = '/usr/bin/arm-linux-gnueabihf-pkg-config' + +[properties] +root = '/usr/arm-linux-gnueabihf' +c_args = ['-U_FILE_OFFSET_BITS'] + +[host_machine] +system = 'linux' +cpu_family = 'arm' +cpu = 'armv7' # Not sure if correct. +endian = 'little' |