Repository: mesonbuild/meson

Issue:
Meson 0.52.0 no longer builds D shared libraries on Linux
The unit_threaded Meson build used to work fine. I built unit_threaded 0.10.3 using the Meson build of the time 2019-10-15 which I think was a Meson 0.51.1 setup from 2019-07-24. On upgrading the unit_threaded Git repository to try and build 0.10.4, Ninja did a rebuild of build files due to Meson 0.51.1 → 0.52.0 and now I get the error:
```
|> ninja
[31/35] Linking target libunit-threaded.so.0.10.4.
FAILED: libunit-threaded.so.0.10.4 
ldc2  -of=libunit-threaded.so.0.10.4 'unit-threaded@sha/.._source_unit_threaded_attrs.d.o' 'unit-threaded@sha/.._source_unit_threaded_io.d.o' 'unit-threaded@sha/.._source_unit_threaded_light.d.o' 'unit-threaded@sha/.._source_unit_threaded_package.d.o' 'unit-threaded@sha/.._source_unit_threaded_reflection.d.o' 'unit-threaded@sha/.._source_unit_threaded_should.d.o' 'unit-threaded@sha/.._source_unit_threaded_static_.d.o' 'unit-threaded@sha/.._source_unit_threaded_testcase.d.o' 'unit-threaded@sha/.._subpackages_assertions_source_unit_threaded_assertions.d.o' 'unit-threaded@sha/.._subpackages_exception_source_unit_threaded_exception.d.o' 'unit-threaded@sha/.._subpackages_from_source_unit_threaded_from.d.o' 'unit-threaded@sha/.._subpackages_integration_source_unit_threaded_integration.d.o' 'unit-threaded@sha/.._subpackages_mocks_source_unit_threaded_mock.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_property.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_benchmark.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_gen.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_package.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_random.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_attrs.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_factory.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_io.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_options.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_package.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_reflection.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_runner.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_static._.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_testcase.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_testsuite.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_traits.d.o' -L=--start-group -L-soname,libunit-threaded.so.0 -L=--end-group -L--allow-shlib-undefined -relocation-model=pic
/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main'
collect2: error: ld returned 1 exit status
Error: /usr/bin/gcc failed with status: 1
[33/35] Compiling D object 'gen-ut-main@exe/.._gen_source_unit_threaded_runtime_runtime.d.o'.
ninja: build stopped: subcommand failed.
```
The Meson file for unit_threaded is:
```
# -*- mode: python; -*-

project(
    'unit-threaded',
    'd',
    version: '0.10.4',
    meson_version: '>=0.48',
    default_options: ['buildtype=release'],
)

main_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../source/unit_threaded/*.d').stdout().split()
assertions_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/assertions/source/unit_threaded/*.d').stdout().split()
exception_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/exception/source/unit_threaded/*.d').stdout().split()
from_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/from/source/unit_threaded/*.d').stdout().split()
integration_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/integration/source/unit_threaded/*.d').stdout().split()
mocks_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/mocks/source/unit_threaded/*.d').stdout().split()
property_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/property/source/unit_threaded/*.d').stdout().split()
property_subpackage_randomized_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/property/source/unit_threaded/randomized/*.d').stdout().split()
runner_subpackage_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../subpackages/runner/source/unit_threaded/runner/*.d').stdout().split()

# Treat the single source file packages differently to those with multiple files including a package.d file.
install_headers(main_source, subdir: 'd/unit_threaded')
install_headers(assertions_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(exception_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(from_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(integration_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(mocks_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(property_subpackage_source, subdir: 'd/unit_threaded')  # Single file package.
install_headers(property_subpackage_randomized_source, subdir: 'd/unit_threaded/randomized')
install_headers(runner_subpackage_source, subdir: 'd/unit_threaded/runner')

include_dirs = [
    include_directories('../source/'),
    include_directories('../subpackages/assertions/source/'),
    include_directories('../subpackages/exception/source/'),
    include_directories('../subpackages/from/source/'),
    include_directories('../subpackages/integration/source/'),
    include_directories('../subpackages/mocks/source/'),
    include_directories('../subpackages/property/source/'),
    include_directories('../subpackages/runner/source/'),
]

lib = library(
    meson.project_name(),
    main_source +
        assertions_subpackage_source +
        exception_subpackage_source +
        from_subpackage_source +
        integration_subpackage_source +
        mocks_subpackage_source +
        property_subpackage_source +
        property_subpackage_randomized_source +
        runner_subpackage_source,
    include_directories: include_dirs,
    version: meson.project_version(),
    pic: true,
    install: true,
)

pkgconfig = import('pkgconfig')
pkgconfig.generate(
    name: meson.project_name(),
    libraries: lib,
    subdirs: 'd/',
    version: meson.project_version(),
    description: 'Advanced multi-threaded unit testing framework with minimal to no boilerplate using built-in unittest blocks.',
)

gen_ut_main_runtime_source = run_command('sh', '-c', 'cd $MESON_SOURCE_ROOT && ls ../gen/source/unit_threaded/runtime/*.d').stdout().split()

executable(
    'gen-ut-main',
    ['../gen/gen_ut_main.d'] + gen_ut_main_runtime_source,
    include_directories: include_dirs + [include_directories('../gen/source')],
    link_with: lib,
    link_args: ['-link-defaultlib-shared'],
    install: true,
)

unitthreaded_dep = declare_dependency(
    link_with: lib,
    include_directories: [include_dirs],
)
```
The unit_threaded Git repository is at: https://github.com/atilaneves/unit-threaded


