Merge pull request #6356 from dcbaker/fix-d-compiler-abstractions

Fix d compiler abstractions
pull/6772/head
Jussi Pakkanen 5 years ago committed by GitHub
commit 88e40c7081
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .travis.yml
  2. 39
      docs/markdown/Contributing.md
  3. 19
      mesonbuild/compilers/compilers.py
  4. 85
      mesonbuild/compilers/d.py
  5. 4
      mesonbuild/compilers/mixins/visualstudio.py
  6. 88
      mesonbuild/environment.py
  7. 15
      mesonbuild/linkers.py
  8. 79
      run_project_tests.py
  9. 3
      test cases/d/1 simple/test.json
  10. 1
      test cases/d/2 static library/test.json
  11. 20
      test cases/d/3 shared library/lld-test.py
  12. 7
      test cases/d/3 shared library/meson.build
  13. 14
      test cases/d/3 shared library/sub/libstuff.d
  14. 2
      test cases/d/3 shared library/sub/meson.build
  15. 6
      test cases/d/3 shared library/test.json
  16. 38
      test cases/d/4 library versions/test.json
  17. 8
      test cases/d/5 mixed/test.json
  18. 3
      test cases/d/6 unittest/test.json
  19. 23
      test cases/d/7 multilib/test.json

@ -41,7 +41,7 @@ matrix:
before_install: before_install:
- python ./skip_ci.py --base-branch-env=TRAVIS_BRANCH --is-pull-env=TRAVIS_PULL_REQUEST - python ./skip_ci.py --base-branch-env=TRAVIS_BRANCH --is-pull-env=TRAVIS_PULL_REQUEST
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt llvm ninja; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install qt ldc llvm ninja; fi
# # Run one macOS build without pkg-config available, and the other (unity=on) with pkg-config # # Run one macOS build without pkg-config available, and the other (unity=on) with pkg-config
- if [[ "$TRAVIS_OS_NAME" == "osx" && "$MESON_ARGS" =~ .*unity=on.* ]]; then brew install pkg-config; fi - if [[ "$TRAVIS_OS_NAME" == "osx" && "$MESON_ARGS" =~ .*unity=on.* ]]; then brew install pkg-config; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:eoan; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then docker pull jpakkane/mesonci:eoan; fi

