Skip to content

koga: make ninja install produce a usable libclasp.pc - #1822

Open
dg1sbg wants to merge 2 commits into
clasp-developers:mainfrom
dg1sbg:fix/koga-pkgconfig-install
Open

koga: make ninja install produce a usable libclasp.pc#1822
dg1sbg wants to merge 2 commits into
clasp-developers:mainfrom
dg1sbg:fix/koga-pkgconfig-install

Conversation

@dg1sbg

@dg1sbg dg1sbg commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Two independent defects in koga's pkg-config handling, one commit each. Together they mean ninja install cannot complete on macOS, and that the pkg-config file it installs cannot link anything on any platform.

1. pkgconfig-path defaults into a SIP-protected directory

bin-path, lib-path, dylib-path and share-path all default under /usr/local, but pkgconfig-path defaults to /usr/lib/pkgconfig/. On macOS Catalina and later /usr/lib is on the sealed read-only system volume and is protected by System Integrity Protection, so ninja -C build install ends with:

[1/1] Installing libclasp.pc to /usr/lib/pkgconfig/libclasp.pc
FAILED: [code=71] /usr/lib/pkgconfig/libclasp.pc
install -C -m 644 libclasp.pc /usr/lib/pkgconfig/libclasp.pc
install: /usr/lib/pkgconfig/INS@na30mW: Operation not permitted
ninja: build stopped: subcommand failed.

sudo does not help — the EPERM is SIP, not ownership; root has no access to that directory either. So ninja install cannot run to completion out of the box on any recent Mac.

Changed the default to /usr/local/lib/pkgconfig/, which is in pkg-config's default pc_path on both Linux and macOS and is consistent with the other four install paths.

2. The installed libclasp.pc cannot link anything

print-prologue (:libclasp-pc) passed "" where write-pc expects the Libs prefix, producing Libs: -lclasp with no library search path:

$ clang++ t.cc -o t $(pkg-config --libs libclasp)
ld: library 'clasp' not found

The per-variant .pc (:libclasp-pc-variant) does pass the variant ldflags, so only the installed copy was affected.

-L alone is not sufficient on macOS. otool -D shows libclasp.dylib's install name is @rpath/libclasp.dylib, so a consumer that now links successfully still dies at startup:

dyld: Library not loaded: @rpath/libclasp.dylib
  Reason: no LC_RPATH's found

so the fix emits -Wl,-rpath, as well, and the flags work end to end.

dylib-path had no entry in *root-paths*:install-bin, :install-share and :install-lib are all present — so this adds :install-dylib alongside them. It deliberately uses the unresolved install path rather than the --package-path-resolved one, matching :install-share directly above: a staged package must still describe where the files will finally live.

One point you may want to push back on: putting -Wl,-rpath, in Libs: is the pragmatic fix. The alternative is to give the installed dylib an absolute -install_name so no rpath is needed at all, but that is a larger change and would diverge from the build tree, which relies on @rpath. Happy to split the rpath half out or rework it that way if you prefer.

Testing

macOS 15 / Apple Silicon, :build-mode :native, install prefix /opt/clasp.

Before: ninja install fails on the .pc; with the file installed by hand, pkg-config --libs libclasp yields -lclasp and linking fails with ld: library 'clasp' not found.

After:

$ pkg-config --libs libclasp
-L/opt/clasp/lib/ -Wl,-rpath,/opt/clasp/lib/ -lclasp -L.../gmp/lib -lgmpxx -lgmp -L.../fmt/lib -lfmt

$ clang++ t.cc -o t $(pkg-config --libs libclasp) && ./t && echo ok
ok

$ otool -l t | grep -A2 LC_RPATH
          cmd LC_RPATH
      cmdsize 32
         path /opt/clasp/lib/ (offset 12)

ninja -C build install then completes with exit 0, and a second run reports no work to do.

The new default was verified independently by removing the local config.sexp override and re-running ./koga: build.ninja then targets /usr/local/lib/pkgconfig/libclasp.pc.

Neither change triggers a rebuild. Regenerating with ./koga touches only build.ninja — koga's timestamp-preserving output streams leave config.h and the generated headers byte-identical, so ninja -n install afterwards shows a single pending edge.

dg1sbg added 2 commits August 3, 2026 12:34
Every other install path defaults under /usr/local, but pkgconfig-path
defaulted to /usr/lib/pkgconfig/.  On macOS Catalina and later that
directory sits on the sealed, read-only system volume and is protected
by System Integrity Protection, so `ninja install` fails with

  install: /usr/lib/pkgconfig/INS@na30mW: Operation not permitted

and sudo does not help -- root has no access there either.  This left
`ninja install` unable to run to completion on any recent Mac.

/usr/local/lib/pkgconfig/ is in pkg-config's default search path on
both Linux and macOS and matches bin-path, lib-path, dylib-path and
share-path, so a default install now lands entirely under one prefix.
The installed pkg-config file passed an empty string as its Libs
prefix, so it read

  Libs:  -lclasp

with no library search path, and anything built against it failed at
link time with `ld: library 'clasp' not found`.  The per-variant .pc
written into the build tree does pass the variant ldflags, so only the
installed copy was affected.

Adding -L alone is not enough on macOS: libclasp.dylib has an @rpath
install name, so a consumer that links successfully still dies at
startup with "Library not loaded: @rpath/libclasp.dylib ... no
LC_RPATH's found".  Emit -Wl,-rpath as well so the flags work end to
end.

dylib-path had no entry in *root-paths* -- :install-bin, :install-share
and :install-lib were all present -- so add :install-dylib alongside
them and use it here.  This deliberately uses the unresolved install
path rather than the --package-path-resolved one, matching
:install-share directly above: a staged package must still describe
where the files will finally live.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant