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 "objects" support to Rust targets #14031

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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 docs/markdown/snippets/rust-objects.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
== `objects` added correctly to Rust executables ==

Any objects included in a Rust executable were previously ignored. They
are now added correctly.
10 changes: 10 additions & 0 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,16 @@ def relpath(todir: str, fromdir: str) -> str:
return os.path.relpath(os.path.join('dummyprefixdir', todir),
os.path.join('dummyprefixdir', fromdir))

def get_compiler_order_deps(self, target: build.BuildTarget, compiler: Compiler) -> T.List[str]:
return [
os.path.join(self.get_target_dir(lt), lt.get_filename())
for lt in compiler.get_extra_order_deps(target)
]

def get_fortran_order_deps(self, deps: T.List[build.BuildTarget]) -> T.List[File]:
return [File(True, *os.path.split(self.get_target_filename(t))) for t in deps
if t.uses_fortran()]

def flatten_object_list(self, target: build.BuildTarget, proj_dir_to_build_root: str = ''
) -> T.Tuple[T.List[str], T.List[build.BuildTargetTypes]]:
obj_list, deps = self._flatten_object_list(target, target.get_objects(), proj_dir_to_build_root)
Expand Down
103 changes: 58 additions & 45 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,14 +1025,13 @@ def generate_target(self, target) -> None:
pch_objects = []

o, od = self.flatten_object_list(target)
obj_targets = [t for t in od if t.uses_fortran()]
obj_list.extend(o)

fortran_order_deps = [File(True, *os.path.split(self.get_target_filename(t))) for t in obj_targets]
fortran_order_deps = self.get_fortran_order_deps(od)
fortran_inc_args: T.List[str] = []
if target.uses_fortran():
fortran_inc_args = mesonlib.listify([target.compilers['fortran'].get_include_args(
self.get_target_private_dir(t), is_system=False) for t in obj_targets])
self.get_target_private_dir(t), is_system=False) for t in od if t.uses_fortran()])