@ -190,6 +190,7 @@ Exanple `test.json`:
"installed": [ "installed": [
{ "type": "exe", "file": "usr/bin/testexe" }, { "type": "exe", "file": "usr/bin/testexe" },
{ "type": "pdb", "file": "usr/bin/testexe" }, { "type": "pdb", "file": "usr/bin/testexe" },
{ "type": "shared_lib", "file": "usr/lib/z", "version": "1.2.3" },
], ],
"matrix": { "matrix": {
"options": { "options": {
@ -226,6 +227,8 @@ to be installed. Each dict contains the following keys:
- `file` - `file`
- `type` - `type`
- `platform` (optional) - `platform` (optional)
- `version` (optional)
- `language` (optional)
The `file` entry contains the relative path (from the install root) to the The `file` entry contains the relative path (from the install root) to the
actually installed file. actually installed file.
@ -233,17 +236,37 @@ actually installed file.
The `type` entry specifies how the `file` path should be interpreted based on the The `type` entry specifies how the `file` path should be interpreted based on the
current platform. The following values are currently supported: current platform. The following values are currently supported:
| `type` | Description | | `type` | Description |
| :-----------: | -------------------------------------------------------------------------------- | | :-----------: | ------------------------------------------------------------------------------------------------------- |
| `file` | No postprocessing, just use the provided path | | `file` | No postprocessing, just use the provided path |
| `exe` | For executables. On Windows the `.exe` suffix is added to the path in `file` | | `exe` | For executables. On Windows the `.exe` suffix is added to the path in `file` |
| `pdb` | For Windows PDB files. PDB entries are ignored on non Windows platforms | | `shared_lib` | For shared libraries, always written as `name`. The appropriate suffix and prefix are added by platform |
| `implib` | For Windows import libraries. These entries are ignored on non Windows platforms | | `pdb` | For Windows PDB files. PDB entries are ignored on non Windows platforms |
| `implibempty` | Like `implib`, but no symbols are exported in the library | | `implib` | For Windows import libraries. These entries are ignored on non Windows platforms |
| `expr` | `file` is an expression. This type should be avoided and removed if possible | | `implibempty` | Like `implib`, but no symbols are exported in the library |
| `expr` | `file` is an expression. This type should be avoided and removed if possible |
Except for the `file` and `expr` types, all paths should be provided *without* a suffix. Except for the `file` and `expr` types, all paths should be provided *without* a suffix.
| Argument | Applies to | Description |
| :---------:|----------------------------|-------------------------------------------------------------------------------|
| `version` | `shared_lib`, `pdb` | Sets the version to look for appropriately per-platform |
| `language` | `pdb` | Determines which compiler/linker determines the existence of this file |
The `shared_lib` and `pdb` types takes an optional additional parameter, `version`, this is us a string in `X.Y.Z` format that will be applied to the library. Each version to be tested must have a single version. The harness will apply this correctly per platform:
`pdb` takes an optional `language` argument. This determines which compiler/linker should generate the pdb file. Because it's possible to mix compilers that do and don't generate pdb files (dmd's optlink doesn't). Currently this is only needed when mixing D and C code.
```json
{
"type": "shared_lib", "file": "usr/lib/lib",
"type": "shared_lib", "file": "usr/lib/lib", "version": "1",
"type": "shared_lib", "file": "usr/lib/lib", "version": "1.2.3.",
}
```
This will be applied appropriatly per platform. On windows this expects `lib.dll` and `lib-1.dll`. on MacOS it expects `liblib.dylib` and `liblib.1.dylib`. On other Unices it expects `liblib.so`, `liblib.so.1`, and `liblib.so.1.2.3`.
If the `platform` key is present, the installed file entry is only considered if If the `platform` key is present, the installed file entry is only considered if
the platform matches. The following values for `platform` are currently supported: the platform matches. The following values for `platform` are currently supported:

@ -536,6 +536,15 @@ class CompilerArgs(collections.abc.MutableSequence):
# both of which are invalid. # both of which are invalid.
if arg in cls.dedup2_prefixes: if arg in cls.dedup2_prefixes:
return 0 return 0
if arg.startswith('-L='):
# DMD and LDC proxy all linker arguments using -L=; in conjunction
# with ld64 on macOS this can lead to command line arguments such
# as: `-L=-compatibility_version -L=0 -L=current_version -L=0`.
# These cannot be combined, ld64 insists they must be passed with
# spaces and quoting does not work. if we deduplicate these then
# one of the -L=0 arguments will be removed and the version
# argument will consume the next argument instead.
return 0
if arg in cls.dedup2_args or \ if arg in cls.dedup2_args or \
arg.startswith(cls.dedup2_prefixes) or \ arg.startswith(cls.dedup2_prefixes) or \
arg.endswith(cls.dedup2_suffixes): arg.endswith(cls.dedup2_suffixes):
@ -571,7 +580,17 @@ class CompilerArgs(collections.abc.MutableSequence):
isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker))): isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker))):
group_start = -1 group_start = -1
group_end = -1 group_end = -1
is_soname = False
for i, each in enumerate(new): for i, each in enumerate(new):
if is_soname:
is_soname = False
continue
elif '-soname' in each:
# To proxy these arguments with D you need to split the
# arguments, thus you get `-L=-soname -L=lib.so` we don't
# want to put the lib in a link -roup
is_soname = True
continue
if not each.startswith(('-Wl,-l', '-l')) and not each.endswith('.a') and \ if not each.startswith(('-Wl,-l', '-l')) and not each.endswith('.a') and \
not soregex.match(each): not soregex.match(each):
continue continue

