The Meson Build System
http://mesonbuild.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
9.3 KiB
275 lines
9.3 KiB
# Copyright 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. |
|
# 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. |
|
|
|
from .boost import BoostDependency |
|
from .cuda import CudaDependency |
|
from .hdf5 import hdf5_factory |
|
from .base import Dependency, InternalDependency, ExternalDependency, NotFoundDependency |
|
from .base import ( |
|
ExternalLibrary, DependencyException, DependencyMethods, |
|
BuiltinDependency, SystemDependency) |
|
from .cmake import CMakeDependency |
|
from .configtool import ConfigToolDependency |
|
from .dub import DubDependency |
|
from .framework import ExtraFrameworkDependency |
|
from .pkgconfig import PkgConfigDependency |
|
from .factory import DependencyFactory |
|
from .detect import find_external_dependency, get_dep_identifier, packages, _packages_accept_language |
|
from .dev import ( |
|
ValgrindDependency, JDKSystemDependency, gmock_factory, gtest_factory, |
|
llvm_factory, zlib_factory) |
|
from .coarrays import coarray_factory |
|
from .mpi import mpi_factory |
|
from .scalapack import scalapack_factory |
|
from .misc import ( |
|
BlocksDependency, OpenMPDependency, cups_factory, curses_factory, gpgme_factory, |
|
libgcrypt_factory, libwmf_factory, netcdf_factory, pcap_factory, python3_factory, |
|
shaderc_factory, threads_factory, ThreadDependency, intl_factory, |
|
) |
|
from .platform import AppleFrameworks |
|
from .qt import qt4_factory, qt5_factory, qt6_factory |
|
from .ui import GnuStepDependency, WxDependency, gl_factory, sdl2_factory, vulkan_factory |
|
|
|
__all__ = [ |
|
'Dependency', |
|
'InternalDependency', |
|
'ExternalDependency', |
|
'SystemDependency', |
|
'BuiltinDependency', |
|
'NotFoundDependency', |
|
'ExternalLibrary', |
|
'DependencyException', |
|
'DependencyMethods', |
|
|
|
'CMakeDependency', |
|
'ConfigToolDependency', |
|
'DubDependency', |
|
'ExtraFrameworkDependency', |
|
'PkgConfigDependency', |
|
|
|
'DependencyFactory', |
|
|
|
'ThreadDependency', |
|
|
|
'find_external_dependency', |
|
'get_dep_identifier', |
|
] |
|
|
|
"""Dependency representations and discovery logic. |
|
|
|
Meson attempts to largely abstract away dependency discovery information, and |
|
to encapsulate that logic itself so that the DSL doesn't have too much direct |
|
information. There are some cases where this is impossible/undesirable, such |
|
as the `get_variable()` method. |
|
|
|
Meson has four primary dependency types: |
|
1. pkg-config |
|
2. apple frameworks |
|
3. CMake |
|
4. system |
|
|
|
Plus a few more niche ones. |
|
|
|
When a user calls `dependency('foo')` Meson creates a list of candidates, and |
|
tries those candidates in order to find one that matches the criteria |
|
provided by the user (such as version requirements, or optional components |
|
that are required.) |
|
|
|
Except to work around bugs or handle odd corner cases, pkg-config and CMake |
|
generally just work™, though there are exceptions. Most of this package is |
|
concerned with dependencies that don't (always) provide CMake and/or |
|
pkg-config files. |
|
|
|
For these cases one needs to write a `system` dependency. These dependencies |
|
descend directly from `ExternalDependency`, in their constructor they |
|
manually set up the necessary link and compile args (and additional |
|
dependencies as necessary). |
|
|
|
For example, imagine a dependency called Foo, it uses an environment variable |
|
called `$FOO_ROOT` to point to its install root, which looks like this: |
|
```txt |
|
$FOOROOT |
|
→ include/ |
|
→ lib/ |
|
``` |
|
To use Foo, you need its include directory, and you need to link to |
|
`lib/libfoo.ext`. |
|
|
|
You could write code that looks like: |
|
|
|
```python |
|
class FooSystemDependency(ExternalDependency): |
|
|
|
def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]): |
|
super().__init__(name, environment, kwargs) |
|
root = os.environ.get('FOO_ROOT') |
|
if root is None: |
|
mlog.debug('$FOO_ROOT is unset.') |
|
self.is_found = False |
|
return |
|
|
|
lib = self.clib_compiler.find_library('foo', environment, [os.path.join(root, 'lib')]) |
|
if lib is None: |
|
mlog.debug('Could not find lib.') |
|
self.is_found = False |
|
return |
|
|
|
self.compile_args.append(f'-I{os.path.join(root, "include")}') |
|
self.link_args.append(lib) |
|
self.is_found = True |
|
``` |
|
|
|
This code will look for `FOO_ROOT` in the environment, handle `FOO_ROOT` being |
|
undefined gracefully, then set its `compile_args` and `link_args` gracefully. |
|
It will also gracefully handle not finding the required lib (hopefully that |
|
doesn't happen, but it could if, for example, the lib is only static and |
|
shared linking is requested). |
|
|
|
There are a couple of things about this that still aren't ideal. For one, we |
|
don't want to be reading random environment variables at this point. Those |
|
should actually be added to `envconfig.Properties` and read in |
|
`environment.Environment._set_default_properties_from_env` (see how |
|
`BOOST_ROOT` is handled). We can also handle the `static` keyword. So |
|
now that becomes: |
|
|
|
```python |
|
class FooSystemDependency(ExternalDependency): |
|
|
|
def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]): |
|
super().__init__(name, environment, kwargs) |
|
root = environment.properties[self.for_machine].foo_root |
|
if root is None: |
|
mlog.debug('foo_root is unset.') |
|
self.is_found = False |
|
return |
|
|
|
static = Mesonlib.LibType.STATIC if kwargs.get('static', False) else Mesonlib.LibType.SHARED |
|
lib = self.clib_compiler.find_library( |
|
'foo', environment, [os.path.join(root, 'lib')], libtype=static) |
|
if lib is None: |
|
mlog.debug('Could not find lib.') |
|
self.is_found = False |
|
return |
|
|
|
self.compile_args.append(f'-I{os.path.join(root, "include")}') |
|
self.link_args.append(lib) |
|
self.is_found = True |
|
``` |
|
|
|
This is nicer in a couple of ways. First we can properly cross compile as we |
|
are allowed to set `FOO_ROOT` for both the build and host machines, it also |
|
means that users can override this in their machine files, and if that |
|
environment variables changes during a Meson reconfigure Meson won't re-read |
|
it, this is important for reproducibility. Finally, Meson will figure out |
|
whether it should be finding `libfoo.so` or `libfoo.a` (or the platform |
|
specific names). Things are looking pretty good now, so it can be added to |
|
the `packages` dict below: |
|
|
|
```python |
|
packages.update({ |
|
'foo': FooSystemDependency, |
|
}) |
|
``` |
|
|
|
Now, what if foo also provides pkg-config, but it's only shipped on Unices, |
|
or only included in very recent versions of the dependency? We can use the |
|
`DependencyFactory` class: |
|
|
|
```python |
|
foo_factory = DependencyFactory( |
|
'foo', |
|
[DependencyMethods.PKGCONFIG, DependencyMethods.SYSTEM], |
|
system_class=FooSystemDependency, |
|
) |
|
``` |
|
|
|
This is a helper function that will generate a default pkg-config based |
|
dependency, and use the `FooSystemDependency` as well. It can also handle |
|
custom finders for pkg-config and cmake based dependencies that need some |
|
extra help. You would then add the `foo_factory` to packages instead of |
|
`FooSystemDependency`: |
|
|
|
```python |
|
packages.update({ |
|
'foo': foo_factory, |
|
}) |
|
``` |
|
|
|
If you have a dependency that is very complicated, (such as having multiple |
|
implementations) you may need to write your own factory function. There are a |
|
number of examples in this package. |
|
|
|
_Note_ before we moved to factory functions it was common to use an |
|
`ExternalDependency` class that would instantiate different types of |
|
dependencies and hold the one it found. There are a number of drawbacks to |
|
this approach, and no new dependencies should do this. |
|
""" |
|
|
|
# This is a dict where the keys should be strings, and the values must be one |
|
# of: |
|
# - An ExternalDependency subclass |
|
# - A DependencyFactory object |
|
# - A callable with a signature of (Environment, MachineChoice, Dict[str, Any]) -> List[Callable[[], ExternalDependency]] |
|
packages.update({ |
|
# From dev: |
|
'gtest': gtest_factory, |
|
'gmock': gmock_factory, |
|
'llvm': llvm_factory, |
|
'valgrind': ValgrindDependency, |
|
'zlib': zlib_factory, |
|
'jdk': JDKSystemDependency, |
|
|
|
'boost': BoostDependency, |
|
'cuda': CudaDependency, |
|
|
|
# per-file |
|
'coarray': coarray_factory, |
|
'hdf5': hdf5_factory, |
|
'mpi': mpi_factory, |
|
'scalapack': scalapack_factory, |
|
|
|
# From misc: |
|
'blocks': BlocksDependency, |
|
'curses': curses_factory, |
|
'netcdf': netcdf_factory, |
|
'openmp': OpenMPDependency, |
|
'python3': python3_factory, |
|
'threads': threads_factory, |
|
'pcap': pcap_factory, |
|
'cups': cups_factory, |
|
'libwmf': libwmf_factory, |
|
'libgcrypt': libgcrypt_factory, |
|
'gpgme': gpgme_factory, |
|
'shaderc': shaderc_factory, |
|
'intl': intl_factory, |
|
|
|
# From platform: |
|
'appleframeworks': AppleFrameworks, |
|
|
|
# From ui: |
|
'gl': gl_factory, |
|
'gnustep': GnuStepDependency, |
|
'qt4': qt4_factory, |
|
'qt5': qt5_factory, |
|
'qt6': qt6_factory, |
|
'sdl2': sdl2_factory, |
|
'wxwidgets': WxDependency, |
|
'vulkan': vulkan_factory, |
|
}) |
|
_packages_accept_language.update({ |
|
'hdf5', |
|
'mpi', |
|
'netcdf', |
|
'openmp', |
|
})
|
|
|