# Generate compilation targets for sources generated by transpilers.
#
Expand Down Expand Up @@ -1928,25 +1927,12 @@ def _get_rust_dependency_name(self, target: build.BuildTarget, dependency: LibTy
# in Rust
return target.rust_dependency_map.get(dependency.name, dependency.name).replace('-', '_')

def generate_rust_target(self, target: build.BuildTarget) -> None:
rustc = target.compilers['rust']
def generate_rust_sources(self, target: build.BuildTarget) -> T.Tuple[T.List[str], str]:
orderdeps: T.List[str] = []

# Rust compiler takes only the main file as input and
# figures out what other files are needed via import
# statements and magic.
base_proxy = target.get_options()
args = rustc.compiler_args()
# Compiler args for compiling this target
args += compilers.get_base_compile_args(base_proxy, rustc, self.environment)
self.generate_generator_list_rules(target)

# dependencies need to cause a relink, they're not just for ordering
deps: T.List[str] = []

# Dependencies for rust-project.json
project_deps: T.List[RustDep] = []

orderdeps: T.List[str] = []

main_rust_file = None
if target.structured_sources:
if target.structured_sources.needs_copy():
Expand Down Expand Up @@ -1976,41 +1962,47 @@ def generate_rust_target(self, target: build.BuildTarget) -> None:
orderdeps.extend(_ods)

for i in target.get_sources():
if not rustc.can_compile(i):
raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')
if main_rust_file is None:
main_rust_file = i.rel_to_builddir(self.build_to_src)
for g in target.get_generated_sources():
for i in g.get_outputs():
if not rustc.can_compile(i):
raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')
if isinstance(g, GeneratedList):
fname = os.path.join(self.get_target_private_dir(target), i)
else:
fname = os.path.join(g.get_subdir(), i)
if main_rust_file is None:
main_rust_file = fname
orderdeps.append(fname)
if main_rust_file is None:
raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report')

return orderdeps, main_rust_file

def get_rust_compiler_args(self, target: build.BuildTarget, rustc: Compiler,
depfile: T.Optional[str] = None) -> T.List[str]:
base_proxy = target.get_options()
# Compiler args for compiling this target
args = compilers.get_base_compile_args(base_proxy, rustc, self.environment)

target_name = self.get_target_filename(target)
args.extend(['--crate-type', target.rust_crate_type])

# If we're dynamically linking, add those arguments
#
# Rust is super annoying, calling -C link-arg foo does not work, it has
# to be -C link-arg=foo
if target.rust_crate_type in {'bin', 'dylib'}:
args.extend(rustc.get_linker_always_args())

args += self.generate_basic_compiler_args(target, rustc)
# Rustc replaces - with _. spaces or dots are not allowed, so we replace them with underscores
args += ['--crate-name', target.name.replace('-', '_').replace(' ', '_').replace('.', '_')]
depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d')
args += rustc.get_dependency_gen_args(target_name, depfile)
if depfile:
args += rustc.get_dependency_gen_args(target_name, depfile)
args += rustc.get_output_args(target_name)
args += ['-C', 'metadata=' + target.get_id()]
args += target.get_extra_args('rust')
return args

def get_rust_compiler_deps_and_args(self, target: build.BuildTarget, rustc: Compiler) -> T.Tuple[T.List[str], T.List[str], T.List[RustDep], T.List[str]]:
deps: T.List[str] = []
project_deps: T.List[RustDep] = []
args: T.List[str] = []

# Rustc always use non-debug Windows runtime. Inject the one selected
# by Meson options instead.
Expand Down Expand Up @@ -2039,6 +2031,11 @@ def _link_library(libname: str, static: bool, bundle: bool = False):
type_ += ':' + ','.join(modifiers)
args.append(f'-l{type_}={libname}')

objs, od = self.flatten_object_list(target)
for o in objs:
args.append(f'-Clink-arg={o}')
fortran_order_deps = self.get_fortran_order_deps(od)

linkdirs = mesonlib.OrderedSet()
external_deps = target.external_deps.copy()
target_deps = target.get_dependencies()
Expand Down Expand Up @@ -2124,6 +2121,33 @@ def _link_library(libname: str, static: bool, bundle: bool = False):
if isinstance(target, build.SharedLibrary) or has_shared_deps:
args += self.get_build_rpath_args(target, rustc)

return deps, fortran_order_deps, project_deps, args

def generate_rust_target(self, target: build.BuildTarget) -> None:
rustc = target.compilers['rust']
self.generate_generator_list_rules(target)

for i in target.get_sources():
if not rustc.can_compile(i):
raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')
for g in target.get_generated_sources():
for i in g.get_outputs():
if not rustc.can_compile(i):
raise InvalidArguments(f'Rust target {target.get_basename()} contains a non-rust source file.')

orderdeps, main_rust_file = self.generate_rust_sources(target)
target_name = self.get_target_filename(target)
if main_rust_file is None:
raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report')

args = rustc.compiler_args()

depfile = os.path.join(self.get_target_private_dir(target), target.name + '.d')
args += self.get_rust_compiler_args(target, rustc, depfile)

deps, fortran_order_deps, project_deps, deps_args = self.get_rust_compiler_deps_and_args(target, rustc)
args += deps_args

proc_macro_dylib_path = None
if target.rust_crate_type == 'proc-macro':
proc_macro_dylib_path = self.get_target_filename_abs(target)
Expand All @@ -2139,7 +2163,10 @@ def _link_library(libname: str, static: bool, bundle: bool = False):
element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file)
if orderdeps:
element.add_orderdep(orderdeps)
if fortran_order_deps:
element.add_orderdep(fortran_order_deps)
if deps:
# dependencies need to cause a relink, they're not just for ordering
element.add_dep(deps)
element.add_item('ARGS', args)
element.add_item('targetdep', depfile)
Expand Down Expand Up @@ -3104,7 +3131,7 @@ def generate_single_compile(self, target: build.BuildTarget, src,
d = os.path.join(self.get_target_private_dir(target), d)
element.add_orderdep(d)
element.add_dep(pch_dep)
for i in self.get_fortran_orderdeps(target, compiler):
for i in self.get_compiler_order_deps(target, compiler):
element.add_orderdep(i)
if dep_file:
element.add_item('DEPFILE', dep_file)
Expand Down Expand Up @@ -3169,20 +3196,6 @@ def has_dir_part(self, fname: FileOrString) -> bool:
return fname.subdir != ''
return has_path_sep(fname)

# Fortran is a bit weird (again). When you link against a library, just compiling a source file
# requires the mod files that are output when single files are built. To do this right we would need to
# scan all inputs and write out explicit deps for each file. That is too slow and too much effort so
# instead just have an ordered dependency on the library. This ensures all required mod files are created.
# The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
# produce incorrect dep files but such is life.
def get_fortran_orderdeps(self, target, compiler):
if compiler.language != 'fortran':
return []
return [
os.path.join(self.get_target_dir(lt), lt.get_filename())
for lt in itertools.chain(target.link_targets, target.link_whole_targets)
]

def generate_msvc_pch_command(self, target, compiler, pch):
header = pch[0]
pchname = compiler.get_pch_name(header)
Expand Down
5 changes: 4 additions & 1 deletion mesonbuild/compilers/compilers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

if T.TYPE_CHECKING:
from .. import coredata
from ..build import BuildTarget, DFeatures
from ..build import BuildTarget, BuildTargetTypes, DFeatures

Check failure

Code scanning / CodeQL

Module-level cyclic import

'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](4) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](5) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](6) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](7) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](8) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](9) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](10) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](11) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](12) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](13) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](14) of mesonbuild.compilers.compilers. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](15) of mesonbuild.compilers.compilers.

Check failure

Code scanning / CodeQL

Module-level cyclic import

'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](4) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](5) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](6) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](7) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](8) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](9) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](10) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](11) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](12) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](13) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](14) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](15) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of BuildTargetTypes occurs after the cyclic [import](4) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of BuildTargetTypes occurs after the cyclic [import](5) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of BuildTargetTypes occurs after the cyclic [import](6) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of BuildTargetTypes occurs after the cyclic [import](7) of mesonbuild.compilers.compilers. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), a

Check failure

Code scanning / CodeQL

Module-level cyclic import

'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](4) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](5) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](6) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](7) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](8) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](9) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](10) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](11) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](12) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](13) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](14) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](3) of DFeatures occurs after the cyclic [import](15) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of DFeatures occurs after the cyclic [import](4) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of DFeatures occurs after the cyclic [import](5) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of DFeatures occurs after the cyclic [import](6) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of DFeatures occurs after the cyclic [import](7) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2), as the [definition](16) of DFeatures occurs after the cyclic [import](8) of mesonbuild.compilers.compilers. 'DFeatures' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.compilers](2)
from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
from ..envconfig import MachineInfo
from ..environment import Environment
Expand Down Expand Up @@ -1223,6 +1223,9 @@ def get_module_outdir_args(self, path: str) -> T.List[str]:
def module_name_to_filename(self, module_name: str) -> str:
raise EnvironmentException(f'{self.id} does not implement module_name_to_filename')

def get_extra_order_deps(self, target: BuildTarget) -> T.Iterable[BuildTargetTypes]:
return []

def get_compiler_check_args(self, mode: CompileCheckMode) -> T.List[str]:
"""Arguments to pass the compiler and/or linker for checks.

Expand Down
11 changes: 11 additions & 0 deletions mesonbuild/compilers/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import typing as T
import functools
import itertools
import os

from .. import options
Expand All @@ -27,6 +28,7 @@
)

if T.TYPE_CHECKING:
from ..build import BuildTarget, BuildTargetTypes

Check failure

Code scanning / CodeQL

Module-level cyclic import

'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](4) of mesonbuild.compilers.fortran. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](5) of mesonbuild.compilers.fortran. 'BuildTarget' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTarget occurs after the cyclic [import](6) of mesonbuild.compilers.fortran.

Check failure

Code scanning / CodeQL

Module-level cyclic import

'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](4) of mesonbuild.compilers.fortran. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](5) of mesonbuild.compilers.fortran. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](3) of BuildTargetTypes occurs after the cyclic [import](6) of mesonbuild.compilers.fortran. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](7) of BuildTargetTypes occurs after the cyclic [import](4) of mesonbuild.compilers.fortran. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](7) of BuildTargetTypes occurs after the cyclic [import](5) of mesonbuild.compilers.fortran. 'BuildTargetTypes' may not be defined if module [mesonbuild.build](1) is imported before module [mesonbuild.compilers.fortran](2), as the [definition](7) of BuildTargetTypes occurs after the cyclic [import](6) of mesonbuild.compilers.fortran.
from ..coredata import MutableKeyedOptionDictType, KeyedOptionDictType
from ..dependencies import Dependency
from ..envconfig import MachineInfo
Expand Down Expand Up @@ -80,6 +82,15 @@ def get_module_incdir_args(self) -> T.Tuple[str, ...]:
def get_module_outdir_args(self, path: str) -> T.List[str]:
return ['-module', path]

# Fortran is a bit weird (again). When you link against a library, just compiling a source file
# requires the mod files that are output when single files are built. To do this right we would need to
# scan all inputs and write out explicit deps for each file. That is too slow and too much effort so
# instead just have an ordered dependency on the library. This ensures all required mod files are created.
# The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
# produce incorrect dep files but such is life.
def get_extra_order_deps(self, target: BuildTarget) -> T.Iterable[BuildTargetTypes]:
return itertools.chain(target.link_targets, target.link_whole_targets)

def compute_parameters_with_absolute_paths(self, parameter_list: T.List[str],
build_dir: str) -> T.List[str]:
for idx, i in enumerate(parameter_list):
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/compilers/rust.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def get_colorout_args(self, colortype: str) -> T.List[str]:

def get_linker_always_args(self) -> T.List[str]:
args: T.List[str] = []
# Rust is super annoying, calling -C link-arg foo does not work, it has
# to be -C link-arg=foo
for a in super().get_linker_always_args():
args.extend(['-C', f'link-arg={a}'])
return args
Expand Down
2 changes: 2 additions & 0 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3514,6 +3514,8 @@ def build_target(self, node: mparser.BaseNode, args: T.Tuple[str, SourcesVarargs

target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs,
self.environment, self.compilers[for_machine], kwargs)
if objs and target.uses_rust():
FeatureNew.single_use('objects in Rust targets', '1.8.0', self.subproject)

self.add_target(name, target)
self.project_args_frozen = True
Expand Down
15 changes: 15 additions & 0 deletions test cases/rust/27 objects/lib1-dylib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
extern "C" {
fn from_lib1();
}

#[no_mangle]
extern "C" fn from_lib2()
{
println!("hello world");
}

#[no_mangle]
pub extern "C" fn c_func()
{
unsafe { from_lib1(); }
}
11 changes: 11 additions & 0 deletions test cases/rust/27 objects/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 objects/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 objects/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 objects/lib2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#pragma once

void from_lib1(void);
9 changes: 9 additions & 0 deletions test cases/rust/27 objects/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();
}
}
20 changes: 20 additions & 0 deletions test cases/rust/27 objects/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
project('staticlib group', 'c', 'rust', meson_version: '>=1.7.0')

lib1 = static_library('lib1', 'lib1.c')
lib2 = static_library('lib2', 'lib2.c')
executable('lib1objs', 'main.rs',
objects: lib1.extract_all_objects(recursive: false),
link_with: lib2)
executable('lib2objs', 'main.rs',
objects: lib2.extract_all_objects(recursive: false),
link_with: lib1)
executable('lib1objs_as_dep', 'main.rs',
dependencies: declare_dependency(objects: lib1.extract_all_objects(recursive: false)),
link_with: lib2)
executable('lib2objs_as_dep', 'main.rs',
dependencies: declare_dependency(objects: lib2.extract_all_objects(recursive: false)),
link_with: lib1)

lib12 = shared_library('lib12', 'lib1-dylib.rs', objects: lib2.extract_all_objects(recursive: false),
rust_abi: 'c')
executable('lib12', 'main.rs', link_with: lib12)
Loading