diff --git a/ebuild/build/ninja_backend.py b/ebuild/build/ninja_backend.py index 5aa2f37..a3434d9 100644 --- a/ebuild/build/ninja_backend.py +++ b/ebuild/build/ninja_backend.py @@ -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: @@ -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) @@ -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: diff --git a/tests/ebuild/test_ninja_backend.py b/tests/ebuild/test_ninja_backend.py index cca5812..5d06fb3 100644 --- a/tests/ebuild/test_ninja_backend.py +++ b/tests/ebuild/test_ninja_backend.py @@ -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 diff --git a/tests/unit/test_ninja_backend.py b/tests/unit/test_ninja_backend.py index e973ca0..ff68f09 100644 --- a/tests/unit/test_ninja_backend.py +++ b/tests/unit/test_ninja_backend.py @@ -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(