Discussion:
There have been a few D related bugs fixed in master since the 0.52 release; could you try with master and see if it works?
I cloned the Git repository and used it directly, it seems no luck:
```
|> /home/Checkouts/Git/Meson/meson.py --prefix=$HOME/Built ~/Repositories/Git/Forks/Unit_Threaded/build
The Meson build system
Version: 0.52.999
Source dir: /home/users/russel/Repositories/Git/Forks/Unit_Threaded/build
Build dir: /home/users/russel/BuildArea/Unit_Threaded
Build type: native build
Project name: unit-threaded
Project version: 0.10.4
D compiler for the host machine: ldc2 (llvm 1.17.0 "LDC - the LLVM D compiler (1.17.0):")
D linker for the host machine: GNU ld.gold 2.33
Host machine cpu family: x86_64
Host machine cpu: x86_64
Build targets in project: 2
Found ninja-1.9.0 at /usr/bin/ninja

|> ninja
[32/35] Linking target libunit-threaded.so.0.10.4.
FAILED: libunit-threaded.so.0.10.4 
ldc2  -of=libunit-threaded.so.0.10.4 'unit-threaded@sha/.._source_unit_threaded_attrs.d.o' 'unit-threaded@sha/.._source_unit_threaded_io.d.o' 'unit-threaded@sha/.._source_unit_threaded_light.d.o' 'unit-threaded@sha/.._source_unit_threaded_package.d.o' 'unit-threaded@sha/.._source_unit_threaded_reflection.d.o' 'unit-threaded@sha/.._source_unit_threaded_should.d.o' 'unit-threaded@sha/.._source_unit_threaded_static_.d.o' 'unit-threaded@sha/.._source_unit_threaded_testcase.d.o' 'unit-threaded@sha/.._subpackages_assertions_source_unit_threaded_assertions.d.o' 'unit-threaded@sha/.._subpackages_exception_source_unit_threaded_exception.d.o' 'unit-threaded@sha/.._subpackages_from_source_unit_threaded_from.d.o' 'unit-threaded@sha/.._subpackages_integration_source_unit_threaded_integration.d.o' 'unit-threaded@sha/.._subpackages_mocks_source_unit_threaded_mock.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_property.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_benchmark.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_gen.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_package.d.o' 'unit-threaded@sha/.._subpackages_property_source_unit_threaded_randomized_random.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_attrs.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_factory.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_io.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_options.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_package.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_reflection.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_runner.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_static._.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_testcase.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_testsuite.d.o' 'unit-threaded@sha/.._subpackages_runner_source_unit_threaded_runner_traits.d.o' -L=--start-group -L-soname,libunit-threaded.so.0 -L=--end-group -L--allow-shlib-undefined -relocation-model=pic
/usr/lib/gcc/x86_64-linux-gnu/9/../../../x86_64-linux-gnu/Scrt1.o:function _start: error: undefined reference to 'main'
collect2: error: ld returned 1 exit status
Error: /usr/bin/gcc failed with status: 1
[33/35] Compiling D object 'gen-ut-main@exe/.._gen_source_unit_threaded_runtime_runtime.d.o'.
ninja: build stopped: subcommand failed.
```
Hi @russel and @dcbaker I got the same problem reported here #6086 . 
The generated linker flag is wrong at least with `ldc` compiler.
The statement `-L-soname,libunit-threaded.so.0 ` need to be replaced by `-soname libunit-threaded.so.0 `

