| # Copyright 2013-2021 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 .base import ExternalDependency, DependencyException, DependencyTypeName |
| from .pkgconfig import PkgConfigDependency |
| from ..mesonlib import Popen_safe |
| from ..programs import ExternalProgram |
| from ..compilers import DCompiler |
| from .. import mlog |
| import re |
| import os |
| import copy |
| import json |
| import platform |
| import typing as T |
| |
| if T.TYPE_CHECKING: |
| from ..environment import Environment |
| |
| class DubDependency(ExternalDependency): |
| class_dubbin = None |
| |
| def __init__(self, name: str, environment: 'Environment', kwargs: T.Dict[str, T.Any]): |
| super().__init__(DependencyTypeName('dub'), environment, kwargs, language='d') |
| self.name = name |
| self.module_path: T.Optional[str] = None |
| |
| _temp_comp = super().get_compiler() |
| assert isinstance(_temp_comp, DCompiler) |
| self.compiler = _temp_comp |
| |
| if 'required' in kwargs: |
| self.required = kwargs.get('required') |
| |
| if DubDependency.class_dubbin is None: |
| self.dubbin = self._check_dub() |
| DubDependency.class_dubbin = self.dubbin |
| else: |
| self.dubbin = DubDependency.class_dubbin |
| |
| if not self.dubbin: |
| if self.required: |
| raise DependencyException('DUB not found.') |
| self.is_found = False |
| return |
| |
| assert isinstance(self.dubbin, ExternalProgram) |
| mlog.debug('Determining dependency {!r} with DUB executable ' |
| '{!r}'.format(name, self.dubbin.get_path())) |
| |
| # we need to know the target architecture |
| arch = self.compiler.arch |
| |
| # Ask dub for the package |
| ret, res = self._call_dubbin(['describe', name, '--arch=' + arch]) |
| |
| if ret != 0: |
| self.is_found = False |
| return |
| |
| comp = self.compiler.get_id().replace('llvm', 'ldc').replace('gcc', 'gdc') |
| packages = [] |
| description = json.loads(res) |
| for package in description['packages']: |
| packages.append(package['name']) |
| if package['name'] == name: |
| self.is_found = True |
| |
| not_lib = True |
| if 'targetType' in package: |
| if package['targetType'] in ['library', 'sourceLibrary', 'staticLibrary', 'dynamicLibrary']: |
| not_lib = False |
| |
| if not_lib: |
| mlog.error(mlog.bold(name), "found but it isn't a library") |
| self.is_found = False |
| return |
| |
| self.module_path = self._find_right_lib_path(package['path'], comp, description, True, package['targetFileName']) |
| if not os.path.exists(self.module_path): |
| # check if the dependency was built for other archs |
| archs = [['x86_64'], ['x86'], ['x86', 'x86_mscoff']] |
| for a in archs: |
| description_a = copy.deepcopy(description) |
| description_a['architecture'] = a |
| arch_module_path = self._find_right_lib_path(package['path'], comp, description_a, True, package['targetFileName']) |
| if arch_module_path: |
| mlog.error(mlog.bold(name), "found but it wasn't compiled for", mlog.bold(arch)) |
| self.is_found = False |
| return |
| |
| mlog.error(mlog.bold(name), "found but it wasn't compiled with", mlog.bold(comp)) |
| self.is_found = False |
| return |
| |
| self.version = package['version'] |
| self.pkg = package |
| |
| if self.pkg['targetFileName'].endswith('.a'): |
| self.static = True |
| |
| self.compile_args = [] |
| for flag in self.pkg['dflags']: |
| self.link_args.append(flag) |
| for path in self.pkg['importPaths']: |
| self.compile_args.append('-I' + os.path.join(self.pkg['path'], path)) |
| |
| self.link_args = self.raw_link_args = [] |
| for flag in self.pkg['lflags']: |
| self.link_args.append(flag) |
| |
| self.link_args.append(os.path.join(self.module_path, self.pkg['targetFileName'])) |
| |
| # Handle dependencies |
| libs = [] |
| |
| def add_lib_args(field_name: str, target: T.Dict[str, T.Dict[str, str]]) -> None: |
| if field_name in target['buildSettings']: |
| for lib in target['buildSettings'][field_name]: |
| if lib not in libs: |
| libs.append(lib) |
| if os.name != 'nt': |
| pkgdep = PkgConfigDependency(lib, environment, {'required': 'true', 'silent': 'true'}) |
| for arg in pkgdep.get_compile_args(): |
| self.compile_args.append(arg) |
| for arg in pkgdep.get_link_args(): |
| self.link_args.append(arg) |
| for arg in pkgdep.get_link_args(raw=True): |
| self.raw_link_args.append(arg) |
| |
| for target in description['targets']: |
| if target['rootPackage'] in packages: |
| add_lib_args('libs', target) |
| add_lib_args(f'libs-{platform.machine()}', target) |
| for file in target['buildSettings']['linkerFiles']: |
| lib_path = self._find_right_lib_path(file, comp, description) |
| if lib_path: |
| self.link_args.append(lib_path) |
| else: |
| self.is_found = False |
| |
| def _find_right_lib_path(self, |
| default_path: str, |
| comp: str, |
| description: T.Dict[str, str], |
| folder_only: bool = False, |
| file_name: str = '') -> T.Optional[str]: |
| module_path = lib_file_name = '' |
| if folder_only: |
| module_path = default_path |
| lib_file_name = file_name |
| else: |
| module_path = os.path.dirname(default_path) |
| lib_file_name = os.path.basename(default_path) |
| module_build_path = os.path.join(module_path, '.dub', 'build') |
| |
| # If default_path is a path to lib file and |
| # directory of lib don't have subdir '.dub/build' |
| if not os.path.isdir(module_build_path) and os.path.isfile(default_path): |
| if folder_only: |
| return module_path |
| else: |
| return default_path |
| |
| # Get D version implemented in the compiler |
| # gdc doesn't support this |
| ret, res = self._call_dubbin(['--version']) |
| |
| if ret != 0: |
| mlog.error('Failed to run {!r}', mlog.bold(comp)) |
| return None |
| |
| d_ver_reg = re.search('v[0-9].[0-9][0-9][0-9].[0-9]', res) # Ex.: v2.081.2 |
| if d_ver_reg is not None: |
| d_ver = d_ver_reg.group().rsplit('.', 1)[0].replace('v', '').replace('.', '') # Fix structure. Ex.: 2081 |
| else: |
| d_ver = '' # gdc |
| |
| if not os.path.isdir(module_build_path): |
| return '' |
| |
| # Ex.: library-debug-linux.posix-x86_64-ldc_2081-EF934983A3319F8F8FF2F0E107A363BA |
| build_name = '-{}-{}-{}-{}_{}'.format(description['buildType'], '.'.join(description['platform']), '.'.join(description['architecture']), comp, d_ver) |
| for entry in os.listdir(module_build_path): |
| if build_name in entry: |
| for file in os.listdir(os.path.join(module_build_path, entry)): |
| if file == lib_file_name: |
| if folder_only: |
| return os.path.join(module_build_path, entry) |
| else: |
| return os.path.join(module_build_path, entry, lib_file_name) |
| |
| return '' |
| |
| def _call_dubbin(self, args: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str]: |
| assert isinstance(self.dubbin, ExternalProgram) |
| p, out = Popen_safe(self.dubbin.get_command() + args, env=env)[0:2] |
| return p.returncode, out.strip() |
| |
| def _call_copmbin(self, args: T.List[str], env: T.Optional[T.Dict[str, str]] = None) -> T.Tuple[int, str]: |
| p, out = Popen_safe(self.compiler.get_exelist() + args, env=env)[0:2] |
| return p.returncode, out.strip() |
| |
| def _check_dub(self) -> T.Union[bool, ExternalProgram]: |
| dubbin: T.Union[bool, ExternalProgram] = ExternalProgram('dub', silent=True) |
| assert isinstance(dubbin, ExternalProgram) |
| if dubbin.found(): |
| try: |
| p, out = Popen_safe(dubbin.get_command() + ['--version'])[0:2] |
| if p.returncode != 0: |
| mlog.warning('Found dub {!r} but couldn\'t run it' |
| ''.format(' '.join(dubbin.get_command()))) |
| # Set to False instead of None to signify that we've already |
| # searched for it and not found it |
| dubbin = False |
| except (FileNotFoundError, PermissionError): |
| dubbin = False |
| else: |
| dubbin = False |
| if isinstance(dubbin, ExternalProgram): |
| mlog.log('Found DUB:', mlog.bold(dubbin.get_path()), |
| '(%s)' % out.strip()) |
| else: |
| mlog.log('Found DUB:', mlog.red('NO')) |
| return dubbin |