Background
Installing ruby-vips currently requires installing libvips separately on Linux and macOS:
# one of these
apt install libvips42
brew install vips
gem install ruby-vips
This complicates local setup, CI, containers, and deployment. It also allows the installed libvips version and features to vary between environments. In some cases Bundler succeeds, but require "vips" later fails because the shared library is missing.
Ideally, this would be sufficient on common platforms:
Windows already provides a similar experience through the gemspec's msys2_mingw_dependencies.
Why consider bundling libvips
Bundling could provide:
- A simpler and more conventional gem installation.
- Reproducible libvips versions and capabilities across development, CI, and production.
- No requirement for root access or an additional package manager.
- Fewer runtime failures caused by missing or incompatible shared libraries.
- Better isolation when different applications need different libvips versions.
Because ruby-vips uses FFI rather than a compiled Ruby extension, binary packages would primarily vary by operating system, architecture, and libc. They would not necessarily need to be rebuilt for every Ruby version.
Possible approaches (in no particular order)
1. Platform-specific ruby-vips gems
Publish platform variants containing libvips and its runtime dependencies, following the native-gem model used by projects such as Nokogiri.
Initial platforms could include:
x86_64-linux-gnu
aarch64-linux-gnu
x86_64-darwin
arm64-darwin
Musl variants could be added separately.
ruby-vips could load the bundled library by an explicit path. The generic ruby platform gem could continue using a system installation.
2. A separate libvips binary gem
Publish libvips in a companion gem, with ruby-vips depending on the appropriate platform variant.
This would separate the bindings from binary packaging and could potentially be reused by other Ruby projects. The cost would be additional dependency and version-resolution complexity.
3. Download a pinned binary during installation
An installation extension could download a platform-specific archive, verify its checksum, and install it within the gem directory.
This would keep the gem itself smaller, but introduces network access during installation, offline-installation problems, and another artifact hosting and integrity mechanism.
4. Build libvips during gem installation
The source archive could be bundled or downloaded and compiled during installation, similar to the source fallback offered by some native gems.
This would support more platforms, but libvips and its optional codec dependencies make compilation relatively slow and complex. It may be better as a fallback than the default.
Existing build infrastructure such as https://github.com/lovell/sharp-libvips may also provide useful prior art for producing relocatable Linux and macOS builds.
Questions for the maintainer
Whichever approach is selected, some policy decisions would be needed:
- Which platforms and minimum OS or glibc versions should be supported?
- Which image formats and optional dependencies should the bundled build include?
- How closely should ruby-vips releases track bundled libvips versions?
- Should bundled libvips take precedence over a system installation?
- How should users explicitly opt into a system-provided libvips?
- How should licenses, checksums, build provenance, and security updates be handled?
- What gem size is acceptable?
The platform-specific gem approach appears closest to established Ruby packaging practices, but this suggestion is intentionally open-ended.
The main goal is to make gem install ruby-vips sufficient on common Linux and macOS platforms while preserving a supported system-library option.
Examples from other gems
Approach 1: Platform-specific binary gems
nokogiri (source) ships precompiled Linux, macOS, and Windows gems containing its native dependencies. It retains source and system-library options.
sqlite3 (source) ships platform gems with SQLite statically linked. Its ruby platform gem compiles packaged SQLite source.
pg (source) ships platform gems containing libpq. Users can select the source gem to use a system installation.
grpc (source) publishes platform gems for Linux and macOS, plus a source gem.
google-protobuf (source) publishes platform gems containing its native UPB implementation.
sass-embedded (source) packages the Dart Sass executable in platform-specific gems.
commonmarker (source) publishes platform gems containing its Rust implementation, plus a source build.
Approach 2: Separate binary dependency gem
mini_racer (source) uses the companion libv8-node gem to distribute V8. libv8-node provides platform binaries and a source fallback.
Approach 3: Managed dependency download
selenium-webdriver (source) includes Selenium Manager, which detects and downloads the appropriate browser driver. This is not a shared-library example, but demonstrates automatic management of an external platform dependency.
Approach 4: Bundled source compiled during installation
rugged (source) builds its vendored libgit2 by default and supports --use-system-libraries.
ffi (source) builds its bundled libffi when the system version is missing or too old. Flags can force either version.
hiredis-client (source) builds and statically links its bundled hiredis source.
zstd-ruby (source) compiles its bundled Zstandard source.
brotli (source) compiles its bundled Brotli source.
sassc (source) historically bundled and compiled libsass. It is now end-of-life and has been replaced by sass-embedded.
Background
Installing
ruby-vipscurrently requires installing libvips separately on Linux and macOS:# one of these apt install libvips42 brew install vips gem install ruby-vipsThis complicates local setup, CI, containers, and deployment. It also allows the installed libvips version and features to vary between environments. In some cases Bundler succeeds, but
require "vips"later fails because the shared library is missing.Ideally, this would be sufficient on common platforms:
Windows already provides a similar experience through the gemspec's
msys2_mingw_dependencies.Why consider bundling libvips
Bundling could provide:
Because ruby-vips uses FFI rather than a compiled Ruby extension, binary packages would primarily vary by operating system, architecture, and libc. They would not necessarily need to be rebuilt for every Ruby version.
Possible approaches (in no particular order)
1. Platform-specific ruby-vips gems
Publish platform variants containing libvips and its runtime dependencies, following the native-gem model used by projects such as Nokogiri.
Initial platforms could include:
x86_64-linux-gnuaarch64-linux-gnux86_64-darwinarm64-darwinMusl variants could be added separately.
ruby-vips could load the bundled library by an explicit path. The generic
rubyplatform gem could continue using a system installation.2. A separate libvips binary gem
Publish libvips in a companion gem, with ruby-vips depending on the appropriate platform variant.
This would separate the bindings from binary packaging and could potentially be reused by other Ruby projects. The cost would be additional dependency and version-resolution complexity.
3. Download a pinned binary during installation
An installation extension could download a platform-specific archive, verify its checksum, and install it within the gem directory.
This would keep the gem itself smaller, but introduces network access during installation, offline-installation problems, and another artifact hosting and integrity mechanism.
4. Build libvips during gem installation
The source archive could be bundled or downloaded and compiled during installation, similar to the source fallback offered by some native gems.
This would support more platforms, but libvips and its optional codec dependencies make compilation relatively slow and complex. It may be better as a fallback than the default.
Existing build infrastructure such as https://github.com/lovell/sharp-libvips may also provide useful prior art for producing relocatable Linux and macOS builds.
Questions for the maintainer
Whichever approach is selected, some policy decisions would be needed:
The platform-specific gem approach appears closest to established Ruby packaging practices, but this suggestion is intentionally open-ended.
The main goal is to make
gem install ruby-vipssufficient on common Linux and macOS platforms while preserving a supported system-library option.Examples from other gems
Approach 1: Platform-specific binary gems
nokogiri(source) ships precompiled Linux, macOS, and Windows gems containing its native dependencies. It retains source and system-library options.sqlite3(source) ships platform gems with SQLite statically linked. Itsrubyplatform gem compiles packaged SQLite source.pg(source) ships platform gems containinglibpq. Users can select the source gem to use a system installation.grpc(source) publishes platform gems for Linux and macOS, plus a source gem.google-protobuf(source) publishes platform gems containing its native UPB implementation.sass-embedded(source) packages the Dart Sass executable in platform-specific gems.commonmarker(source) publishes platform gems containing its Rust implementation, plus a source build.Approach 2: Separate binary dependency gem
mini_racer(source) uses the companionlibv8-nodegem to distribute V8.libv8-nodeprovides platform binaries and a source fallback.Approach 3: Managed dependency download
selenium-webdriver(source) includes Selenium Manager, which detects and downloads the appropriate browser driver. This is not a shared-library example, but demonstrates automatic management of an external platform dependency.Approach 4: Bundled source compiled during installation
rugged(source) builds its vendoredlibgit2by default and supports--use-system-libraries.ffi(source) builds its bundledlibffiwhen the system version is missing or too old. Flags can force either version.hiredis-client(source) builds and statically links its bundledhiredissource.zstd-ruby(source) compiles its bundled Zstandard source.brotli(source) compiles its bundled Brotli source.sassc(source) historically bundled and compiledlibsass. It is now end-of-life and has been replaced bysass-embedded.