@ -28,7 +28,6 @@ from .compilers import (
CompilerArgs, CompilerArgs,
) )
from .mixins.gnu import GnuCompiler from .mixins.gnu import GnuCompiler
from .mixins.islinker import LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from ..envconfig import MachineInfo from ..envconfig import MachineInfo
@ -69,7 +68,7 @@ dmd_optimization_args = {'0': [],
class DmdLikeCompilerMixin: class DmdLikeCompilerMixin:
LINKER_PREFIX = '-L' LINKER_PREFIX = '-L='
def get_output_args(self, target): def get_output_args(self, target):
return ['-of=' + target] return ['-of=' + target]
@ -214,26 +213,30 @@ class DmdLikeCompilerMixin:
return [] return []
def gen_import_library_args(self, implibname): def gen_import_library_args(self, implibname):
return ['-Wl,--out-implib=' + implibname] return self.linker.import_library_args(implibname)
def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath): def build_rpath_args(self, env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
if self.info.is_windows(): if self.info.is_windows():
return [] return []
# This method is to be used by LDC and DMD. # GNU ld, solaris ld, and lld acting like GNU ld
# GDC can deal with the verbatim flags. if self.linker.id.startswith('ld'):
if not rpath_paths and not install_rpath: # The way that dmd and ldc pass rpath to gcc is different than we would
return [] # do directly, each argument -rpath and the value to rpath, need to be
paths = ':'.join([os.path.join(build_dir, p) for p in rpath_paths]) # split into two separate arguments both prefaced with the -L=.
if build_rpath != '': args = []
paths += ':' + build_rpath for r in super().build_rpath_args(
if len(paths) < len(install_rpath): env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath):
padding = 'X' * (len(install_rpath) - len(paths)) if ',' in r:
if not paths: a, b = r.split(',', maxsplit=1)
paths = padding args.append(a)
else: args.append(self.LINKER_PREFIX + b)
paths = paths + ':' + padding else:
return ['-Wl,-rpath,{}'.format(paths)] args.append(r)
return args
return super().build_rpath_args(
env, build_dir, from_dir, rpath_paths, build_rpath, install_rpath)
def translate_args_to_nongnu(self, args): def translate_args_to_nongnu(self, args):
dcargs = [] dcargs = []
@ -391,15 +394,33 @@ class DmdLikeCompilerMixin:
def get_soname_args(self, *args, **kwargs) -> T.List[str]: def get_soname_args(self, *args, **kwargs) -> T.List[str]:
# LDC and DMD actually do use a linker, but they proxy all of that with # LDC and DMD actually do use a linker, but they proxy all of that with
# their own arguments # their own arguments
soargs = [] if self.linker.id.startswith('ld.'):
for arg in Compiler.get_soname_args(self, *args, **kwargs): soargs = []
soargs.append('-L=' + arg) for arg in super().get_soname_args(*args, **kwargs):
return soargs a, b = arg.split(',', maxsplit=1)
soargs.append(a)
soargs.append(self.LINKER_PREFIX + b)
return soargs
elif self.linker.id.startswith('ld64'):
soargs = []
for arg in super().get_soname_args(*args, **kwargs):
if not arg.startswith(self.LINKER_PREFIX):
soargs.append(self.LINKER_PREFIX + arg)
else:
soargs.append(arg)
return soargs
else:
return super().get_soname_args(*args, **kwargs)
def get_allow_undefined_link_args(self) -> T.List[str]: def get_allow_undefined_link_args(self) -> T.List[str]:
args = [] args = self.linker.get_allow_undefined_args()
for arg in self.linker.get_allow_undefined_args(): if self.info.is_darwin():
args.append('-L=' + arg) # On macOS we're passing these options to the C compiler, but
# they're linker options and need -Wl, so clang/gcc knows what to
# do with them. I'm assuming, but don't know for certain, that
# ldc/dmd do some kind of mapping internally for arguments they
# understand, but pass arguments they don't understand directly.
args = [a.replace('-L=', '-Xcc=-Wl,') for a in args]
return args return args
@ -600,7 +621,7 @@ class DCompiler(Compiler):
return [] return []
def thread_link_flags(self, env): def thread_link_flags(self, env):
return ['-pthread'] return self.linker.thread_flags(env)
def name_string(self): def name_string(self):
return ' '.join(self.exelist) return ' '.join(self.exelist)
@ -658,7 +679,7 @@ class GnuDCompiler(DCompiler, GnuCompiler):
return self.linker.get_allow_undefined_args() return self.linker.get_allow_undefined_args()
class LLVMDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): class LLVMDCompiler(DmdLikeCompilerMixin, DCompiler):
def __init__(self, exelist, version, for_machine: MachineChoice, def __init__(self, exelist, version, for_machine: MachineChoice,
info: 'MachineInfo', arch, **kwargs): info: 'MachineInfo', arch, **kwargs):
@ -687,9 +708,6 @@ class LLVMDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompi
def get_pic_args(self): def get_pic_args(self):
return ['-relocation-model=pic'] return ['-relocation-model=pic']
def get_std_shared_lib_link_args(self):
return ['-shared']
def get_crt_link_args(self, crt_val, buildtype): def get_crt_link_args(self, crt_val, buildtype):
return self.get_crt_args(crt_val, buildtype) return self.get_crt_args(crt_val, buildtype)
@ -699,8 +717,12 @@ class LLVMDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompi
def get_optimization_args(self, optimization_level): def get_optimization_args(self, optimization_level):
return ldc_optimization_args[optimization_level] return ldc_optimization_args[optimization_level]
@classmethod
def use_linker_args(cls, linker: str) -> T.List[str]:
return ['-linker={}'.format(linker)]
class DmdDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompilerMixin, DCompiler): class DmdDCompiler(DmdLikeCompilerMixin, DCompiler):
def __init__(self, exelist, version, for_machine: MachineChoice, def __init__(self, exelist, version, for_machine: MachineChoice,
info: 'MachineInfo', arch, **kwargs): info: 'MachineInfo', arch, **kwargs):
@ -760,3 +782,6 @@ class DmdDCompiler(DmdLikeCompilerMixin, LinkerEnvVarsMixin, BasicLinkerIsCompil
def get_optimization_args(self, optimization_level): def get_optimization_args(self, optimization_level):
return dmd_optimization_args[optimization_level] return dmd_optimization_args[optimization_level]
def can_linker_accept_rsp(self) -> bool:
return False

@ -205,10 +205,6 @@ class VisualStudioLikeCompiler(metaclass=abc.ABCMeta):
objname = os.path.splitext(pchname)[0] + '.obj' objname = os.path.splitext(pchname)[0] + '.obj'
return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname] return objname, ['/Yc' + header, '/Fp' + pchname, '/Fo' + objname]
def gen_import_library_args(self, implibname: str) -> T.List[str]:
"The name of the outputted import library"
return ['/IMPLIB:' + implibname]
def openmp_flags(self) -> T.List[str]: def openmp_flags(self) -> T.List[str]:
return ['/openmp'] return ['/openmp']

