Skip to content
Closed
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
26 changes: 17 additions & 9 deletions ebuild/build/ninja_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,25 @@ def _write_ninja(self) -> None:
" command = $cc $ldflags $in -o $out $libs",
" description = LINK $out",
"",
"rule link_shared",
" command = $cc -shared $ldflags $in -o $out $libs",
" description = LINK_SHARED $out",
"",
"rule ar_rule",
" command = $ar rcs $out $in",
" description = AR $out",
"",
]

# The flag that turns a link into a shared object is spelled
# differently on Darwin. Emit the rule only when something needs it,
# so a purely static project's build.ninja does not carry a rule for
# a link it never performs.
shared_flag = "-dynamiclib" if sys.platform == "darwin" else "-shared"
if any(t.target_type == "shared_library" for t in self.config.targets):
lines += [
"rule link_shared",
f" command = $cc {shared_flag} $ldflags $in -o $out $libs",
" description = LINK_SHARED $out",
"",
]

toolchain_ldflags = self._get_toolchain_ldflags()

for target in self.config.targets:
Expand Down Expand Up @@ -190,11 +199,10 @@ def _write_ninja(self) -> None:
if target.target_type == "static_library":
lines.append(f"build {out}: ar_rule {' '.join(obj_files)}")
else:
# Shared libraries need the platform's "build a shared
# object" flag and the same -L/-l wiring executables get,
# neither of which the generic `link` rule provides.
# link_shared carries the platform's "build a shared
# object" flag; the -L/-l wiring is the same as for
# executables and still goes through ldflags/libs.
ldflags = list(target.ldflags)
ldflags.insert(0, "-dynamiclib" if sys.platform == "darwin" else "-shared")
libs = []
for pkg_name in target.uses:
pkg = self.package_paths.get(pkg_name)
Expand All @@ -204,7 +212,7 @@ def _write_ninja(self) -> None:
for lib in pkg.libraries:
libs.append(f"-l{lib}")

lines.append(f"build {out}: link {' '.join(obj_files)}")
lines.append(f"build {out}: link_shared {' '.join(obj_files)}")
if ldflags:
lines.append(f" ldflags = {' '.join(ldflags)}")
if libs:
Expand Down
4 changes: 3 additions & 1 deletion tests/ebuild/test_ninja_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def test_shared_library_uses_shared_link_rule(tmp_path):
NinjaBackend(config, tmp_path / "build", toolchain).generate()

ninja_file = (tmp_path / "build" / "build.ninja").read_text(encoding="utf-8")
assert "rule link_shared\n command = $cc -shared" in ninja_file
# Darwin spells the flag -dynamiclib; the CI matrix covers macos-13.
shared_flag = "-dynamiclib" if sys.platform == "darwin" else "-shared"
assert f"rule link_shared\n command = $cc {shared_flag}" in ninja_file
assert "build " in ninja_file
assert ": link_shared " in ninja_file

Expand Down
5 changes: 3 additions & 2 deletions tests/unit/test_ninja_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ def test_shared_library_gets_shared_flag(self):

shared_flag = "-dynamiclib" if sys.platform == "darwin" else "-shared"
self.assertIn(shared_flag, ninja)
# It must use the `link` rule (compiler driver), not `ar_rule`.
# It must go through a compiler-driver link rule, not `ar_rule`.
lib_line = next(line for line in ninja.splitlines() if "libmylib" in line and line.startswith("build"))
self.assertIn(": link ", lib_line)
self.assertNotIn(": ar_rule", lib_line)
self.assertIn(": link_shared ", lib_line)

def test_shared_library_gets_lib_dirs_and_libs(self):
target = TargetConfig(
Expand Down