Skip to content

Rename CZlib system module to FTZlib to avoid Linux collisions#3

Open
penguinboi wants to merge 1 commit into
velocityzen:masterfrom
penguinboi:fix/czlib-module-collision
Open

Rename CZlib system module to FTZlib to avoid Linux collisions#3
penguinboi wants to merge 1 commit into
velocityzen:masterfrom
penguinboi:fix/czlib-module-collision

Conversation

@penguinboi
Copy link
Copy Markdown

Problem

FileType declares its zlib system library wrapper as a module named CZlib. That generic name collides with other Swift packages that wrap zlib the same way — most notably weichsel/ZIPFoundation, which also calls its module CZlib. When both packages end up in the same SwiftPM resolution graph, the Linux toolchain rejects the duplicate module declaration:

error: redefinition of module 'CZlib'
note: previously defined here

The macOS toolchain currently tolerates the collision, but Linux builds (including release Docker images for server-side Swift projects) fail outright. We hit this in jocosocial/swiftarr#548 when adopting FileType alongside an existing ZIPFoundation dependency.

Fix

Rename the system library module from CZlib to FTZlib (FileType-namespaced):

  • Sources/CZlib/Sources/FTZlib/ (modulemap + shim header)
  • module CZlib [system]module FTZlib [system] in the modulemap
  • import CZlibimport FTZlib in Sources/FileType/zip+office.swift
  • Target name + dependencies updated in Package.swift

Public API is untouched — only the internal system module name changes. Downstream packages don't see the rename.

Verification

  • swift build — clean (macOS 14, Swift 6.2)
  • swift test — all 27 tests pass
  • Verified end-to-end against jocosocial/swiftarr building inside the project's Docker image (Ubuntu Noble + Swift 6.2): the redefinition of module 'CZlib' error is gone, full release build succeeds in ~160s.

Rationale for the name

System library module names should be unique enough that they can't collide with other packages wrapping the same C library. FTZlib follows the convention of prefixing with the project initials (similar to how Apple uses NIO* for SwiftNIO's modules). Open to a different name if you'd prefer something else.

The system library module was previously named CZlib, the same name
ZIPFoundation uses for its zlib wrapper. When both packages are pulled
into the same SwiftPM resolution graph, the Linux toolchain rejects the
duplicate module declaration and aborts the build:

  error: redefinition of module 'CZlib'

macOS's linker tolerates the collision, but Linux does not. Renaming to
FTZlib namespaces the module so it cannot collide with other packages
that wrap zlib.
penguinboi pushed a commit to penguinboi/swiftarr that referenced this pull request Apr 27, 2026
velocityzen/FileType ships a system library module called CZlib that
collides with ZIPFoundation's identically-named module. The macOS
toolchain accepts the duplicate, but Linux rejects it with
"redefinition of module 'CZlib'", which broke the release Docker
build for this PR.

Point the dependency at penguinboi/FileType, which renames the system
module to FTZlib. Verified via `docker build` against this repo's
Dockerfile: full release build now succeeds in ~160s. Upstream PR is
open at velocityzen/FileType#3; the fork pin will be removed once it
lands.
cohoe pushed a commit to jocosocial/swiftarr that referenced this pull request Apr 29, 2026
* feat: add Cvips system module with C shim functions for libvips

Cvips is a system library target wrapping libvips via pkg-config.
CvipsShim compiles typed C wrapper functions around variadic vips calls
that Swift cannot call directly.

* refactor: rename GDError to ImageError

* feat: add SwiftarrImage libvips wrapper with comprehensive tests

Implements the core image wrapper class backed by libvips, replacing
GDImage for image load/resize/crop/flatten/export operations.

Key changes:
- SwiftarrImage.swift: VipsImage-backed class with RAII cleanup
- SwiftarrImageTests.swift: 17 tests covering all operations
- shim.c: fix vips_image_new_from_buffer calling convention (returns
  VipsImage* directly, not via output param), add swiftarr_vips_copy
  shim, change thumbnail resize mode to VIPS_SIZE_FORCE
- Disable old GD-dependent files (Image.swift, Format.swift,
  ImageHandler.swift, ImageController.swift) with temporary stubs
  to keep the project building during migration

* infra: initialize libvips at app startup

* refactor: remove libgd, gdOverrides, and GD format machinery

* feat: rewrite ImageHandler and ImageController to use SwiftarrImage (libvips)

Replace all GDImage usage with SwiftarrImage in the image upload pipeline
and generated image endpoints (QR codes, identicons). All uploaded images
are now re-encoded as JPEG via libvips, eliminating format/orientation
tracking. Fix identicon color2 bug that used r1 instead of r2. Remove
dead `import gd` and gdSupportsFileType calls from configure.swift.

* infra: replace libgd with libvips in Docker build

* test: add HEIC/AVIF/JXL loading, pipeline, and identicon tests

12 new tests covering format support, loadImageFromData alpha
flattening, and identicon determinism/dimensions.

* fix: use VIPS_SIZE_BOTH for resize and add aspect ratio + pipeline tests

VIPS_SIZE_FORCE could distort aspect ratio. VIPS_SIZE_BOTH fits within
the bounding box preserving ratio, allowing both up and downscaling.
Also adds tests for aspect ratio preservation and the crop→resize
user profile pipeline.

* feat: preserve animated GIF/WebP uploads when allowAnimatedImages is true

Animated formats are detected via magic bytes and saved as original
data instead of re-encoding to JPEG. Thumbnails are always static JPEG.
Respects the existing Settings.shared.allowAnimatedImages toggle.

* feat: preserve original format for unmodified uploads

