Skip to content

Commit 6eb2b47

Browse files
committed
chore(macos): enable out-of-the-box build and test on macOS
- add macOS prerequisites section to INSTALL.md (macFUSE cask, Homebrew deps) - remove manual PKG_CONFIG_PATH/CPPFLAGS instructions; Makefile now handles them - Makefile detects Darwin via uname, queries brew --prefix, and overrides PKG_CONFIG to carry a pinned icu4c path inline — immune to shell env injection (e.g. pkgx shadowing icu4c with the wrong version); exports PKG_CONFIG and CPPFLAGS so all sub-makes inherit them; libzip and macFUSE need no special handling as their .pc files are on the default pkg-config search path - add #include <boost/container_hash/hash.hpp> to lib/node.h; Apple Clang does not pull it in transitively, causing boost::hash_combine to be undefined - guard <sys/sysmacros.h> with #ifndef __APPLE__ in extra_field_test.cc; the header is Linux-only, makedev is available via <sys/types.h> on macOS - use umount -f on Darwin instead of -l (lazy) in blackbox test runner; macOS umount does not support the -l flag - update DEVELOPMENT.md with macOS portability notes and build workflow 🤖 Generated with [Claude Code](https://claude.ai/code) Signed-Off-By: Paal Øye-Strømme <paal.o.eye@gmail.com>
1 parent 625963a commit 6eb2b47

6 files changed

Lines changed: 102 additions & 27 deletions

File tree

DEVELOPMENT.md

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,36 +9,69 @@ This document provides project-specific context, conventions, and workflows for
99
## Build and Development
1010

1111
### Makefile Variables
12-
- `DEBUG=1`: Enable debug symbols and disable optimizations.
13-
- `ASAN=1`: Enable AddressSanitizer for memory safety checks.
12+
13+
| Variable | Default | Effect |
14+
| ---------------------- | ------- | ----------------------------------------------------------------------------- |
15+
| `DEBUG=1` | off | Enable debug symbols, disable optimisations, enable tree teardown on shutdown |
16+
| `ASAN=1` | off | Enable AddressSanitizer |
17+
| `UBSAN=1` | off | Enable UndefinedBehaviourSanitizer |
18+
| `FUSE_MAJOR_VERSION=2` | 3 | Build against FUSE 2 instead of FUSE 3 (Linux only) |
1419

1520
### Key Makefile Targets
16-
- `make all`: Build the `mount-zip` binary and the man page.
17-
- `make check-fast`: Run the fast subset of tests.
18-
- `make check`: Run the full test suite.
19-
- `make doc`: Regenerate the `mount-zip.1` man page from `README.md`.
20-
- `make clean`: Remove build artifacts.
21+
22+
| Target | Description |
23+
| ----------------- | ---------------------------------------------------- |
24+
| `make all` | Build the `mount-zip` binary and the man page |
25+
| `make check-fast` | Run the fast subset of tests |
26+
| `make check` | Run the full test suite (includes slow tests) |
27+
| `make doc` | Regenerate `mount-zip.1` from `README.md` via pandoc |
28+
| `make clean` | Remove all build artefacts |
29+
| `make debug` | Shorthand for `DEBUG=1 make all` |
30+
| `make valgrind` | Run tests under Valgrind (Linux only) |
31+
32+
### macOS Environment
33+
34+
The `Makefile` detects macOS at parse time via `uname -s`, queries the
35+
Homebrew prefix with `brew --prefix`, and overrides `PKG_CONFIG` to carry
36+
pinned search paths for macFUSE (`/usr/local`), ICU, and libzip. `CPPFLAGS`
37+
is extended with the Boost include directory. Both are exported so all
38+
sub-makes inherit them.
39+
40+
```sh
41+
make
42+
make check-fast
43+
```
2144

2245
## Documentation Pipeline
2346

2447
The `README.md` file serves as both the user guide and the source for the man page.
25-
- **Generation**: `pandoc` converts `README.md` to `roff` format.
26-
- **Formatting**: The `Makefile` uses `sed` post-processing on the `pandoc` output to ensure bulleted lists are rendered compactly (using `.PD 0`) in the man page.
27-
- **Markdown requirement**: Bulleted lists in `README.md` should be preceded by a blank line for correct `pandoc` parsing.
48+
49+
* **Generation**: `pandoc` converts `README.md` to `roff` format.
50+
* **Formatting**: The `Makefile` uses `sed` post-processing on the `pandoc` output to ensure bulleted lists are rendered compactly (using `.PD 0`) in the man page.
51+
* **Markdown requirement**: Bulleted lists in `README.md` should be preceded by a blank line for correct `pandoc` parsing.
2852