@ -752,7 +752,8 @@ class Environment:
def _guess_win_linker(self, compiler: T.List[str], comp_class: Compiler, def _guess_win_linker(self, compiler: T.List[str], comp_class: Compiler,
for_machine: MachineChoice, *, for_machine: MachineChoice, *,
use_linker_prefix: bool = True) -> 'DynamicLinker': use_linker_prefix: bool = True, invoked_directly: bool = True,
extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self) self.coredata.add_lang_args(comp_class.language, comp_class, for_machine, self)
# Explicitly pass logo here so that we can get the version of link.exe # Explicitly pass logo here so that we can get the version of link.exe
@ -771,25 +772,29 @@ class Environment:
override = comp_class.use_linker_args(value[0]) override = comp_class.use_linker_args(value[0])
check_args += override check_args += override
if extra_args is not None:
check_args.extend(extra_args)
p, o, _ = Popen_safe(compiler + check_args) p, o, _ = Popen_safe(compiler + check_args)
if o.startswith('LLD'): if o.startswith('LLD'):
if '(compatible with GNU linkers)' in o: if '(compatible with GNU linkers)' in o:
return LLVMDynamicLinker( return LLVMDynamicLinker(
compiler, for_machine, comp_class.LINKER_PREFIX, compiler, for_machine, comp_class.LINKER_PREFIX,
override, version=search_version(o)) override, version=search_version(o), direct=invoked_directly)
if value is not None: if value is not None and invoked_directly:
compiler = value compiler = value
# We've already hanedled the non-direct case above
p, o, e = Popen_safe(compiler + check_args) p, o, e = Popen_safe(compiler + check_args)
if o.startswith('LLD'): if o.startswith('LLD'):
return ClangClDynamicLinker( return ClangClDynamicLinker(
for_machine, [], for_machine, [],
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
exelist=compiler, version=search_version(o)) exelist=compiler, version=search_version(o), direct=invoked_directly)
elif 'OPTLINK' in o: elif 'OPTLINK' in o:
# Opltink's stdout *may* beging with a \r character. # Opltink's stdout *may* beging with a \r character.
return OptlinkDynamicLinker(for_machine, version=search_version(o)) return OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
elif o.startswith('Microsoft') or e.startswith('Microsoft'): elif o.startswith('Microsoft') or e.startswith('Microsoft'):
out = o or e out = o or e
match = re.search(r'.*(X86|X64|ARM|ARM64).*', out) match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
@ -801,7 +806,7 @@ class Environment:
return MSVCDynamicLinker( return MSVCDynamicLinker(
for_machine, [], machine=target, exelist=compiler, for_machine, [], machine=target, exelist=compiler,
prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [], prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
version=search_version(out)) version=search_version(out), direct=invoked_directly)
elif 'GNU coreutils' in o: elif 'GNU coreutils' in o:
raise EnvironmentException( raise EnvironmentException(
"Found GNU link.exe instead of MSVC link.exe. This link.exe " "Found GNU link.exe instead of MSVC link.exe. This link.exe "
@ -1496,28 +1501,31 @@ class Environment:
if 'LLVM D compiler' in out: if 'LLVM D compiler' in out:
# LDC seems to require a file # LDC seems to require a file
if info.is_windows() or info.is_cygwin(): # We cannot use NamedTemproraryFile on windows, its documented
# Getting LDC on windows to give useful linker output when # to not work for our uses. So, just use mkstemp and only have
# not doing real work is painfully hard. It ships with a # one path for simplicity.
# version of lld-link, so unless we think the user wants o, f = tempfile.mkstemp('.d')
# link.exe, just assume that we're going to use lld-link os.close(o)
# with it.
linker = self._guess_win_linker( try:
['link' if is_msvc else 'lld-link'], if info.is_windows() or info.is_cygwin():
compilers.LLVMDCompiler, for_machine, use_linker_prefix=False) objfile = os.path.basename(f)[:-1] + 'obj'
else: linker = self._guess_win_linker(
with tempfile.NamedTemporaryFile(suffix='.d') as f: exelist,
compilers.LLVMDCompiler, for_machine,
use_linker_prefix=True, invoked_directly=False,
extra_args=[f])
else:
# LDC writes an object file to the current working directory. # LDC writes an object file to the current working directory.
# Clean it up. # Clean it up.
objectfile = os.path.basename(f.name)[:-1] + 'o' objfile = os.path.basename(f)[:-1] + 'o'
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
exelist, compilers.LLVMDCompiler, for_machine, exelist, compilers.LLVMDCompiler, for_machine,
extra_args=[f.name]) extra_args=[f])
try: finally:
os.unlink(objectfile) mesonlib.windows_proof_rm(f)
except Exception: mesonlib.windows_proof_rm(objfile)
# Thank you Windows file system semantics and virus scanners.
pass
return compilers.LLVMDCompiler( return compilers.LLVMDCompiler(
exelist, version, for_machine, info, arch, exelist, version, for_machine, info, arch,
full_version=full_version, linker=linker) full_version=full_version, linker=linker)
@ -1528,20 +1536,30 @@ class Environment:
full_version=full_version, linker=linker) full_version=full_version, linker=linker)
elif 'The D Language Foundation' in out or 'Digital Mars' in out: elif 'The D Language Foundation' in out or 'Digital Mars' in out:
# DMD seems to require a file # DMD seems to require a file
if info.is_windows() or info.is_cygwin(): # We cannot use NamedTemproraryFile on windows, its documented
if is_msvc: # to not work for our uses. So, just use mkstemp and only have
linker_cmd = ['link'] # one path for simplicity.
elif arch == 'x86': o, f = tempfile.mkstemp('.d')
linker_cmd = ['optlink'] os.close(o)
# DMD as different detection logic for x86 and x86_64
arch_arg = '-m64' if arch == 'x86_64' else '-m32'
try:
if info.is_windows() or info.is_cygwin():
objfile = os.path.basename(f)[:-1] + 'obj'
linker = self._guess_win_linker(
exelist, compilers.DmdDCompiler, for_machine,
invoked_directly=False, extra_args=[f, arch_arg])
else: else:
linker_cmd = ['lld-link'] objfile = os.path.basename(f)[:-1] + 'o'
linker = self._guess_win_linker(linker_cmd, compilers.DmdDCompiler, for_machine,
use_linker_prefix=False)
else:
with tempfile.NamedTemporaryFile(suffix='.d') as f:
linker = self._guess_nix_linker( linker = self._guess_nix_linker(
exelist, compilers.DmdDCompiler, for_machine, exelist, compilers.DmdDCompiler, for_machine,
extra_args=[f.name]) extra_args=[f, arch_arg])
finally:
mesonlib.windows_proof_rm(f)
mesonlib.windows_proof_rm(objfile)
return compilers.DmdDCompiler( return compilers.DmdDCompiler(
exelist, version, for_machine, info, arch, exelist, version, for_machine, info, arch,
full_version=full_version, linker=linker) full_version=full_version, linker=linker)