Matches original behavior: images that don't need resizing or cropping
are saved in their original format (PNG stays PNG, GIF stays GIF).
Only modified images (resized, cropped, profile avatars) are re-encoded
as JPEG. Animated GIF/WebP passthrough respects allowAnimatedImages.

* fix: always re-encode JPEGs to apply EXIF orientation

Original JPEG bytes carry the pre-autorot EXIF orientation tag.
Saving them as-is causes sideways display in viewers that ignore
EXIF. Matches original libgd behavior which always re-encoded JPEGs.
May fix #516.

* style: fix indentation to tabs, update comments from review

Convert SwiftarrImage.swift and tests to tab indentation matching
project convention. Update ABOUTME and doc comments to describe
current code rather than historical context.

* fix: re-encode non-web formats and use nearest-neighbor for pixel art

HEIC/AVIF/JXL/TIFF/BMP uploads are always re-encoded as JPEG since
browsers can't reliably display them. Prevents saving non-web bytes
with a .jpg extension.

QR codes and identicons now use nearest-neighbor interpolation for
crisp pixel-art scaling, matching the original libgd behavior.

* chore: remove .claude/settings.local.json from tracking

* test: add realistic image size tests (3000x2000, 4032x3024, profile crop)

* fix: match thumbnail extension to full image format

Thumbnails were saved as .jpg while passthrough images kept their
original extension (.png, .gif, .webp). The UI looks up thumbnails
using the full image's filename, causing broken links for non-JPEG
passthrough uploads.

* feat: add GIF and WebP export for correct thumbnail format

Thumbnails for GIF/WebP uploads are now exported in the matching
format instead of JPEG data with a mismatched extension. Ensures
the app and browser both get correct content types.

* feat: animated GIF/WebP thumbnails preserve all frames

Thumbnails for animated uploads now resize all frames using
vips_thumbnail_buffer with [n=-1], producing properly sized
animated thumbnails instead of static single-frame previews.

* fix: address review feedback — install docs and Sendable warnings

Update macOS and Linux install docs to reflect the libvips
migration (libvips-dev / vips-devel / brew install vips).

Move loadImageFromData, createThumbnail, isAnimatableFormat, and
detectExtension out of the APIRouteCollection extension and into
top-level functions. The static methods captured Self.Type when
called inside the threadPool isolated closure, producing
#SendableMetatypes warnings. Free functions have no Self capture.

* chore: address review nits — Fedora provider and remove ABOUTME headers

- Add `.yum(["vips-devel"])` to the Cvips system library providers
  for Fedora dev experience parity.
- Remove `// ABOUTME:` headers from new files. They were a personal
  convention rather than a project standard.

* refactor: detect image formats with FileType library

Replace hand-rolled magic-byte sniffing in isAnimatableFormat and
detectExtension with the velocityzen/FileType package. Same goes for
the inline JPEG header check in processImage. FileType matches against
a curated set of image signatures and lets us narrow detection to the
image MIME group, so non-image uploads no longer accidentally get a
plausible-looking extension.

Bumps swift-tools-version to 6.2 (FileType requires it; the project's
.swift-version is already pinned to 6.2.0).

* test: cover FileType-backed format detection helpers directly

Adds 11 tests for isAnimatableFormat and detectExtension. Each helper
is hit with real JPEG/PNG/GIF/WebP buffers (exported via SwiftarrImage)
plus a non-image (zip) and garbage payload to verify the .image
MIME-group filter rejects non-images cleanly.

* fix: pin FileType to fork that namespaces its zlib module

velocityzen/FileType ships a system library module called CZlib that
collides with ZIPFoundation's identically-named module. The macOS
toolchain accepts the duplicate, but Linux rejects it with
"redefinition of module 'CZlib'", which broke the release Docker
build for this PR.

Point the dependency at penguinboi/FileType, which renames the system
module to FTZlib. Verified via `docker build` against this repo's
Dockerfile: full release build now succeeds in ~160s. Upstream PR is
open at velocityzen/FileType#3; the fork pin will be removed once it
lands.

* ci: install vips for docs build

The Generate Documentation workflow runs `swift build` (via
sourcekitten) on a macOS runner to extract symbol info. It was still
brewing the old `gd` dependency, which silently worked while libgd was
in the source tree but fails now that the build resolves `pkg-config
--exists vips` instead.

* ci: build docs in a Linux Swift 6.2 container

The macOS runner ships Swift 6.1.2, but the package now requires
swift-tools-version 6.2 (FileType dependency), so `sourcekitten doc
--spm` fails before it can extract symbols. Run the docs build in the
swift:6.2-noble container instead. Matches the toolchain the main
build uses, and stays consistent automatically the next time the
project bumps Swift.

Side effects: install SourceKitten from source (no apt package), bump
actions/checkout from v2 to v4 (the v2 deprecation warning was already
in the build log), and switch the system dependency from brew to apt.

* Preserve alpha in PNG/GIF/WebP thumbnails

Previously loadImageFromData unconditionally flattened alpha onto white,
which made sense under libgd (always exported JPEG) but no longer fits
the per-format export pipeline. The full image preserved alpha by
passing original bytes through, while the thumbnail was generated from
the in-memory flattened copy, so transparent PNGs would lose alpha in
their thumbnails but keep it at full size.

Move the flatten to the two JPEG export sites instead:
- thumbnailData() flattens only when writing JPEG
- the modified-image full re-encode flattens before JPEG output

PNG/GIF/WebP thumbnails now keep their transparency, matching the full
image. Animated GIF/WebP thumbnails are unchanged (they bypass the
flattened in-memory copy via vips_animated_thumbnail).

Adds 4 new unit tests; updates the now-inverted alpha-flatten test.

---------

Co-authored-by: Skyler Lister Aley <slisteraley@curtail.com>
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.

2 participants