aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/CMake-module.md71
-rw-r--r--docs/markdown/Reference-manual.md3
-rw-r--r--docs/markdown/Release-notes-for-0.50.0.md8
-rw-r--r--docs/sitemap.txt1
-rw-r--r--mesonbuild/backend/backends.py8
-rw-r--r--mesonbuild/build.py5
-rw-r--r--mesonbuild/coredata.py15
-rw-r--r--mesonbuild/mesonmain.py17
-rw-r--r--mesonbuild/modules/cmake.py221
-rw-r--r--mesonbuild/mtest.py4
-rw-r--r--test cases/common/209 custom target build by default/docgen.py12
-rw-r--r--test cases/common/209 custom target build by default/installed_files.txt0
-rw-r--r--test cases/common/209 custom target build by default/meson.build10
-rw-r--r--test cases/common/211 cmake module/cmake_project/CMakeLists.txt4
-rw-r--r--test cases/common/211 cmake module/installed_files.txt2
-rw-r--r--test cases/common/211 cmake module/meson.build31
-rw-r--r--test cases/common/211 cmake module/projectConfig.cmake.in4
-rw-r--r--test cases/unit/17 prebuilt shared/patron.c1
18 files changed, 410 insertions, 7 deletions
diff --git a/docs/markdown/CMake-module.md b/docs/markdown/CMake-module.md
new file mode 100644
index 0000000..4cc97cf
--- /dev/null
+++ b/docs/markdown/CMake-module.md
@@ -0,0 +1,71 @@
+# CMake module
+
+This module provides helper tools for generating cmake package files.
+
+
+## Usage
+
+To use this module, just do: **`cmake = import('cmake')`**. The
+following functions will then be available as methods on the object
+with the name `cmake`. You can, of course, replace the name `cmake`
+with anything else.
+
+### cmake.write_basic_package_version_file()
+
+This function is the equivalent of the corresponding [CMake function](https://cmake.org/cmake/help/v3.11/module/CMakePackageConfigHelpers.html#generating-a-package-version-file),
+it generates a `name` package version file.
+
+* `name`: the name of the package.
+* `version`: the version of the generated package file.
+* `compatibility`: a string indicating the kind of compatibility, the accepted values are
+`AnyNewerVersion`, `SameMajorVersion`, `SameMinorVersion` or `ExactVersion`.
+It defaults to `AnyNewerVersion`. Depending on your cmake installation some kind of
+compatibility may not be available.
+* `install_dir`: optional installation directory, it defaults to `$(libdir)/cmake/$(name)`
+
+
+Example:
+
+```meson
+cmake = import('cmake')
+
+cmake.write_basic_package_version_file(name: 'myProject', version: '1.0.0')
+```
+
+### cmake.configure_package_config_file()
+
+This function is the equivalent of the corresponding [CMake function](https://cmake.org/cmake/help/v3.11/module/CMakePackageConfigHelpers.html#generating-a-package-configuration-file),
+it generates a `name` package configuration file from the `input` template file. Just like the cmake function
+in this file the `@PACKAGE_INIT@` statement will be replaced by the appropriate piece of cmake code.
+The equivalent `PATH_VARS` argument is given through the `configuration` parameter.
+
+* `name`: the name of the package.
+* `input`: the template file where that will be treated for variable substitutions contained in `configuration`.
+* `install_dir`: optional installation directory, it defaults to `$(libdir)/cmake/$(name)`.
+* `configuration`: a `configuration_data` object that will be used for variable substitution in the template file.
+
+
+Example:
+
+meson.build:
+
+```meson
+cmake = import('cmake')
+
+conf = configuration_data()
+conf.set_quoted('VAR', 'variable value')
+
+cmake.configure_package_config_file(
+ name: 'myProject',
+ input: 'myProject.cmake.in',
+ configuration: conf
+)
+```
+
+myProject.cmake.in:
+
+```text
+@PACKAGE_INIT@
+
+set(MYVAR VAR)
+```
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 5436ec3..e913e25 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -266,6 +266,9 @@ following.
- `build_by_default` *(added 0.38)* causes, when set to true, to
have this target be built by default, that is, when invoking plain
`ninja`; the default value is false
+ *(changed in 0.50)* if `build_by_default` is explicitly set to false, `install`
+ will no longer override it. If `build_by_default` is not set, `install` will
+ still determine its default.
- `build_always` (deprecated) if `true` this target is always considered out of
date and is rebuilt every time. Equivalent to setting both
`build_always_stale` and `build_by_default` to true.
diff --git a/docs/markdown/Release-notes-for-0.50.0.md b/docs/markdown/Release-notes-for-0.50.0.md
index cb4fe0d..a08edfb 100644
--- a/docs/markdown/Release-notes-for-0.50.0.md
+++ b/docs/markdown/Release-notes-for-0.50.0.md
@@ -15,3 +15,11 @@ whose contents should look like this:
A short description explaining the new feature and how it should be used.
+## custom_target: install no longer overrides build_by_default
+
+Earlier, if `build_by_default` was set to false and `install` was set to true in
+a `custom_target`, `install` would override it and the `custom_target` would
+always be built by default.
+Now if `build_by_default` is explicitly set to false it will no longer be
+overridden. If `build_by_default` is not set, its default will still be
+determined by the value of `install` for greater backward compatibility.
diff --git a/docs/sitemap.txt b/docs/sitemap.txt
index b8c41b4..f80c279 100644
--- a/docs/sitemap.txt
+++ b/docs/sitemap.txt
@@ -30,6 +30,7 @@ index.md
Subprojects.md
Disabler.md
Modules.md
+ CMake-module.md
Dlang-module.md
Gnome-module.md
Hotdoc-module.md
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 0637905..9f3f5d6 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -813,7 +813,7 @@ class Backend:
result = OrderedDict()
# Get all build and custom targets that must be built by default
for name, t in self.build.get_targets().items():
- if t.build_by_default or t.install:
+ if t.build_by_default:
result[name] = t
# Get all targets used as test executables and arguments. These must
# also be built by default. XXX: Sometime in the future these should be
@@ -1074,7 +1074,8 @@ class Backend:
if num_outdirs == 1 and num_out > 1:
for output in t.get_outputs():
f = os.path.join(self.get_target_dir(t), output)
- i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode)
+ i = TargetInstallData(f, outdirs[0], {}, False, {}, None, install_mode,
+ optional=not t.build_by_default)
d.targets.append(i)
else:
for output, outdir in zip(t.get_outputs(), outdirs):
@@ -1082,7 +1083,8 @@ class Backend:
if outdir is False:
continue
f = os.path.join(self.get_target_dir(t), output)
- i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode)
+ i = TargetInstallData(f, outdir, {}, False, {}, None, install_mode,
+ optional=not t.build_by_default)
d.targets.append(i)
def generate_custom_install_script(self, d):
diff --git a/mesonbuild/build.py b/mesonbuild/build.py
index 52af562..d20b576 100644
--- a/mesonbuild/build.py
+++ b/mesonbuild/build.py
@@ -408,6 +408,11 @@ a hard error in the future.''' % name)
self.build_by_default = kwargs['build_by_default']
if not isinstance(self.build_by_default, bool):
raise InvalidArguments('build_by_default must be a boolean value.')
+ elif kwargs.get('install', False):
+ # For backward compatibility, if build_by_default is not explicitly
+ # set, use the value of 'install' if it's enabled.
+ self.build_by_default = True
+
self.option_overrides = self.parse_overrides(kwargs)
def parse_overrides(self, kwargs):
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index d5f7d94..c7e3689 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -1,4 +1,4 @@
-# Copyright 2012-2018 The Meson development team
+# Copyright 2012-2019 The Meson development team
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -615,6 +615,10 @@ def read_cmd_line_file(build_dir, options):
properties = config['properties']
if options.cross_file is None:
options.cross_file = properties.get('cross_file', None)
+ if not options.native_file:
+ # This will be a string in the form: "['first', 'second', ...]", use
+ # literal_eval to get it into the list of strings.
+ options.native_file = ast.literal_eval(properties.get('native_file', '[]'))
def write_cmd_line_file(build_dir, options):
filename = get_cmd_line_file(build_dir)
@@ -623,6 +627,8 @@ def write_cmd_line_file(build_dir, options):
properties = {}
if options.cross_file is not None:
properties['cross_file'] = options.cross_file
+ if options.native_file:
+ properties['native_file'] = options.native_file
config['options'] = options.cmd_line_options
config['properties'] = properties
@@ -643,8 +649,13 @@ def load(build_dir):
try:
with open(filename, 'rb') as f:
obj = pickle.load(f)
- except pickle.UnpicklingError:
+ except (pickle.UnpicklingError, EOFError):
raise MesonException(load_fail_msg)
+ except AttributeError:
+ raise MesonException(
+ "Coredata file {!r} references functions or classes that don't "
+ "exist. This probably means that it was generated with an old "
+ "version of meson.".format(filename))
if not isinstance(obj, CoreData):
raise MesonException(load_fail_msg)
if obj.version != version:
diff --git a/mesonbuild/mesonmain.py b/mesonbuild/mesonmain.py
index 7236d1a..037d76c 100644
--- a/mesonbuild/mesonmain.py
+++ b/mesonbuild/mesonmain.py
@@ -17,6 +17,7 @@ import os.path
import importlib
import traceback
import argparse
+import codecs
from . import mesonlib
from . import mlog
@@ -150,6 +151,17 @@ def run_script_command(script_name, script_args):
mlog.exception(e)
return 1
+def ensure_stdout_accepts_unicode():
+ if sys.stdout.encoding and not sys.stdout.encoding.upper().startswith('UTF-'):
+ if sys.version_info >= (3, 7):
+ sys.stdout.reconfigure(errors='surrogateescape')
+ else:
+ sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach(),
+ errors='surrogateescape')
+ sys.stdout.encoding = 'UTF-8'
+ if not hasattr(sys.stdout, 'buffer'):
+ sys.stdout.buffer = sys.stdout.raw if hasattr(sys.stdout, 'raw') else sys.stdout
+
def run(original_args, mainfile):
if sys.version_info < (3, 5):
print('Meson works correctly only with python 3.5+.')
@@ -157,6 +169,11 @@ def run(original_args, mainfile):
print('Please update your environment')
return 1
+ # Meson gets confused if stdout can't output Unicode, if the
+ # locale isn't Unicode, just force stdout to accept it. This tries
+ # to emulate enough of PEP 540 to work elsewhere.
+ ensure_stdout_accepts_unicode()
+
# https://github.com/mesonbuild/meson/issues/3653
if sys.platform.lower() == 'msys':
mlog.error('This python3 seems to be msys/python on MSYS2 Windows, which is known to have path semantics incompatible with Meson')
diff --git a/mesonbuild/modules/cmake.py b/mesonbuild/modules/cmake.py
new file mode 100644
index 0000000..d98213d
--- /dev/null
+++ b/mesonbuild/modules/cmake.py
@@ -0,0 +1,221 @@
+# Copyright 2018 The Meson development team
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+# http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+import re
+import os, os.path, pathlib
+import shutil
+
+from . import ExtensionModule, ModuleReturnValue
+
+from .. import build, dependencies, mesonlib, mlog
+from ..interpreterbase import permittedKwargs
+from ..interpreter import ConfigurationDataHolder
+
+
+COMPATIBILITIES = ['AnyNewerVersion', 'SameMajorVersion', 'SameMinorVersion', 'ExactVersion']
+
+# Taken from https://github.com/Kitware/CMake/blob/master/Modules/CMakePackageConfigHelpers.cmake
+PACKAGE_INIT_BASE = '''
+####### Expanded from \\@PACKAGE_INIT\\@ by configure_package_config_file() #######
+####### Any changes to this file will be overwritten by the next CMake run ####
+####### The input file was @inputFileName@ ########
+get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/@PACKAGE_RELATIVE_PATH@" ABSOLUTE)
+'''
+PACKAGE_INIT_EXT = '''
+# Use original install prefix when loaded through a "/usr move"
+# cross-prefix symbolic link such as /lib -> /usr/lib.
+get_filename_component(_realCurr "${CMAKE_CURRENT_LIST_DIR}" REALPATH)
+get_filename_component(_realOrig "@absInstallDir@" REALPATH)
+if(_realCurr STREQUAL _realOrig)
+ set(PACKAGE_PREFIX_DIR "@installPrefix@")
+endif()
+unset(_realOrig)
+unset(_realCurr)
+'''
+
+
+class CmakeModule(ExtensionModule):
+ cmake_detected = False
+ cmake_root = None
+
+ def __init__(self, interpreter):
+ super().__init__(interpreter)
+ self.snippets.add('configure_package_config_file')
+
+ def detect_voidp_size(self, compilers, env):
+ compiler = compilers.get('c', None)
+ if not compiler:
+ compiler = compilers.get('cpp', None)
+
+ if not compiler:
+ raise mesonlib.MesonException('Requires a C or C++ compiler to compute sizeof(void *).')
+
+ return compiler.sizeof('void *', '', env)
+
+ def detect_cmake(self):
+ if self.cmake_detected:
+ return True
+
+ cmakebin = dependencies.ExternalProgram('cmake', silent=False)
+ p, stdout, stderr = mesonlib.Popen_safe(cmakebin.get_command() + ['--system-information', '-G', 'Ninja'])[0:3]
+ if p.returncode != 0:
+ mlog.log('error retrieving cmake informations: returnCode={0} stdout={1} stderr={2}'.format(p.returncode, stdout, stderr))
+ return False
+
+ match = re.search('\n_INCLUDED_FILE \\"([^"]+)"\n', stdout.strip())
+ if not match:
+ mlog.log('unable to determine cmake root')
+ return False
+
+ # compilerpath is something like '/usr/share/cmake-3.5/Modules/Platform/Linux-GNU-CXX.cmake'
+ # or 'C:/Program Files (x86)/CMake 2.8/share/cmake-2.8/Modules/Platform/Windows-MSVC-CXX.cmake' under windows
+ compilerpath = match.group(1)
+ pos = compilerpath.find('/Modules/Platform/')
+ if pos < 0:
+ mlog.log('unknown _INCLUDED_FILE path scheme')
+ return False
+
+ cmakePath = pathlib.PurePath(compilerpath[0:pos])
+ self.cmake_root = os.path.join(*cmakePath.parts)
+ self.cmake_detected = True
+ return True
+
+ @permittedKwargs({'version', 'name', 'compatibility', 'install_dir'})
+ def write_basic_package_version_file(self, state, _args, kwargs):
+ version = kwargs.get('version', None)
+ if not isinstance(version, str):
+ raise mesonlib.MesonException('Version must be specified.')
+
+ name = kwargs.get('name', None)
+ if not isinstance(name, str):
+ raise mesonlib.MesonException('Name not specified.')
+
+ compatibility = kwargs.get('compatibility', 'AnyNewerVersion')
+ if not isinstance(compatibility, str):
+ raise mesonlib.MesonException('compatibility is not string.')
+ if compatibility not in COMPATIBILITIES:
+ raise mesonlib.MesonException('compatibility must be either AnyNewerVersion, SameMajorVersion or ExactVersion.')
+
+ if not self.detect_cmake():
+ raise mesonlib.MesonException('Unable to find cmake')
+
+ pkgroot = kwargs.get('install_dir', None)
+ if pkgroot is None:
+ pkgroot = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'cmake', name)
+ if not isinstance(pkgroot, str):
+ raise mesonlib.MesonException('Install_dir must be a string.')
+
+ template_file = os.path.join(self.cmake_root, 'Modules', 'BasicConfigVersion-{}.cmake.in'.format(compatibility))
+ if not os.path.exists(template_file):
+ raise mesonlib.MesonException('your cmake installation doesn\'t support the {} compatibility'.format(compatibility))
+
+ version_file = os.path.join(state.environment.scratch_dir, '{}ConfigVersion.cmake'.format(name))
+
+ conf = {
+ 'CVF_VERSION': (version, ''),
+ 'CMAKE_SIZEOF_VOID_P': (str(self.detect_voidp_size(state.compilers, state.environment)), '')
+ }
+ mesonlib.do_conf_file(template_file, version_file, conf, 'meson')
+
+ res = build.Data(mesonlib.File(True, state.environment.get_scratch_dir(), version_file), pkgroot)
+ return ModuleReturnValue(res, [res])
+
+ def create_package_file(self, infile, outfile, PACKAGE_RELATIVE_PATH, extra, confdata):
+ package_init = PACKAGE_INIT_BASE.replace('@PACKAGE_RELATIVE_PATH@', PACKAGE_RELATIVE_PATH)
+ package_init = package_init.replace('@inputFileName@', infile)
+ package_init += extra
+
+ try:
+ with open(infile, "r") as fin:
+ data = fin.readlines()
+ except Exception as e:
+ raise mesonlib.MesonException('Could not read input file %s: %s' % (infile, str(e)))
+
+ result = []
+ regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
+ for line in data:
+ line = line.replace('@PACKAGE_INIT@', package_init)
+ line, _missing = mesonlib.do_replacement(regex, line, 'meson', confdata)
+
+ result.append(line)
+
+ outfile_tmp = outfile + "~"
+ with open(outfile_tmp, "w", encoding='utf-8') as fout:
+ fout.writelines(result)
+
+ shutil.copymode(infile, outfile_tmp)
+ mesonlib.replace_if_different(outfile, outfile_tmp)
+
+ @permittedKwargs({'input', 'name', 'install_dir', 'configuration'})
+ def configure_package_config_file(self, interpreter, state, args, kwargs):
+ if len(args) > 0:
+ raise mesonlib.MesonException('configure_package_config_file takes only keyword arguments.')
+
+ if 'input' not in kwargs:
+ raise mesonlib.MesonException('configure_package_config_file requires "input" keyword.')
+ inputfile = kwargs['input']
+ if isinstance(inputfile, list):
+ if len(inputfile) != 1:
+ m = "Keyword argument 'input' requires exactly one file"
+ raise mesonlib.MesonException(m)
+ inputfile = inputfile[0]
+ if not isinstance(inputfile, (str, mesonlib.File)):
+ raise mesonlib.MesonException("input must be a string or a file")
+ if isinstance(inputfile, str):
+ inputfile = mesonlib.File.from_source_file(state.environment.source_dir, state.subdir, inputfile)
+
+ ifile_abs = inputfile.absolute_path(state.environment.source_dir, state.environment.build_dir)
+
+ if 'name' not in kwargs:
+ raise mesonlib.MesonException('"name" not specified.')
+ name = kwargs['name']
+
+ (ofile_path, ofile_fname) = os.path.split(os.path.join(state.subdir, '{}Config.cmake'.format(name)))
+ ofile_abs = os.path.join(state.environment.build_dir, ofile_path, ofile_fname)
+
+ if 'install_dir' not in kwargs:
+ install_dir = os.path.join(state.environment.coredata.get_builtin_option('libdir'), 'cmake', name)
+ if not isinstance(install_dir, str):
+ raise mesonlib.MesonException('"install_dir" must be a string.')
+
+ if 'configuration' not in kwargs:
+ raise mesonlib.MesonException('"configuration" not specified.')
+ conf = kwargs['configuration']
+ if not isinstance(conf, ConfigurationDataHolder):
+ raise mesonlib.MesonException('Argument "configuration" is not of type configuration_data')
+
+ prefix = state.environment.coredata.get_builtin_option('prefix')
+ abs_install_dir = install_dir
+ if not os.path.isabs(abs_install_dir):
+ abs_install_dir = os.path.join(prefix, install_dir)
+
+ PACKAGE_RELATIVE_PATH = os.path.relpath(prefix, abs_install_dir)
+ extra = ''
+ if re.match('^(/usr)?/lib(64)?/.+', abs_install_dir):
+ extra = PACKAGE_INIT_EXT.replace('@absInstallDir@', abs_install_dir)
+ extra = extra.replace('@installPrefix@', prefix)
+
+ self.create_package_file(ifile_abs, ofile_abs, PACKAGE_RELATIVE_PATH, extra, conf.held_object)
+ conf.mark_used()
+
+ conffile = os.path.normpath(inputfile.relative_name())
+ if conffile not in interpreter.build_def_files:
+ interpreter.build_def_files.append(conffile)
+
+ res = build.Data(mesonlib.File(True, ofile_path, ofile_fname), install_dir)
+ interpreter.build.data.append(res)
+
+ return res
+
+def initialize(*args, **kwargs):
+ return CmakeModule(*args, **kwargs)
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 8ce9538..b4bd4f2 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -647,8 +647,8 @@ Timeout: %4d
self.logfilename = logfile_base + '.txt'
self.jsonlogfilename = logfile_base + '.json'
- self.jsonlogfile = open(self.jsonlogfilename, 'w', encoding='utf-8')
- self.logfile = open(self.logfilename, 'w', encoding='utf-8')
+ self.jsonlogfile = open(self.jsonlogfilename, 'w', encoding='utf-8', errors='replace')
+ self.logfile = open(self.logfilename, 'w', encoding='utf-8', errors='surrogateescape')
self.logfile.write('Log of Meson test suite run on %s\n\n'
% datetime.datetime.now().isoformat())
diff --git a/test cases/common/209 custom target build by default/docgen.py b/test cases/common/209 custom target build by default/docgen.py
new file mode 100644
index 0000000..f343f21
--- /dev/null
+++ b/test cases/common/209 custom target build by default/docgen.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+
+out = sys.argv[1]
+
+os.mkdir(out)
+
+for name in ('a', 'b', 'c'):
+ with open(os.path.join(out, name + '.txt'), 'w') as f:
+ f.write(name)
diff --git a/test cases/common/209 custom target build by default/installed_files.txt b/test cases/common/209 custom target build by default/installed_files.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test cases/common/209 custom target build by default/installed_files.txt
diff --git a/test cases/common/209 custom target build by default/meson.build b/test cases/common/209 custom target build by default/meson.build
new file mode 100644
index 0000000..7c81aa2
--- /dev/null
+++ b/test cases/common/209 custom target build by default/meson.build
@@ -0,0 +1,10 @@
+project('custom-target-dir-install', 'c')
+
+docgen = find_program('docgen.py')
+
+custom_target('docgen',
+ output : 'html',
+ command : [docgen, '@OUTPUT@'],
+ install : true,
+ build_by_default : false,
+ install_dir : join_paths(get_option('datadir'), 'doc/testpkgname'))
diff --git a/test cases/common/211 cmake module/cmake_project/CMakeLists.txt b/test cases/common/211 cmake module/cmake_project/CMakeLists.txt
new file mode 100644
index 0000000..cd91584
--- /dev/null
+++ b/test cases/common/211 cmake module/cmake_project/CMakeLists.txt
@@ -0,0 +1,4 @@
+cmake_minimum_required(VERSION 2.8)
+project(cmakeMeson C)
+
+find_package(cmakeModule REQUIRED) \ No newline at end of file
diff --git a/test cases/common/211 cmake module/installed_files.txt b/test cases/common/211 cmake module/installed_files.txt
new file mode 100644
index 0000000..f8b11f0
--- /dev/null
+++ b/test cases/common/211 cmake module/installed_files.txt
@@ -0,0 +1,2 @@
+usr/lib/cmake/cmakeModule/cmakeModuleConfig.cmake
+usr/lib/cmake/cmakeModule/cmakeModuleConfigVersion.cmake \ No newline at end of file
diff --git a/test cases/common/211 cmake module/meson.build b/test cases/common/211 cmake module/meson.build
new file mode 100644
index 0000000..68f9993
--- /dev/null
+++ b/test cases/common/211 cmake module/meson.build
@@ -0,0 +1,31 @@
+project('cmakeModule', 'c', version: '1.0.0')
+
+if build_machine.system() == 'cygwin'
+ error('MESON_SKIP_TEST CMake is broken on Cygwin.')
+endif
+
+cmake_bin = find_program('cmake', required: false)
+if not cmake_bin.found()
+ error('MESON_SKIP_TEST CMake not installed.')
+endif
+
+cc = meson.get_compiler('c')
+if cc.get_id() == 'clang-cl' and meson.backend() == 'ninja' and build_machine.system() == 'windows'
+ error('MESON_SKIP_TEST CMake installation nor operational for vs2017 clangclx64ninja')
+endif
+
+cmake = import('cmake')
+
+cmake.write_basic_package_version_file(version: '0.0.1',
+ name: 'cmakeModule',
+)
+
+conf = configuration_data()
+conf.set('MYVAR', 'my variable value')
+conf.set_quoted('MYQUOTEDVAR', 'my quoted variable value')
+
+cmake.configure_package_config_file(
+ input: 'projectConfig.cmake.in',
+ name: 'cmakeModule',
+ configuration: conf,
+)
diff --git a/test cases/common/211 cmake module/projectConfig.cmake.in b/test cases/common/211 cmake module/projectConfig.cmake.in
new file mode 100644
index 0000000..fa3dfca
--- /dev/null
+++ b/test cases/common/211 cmake module/projectConfig.cmake.in
@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+set(MYVAR "@MYVAR@")
+set(MYQUOTEDVAR @MYQUOTEDVAR@)
diff --git a/test cases/unit/17 prebuilt shared/patron.c b/test cases/unit/17 prebuilt shared/patron.c
index 82d9678..461d7b4 100644
--- a/test cases/unit/17 prebuilt shared/patron.c
+++ b/test cases/unit/17 prebuilt shared/patron.c
@@ -5,4 +5,5 @@ int main(int argc, char **argv) {
printf("You are standing outside the Great Library of Alexandria.\n");
printf("You decide to go inside.\n\n");
alexandria_visit();
+ return 0;
}