@ -901,6 +901,10 @@ class VisualStudioLikeLinkerMixin:
is_shared_module: bool) -> T.List[str]: is_shared_module: bool) -> T.List[str]:
return [] return []
def import_library_args(self, implibname: str) -> T.List[str]:
"""The command to generate the import library."""
return self._apply_prefix(['/IMPLIB:' + implibname])
class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker): class MSVCDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
@ -993,15 +997,22 @@ class OptlinkDynamicLinker(VisualStudioLikeLinkerMixin, DynamicLinker):
"""Digital Mars dynamic linker for windows.""" """Digital Mars dynamic linker for windows."""
def __init__(self, for_machine: mesonlib.MachineChoice, def __init__(self, exelist: T.List[str], for_machine: mesonlib.MachineChoice,
*, version: str = 'unknown version'): *, version: str = 'unknown version'):
# Use optlink instead of link so we don't interfer with other link.exe # Use optlink instead of link so we don't interfer with other link.exe
# implementations. # implementations.
super().__init__('optlink', ['optlink.exe'], for_machine, '', [], version=version) super().__init__('optlink', exelist, for_machine, '', [], version=version)
def get_allow_undefined_args(self) -> T.List[str]: def get_allow_undefined_args(self) -> T.List[str]:
return [] return []
def get_debugfile_args(self, targetfile: str) -> T.List[str]:
# Optlink does not generate pdb files.
return []
def get_always_args(self) -> T.List[str]:
return []
class CudaLinker(PosixDynamicLinkerMixin, DynamicLinker): class CudaLinker(PosixDynamicLinkerMixin, DynamicLinker):
"""Cuda linker (nvlink)""" """Cuda linker (nvlink)"""