2953
## Technical Standards
3054

3155
### Memory Safety
56+
3257
The project aims for full **ASAN compliance**. Always verify changes with `ASAN=1 make check-fast`.
3358

3459
### Resource Management (RAII)
35-
- Use RAII guards for resource cleanup (e.g., `ScopedFile`, `Cleanup`).
36-
- **Shutdown Performance**: Global teardown of the virtual tree is wrapped in `#ifndef NDEBUG`. It is only performed in debug builds to keep production shutdown nearly instant.
60+
61+
* Use RAII guards for resource cleanup (e.g., `ScopedFile`, `Cleanup`).
62+
* **Shutdown Performance**: Global teardown of the virtual tree is wrapped in `#ifndef NDEBUG`. It is only performed in debug builds to keep production shutdown nearly instant.
3763

3864
### Portability
39-
- The project is 32-bit compatible.
40-
- **Year 2038**: Always build with `-D_TIME_BITS=64` (handled in `Makefile`) to ensure correct timestamp handling on 32-bit systems.
65+
66+
* The project is 32-bit compatible.
67+
* **Year 2038**: Always build with `-D_TIME_BITS=64` (handled in `Makefile`) to ensure correct timestamp handling on 32-bit systems.
68+
* **macOS**: The source guards Apple-specific differences with `#ifdef __APPLE__`. Notable divergences:
69+
* No `memfd_create()``lib/reader.cc` uses a temp-file fallback.
70+
* `typeof` must be undefined around the `fuse.h` include to satisfy `-pedantic`.
71+
* `boost::hash_combine` requires an explicit `#include <boost/container_hash/hash.hpp>` because Apple Clang does not pull it in transitively.
4172

