diff --git a/docker/manylinux2014/.gitignore b/docker/manylinux2014/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..dfcc51221bb1d3f2c1114ac25c3578c62e44976f
--- /dev/null
+++ b/docker/manylinux2014/.gitignore
@@ -0,0 +1,2 @@
+dist
+wheelhouse
diff --git a/docker/manylinux2014/Dockerfile b/docker/manylinux2014/Dockerfile
index 112d1415a9dbb414b25b831d8579c8eda9ff7361..d8d0dbc446ce9b2ec30b5b2965e6acec545bf518 100644
--- a/docker/manylinux2014/Dockerfile
+++ b/docker/manylinux2014/Dockerfile
@@ -1,3 +1,9 @@
+FROM memmos.laas.fr:5000/gepetto/buildfarm/robotpkg:centos7 as build
+
+ADD config/robotpkg /
+RUN while read pkg ; do make -C $pkg install; done < /robotpkg; \
+    rm -rf $(find . -name work.$HOSTNAME)
+
 FROM quay.io/pypa/manylinux2014_x86_64:latest
 
 ENV PLAT manylinux2014_x86_64
@@ -9,6 +15,12 @@ RUN ./install_boost.sh
 COPY build/setup_python.sh ./
 RUN ./setup_python.sh
 
+ADD config/packages /
+RUN yum install -y $(cat /packages)
+
 WORKDIR /src
 
+ADD scripts /scripts
 CMD ["/bin/bash"]
+
+COPY --from=build /opt/openrobots /opt/openrobots
diff --git a/docker/manylinux2014/README.md b/docker/manylinux2014/README.md
index d56ed1497cb8a45824fb1afa57f3bbc60d5ea18e..4020d530f430c261d58d2b77ed75c698483fc3f4 100644
--- a/docker/manylinux2014/README.md
+++ b/docker/manylinux2014/README.md
@@ -4,92 +4,39 @@ This is set of scripts to help build manylinux2014 wheels, the wheels are built
 python 2.7, 3.5, 3.6, 3.7 and 3.8.
 
 
-## Config
-
-In the `config/config` file, set the variables for your project.
-
-The `config/packages` file need to be edited with the packages needed to build the 
-wheels, they need to be specified one per line and will be installed with `yum`.
-
-You can also edit `scripts/pre_builds.sh` if needed, it will be run before building the wheels.
-
-
 ## Build the wheels
 
 To build the wheels run:
 ```
-docker build . -t manylinux
-docker run -it -v `pwd`:/io manylinux /io/scripts/setup.sh
-```
-Then you need to verify that the generated `setup.py` is as you want,
-and run `./build_wheels.sh`.
-
-When the build is finished, you can exit docker. All the wheels can be found in `wheelhouse/`.
-
-
-## Upload the wheels on PyPi
-```
-twine upload wheelhouse/*
+docker build -t manylinux .
+docker run -v (pwd -P):/io -it manylinux /scripts/setup.sh eigenpy
+docker run -v (pwd -P):/io -it manylinux /scripts/setup.sh hpp-fcl
+docker run -v (pwd -P):/io -it manylinux /scripts/setup.sh pinocchio
 ```
 
-## Examples
+## Test'em
 
-### eigenpy 2.4.3:
-
-`config/config`:
-```
-PACKAGE_NAME=eigenpy
-TARGET_NAME=eigenpy
-VERSION=2.4.3
-GIT_URL=https://github.com/stack-of-tasks/eigenpy.git
-INSTALL_REQUIRES='["numpy"]'
-NPROC=8
-```
-
-`config/packages`:
 ```
-eigen3-devel
+docker build -f test.Dockerfile -t manylinux-test .
+docker run --rm -it manylinux-test
 ```
 
-### hppfcl 1.5.1:
-`config/config`:
-```
-PACKAGE_NAME=hppfcl
-TARGET_NAME=hppfcl
-VERSION=1.5.1
-GIT_URL=https://github.com/humanoid-path-planner/hpp-fcl.git
-INSTALL_REQUIRES='["numpy"]'
-NPROC=8
-```
+## Upload the wheels on PyPi
 