@ -14,19 +14,27 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import typing as T from concurrent.futures import ProcessPoolExecutor, CancelledError
from enum import Enum
from io import StringIO
from pathlib import Path, PurePath
import argparse
import functools import functools
import itertools import itertools
import json
import multiprocessing
import os import os
import subprocess import re
import shlex
import shutil import shutil
import sys
import signal import signal
import shlex import subprocess
from io import StringIO import sys
from enum import Enum
import tempfile import tempfile
from pathlib import Path, PurePath import time
import typing as T
import xml.etree.ElementTree as ET
from mesonbuild import build from mesonbuild import build
from mesonbuild import environment from mesonbuild import environment
from mesonbuild import compilers from mesonbuild import compilers
@ -35,13 +43,7 @@ from mesonbuild import mlog
from mesonbuild import mtest from mesonbuild import mtest
from mesonbuild.mesonlib import MachineChoice, Popen_safe from mesonbuild.mesonlib import MachineChoice, Popen_safe
from mesonbuild.coredata import backendlist from mesonbuild.coredata import backendlist
import argparse
import json
import xml.etree.ElementTree as ET
import time
import multiprocessing
from concurrent.futures import ProcessPoolExecutor, CancelledError
import re
from run_tests import get_fake_options, run_configure, get_meson_script from run_tests import get_fake_options, run_configure, get_meson_script
from run_tests import get_backend_commands, get_backend_args_for_dir, Backend from run_tests import get_backend_commands, get_backend_args_for_dir, Backend
from run_tests import ensure_backend_detects_changes from run_tests import ensure_backend_detects_changes
@ -94,13 +96,29 @@ class InstalledFile:
self.path = raw['file'] self.path = raw['file']
self.typ = raw['type'] self.typ = raw['type']
self.platform = raw.get('platform', None) self.platform = raw.get('platform', None)
self.language = raw.get('language', 'c') # type: str
version = raw.get('version', '') # type: str
if version:
self.version = version.split('.') # type: T.List[str]
else:
# split on '' will return [''], we want an empty list though
self.version = []
def get_path(self, compiler: str, env: environment.Environment) -> T.Optional[Path]: def get_path(self, compiler: str, env: environment.Environment) -> T.Optional[Path]:
p = Path(self.path) p = Path(self.path)
canonical_compiler = compiler canonical_compiler = compiler
if (compiler in ['clang-cl', 'intel-cl']) or (env.machines.host.is_windows() and compiler == 'pgi'): if ((compiler in ['clang-cl', 'intel-cl']) or
(env.machines.host.is_windows() and compiler in {'pgi', 'dmd', 'ldc'})):
canonical_compiler = 'msvc' canonical_compiler = 'msvc'
has_pdb = False
if self.language in {'c', 'cpp'}:
has_pdb = canonical_compiler == 'msvc'
elif self.language == 'd':
# dmd's optlink does not genearte pdb iles
has_pdb = env.coredata.compilers.host['d'].linker.id in {'link', 'lld-link'}
# Abort if the platform does not match # Abort if the platform does not match
matches = { matches = {
'msvc': canonical_compiler == 'msvc', 'msvc': canonical_compiler == 'msvc',
@ -114,11 +132,38 @@ class InstalledFile:
# Handle the different types # Handle the different types
if self.typ == 'file': if self.typ == 'file':
return p return p
elif self.typ == 'shared_lib':
if env.machines.host.is_windows() or env.machines.host.is_cygwin():
# Windows only has foo.dll and foo-X.dll
if len(self.version) > 1:
return None
if self.version:
p = p.with_name('{}-{}'.format(p.name, self.version[0]))
return p.with_suffix('.dll')
p = p.with_name('lib{}'.format(p.name))
if env.machines.host.is_darwin():
# MacOS only has libfoo.dylib and libfoo.X.dylib
if len(self.version) > 1:
return None
# pathlib.Path.with_suffix replaces, not appends
suffix = '.dylib'
if self.version:
suffix = '.{}{}'.format(self.version[0], suffix)
else:
# pathlib.Path.with_suffix replaces, not appends
suffix = '.so'
if self.version:
suffix = '{}.{}'.format(suffix, '.'.join(self.version))
return p.with_suffix(suffix)
elif self.typ == 'exe': elif self.typ == 'exe':
if env.machines.host.is_windows() or env.machines.host.is_cygwin(): if env.machines.host.is_windows() or env.machines.host.is_cygwin():
return p.with_suffix('.exe') return p.with_suffix('.exe')
elif self.typ == 'pdb': elif self.typ == 'pdb':
return p.with_suffix('.pdb') if canonical_compiler == 'msvc' else None if self.version:
p = p.with_name('{}-{}'.format(p.name, self.version[0]))
return p.with_suffix('.pdb') if has_pdb else None
elif self.typ == 'implib' or self.typ == 'implibempty': elif self.typ == 'implib' or self.typ == 'implibempty':
if env.machines.host.is_windows() and canonical_compiler == 'msvc': if env.machines.host.is_windows() and canonical_compiler == 'msvc':
# only MSVC doesn't generate empty implibs # only MSVC doesn't generate empty implibs
@ -208,7 +253,7 @@ def setup_commands(optbackend):
uninstall_commands = get_backend_commands(backend, do_debug) uninstall_commands = get_backend_commands(backend, do_debug)
# TODO try to eliminate or at least reduce this function # TODO try to eliminate or at least reduce this function
def platform_fix_name(fname: str, canonical_compiler: str, env: environment.EnvironmentException) -> str: def platform_fix_name(fname: str, canonical_compiler: str, env: environment.Environment) -> str:
if '?lib' in fname: if '?lib' in fname:
if env.machines.host.is_windows() and canonical_compiler == 'msvc': if env.machines.host.is_windows() and canonical_compiler == 'msvc':
fname = re.sub(r'lib/\?lib(.*)\.', r'bin/\1.', fname) fname = re.sub(r'lib/\?lib(.*)\.', r'bin/\1.', fname)

@ -1,5 +1,6 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/dsimpleapp"} {"type": "exe", "file": "usr/bin/dsimpleapp"},
{"type": "pdb", "file": "usr/bin/dsimpleapp", "language": "d"}
] ]
} }