4273
## Testing
43-
- **Main Runner**: `tests/blackbox/test.py`
44-
- **Whitebox Tests**: C++ unit tests in `tests/whitebox/` using the [GoogleTest](https://github.com/google/googletest) framework.
74+
75+
* **Main Runner**: `tests/blackbox/test.py`
76+
* **Whitebox Tests**: C++ unit tests in `tests/whitebox/` using the [GoogleTest](https://github.com/google/googletest) framework.
77+
* **Unmounting**: The blackbox test runner detects the host OS and uses `umount -f` on macOS and `umount -l` on Linux.

INSTALL.md

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ To build **mount-zip**, you need the following libraries:
99
* [libfuse >= 3.1](https://github.com/libfuse/libfuse)
1010
* [libzip >= 1.9.1](https://libzip.org)
1111

12-
On Debian systems, you can get these libraries by installing the following
13-
packages:
12+
### Debian / Ubuntu
1413

1514
```sh
1615
$ sudo apt install libboost-container-dev libicu-dev libfuse3-dev libzip-dev
@@ -32,8 +31,6 @@ To build **mount-zip**, you also need the following tools:
3231
* [GoogleTest](https://github.com/google/googletest) (for unit tests)
3332
* [Pandoc](https://pandoc.org) to generate the man page
3433

35-
On Debian systems, you can get these tools by installing the following packages:
36-
3734
```sh
3835
$ sudo apt install g++ pkg-config make libgtest-dev pandoc
3936
```
@@ -43,12 +40,29 @@ To test **mount-zip**, you also need the following tools:
4340
* `umount`
4441
* [Python >= 3.8](https://www.python.org)
4542

46-
On Debian systems, you can get these tools by installing the following packages:
47-
4843
```sh
4944
$ sudo apt install mount python3
5045
```
5146

47+
### macOS
48+
49+
macOS requires [macFUSE](https://osxfuse.github.io) instead of libfuse. Install
50+
it from the official disk image (it installs a kernel extension and requires
51+
approval in **System Settings → Privacy & Security**):
52+
53+
```sh
54+
$ brew install --cask macfuse
55+
```
56+
57+
Then install the remaining dependencies via [Homebrew](https://brew.sh):
58+
59+
```sh
60+
$ brew install boost icu4c libzip googletest pandoc
61+
```
62+
63+
The `Makefile` detects macOS automatically and locates all Homebrew-installed
64+
libraries without any extra environment variables.
65+
5266
## Build **mount-zip**
5367

5468
```sh
@@ -61,7 +75,7 @@ $ make
6175
$ DEBUG=1 make
6276
```
6377

64-
### With FUSE 2
78+
### With FUSE 2 (Linux only)
6579

6680
```sh
6781
$ FUSE_MAJOR_VERSION=2 make
@@ -81,6 +95,10 @@ $ make check
8195
$ make check-fast
8296
```
8397

98+
> [!NOTE]
99+
> On macOS the test runner requires `python3` (from Xcode CLT or Homebrew) and
100+
> uses `umount -f` automatically instead of the Linux-only `umount -l`.
101+
84102
## Install **mount-zip**
85103

86104
```sh

Makefile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,23 @@ PROJECT_CXXFLAGS += -DFUSE_USE_VERSION=26
4040
endif
4141

4242
DEPS += libzip icu-uc icu-i18n
43+
44+
# On macOS, icu4c is keg-only (Homebrew does not symlink it into the default
45+
# search path because macOS ships its own libicucore). Override PKG_CONFIG to
46+
# carry the pinned icu4c path inline so every pkg-config call in this make and
47+
# all sub-makes resolves the correct version regardless of what the shell
48+
# environment (e.g. pkgx) may have injected.
49+
50+
ifeq ($(shell uname -s),Darwin)
51+
BREW_PREFIX := $(shell brew --prefix 2>/dev/null)
52+
ifneq ($(BREW_PREFIX),)
53+
PKG_CONFIG = env PKG_CONFIG_PATH="$(BREW_PREFIX)/opt/icu4c/lib/pkgconfig" pkg-config
54+
CPPFLAGS += -I$(BREW_PREFIX)/opt/boost/include
55+
export PKG_CONFIG
56+
export CPPFLAGS
57+
endif
58+
endif
59+
4360
PROJECT_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags $(DEPS))
4461
PROJECT_LDFLAGS = -L$(OUT) -lmountzip $(shell $(PKG_CONFIG) --libs $(DEPS))
4562

lib/node.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <unistd.h>
3131
#include <zip.h>
3232

33+
#include <boost/container_hash/hash.hpp>
3334
#include <boost/intrusive/slist.hpp>
3435
#include <boost/intrusive/unordered_set.hpp>
3536

tests/blackbox/test.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import hashlib
1919
import logging
2020
import os
21+
import platform
2122
import pprint
2223
import random
2324
import stat
@@ -26,6 +27,9 @@
2627
import tempfile
2728
import time
2829

30+
# macOS umount lacks -l (lazy); use -f (force) instead.
31+
_UMOUNT_FLAG = '-f' if platform.system() == 'Darwin' else '-l'
32+
2933
sys.setrecursionlimit(3000)
3034

3135
# Computes the MD5 hash of the given file.
@@ -153,7 +157,7 @@ def MountZipAndGetTree(zip_names, options=[], password='', use_md5=True):
153157
return GetTree(mount_point, use_md5=use_md5), os.statvfs(mount_point)
154158
finally:
155159
logging.debug(f'Unmounting {zip_paths!r} from {mount_point!r}...')
156-
subprocess.run(['umount', '-l', mount_point], check=True)
160+
subprocess.run(['umount', _UMOUNT_FLAG, mount_point], check=True)
157161
logging.debug(f'Unmounted {zip_paths!r} from {mount_point!r}')
158162

159163

@@ -1909,7 +1913,7 @@ def TestBigZip(options=[]):
19091913
os.close(fd)
19101914
finally:
19111915
logging.debug(f'Unmounting {zip_path!r} from {mount_point!r}...')
1912-
subprocess.run(['umount', '-l', mount_point], check=True)
1916+
subprocess.run(['umount', _UMOUNT_FLAG, mount_point], check=True)
19131917
logging.debug(f'Unmounted {zip_path!r} from {mount_point!r}')
19141918

19151919

@@ -1950,7 +1954,7 @@ def TestManyNodes():
19501954

19511955
finally:
19521956
logging.debug(f'Unmounting {zip_path!r} from {mount_point!r}...')
1953-
subprocess.run(['umount', '-l', mount_point], check=True)
1957+
subprocess.run(['umount', _UMOUNT_FLAG, mount_point], check=True)
19541958
logging.debug(f'Unmounted {zip_path!r} from {mount_point!r}')
19551959

19561960

@@ -1994,7 +1998,7 @@ def TestBigZipNoCache(options=['-o', 'nocache']):
19941998
os.close(fd)
19951999
finally:
19962000
logging.debug(f'Unmounting {zip_path!r} from {mount_point!r}...')
1997-
subprocess.run(['umount', '-l', mount_point], check=True)
2001+
subprocess.run(['umount', _UMOUNT_FLAG, mount_point], check=True)
19982002
logging.debug(f'Unmounted {zip_path!r} from {mount_point!r}')
19992003

20002004

tests/whitebox/extra_field_test.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include <gtest/gtest.h>
2121

2222
#include <sys/stat.h>
23+
#ifndef __APPLE__
2324
#include <sys/sysmacros.h>
25+
#endif
2426
#include <cstdint>
2527

2628
namespace {

0 commit comments

Comments
 (0)