diff options
62 files changed, 1097 insertions, 453 deletions
diff --git a/docs/markdown/Build-options.md b/docs/markdown/Build-options.md index cb7b19e..f05eb7b 100644 --- a/docs/markdown/Build-options.md +++ b/docs/markdown/Build-options.md @@ -16,18 +16,37 @@ Here is a simple option file. option('someoption', type : 'string', value : 'optval', description : 'An option') option('other_one', type : 'boolean', value : false) option('combo_opt', type : 'combo', choices : ['one', 'two', 'three'], value : 'three') +option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two']) ``` -This demonstrates the three basic option types and their usage. String -option is just a free form string and a boolean option is, -unsurprisingly, true or false. The combo option can have any value -from the strings listed in argument `choices`. If `value` is not set, -it defaults to empty string for strings, `true` for booleans or the -first element in a combo. You can specify `description`, which is a -free form piece of text describing the option. It defaults to option -name. +All types allow a `description` value to be set describing the option, if no +option is set then the name of the option will be used instead. -These options are accessed in Meson code with the `get_option` function. +### Strings + +The string type is a free form string. If the default value is not set then an +empty string will be used as the default. + +### Booleans + +Booleans may have values of either `true` or `false`. If not default value is +supplied then `true` will be used as the default. + +### Combos + +A combo allows any one of the values in the `choices` parameter to be selected. +If no default value is set then the first value will be the default. + +### Arrays + +Arrays allow one or more of the values in the `choices` parameter to be selected. +If the `value` parameter is unset then the values of `choices` will be used as +the default. + +This type is new in version 0.44.0 + + +## Using build options ```meson optval = get_option('opt_name') diff --git a/docs/markdown/Conference-presentations.md b/docs/markdown/Conference-presentations.md index 15e4396..abfc52f 100644 --- a/docs/markdown/Conference-presentations.md +++ b/docs/markdown/Conference-presentations.md @@ -8,7 +8,7 @@ - GStreamer conference 2015, [Done in 6.0 seconds](https://gstconf.ubicast.tv/videos/done-in-60-seconds-a-new-build-system-for-gstreamer) (jpakkane) -- LCA 2016, [Builds, dependencies and deployment in the modern multiplatform world](https://www.youtube.com/watch?v=CTJtKtQ8R5k&feature=youtu.be) (jpakkane) +- LCA 2016, [Builds, dependencies and deployment in the modern multiplatform world](https://www.youtube.com/watch?v=CTJtKtQ8R5k) (jpakkane) - GUADEC 2016, [Making your GNOME app compile 2.4x faster](https://media.ccc.de/v/44-making_your_gnome_app_compile_24x_faster) (nirbheek) diff --git a/docs/markdown/Cross-compilation.md b/docs/markdown/Cross-compilation.md index f68b1f5..c1ad317 100644 --- a/docs/markdown/Cross-compilation.md +++ b/docs/markdown/Cross-compilation.md @@ -257,3 +257,29 @@ then you can access that using the `meson` object like this: myvar = meson.get_cross_property('somekey') # myvar now has the value 'somevalue' ``` + +## Cross file locations + +As of version 0.44.0 meson supports loading cross files from system locations +on Linux and the BSDs. This will be $XDG_DATA_DIRS/meson/cross, or if +XDG_DATA_DIRS is undefined, then /usr/local/share/meson/cross and +/usr/share/meson/cross will be tried in that order, for system wide cross +files. User local files can be put in $XDG_DATA_HOME/meson/cross, or +~/.local/share/meson/cross if that is undefined. + +The order of locations tried is as follows: + - A file relative to the local dir + - The user local location + - The system wide locations in order + +Linux and BSD distributions are encouraged to ship cross files either with +their cross compiler toolchain packages or as a standalone package, and put +them in one of the system paths referenced above. + +These files can be loaded automatically without adding a path to the cross +file. For example, if a ~/.local/share/meson/cross contains a file called x86-linux, +then the following command would start a cross build using that cross files: + +```sh +meson builddir/ --cross-file x86-linux +``` diff --git a/docs/markdown/Dependencies.md b/docs/markdown/Dependencies.md index 8e780d6..bae3edc 100644 --- a/docs/markdown/Dependencies.md +++ b/docs/markdown/Dependencies.md @@ -178,32 +178,51 @@ the list of sources for the target. The `modules` keyword of `dependency` works just like it does with Boost. It tells which subparts of Qt the program uses. -## Pcap +## Dependencies using config tools -The pcap library does not ship with pkg-config at the time or writing -but instead it has its own `pcap-config` util. Meson will use it -automatically: +CUPS, LLVM, PCAP, WxWidgets, libwmf, and GnuStep either do not provide +pkg-config modules or additionally can be detected via a config tool +(cups-config, llvm-config, etc). Meson has native support for these tools, and +then can be found like other dependencies: ```meson pcap_dep = dependency('pcap', version : '>=1.0') +cups_dep = dependency('cups', version : '>=1.4') +llvm_dep = dependency('llvm', version : '>=4.0') ``` -## CUPS - -The cups library does not ship with pkg-config at the time or writing -but instead it has its own `cups-config` util. Meson will use it -automatically: +Some of these tools (like wmf and cups) provide both pkg-config and config +tools support. You can force one or another via the method keyword: ```meson -cups_dep = dependency('cups', version : '>=1.4') +wmf_dep = dependency('wmf', method : 'config-tool') ``` -## LibWMF +## LLVM + +Meson has native support for LLVM going back to version LLVM version 3.5. +It supports a few additional features compared to other config-tool based +dependencies. + +As of 0.44.0 Meson supports the `static` keyword argument for LLVM. Before this +LLVM >= 3.9 would always dynamically link, while older versions would +statically link, due to a quirk in `llvm-config`. + +### Modules, a.k.a. Components -The libwmf library does not ship with pkg-config at the time or writing -but instead it has its own `libwmf-config` util. Meson will use it -automatically: +Meson wraps LLVM's concept of components in it's own modules concept. +When you need specific components you add them as modules as meson will do the +right thing: ```meson -libwmf_dep = dependency('libwmf', version : '>=0.2.8') +llvm_dep = dependency('llvm', version : '>= 4.0', modules : ['amdgpu']) +``` + +As of 0.44.0 it can also take optional modules (these will affect the arguments +generated for a static link): + +```meson +llvm_dep = dependency( + 'llvm', version : '>= 4.0', modules : ['amdgpu'], optional_modules : ['inteljitevents'], +) ``` diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 2aa9665..e6aa9d3 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -430,7 +430,7 @@ be passed to [shared and static libraries](#library). `project`'s `default_options` overriding the values of these options for this target only, since 0.40.0 - `d_import_dirs` list of directories to look in for string imports used - in the D programmling language + in the D programming language - `d_unittest`, when set to true, the D modules are compiled in debug mode - `d_module_versions` list of module versions set when compiling D sources @@ -855,6 +855,8 @@ This function prints its argument to stdout. This function prints its argument to stdout prefixed with WARNING:. +*Added 0.44.0* + ### project() ``` meson @@ -1019,7 +1021,7 @@ has one argument the others don't have: ### subdir() ``` meson - void subdir(dir_name) + void subdir(dir_name, ...) ``` Enters the specified subdirectory and executes the `meson.build` file @@ -1032,6 +1034,12 @@ current build file and in all subsequent build files executed with Note that this means that each `meson.build` file in a source tree can and must only be executed once. +This function has one keyword argument. + + - `if_found` takes one or several dependency objects and will only + recurse in the subdir if they all return `true` when queried with + `.found()` + ### subproject() ``` meson @@ -1608,6 +1616,10 @@ an external dependency with the following methods: pkg-config variable specified, or, if invoked on a non pkg-config dependency, error out + - `get_configtool_variable(varname)` (*Added 0.44.0*) will get the + command line argument from the config tool (with `--` prepended), or, + if invoked on a non config-tool dependency, error out. + - `type_name()` which returns a string describing the type of the dependency, the most common values are `internal` for deps created with `declare_dependencies` and `pkgconfig` for system dependencies diff --git a/docs/markdown/Subprojects.md b/docs/markdown/Subprojects.md index 923b6a3..14f01d4 100644 --- a/docs/markdown/Subprojects.md +++ b/docs/markdown/Subprojects.md @@ -4,69 +4,76 @@ short-description: Using meson projects as subprojects within other meson projec # Subprojects -Some platforms do not provide a native packaging system. In these cases it is common to bundle all third party libraries in your source tree. This is usually frowned upon because it makes it hard to add these kinds of projects into e.g. those Linux distributions that forbid bundled libraries. - -Meson tries to solve this problem by making it extremely easy to provide both at the same time. The way this is done is that Meson allows you to take any other Meson project and make it a part of your build without (in the best case) any changes to its Meson setup. It becomes a transparent part of the project. The basic idiom goes something like this. +Some platforms do not provide a native packaging system. In these +cases it is common to bundle all third party libraries in your source +tree. This is usually frowned upon because it makes it hard to add +these kinds of projects into e.g. those Linux distributions that +forbid bundled libraries. + +Meson tries to solve this problem by making it extremely easy to +provide both at the same time. The way this is done is that Meson +allows you to take any other Meson project and make it a part of your +build without (in the best case) any changes to its Meson setup. It +becomes a transparent part of the project. The basic idiom goes +something like this. ```meson -dep = dependency('foo', required : false) -if dep.found() - # set up project using external dependency -else - subproject('foo') - # set up rest of project as if foo was provided by this project -endif +dep = dependency('foo', fallback : [subproject_name, variable_name] ``` -All Meson features of the subproject, such as project options keep working and can be set in the master project. There are a few limitations, the most important being that global compiler arguments must be set in the main project before calling subproject. Subprojects must not set global arguments because there is no way to do that reliably over multiple subprojects. To check whether you are running as a subproject, use the `is_subproject` function. - -As an example, suppose we have a simple project that provides a shared library. +As an example, suppose we have a simple project that provides a shared +library. It would be set up like this. ```meson project('simple', 'c') i = include_directories('include') l = shared_library('simple', 'simple.c', include_directories : i, install : true) +simple_dep = declare_dependency(include_directories : i, + link_with : l) ``` -Then we could use that from a master project. First we generate a subdirectory called `subprojects` in the root of the master directory. Then we create a subdirectory called `simple` and put the subproject in that directory. Now the subproject can be used like this. +Then we could use that from a master project. First we generate a +subdirectory called `subprojects` in the root of the master +directory. Then we create a subdirectory called `simple` and put the +subproject in that directory. Now the subproject can be used like +this. ```meson project('master', 'c') -dep = dependency('simple', required : false) -if dep.found() - i = [] - l = [] -else - sp = subproject('simple') # This is a name of a subdirectory in subprojects. - i = sp.get_variable('i') - l = sp.get_variable('l') -endif -exe = executable('prog', 'prog.c', include_directories : i, link_with : l, +dep = dependency('simple', fallback : ['simple', 'simple_dep'] +exe = executable('prog', 'prog.c', dependencies : dep, install : true) ``` -With this setup the system dependency is used when it is available, otherwise we fall back on the bundled version. - -It should be noted that this only works for subprojects that are built with Meson. It can not be used with any other build system. The reason is the simple fact that there is no possible way to do this reliably with mixed build systems. - -Subprojects can use other subprojects, but all subprojects must reside in the top level `subprojects` directory. Recursive use of subprojects is not allowed, though, so you can't have subproject `a` that uses subproject `b` and have `b` also use `a`. - -## Subprojects and dependencies - -A common use case is to use subprojects to provide dependencies on platforms that do not provide them out of the box. This is especially common on Windows. Meson makes this easy while at the same time using system dependencies if are available. The way to do this is to set up a subproject that builds the dependency and has an internal dependency declared like this: +With this setup the system dependency is used when it is available, +otherwise we fall back on the bundled version. If you wish to always +use the embedded version, then you would declare it like this: ```meson -proj_dep = declare_dependency(...) +simple_sp = subproject('simple') +dep = simple_sp.get_variable('simple_dep') ``` -Then you can use the subproject in the master project like this: +All Meson features of the subproject, such as project options keep +working and can be set in the master project. There are a few +limitations, the most important being that global compiler arguments +must be set in the main project before calling subproject. Subprojects +must not set global arguments because there is no way to do that +reliably over multiple subprojects. To check whether you are running +as a subproject, use the `is_subproject` function. -```meson -sp_dep = dependency('subproj_pkgconfig_name', fallback : ['subproj_name', 'proj_dep']) -``` +It should be noted that this only works for subprojects that are built +with Meson. It can not be used with any other build system. The reason +is the simple fact that there is no possible way to do this reliably +with mixed build systems. -This uses the system dependency when available and the self built version if not. If you want to always use the subproject, that is also possible, just use `subproject` and `get_variable` as discussed above to get the dependency object. +Subprojects can use other subprojects, but all subprojects must reside +in the top level `subprojects` directory. Recursive use of subprojects +is not allowed, though, so you can't have subproject `a` that uses +subproject `b` and have `b` also use `a`. # Obtaining subprojects -Meson ships with a dependency system to automatically obtain dependency subprojects. It is documented in the [Wrap dependency system manual](Wrap-dependency-system-manual.md). +Meson ships with a dependency system to automatically obtain +dependency subprojects. It is documented in the [Wrap dependency +system manual](Wrap-dependency-system-manual.md). diff --git a/docs/markdown/snippets/config-tool-variable-method.md b/docs/markdown/snippets/config-tool-variable-method.md new file mode 100644 index 0000000..7725982 --- /dev/null +++ b/docs/markdown/snippets/config-tool-variable-method.md @@ -0,0 +1,11 @@ +# Config-Tool based dependencies gained a method to get arbitrary options + +A number of dependencies (CUPS, LLVM, pcap, WxWidgets, GnuStep) use a config +tool instead of pkg-config. As of this version they now have a +`get_configtool_variable` method, which is analogous to the +`get_pkgconfig_variable` for pkg config. + +```meson +dep_llvm = dependency('LLVM') +llvm_inc_dir = dep_llvm.get_configtool_variable('includedir') +``` diff --git a/docs/markdown/snippets/if-found.md b/docs/markdown/snippets/if-found.md new file mode 100644 index 0000000..a8d4932 --- /dev/null +++ b/docs/markdown/snippets/if-found.md @@ -0,0 +1,13 @@ +# Added `if_found` to subdir + +Added a new keyword argument to the `subdir` command. It is given a +list of dependency objects and the function will only recurse in the +subdirectory if they are all found. Typical usage goes like this. + + d1 = dependency('foo') # This is found + d2 = dependency('bar') # This is not found + + subdir('somedir', if_found : [d1, d2]) + +In this case the subdirectory would not be entered since `d2` could +not be found. diff --git a/docs/markdown/snippets/option-array-type.md b/docs/markdown/snippets/option-array-type.md new file mode 100644 index 0000000..f073dc1 --- /dev/null +++ b/docs/markdown/snippets/option-array-type.md @@ -0,0 +1,17 @@ +# An array type for user options + +Previously to have an option that took more than one value a string value would +have to be created and split, but validating this was difficult. A new array type +has been added to the meson_options.txt for this case. It works like a 'combo', but +allows more than one option to be passed. When used on the command line (with -D), +values are passed as a comma separated list. + +```meson +option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one']) +``` + +These can be overwritten on the command line, + +```meson +meson _build -Darray_opt=two,three +``` diff --git a/docs/markdown/snippets/system-wide-cross-files.md b/docs/markdown/snippets/system-wide-cross-files.md new file mode 100644 index 0000000..66c454f --- /dev/null +++ b/docs/markdown/snippets/system-wide-cross-files.md @@ -0,0 +1,20 @@ +## System wide and user local cross files + +Meson has gained the ability to load cross files from predefined locations +without passing a full path on Linux and the BSD OSes. User local files will be +loaded from `$XDG_DATA_HOME/meson/cross`, or if XDG_DATA_HOME is undefined, +`~/.local/share/meson/cross` will be used. + +For system wide paths the values of `$XDG_DATA_DIRS` + `/meson/cross` will be used, +if XDG_DATA_DIRS is undefined then `/usr/local/share/meson/cross:/usr/share/meson/cross` +will be used instead. + +A file relative to the current working directory will be tried first, then the +user specific path will be tried before the system wide paths. + +Assuming that a file x86-linux is located in one of those places a cross build +can be started with: + +```sh +meson builddir/ --cross-file x86-linux +``` diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 33d779a..df58271 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -446,9 +446,9 @@ class Backend: copt_proxy = OptionOverrideProxy(target.option_overrides, self.environment.coredata.compiler_options) # First, the trivial ones that are impossible to override. # - # Add -nostdinc/-nostdinc++ if needed; can't be overriden + # Add -nostdinc/-nostdinc++ if needed; can't be overridden commands += self.get_cross_stdlib_args(target, compiler) - # Add things like /NOLOGO or -pipe; usually can't be overriden + # Add things like /NOLOGO or -pipe; usually can't be overridden commands += compiler.get_always_args() # Only add warning-flags by default if the buildtype enables it, and if # we weren't explicitly asked to not emit warnings (for Vala, f.ex) @@ -570,7 +570,7 @@ class Backend: if isinstance(exe, build.BuildTarget): is_cross = is_cross and exe.is_cross if isinstance(exe, dependencies.ExternalProgram): - # E.g. an external verificator or simulator program run on a generated executable. + # E.g. an external verifier or simulator program run on a generated executable. # Can always be run. is_cross = False if is_cross: diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index cea1b08..bcda603 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -220,7 +220,7 @@ int dummy; outfile.write('# Suffix\n\n') self.generate_utils(outfile) self.generate_ending(outfile) - # Only ovewrite the old build file after the new one has been + # Only overwrite the old build file after the new one has been # fully created. os.replace(tempfilename, outfilename) self.generate_compdb() @@ -2236,7 +2236,7 @@ rule FORTRAN_DEP_HACK # Fortran is a bit weird (again). When you link against a library, just compiling a source file # requires the mod files that are output when single files are built. To do this right we would need to # scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so - # instead just have an ordered dependendy on the library. This ensures all required mod files are created. + # instead just have an ordered dependency on the library. This ensures all required mod files are created. # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that # produce incorrect dep files but such is life. def get_fortran_orderdeps(self, target, compiler): @@ -2387,9 +2387,9 @@ rule FORTRAN_DEP_HACK commands += compilers.get_base_link_args(self.environment.coredata.base_options, linker, isinstance(target, build.SharedModule)) - # Add -nostdlib if needed; can't be overriden + # Add -nostdlib if needed; can't be overridden commands += self.get_cross_stdlib_link_args(target, linker) - # Add things like /NOLOGO; usually can't be overriden + # Add things like /NOLOGO; usually can't be overridden commands += linker.get_linker_always_args() # Add buildtype linker args: optimization level, etc. commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target)) diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 69ee3e5..ea02580 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -672,9 +672,6 @@ class Vs2010Backend(backends.Backend): ET.SubElement(type_config, 'DebugInformationFormat').text = 'ProgramDatabase' elif '/Z7' in buildtype_args: ET.SubElement(type_config, 'DebugInformationFormat').text = 'OldStyle' - # Generate Debug info - if '/DEBUG' in buildtype_link_args: - ET.SubElement(type_config, 'GenerateDebugInformation').text = 'true' # Runtime checks if '/RTC1' in buildtype_args: ET.SubElement(type_config, 'BasicRuntimeChecks').text = 'EnableFastChecks' @@ -885,6 +882,9 @@ class Vs2010Backend(backends.Backend): # vcxproj file (similar to buildtype compiler args) instead of in # AdditionalOptions? extra_link_args += compiler.get_buildtype_linker_args(self.buildtype) + # Generate Debug info + if self.buildtype.startswith('debug'): + self.generate_debug_information(link) if not isinstance(target, build.StaticLibrary): if isinstance(target, build.SharedModule): extra_link_args += compiler.get_std_shared_module_link_args() @@ -1190,3 +1190,7 @@ if %%errorlevel%% neq 0 goto :VCEnd''' cmd_templ % ('" "'.join(test_command)) ET.SubElement(root, 'Import', Project='$(VCTargetsPath)\Microsoft.Cpp.targets') self._prettyprint_vcxproj_xml(ET.ElementTree(root), ofname) + + def generate_debug_information(self, link): + # valid values for vs2015 is 'false', 'true', 'DebugFastLink' + ET.SubElement(link, 'GenerateDebugInformation').text = 'true' diff --git a/mesonbuild/backend/vs2017backend.py b/mesonbuild/backend/vs2017backend.py index fe1d7c7..9098226 100644 --- a/mesonbuild/backend/vs2017backend.py +++ b/mesonbuild/backend/vs2017backend.py @@ -13,6 +13,7 @@ # limitations under the License. import os +import xml.etree.ElementTree as ET from .vs2010backend import Vs2010Backend @@ -27,3 +28,7 @@ class Vs2017Backend(Vs2010Backend): sdk_version = os.environ.get('WindowsSDKVersion', None) if sdk_version: self.windows_target_platform_version = sdk_version.rstrip('\\') + + def generate_debug_information(self, link): + # valid values for vs2017 is 'false', 'true', 'DebugFastLink', 'DebugFull' + ET.SubElement(link, 'GenerateDebugInformation').text = 'DebugFull' diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 5f552c2..8eb95dc 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -1137,7 +1137,7 @@ class GeneratedList: class Executable(BuildTarget): def __init__(self, name, subdir, subproject, is_cross, sources, objects, environment, kwargs): super().__init__(name, subdir, subproject, is_cross, sources, objects, environment, kwargs) - # Unless overriden, executables have no suffix or prefix. Except on + # Unless overridden, executables have no suffix or prefix. Except on # Windows and with C#/Mono executables where the suffix is 'exe' if not hasattr(self, 'prefix'): self.prefix = '' @@ -1746,7 +1746,7 @@ class Jar(BuildTarget): class CustomTargetIndex: - """A special opaque object returned by indexing a CustomTaget. This object + """A special opaque object returned by indexing a CustomTarget. This object exists in meson, but acts as a proxy in the backends, making targets depend on the CustomTarget it's derived from, but only adding one source file to the sources. diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index c2dbad7..011c222 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -451,9 +451,9 @@ class CompilerArgs(list): Returns whether the argument can be safely de-duped. This is dependent on three things: - a) Whether an argument can be 'overriden' by a later argument. For + a) Whether an argument can be 'overridden' by a later argument. For example, -DFOO defines FOO and -UFOO undefines FOO. In this case, we - can safely remove the previous occurance and add a new one. The same + can safely remove the previous occurrence and add a new one. The same is true for include paths and library paths with -I and -L. For these we return `2`. See `dedup2_prefixes` and `dedup2_args`. b) Arguments that once specified cannot be undone, such as `-c` or @@ -511,10 +511,10 @@ class CompilerArgs(list): continue i = self.index(each) if group_start < 0: - # First occurance of a library + # First occurrence of a library group_start = i if group_start >= 0: - # Last occurance of a library + # Last occurrence of a library self.insert(i + 1, '-Wl,--end-group') self.insert(group_start, '-Wl,--start-group') return self.compiler.unix_args_to_native(self) @@ -548,15 +548,15 @@ class CompilerArgs(list): raise TypeError('can only concatenate list (not "{}") to list'.format(args)) for arg in args: # If the argument can be de-duped, do it either by removing the - # previous occurance of it and adding a new one, or not adding the - # new occurance. + # previous occurrence of it and adding a new one, or not adding the + # new occurrence. dedup = self._can_dedup(arg) if dedup == 1: # Argument already exists and adding a new instance is useless if arg in self or arg in pre or arg in post: continue if dedup == 2: - # Remove all previous occurances of the arg and add it anew + # Remove all previous occurrences of the arg and add it anew if arg in self: self.remove(arg) if arg in pre: @@ -605,7 +605,7 @@ class Compiler: self.exelist = exelist else: raise TypeError('Unknown argument to Compiler') - # In case it's been overriden by a child class already + # In case it's been overridden by a child class already if not hasattr(self, 'file_suffixes'): self.file_suffixes = lang_suffixes[self.language] if not hasattr(self, 'can_compile_suffixes'): diff --git a/mesonbuild/compilers/cpp.py b/mesonbuild/compilers/cpp.py index a9093b3..15eb9a0 100644 --- a/mesonbuild/compilers/cpp.py +++ b/mesonbuild/compilers/cpp.py @@ -76,7 +76,7 @@ class ClangCPPCompiler(ClangCompiler, CPPCompiler): def get_options(self): return {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none', 'c++03', 'c++11', 'c++14', 'c++1z', + ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++1z', 'gnu++11', 'gnu++14', 'gnu++1z'], 'none')} @@ -102,7 +102,7 @@ class GnuCPPCompiler(GnuCompiler, CPPCompiler): def get_options(self): opts = {'cpp_std': coredata.UserComboOption('cpp_std', 'C++ language standard to use', - ['none', 'c++03', 'c++11', 'c++14', 'c++1z', + ['none', 'c++98', 'c++03', 'c++11', 'c++14', 'c++1z', 'gnu++03', 'gnu++11', 'gnu++14', 'gnu++1z'], 'none'), 'cpp_debugstl': coredata.UserBooleanOption('cpp_debugstl', diff --git a/mesonbuild/compilers/vala.py b/mesonbuild/compilers/vala.py index 9da9b81..b91da6d 100644 --- a/mesonbuild/compilers/vala.py +++ b/mesonbuild/compilers/vala.py @@ -94,3 +94,9 @@ class ValaCompiler(Compiler): return [vapi] mlog.debug('Searched {!r} and {!r} wasn\'t found'.format(extra_dirs, libname)) return None + + def thread_flags(self): + return [] + + def thread_link_flags(self): + return [] diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 401211a..7fbf18a 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -14,6 +14,7 @@ # limitations under the License. import pickle, os, uuid +import sys from pathlib import PurePath from collections import OrderedDict from .mesonlib import MesonException, commonpath @@ -127,24 +128,37 @@ class UserComboOption(UserOption): class UserStringArrayOption(UserOption): def __init__(self, name, description, value, **kwargs): super().__init__(name, description, kwargs.get('choices', [])) - self.set_value(value) - - def validate(self, value): - if isinstance(value, str): - if not value.startswith('['): - raise MesonException('Valuestring does not define an array: ' + value) - newvalue = ast.literal_eval(value) + self.set_value(value, user_input=False) + + def validate(self, value, user_input): + # User input is for options defined on the command line (via -D + # options). Users should put their input in as a comma separated + # string, but for defining options in meson_options.txt the format + # should match that of a combo + if not user_input: + if isinstance(value, str): + if not value.startswith('['): + raise MesonException('Valuestring does not define an array: ' + value) + newvalue = ast.literal_eval(value) + else: + newvalue = value else: - newvalue = value + assert isinstance(value, str) + newvalue = [v.strip() for v in value.split(',')] if not isinstance(newvalue, list): raise MesonException('"{0}" should be a string array, but it is not'.format(str(newvalue))) for i in newvalue: if not isinstance(i, str): raise MesonException('String array element "{0}" is not a string.'.format(str(newvalue))) + if self.choices: + bad = [x for x in newvalue if x not in self.choices] + if bad: + raise MesonException('Options "{}" are not in allowed choices: "{}"'.format( + ', '.join(bad), ', '.join(self.choices))) return newvalue - def set_value(self, newvalue): - self.value = self.validate(newvalue) + def set_value(self, newvalue, user_input=True): + self.value = self.validate(newvalue, user_input) def validate_value(self, value): self.validate(value) @@ -172,10 +186,7 @@ class CoreData: self.external_preprocess_args = {} # CPPFLAGS only self.external_args = {} # CPPFLAGS + CFLAGS self.external_link_args = {} # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS) - if options.cross_file is not None: - self.cross_file = os.path.join(os.getcwd(), options.cross_file) - else: - self.cross_file = None + self.cross_file = self.__load_cross_file(options.cross_file) self.wrap_mode = options.wrap_mode self.compilers = OrderedDict() self.cross_compilers = OrderedDict() @@ -184,6 +195,46 @@ class CoreData: # Only to print a warning if it changes between Meson invocations. self.pkgconf_envvar = os.environ.get('PKG_CONFIG_PATH', '') + @staticmethod + def __load_cross_file(filename): + """Try to load the cross file. + + If the filename is None return None. If the filename is an absolute + (after resolving variables and ~), return that absolute path. Next, + check if the file is relative to the current source dir. If the path + still isn't resolved do the following: + Linux + BSD: + - $XDG_DATA_HOME/meson/cross (or ~/.local/share/meson/cross if + undefined) + - $XDG_DATA_DIRS/meson/cross (or + /usr/local/share/meson/cross:/usr/share/meson/cross if undefined) + - Error + *: + - Error + BSD follows the Linux path and will honor XDG_* if set. This simplifies + the implementation somewhat, especially since most BSD users wont set + those environment variables. + """ + if filename is None: + return None + filename = os.path.expanduser(os.path.expandvars(filename)) + if os.path.isabs(filename): + return filename + path_to_try = os.path.abspath(filename) + if os.path.exists(path_to_try): + return path_to_try + if sys.platform == 'linux' or 'bsd' in sys.platform.lower(): + paths = [ + os.environ.get('XDG_DATA_HOME', os.path.expanduser('~/.local/share')), + ] + os.environ.get('XDG_DATA_DIRS', '/usr/local/share:/usr/share').split(':') + for path in paths: + path_to_try = os.path.join(path, 'meson', 'cross', filename) + if os.path.exists(path_to_try): + return path_to_try + raise MesonException('Cannot find specified cross file: ' + filename) + + raise MesonException('Cannot find specified cross file: ' + filename) + def sanitize_prefix(self, prefix): if not os.path.isabs(prefix): raise MesonException('prefix value {!r} must be an absolute path' diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 15b47bc..a720232 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -20,11 +20,14 @@ import sys import stat import shlex import shutil +import textwrap from enum import Enum from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, version_compare_many, listify +from ..mesonlib import ( + MesonException, Popen_safe, version_compare_many, version_compare, listify +) # These must be defined in this file to avoid cyclical references. @@ -43,18 +46,17 @@ class DependencyMethods(Enum): QMAKE = 'qmake' # Just specify the standard link arguments, assuming the operating system provides the library. SYSTEM = 'system' - # Detect using sdl2-config - SDLCONFIG = 'sdlconfig' - # Detect using pcap-config - PCAPCONFIG = 'pcap-config' - # Detect using cups-config - CUPSCONFIG = 'cups-config' - # Detect using libwmf-config - LIBWMFCONFIG = 'libwmf-config' # This is only supported on OSX - search the frameworks directory by name. EXTRAFRAMEWORK = 'extraframework' # Detect using the sysconfig module. SYSCONFIG = 'sysconfig' + # Specify using a "program"-config style tool + CONFIG_TOOL = 'config-tool' + # For backewards compatibility + SDLCONFIG = 'sdlconfig' + CUPSCONFIG = 'cups-config' + PCAPCONFIG = 'pcap-config' + LIBWMFCONFIG = 'libwmf-config' class Dependency: @@ -72,6 +74,16 @@ class Dependency: raise DependencyException('method {!r} is invalid'.format(method)) method = DependencyMethods(method) + # This sets per-too config methods which are deprecated to to the new + # generic CONFIG_TOOL value. + if method in [DependencyMethods.SDLCONFIG, DependencyMethods.CUPSCONFIG, + DependencyMethods.PCAPCONFIG, DependencyMethods.LIBWMFCONFIG]: + mlog.warning(textwrap.dedent("""\ + Configuration method {} has been deprecated in favor of + 'config-tool'. This will be removed in a future version of + meson.""".format(method))) + method = DependencyMethods.CONFIG_TOOL + # Set the detection method. If the method is set to auto, use any available method. # If method is set to a specific string, allow only that detection method. if method == DependencyMethods.AUTO: @@ -82,7 +94,7 @@ class Dependency: raise DependencyException( 'Unsupported detection method: {}, allowed methods are {}'.format( method.value, - mlog.format_list(map(lambda x: x.value, [DependencyMethods.AUTO] + self.get_methods())))) + mlog.format_list([x.value for x in [DependencyMethods.AUTO] + self.get_methods()]))) def __repr__(self): s = '<{0} {1}: {2}>' @@ -120,6 +132,9 @@ class Dependency: def get_pkgconfig_variable(self, variable_name): raise NotImplementedError('{!r} is not a pkgconfig dependency'.format(self.name)) + def get_configtool_variable(self, variable_name): + raise NotImplementedError('{!r} is not a config-tool dependency'.format(self.name)) + class InternalDependency(Dependency): def __init__(self, version, incdirs, compile_args, link_args, libraries, sources, ext_deps): @@ -167,6 +182,127 @@ class ExternalDependency(Dependency): return self.compiler +class ConfigToolDependency(ExternalDependency): + + """Class representing dependencies found using a config tool.""" + + tools = None + tool_name = None + + def __init__(self, name, environment, language, kwargs): + super().__init__('config-tool', environment, language, kwargs) + self.name = name + self.tools = listify(kwargs.get('tools', self.tools)) + + req_version = kwargs.get('version', None) + tool, version = self.find_config(req_version) + self.config = tool + self.is_found = self.report_config(version, req_version) + if not self.is_found: + self.config = None + return + self.version = version + + @classmethod + def factory(cls, name, environment, language, kwargs, tools, tool_name): + """Constructor for use in dependencies that can be found multiple ways. + + In addition to the standard constructor values, this constructor sets + the tool_name and tools values of the instance. + """ + # This deserves some explanation, because metaprogramming is hard. + # This uses type() to create a dynamic subclass of ConfigToolDependency + # with the tools and tool_name class attributes set, this class is then + # instantiated and returned. The reduce function (method) is also + # attached, since python's pickle module won't be able to do anything + # with this dynamically generated class otherwise. + def reduce(_): + return (cls.factory, + (name, environment, language, kwargs, tools, tool_name)) + sub = type('{}Dependency'.format(name.capitalize()), (cls, ), + {'tools': tools, 'tool_name': tool_name, '__reduce__': reduce}) + + return sub(name, environment, language, kwargs) + + def find_config(self, versions=None): + """Helper method that searchs for config tool binaries in PATH and + returns the one that best matches the given version requirements. + """ + if not isinstance(versions, list) and versions is not None: + versions = listify(versions) + + best_match = (None, None) + for tool in self.tools: + try: + p, out = Popen_safe([tool, '--version'])[:2] + except (FileNotFoundError, PermissionError): + continue + if p.returncode != 0: + continue + + out = out.strip() + # Some tools, like pcap-config don't supply a version, but also + # dont fail with --version, in that case just assume that there is + # only one verison and return it. + if not out: + return (tool, 'none') + if versions: + is_found = version_compare_many(out, versions)[0] + # This allows returning a found version without a config tool, + # which is useful to inform the user that you found version x, + # but y was required. + if not is_found: + tool = None + if best_match[1]: + if version_compare(out, '> {}'.format(best_match[1])): + best_match = (tool, out) + else: + best_match = (tool, out) + + return best_match + + def report_config(self, version, req_version): + """Helper method to print messages about the tool.""" + if self.config is None: + if version is not None: + mlog.log('found {} {!r} but need:'.format(self.tool_name, version), + req_version) + else: + mlog.log("No {} found; can't detect dependency".format(self.tool_name)) + mlog.log('Dependency {} found:'.format(self.name), mlog.red('NO')) + if self.required: + raise DependencyException('Dependency {} not found'.format(self.name)) + return False + mlog.log('Found {}:'.format(self.tool_name), mlog.bold(shutil.which(self.config)), + '({})'.format(version)) + mlog.log('Dependency {} found:'.format(self.name), mlog.green('YES')) + return True + + def get_config_value(self, args, stage): + p, out, err = Popen_safe([self.config] + args) + if p.returncode != 0: + if self.required: + raise DependencyException( + 'Could not generate {} for {}.\n{}'.format( + stage, self.name, err)) + return [] + return shlex.split(out) + + def get_methods(self): + return [DependencyMethods.AUTO, DependencyMethods.CONFIG_TOOL] + + def get_configtool_variable(self, variable_name): + p, out, _ = Popen_safe([self.config, '--{}'.format(variable_name)]) + if p.returncode != 0: + if self.required: + raise DependencyException( + 'Could not get variable "{}" for dependency {}'.format( + variable_name, self.name)) + variable = out.strip() + mlog.debug('Got config-tool variable {} : {}'.format(variable_name, variable)) + return variable + + 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. @@ -424,7 +560,11 @@ class ExternalProgram: with open(script) as f: first_line = f.readline().strip() if first_line.startswith('#!'): - commands = first_line[2:].split('#')[0].strip().split() + # In a shebang, everything before the first space is assumed to + # be the command to run and everything after the first space is + # the single argument to pass to that command. So we must split + # exactly once. + commands = first_line[2:].split('#')[0].strip().split(maxsplit=1) if mesonlib.is_windows(): # Windows does not have UNIX paths so remove them, # but don't remove Windows paths diff --git a/mesonbuild/dependencies/dev.py b/mesonbuild/dependencies/dev.py index 257bf7a..d15d5da 100644 --- a/mesonbuild/dependencies/dev.py +++ b/mesonbuild/dependencies/dev.py @@ -17,14 +17,16 @@ import os import re -import shlex import shutil from .. import mlog from .. import mesonlib from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list -from .base import DependencyException, ExternalDependency, PkgConfigDependency -from .base import strip_system_libdirs +from .base import ( + DependencyException, ExternalDependency, PkgConfigDependency, + strip_system_libdirs, ConfigToolDependency, +) + class GTestDependency(ExternalDependency): def __init__(self, environment, kwargs): @@ -109,19 +111,19 @@ class GMockDependency(ExternalDependency): self.is_found = False -class LLVMDependency(ExternalDependency): +class LLVMDependency(ConfigToolDependency): """ LLVM uses a special tool, llvm-config, which has arguments for getting c args, cxx args, and ldargs as well as version. """ # Ordered list of llvm-config binaries to try. Start with base, then try - # newest back to oldest (3.5 is abitrary), and finally the devel version. + # newest back to oldest (3.5 is arbitrary), and finally the devel version. # Please note that llvm-config-6.0 is a development snapshot and it should # not be moved to the beginning of the list. The only difference between # llvm-config-6.0 and llvm-config-devel is that the former is used by # Debian and the latter is used by FreeBSD. - llvm_config_bins = [ + tools = [ 'llvm-config', # base 'llvm-config-5.0', 'llvm-config50', # latest stable release 'llvm-config-4.0', 'llvm-config40', # old stable releases @@ -132,61 +134,31 @@ class LLVMDependency(ExternalDependency): 'llvm-config-3.5', 'llvm-config35', 'llvm-config-6.0', 'llvm-config-devel', # development snapshot ] + tool_name = 'llvm-config' __cpp_blacklist = {'-DNDEBUG'} def __init__(self, environment, kwargs): # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0 # the C linker works fine if only using the C API. - super().__init__('llvm-config', environment, 'cpp', kwargs) + super().__init__('config-tool', environment, 'cpp', kwargs) self.provided_modules = [] self.required_modules = set() - self.llvmconfig = None - self.static = kwargs.get('static', False) - self.__best_found = None - # FIXME: Support multiple version requirements ala PkgConfigDependency - req_version = kwargs.get('version', None) - self.check_llvmconfig(req_version) - if self.llvmconfig is None: - if self.__best_found is not None: - mlog.log('found {!r} but need:'.format(self.__best_found), - req_version) - else: - mlog.log("No llvm-config found; can't detect dependency") - mlog.log('Dependency LLVM found:', mlog.red('NO')) - if self.required: - raise DependencyException('Dependency LLVM not found') + if not self.is_found: return + self.static = kwargs.get('static', False) - p, out, err = Popen_safe([self.llvmconfig, '--version']) - if p.returncode != 0: - mlog.debug('stdout: {}\nstderr: {}'.format(out, err)) - if self.required: - raise DependencyException('Dependency LLVM not found') - mlog.log('Dependency LLVM found:', mlog.red('NO')) - return - - mlog.log('Dependency LLVM found:', mlog.green('YES')) - self.is_found = True - - # Currently meson doesn't really atempt to handle pre-release versions, + # Currently meson doesn't really attempt to handle pre-release versions, # so strip the 'svn' off the end, since it will probably cuase problems # for users who want the patch version. - self.version = out.strip().rstrip('svn') - - p, out, err = Popen_safe([self.llvmconfig, '--components']) - if p.returncode != 0: - raise DependencyException('Could not generate modules for LLVM:\n' + err) - self.provided_modules = shlex.split(out) + self.version = self.version.rstrip('svn') + self.provided_modules = self.get_config_value(['--components'], 'modules') modules = stringlistify(extract_as_list(kwargs, 'modules')) self.check_components(modules) opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules')) self.check_components(opt_modules, required=False) - p, out, err = Popen_safe([self.llvmconfig, '--cppflags']) - if p.returncode != 0: - raise DependencyException('Could not generate includedir for LLVM:\n' + err) - cargs = mesonlib.OrderedSet(shlex.split(out)) + cargs = set(self.get_config_value(['--cppflags'], 'compile_args')) self.compile_args = list(cargs.difference(self.__cpp_blacklist)) if version_compare(self.version, '>= 3.9'): @@ -198,11 +170,9 @@ class LLVMDependency(ExternalDependency): def _set_new_link_args(self): """How to set linker args for LLVM versions >= 3.9""" link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared'] - p, out, err = Popen_safe( - [self.llvmconfig, '--libs', '--ldflags'] + link_args + list(self.required_modules)) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - self.link_args = shlex.split(out) + self.link_args = self.get_config_value( + ['--libs', '--ldflags'] + link_args + list(self.required_modules), + 'link_args') def _set_old_link_args(self): """Setting linker args for older versions of llvm. @@ -213,20 +183,15 @@ class LLVMDependency(ExternalDependency): of course we do. """ if self.static: - p, out, err = Popen_safe( - [self.llvmconfig, '--libs', '--ldflags', '--system-libs'] + list(self.required_modules)) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - self.link_args = shlex.split(out) + self.link_args = self.get_config_value( + ['--libs', '--ldflags', '--system-libs'] + list(self.required_modules), + 'link_args') else: # llvm-config will provide arguments for static linking, so we get # to figure out for ourselves what to link with. We'll do that by # checking in the directory provided by --libdir for a library # called libLLVM-<ver>.(so|dylib|dll) - p, out, err = Popen_safe([self.llvmconfig, '--libdir']) - if p.returncode != 0: - raise DependencyException('Could not generate libs for LLVM:\n' + err) - libdir = out.strip() + libdir = self.get_config_value(['--libdir'], 'link_args')[0] expected_name = 'libLLVM-{}'.format(self.version) re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name)) @@ -259,34 +224,6 @@ class LLVMDependency(ExternalDependency): self.required_modules.add(mod) mlog.log('LLVM module', mod, 'found:', mlog.green('YES')) - def check_llvmconfig(self, version_req): - """Try to find the highest version of llvm-config.""" - for llvmconfig in self.llvm_config_bins: - try: - p, out = Popen_safe([llvmconfig, '--version'])[0:2] - out = out.strip() - if p.returncode != 0: - continue - if version_req: - if version_compare(out, version_req, strict=True): - if self.__best_found and version_compare( - out, '<={}'.format(self.__best_found), strict=True): - continue - self.__best_found = out - self.llvmconfig = llvmconfig - else: - # If no specific version is requested use the first version - # found, since that should be the best. - self.__best_found = out - self.llvmconfig = llvmconfig - break - except (FileNotFoundError, PermissionError): - pass - if self.__best_found: - mlog.log('Found llvm-config:', - mlog.bold(shutil.which(self.llvmconfig)), - '({})'.format(out.strip())) - def need_threads(self): return True diff --git a/mesonbuild/dependencies/misc.py b/mesonbuild/dependencies/misc.py index 4a023e4..41666a3 100644 --- a/mesonbuild/dependencies/misc.py +++ b/mesonbuild/dependencies/misc.py @@ -1,4 +1,4 @@ -# Copyright 2013-2017 The Meson development team +# Copyright 2013-2017 The Meson development team # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -26,8 +26,11 @@ from .. import mesonlib from ..mesonlib import Popen_safe, extract_as_list from ..environment import detect_cpu_family -from .base import DependencyException, DependencyMethods -from .base import ExternalDependency, ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency +from .base import ( + DependencyException, DependencyMethods, ExternalDependency, + ExternalProgram, ExtraFrameworkDependency, PkgConfigDependency, + ConfigToolDependency, +) # On windows 3 directory layouts are supported: # * The default layout (versioned) installed: @@ -622,8 +625,10 @@ class Python3Dependency(ExternalDependency): elif mesonlib.is_osx() and DependencyMethods.EXTRAFRAMEWORK in self.methods: # In OSX the Python 3 framework does not have a version # number in its name. - fw = ExtraFrameworkDependency('python', False, None, self.env, - self.language, kwargs) + # There is a python in /System/Library/Frameworks, but that's + # python 2, Python 3 will always bin in /Library + fw = ExtraFrameworkDependency( + 'python', False, '/Library/Frameworks', self.env, self.language, kwargs) if fw.found(): self.compile_args = fw.get_compile_args() self.link_args = fw.get_link_args() @@ -687,9 +692,9 @@ class Python3Dependency(ExternalDependency): class PcapDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('pcap', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('pcap', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -700,25 +705,26 @@ class PcapDependency(ExternalDependency): return except Exception as e: mlog.debug('Pcap not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.PCAPCONFIG in self.methods: - pcapconf = shutil.which('pcap-config') - if pcapconf: - stdo = Popen_safe(['pcap-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['pcap-config', '--libs'])[1] - self.link_args = stdo.strip().split() - self.version = self.get_pcap_lib_version() - self.is_found = True - mlog.log('Dependency', mlog.bold('pcap'), 'found:', - mlog.green('YES'), '(%s)' % pcapconf) - return - mlog.debug('Could not find pcap-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'pcap', environment, None, kwargs, ['pcap-config'], 'pcap-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-tool' + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.version = self.get_pcap_lib_version() + self.is_found = True + return + except Exception as e: + mlog.debug('Pcap not found via pcap-config. Trying next, error was:', str(e)) def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.PCAPCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] def get_pcap_lib_version(self): return self.compiler.get_return_value('pcap_lib_version', 'string', @@ -728,9 +734,9 @@ class PcapDependency(ExternalDependency): class CupsDependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('cups', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('cups', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -741,20 +747,20 @@ class CupsDependency(ExternalDependency): return except Exception as e: mlog.debug('cups not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.CUPSCONFIG in self.methods: - cupsconf = shutil.which('cups-config') - if cupsconf: - stdo = Popen_safe(['cups-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['cups-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['cups-config', '--version'])[1] - self.version = stdo.strip().split() - self.is_found = True - mlog.log('Dependency', mlog.bold('cups'), 'found:', - mlog.green('YES'), '(%s)' % cupsconf) - return - mlog.debug('Could not find cups-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'cups', environment, None, kwargs, ['cups-config'], 'cups-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-tool' + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('cups not found via cups-config. Trying next, error was:', str(e)) if DependencyMethods.EXTRAFRAMEWORK in self.methods: if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency('cups', False, None, self.env, @@ -769,9 +775,9 @@ class CupsDependency(ExternalDependency): def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.CUPSCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] class LibWmfDependency(ExternalDependency): @@ -790,26 +796,27 @@ class LibWmfDependency(ExternalDependency): return except Exception as e: mlog.debug('LibWmf not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.LIBWMFCONFIG in self.methods: - libwmfconf = shutil.which('libwmf-config') - if libwmfconf: - stdo = Popen_safe(['libwmf-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['libwmf-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['libwmf-config', '--version'])[1] - self.version = stdo.strip() - self.is_found = True - mlog.log('Dependency', mlog.bold('libwmf'), 'found:', - mlog.green('YES'), '(%s)' % libwmfconf) - return - mlog.debug('Could not find libwmf-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'libwmf', environment, None, kwargs, ['libwmf-config'], 'libwmf-config') + if ctdep.found(): + self.config = ctdep.config + self.type_name = 'config-too' + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.link_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('cups not found via libwmf-config. Trying next, error was:', str(e)) def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.LIBWMFCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] + # Generated with boost_names.py BOOST_LIBS = [ diff --git a/mesonbuild/dependencies/ui.py b/mesonbuild/dependencies/ui.py index 837149c..dd04580 100644 --- a/mesonbuild/dependencies/ui.py +++ b/mesonbuild/dependencies/ui.py @@ -23,13 +23,16 @@ from collections import OrderedDict from .. import mlog from .. import mesonlib -from ..mesonlib import MesonException, Popen_safe, version_compare -from ..mesonlib import extract_as_list, for_windows +from ..mesonlib import ( + MesonException, Popen_safe, extract_as_list, for_windows, + version_compare_many +) from ..environment import detect_cpu from .base import DependencyException, DependencyMethods from .base import ExternalDependency, ExternalProgram from .base import ExtraFrameworkDependency, PkgConfigDependency +from .base import ConfigToolDependency class GLDependency(ExternalDependency): @@ -70,49 +73,48 @@ class GLDependency(ExternalDependency): return [DependencyMethods.PKGCONFIG] -class GnuStepDependency(ExternalDependency): +class GnuStepDependency(ConfigToolDependency): + + tools = ['gnustep-config'] + tool_name = 'gnustep-config' + def __init__(self, environment, kwargs): super().__init__('gnustep', environment, 'objc', kwargs) + if not self.is_found: + return self.modules = kwargs.get('modules', []) - self.detect() - - def detect(self): - self.confprog = 'gnustep-config' + self.compile_args = self.filter_args( + self.get_config_value(['--objc-flags'], 'compile_args')) + self.link_args = self.weird_filter(self.get_config_value( + ['--gui-libs' if 'gui' in self.modules else '--base-libs'], + 'link_args')) + + def find_config(self, versions=None): + tool = self.tools[0] try: - gp = Popen_safe([self.confprog, '--help'])[0] + p, out = Popen_safe([tool, '--help'])[:2] except (FileNotFoundError, PermissionError): - mlog.log('Dependency GnuStep found:', mlog.red('NO'), '(no gnustep-config)') - return - if gp.returncode != 0: - mlog.log('Dependency GnuStep found:', mlog.red('NO')) - return - if 'gui' in self.modules: - arg = '--gui-libs' - else: - arg = '--base-libs' - fp, flagtxt, flagerr = Popen_safe([self.confprog, '--objc-flags']) - if fp.returncode != 0: - raise DependencyException('Error getting objc-args: %s %s' % (flagtxt, flagerr)) - args = flagtxt.split() - self.compile_args = self.filter_args(args) - fp, libtxt, liberr = Popen_safe([self.confprog, arg]) - if fp.returncode != 0: - raise DependencyException('Error getting objc-lib args: %s %s' % (libtxt, liberr)) - self.link_args = self.weird_filter(libtxt.split()) - self.version = self.detect_version() - self.is_found = True - mlog.log('Dependency', mlog.bold('GnuStep'), 'found:', - mlog.green('YES'), self.version) + return (None, None) + if p.returncode != 0: + return (None, None) + self.config = tool + found_version = self.detect_version() + if versions and not version_compare_many(found_version, versions)[0]: + return (None, found_version) + + return (tool, found_version) def weird_filter(self, elems): - """When building packages, the output of the enclosing Make -is sometimes mixed among the subprocess output. I have no idea -why. As a hack filter out everything that is not a flag.""" + """When building packages, the output of the enclosing Make is + sometimes mixed among the subprocess output. I have no idea why. As a + hack filter out everything that is not a flag. + """ return [e for e in elems if e.startswith('-')] def filter_args(self, args): - """gnustep-config returns a bunch of garbage args such - as -O2 and so on. Drop everything that is not needed.""" + """gnustep-config returns a bunch of garbage args such as -O2 and so + on. Drop everything that is not needed. + """ result = [] for f in args: if f.startswith('-D') \ @@ -124,8 +126,8 @@ why. As a hack filter out everything that is not a flag.""" return result def detect_version(self): - gmake = self.get_variable('GNUMAKE') - makefile_dir = self.get_variable('GNUSTEP_MAKEFILES') + gmake = self.get_config_value(['--variable=GNUMAKE'], 'variable')[0] + makefile_dir = self.get_config_value(['--variable=GNUSTEP_MAKEFILES'], 'variable')[0] # This Makefile has the GNUStep version set base_make = os.path.join(makefile_dir, 'Additional', 'base.make') # Print the Makefile variable passed as the argument. For instance, if @@ -145,13 +147,6 @@ why. As a hack filter out everything that is not a flag.""" version = '1' return version - def get_variable(self, var): - p, o, e = Popen_safe([self.confprog, '--variable=' + var]) - if p.returncode != 0 and self.required: - raise DependencyException('{!r} for variable {!r} failed to run' - ''.format(self.confprog, var)) - return o.strip() - class QtBaseDependency(ExternalDependency): def __init__(self, name, env, kwargs): @@ -380,9 +375,9 @@ class Qt5Dependency(QtBaseDependency): class SDL2Dependency(ExternalDependency): def __init__(self, environment, kwargs): super().__init__('sdl2', environment, None, kwargs) + kwargs['required'] = False if DependencyMethods.PKGCONFIG in self.methods: try: - kwargs['required'] = False pcdep = PkgConfigDependency('sdl2', environment, kwargs) if pcdep.found(): self.type_name = 'pkgconfig' @@ -393,20 +388,20 @@ class SDL2Dependency(ExternalDependency): return except Exception as e: mlog.debug('SDL 2 not found via pkgconfig. Trying next, error was:', str(e)) - if DependencyMethods.SDLCONFIG in self.methods: - sdlconf = shutil.which('sdl2-config') - if sdlconf: - stdo = Popen_safe(['sdl2-config', '--cflags'])[1] - self.compile_args = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--libs'])[1] - self.link_args = stdo.strip().split() - stdo = Popen_safe(['sdl2-config', '--version'])[1] - self.version = stdo.strip() - self.is_found = True - mlog.log('Dependency', mlog.bold('sdl2'), 'found:', mlog.green('YES'), - self.version, '(%s)' % sdlconf) - return - mlog.debug('Could not find sdl2-config binary, trying next.') + if DependencyMethods.CONFIG_TOOL in self.methods: + try: + ctdep = ConfigToolDependency.factory( + 'sdl2', environment, None, kwargs, ['sdl2-config'], 'sdl2-config') + if ctdep.found(): + self.type_name = 'config-tool' + self.config = ctdep.config + self.version = ctdep.version + self.compile_args = ctdep.get_config_value(['--cflags'], 'compile_args') + self.links_args = ctdep.get_config_value(['--libs'], 'link_args') + self.is_found = True + return + except Exception as e: + mlog.debug('SDL 2 not found via sdl2-config. Trying next, error was:', str(e)) if DependencyMethods.EXTRAFRAMEWORK in self.methods: if mesonlib.is_osx(): fwdep = ExtraFrameworkDependency('sdl2', False, None, self.env, @@ -421,54 +416,25 @@ class SDL2Dependency(ExternalDependency): def get_methods(self): if mesonlib.is_osx(): - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG, DependencyMethods.EXTRAFRAMEWORK] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL, DependencyMethods.EXTRAFRAMEWORK] else: - return [DependencyMethods.PKGCONFIG, DependencyMethods.SDLCONFIG] + return [DependencyMethods.PKGCONFIG, DependencyMethods.CONFIG_TOOL] -class WxDependency(ExternalDependency): - wx_found = None +class WxDependency(ConfigToolDependency): + + tools = ['wx-config-3.0', 'wx-config'] + tool_name = 'wx-config' def __init__(self, environment, kwargs): - super().__init__('wx', environment, None, kwargs) - self.version = 'none' - if WxDependency.wx_found is None: - self.check_wxconfig() - else: - self.wxc = WxDependency.wx_found - if not WxDependency.wx_found: - mlog.log("Neither wx-config-3.0 nor wx-config found; can't detect dependency") + super().__init__('WxWidgets', environment, None, kwargs) + if not self.is_found: return - - # FIXME: This should print stdout and stderr using mlog.debug - p, out = Popen_safe([self.wxc, '--version'])[0:2] - if p.returncode != 0: - mlog.log('Dependency wxwidgets found:', mlog.red('NO')) - else: - self.version = out.strip() - # FIXME: Support multiple version reqs like PkgConfigDependency - version_req = kwargs.get('version', None) - if version_req is not None: - if not version_compare(self.version, version_req, strict=True): - mlog.log('Wxwidgets version %s does not fullfill requirement %s' % - (self.version, version_req)) - return - mlog.log('Dependency wxwidgets found:', mlog.green('YES')) - self.is_found = True - self.requested_modules = self.get_requested(kwargs) - # wx-config seems to have a cflags as well but since it requires C++, - # this should be good, at least for now. - p, out = Popen_safe([self.wxc, '--cxxflags'])[0:2] - # FIXME: this error should only be raised if required is true - if p.returncode != 0: - raise DependencyException('Could not generate cargs for wxwidgets.') - self.compile_args = out.split() - - # FIXME: this error should only be raised if required is true - p, out = Popen_safe([self.wxc, '--libs'] + self.requested_modules)[0:2] - if p.returncode != 0: - raise DependencyException('Could not generate libs for wxwidgets.') - self.link_args = out.split() + self.requested_modules = self.get_requested(kwargs) + # wx-config seems to have a cflags as well but since it requires C++, + # this should be good, at least for now. + self.compile_args = self.get_config_value(['--cxxflags'], 'compile_args') + self.link_args = self.get_config_value(['--libs'], 'link_args') def get_requested(self, kwargs): if 'modules' not in kwargs: @@ -479,20 +445,6 @@ class WxDependency(ExternalDependency): raise DependencyException('wxwidgets module argument is not a string') return candidates - def check_wxconfig(self): - for wxc in ['wx-config-3.0', 'wx-config']: - try: - p, out = Popen_safe([wxc, '--version'])[0:2] - if p.returncode == 0: - mlog.log('Found wx-config:', mlog.bold(shutil.which(wxc)), - '(%s)' % out.strip()) - self.wxc = wxc - WxDependency.wx_found = wxc - return - except (FileNotFoundError, PermissionError): - pass - WxDependency.wxconfig_found = False - mlog.log('Found wx-config:', mlog.red('NO')) class VulkanDependency(ExternalDependency): def __init__(self, environment, kwargs): diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py index 58cc9b9..0cb1450 100644 --- a/mesonbuild/environment.py +++ b/mesonbuild/environment.py @@ -154,7 +154,7 @@ def detect_windows_arch(compilers): # Check if we're using and inside an MSVC toolchain environment if compiler.id == 'msvc' and 'VCINSTALLDIR' in os.environ: # 'Platform' is only set when the target arch is not 'x86'. - # It's 'x64' when targetting x86_64 and 'arm' when targetting ARM. + # It's 'x64' when targeting x86_64 and 'arm' when targeting ARM. platform = os.environ.get('Platform', 'x86').lower() if platform == 'x86': return platform diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index fbf9a21..3e89305 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -274,6 +274,7 @@ class DependencyHolder(InterpreterObject, ObjectHolder): 'type_name': self.type_name_method, 'version': self.version_method, 'get_pkgconfig_variable': self.pkgconfig_method, + 'get_configtool_variable': self.configtool_method, }) def type_name_method(self, args, kwargs): @@ -296,6 +297,15 @@ class DependencyHolder(InterpreterObject, ObjectHolder): raise InterpreterException('Variable name must be a string.') return self.held_object.get_pkgconfig_variable(varname) + def configtool_method(self, args, kwargs): + args = listify(args) + if len(args) != 1: + raise InterpreterException('get_configtool_variable takes exactly one argument.') + varname = args[0] + if not isinstance(varname, str): + raise InterpreterException('Variable name must be a string.') + return self.held_object.get_configtool_variable(varname) + class InternalDependencyHolder(InterpreterObject, ObjectHolder): def __init__(self, dep): InterpreterObject.__init__(self) @@ -1358,7 +1368,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'build_target': build_target_kwargs, 'configure_file': {'input', 'output', 'configuration', 'command', 'install_dir', 'capture', 'install'}, 'custom_target': {'input', 'output', 'command', 'install', 'install_dir', 'build_always', 'capture', 'depends', 'depend_files', 'depfile', 'build_by_default'}, - 'dependency': {'default_options', 'fallback', 'language', 'method', 'modules', 'native', 'required', 'static', 'version'}, + 'dependency': {'default_options', 'fallback', 'language', 'method', 'modules', 'optional_modules', 'native', 'required', 'static', 'version'}, 'declare_dependency': {'include_directories', 'link_with', 'sources', 'dependencies', 'compile_args', 'link_args', 'version'}, 'executable': exe_kwargs, 'find_program': {'required', 'native'}, @@ -1374,6 +1384,7 @@ permitted_kwargs = {'add_global_arguments': {'language'}, 'shared_library': shlib_kwargs, 'shared_module': shmod_kwargs, 'static_library': stlib_kwargs, + 'subdir': {'if_found'}, 'subproject': {'version', 'default_options'}, 'test': {'args', 'env', 'is_parallel', 'should_fail', 'timeout', 'workdir', 'suite'}, 'vcs_tag': {'input', 'output', 'fallback', 'command', 'replace_string'}, @@ -2477,7 +2488,7 @@ to directly access options of other subprojects.''') self.build.man.append(m) return m - @noKwargs + @permittedKwargs(permitted_kwargs['subdir']) def func_subdir(self, node, args, kwargs): self.validate_arguments(args, 1, [str]) if '..' in args[0]: @@ -2486,6 +2497,11 @@ to directly access options of other subprojects.''') raise InvalidArguments('Must not go into subprojects dir with subdir(), use subproject() instead.') if self.subdir == '' and args[0].startswith('meson-'): raise InvalidArguments('The "meson-" prefix is reserved and cannot be used for top-level subdir().') + for i in mesonlib.extract_as_list(kwargs, 'if_found'): + if not hasattr(i, 'found_method'): + raise InterpreterException('Object used in if_found does not have a found method.') + if not i.found_method([], {}): + return prev_subdir = self.subdir subdir = os.path.join(prev_subdir, args[0]) if os.path.isabs(subdir): diff --git a/mesonbuild/mconf.py b/mesonbuild/mconf.py index 09b0f12..771e9ee 100644 --- a/mesonbuild/mconf.py +++ b/mesonbuild/mconf.py @@ -51,7 +51,7 @@ class Conf: with open(self.coredata_file, 'wb') as f: pickle.dump(self.coredata, f) # We don't write the build file because any changes to it - # are erased when Meson is executed the next time, i.e. whne + # are erased when Meson is executed the next time, i.e. when # Ninja is run. def print_aligned(self, arr): @@ -184,7 +184,7 @@ class Conf: coarr = [] for k in okeys: o = self.coredata.base_options[k] - coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': ''}) + coarr.append({'name': k, 'descr': o.description, 'value': o.value, 'choices': o.choices}) self.print_aligned(coarr) print('') print('Compiler arguments:') diff --git a/mesonbuild/mesonlib.py b/mesonbuild/mesonlib.py index b432bf3..a35345b 100644 --- a/mesonbuild/mesonlib.py +++ b/mesonbuild/mesonlib.py @@ -32,6 +32,13 @@ def detect_meson_py_location(): # $ meson <args> (gets run from /usr/bin/meson) in_path_exe = shutil.which(c_fname) if in_path_exe: + # Special case: when run like "./meson.py <opts>" and user has + # period in PATH, we need to expand it out, because, for example, + # "ninja test" will be run from a different directory. + if '.' in os.environ['PATH'].split(':'): + p, f = os.path.split(in_path_exe) + if p == '' or p == '.': + return os.path.join(os.getcwd(), f) return in_path_exe # $ python3 ./meson.py <args> if os.path.exists(c): @@ -52,7 +59,6 @@ else: python_command = [sys.executable] meson_command = python_command + [detect_meson_py_location()] - # Put this in objects that should not get dumped to pickle files # by accident. import threading @@ -528,7 +534,7 @@ def replace_if_different(dst, dst_tmp): # unnecessary rebuilds. different = True try: - with open(dst, 'r') as f1, open(dst_tmp, 'r') as f2: + with open(dst, 'rb') as f1, open(dst_tmp, 'rb') as f2: if f1.read() == f2.read(): different = False except FileNotFoundError: diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py index fa8c9e3..f261935 100644 --- a/mesonbuild/mesonmain.py +++ b/mesonbuild/mesonmain.py @@ -300,7 +300,11 @@ def run(original_args, mainfile=None): elif cmd_name == 'rewrite': return rewriter.run(remaining_args) elif cmd_name == 'configure': - return mconf.run(remaining_args) + try: + return mconf.run(remaining_args) + except MesonException as e: + mlog.log(mlog.red('\nError configuring project:'), e) + sys.exit(1) elif cmd_name == 'wrap': return wraptool.run(remaining_args) elif cmd_name == 'runpython': diff --git a/mesonbuild/mlog.py b/mesonbuild/mlog.py index 18c1e6a..a0d07ec 100644 --- a/mesonbuild/mlog.py +++ b/mesonbuild/mlog.py @@ -18,8 +18,10 @@ import sys, os, platform, io information about Meson runs. Some output goes to screen, some to logging dir and some goes to both.""" -colorize_console = platform.system().lower() != 'windows' and os.isatty(sys.stdout.fileno()) and \ - os.environ.get('TERM') != 'dumb' +if platform.system().lower() == 'windows': + colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('ANSICON') +else: + colorize_console = os.isatty(sys.stdout.fileno()) and os.environ.get('TERM') != 'dumb' log_dir = None log_file = None log_fname = 'meson-log.txt' diff --git a/mesonbuild/modules/gnome.py b/mesonbuild/modules/gnome.py index 7e61242..f916c2c 100644 --- a/mesonbuild/modules/gnome.py +++ b/mesonbuild/modules/gnome.py @@ -220,9 +220,10 @@ class GnomeModule(ExtensionModule): input_file, '--generate-dependencies'] + # Prefer generated files over source files + cmd += ['--sourcedir', state.subdir] # Current build dir for source_dir in source_dirs: cmd += ['--sourcedir', os.path.join(state.subdir, source_dir)] - cmd += ['--sourcedir', state.subdir] # Current dir pc, stdout, stderr = Popen_safe(cmd, cwd=state.environment.get_source_dir()) if pc.returncode != 0: @@ -240,25 +241,20 @@ class GnomeModule(ExtensionModule): # # If there are multiple generated resource files with the same basename # then this code will get confused. - def exists_in_srcdir(f): return os.path.exists(os.path.join(state.environment.get_source_dir(), f)) - missing_dep_files = [f for f in dep_files if not exists_in_srcdir(f)] depends = [] subdirs = [] - for missing in missing_dep_files: - found = False - missing_basename = os.path.basename(missing) - + for resfile in dep_files[:]: + resbasename = os.path.basename(resfile) for dep in dependencies: if hasattr(dep, 'held_object'): dep = dep.held_object if isinstance(dep, mesonlib.File): - if dep.fname != missing_basename: + if dep.fname != resbasename: continue - found = True - dep_files.remove(missing) + dep_files.remove(resfile) dep_files.append(dep) subdirs.append(dep.subdir) break @@ -266,12 +262,11 @@ class GnomeModule(ExtensionModule): fname = None outputs = {(o, os.path.basename(o)) for o in dep.get_outputs()} for o, baseo in outputs: - if baseo == missing_basename: + if baseo == resbasename: fname = o break if fname is not None: - found = True - dep_files.remove(missing) + dep_files.remove(resfile) dep_files.append( mesonlib.File( is_built=True, @@ -280,16 +275,13 @@ class GnomeModule(ExtensionModule): depends.append(dep) subdirs.append(dep.get_subdir()) break - else: - raise RuntimeError('Unreachable code.') - - if not found: - raise MesonException( - 'Resource "%s" listed in "%s" was not found. If this is a ' - 'generated file, pass the target that generates it to ' - 'gnome.compile_resources() using the "dependencies" ' - 'keyword argument.' % (missing, input_file)) - + else: + if not exists_in_srcdir(resfile): + raise MesonException( + 'Resource "%s" listed in "%s" was not found. If this is a ' + 'generated file, pass the target that generates it to ' + 'gnome.compile_resources() using the "dependencies" ' + 'keyword argument.' % (resfile, input_file)) return dep_files, depends, subdirs def _get_link_args(self, state, lib, depends=None, include_rpath=False, @@ -723,7 +715,8 @@ This will become a hard error in the future.''') @permittedKwargs({'main_xml', 'main_sgml', 'src_dir', 'dependencies', 'install', 'install_dir', 'scan_args', 'scanobjs_args', 'gobject_typesfile', 'fixxref_args', 'html_args', 'html_assets', 'content_files', - 'mkdb_args', 'ignore_headers', 'include_directories'}) + 'mkdb_args', 'ignore_headers', 'include_directories', + 'namespace', 'mode', 'expand_content_files'}) def gtkdoc(self, state, args, kwargs): if len(args) != 1: raise MesonException('Gtkdoc must have one positional argument.') diff --git a/mesonbuild/modules/qt.py b/mesonbuild/modules/qt.py index 463bf01..54e2c73 100644 --- a/mesonbuild/modules/qt.py +++ b/mesonbuild/modules/qt.py @@ -49,7 +49,11 @@ class QtBaseModule: # What kind of an idiot thought that was a good idea? for compiler, compiler_name in ((self.moc, "Moc"), (self.uic, "Uic"), (self.rcc, "Rcc"), (self.lrelease, "lrelease")): if compiler.found(): - stdout, stderr = Popen_safe(compiler.get_command() + ['-version'])[1:3] + # Workaround since there is no easy way to know which tool/version support which flag + for flag in ['-v', '-version']: + p, stdout, stderr = Popen_safe(compiler.get_command() + [flag])[0:3] + if p.returncode == 0: + break stdout = stdout.strip() stderr = stderr.strip() if 'Qt {}'.format(self.qt_version) in stderr: diff --git a/mesonbuild/mparser.py b/mesonbuild/mparser.py index 8400a1a..0465d24 100644 --- a/mesonbuild/mparser.py +++ b/mesonbuild/mparser.py @@ -111,6 +111,7 @@ 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 @@ -139,10 +140,9 @@ class Lexer: elif tid == 'dblquote': raise ParseException('Double quotes are not supported. Use single quotes.', self.getline(line_start), lineno, col) elif tid == 'string': - value = match_text[1:-1]\ - .replace(r"\'", "'")\ - .replace(r" \\ ".strip(), r" \ ".strip())\ - .replace("\\n", "\n") + value = match_text[1:-1].replace(r"\'", "'") + value = newline_rx.sub(r'\1\n', value) + value = value.replace(r" \\ ".strip(), r" \ ".strip()) elif tid == 'multiline_string': tid = 'string' value = match_text[3:-3] diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 30322aa..b39f5af 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -482,35 +482,41 @@ TIMEOUT: %4d numlen = len('%d' % len(tests)) self.open_log_files() wrap = self.get_wrapper() + startdir = os.getcwd() + if self.options.wd: + os.chdir(self.options.wd) - for _ in range(self.options.repeat): - for i, test in enumerate(tests): - visible_name = self.get_pretty_suite(test) - - if self.options.gdb: - test.timeout = None - - if not test.is_parallel or self.options.gdb: - self.drain_futures(futures) - futures = [] - res = self.run_single_test(wrap, test) - self.print_stats(numlen, tests, visible_name, res, i) - else: - if not executor: - executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) - f = executor.submit(self.run_single_test, wrap, test) - futures.append((f, numlen, tests, visible_name, i)) + try: + for _ in range(self.options.repeat): + for i, test in enumerate(tests): + visible_name = self.get_pretty_suite(test) + + if self.options.gdb: + test.timeout = None + + if not test.is_parallel or self.options.gdb: + self.drain_futures(futures) + futures = [] + res = self.run_single_test(wrap, test) + self.print_stats(numlen, tests, visible_name, res, i) + else: + if not executor: + executor = conc.ThreadPoolExecutor(max_workers=self.options.num_processes) + f = executor.submit(self.run_single_test, wrap, test) + futures.append((f, numlen, tests, visible_name, i)) + if self.options.repeat > 1 and self.fail_count: + break if self.options.repeat > 1 and self.fail_count: break - if self.options.repeat > 1 and self.fail_count: - break - self.drain_futures(futures) - self.print_summary() - self.print_collected_logs() + self.drain_futures(futures) + self.print_summary() + self.print_collected_logs() - if self.logfilename: - print('Full log written to %s' % self.logfilename) + if self.logfilename: + print('Full log written to %s' % self.logfilename) + finally: + os.chdir(startdir) def drain_futures(self, futures): for i in futures: diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py index 01dd036..22a263e 100644 --- a/mesonbuild/optinterpreter.py +++ b/mesonbuild/optinterpreter.py @@ -84,9 +84,25 @@ def ComboParser(name, description, kwargs): raise OptionException('Combo choice elements must be strings.') return coredata.UserComboOption(name, description, choices, kwargs.get('value', choices[0])) +@permitted_kwargs({'value', 'choices'}) +def string_array_parser(name, description, kwargs): + if 'choices' not in kwargs: + raise OptionException('Array option missing "choices" keyword.') + choices = kwargs['choices'] + if not isinstance(choices, list): + raise OptionException('Array choices must be an array.') + for i in choices: + if not isinstance(i, str): + raise OptionException('Array choice elements must be strings.') + value = kwargs.get('value', choices) + if not isinstance(value, list): + raise OptionException('Array choices must be passed as an array.') + return coredata.UserStringArrayOption(name, description, value, choices=choices) + option_types = {'string': StringParser, 'boolean': BooleanParser, 'combo': ComboParser, + 'array': string_array_parser, } class OptionInterpreter: diff --git a/run_unittests.py b/run_unittests.py index 52cd419..79805b2 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -18,10 +18,12 @@ import shlex import subprocess import re, json import tempfile +import textwrap import os import shutil import sys import unittest +from unittest import mock from configparser import ConfigParser from glob import glob from pathlib import PurePath @@ -1565,6 +1567,53 @@ int main(int argc, char **argv) { cargs = ['-I' + incdir.as_posix()] self.assertEqual(foo_dep.get_compile_args(), cargs) + def test_array_option_change(self): + def get_opt(): + opts = self.introspect('--buildoptions') + for x in opts: + if x.get('name') == 'list': + return x + raise Exception(opts) + + expected = { + 'name': 'list', + 'description': 'list', + 'type': 'stringarray', + 'value': ['foo', 'bar'], + } + tdir = os.path.join(self.unit_test_dir, '18 array option') + self.init(tdir) + original = get_opt() + self.assertDictEqual(original, expected) + + expected['value'] = ['oink', 'boink'] + self.setconf('-Dlist=oink,boink') + changed = get_opt() + self.assertEqual(changed, expected) + + def test_array_option_bad_change(self): + def get_opt(): + opts = self.introspect('--buildoptions') + for x in opts: + if x.get('name') == 'list': + return x + raise Exception(opts) + + expected = { + 'name': 'list', + 'description': 'list', + 'type': 'stringarray', + 'value': ['foo', 'bar'], + } + tdir = os.path.join(self.unit_test_dir, '18 array option') + self.init(tdir) + original = get_opt() + self.assertDictEqual(original, expected) + with self.assertRaises(subprocess.CalledProcessError): + self.setconf('-Dlist=bad') + changed = get_opt() + self.assertDictEqual(changed, expected) + class FailureTests(BasePlatformTests): ''' @@ -1663,7 +1712,7 @@ class FailureTests(BasePlatformTests): raise unittest.SkipTest('wx-config or wx-config-3.0 found') self.assertMesonRaises("dependency('wxwidgets')", self.dnf) self.assertMesonOutputs("dependency('wxwidgets', required : false)", - "nor wx-config found") + "No wx-config found;") def test_wx_dependency(self): if not shutil.which('wx-config-3.0') and not shutil.which('wx-config'): @@ -1960,7 +2009,7 @@ class LinuxlikeTests(BasePlatformTests): # Verify that -I flags from the `args` kwarg are first # This is set in the '43 has function' test case self.assertEqual(cmd[1], '-I/tmp') - # Verify that -O3 set via the environment is overriden by -O0 + # Verify that -O3 set via the environment is overridden by -O0 Oargs = [arg for arg in cmd if arg.startswith('-O')] self.assertEqual(Oargs, [Oflag, '-O0']) @@ -2225,6 +2274,50 @@ endian = 'little' self.init(testdir, ['-Db_lto=true'], default_args=False) self.build('reconfigure') + def test_cross_file_system_paths(self): + testdir = os.path.join(self.common_test_dir, '1 trivial') + cross_content = textwrap.dedent("""\ + [binaries] + c = '/usr/bin/cc' + ar = '/usr/bin/ar' + strip = '/usr/bin/ar' + + [properties] + + [host_machine] + system = 'linux' + cpu_family = 'x86' + cpu = 'i686' + endian = 'little' + """) + + with tempfile.TemporaryDirectory() as d: + dir_ = os.path.join(d, 'meson', 'cross') + os.makedirs(dir_) + with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: + f.write(cross_content) + name = os.path.basename(f.name) + + with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}): + self.init(testdir, ['--cross-file=' + name], inprocess=True) + self.wipe() + + with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}): + os.environ.pop('XDG_DATA_HOME', None) + self.init(testdir, ['--cross-file=' + name], inprocess=True) + self.wipe() + + with tempfile.TemporaryDirectory() as d: + dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross') + os.makedirs(dir_) + with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f: + f.write(cross_content) + name = os.path.basename(f.name) + + with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)): + self.init(testdir, ['--cross-file=' + name], inprocess=True) + self.wipe() + class LinuxArmCrossCompileTests(BasePlatformTests): ''' diff --git a/test cases/common/125 shared module/meson.build b/test cases/common/125 shared module/meson.build index d96d8fc..29277e9 100644 --- a/test cases/common/125 shared module/meson.build +++ b/test cases/common/125 shared module/meson.build @@ -6,7 +6,7 @@ l = shared_library('runtime', 'runtime.c') # is a common approach for plugins that are only used # with dlopen. Any symbols are resolved dynamically # at runtime. This requires extra help on Windows, so -# should be avoided unless really neccessary. +# should be avoided unless really necessary. m = shared_module('mymodule', 'module.c') e = executable('prog', 'prog.c', link_with : l, dependencies : dl) test('import test', e, args : m) diff --git a/test cases/common/126 llvm ir and assembly/meson.build b/test cases/common/126 llvm ir and assembly/meson.build index 4ce4636..acff93f 100644 --- a/test cases/common/126 llvm ir and assembly/meson.build +++ b/test cases/common/126 llvm ir and assembly/meson.build @@ -6,7 +6,7 @@ supported_cpus = ['arm', 'x86', 'x86_64'] foreach lang : ['c', 'cpp'] cc = meson.get_compiler(lang) cc_id = cc.get_id() - ## Build a trivial executale with mixed LLVM IR source + ## Build a trivial executable with mixed LLVM IR source if cc_id == 'clang' e = executable('square_ir_' + lang, 'square.ll', 'main.' + lang) test('test IR square' + lang, e) diff --git a/test cases/common/165 config tool variable/meson.build b/test cases/common/165 config tool variable/meson.build new file mode 100644 index 0000000..0643042 --- /dev/null +++ b/test cases/common/165 config tool variable/meson.build @@ -0,0 +1,31 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project('config tool variable', 'cpp') + + +dep_llvm = dependency('llvm', required : false) +if not dep_llvm.found() + error('MESON_SKIP_TEST LLVM not installed.') +endif + +includedir = dep_llvm.get_configtool_variable('includedir') +includedir = join_paths(includedir, 'llvm') +if host_machine.system() == 'windows' + cmd = run_command(['dir', includedir]) +else + cmd = run_command(['ls', includedir]) +endif + +assert(cmd.returncode() == 0, 'did not run successfully') diff --git a/test cases/common/165 subdir if_found/meson.build b/test cases/common/165 subdir if_found/meson.build new file mode 100644 index 0000000..2c640cf --- /dev/null +++ b/test cases/common/165 subdir if_found/meson.build @@ -0,0 +1,11 @@ +project('subdir if found', 'c') + +found_dep = declare_dependency() +not_found_dep = dependency('nonexisting', required : false) + +subdir('nonexisting_dir', if_found : not_found_dep) + +variable = 3 + +subdir('subdir', if_found : found_dep) +assert(variable == 5, 'Subdir was not properly entered.') diff --git a/test cases/common/165 subdir if_found/subdir/meson.build b/test cases/common/165 subdir if_found/subdir/meson.build new file mode 100644 index 0000000..1030e25 --- /dev/null +++ b/test cases/common/165 subdir if_found/subdir/meson.build @@ -0,0 +1 @@ +variable = 5 diff --git a/test cases/common/166 array option/meson.build b/test cases/common/166 array option/meson.build new file mode 100644 index 0000000..bfcde7c --- /dev/null +++ b/test cases/common/166 array option/meson.build @@ -0,0 +1,17 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project('stringarray default options') + +assert(get_option('array') == ['foo', 'bar'], 'Default value for array is not equal to choices') diff --git a/test cases/common/166 array option/meson_options.txt b/test cases/common/166 array option/meson_options.txt new file mode 100644 index 0000000..7ed0ac1 --- /dev/null +++ b/test cases/common/166 array option/meson_options.txt @@ -0,0 +1,19 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +option( + 'array', + type : 'array', + choices : ['foo', 'bar'], +) diff --git a/test cases/common/167 external program shebang parsing/input.txt b/test cases/common/167 external program shebang parsing/input.txt new file mode 100644 index 0000000..40e30d4 --- /dev/null +++ b/test cases/common/167 external program shebang parsing/input.txt @@ -0,0 +1 @@ +some stuff here diff --git a/test cases/common/167 external program shebang parsing/main.c b/test cases/common/167 external program shebang parsing/main.c new file mode 100644 index 0000000..a90206b --- /dev/null +++ b/test cases/common/167 external program shebang parsing/main.c @@ -0,0 +1,72 @@ +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#ifdef _WIN32 + #include <io.h> + #include <windows.h> +#else + #include <unistd.h> +#endif + +/* Who cares about stack sizes in test programs anyway */ +#define LINE_LENGTH 4096 + +static int +intrp_copyfile (char * src, char * dest) +{ +#ifdef _WIN32 + if (!CopyFile (src, dest, FALSE)) + return 1; + return 0; +#else + return execlp ("cp", "copyfile", src, dest, NULL); +#endif +} + +static void +parser_get_line (FILE * f, char line[LINE_LENGTH]) +{ + if (!fgets (line, LINE_LENGTH, f)) + fprintf (stderr, "%s\n", strerror (errno)); +} + +int +main (int argc, char * argv[]) +{ + FILE *f = NULL; + char line[LINE_LENGTH]; + + if (argc != 4) { + fprintf (stderr, "Invalid number of arguments: %i\n", argc); + goto err; + } + + if ((f = fopen (argv[1], "r")) == NULL) { + fprintf (stderr, "%s\n", strerror (errno)); + goto err; + } + + parser_get_line (f, line); + + if (!line || line[0] != '#' || line[1] != '!') { + fprintf (stderr, "Invalid script\n"); + goto err; + } + + parser_get_line (f, line); + + if (!line || strncmp (line, "copy", 4) != 0) { + fprintf (stderr, "Syntax error: %s\n", line); + goto err; + } + + return intrp_copyfile (argv[2], argv[3]); + +err: + fclose (f); + return 1; +} diff --git a/test cases/common/167 external program shebang parsing/meson.build b/test cases/common/167 external program shebang parsing/meson.build new file mode 100644 index 0000000..c1cc5af --- /dev/null +++ b/test cases/common/167 external program shebang parsing/meson.build @@ -0,0 +1,21 @@ +project('shebang parsing', 'c') + +interpreter = executable('aninterp', 'main.c', native : true) + +cdata = configuration_data() +cdata.set('INTRP', interpreter.full_path()) + +f = configure_file(input : 'script.int.in', + output : 'script.int', + configuration : cdata) + +# Test that parsing a shebang with spaces works properly. See `man execve`, +# specifically the section on "Interpreter scripts" and the one under "NOTES". +script = find_program(f) + +custom_target('interpthis', + input : 'input.txt', + output : 'output.txt', + depends : interpreter, + command : [script, '@INPUT@', '@OUTPUT@'], + build_by_default : true) diff --git a/test cases/common/167 external program shebang parsing/script.int.in b/test cases/common/167 external program shebang parsing/script.int.in new file mode 100644 index 0000000..77ff909 --- /dev/null +++ b/test cases/common/167 external program shebang parsing/script.int.in @@ -0,0 +1,2 @@ +#!/usr/bin/env @INTRP@ +copy diff --git a/test cases/common/42 string operations/meson.build b/test cases/common/42 string operations/meson.build index d9f8130..e60006a 100644 --- a/test cases/common/42 string operations/meson.build +++ b/test cases/common/42 string operations/meson.build @@ -74,3 +74,24 @@ multiline string '''.strip() == '''multiline string''', 'Newlines badly stripped 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''' +nl = ''' +''' +bs_n = '''\n''' +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('\n' == nl, 'Newline escape broken') +assert('\\n' == bs_n, 'Double backslash broken before n') +assert('\\\n' == bs_nl, 'Three backslash broken before n') +assert('\\\\n' == bs_bs_n, 'Four backslash broken before n') +assert('\\\\\n' == bs_bs_nl, 'Five backslash broken before n') diff --git a/test cases/common/47 options/meson.build b/test cases/common/47 options/meson.build index 2a764f0..863703c 100644 --- a/test cases/common/47 options/meson.build +++ b/test cases/common/47 options/meson.build @@ -12,6 +12,11 @@ if get_option('combo_opt') != 'combo' error('Incorrect value to combo option.') endif +if get_option('array_opt') != ['one', 'two'] + message(get_option('array_opt')) + error('Incorrect value for array option') +endif + # If the default changes, update test cases/unit/13 reconfigure if get_option('b_lto') != false error('Incorrect value in base option.') diff --git a/test cases/common/47 options/meson_options.txt b/test cases/common/47 options/meson_options.txt index 653dd75..8978d44 100644 --- a/test cases/common/47 options/meson_options.txt +++ b/test cases/common/47 options/meson_options.txt @@ -1,3 +1,4 @@ option('testoption', type : 'string', value : 'optval', description : 'An option to do something') option('other_one', type : 'boolean', value : false) option('combo_opt', type : 'combo', choices : ['one', 'two', 'combo'], value : 'combo') +option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two']) diff --git a/test cases/common/66 install subdir/subdir/meson.build b/test cases/common/66 install subdir/subdir/meson.build index 37d2da4..a1dadd4 100644 --- a/test cases/common/66 install subdir/subdir/meson.build +++ b/test cases/common/66 install subdir/subdir/meson.build @@ -1,3 +1,3 @@ install_subdir('sub1', install_dir : 'share', - # This mode will be overriden by the mode set in the outer install_subdir + # This mode will be overridden by the mode set in the outer install_subdir install_mode : 'rwxr-x---') diff --git a/test cases/common/68 number arithmetic/meson.build b/test cases/common/68 number arithmetic/meson.build index 02c7878..495a83e 100644 --- a/test cases/common/68 number arithmetic/meson.build +++ b/test cases/common/68 number arithmetic/meson.build @@ -25,16 +25,16 @@ assert((5 % 2) == 1, 'Integer modulo (odd) is broken') assert((4 % 2) == 0, 'Integer modulo (even) is broken') if 2 * 1 % 2 != 0 - error('Modulo precendence with multiplication is broken') + error('Modulo precedence with multiplication is broken') endif if 2 + 1 % 2 != 3 - error('Modulo precendence with addition is broken') + error('Modulo precedence with addition is broken') endif if 9 / 9 % 2 != 1 - error('Modulo precendence with division is broken') + error('Modulo precedence with division is broken') endif if 9 - 9 % 2 != 8 - error('Modulo precendence with subtraction is broken') + error('Modulo precedence with subtraction is broken') endif assert(2.is_even(), 'int is_even() broken for even value') diff --git a/test cases/common/71 arithmetic bidmas/meson.build b/test cases/common/71 arithmetic bidmas/meson.build index 2a530c8..c7334b4 100644 --- a/test cases/common/71 arithmetic bidmas/meson.build +++ b/test cases/common/71 arithmetic bidmas/meson.build @@ -1,7 +1,7 @@ project('arithmetic bidmas', 'c') if 5 * 3 - 6 / 2 + 1 != 13 - error('Arithemtic bidmas broken') + error('Arithmetic bidmas broken') endif if 5 * (3 - 6 / 2) + 1 != 1 error('Arithmetic bidmas with brackets broken') diff --git a/test cases/failing/65 grab sibling/subprojects/b/sneaky.c b/test cases/failing/65 grab sibling/subprojects/b/sneaky.c index f1cb916..46718c6 100644 --- a/test cases/failing/65 grab sibling/subprojects/b/sneaky.c +++ b/test cases/failing/65 grab sibling/subprojects/b/sneaky.c @@ -1,6 +1,6 @@ #include<stdio.h> int main(int argc, char **argv) { - printf("I can only come into existance via trickery.\n"); + printf("I can only come into existence via trickery.\n"); return 0; } diff --git a/test cases/frameworks/16 sdl2/meson.build b/test cases/frameworks/16 sdl2/meson.build index c79bd46..61a34ef 100644 --- a/test cases/frameworks/16 sdl2/meson.build +++ b/test cases/frameworks/16 sdl2/meson.build @@ -6,5 +6,8 @@ e = executable('sdl2prog', 'sdl2prog.c', dependencies : sdl2_dep) test('sdl2test', e) -# Ensure that we can find it with sdl2-config too +# Ensure that we can find it with sdl2-config too, using the legacy method name configdep = dependency('sdl2', method : 'sdlconfig') + +# And the modern method name +configdep = dependency('sdl2', method : 'config-tool') diff --git a/test cases/frameworks/19 pcap/meson.build b/test cases/frameworks/19 pcap/meson.build index c505960..f02f411 100644 --- a/test cases/frameworks/19 pcap/meson.build +++ b/test cases/frameworks/19 pcap/meson.build @@ -8,3 +8,7 @@ assert(pcap_ver.split('.').length() > 1, 'pcap version is "@0@"'.format(pcap_ver e = executable('pcap_prog', 'pcap_prog.c', dependencies : pcap_dep) test('pcaptest', e) + +# Ensure discovery bia the configuration tools work also +pcap_dep = dependency('pcap', version : '>=1.0', method : 'pcap-config') +pcap_dep = dependency('pcap', version : '>=1.0', method : 'config-tool') diff --git a/test cases/frameworks/20 cups/meson.build b/test cases/frameworks/20 cups/meson.build index 6c9b6fe..11f6f63 100644 --- a/test cases/frameworks/20 cups/meson.build +++ b/test cases/frameworks/20 cups/meson.build @@ -5,3 +5,8 @@ cups_dep = dependency('cups', version : '>=1.4') e = executable('cups_prog', 'cups_prog.c', dependencies : cups_dep) test('cupstest', e) + +# ensure we can find the cups dependency via the legacy and modern config-tool +# options +dep = dependency('cups', version : '>=1.4', method : 'cups-config') +dep = dependency('cups', version : '>=1.4', method : 'config-tool') diff --git a/test cases/frameworks/21 libwmf/meson.build b/test cases/frameworks/21 libwmf/meson.build index a7d9263..b39d8f4 100644 --- a/test cases/frameworks/21 libwmf/meson.build +++ b/test cases/frameworks/21 libwmf/meson.build @@ -1,9 +1,14 @@ project('libwmf test', 'c') -libwmf_dep = dependency('libwmf', version : '>=3.0') +libwmf_dep = dependency('libwmf', version : '>= 0.2.8') libwmf_ver = libwmf_dep.version() assert(libwmf_ver.split('.').length() > 1, 'libwmf version is "@0@"'.format(libwmf_ver)) message('libwmf version is "@0@"'.format(libwmf_ver)) e = executable('libwmf_prog', 'libwmf_prog.c', dependencies : libwmf_dep) test('libwmftest', e) + +# Test using the method keyword: + +dependency('libwmf', method : 'config-tool') +dependency('libwmf', method : 'libwmf-config') diff --git a/test cases/frameworks/9 wxwidgets/meson.build b/test cases/frameworks/9 wxwidgets/meson.build index da3aa26..5f9419c 100644 --- a/test cases/frameworks/9 wxwidgets/meson.build +++ b/test cases/frameworks/9 wxwidgets/meson.build @@ -1,4 +1,4 @@ -project('wxwidgets test', 'cpp') +project('wxwidgets test', 'cpp', default_options : ['cpp_std=c++11']) wxd = dependency('wxwidgets', version : '>=5', required : false) wxd = dependency('wxwidgets', version : '>=3.0.0', required : false) diff --git a/test cases/linuxlike/1 pkg-config/meson.build b/test cases/linuxlike/1 pkg-config/meson.build index 7e43821..17feb21 100644 --- a/test cases/linuxlike/1 pkg-config/meson.build +++ b/test cases/linuxlike/1 pkg-config/meson.build @@ -23,7 +23,7 @@ dep2 = declare_dependency(dependencies : dep) exe2 = executable('zlibprog2', 'prog.c', dependencies : dep2) test('zlibtest2', exe2) -# Try to find a nonexistant library to ensure requires:false works. +# Try to find a nonexistent library to ensure requires:false works. dep = dependency('nvakuhrabnsdfasdf', required : false) diff --git a/test cases/osx/3 has function xcode8/meson.build b/test cases/osx/3 has function xcode8/meson.build index 300d352..edd3688 100644 --- a/test cases/osx/3 has function xcode8/meson.build +++ b/test cases/osx/3 has function xcode8/meson.build @@ -7,20 +7,24 @@ sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/M args_10_11 = ['-mmacosx-version-min=10.11'] + sdk_args args_10_12 = ['-mmacosx-version-min=10.12'] + sdk_args +# XCode 9 location for the macOS 10.13 SDK +sdk_args = ['-isysroot', '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk'] +args_10_12 = ['-mmacosx-version-min=10.13'] + sdk_args + # Test requires XCode 8 which has the MacOSX 10.12 SDK -if cc.version().version_compare('>=8.0') +if cc.version().version_compare('>=8.0') and cc.version().version_compare('<8.1') if cc.has_function('clock_gettime', args : args_10_11, prefix : '#include <time.h>') - error('Should not have found clock_gettime via <time.h> when targetting Mac OS X 10.11') + error('Should not have found clock_gettime via <time.h> when targeting Mac OS X 10.11') endif if not cc.has_function('clock_gettime', args : args_10_12, prefix : '#include <time.h>') - error('Did NOT find clock_gettime via <time.h> when targetting Mac OS X 10.12') + error('Did NOT find clock_gettime via <time.h> when targeting Mac OS X 10.12') endif if not cc.has_function('clock_gettime', args : args_10_11) - error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.11') + error('Did NOT find clock_gettime w/o a prototype when targeting Mac OS X 10.11') endif if not cc.has_function('clock_gettime', args : args_10_12) - error('Did NOT find clock_gettime w/o a prototype when targetting Mac OS X 10.12') + error('Did NOT find clock_gettime w/o a prototype when targeting Mac OS X 10.12') endif else - message('Test needs XCode 8, skipping...') + error('MESON_SKIP_TEST Test needs XCode 8.') endif diff --git a/test cases/unit/18 array option/meson.build b/test cases/unit/18 array option/meson.build new file mode 100644 index 0000000..2b44181 --- /dev/null +++ b/test cases/unit/18 array option/meson.build @@ -0,0 +1,15 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +project('array option test') diff --git a/test cases/unit/18 array option/meson_options.txt b/test cases/unit/18 array option/meson_options.txt new file mode 100644 index 0000000..0ccdcc4 --- /dev/null +++ b/test cases/unit/18 array option/meson_options.txt @@ -0,0 +1,20 @@ +# Copyright © 2017 Intel Corporation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +option( + 'list', + type : 'array', + value : ['foo', 'bar'], + choices : ['foo', 'bar', 'oink', 'boink'], +) |