@ -1,6 +1,7 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/app_s"}, {"type": "exe", "file": "usr/bin/app_s"},
{"type": "pdb", "file": "usr/bin/app_s", "language": "d"},
{"type": "file", "file": "usr/lib/libstuff.a"} {"type": "file", "file": "usr/lib/libstuff.a"}
] ]
} }

@ -0,0 +1,20 @@
#!/usr/bin/env python3
import argparse
import subprocess
def main():
parser = argparse.ArgumentParser()
parser.add_argument('ldd')
parser.add_argument('bin')
args = parser.parse_args()
p, o, _ = subprocess.run([args.ldd, args.bin], stdout=subprocess.PIPE)
assert p == 0
o = o.decode()
assert 'libstuff.so =>' in o, 'libstuff so not in linker path.'
assert 'libstuff.so => not found' not in o, 'libstuff.so not found correctly'
if __name__ == '__main__':
main()

@ -7,7 +7,7 @@ if dc.get_id() == 'gcc'
endif endif
endif endif
ldyn = shared_library('stuff', 'libstuff.d', install : true) subdir('sub')
ed = executable('app_d', 'app.d', link_with : ldyn, install : true) ed = executable('app_d', 'app.d', link_with : ldyn, install : true)
test('linktest_dyn', ed) test('linktest_dyn', ed)
@ -19,3 +19,8 @@ pkgc.generate(name: 'test',
description: 'A test of D attributes to pkgconfig.generate.', description: 'A test of D attributes to pkgconfig.generate.',
d_module_versions: ['Use_Static'] d_module_versions: ['Use_Static']
) )
ldd = find_program('ldd', required : false)
if ldd.found()
test('ldd-test.py', ed)
endif

@ -0,0 +1,14 @@
import std.stdio;
import std.string : format;
export int printLibraryString (string str)
{
writeln ("Library says: %s".format (str));
return 4;
}
version (Windows)
{
import core.sys.windows.dll;
mixin SimpleDllMain;
}

@ -0,0 +1,2 @@
ldyn = shared_library('stuff', 'libstuff.d', install : true)

@ -1,9 +1,11 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/app_d"}, {"type": "exe", "file": "usr/bin/app_d"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff.dll"}, {"type": "pdb", "file": "usr/bin/app_d", "language": "d"},
{"type": "shared_lib", "platform": "msvc", "file": "usr/bin/stuff"},
{"type": "pdb", "file": "usr/bin/stuff", "language": "d"},
{"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff.lib"}, {"type": "file", "platform": "msvc", "file": "usr/lib/stuff.lib"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libstuff.so"},
{"type": "file", "file": "usr/lib/pkgconfig/test.pc"} {"type": "file", "file": "usr/lib/pkgconfig/test.pc"}
] ]
} }