-`config/packages`:
-```
-eigen3-devel
-assimp-devel
-poly2tri-devel
-minizip-devel
-irrXML-devel
-zlib-devel
 ```
-
-`script/pre_build.sh`:
+twine upload wheelhouse/*
 ```
-#!/bin/bash -eux
-
-source "/io/config/config"
 
-for PYBIN in /opt/python/*/bin; do
-  "$PYBIN"/pip install numpy
-  "$PYBIN"/pip install -i https://test.pypi.org/simple/ eigenpy-test==2.4.3
-done
+## TODO
 
-git clone -j"$NPROC" git://github.com/OctoMap/octomap.git
-cd octomap/
-mkdir build && cd build
-cmake ..
-make -j"$NPROC" install
-cd /src
-rm -rf octomap
-```
+- use TARGET instead of PACKAGE_NAME: hpp-fcl on PyPI
+- use robotpkg to build, instead of downloading the sources: could build all non-python stuff in the docker image
+- pyproject.toml in skbuild doc
+- lib/pythonX.Y/site-packages/foo.cpython\*.so links to libfoo.so and has RPATH: $ORIGIN/../../../foo.libs
+  foo.libs/libfoo.so RPATH: $ORIGIN:$ORIGIN/../bar.libs:$ORIGIN/../baz.libs
 
-To test the wheels make sure `libgl1` is installed.
+  ```cmake
+  set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
+  set(CMAKE_INSTALL_RPATH "\$ORIGIN;\$ORIGIN/../bar.libs;\$ORIGIN/../baz.libs")
+  set_target_properties(foo_pywrap POPERTIES INSTALL_RPATH "\$ORIGIN/../../../foo.libs")
+  ```
diff --git a/docker/manylinux2014/all.sh b/docker/manylinux2014/all.sh
new file mode 100755
index 0000000000000000000000000000000000000000..3ec2b36981a67bd08d76fbc38eaf363f7792d67c
--- /dev/null
+++ b/docker/manylinux2014/all.sh
@@ -0,0 +1,10 @@
+#!/bin/bash -eux
+
+sudo touch dist wheelhouse
+sudo rm -rf dist wheelhouse
+docker build -t manylinux .
+docker run -v "$(pwd -P):/io" -t manylinux /scripts/setup.sh eigenpy
+docker run -v "$(pwd -P):/io" -t manylinux /scripts/setup.sh hpp-fcl
+docker run -v "$(pwd -P):/io" -t manylinux /scripts/setup.sh pinocchio
+docker build -f test.Dockerfile -t manylinux-test .
+docker run --rm -t manylinux-test
diff --git a/docker/manylinux2014/config/config b/docker/manylinux2014/config/config
deleted file mode 100644
index 89169a04df881a1159509211958f366458955001..0000000000000000000000000000000000000000
--- a/docker/manylinux2014/config/config
+++ /dev/null
@@ -1,17 +0,0 @@
-# Name of the package
-PACKAGE_NAME=
-
-# Name of the package uploaded on PyPi
-TARGET_NAME=
-
-# Release version
-VERSION=
-
-# Git repo url
-GIT_URL=
-
-# List of python packages needed to build and install the wheel (ex: '["foo", "bar"]')
-INSTALL_REQUIRES=''
-
-# Maximum number of parallel jobs
-NPROC=
diff --git a/docker/manylinux2014/config/eigenpy/config b/docker/manylinux2014/config/eigenpy/config
new file mode 100644
index 0000000000000000000000000000000000000000..a1a51ce66eb0272b42c677411a38a817230e30ff
--- /dev/null
+++ b/docker/manylinux2014/config/eigenpy/config
@@ -0,0 +1,5 @@
+PACKAGE_NAME=eigenpy
+GITHUB_ORG=stack-of-tasks
+VERSION=2.4.3
+INSTALL_REQUIRES='["numpy"]'
+NPROC=8
diff --git a/docker/manylinux2014/config/eigenpy/test.py b/docker/manylinux2014/config/eigenpy/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..af64145debf3bba3220ad2a036520c9730bbef68
--- /dev/null
+++ b/docker/manylinux2014/config/eigenpy/test.py
@@ -0,0 +1,12 @@
+import unittest
+
+import eigenpy
+
+
+class TestEigenpy(unittest.TestCase):
+    def test_trivial(self):
+        self.assertLess(abs(eigenpy.Quaternion(1, 2, 3, 4).norm() - 5.47722557505), 1e-7)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/docker/manylinux2014/config/hpp-fcl/config b/docker/manylinux2014/config/hpp-fcl/config
new file mode 100644
index 0000000000000000000000000000000000000000..4866c2831a028ddf62baa401a6a6c3107915675a
--- /dev/null
+++ b/docker/manylinux2014/config/hpp-fcl/config
@@ -0,0 +1,5 @@
+PACKAGE_NAME=hppfcl
+GITHUB_ORG=humanoid-path-planner
+VERSION=1.5.1
+INSTALL_REQUIRES='["eigenpy"]'
+NPROC=8
diff --git a/docker/manylinux2014/config/hpp-fcl/pre_build.sh b/docker/manylinux2014/config/hpp-fcl/pre_build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cdf8ecc92e737e12ad7b15894e46e046e8e3a65c
--- /dev/null
+++ b/docker/manylinux2014/config/hpp-fcl/pre_build.sh
@@ -0,0 +1,9 @@
+#!/bin/bash -eux
+
+source "/io/config/hpp-fcl/config"
+
+for PYBIN in /opt/python/*/bin; do
+  CP_VERSION="cp$("$PYBIN"/python -c "import sys; print(''.join(sys.version.split('.')[:2]))")"
+  #"$PYBIN"/pip install numpy
+  "$PYBIN"/pip install /io/dist/eigenpy-*-"$CP_VERSION"-*.whl
+done
diff --git a/docker/manylinux2014/config/hpp-fcl/test.py b/docker/manylinux2014/config/hpp-fcl/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..23856d17358d0f607826fce49536e758e4d5da9e
--- /dev/null
+++ b/docker/manylinux2014/config/hpp-fcl/test.py
@@ -0,0 +1,12 @@
+import unittest
+
+import hppfcl
+
+
+class TestHPPFCL(unittest.TestCase):
+    def test_trivial(self):
+        self.assertLess(abs(hppfcl.Capsule(2, 3).computeVolume() - 71.2094334814), 1e-7)
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/docker/manylinux2014/config/packages b/docker/manylinux2014/config/packages
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3831f5bc5b92edc78fef0ca83273b044145bfefb 100644
--- a/docker/manylinux2014/config/packages
+++ b/docker/manylinux2014/config/packages
@@ -0,0 +1,6 @@
+assimp-devel
+eigen3-devel
+tinyxml
+tinyxml2
+tinyxml2-devel
+tinyxml-devel
diff --git a/docker/manylinux2014/config/pinocchio/config b/docker/manylinux2014/config/pinocchio/config
new file mode 100644
index 0000000000000000000000000000000000000000..0d2491b73b5c90d8dd479011bb33295f4bdb64f8
--- /dev/null
+++ b/docker/manylinux2014/config/pinocchio/config
@@ -0,0 +1,5 @@
+PACKAGE_NAME=pinocchio
+GITHUB_ORG=stack-of-tasks
+VERSION=2.4.7
+INSTALL_REQUIRES='["hppfcl"]'
+NPROC=8
diff --git a/docker/manylinux2014/config/pinocchio/pre_build.sh b/docker/manylinux2014/config/pinocchio/pre_build.sh
new file mode 100755
index 0000000000000000000000000000000000000000..279d0a1bc01705d9b177b12bcadee15605d77757
--- /dev/null
+++ b/docker/manylinux2014/config/pinocchio/pre_build.sh
@@ -0,0 +1,10 @@
+#!/bin/bash -eux
+
+source "/io/config/hpp-fcl/config"
+
+for PYBIN in /opt/python/*/bin; do
+  CP_VERSION="cp$("$PYBIN"/python -c "import sys; print(''.join(sys.version.split('.')[:2]))")"
+  #"$PYBIN"/pip install numpy
+  "$PYBIN"/pip install /io/dist/eigenpy-*-"$CP_VERSION"-*.whl
+  "$PYBIN"/pip install /io/dist/hppfcl-*-"$CP_VERSION"-*.whl
+done
diff --git a/docker/manylinux2014/config/pinocchio/test.py b/docker/manylinux2014/config/pinocchio/test.py
new file mode 100644
index 0000000000000000000000000000000000000000..9025d6fe799d5ba542f7bd979cae2c9d5d05e9b2
--- /dev/null
+++ b/docker/manylinux2014/config/pinocchio/test.py
@@ -0,0 +1,12 @@
+import unittest
+
+import pinocchio
+
+
+class TestPinocchio(unittest.TestCase):
+    def test_trivial(self):
+        self.assertEqual(str(pinocchio.SE3.Identity().inverse()), '  R =\n1 0 0\n0 1 0\n0 0 1\n  p = -0 -0 -0\n')
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/docker/manylinux2014/config/robotpkg b/docker/manylinux2014/config/robotpkg
new file mode 100644
index 0000000000000000000000000000000000000000..d3fe9c185217f9297549990531cf60396e7a3b77
--- /dev/null
+++ b/docker/manylinux2014/config/robotpkg
@@ -0,0 +1,3 @@
+mapping/octomap
+graphics/urdfdom-headers
+graphics/urdfdom
diff --git a/docker/manylinux2014/config/targets b/docker/manylinux2014/config/targets
new file mode 100644
index 0000000000000000000000000000000000000000..ed5626ac29fa5d5ce1fa1ea0d966e5f63c66801e
--- /dev/null
+++ b/docker/manylinux2014/config/targets
@@ -0,0 +1,3 @@
+eigenpy
+hpp-fcl
+pinocchio
diff --git a/docker/manylinux2014/scripts/build_wheels.sh b/docker/manylinux2014/scripts/build_wheels.sh
index 3222983824d8abbf1a3175bc3ca40e0423f84622..485df2d76471f6fc92d863a0a8bcb7441403bf3c 100755
--- a/docker/manylinux2014/scripts/build_wheels.sh
+++ b/docker/manylinux2014/scripts/build_wheels.sh
@@ -1,5 +1,9 @@
 #!/bin/bash -eux
 
+export LD_LIBRARY_PATH=/opt/openrobots/lib:$LD_LIBRARY_PATH
+
+TARGET=${1:-eigenpy}
+
 function repair_wheel {
   wheel="$1"
   if ! auditwheel show "$wheel"; then
@@ -9,54 +13,35 @@ function repair_wheel {
   fi
 }
 
-TEST_DIR=$(echo *test*) # Either 'unittest' or 'test' depending on the project
-PYTHON_TEST_DIR=$(echo "$TEST_DIR"/python*) # Either 'python' or 'python_unit' depending on the project
-SKIP_TESTS=$(grep -l BOOST_PYTHON_MODULE "$TEST_DIR"/*.cpp | cut -d'/' -f2 | sed 's/\.cpp/\.py/' | sed 's/^/test_/')
-function test {
-  python="$1"
-  for f in /src/"$PYTHON_TEST_DIR"/*.py; do
-    if ! echo "$SKIP_TESTS" | grep "$(basename "$f")" > /dev/null; then
-      printf "\nTesting %s\n" "$f"
-      "$python" "$f"
-      printf "Ok\n"
-    fi
-  done
-  printf "\n"
-}
-
-source config
-
-## Install dependencies
-[ -s packages ] && xargs -a packages yum -y install
+source "/io/config/$TARGET/config"
 
 # Build wheels
 for PYBIN in /opt/python/*/bin; do
   [ -n "$INSTALL_REQUIRES" ] && "$PYBIN/pip" install "$(echo "$INSTALL_REQUIRES" | sed -E 's/"|\[|\]|,//g')"
 
   PYVERSION=$(find "$PYBIN" -type f -name 'python*.*' | head -1 | grep -Eo "[0-9]\.[0-9]")
-  CMAKE_PREFIX_PATH=$(find / -path "*$PYVERSION*" -type d -name cmake | tr '\n' ';')
-
-  "$PYBIN/python" setup.py bdist_wheel -DCMAKE_PREFIX_PATH="$CMAKE_PREFIX_PATH" -DBUILD_TESTING=OFF -j"$NPROC"
+  export CMAKE_PREFIX_PATH="$(find / -path "*$PYVERSION*" -type d -name cmake | tr '\n' ':')/opt/openrobots"
 
+  "$PYBIN/python" setup.py bdist_wheel -j"$NPROC" -DBUILD_TESTING=OFF -DINSTALL_DOCUMENTATION=OFF \
+      -DCMAKE_INSTALL_LIBDIR=lib -DPYTHON_STANDARD_LAYOUT=ON -DENFORCE_MINIMAL_CXX_STANDARD=ON
   # Bundle external shared libraries into the wheels
-  MAIN_LIB=$(find _skbuild/*/cmake-build -type f ! -path '*test/*' -name 'lib*.so*')
-  MAIN_LIB_DIR=$(pwd)/$(dirname "$MAIN_LIB")
+  MAIN_LIB_DIR="$PWD/_skbuild/linux-x86_64-$PYVERSION/cmake-install/lib"
   OTHER_LIB_DIRS=$(find / -path "*$PYVERSION*.libs/*" -name *.so | xargs dirname | tr '\n' ':')
   WHEEL_NAME=$(ls -Art dist/ | tail -n 1)
 
   LD_LIBRARY_PATH="$MAIN_LIB_DIR:$OTHER_LIB_DIRS$LD_LIBRARY_PATH" \
-    repair_wheel "dist/$WHEEL_NAME"
+    auditwheel repair "dist/$WHEEL_NAME" --plat "$PLAT" -w /io/wheelhouse/
+    #repair_wheel "dist/$WHEEL_NAME"
   rm -rf _skbuild
 done
 
 for whl in /io/wheelhouse/*.whl; do
   wheel unpack "$whl"
 
-  MAIN_LIB=$(find . -type f ! -path '*.libs/*' ! -path '*lib64/*' -name '*.so*')
+  MAIN_LIB=$(find . -type f ! -path '*.libs/*' -name '*.so' | grep 'lib/python' | head -1)
   LIB_DIR=$(find . -type d -name '*.libs')
   PACKAGE_DIR=$(dirname "$MAIN_LIB")
-  LIB64_DIR=$(find . -type d -name 'lib64')
-  WHEEL_DIR=$(find . -name *.so | head -1 | cut -d'/' -f2)
+  WHEEL_DIR=$(find . -name '*.so' | head -1 | cut -d'/' -f2)
 
   # Put the libs inside the package directory so the relative path to the libs is always the same
   mv "$LIB_DIR" "$PACKAGE_DIR"/
@@ -70,17 +55,14 @@ for whl in /io/wheelhouse/*.whl; do
     patchelf --set-rpath '$ORIGIN' "$lib"
   done
 
-  # Remove libraries that are present twice (auditwheel already copied them in the .libs directory)
-  rm -f "$LIB64_DIR"/*.so*
-
   wheel pack "$WHEEL_DIR" -d /io/wheelhouse
   rm -rf "${WHEEL_DIR:?}"/
 done
 
 # Install packages and test
 for PYBIN in /opt/python/*/bin; do
-    "$PYBIN/pip" install "$TARGET_NAME" --no-index --find-links=/io/wheelhouse/
-    (cd "$HOME"; test "$PYBIN/python")
+    "$PYBIN/pip" install "$PACKAGE_NAME" --no-index --find-links=/io/wheelhouse/
+    (cd "$HOME"; "$PYBIN/python" "/io/config/$TARGET/test.py")
 done
 
 # TODO: Fix generated .cmake files (need to be done after installing the wheels so we know where the shared libraries are installed)
@@ -88,23 +70,24 @@ for whl in /io/wheelhouse/*.whl; do
   wheel unpack "$whl"
 
   WHEEL_DIR=$(find . -name *.so | head -1 | cut -d'/' -f2)
-  PYVERSION=$(find "$WHEEL_DIR" -type d -name 'python*' | tail -c4)
-  MAIN_LIB=$(find / ! -path "*$WHEEL_DIR*" -path "*$PYVERSION*$PACKAGE_NAME*.libs/*" -name "*$PACKAGE_NAME*.so")
+  PYVERSION=$(find "$WHEEL_DIR" -type d -name 'python*' | grep 'lib/python' | tail -c4)
+  MAIN_LIB=$(find / ! -path "*$WHEEL_DIR*" -path "*$PYVERSION*$PACKAGE_NAME*.libs/*" -name "*$PACKAGE_NAME*.so*")
   MAIN_LIB_NAME=$(basename "$MAIN_LIB") # libeigenpy-[hash].so
   MAIN_LIB_NAME_NO_HASH=$(echo $MAIN_LIB_NAME | sed 's/-.*\.so/\.so/') # libeigenpy.so
 
   #TODO: .cmake files should not have references to _skbuild, this is only a temporary fix to be able to build hpp-fcl
   find "$WHEEL_DIR" -name '*.cmake' | while read -r file; do
     sed -i 's:/src/_skbuild/linux-x86_64-.*/cmake-install/include:/usr/local/include:' "$file" || true
-    sed -i "s:/src/_skbuild/linux-x86_64-.*/cmake-install/lib64/.*\.so:$MAIN_LIB:" "$file" || true
+    sed -i "s:/src/_skbuild/linux-x86_64-.*/cmake-install/lib/.*\.so:$MAIN_LIB:" "$file" || true
     sed -i "s:$MAIN_LIB_NAME_NO_HASH:$MAIN_LIB_NAME:" "$file" || true
-    sed -i 's:/src/_skbuild/linux-x86_64-.*/cmake-install/lib64:/usr/local/lib64:' "$file" || true
+    sed -i 's:/src/_skbuild/linux-x86_64-.*/cmake-install/lib:/usr/local/lib:' "$file" || true
   done
 
   wheel pack "$WHEEL_DIR" -d /io/wheelhouse
   rm -rf "${WHEEL_DIR:?}"/
 done
 
-rm -rf "$TARGET_NAME".egg-info/ dist/
+rm -rf "$PACKAGE_NAME".egg-info/ dist/
 
-ls /io/wheelhouse
+mkdir -p /io/dist
+mv /io/wheelhouse/* /io/dist/
diff --git a/docker/manylinux2014/scripts/pre_build.sh b/docker/manylinux2014/scripts/pre_build.sh
deleted file mode 100755
index a9bf588e2f88457fdf73ac7361ef1d596fb81453..0000000000000000000000000000000000000000
--- a/docker/manylinux2014/scripts/pre_build.sh
+++ /dev/null
@@ -1 +0,0 @@
-#!/bin/bash
diff --git a/docker/manylinux2014/scripts/setup.py b/docker/manylinux2014/scripts/setup.py
index d37f4451c407cc6036971c20433131a682ca8129..6d3a958de37fa10cf20b6c11f50c4f8e9f612dfb 100644
--- a/docker/manylinux2014/scripts/setup.py
+++ b/docker/manylinux2014/scripts/setup.py
@@ -7,18 +7,16 @@ except FileNotFoundError:
     long_description = ""
 
 setup(
-    name=TARGET_NAME,
-    version=VERSION,
-    description=DESCRIPTION,
+    name="PACKAGE_NAME",
+    version="VERSION",
+    description="DESCRIPTION",
     long_description=long_description,
     long_description_content_type="text/markdown",
-    url=GIT_URL,
+    url="https://github.com/GITHUB_ORG/TARGET",
     install_requires=INSTALL_REQUIRES,
     classifiers=[
-        "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 3",
-        "License :: OSI Approved :: BSD License",
-        "Operating System :: POSIX :: Linux"
+        "Programming Language :: Python :: 2", "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: BSD License", "Operating System :: POSIX :: Linux"
     ],
     python_requires='>=2.7',
 )
diff --git a/docker/manylinux2014/scripts/setup.sh b/docker/manylinux2014/scripts/setup.sh
index 69a0897b011a8bbaf3054008374db72f65d3410b..53f5f91044a56b09acc111e2495fe7bf859482a2 100755
--- a/docker/manylinux2014/scripts/setup.sh
+++ b/docker/manylinux2014/scripts/setup.sh
@@ -1,37 +1,42 @@
-#!/bin/bash -eu
+#!/bin/bash -eux
+
+TARGET=${1:-eigenpy}
 
 # Verify that variables are set properly
-source "/io/config/config"
-for var in TARGET_NAME PACKAGE_NAME GIT_URL VERSION NPROC; do
+source "/io/config/$TARGET/config"
+for var in PACKAGE_NAME GITHUB_ORG VERSION NPROC; do
   [ -z "${!var}" ] && { echo "$var is not set."; exit; }
 done
 
 mkdir -p /io/wheelhouse
 [ "$(ls -A /io/wheelhouse)" ] && { echo "./wheelhouse should be empty before running this script."; exit; }
 
-git clone --recursive -j"$NPROC" -b v"$VERSION" "$GIT_URL" /src
-cp /io/scripts/* /src
-cp /io/config/* /src
+curl -sSL "https://github.com/$GITHUB_ORG/$TARGET/releases/download/v$VERSION/$TARGET-$VERSION.tar.gz" \
+    | tar xz --strip-components=1 2> /dev/null
+
+cp /scripts/setup.py .
 
 # Fix CMake for scikit-build
 find . -name CMakeLists.txt | xargs sed -i 's/PYTHON_INCLUDE_DIRS/PYTHON_INCLUDE_DIR/'
 sed -i 's/REQUIRED COMPONENTS Interpreter Development/REQUIRED COMPONENTS Interpreter/' cmake/python.cmake
 
 # Write setup.py
-DESCRIPTION=$(grep PROJECT_DESCRIPTION CMakeLists.txt | cut -d'"' -f2) # TODO: Not working if description on multiple lines
-for var in TARGET_NAME VERSION DESCRIPTION GIT_URL; do
-  sed -i "s~$var~\"${!var}\"~" setup.py
+DESCRIPTION=$(grep PROJECT_DESCRIPTION CMakeLists.txt | cut -d'"' -f2) # TODO: Not working on multiple lines
+for var in PACKAGE_NAME GITHUB_ORG TARGET VERSION DESCRIPTION; do
+  sed -i "s~$var~${!var}~" setup.py
 done
 
 [ -z "$INSTALL_REQUIRES" ] && sed -i '/INSTALL_REQUIRES/d' setup.py
 sed -i "s/INSTALL_REQUIRES/$INSTALL_REQUIRES/" setup.py
 
-python setup.py --help > /dev/null 2>&1 || { echo "Syntax error in config/setup.py, please edit this file."; exit; }
+cat setup.py
+
+python setup.py --help || { echo "Syntax error in config/setup.py, please edit this file."; exit; }
 
-./pre_build.sh
+echo -e "\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n"
 
-echo -e "\nCurrent setup.py: \n"
-cat setup.py
-echo -e "\nMake sure that setup.py has the right values before continuing. Then run ./build_wheels.sh\n"
+[ -x "/io/config/$TARGET/pre_build.sh" ] && "/io/config/$TARGET/pre_build.sh" "$TARGET"
+
+echo -e "\n=========================================================================================================\n"
 
-bash
+/scripts/build_wheels.sh "$TARGET"
diff --git a/docker/manylinux2014/test.Dockerfile b/docker/manylinux2014/test.Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..a35e1db359b0c8ab8e9a66e192e166fa5f28cc96
--- /dev/null
+++ b/docker/manylinux2014/test.Dockerfile
@@ -0,0 +1,6 @@
+FROM python:3.7
+
+RUN apt-get update -qqy && apt-get install -qqy libgl1 && rm -rf /var/lib/apt/lists/*
+
+ADD dist config test.sh /
+CMD /test.sh
diff --git a/docker/manylinux2014/test.sh b/docker/manylinux2014/test.sh
new file mode 100755
index 0000000000000000000000000000000000000000..c62a66030aa5f23100fca19e54036abd2222023a
--- /dev/null
+++ b/docker/manylinux2014/test.sh
@@ -0,0 +1,11 @@
+#!/bin/bash -eu
+
+CP_VERSION="cp$(python -c "import sys; print(''.join(sys.version.split('.')[:2]))")"
+
+while read tgt
+do
+    echo -e "\n================================ $tgt ===================================\n"
+    source "/$tgt/config"
+    pip install /"$PACKAGE_NAME"-*-"$CP_VERSION"-*.whl
+    python "/$tgt/test.py"
+done < targets