This adds an experimental meson module to build projects with other build systems. Closes: #4316pull/7490/head
parent
19696c3dcd
commit
9d338200da
20 changed files with 654 additions and 19 deletions
@ -1,5 +1,6 @@ |
||||
* @jpakkane |
||||
/mesonbuild/modules/pkgconfig.py @xclaesse |
||||
/mesonbuild/modules/cmake.py @mensinda |
||||
/mesonbuild/modules/unstable_external_project.py @xclaesse |
||||
/mesonbuild/ast/* @mensinda |
||||
/mesonbuild/cmake/* @mensinda |
||||
|
@ -0,0 +1,116 @@ |
||||
# External Project module |
||||
|
||||
**Note**: the functionality of this module is governed by [Meson's |
||||
rules on mixing build systems](Mixing-build-systems.md). |
||||
|
||||
*This is an experimental module, API could change.* |
||||
|
||||
This module allows building code that uses build systems other than Meson. This |
||||
module is intended to be used to build Autotools subprojects as fallback if the |
||||
dependency couldn't be found on the system (e.g. too old distro version). |
||||
|
||||
The project will be compiled out-of-tree inside Meson's build directory. The |
||||
project will also be installed inside Meson's build directory using make's |
||||
[`DESTDIR`](https://www.gnu.org/prep/standards/html_node/DESTDIR.html) |
||||
feature. During project installation step, that DESTDIR will be copied verbatim |
||||
into the desired location. |
||||
|
||||
External subprojects can use libraries built by Meson (main project, or other |
||||
subprojects) using pkg-config, thanks to `*-uninstalled.pc` files generated by |
||||
[`pkg.generate()`](Pkgconfig-module.md). |
||||
|
||||
External build system requirements: |
||||
- Must support out-of-tree build. The configure script will be invoked with the |
||||
current workdir inside Meson's build directory and not subproject's top source |
||||
directory. |
||||
- Configure script must generate a `Makefile` in the current workdir. |
||||
- Configure script must take common directories like prefix, libdir, etc, as |
||||
command line arguments. |
||||
- Configure script must support common environment variable like CFLAGS, CC, etc. |
||||
- Compilation step must detect when a reconfigure is needed, and do it |
||||
transparently. |
||||
|
||||
Known limitations: |
||||
- Executables from external projects cannot be used uninstalled, because they |
||||
would need its libraries to be installed in the final location. This is why |
||||
there is no `find_program()` method. |
||||
- The configure script must generate a `Makefile`, other build systems are not |
||||
yet supported. |
||||
- When cross compiling, if `PKG_CONFIG_SYSROOT_DIR` is set in environment or |
||||
`sys_root` in the cross file properties, the external subproject will not be |
||||
able to find dependencies built by meson using pkg-config. The reason is |
||||
pkg-config and pkgconf both prepend the sysroot path to `-I` and `-L` arguments |
||||
from `-uninstalled.pc` files. This is arguably a bug that could be fixed in |
||||
future version of pkg-config/pkgconf. |
||||
|
||||
*Added 0.56.0* |
||||
|
||||
## Functions |
||||
|
||||
### `add_project()` |
||||
|
||||
This function should be called at the root directory of a project using another |
||||
build system. Usually in a `meson.build` file placed in the top directory of a |
||||
subproject, but could be also in any subdir. |
||||
|
||||
Its first positional argument is the name of the configure script to be |
||||
executed (e.g. `configure` or `autogen.sh`), that file must be in the current |
||||
directory and executable. |
||||
|
||||
Keyword arguments: |
||||
- `configure_options`: An array of strings to be passed as arguments to the |
||||
configure script. Some special tags will be replaced by Meson before passing |
||||
them to the configure script: `@PREFIX@`, `@LIBDIR@` and `@INCLUDEDIR@`. |
||||
Note that `libdir` and `includedir` paths are relative to `prefix` in Meson |
||||
but some configure scripts requires absolute path, in that case they can be |
||||
passed as `'--libdir=@PREFIX@/@LIBDIR@'`. |
||||
- `cross_configure_options`: Extra options appended to `configure_options` only |
||||
when cross compiling. special tag `@HOST@` will be replaced by |
||||
`'{}-{}-{}'.format(host_machine.cpu_family(), build_machine.system(), host_machine.system()`. |
||||
If omitted it defaults to `['--host=@HOST@']`. |
||||
- `verbose`: If set to `true` the output of sub-commands ran to configure, build |
||||
and install the project will be printed onto Meson's stdout. |
||||
- `env` : environment variables to set, such as `['NAME1=value1', 'NAME2=value2']`, |
||||
a dictionary, or an [`environment()` object](Reference-manual.md#environment-object). |
||||
|
||||
Returns an [`ExternalProject`](#ExternalProject_object) object |
||||
|
||||
## `ExternalProject` object |
||||
|
||||
### Methods |
||||
|
||||
#### `dependency(libname)` |
||||
|
||||
Return a dependency object that can be used to build targets against a library |
||||
from the external project. |
||||
|
||||
Keyword arguments: |
||||
- `subdir` path relative to `includedir` to be added to the header search path. |
||||
|
||||
## Example `meson.build` file for a subproject |
||||
|
||||
```meson |
||||
project('My Autotools Project', 'c', |
||||
meson_version : '>=0.56.0', |
||||
) |
||||
|
||||
mod = import('unstable_external_project') |
||||
|
||||
p = mod.add_project('configure', |
||||
configure_options : ['--prefix=@PREFIX@', |
||||
'--libdir=@LIBDIR@', |
||||
'--incdir=@INCLUDEDIR@', |
||||
'--enable-foo', |
||||
], |
||||
) |
||||
|
||||
mylib_dep = p.dependency('mylib') |
||||
``` |
||||
|
||||
## Using wrap file |
||||
|
||||
Most of the time the project will be built as a subproject, and fetched using |
||||
a `.wrap` file. In that case the simple `meson.build` file needed to build the |
||||
subproject can be provided by adding `patch_directory=mysubproject` line |
||||
in the wrap file, and place the build definition file at |
||||
`subprojects/packagefiles/mysubproject/meson.build`. |
@ -0,0 +1,24 @@ |
||||
## External projects |
||||
|
||||
A new experimental module `unstable_external_project` has been added to build |
||||
code using other build systems than Meson. Currently only supporting projects |
||||
with a configure script that generates Makefiles. |
||||
|
||||
```meson |
||||
project('My Autotools Project', 'c', |
||||
meson_version : '>=0.56.0', |
||||
) |
||||
|
||||
mod = import('unstable_external_project') |
||||
|
||||
p = mod.add_project('configure', |
||||
configure_options : ['--prefix=@PREFIX@', |
||||
'--libdir=@LIBDIR@', |
||||
'--incdir=@INCLUDEDIR@', |
||||
'--enable-foo', |
||||
], |
||||
) |
||||
|
||||
mylib_dep = p.dependency('mylib') |
||||
``` |
||||
|
@ -0,0 +1,253 @@ |
||||
# Copyright 2020 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 os, subprocess, shlex |
||||
from pathlib import Path |
||||
|
||||
from . import ExtensionModule, ModuleReturnValue |
||||
from .. import mlog, build |
||||
from ..mesonlib import (MesonException, Popen_safe, MachineChoice, |
||||
get_variable_regex, do_replacement) |
||||
from ..interpreterbase import InterpreterObject, InterpreterException, FeatureNew |
||||
from ..interpreterbase import stringArgs, permittedKwargs |
||||
from ..interpreter import DependencyHolder, InstallDir |
||||
from ..compilers.compilers import cflags_mapping, cexe_mapping |
||||
from ..dependencies.base import InternalDependency, PkgConfigDependency |
||||
|
||||
class ExternalProject(InterpreterObject): |
||||
def __init__(self, interpreter, subdir, project_version, subproject, environment, build_machine, host_machine, |
||||
configure_command, configure_options, cross_configure_options, env, verbose): |
||||
InterpreterObject.__init__(self) |
||||
self.methods.update({'dependency': self.dependency_method, |
||||
}) |
||||
|
||||
self.interpreter = interpreter |
||||
self.subdir = Path(subdir) |
||||
self.project_version = project_version |
||||
self.subproject = subproject |
||||
self.env = environment |
||||
self.build_machine = build_machine |
||||
self.host_machine = host_machine |
||||
self.configure_command = configure_command |
||||
self.configure_options = configure_options |
||||
self.cross_configure_options = cross_configure_options |
||||
self.verbose = verbose |
||||
self.user_env = env |
||||
|
||||
self.name = self.subdir.name |
||||
self.src_dir = Path(self.env.get_source_dir(), self.subdir) |
||||
self.build_dir = Path(self.env.get_build_dir(), self.subdir, 'build') |
||||
self.install_dir = Path(self.env.get_build_dir(), self.subdir, 'dist') |
||||
self.prefix = Path(self.env.coredata.get_builtin_option('prefix')) |
||||
self.libdir = Path(self.env.coredata.get_builtin_option('libdir')) |
||||
self.includedir = Path(self.env.coredata.get_builtin_option('includedir')) |
||||
|
||||
# On Windows if the prefix is "c:/foo" and DESTDIR is "c:/bar", `make` |
||||
# will install files into "c:/bar/c:/foo" which is an invalid path. |
||||
# Work around that issue by removing the drive from prefix. |
||||
if self.prefix.drive: |
||||
self.prefix = self.prefix.relative_to(self.prefix.drive) |
||||
|
||||
# self.prefix is an absolute path, so we cannot append it to another path. |
||||
self.rel_prefix = self.prefix.relative_to(self.prefix.root) |
||||
|
||||
self.make = self.interpreter.find_program_impl('make') |
||||
self.make = self.make.get_command()[0] |
||||
|
||||
self._configure() |
||||
|
||||
self.targets = self._create_targets() |
||||
|
||||
def _configure(self): |
||||
# Assume it's the name of a script in source dir, like 'configure', |
||||
# 'autogen.sh', etc). |
||||
configure_path = Path(self.src_dir, self.configure_command) |
||||
configure_prog = self.interpreter.find_program_impl(configure_path.as_posix()) |
||||
configure_cmd = configure_prog.get_command() |
||||
|
||||
d = {'PREFIX': self.prefix.as_posix(), |
||||
'LIBDIR': self.libdir.as_posix(), |
||||
'INCLUDEDIR': self.includedir.as_posix(), |
||||
} |
||||
self._validate_configure_options(d.keys()) |
||||
|
||||
configure_cmd += self._format_options(self.configure_options, d) |
||||
|
||||
if self.env.is_cross_build(): |
||||
host = '{}-{}-{}'.format(self.host_machine.cpu_family, |
||||
self.build_machine.system, |
||||
self.host_machine.system) |
||||
d = {'HOST': host} |
||||
configure_cmd += self._format_options(self.cross_configure_options, d) |
||||
|
||||
# Set common env variables like CFLAGS, CC, etc. |
||||
link_exelist = [] |
||||
link_args = [] |
||||
self.run_env = os.environ.copy() |
||||
for lang, compiler in self.env.coredata.compilers[MachineChoice.HOST].items(): |
||||
if any(lang not in i for i in (cexe_mapping, cflags_mapping)): |
||||
continue |
||||
cargs = self.env.coredata.get_external_args(MachineChoice.HOST, lang) |
||||
self.run_env[cexe_mapping[lang]] = self._quote_and_join(compiler.get_exelist()) |
||||
self.run_env[cflags_mapping[lang]] = self._quote_and_join(cargs) |
||||
if not link_exelist: |
||||
link_exelist = compiler.get_linker_exelist() |
||||
link_args = self.env.coredata.get_external_link_args(MachineChoice.HOST, lang) |
||||
if link_exelist: |
||||
self.run_env['LD'] = self._quote_and_join(link_exelist) |
||||
self.run_env['LDFLAGS'] = self._quote_and_join(link_args) |
||||
|
||||
self.run_env = self.user_env.get_env(self.run_env) |
||||
|
||||
PkgConfigDependency.setup_env(self.run_env, self.env, MachineChoice.HOST, |
||||
Path(self.env.get_build_dir(), 'meson-uninstalled').as_posix()) |
||||
|
||||
self.build_dir.mkdir(parents=True, exist_ok=True) |
||||
self._run('configure', configure_cmd) |
||||
|
||||
def _quote_and_join(self, array): |
||||
return ' '.join([shlex.quote(i) for i in array]) |
||||
|
||||
def _validate_configure_options(self, required_keys): |
||||
# Ensure the user at least try to pass basic info to the build system, |
||||
# like the prefix, libdir, etc. |
||||
for key in required_keys: |
||||
key_format = '@{}@'.format(key) |
||||
for option in self.configure_options: |
||||
if key_format in option: |
||||
break |
||||
else: |
||||
m = 'At least one configure option must contain "{}" key' |
||||
raise InterpreterException(m.format(key_format)) |
||||
|
||||
def _format_options(self, options, variables): |
||||
out = [] |
||||
missing = set() |
||||
regex = get_variable_regex('meson') |
||||
confdata = {k: (v, None) for k, v in variables.items()} |
||||
for o in options: |
||||
arg, missing_vars = do_replacement(regex, o, 'meson', confdata) |
||||
missing.update(missing_vars) |
||||
out.append(arg) |
||||
if missing: |
||||
var_list = ", ".join(map(repr, sorted(missing))) |
||||
raise EnvironmentException( |
||||
"Variables {} in configure options are missing.".format(var_list)) |
||||
return out |
||||
|
||||
def _run(self, step, command): |
||||
mlog.log('External project {}:'.format(self.name), mlog.bold(step)) |
||||
output = None if self.verbose else subprocess.DEVNULL |
||||
p, o, e = Popen_safe(command, cwd=str(self.build_dir), env=self.run_env, |
||||
stderr=subprocess.STDOUT, |
||||
stdout=output) |
||||
if p.returncode != 0: |
||||
m = '{} step failed:\n{}'.format(step, e) |
||||
raise MesonException(m) |
||||
|
||||
def _create_targets(self): |
||||
cmd = self.env.get_build_command() |
||||
cmd += ['--internal', 'externalproject', |
||||
'--name', self.name, |
||||
'--srcdir', self.src_dir.as_posix(), |
||||
'--builddir', self.build_dir.as_posix(), |
||||
'--installdir', self.install_dir.as_posix(), |
||||
'--make', self.make, |
||||
] |
||||
if self.verbose: |
||||
cmd.append('--verbose') |
||||
|
||||
target_kwargs = {'output': '{}.stamp'.format(self.name), |
||||
'depfile': '{}.d'.format(self.name), |
||||
'command': cmd + ['@OUTPUT@', '@DEPFILE@'], |
||||
'console': True, |
||||
} |
||||
self.target = build.CustomTarget(self.name, |
||||
self.subdir.as_posix(), |
||||
self.subproject, |
||||
target_kwargs) |
||||
|
||||
idir = InstallDir(self.subdir.as_posix(), |
||||
Path('dist', self.rel_prefix).as_posix(), |
||||
install_dir='.', |
||||
install_mode=None, |
||||
exclude=None, |
||||
strip_directory=True, |
||||
from_source_dir=False) |
||||
|
||||
return [self.target, idir] |
||||
|
||||
@stringArgs |
||||
@permittedKwargs({'subdir'}) |
||||
def dependency_method(self, args, kwargs): |
||||
if len(args) != 1: |
||||
m = 'ExternalProject.dependency takes exactly 1 positional arguments' |
||||
raise InterpreterException(m) |
||||
libname = args[0] |
||||
|
||||
subdir = kwargs.get('subdir', '') |
||||
if not isinstance(subdir, str): |
||||
m = 'ExternalProject.dependency subdir keyword argument must be string.' |
||||
raise InterpreterException(m) |
||||
|
||||
abs_includedir = Path(self.install_dir, self.rel_prefix, self.includedir) |
||||
if subdir: |
||||
abs_includedir = Path(abs_includedir, subdir) |
||||
abs_libdir = Path(self.install_dir, self.rel_prefix, self.libdir) |
||||
|
||||
version = self.project_version['version'] |
||||
incdir = [] |
||||
compile_args = ['-I{}'.format(abs_includedir)] |
||||
link_args = ['-L{}'.format(abs_libdir), '-l{}'.format(libname)] |
||||
libs = [] |
||||
libs_whole = [] |
||||
sources = self.target |
||||
final_deps = [] |
||||
variables = [] |
||||
dep = InternalDependency(version, incdir, compile_args, link_args, libs, |
||||
libs_whole, sources, final_deps, variables) |
||||
return DependencyHolder(dep, self.subproject) |
||||
|
||||
|
||||
class ExternalProjectModule(ExtensionModule): |
||||
@FeatureNew('External build system Module', '0.56.0') |
||||
def __init__(self, interpreter): |
||||
super().__init__(interpreter) |
||||
|
||||
@stringArgs |
||||
@permittedKwargs({'configure_options', 'cross_configure_options', 'verbose', 'env'}) |
||||
def add_project(self, state, args, kwargs): |
||||
if len(args) != 1: |
||||
raise InterpreterException('add_project takes exactly one positional argument') |
||||
configure_command = args[0] |
||||
configure_options = kwargs.get('configure_options', []) |
||||
cross_configure_options = kwargs.get('cross_configure_options', ['--host={host}']) |
||||
verbose = kwargs.get('verbose', False) |
||||
env = self.interpreter.unpack_env_kwarg(kwargs) |
||||
project = ExternalProject(self.interpreter, |
||||
state.subdir, |
||||
state.project_version, |
||||
state.subproject, |
||||
state.environment, |
||||
state.build_machine, |
||||
state.host_machine, |
||||
configure_command, |
||||
configure_options, |
||||
cross_configure_options, |
||||
env, verbose) |
||||
return ModuleReturnValue(project, project.targets) |
||||
|
||||
|
||||
def initialize(*args, **kwargs): |
||||
return ExternalProjectModule(*args, **kwargs) |
@ -0,0 +1,95 @@ |
||||
# Copyright 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. |
||||
# 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 os |
||||
import argparse |
||||
import multiprocessing |
||||
import subprocess |
||||
from pathlib import Path |
||||
|
||||
from ..mesonlib import Popen_safe |
||||
|
||||
class ExternalProject: |
||||
def __init__(self, options): |
||||
self.name = options.name |
||||
self.src_dir = options.srcdir |
||||
self.build_dir = options.builddir |
||||
self.install_dir = options.installdir |
||||
self.verbose = options.verbose |
||||
self.stampfile = options.stampfile |
||||
self.depfile = options.depfile |
||||
self.make = options.make |
||||
|
||||
def write_depfile(self): |
||||
with open(self.depfile, 'w') as f: |
||||
f.write('{}: \\\n'.format(self.stampfile)) |
||||
for dirpath, dirnames, filenames in os.walk(self.src_dir): |
||||
dirnames[:] = [d for d in dirnames if not d.startswith('.')] |
||||
for fname in filenames: |
||||
if fname.startswith('.'): |
||||
continue |
||||
path = Path(dirpath, fname) |
||||
f.write(' {} \\\n'.format(path.as_posix().replace(' ', '\\ '))) |
||||
|
||||
def write_stampfile(self): |
||||
with open(self.stampfile, 'w') as f: |
||||
pass |
||||
|
||||
def gnu_make(self): |
||||
p, o, e = Popen_safe([self.make, '--version']) |
||||
if p.returncode == 0 and 'GNU Make' in o: |
||||
return True |
||||
return False |
||||
|
||||
def build(self): |
||||
make_cmd = [self.make] |
||||
if not self.verbose: |
||||
make_cmd.append('--quiet') |
||||
if self.gnu_make(): |
||||
make_cmd.append('-j' + str(multiprocessing.cpu_count())) |
||||
|
||||
rc = self._run(make_cmd) |
||||
if rc != 0: |
||||
return rc |
||||
|
||||
install_cmd = make_cmd + ['DESTDIR= ' + self.install_dir, 'install'] |
||||
rc = self._run(install_cmd) |
||||
if rc != 0: |
||||
return rc |
||||
|
||||
self.write_depfile() |
||||
self.write_stampfile() |
||||
|
||||
return 0 |
||||
|
||||
def _run(self, command): |
||||
output = None if self.verbose else subprocess.DEVNULL |
||||
p, o, e = Popen_safe(command, stderr=subprocess.STDOUT, stdout=output, |
||||
cwd=self.build_dir) |
||||
return p.returncode |
||||
|
||||
def run(args): |
||||
parser = argparse.ArgumentParser() |
||||
parser.add_argument('--name') |
||||
parser.add_argument('--srcdir') |
||||
parser.add_argument('--builddir') |
||||
parser.add_argument('--installdir') |
||||
parser.add_argument('--make') |
||||
parser.add_argument('--verbose', action='store_true') |
||||
parser.add_argument('stampfile') |
||||
parser.add_argument('depfile') |
||||
|
||||
options = parser.parse_args(args) |
||||
ep = ExternalProject(options) |
||||
return ep.build() |
@ -0,0 +1,7 @@ |
||||
#include <libfoo.h> |
||||
|
||||
int main(void) |
||||
{ |
||||
return call_foo() == 42 ? 0 : 1; |
||||
} |
||||
|
@ -0,0 +1,7 @@ |
||||
#include "func.h" |
||||
|
||||
int func(void) |
||||
{ |
||||
return 1; |
||||
} |
||||
|
@ -0,0 +1 @@ |
||||
int func(void); |
@ -0,0 +1,44 @@ |
||||
#! /bin/sh |
||||
|
||||
srcdir=$(dirname "$0") |
||||
|
||||
for i in "$@" |
||||
do |
||||
case $i in |
||||
--prefix=*) |
||||
PREFIX="${i#*=}" |
||||
shift |
||||
;; |
||||
--libdir=*) |
||||
LIBDIR="${i#*=}" |
||||
shift |
||||
;; |
||||
--includedir=*) |
||||
INCDIR="${i#*=}" |
||||
shift |
||||
;; |
||||
--libext=*) |
||||
LIBEXT="${i#*=}" |
||||
shift |
||||
;; |
||||
*) |
||||
shift |
||||
;; |
||||
esac |
||||
done |
||||
|
||||
DEP_ARGS=$(pkg-config somelib --cflags --libs) |
||||
|
||||
cat > Makefile << EOL |
||||
all: libfoo.$LIBEXT |
||||
|
||||
libfoo.$LIBEXT: |
||||
$CC "$srcdir/libfoo.c" -shared -fPIC $DEP_ARGS -o \$@ |
||||
|
||||
install: libfoo.$LIBEXT |
||||
mkdir -p "\$(DESTDIR)$LIBDIR"; |
||||
mkdir -p "\$(DESTDIR)$LIBDIR/pkgconfig"; |
||||
mkdir -p "\$(DESTDIR)$INCDIR"; |
||||
cp \$< "\$(DESTDIR)$LIBDIR"; |
||||
cp "$srcdir/libfoo.h" "\$(DESTDIR)$INCDIR"; |
||||
EOL |
@ -0,0 +1,8 @@ |
||||
#include "libfoo.h" |
||||
|
||||
int func(void); |
||||
|
||||
int call_foo() |
||||
{ |
||||
return func() == 1 ? 42 : 0; |
||||
} |
@ -0,0 +1,3 @@ |
||||
#pragma once |
||||
|
||||
int call_foo(void); |
@ -0,0 +1,22 @@ |
||||
mod = import('unstable_external_project') |
||||
|
||||
target_system = target_machine.system() |
||||
if target_system in ['windows', 'cygwin'] |
||||
libext = 'dll' |
||||
elif target_system == 'darwin' |
||||
libext = 'dylib' |
||||
else |
||||
libext = 'so' |
||||
endif |
||||
|
||||
p = mod.add_project('configure', |
||||
configure_options : [ |
||||
'--prefix=@PREFIX@', |
||||
'--libdir=@PREFIX@/@LIBDIR@', |
||||
'--includedir=@PREFIX@/@INCLUDEDIR@', |
||||
'--libext=' + libext, |
||||
], |
||||
) |
||||
|
||||
libfoo_dep = declare_dependency(link_with : somelib, |
||||
dependencies : p.dependency('foo')) |
@ -0,0 +1,27 @@ |
||||
project('test external project', 'c') |
||||
|
||||
if not find_program('pkg-config', required: false).found() |
||||
error('MESON_SKIP_TEST: pkg-config not found') |
||||
endif |
||||
|
||||
if not find_program('make', required : false).found() |
||||
error('MESON_SKIP_TEST: make not found') |
||||
endif |
||||
|
||||
if host_machine.system() == 'windows' |
||||
error('MESON_SKIP_TEST: The fake configure script is too dumb to work on Windows') |
||||
endif |
||||
|
||||
if meson.is_cross_build() |
||||
# CI uses PKG_CONFIG_SYSROOT_DIR which breaks -uninstalled.pc usage. |
||||
error('MESON_SKIP_TEST: Cross build support is too limited for this test') |
||||
endif |
||||
|
||||
pkg = import('pkgconfig') |
||||
|
||||
somelib = library('somelib', 'func.c') |
||||
pkg.generate(somelib) |
||||
|
||||
subdir('libfoo') |
||||
|
||||
executable('test-find-library', 'app.c', dependencies : libfoo_dep) |
@ -0,0 +1,7 @@ |
||||
{ |
||||
"installed": [ |
||||
{ "type": "shared_lib", "file": "usr/lib/foo" }, |
||||
{ "type": "file", "file": "usr/include/libfoo.h" }, |
||||
{ "type": "file", "file": "usr/lib/pkgconfig/somelib.pc" } |
||||
] |
||||
} |
Loading…
Reference in new issue