@ -1,21 +1,25 @@
{ {
"installed": [ "installed": [
{"type": "file", "platform": "gcc", "file": "usr/lib/libsome.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/some"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsome.so.0"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/some", "version": "0"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsome.so.1.2.3"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/some", "version": "1.2.3"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libnoversion.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/noversion"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libonlyversion.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/onlyversion"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libonlyversion.so.1"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/onlyversion", "version": "1"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libonlyversion.so.1.4.5"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/onlyversion", "version": "1.4.5"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libonlysoversion.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/onlysoversion"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libonlysoversion.so.5"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/onlysoversion", "version": "5"},
{"type": "file", "platform": "msvc", "file": "usr/bin/noversion.dll"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/noversion"},
{"type": "file", "platform": "msvc", "file": "usr/bin/onlysoversion-5.dll"}, {"type": "pdb", "file": "usr/bin/noversion", "language": "d"},
{"type": "file", "platform": "msvc", "file": "usr/bin/onlyversion-1.dll"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/onlysoversion", "version": "5"},
{"type": "file", "platform": "msvc", "file": "usr/bin/some-0.dll"}, {"type": "pdb", "file": "usr/bin/onlysoversion", "version": "5", "language": "d"},
{"type": "file", "platform": "msvc", "file": "usr/lib/noversion.lib"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/onlyversion", "version": "1"},
{"type": "file", "platform": "msvc", "file": "usr/lib/onlysoversion.lib"}, {"type": "pdb", "file": "usr/bin/onlyversion", "version": "1", "language": "d"},
{"type": "file", "platform": "msvc", "file": "usr/lib/onlyversion.lib"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/some", "version": "0"},
{"type": "file", "platform": "msvc", "file": "usr/lib/some.lib"} {"type": "pdb", "file": "usr/bin/some", "version": "0", "language": "d"},
{"type": "implib", "file": "usr/lib/noversion"},
{"type": "implib", "file": "usr/lib/onlysoversion"},
{"type": "implib", "file": "usr/lib/onlyversion"},
{"type": "implib", "file": "usr/lib/some"}
] ]
} }

@ -1,11 +1,13 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/appdc_d"}, {"type": "exe", "file": "usr/bin/appdc_d"},
{"type": "pdb", "file": "usr/bin/appdc_d", "language": "d"},
{"type": "exe", "file": "usr/bin/appdc_s"}, {"type": "exe", "file": "usr/bin/appdc_s"},
{"type": "pdb", "file": "usr/bin/appdc_s", "language": "d"},
{"type": "file", "file": "usr/lib/libstuff.a"}, {"type": "file", "file": "usr/lib/libstuff.a"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libstuff.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/stuff"},
{"type": "file", "platform": "msvc", "file": "usr/bin/stuff.dll"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/stuff"},
{"type": "pdb", "file": "usr/bin/stuff"}, {"type": "pdb", "file": "usr/bin/stuff", "language": "c"},
{"type": "file", "platform": "msvc", "file": "usr/lib/stuff.lib"} {"type": "file", "platform": "msvc", "file": "usr/lib/stuff.lib"}
] ]
} }

@ -1,5 +1,6 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/dapp"} {"type": "exe", "file": "usr/bin/dapp"},
{"type": "pdb", "file": "usr/bin/dapp", "language": "d"}
] ]
} }

@ -1,15 +1,18 @@
{ {
"installed": [ "installed": [
{"type": "exe", "file": "usr/bin/app_d"}, {"type": "exe", "file": "usr/bin/app_d"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay1.so"}, {"type": "pdb", "file": "usr/bin/app_d", "language": "d"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay1.so.0"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say1"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay1.so.1.2.3"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say1", "version": "0"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay2.so"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say1", "version": "1.2.3"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay2.so.1"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say2"},
{"type": "file", "platform": "gcc", "file": "usr/lib/libsay2.so.1.2.4"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say2", "version": "1"},
{"type": "file", "platform": "msvc", "file": "usr/bin/say1-0.dll"}, {"type": "shared_lib", "platform": "gcc", "file": "usr/lib/say2", "version": "1.2.4"},
{"type": "file", "platform": "msvc", "file": "usr/bin/say2-1.dll"}, {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/say1", "version": "0"},
{"type": "file", "platform": "msvc", "file": "usr/lib/say1.lib"}, {"type": "pdb", "file": "usr/bin/say1", "version": "0", "language": "d"},
{"type": "file", "platform": "msvc", "file": "usr/lib/say2.lib"} {"type": "shared_lib", "platform": "msvc", "file": "usr/bin/say2", "version": "1"},
{"type": "pdb", "file": "usr/bin/say2", "version": "1", "language": "d"},
{"type": "implib", "file": "usr/lib/say1"},
{"type": "implib", "file": "usr/lib/say2"}
] ]
} }

Loading…
Cancel
Save