external-project: New module to build configure/make projects

This adds an experimental meson module to build projects with other
build systems.

Closes: #4316
pull/7490/head
Xavier Claessens 6 years ago committed by Xavier Claessens
parent 19696c3dcd
commit 9d338200da
  1. 1
      CODEOWNERS
  2. 116
      docs/markdown/External-Project-module.md
  3. 24
      docs/markdown/snippets/external_project.md
  4. 1
      docs/sitemap.txt
  5. 3
      mesonbuild/compilers/compilers.py
  6. 29
      mesonbuild/dependencies/base.py
  7. 1
      mesonbuild/envconfig.py
  8. 4
      mesonbuild/interpreter.py
  9. 20
      mesonbuild/mesonlib.py
  10. 253
      mesonbuild/modules/unstable_external_project.py
  11. 95
      mesonbuild/scripts/externalproject.py
  12. 7
      test cases/common/236 external project/app.c
  13. 7
      test cases/common/236 external project/func.c
  14. 1
      test cases/common/236 external project/func.h
  15. 44
      test cases/common/236 external project/libfoo/configure
  16. 8
      test cases/common/236 external project/libfoo/libfoo.c
  17. 3
      test cases/common/236 external project/libfoo/libfoo.h
  18. 22
      test cases/common/236 external project/libfoo/meson.build
  19. 27
      test cases/common/236 external project/meson.build
  20. 7
      test cases/common/236 external project/test.json

@ -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')
```

@ -50,6 +50,7 @@ index.md
Windows-module.md
Cuda-module.md
Keyval-module.md
External-Project-module.md
Java.md
Vala.md
D.md

@ -96,6 +96,9 @@ cflags_mapping = {'c': 'CFLAGS',
'vala': 'VALAFLAGS',
'rust': 'RUSTFLAGS'}
cexe_mapping = {'c': 'CC',
'cpp': 'CXX'}
# All these are only for C-linkable languages; see `clink_langs` above.
def sort_clink(lang):

@ -642,28 +642,35 @@ class PkgConfigDependency(ExternalDependency):
mlog.debug("Called `{}` -> {}\n{}".format(call, rc, out))
return rc, out, err
def _call_pkgbin(self, args, env=None):
# Always copy the environment since we're going to modify it
# with pkg-config variables
if env is None:
env = os.environ.copy()
else:
env = env.copy()
extra_paths = self.env.coredata.builtins_per_machine[self.for_machine]['pkg_config_path'].value
sysroot = self.env.properties[self.for_machine].get_sys_root()
@staticmethod
def setup_env(env, environment, for_machine, extra_path=None):
extra_paths = environment.coredata.builtins_per_machine[for_machine]['pkg_config_path'].value
if extra_path:
extra_paths.append(extra_path)
sysroot = environment.properties[for_machine].get_sys_root()
if sysroot:
env['PKG_CONFIG_SYSROOT_DIR'] = sysroot
new_pkg_config_path = ':'.join([p for p in extra_paths])
mlog.debug('PKG_CONFIG_PATH: ' + new_pkg_config_path)
env['PKG_CONFIG_PATH'] = new_pkg_config_path
pkg_config_libdir_prop = self.env.properties[self.for_machine].get_pkg_config_libdir()
pkg_config_libdir_prop = environment.properties[for_machine].get_pkg_config_libdir()
if pkg_config_libdir_prop:
new_pkg_config_libdir = ':'.join([p for p in pkg_config_libdir_prop])
env['PKG_CONFIG_LIBDIR'] = new_pkg_config_libdir
mlog.debug('PKG_CONFIG_LIBDIR: ' + new_pkg_config_libdir)
def _call_pkgbin(self, args, env=None):
# Always copy the environment since we're going to modify it
# with pkg-config variables
if env is None:
env = os.environ.copy()
else:
env = env.copy()
PkgConfigDependency.setup_env(env, self.env, self.for_machine)
fenv = frozenset(env.items())
targs = tuple(args)
cache = PkgConfigDependency.pkgbin_cache

@ -339,6 +339,7 @@ class BinaryTable:
'cmake': 'CMAKE',
'qmake': 'QMAKE',
'pkgconfig': 'PKG_CONFIG',
'make': 'MAKE',
} # type: T.Dict[str, str]
# Deprecated environment variables mapped from the new variable to the old one

@ -2505,6 +2505,8 @@ class Interpreter(InterpreterBase):
return ExternalProgramHolder(item, self.subproject)
elif hasattr(item, 'held_object'):
return item
elif isinstance(item, InterpreterObject):
return item
else:
raise InterpreterException('Module returned a value of unknown type.')
@ -2530,6 +2532,8 @@ class Interpreter(InterpreterBase):
# FIXME: This is special cased and not ideal:
# The first source is our new VapiTarget, the rest are deps
self.process_new_values(v.sources[0])
elif isinstance(v, InstallDir):
self.build.install_dirs.append(v)
elif hasattr(v, 'held_object'):
pass
elif isinstance(v, (int, str, bool, Disabler)):

@ -971,6 +971,17 @@ def do_define(regex: T.Pattern[str], line: str, confdata: 'ConfigurationData', v
else:
raise MesonException('#mesondefine argument "%s" is of unknown type.' % varname)
def get_variable_regex(variable_format: str = 'meson') -> T.Pattern[str]:
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
# Also allow escaping '@' with '\@'
if variable_format in ['meson', 'cmake@']:
regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
elif variable_format == 'cmake':
regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}')
else:
raise MesonException('Format "{}" not handled'.format(variable_format))
return regex
def do_conf_str (data: list, confdata: 'ConfigurationData', variable_format: str,
encoding: str = 'utf-8') -> T.Tuple[T.List[str],T.Set[str], bool]:
def line_is_valid(line : str, variable_format: str) -> bool:
@ -982,14 +993,7 @@ def do_conf_str (data: list, confdata: 'ConfigurationData', variable_format: str
return False
return True
# Only allow (a-z, A-Z, 0-9, _, -) as valid characters for a define
# Also allow escaping '@' with '\@'
if variable_format in ['meson', 'cmake@']:
regex = re.compile(r'(?:\\\\)+(?=\\?@)|\\@|@([-a-zA-Z0-9_]+)@')
elif variable_format == 'cmake':
regex = re.compile(r'(?:\\\\)+(?=\\?\$)|\\\${|\${([-a-zA-Z0-9_]+)}')
else:
raise MesonException('Format "{}" not handled'.format(variable_format))
regex = get_variable_regex(variable_format)
search_token = '#mesondefine'
if variable_format != 'meson':

@ -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,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…
Cancel
Save