Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add start-group/end-group flags when linking a program with rustc #14026

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions mesonbuild/arglist.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ def _can_dedup(cls, arg: str) -> Dedup:
def _should_prepend(cls, arg: str) -> bool:
return arg.startswith(cls.prepend_prefixes)

def to_native_inplace(self) -> None:
pass

def to_native(self, copy: bool = False) -> T.List[str]:
# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
Expand All @@ -229,6 +232,7 @@ def to_native(self, copy: bool = False) -> T.List[str]:
new = self.copy()
else:
new = self
new.to_native_inplace()
return self.compiler.unix_args_to_native(new._container)

def append_direct(self, arg: str) -> None:
Expand Down
24 changes: 7 additions & 17 deletions mesonbuild/compilers/mixins/clike.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,26 +57,17 @@ class CLikeCompilerArgs(arglist.CompilerArgs):
dedup1_suffixes = ('.lib', '.dll', '.so', '.dylib', '.a')
dedup1_args = ('-c', '-S', '-E', '-pipe', '-pthread')

def to_native(self, copy: bool = False) -> T.List[str]:
def to_native_inplace(self) -> None:
# This seems to be allowed, but could never work?
assert isinstance(self.compiler, compilers.Compiler), 'How did you get here'

# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
# needed by static libraries that are provided by object files or
# shared libraries.
self.flush_pre_post()
if copy:
new = self.copy()
else:
new = self
# This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
# all act like (or are) gnu ld
# TODO: this could probably be added to the DynamicLinker instead
if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker)):
group_start = -1
group_end = -1
for i, each in enumerate(new):
for i, each in enumerate(self):
if not GROUP_FLAGS.search(each):
continue
group_end = i
Expand All @@ -86,29 +77,28 @@ def to_native(self, copy: bool = False) -> T.List[str]:
# Only add groups if there are multiple libraries.
if group_end > group_start >= 0:
# Last occurrence of a library
new.insert(group_end + 1, '-Wl,--end-group')
new.insert(group_start, '-Wl,--start-group')
self.insert(group_end + 1, '-Wl,--end-group')
self.insert(group_start, '-Wl,--start-group')
# Remove system/default include paths added with -isystem
default_dirs = self.compiler.get_default_include_dirs()
if default_dirs:
real_default_dirs = [self._cached_realpath(i) for i in default_dirs]
bad_idx_list: T.List[int] = []
for i, each in enumerate(new):
for i, each in enumerate(self):
if not each.startswith('-isystem'):
continue

# Remove the -isystem and the path if the path is a default path
if each == '-isystem':
if i < (len(new) - 1) and self._cached_realpath(new[i + 1]) in real_default_dirs:
if i < (len(self) - 1) and self._cached_realpath(self[i + 1]) in real_default_dirs:
bad_idx_list += [i, i + 1]
elif each.startswith('-isystem='):
if self._cached_realpath(each[9:]) in real_default_dirs:
bad_idx_list += [i]
elif self._cached_realpath(each[8:]) in real_default_dirs:
bad_idx_list += [i]
for i in reversed(bad_idx_list):
new.pop(i)
return self.compiler.unix_args_to_native(new._container)
self.pop(i)

@staticmethod
@functools.lru_cache(maxsize=None)
Expand Down
38 changes: 38 additions & 0 deletions mesonbuild/compilers/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import re
import typing as T

from .. import arglist
from .. import options
from ..linkers.linkers import GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GnuLikeDynamicLinkerMixin' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GnuLikeDynamicLinkerMixin occurs after the cyclic
import
of mesonbuild.compilers.rust.

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'SolarisDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of SolarisDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
'CompCertDynamicLinker' may not be defined if module
mesonbuild.linkers.linkers
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompCertDynamicLinker occurs after the cyclic
import
of mesonbuild.compilers.rust.
from ..mesonlib import EnvironmentException, MesonException, Popen_safe_logged
from ..options import OptionKey
from .compilers import Compiler, clike_debug_args
from .mixins.clike import GROUP_FLAGS

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.
'GROUP_FLAGS' may not be defined if module
mesonbuild.compilers.mixins.clike
is imported before module
mesonbuild.compilers.rust
, as the
definition
of GROUP_FLAGS occurs after the cyclic
import
of mesonbuild.compilers.rust.