Usualy I use `sed` once` meson` have generated the  `ninja` file to fix it
`sed 's/-L-soname,libunit-threaded.so.0/-soname libunit-threaded.so.0/g' <ninja file path>`
I tried manually running the link step with the -soname change but got:
```
Error: -soname can be used only when building a shared library
```
so I am assuming there is something else wrong as well.
@russel I use the meson statement shared_library instead of library

Example:

```meson
mir_core_soname = 'lib@0@.so.@1@'.format(meson.project_name(), meson.project_version()[0])
mir_core_lib = shared_library(meson.project_name(),
    mir_core_src,
    include_directories: mir_core_dir,
    link_args : ['-shared', '-soname ' + mir_core_soname ],
    install: true,
    version: meson.project_version(),
)

```

and from the ninja generated file I remove the statement `-L-soname...` using `sed`
Hummm… maybe I should change back to using `shared_library` instead of `library` for the Unit_Threaded build. After all the Meson build is only going to be used to build shared library on Linux – unless someone wants it to build on macOS and Windows, in which case they will probably want a dylib or dll.

(Of course this will just be a workaround, and not fix the highlighted problem.)
https://github.com/mesonbuild/meson/pull/6090 addresses the missing `-shared` flag. The last refactor seems to have affected how link arguments should be handled, but I can't wrap my head around how and where the translation should be done for correctness.
@russel you can use the meson flag `--default-library shared`  and still use the library statement
I made a patch for fix meson build on mir-core project, if that can help
 https://github.com/libmir/mir-core/blob/master/meson.build

@GoaLitiuM thanks for your meson patch
https://wiki.dlang.org/Using_LDC

shows that you should pass `-shared` when building shared library, and probably `-c` or `-static` when building static libraries with ldc2.

No wonder it is trying to link and find `_main` as ldc2 is trying to link as entire program here.


Resolving patch:
diff --git a/mesonbuild/compilers/d.py b/mesonbuild/compilers/d.py
--- a/mesonbuild/compilers/d.py
+++ b/mesonbuild/compilers/d.py
@@ -391,7 +391,10 @@ def get_crt_args(self, crt_val, buildtype):
     def get_soname_args(self, *args, **kwargs) -> typing.List[str]:
         # LDC and DMD actually do use a linker, but they proxy all of that with
         # their own arguments
-        return Compiler.get_soname_args(self, *args, **kwargs)
+        soargs = []
+        for arg in Compiler.get_soname_args(self, *args, **kwargs):
+            soargs.append('-L=' + arg)
+        return soargs
 
     def get_allow_undefined_link_args(self) -> typing.List[str]:
         args = []


Test patch: