[MacPorts] #74309: macfuse: new PortGroup for FUSE3 support, avoid misresolving fuse.h to FUSE2 API
MacPorts
noreply at macports.org
Mon Aug 10 05:22:46 UTC 2026
#74309: macfuse: new PortGroup for FUSE3 support, avoid misresolving fuse.h to
FUSE2 API
------------------------------------------------+------------------------
Reporter: trodemaster | Owner: mohd-akram
Type: enhancement | Status: assigned
Priority: Normal | Milestone:
Component: ports | Version:
Keywords: fuse fuse3 macfuse meson portgroup | Port: macfuse
------------------------------------------------+------------------------
Hey, I have been trying to sort out some issues with ports that depend on
macfuse and bumped into this issue. Here is one option I generated to
address the issue with assistance from Claude. If this seems viable, I can
refine it into a PR, and if not, I can just stick to per port workaround.
Seems like it would be nice to make a break from the pre-macfuse versions
of fuse and move on from there.
== Problem ==
macFUSE ships two userspace API generations side by side under the same
MacPorts prefix: a legacy FUSE 2.x-compatible `fuse.h` (a shim, per its
own header comment: `"This header is for compatibility with older
software using FUSE. Please use 'pkg-config --cflags fuse' to set
include path."`) and the modern FUSE 3.x API at `fuse3/fuse.h`. Both are
genuinely part of upstream macFUSE's own package layout, per the project
wiki: *"macFUSE ships with patched versions of libfuse 2 and
libfuse 3."* [https://github.com/macfuse/macfuse/wiki/FUSE-User-Space-
Libraries]
MacPorts' `macfuse` port installs both into the single shared
`${prefix}/include`. Because MacPorts base unconditionally adds
`-I${prefix}/include` to every build (so any port's headers are
findable without per-dependency wiring), a consumer that writes a bare
`#include <fuse.h>`, instead of the fully-qualified `#include
<fuse3/fuse.h>`, silently resolves to the **FUSE 2.x** struct layout
even when it links against `libfuse3` and intends to use the FUSE 3
API. This is not usually a build failure at all. It's a silent ABI
mismatch: function pointers get assigned with the old 2.x signatures
(e.g. `chmod(path, mode)` instead of `chmod(path, mode, fuse_file_info
*)`), which the compiler correctly flags as
`-Wincompatible-function-pointer-types`, but which several ports
currently silence rather than fix. This is specific to
MacPorts' shared-prefix convention; it doesn't reproduce on Homebrew or
a native `/usr/local` install, since neither of those blanket-inject the
parent include dir the way MacPorts does.
This is easy to miss because the *actual* build failure, when it does
surface, points nowhere near the real cause. It shows up as e.g.:
{{{
In file included from fuse2fs.c:29:
In file included from /opt/local/include/fuse.h:9:
In file included from /opt/local/include/fuse/fuse.h:31:
/opt/local/include/fuse/fuse_common.h:52:2: error: Please add
-D_FILE_OFFSET_BITS=64 to your compile flags!
}}}
== Root cause in the existing `fuse` PortGroup ==
`_resources/port1.0/group/fuse-1.0.tcl`'s modern branch (macfuse4 /
macfuse, i.e. `os.major >= 16`) still targets the **FUSE 2.x**
pkg-config identity and include directory, not FUSE 3:
{{{
} else {
set fuse_port "macfuse"
set fuse_path "lib/pkgconfig/fuse.pc"
if {${os.platform} eq "darwin" && ${os.major} < 21} {
set fuse_port "macfuse4"
}
configure.cflags-append -I${prefix}/include/fuse
configure.cppflags-append -I${prefix}/include/fuse
}
}}}
`fuse.pc` and `include/fuse` are the FUSE 2.x pkg-config file and
include dir. The group was written for osxfuse (FUSE2-only), and this
branch was never updated when macfuse4/macfuse (FUSE3-capable) support
was added; it still wires consumers to the legacy API surface even
though modern macfuse's headline capability is FUSE3.
== Proposed fix: a new `macfuse` PortGroup ==
Rather than rev the existing `fuse` group in place, I'd propose a new,
separate group. `fuse-1.0` is generically named for a real reason: it
genuinely dispatches between three vendored implementations (osxfuse,
macfuse4, macfuse) depending on macOS version. A FUSE3-only group
wouldn't do any of that dispatching. No other FUSE3-capable
implementation is currently packaged in MacPorts (fuse-t exists as a
project but isn't in the tree), so it would just be a fixed dependency
on the `macfuse` port. Naming it for what it actually depends on,
rather than the API version (which explains why the group exists, not
what it targets), better reflects its current scope, and matches the
precedent of narrowly-scoped, literally-named groups like
`qt5_variables`/`qt5_version_info`. It's also not a good fit for a
`fuse-1.1` bump: FUSE2 and FUSE3 are different, non-drop-in-compatible
APIs with a different consumer contract (the `#include` path itself),
so treating this as an additive/backward-compatible extension of
`fuse-1.0` (the way `legacysupport-1.1` is a strict superset of
`legacysupport-1.0`) would be misleading.
Sketch:
{{{
#!tcl
# Provides FUSE 3.x (libfuse3 API) support via macFUSE only. No
# osxfuse, no macfuse4; those predate FUSE3 support in macFUSE and
# require the legacy 2.x struct layout, which this group does not
# target. Requires macOS 12 (Monterey, darwin 21)+, matching macfuse's
# own platform floor.
#
# Usage:
#
# PortGroup macfuse 1.0
#
# IMPORTANT: consumer source must #include <fuse3/fuse.h> explicitly,
# never bare #include <fuse.h>. See macfuse's own compatibility shim
# note in include/fuse.h. This group cannot force header resolution
# order for meson-based ports (environment CPPFLAGS are applied ahead
# of per-dependency cflags in meson's own compile invocations), so it
# can only catch the mistake, not silently correct it.
if {${os.platform} eq "darwin" && ${os.major} < 21} {
pre-fetch {
return -code error "macfuse PortGroup requires macOS 12 (Monterey)
or later (darwin >= 21); this system is darwin ${os.major}"
}
}
depends_build-append path:bin/pkg-config:pkgconfig
depends_lib-append path:lib/pkgconfig/fuse3.pc:macfuse
pre-configure {
set offenders [split [exec find ${worksrcpath} -type f \
\( -name *.c -o -name *.h -o -name *.cpp -o -name *.cc \) \
-exec grep -lE {^#include <fuse\.h>} {} +] "\n"]
if {[llength ${offenders}] > 0} {
return -code error "macfuse PortGroup: bare '#include <fuse.h>'
found in:\n[join ${offenders} \n]\n\nThis resolves to macFUSE's FUSE 2.x
compatibility header, not fuse3/fuse.h, and will misbuild against
libfuse3. Patch these includes to <fuse3/fuse.h>."
}
}
}}}
Notes on the sketch:
* It's deliberately narrow: macfuse only, macOS 12+. Ports needing
osxfuse/macfuse4 for older macOS keep using `fuse-1.0` unchanged;
nothing about this proposal touches that group or its consumers.
* It can't magically fix header resolution order for every build
system (confirmed empirically for meson: environment CPPFLAGS beat
per-dependency cflags in the actual compile invocation, so no
`configure.cflags` ordering trick reliably wins). Its value is
turning a silent ABI mismatch into a build-time error with an
actionable message, at the one place (`pre-configure`, after patches
are applied) where the real include is knowable.
* Existing consumers (`sshfs`, `bindfs`) could migrate to it and drop
their `-Wno-error=incompatible-function-pointer-types` workarounds
once actually fixed at the source level. Happy to help with those
patches once the group exists, since I'd guess they're silently
building the wrong ABI today too.
I'm happy to submit this as a PR against `macports-ports` if the
approach sounds right. Wanted to raise it here first since it touches
shared infrastructure (a new PortGroup) rather than a single port.
--
Ticket URL: <https://trac.macports.org/ticket/74309>
MacPorts <https://www.macports.org/>
Ports system for macOS
More information about the macports-tickets
mailing list