From a0e034a9e933b7a813c14cfdfb5ea533b4a9ac18 Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Wed, 6 Jan 2016 15:50:36 +0000 Subject: [PATCH] Add support for pre-compiled libvips on ARM CPUs Uses a HypriotOS-managed docker container for this --- README.md | 4 +- binding.js | 11 ++-- docs/api.md | 4 +- docs/changelog.md | 4 ++ docs/install.md | 5 +- packaging/arm-build.sh | 34 ++++++++++ packaging/arm-test.sh | 28 ++++++++ packaging/arm/Dockerfile | 5 ++ packaging/arm/build.sh | 135 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 219 insertions(+), 11 deletions(-) create mode 100755 packaging/arm-build.sh create mode 100755 packaging/arm-test.sh create mode 100644 packaging/arm/Dockerfile create mode 100755 packaging/arm/build.sh diff --git a/README.md b/README.md index f73757e2..3f9aaf06 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Bicubic interpolation with Lanczos anti-alias filtering ensures quality is not s As well as image resizing, operations such as rotation, extraction, compositing and gamma correction are available. -64-bit Windows and recent Linux systems do not require +Most Windows (x64), Linux and ARMv6+ systems do not require the installation of any external runtime dependencies. Use with OS X is as simple as running `brew install homebrew/science/vips` @@ -36,7 +36,7 @@ covers reporting bugs, requesting features and submitting code changes. ### Licence -Copyright 2013, 2014, 2015 Lovell Fuller and contributors. +Copyright 2013, 2014, 2015, 2016 Lovell Fuller and contributors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/binding.js b/binding.js index fc843595..fb97c642 100644 --- a/binding.js +++ b/binding.js @@ -54,9 +54,9 @@ var error = function(msg) { module.exports.download_vips = function() { // Has vips been installed locally? if (!isFile(vipsHeaderPath)) { - // Ensure 64-bit - if (process.arch !== 'x64') { - error('ARM and 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/'); + // Ensure Intel 64-bit or ARM + if (process.arch === 'ia32') { + error('Intel Architecture 32-bit systems require manual installation - please see http://sharp.dimens.io/en/stable/install/'); } // Ensure libc >= 2.15 var lddVersion = process.env.LDD_VERSION; @@ -66,8 +66,9 @@ module.exports.download_vips = function() { error('libc version ' + libcVersion + ' requires manual installation - please see http://sharp.dimens.io/en/stable/install/'); } } - // Platform-specific .tar.gz - var tarFilename = ['libvips', process.env.npm_package_config_libvips, process.platform.substr(0, 3)].join('-') + '.tar.gz'; + // Arch/platform-specific .tar.gz + var platform = (process.arch === 'arm') ? 'arm' : process.platform.substr(0, 3); + var tarFilename = ['libvips', process.env.npm_package_config_libvips, platform].join('-') + '.tar.gz'; var tarPath = path.join(__dirname, 'packaging', tarFilename); if (isFile(tarPath)) { unpack(tarPath); diff --git a/docs/api.md b/docs/api.md index dcfbf178..294ebdee 100644 --- a/docs/api.md +++ b/docs/api.md @@ -635,7 +635,7 @@ var counters = sharp.counters(); // { queue: 2, process: 4 } _Requires libvips to have been compiled with liborc support_ Improves the performance of `resize`, `blur` and `sharpen` operations -by taking advantage of the SIMD vector unit of the CPU. +by taking advantage of the SIMD vector unit of the CPU, e.g. Intel SSE and ARM NEON. * `enable`, if present, is a boolean where `true` enables and `false` disables the use of SIMD. @@ -650,7 +650,7 @@ have been known to crash under heavy load. ```javascript var simd = sharp.simd(); -// simd is `true` is SIMD is currently enabled +// simd is `true` if SIMD is currently enabled ``` ```javascript diff --git a/docs/changelog.md b/docs/changelog.md index 7488ea90..86069b98 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -4,6 +4,10 @@ #### v0.12.2 - TBD +* Upgrade libvips to v8.2.0 for improved vips_shrink. + +* Add pre-compiled libvips for ARMv6+ CPUs. + * Ensure 16-bit input images work with embed option. [#325](https://github.com/lovell/sharp/issues/325) [@janaz](https://github.com/janaz) diff --git a/docs/install.md b/docs/install.md index c30bb9e4..ce723df0 100644 --- a/docs/install.md +++ b/docs/install.md @@ -17,7 +17,7 @@ npm install sharp libvips and its dependencies are fetched and stored within `node_modules/sharp` during `npm install`. This involves an automated HTTPS download of approximately 6MB. -Most recent 64-bit Linux-based operating systems should "just work", e.g.: +Most recent Linux-based operating systems running on x64 and ARMv6+ CPUs should "just work", e.g.: * Debian 7, 8 * Ubuntu 12.04, 14.04, 14.10, 15.04, 15.10 @@ -25,12 +25,13 @@ Most recent 64-bit Linux-based operating systems should "just work", e.g.: * Fedora 21, 22, 23 * openSUSE 13.2 * Archlinux 2015.06.01 +* Raspbian Jessie Preference will be given to an existing globally-installed (via `pkg-config`) version of libvips that meets the minimum version requirement. This allows the use of newer versions of libvips with older versions of sharp. -For older and 32-bit Linux-based operating systems, +For older Linux-based operating systems and 32-bit Intel CPUs, a system-wide installation of the most suitable version of libvips and its dependencies can be achieved by running the following command as a user with `sudo` access diff --git a/packaging/arm-build.sh b/packaging/arm-build.sh new file mode 100755 index 00000000..dcb57d23 --- /dev/null +++ b/packaging/arm-build.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then + echo "Usage: $0 IP" + echo "Build libvips for ARM using Docker, where IP is" + echo "the address of a Raspberry Pi running HypriotOS" + exit 1 +fi +IP="$1" + +echo "Verifying connectivity to $IP" +if ! ping -c 1 $IP; then + echo "Could not connect to $IP" + exit 1 +fi + +if ! type sshpass >/dev/null; then + echo "Please install sshpass" + exit 1 +fi + +export SSHPASS=hypriot + +echo "Copying arm/Dockerfile and arm/build.sh to device" +sshpass -e scp -o PreferredAuthentications=password -r arm root@${IP}:/root + +echo "Building Raspbian-based container" +sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker build -t vips-dev-arm arm" + +echo "Running arm/build.sh within container" +sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/arm:/arm vips-dev-arm sh -c 'cd /arm && ./build.sh' | tee arm/build.log" + +echo "Copying resultant tar.gz file from device" +sshpass -e scp -o PreferredAuthentications=password root@${IP}:/root/arm/*.tar.gz . diff --git a/packaging/arm-test.sh b/packaging/arm-test.sh new file mode 100755 index 00000000..37c83bb5 --- /dev/null +++ b/packaging/arm-test.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +if [ $# -lt 1 ]; then + echo "Usage: $0 IP" + echo "Test sharp on ARM using Docker, where IP is" + echo "the address of a Raspberry Pi running HypriotOS" + exit 1 +fi +IP="$1" + +echo "Verifying connectivity to $IP" +if ! ping -c 1 $IP; then + echo "Could not connect to $IP" + exit 1 +fi + +if ! type sshpass >/dev/null; then + echo "Please install sshpass" + exit 1 +fi + +export SSHPASS=hypriot + +echo "Copying sharp source to device" +sshpass -e scp -o PreferredAuthentications=password -r ../../sharp root@${IP}:/root/sharp + +echo "Compile and test within container" +sshpass -e ssh -o PreferredAuthentications=password -t root@${IP} "docker run -i -t --rm -v \${PWD}/sharp:/s hypriot/rpi-node:5 sh -c 'cd /s && npm install --unsafe-perm && npm test'" diff --git a/packaging/arm/Dockerfile b/packaging/arm/Dockerfile new file mode 100644 index 00000000..3a329875 --- /dev/null +++ b/packaging/arm/Dockerfile @@ -0,0 +1,5 @@ +FROM resin/rpi-raspbian:jessie +MAINTAINER Lovell Fuller + +# Build dependencies +RUN apt-get update && apt-get install -y build-essential autoconf libtool nasm gtk-doc-tools texinfo curl diff --git a/packaging/arm/build.sh b/packaging/arm/build.sh new file mode 100755 index 00000000..eeacf3ca --- /dev/null +++ b/packaging/arm/build.sh @@ -0,0 +1,135 @@ +#!/bin/sh + +# To be run inside a Raspbian container + +# Working directories +DEPS=/deps +TARGET=/target +mkdir ${DEPS} +mkdir ${TARGET} + +# Common build paths and flags +export PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${TARGET}/lib/pkgconfig" +export PATH="${PATH}:${TARGET}/bin" +export CPPFLAGS="-I${TARGET}/include" +export LDFLAGS="-L${TARGET}/lib" + +# Dependency version numbers +VERSION_ZLIB=1.2.8 +VERSION_FFI=3.2.1 +VERSION_GLIB=2.46.2 +VERSION_XML2=2.9.3 +VERSION_GSF=1.14.34 +VERSION_EXIF=0.6.21 +VERSION_JPEG=1.4.2 +VERSION_PNG16=1.6.20 +VERSION_LCMS2=2.7 +VERSION_WEBP=0.5.0 +VERSION_TIFF=4.0.6 +VERSION_MAGICK=6.9.2-10 +VERSION_ORC=0.4.24 +VERSION_VIPS=8.2.0 + +mkdir ${DEPS}/zlib +curl -Ls http://zlib.net/zlib-${VERSION_ZLIB}.tar.xz | tar xJC ${DEPS}/zlib --strip-components=1 +cd ${DEPS}/zlib +./configure --prefix=${TARGET} && make install +rm ${TARGET}/lib/libz.a + +mkdir ${DEPS}/ffi +curl -Ls ftp://sourceware.org/pub/libffi/libffi-${VERSION_FFI}.tar.gz | tar xzC ${DEPS}/ffi --strip-components=1 +cd ${DEPS}/ffi +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --disable-builddir && make install-strip + +mkdir ${DEPS}/glib +curl -Ls http://ftp.gnome.org/pub/gnome/sources/glib/2.46/glib-${VERSION_GLIB}.tar.xz | tar xJC ${DEPS}/glib --strip-components=1 +cd ${DEPS}/glib +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/xml2 +curl -Ls http://xmlsoft.org/sources/libxml2-${VERSION_XML2}.tar.gz | tar xzC ${DEPS}/xml2 --strip-components=1 +cd ${DEPS}/xml2 +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-python --with-zlib=${TARGET} && make install-strip + +mkdir ${DEPS}/gsf +curl -Ls http://ftp.gnome.org/pub/GNOME/sources/libgsf/1.14/libgsf-${VERSION_GSF}.tar.xz | tar xJC ${DEPS}/gsf --strip-components=1 +cd ${DEPS}/gsf +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/exif +curl -Ls http://heanet.dl.sourceforge.net/project/libexif/libexif/${VERSION_EXIF}/libexif-${VERSION_EXIF}.tar.bz2 | tar xjC ${DEPS}/exif --strip-components=1 +cd ${DEPS}/exif +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/jpeg +curl -Ls http://heanet.dl.sourceforge.net/project/libjpeg-turbo/${VERSION_JPEG}/libjpeg-turbo-${VERSION_JPEG}.tar.gz | tar xzC ${DEPS}/jpeg --strip-components=1 +cd ${DEPS}/jpeg +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --with-jpeg8 --without-turbojpeg && make install-strip + +mkdir ${DEPS}/png16 +curl -Ls http://heanet.dl.sourceforge.net/project/libpng/libpng16/${VERSION_PNG16}/libpng-${VERSION_PNG16}.tar.xz | tar xJC ${DEPS}/png16 --strip-components=1 +cd ${DEPS}/png16 +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/lcms2 +curl -Ls http://heanet.dl.sourceforge.net/project/lcms/lcms/${VERSION_LCMS2}/lcms2-${VERSION_LCMS2}.tar.gz | tar xzC ${DEPS}/lcms2 --strip-components=1 +cd ${DEPS}/lcms2 +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/webp +curl -Ls http://downloads.webmproject.org/releases/webp/libwebp-${VERSION_WEBP}.tar.gz | tar xzC ${DEPS}/webp --strip-components=1 +cd ${DEPS}/webp +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/tiff +curl -Ls http://download.osgeo.org/libtiff/tiff-${VERSION_TIFF}.tar.gz /deps/tiff.tar.gz | tar xzC ${DEPS}/tiff --strip-components=1 +cd ${DEPS}/tiff +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip +rm ${TARGET}/lib/libtiffxx* + +mkdir ${DEPS}/magick +curl -Ls http://www.imagemagick.org/download/releases/ImageMagick-${VERSION_MAGICK}.tar.xz | tar xJC ${DEPS}/magick --strip-components=1 +cd ${DEPS}/magick +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking --without-magick-plus-plus && make install-strip + +mkdir ${DEPS}/orc +curl -Ls http://gstreamer.freedesktop.org/data/src/orc/orc-${VERSION_ORC}.tar.xz | tar xJC ${DEPS}/orc --strip-components=1 +cd ${DEPS}/orc +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking && make install-strip + +mkdir ${DEPS}/vips +curl -Ls http://www.vips.ecs.soton.ac.uk/supported/8.2/vips-${VERSION_VIPS}.tar.gz | tar xzC ${DEPS}/vips --strip-components=1 +cd ${DEPS}/vips +./configure --prefix=${TARGET} --enable-shared --disable-static --disable-dependency-tracking \ + --disable-debug --disable-introspection --without-python --without-fftw \ + --with-zip-includes=${TARGET}/include --with-zip-libraries=${TARGET}/lib \ + --with-jpeg-includes=${TARGET}/include --with-jpeg-libraries=${TARGET}/lib \ + && make install-strip + +# Remove the C++ bindings +cd ${TARGET}/include +rm -rf vips/vipsc++.h vips/vipscpp.h vips/V*.h +cd ${TARGET}/lib +rm -rf pkgconfig .libs *.la libvipsCC* libvips-cpp.* + +# Create JSON file of version numbers +cd ${TARGET} +echo "{\n\ + \"zlib\": \"${VERSION_ZLIB}\",\n\ + \"ffi\": \"${VERSION_FFI}\",\n\ + \"glib\": \"${VERSION_GLIB}\",\n\ + \"xml\": \"${VERSION_XML2}\",\n\ + \"gsf\": \"${VERSION_GSF}\",\n\ + \"exif\": \"${VERSION_EXIF}\",\n\ + \"jpeg\": \"${VERSION_JPEG}\",\n\ + \"png\": \"${VERSION_PNG16}\",\n\ + \"lcms\": \"${VERSION_LCMS2}\",\n\ + \"webp\": \"${VERSION_WEBP}\",\n\ + \"tiff\": \"${VERSION_TIFF}\",\n\ + \"magick\": \"${VERSION_MAGICK}\",\n\ + \"orc\": \"${VERSION_ORC}\",\n\ + \"vips\": \"${VERSION_VIPS}\"\n\ +}" >lib/versions.json + +# Create .tar.gz +GZIP=-9 tar czf /arm/libvips-${VERSION_VIPS}-arm.tar.gz include lib