diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6371035f3fe85c90c7d14e089a4007819426f18d
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,14 @@
+Python 3 POCL:
+  script:
+  - export PY_EXE=python3
+  - export PYOPENCL_TEST=portable
+  - export EXTRA_INSTALL="pybind11 numpy mako"
+  - ". ./build-and-test-py-project.sh"
+  tags:
+  - python3
+  - pocl
+  except:
+  - tags
+  artifacts:
+    reports:
+      junit: pytest.xml
diff --git a/build-and-test-py-project.sh b/build-and-test-py-project.sh
new file mode 100644
index 0000000000000000000000000000000000000000..0810a3871a048bb83a6f317e6eba1fbc8dc00fa3
--- /dev/null
+++ b/build-and-test-py-project.sh
@@ -0,0 +1,92 @@
+echo "-----------------------------------------------"
+echo "Current directory: $(pwd)"
+echo "Python executable: ${PY_EXE}"
+echo "PYOPENCL_TEST: ${PYOPENCL_TEST}"
+echo "PYTEST_ADDOPTS: ${PYTEST_ADDOPTS}"
+echo "PROJECT_INSTALL_FLAGS: ${PROJECT_INSTALL_FLAGS}"
+echo "git revision: $(git rev-parse --short HEAD)"
+echo "git status:"
+git status -s
+echo "-----------------------------------------------"
+
+# {{{ clean up
+
+rm -Rf .env
+rm -Rf build
+find . -name '*.pyc' -delete
+
+rm -Rf env
+git clean -fdx \
+  -e siteconf.py \
+  -e boost-numeric-bindings \
+  -e '.pylintrc.yml' \
+  -e 'prepare-and-run-*.sh' \
+  -e 'run-*.py' \
+  $GIT_CLEAN_EXCLUDE
+
+# }}}
+
+if [[ "$NO_SUBMODULES" = "" ]]; then
+  git submodule update --init --recursive
+fi
+
+# {{{ virtualenv
+
+VIRTUALENV="${PY_EXE} -m venv"
+
+if [ -d ".env" ]; then
+  echo "**> virtualenv exists"
+else
+  echo "**> creating virtualenv"
+  ${VIRTUALENV} .env
+fi
+
+. .env/bin/activate
+
+# }}}
+
+$PY_EXE -m ensurepip
+
+PIP="${PY_EXE} $(which pip)"
+
+# https://github.com/pypa/pip/issues/5345#issuecomment-386443351
+export XDG_CACHE_HOME=$HOME/.cache/$CI_RUNNER_ID
+
+$PIP install --upgrade pip
+$PIP install setuptools
+
+# Pinned to 3.0.4 because of https://github.com/pytest-dev/pytest/issues/2434
+# Install before a newer version gets pulled in as a dependency
+$PIP install pytest==3.0.4 pytest-warnings==0.2.0
+
+if test "$EXTRA_INSTALL" != ""; then
+  for i in $EXTRA_INSTALL ; do
+    if [ "$i" = "numpy" ] && [[ "${PY_EXE}" == pypy* ]]; then
+      $PIP install git+https://bitbucket.org/pypy/numpy.git
+    elif [[ "$i" = *pybind11* ]] && [[ "${PY_EXE}" == pypy* ]]; then
+      # Work around https://github.com/pypa/virtualenv/issues/1198
+      # Running virtualenv --always-copy or -m venv --copies should also do the trick.
+      L=$(readlink .env/include)
+      rm .env/include
+      cp -R $L .env/include
+
+      $PIP install $i
+    elif [ "$i" = "numpy" ] && [[ "${PY_EXE}" == python2.6* ]]; then
+      $PIP install 'numpy==1.10.4'
+    else
+      $PIP install $i
+    fi
+  done
+fi
+
+if test "$REQUIREMENTS_TXT" == ""; then
+  REQUIREMENTS_TXT="requirements.txt"
+fi
+
+if test -f $REQUIREMENTS_TXT; then
+  $PIP install -r $REQUIREMENTS_TXT
+fi
+
+${PY_EXE} -m pytest -rw --durations=10 --tb=native  --junitxml=pytest.xml -rxsw test.py
+
+# vim: foldmethod=marker
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000000000000000000000000000000000000..36d95d70721dcf19677fc5fd2fad9cee4d514ab4
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,13 @@
+git+https://github.com/inducer/pytools.git
+git+https://github.com/inducer/islpy.git
+git+https://github.com/inducer/cgen.git
+git+https://github.com/inducer/pyopencl.git
+git+https://github.com/inducer/pymbolic.git
+git+https://github.com/inducer/genpy.git
+git+https://github.com/inducer/codepy.git
+git+https://gitlab.tiker.net/inducer/loopy.git@kernel_callables_v3
+
+git+https://github.com/inducer/f2py
+
+# Optional, needed for using the C preprocessor on Fortran
+ply>=3.6