if T.TYPE_CHECKING:
from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
Expand Down Expand Up @@ -62,6 +65,37 @@
except StopIteration:
return None


class RustcCompilerArgs(arglist.CompilerArgs):
Fixed Show fixed Hide fixed

Check failure

Code scanning / CodeQL

Module-level cyclic import Error

'CompilerArgs' may not be defined if module
mesonbuild.arglist
is imported before module
mesonbuild.compilers.rust
, as the
definition
of CompilerArgs occurs after the cyclic
import
of mesonbuild.compilers.rust.
def to_native_inplace(self) -> None:
assert isinstance(self.compiler, RustCompiler), 'How did you get here'

# Check if we need to add --start/end-group for circular dependencies
# between static libraries, and for recursively searching for symbols
# needed by static libraries that are provided by object files or
# shared libraries.
# This covers all ld.bfd, ld.gold, ld.gold, and xild on Linux, which
# all act like (or are) gnu ld
if isinstance(self.compiler.linker, (GnuLikeDynamicLinkerMixin, SolarisDynamicLinker, CompCertDynamicLinker)):
group_start = -1
group_end = -1
last_rustc_arg = -1
for i, each in enumerate(self):
if each in {'--emit', '-o', '--extern'}:
last_rustc_arg = i
if last_rustc_arg == i - 1 or not GROUP_FLAGS.search(each):
continue
group_end = i
if group_start < 0:
# First occurrence of a library
group_start = i
# Only add groups if there are multiple libraries.
if group_end > group_start >= 0:
# Last occurrence of a library
self.insert(group_end + 1, '-Clink-arg=-Wl,--end-group')
self.insert(group_start, '-Clink-arg=-Wl,--start-group')


class RustCompiler(Compiler):

# rustc doesn't invoke the compiler itself, it doesn't need a LINKER_PREFIX
Expand Down Expand Up @@ -100,6 +134,10 @@
self.base_options.add(OptionKey('b_vscrt'))
self.native_static_libs: T.List[str] = []

def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> RustcCompilerArgs:
# This is correct, mypy just doesn't understand co-operative inheritance
return RustcCompilerArgs(self, args)

def needs_static_linker(self) -> bool:
return False

Expand Down
11 changes: 11 additions & 0 deletions test cases/rust/27 staticlib group/lib1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"

void from_lib2(void) {
printf("hello world");
}

void c_func(void) {
from_lib1();
}
4 changes: 4 additions & 0 deletions test cases/rust/27 staticlib group/lib1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

void from_lib2(void);
void c_func(void);
8 changes: 8 additions & 0 deletions test cases/rust/27 staticlib group/lib2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include <stdio.h>
#include "lib1.h"
#include "lib2.h"

void from_lib1(void)
{
from_lib2();
}
3 changes: 3 additions & 0 deletions test cases/rust/27 staticlib group/lib2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void from_lib1(void);
6 changes: 6 additions & 0 deletions test cases/rust/27 staticlib group/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "lib1.h"
#include "lib2.h"

void main() {
c_func();
}
9 changes: 9 additions & 0 deletions test cases/rust/27 staticlib group/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extern "C" {
fn c_func();
}

fn main() {
unsafe {
c_func();
}
}
13 changes: 13 additions & 0 deletions test cases/rust/27 staticlib group/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
project('staticlib group', 'c', 'rust')

ld_bfd = find_program('ld.bfd')
if not ld_bfd.found()
error('MESON_SKIP_TEST ld.bfd not found')
endif

lib1 = static_library('lib1', 'lib1.c')
lib2 = static_library('lib2', 'lib2.c')
executable('lib1first', 'main.rs', link_with : [lib1, lib2],
rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd'])
executable('lib2first', 'main.rs', link_with : [lib2, lib1],
rust_args: ['-C', 'lto=n', '-C', 'linker-plugin-lto=n', '-Clink-arg=-fuse-ld=bfd'])
Loading