diff --git a/.gitignore b/.gitignore
index bde2e54547774c9c810de4cfa4c4d91c38b63698..c4e4bd314093facb5aa728f12fd34a6cfbbd5781 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,5 +16,6 @@ dist
 .gdb_history
 build
 *.egg
-src/
 record.txt
+.gitlab-ci-local*
+html/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 845b719f2378d396a8b75296a68d1e32c840e7c7..80ad328f094433833f53a5512cfc85f93eb799a5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1 +1,4 @@
-include: 'https://gitlab.idiap.ch/bob/bob.devtools/raw/master/bob/devtools/data/gitlab-ci/single-package.yaml'
+include:
+  - project: bob/citools
+    ref: master
+    file: /src/citools/data/python.yml
diff --git a/MANIFEST.in b/MANIFEST.in
index b694e27d6980528fd51201e4caac662a8795213f..f7dc149f8d78997a15758351ff138c314ef02cfc 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,4 +1,3 @@
-include LICENSE README.rst buildout.cfg develop.cfg requirements.txt version.txt
+include LICENSE README.rst
 recursive-include doc conf.py *.rst
-recursive-include bob *.h *.cpp
-recursive-include bob/measure/data *.*
+recursive-include bob/tests/data *.*
diff --git a/README.rst b/README.rst
index f18c887ded95954f8961f6025d209be1fee44225..053d9494a446de55e04b6f477a858ea945b5866b 100644
--- a/README.rst
+++ b/README.rst
@@ -2,11 +2,11 @@
 .. Thu 11 Aug 14:52:51 CEST 2016
 
 .. image:: https://img.shields.io/badge/docs-latest-orange.svg
-   :target: https://www.idiap.ch/software/bob/docs/bob/bob.measure/master/index.html
+   :target: https://www.idiap.ch/software/bob/docs/bob/bob.measure/master/sphinx/index.html
 .. image:: https://gitlab.idiap.ch/bob/bob.measure/badges/master/pipeline.svg
    :target: https://gitlab.idiap.ch/bob/bob.measure/commits/master
 .. image:: https://gitlab.idiap.ch/bob/bob.measure/badges/master/coverage.svg
-   :target: https://gitlab.idiap.ch/bob/bob.measure/commits/master
+   :target: https://www.idiap.ch/software/bob/docs/bob/bob.measure/master/coverage/
 .. image:: https://img.shields.io/badge/gitlab-project-0000c0.svg
    :target: https://gitlab.idiap.ch/bob/bob.measure
 
diff --git a/buildout.cfg b/buildout.cfg
deleted file mode 100644
index 19b3ab93e959f4ee21406ff4195deaa9ac453fb6..0000000000000000000000000000000000000000
--- a/buildout.cfg
+++ /dev/null
@@ -1,14 +0,0 @@
-; vim: set fileencoding=utf-8 :
-; Thu Apr  5 11:28:53 CEST 2018
-
-[buildout]
-parts = scripts
-develop = .
-eggs = bob.measure
-extensions = bob.buildout
-newest = false
-verbose = true
-
-[scripts]
-recipe = bob.buildout:scripts
-dependent-scripts = true
diff --git a/conda/meta.yaml b/conda/meta.yaml
index cb3d44b57f71c6ddcbb0120036730a9b910fc120..cd42e1d060c3f6854592be089cffb5624c17a1f3 100644
--- a/conda/meta.yaml
+++ b/conda/meta.yaml
@@ -1,27 +1,29 @@
-{% set name = 'bob.measure' %}
-{% set project_dir = environ.get('RECIPE_DIR') + '/..' %}
+{% set data = load_file_data(RECIPE_DIR + '/../pyproject.toml') %}
+{% set name = data['project']['name'] %}
+
 
 package:
   name: {{ name }}
-  version: {{ environ.get('BOB_PACKAGE_VERSION', '0.0.1') }}
+  version: {{ data['project']['version'] }}
+
+source:
+  path: ..
 
 build:
+  noarch: python
   number: {{ environ.get('BOB_BUILD_NUMBER', 0) }}
   run_exports:
     - {{ pin_subpackage(name) }}
   script:
-    - cd {{ project_dir }}
-    {% if environ.get('BUILD_EGG') %}
-    - "{{ PYTHON }} setup.py sdist --formats=zip"
-    {% endif %}
-    - "{{ PYTHON }} -m pip install . -vv"
+    - "{{ PYTHON }} -m pip install {{ SRC_DIR }} -vv"
 
 requirements:
   host:
     - python {{ python }}
     - setuptools {{ setuptools }}
     - pip {{ pip }}
-    - bob.extension
+    - bob.io.base
+    - exposed
     - numpy {{ numpy }}
     - scipy {{ scipy }}
     - h5py {{ h5py }}
@@ -29,9 +31,10 @@ requirements:
     - tabulate {{ tabulate }}
     - numba {{ numba }}
   run:
-    # - bob.extension  #not required, has run_exports
     - python
     - setuptools
+    - bob.io.base
+    - exposed
     - {{ pin_compatible('numpy') }}
     - {{ pin_compatible('scipy') }}
     - {{ pin_compatible('h5py') }}
@@ -43,20 +46,11 @@ test:
   imports:
     - {{ name }}
   commands:
-    - pytest --verbose --cov {{ name }} --cov-report term-missing --cov-report html:{{ project_dir }}/sphinx/coverage --cov-report xml:{{ project_dir }}/coverage.xml --pyargs {{ name }}
-    - sphinx-build -aEW {{ project_dir }}/doc {{ project_dir }}/sphinx
-    - sphinx-build -aEb doctest {{ project_dir }}/doc sphinx
     - conda inspect linkages -p $PREFIX {{ name }}  # [not win]
     - conda inspect objects -p $PREFIX {{ name }}  # [osx]
-  requires:
-    - pytest {{ pytest }}
-    - pytest-cov {{ pytest_cov }}
-    - coverage {{ coverage }}
-    - sphinx {{ sphinx }}
-    - sphinx_rtd_theme {{ sphinx_rtd_theme }}
 
 about:
-  home: https://www.idiap.ch/software/bob/
-  license: BSD 3-Clause
-  summary: Bob's evalution metrics
+  home: {{ data['project']['urls']['homepage'] }}
+  summary: {{ data['project']['description'] }}
+  license: {{ data['project']['license']['text'] }}
   license_family: BSD
diff --git a/doc/catalog.json b/doc/catalog.json
new file mode 100644
index 0000000000000000000000000000000000000000..0967ef424bce6791893e9a57bb952f80fd536e93
--- /dev/null
+++ b/doc/catalog.json
@@ -0,0 +1 @@
+{}
diff --git a/doc/conf.py b/doc/conf.py
index 1fcd19ccff117fac6e2e75f0ed479bd31676c2ec..228d00d332ce856a8f7f579682b4f9c2ade36e06 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -25,6 +25,7 @@ extensions = [
     "sphinx.ext.viewcode",
     "sphinx.ext.mathjax",
     "matplotlib.sphinxext.plot_directive",
+    "auto_intersphinx",
 ]
 
 # Be picky about warnings
@@ -232,14 +233,15 @@ autodoc_default_options = {
     "show-inheritance": True,
 }
 
-# For inter-documentation mapping:
-from bob.extension.utils import link_documentation, load_requirements
-
-sphinx_requirements = "extra-intersphinx.txt"
-if os.path.exists(sphinx_requirements):
-    intersphinx_mapping = link_documentation(
-        additional_packages=["python", "numpy"]
-        + load_requirements(sphinx_requirements)
-    )
-else:
-    intersphinx_mapping = link_documentation()
+auto_intersphinx_packages = [
+    ("python", "3"),
+    "numpy",
+    "scikit-learn",
+    "scipy",
+    "h5py",
+    "matplotlib",
+    "tabulate",
+    "numba",
+    "xarray",
+]
+auto_intersphinx_catalog = "catalog.json"
diff --git a/pyproject.toml b/pyproject.toml
index b738dc847ff9705c5769673db7415f2eb9a75f4d..93f56475fc492ba4662bb6f001e709401447bdc8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,86 @@
 [build-system]
-    requires = ["setuptools", "wheel", "bob.extension"]
+    requires = ["setuptools>=61.0.0", "wheel"]
     build-backend = "setuptools.build_meta"
 
+[project]
+    name = "bob.measure"
+    version = "6.0.3b0"
+    requires-python = ">=3.9"
+    description = "Evaluation metrics for Bob"
+    dynamic = ["readme"]
+    license = {text = "BSD 3-Clause License"}
+    authors = [
+    {name = "Andre Anjos"},
+    {email = "andre.anjos@idiap.ch"},
+    ]
+    keywords = ["bob", "pipelines"]
+
+    classifiers=[
+        "Framework :: Bob",
+        "Development Status :: 4 - Beta",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: BSD License",
+        "Natural Language :: English",
+        "Programming Language :: Python",
+        "Programming Language :: Python :: 3",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+    ]
+    dependencies = [
+        "setuptools",
+        "numpy",
+        "bob.io.base",
+        "exposed",
+        "scipy",
+        "h5py",
+        "matplotlib",
+        "tabulate",
+        "numba",
+    ]
+
+[project.urls]
+    documentation = "https://www.idiap.ch/software/bob/docs/bob/bob.measure/stable/"
+    homepage = "https://pypi.org/project/bob.measure/"
+    repository = "https://gitlab.idiap.ch/bob/bob.measure"
+    changelog = "https://gitlab.idiap.ch/bob/bob.measure/-/releases"
+
+[project.optional-dependencies]
+    qa = ["pre-commit"]
+    doc = [
+        "sphinx",
+        "sphinx_rtd_theme",
+        "sphinx-autodoc-typehints",
+        "auto-intersphinx",
+        "sphinxcontrib-programoutput",
+        "matplotlib",
+        ]
+    test = [
+        "pytest",
+        "pytest-cov",
+        "coverage",
+        ]
+
+[tool.setuptools]
+    zip-safe = false
+    package-dir = {"" = "src"}
+
+[tool.setuptools.dynamic]
+    readme = {file = "README.rst"}
+
+[project.entry-points."bob.cli"]
+    measure  = "bob.measure.script.measure:measure"
+
+[project.entry-points."bob.measure.cli"]
+    evaluate = "bob.measure.script.commands:evaluate"
+    metrics =  "bob.measure.script.commands:metrics"
+    multi-metrics = "bob.measure.script.commands:multi_metrics"
+    roc = "bob.measure.script.commands:roc"
+    det = "bob.measure.script.commands:det"
+    epc = "bob.measure.script.commands:epc"
+    hist = "bob.measure.script.commands:hist"
+    gen = "bob.measure.script.gen:gen"
+
+[tool.distutils.bdist_wheel]
+    universal = true
 [tool.isort]
     profile = "black"
     line_length = 80
@@ -10,3 +89,12 @@
 
 [tool.black]
     line-length = 80
+
+[tool.pytest.ini_options]
+    addopts = [
+        "--import-mode=append",
+        "--cov-report=term-missing",
+        "--cov=bob.measure",
+    ]
+    junit_logging = "all"
+    junit_log_passing_tests = false
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 04b35759cf536e69403aa0e9cadae0a7304f2294..0000000000000000000000000000000000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-bob.extension
-numpy
-scipy
-h5py
-matplotlib
-tabulate
-numba
diff --git a/setup.py b/setup.py
index 3ce53cc05594434f1b4c547b8c457e907d441bd4..606849326a4002007fd42060b51e69a19c18675c 100644
--- a/setup.py
+++ b/setup.py
@@ -1,49 +1,3 @@
-from setuptools import dist, setup
+from setuptools import setup
 
-dist.Distribution(dict(setup_requires=["bob.extension"]))
-from bob.extension.utils import find_packages, load_requirements
-
-install_requires = load_requirements()
-
-
-setup(
-    name="bob.measure",
-    version=open("version.txt").read().rstrip(),
-    description="Evaluation metrics for Bob",
-    url="http://gitlab.idiap.ch/bob/bob.measure",
-    license="BSD",
-    author="Andre Anjos",
-    author_email="andre.anjos@idiap.ch",
-    long_description=open("README.rst").read(),
-    packages=find_packages(),
-    include_package_data=True,
-    zip_safe=False,
-    install_requires=install_requires,
-    entry_points={
-        # main entry for bob measure cli
-        "bob.cli": [
-            "measure  = bob.measure.script.measure:measure",
-        ],
-        # bob measure scripts
-        "bob.measure.cli": [
-            "evaluate = bob.measure.script.commands:evaluate",
-            "metrics = bob.measure.script.commands:metrics",
-            "multi-metrics = bob.measure.script.commands:multi_metrics",
-            "roc = bob.measure.script.commands:roc",
-            "det = bob.measure.script.commands:det",
-            "epc = bob.measure.script.commands:epc",
-            "hist = bob.measure.script.commands:hist",
-            "gen = bob.measure.script.gen:gen",
-        ],
-    },
-    classifiers=[
-        "Framework :: Bob",
-        "Development Status :: 4 - Beta",
-        "Intended Audience :: Developers",
-        "License :: OSI Approved :: BSD License",
-        "Natural Language :: English",
-        "Programming Language :: Python",
-        "Programming Language :: Python :: 3",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-    ],
-)
+setup()
diff --git a/bob/__init__.py b/src/bob/__init__.py
similarity index 100%
rename from bob/__init__.py
rename to src/bob/__init__.py
diff --git a/bob/measure/__init__.py b/src/bob/measure/__init__.py
similarity index 100%
rename from bob/measure/__init__.py
rename to src/bob/measure/__init__.py
diff --git a/bob/measure/_library.py b/src/bob/measure/_library.py
similarity index 100%
rename from bob/measure/_library.py
rename to src/bob/measure/_library.py
diff --git a/bob/measure/calibration.py b/src/bob/measure/calibration.py
similarity index 100%
rename from bob/measure/calibration.py
rename to src/bob/measure/calibration.py
diff --git a/bob/measure/load.py b/src/bob/measure/load.py
similarity index 100%
rename from bob/measure/load.py
rename to src/bob/measure/load.py
diff --git a/bob/measure/plot.py b/src/bob/measure/plot.py
similarity index 100%
rename from bob/measure/plot.py
rename to src/bob/measure/plot.py
diff --git a/bob/measure/script/__init__.py b/src/bob/measure/script/__init__.py
similarity index 100%
rename from bob/measure/script/__init__.py
rename to src/bob/measure/script/__init__.py
diff --git a/bob/measure/script/commands.py b/src/bob/measure/script/commands.py
similarity index 100%
rename from bob/measure/script/commands.py
rename to src/bob/measure/script/commands.py
diff --git a/bob/measure/script/common_options.py b/src/bob/measure/script/common_options.py
similarity index 89%
rename from bob/measure/script/common_options.py
rename to src/bob/measure/script/common_options.py
index e24f10158229fc2c74cf24cf47ac11196dd04560..ddc856faa8192bafb7a253a770d465caf688b72e 100644
--- a/bob/measure/script/common_options.py
+++ b/src/bob/measure/script/common_options.py
@@ -8,18 +8,140 @@ import matplotlib.pyplot as plt
 import tabulate
 
 from click.types import FLOAT, INT
+from exposed.click import verbosity_option
 from matplotlib.backends.backend_pdf import PdfPages
 
-from bob.extension.scripts.click_helper import (
-    bool_option,
-    list_float_option,
-    open_file_mode_option,
-    verbosity_option,
-)
-
 LOGGER = logging.getLogger(__name__)
 
 
+def bool_option(name, short_name, desc, dflt=False, **kwargs):
+    """Generic provider for boolean options
+
+    Parameters
+    ----------
+    name : str
+        name of the option
+    short_name : str
+        short name for the option
+    desc : str
+        short description for the option
+    dflt : bool or None
+        Default value
+    **kwargs
+        All kwargs are passed to click.option.
+
+    Returns
+    -------
+    ``callable``
+        A decorator to be used for adding this option.
+    """
+
+    def custom_bool_option(func):
+        def callback(ctx, param, value):
+            ctx.meta[name.replace("-", "_")] = value
+            return value
+
+        return click.option(
+            "-%s/-n%s" % (short_name, short_name),
+            "--%s/--no-%s" % (name, name),
+            default=dflt,
+            help=desc,
+            show_default=True,
+            callback=callback,
+            is_eager=True,
+            **kwargs,
+        )(func)
+
+    return custom_bool_option
+
+
+def list_float_option(name, short_name, desc, nitems=None, dflt=None, **kwargs):
+    """Get option to get a list of float f
+
+    Parameters
+    ----------
+    name : str
+        name of the option
+    short_name : str
+        short name for the option
+    desc : str
+        short description for the option
+    nitems : obj:`int`, optional
+        If given, the parsed list must contains this number of items.
+    dflt : :any:`list`, optional
+        List of default  values for axes.
+    **kwargs
+        All kwargs are passed to click.option.
+
+    Returns
+    -------
+    ``callable``
+        A decorator to be used for adding this option.
+    """
+
+    def custom_list_float_option(func):
+        def callback(ctx, param, value):
+            if value is None or not value.replace(" ", ""):
+                value = None
+            elif value is not None:
+                tmp = value.split(",")
+                if nitems is not None and len(tmp) != nitems:
+                    raise click.BadParameter(
+                        "%s Must provide %d axis limits" % (name, nitems)
+                    )
+                try:
+                    value = [float(i) for i in tmp]
+                except Exception:
+                    raise click.BadParameter("Inputs of %s be floats" % name)
+            ctx.meta[name.replace("-", "_")] = value
+            return value
+
+        return click.option(
+            "-" + short_name,
+            "--" + name,
+            default=dflt,
+            show_default=True,
+            help=desc + " Provide just a space (' ') to cancel default values.",
+            callback=callback,
+            **kwargs,
+        )(func)
+
+    return custom_list_float_option
+
+
+def open_file_mode_option(**kwargs):
+    """Get open mode file option
+
+    Parameters
+    ----------
+    **kwargs
+        All kwargs are passed to click.option.
+
+    Returns
+    -------
+    ``callable``
+        A decorator to be used for adding this option.
+    """
+
+    def custom_open_file_mode_option(func):
+        def callback(ctx, param, value):
+            if value not in ["w", "a", "w+", "a+"]:
+                raise click.BadParameter("Incorrect open file mode")
+            ctx.meta["open_mode"] = value
+            return value
+
+        return click.option(
+            "-om",
+            "--open-mode",
+            default="w",
+            help="File open mode",
+            callback=callback,
+            **kwargs,
+        )(func)
+
+    return custom_open_file_mode_option
+
+
 def scores_argument(min_arg=1, force_eval=False, **kwargs):
     """Get the argument for scores, and add `dev-scores` and `eval-scores` in
     the context when `--eval` flag is on (default)
@@ -87,7 +209,7 @@ def alpha_option(dflt=1, **kwargs):
             show_default=True,
             help="Adjusts transparency of plots.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_eval_option
@@ -117,7 +239,7 @@ def eval_option(**kwargs):
             show_default=True,
             help="If set, evaluation scores must be provided",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_eval_option
@@ -138,7 +260,7 @@ def hide_dev_option(dflt=False, **kwargs):
             show_default=True,
             help="If set, hide dev related plots",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_hide_dev_option
@@ -162,7 +284,7 @@ def linestyles_option(dflt=False, **kwargs):
         "S",
         "If given, applies a different line style to each line.",
         dflt,
-        **kwargs
+        **kwargs,
     )
 
 
@@ -187,7 +309,7 @@ def tpr_option(dflt=False, **kwargs):
         "tpr",
         "If set, use TPR (also called 1-FNR, 1-FNMR, or 1-BPCER) on Y axis",
         dflt,
-        **kwargs
+        **kwargs,
     )
 
 
@@ -214,7 +336,7 @@ def const_layout_option(dflt=True, **kwargs):
             show_default=True,
             help="(De)Activate constrained layout",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_layout_option
@@ -229,7 +351,7 @@ def axes_val_option(dflt=None, **kwargs):
         " 0.1,100,0.1,100``)",
         nitems=4,
         dflt=dflt,
-        **kwargs
+        **kwargs,
     )
 
 
@@ -242,7 +364,7 @@ def thresholds_option(**kwargs):
         "0.005,0.001,0.056",
         nitems=None,
         dflt=None,
-        **kwargs
+        **kwargs,
     )
 
 
@@ -256,7 +378,7 @@ def lines_at_option(dflt="1e-3", **kwargs):
         "This option works in ROC and DET curves.",
         nitems=None,
         dflt=dflt,
-        **kwargs
+        **kwargs,
     )
 
 
@@ -277,7 +399,7 @@ def x_rotation_option(dflt=0, **kwargs):
             show_default=True,
             help="X axis labels ration",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_x_rotation_option
@@ -300,7 +422,7 @@ def legend_ncols_option(dflt=3, **kwargs):
             show_default=True,
             help="The number of columns of the legend layout.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_legend_ncols_option
@@ -326,7 +448,7 @@ def subplot_option(dflt=111, **kwargs):
             show_default=True,
             help="The order of subplots.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_subplot_option
@@ -350,7 +472,7 @@ def cost_option(**kwargs):
             show_default=True,
             help="Cost for FPR in minDCF",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_cost_option
@@ -377,7 +499,7 @@ def points_curve_option(**kwargs):
             show_default=True,
             help="The number of points use to draw curves in plots",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_points_curve_option
@@ -424,7 +546,7 @@ def n_bins_option(**kwargs):
             "for some corner cases, the option `auto` and `fd` can lead to "
             "MemoryError.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_n_bins_option
@@ -447,7 +569,7 @@ def table_option(dflt="rst", **kwargs):
             show_default=True,
             help="Format of printed tables.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_table_option
@@ -472,7 +594,7 @@ def output_plot_file_option(default_out="plots.pdf", **kwargs):
             show_default=True,
             help="The file to save the plots in.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_output_plot_file_option
@@ -496,7 +618,7 @@ def output_log_metric_option(**kwargs):
             help="If provided, computed numbers are written to "
             "this file instead of the standard output.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_output_log_file_option
@@ -517,7 +639,7 @@ def no_line_option(**kwargs):
             show_default=True,
             help="If set does not display vertical lines",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_no_line_option
@@ -557,7 +679,7 @@ def criterion_option(
             callback=callback,
             is_eager=True,
             show_default=True,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_criterion_option
@@ -579,7 +701,7 @@ def decimal_option(dflt=1, short="-d", **kwargs):
             help="Number of decimals to be printed.",
             callback=callback,
             show_default=True,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_decimal_option
@@ -607,7 +729,7 @@ def far_option(far_name="FAR", **kwargs):
             "must be used alongside `--criterion far`.".format(far_name),
             callback=callback,
             show_default=True,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_far_option
@@ -635,7 +757,7 @@ def min_far_option(far_name="FAR", dflt=1e-4, **kwargs):
             "should be a power of 10.".format(far_name),
             callback=callback,
             show_default=True,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_min_far_option
@@ -673,7 +795,7 @@ def figsize_option(dflt="4,3", **kwargs):
             "``plt.rcParams['figure.figsize']=figsize)``. "
             "Example: --figsize 4,6",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_figsize_option
@@ -709,7 +831,7 @@ def legend_loc_option(dflt="best", **kwargs):
             ),
             help="The legend location code",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_legend_loc_option
@@ -728,7 +850,7 @@ def line_width_option(**kwargs):
             type=FLOAT,
             help="The line width of plots",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_line_width_option
@@ -747,7 +869,7 @@ def marker_style_option(**kwargs):
             type=FLOAT,
             help="The marker style of the plots",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_marker_style_option
@@ -771,7 +893,7 @@ def legends_option(**kwargs):
             help="The legend for each system comma separated. "
             "Example: --legends ISV,CNN",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_legends_option
@@ -793,7 +915,7 @@ def title_option(**kwargs):
             help="The title of the plots. Provide just a space (-t ' ') to "
             "remove the titles from figures.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_title_option
@@ -820,7 +942,7 @@ def titles_option(**kwargs):
             " Provide just a space (-ts ' ') to "
             "remove the titles from figures.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_title_option
@@ -842,7 +964,7 @@ def x_label_option(dflt=None, **kwargs):
             show_default=True,
             help="Label for x-axis",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_x_label_option
@@ -863,7 +985,7 @@ def y_label_option(dflt=None, **kwargs):
             default=dflt,
             help="Label for y-axis",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_y_label_option
@@ -885,7 +1007,7 @@ def style_option(**kwargs):
             help="The matplotlib style to use for plotting. You can provide "
             "multiple styles by repeating this option",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_style_option
@@ -911,7 +1033,7 @@ def metrics_command(
         @far_option(far_name=far_name)
         @legends_option()
         @open_file_mode_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @decimal_option()
         @functools.wraps(func)
@@ -973,7 +1095,7 @@ def roc_command(docstring, far_name="FAR"):
         @style_option()
         @linestyles_option()
         @alpha_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @functools.wraps(func)
         def wrapper(*args, **kwds):
@@ -1030,7 +1152,7 @@ def det_command(docstring, far_name="FAR"):
         @style_option()
         @linestyles_option()
         @alpha_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @functools.wraps(func)
         def wrapper(*args, **kwds):
@@ -1080,7 +1202,7 @@ def epc_command(docstring):
         @style_option()
         @linestyles_option()
         @alpha_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @functools.wraps(func)
         def wrapper(*args, **kwds):
@@ -1133,7 +1255,7 @@ def hist_command(docstring, far_name="FAR"):
         @style_option()
         @x_label_option()
         @y_label_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @functools.wraps(func)
         def wrapper(*args, **kwds):
@@ -1189,7 +1311,7 @@ def evaluate_command(
         @figsize_option(dflt=None)
         @style_option()
         @linestyles_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @functools.wraps(func)
         def wrapper(*args, **kwds):
@@ -1288,7 +1410,7 @@ def n_protocols_option(required=True, **kwargs):
             required=required,
             help="The number of protocols of cross validation.",
             callback=callback,
-            **kwargs
+            **kwargs,
         )(func)
 
     return custom_n_protocols_option
@@ -1311,7 +1433,7 @@ def multi_metrics_command(
         @far_option(far_name=far_name)
         @legends_option()
         @open_file_mode_option()
-        @verbosity_option()
+        @verbosity_option(LOGGER)
         @click.pass_context
         @decimal_option()
         @functools.wraps(func)
diff --git a/bob/measure/script/figure.py b/src/bob/measure/script/figure.py
similarity index 100%
rename from bob/measure/script/figure.py
rename to src/bob/measure/script/figure.py
diff --git a/bob/measure/script/gen.py b/src/bob/measure/script/gen.py
similarity index 96%
rename from bob/measure/script/gen.py
rename to src/bob/measure/script/gen.py
index f901491062444b716723c6fa96815cd3885b4241..df6581e127a58bd397f89eb89f47d19ef6ea8c50 100644
--- a/bob/measure/script/gen.py
+++ b/src/bob/measure/script/gen.py
@@ -13,8 +13,7 @@ import numpy
 import numpy.random
 
 from click.types import FLOAT
-
-from bob.extension.scripts.click_helper import verbosity_option
+from exposed.click import verbosity_option
 
 logger = logging.getLogger(__name__)
 
@@ -98,7 +97,7 @@ def write_scores_to_file(neg, pos, filename):
 @click.argument("outdir")
 @click.option("--mean-neg", default=-1, type=FLOAT, show_default=True)
 @click.option("--mean-pos", default=1, type=FLOAT, show_default=True)
-@verbosity_option()
+@verbosity_option(logger)
 def gen(outdir, mean_neg, mean_pos, **kwargs):
     """Generate random scores.
 
diff --git a/bob/measure/script/measure.py b/src/bob/measure/script/measure.py
similarity index 83%
rename from bob/measure/script/measure.py
rename to src/bob/measure/script/measure.py
index c18ec36329b8ec9a54e03f9b31ea28f66ffc8375..02eadf3762805ca6d9845ca2813ea9da25eee694 100644
--- a/bob/measure/script/measure.py
+++ b/src/bob/measure/script/measure.py
@@ -4,8 +4,7 @@ import click
 import pkg_resources
 
 from click_plugins import with_plugins
-
-from bob.extension.scripts.click_helper import AliasedGroup
+from exposed.click import AliasedGroup
 
 
 @with_plugins(pkg_resources.iter_entry_points("bob.measure.cli"))
diff --git a/bob/measure/utils.py b/src/bob/measure/utils.py
similarity index 100%
rename from bob/measure/utils.py
rename to src/bob/measure/utils.py
diff --git a/bob/measure/data/dev-1.txt b/tests/data/dev-1.txt
similarity index 100%
rename from bob/measure/data/dev-1.txt
rename to tests/data/dev-1.txt
diff --git a/bob/measure/data/dev-2.txt b/tests/data/dev-2.txt
similarity index 100%
rename from bob/measure/data/dev-2.txt
rename to tests/data/dev-2.txt
diff --git a/bob/measure/data/linsep-negatives.hdf5 b/tests/data/linsep-negatives.hdf5
similarity index 100%
rename from bob/measure/data/linsep-negatives.hdf5
rename to tests/data/linsep-negatives.hdf5
diff --git a/bob/measure/data/linsep-positives.hdf5 b/tests/data/linsep-positives.hdf5
similarity index 100%
rename from bob/measure/data/linsep-positives.hdf5
rename to tests/data/linsep-positives.hdf5
diff --git a/bob/measure/data/make-examples.py b/tests/data/make-examples.py
similarity index 100%
rename from bob/measure/data/make-examples.py
rename to tests/data/make-examples.py
diff --git a/bob/measure/data/nonsep-det.hdf5 b/tests/data/nonsep-det.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-det.hdf5
rename to tests/data/nonsep-det.hdf5
diff --git a/bob/measure/data/nonsep-epc.hdf5 b/tests/data/nonsep-epc.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-epc.hdf5
rename to tests/data/nonsep-epc.hdf5
diff --git a/bob/measure/data/nonsep-negatives.hdf5 b/tests/data/nonsep-negatives.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-negatives.hdf5
rename to tests/data/nonsep-negatives.hdf5
diff --git a/bob/measure/data/nonsep-positives.hdf5 b/tests/data/nonsep-positives.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-positives.hdf5
rename to tests/data/nonsep-positives.hdf5
diff --git a/bob/measure/data/nonsep-precisionrecall.hdf5 b/tests/data/nonsep-precisionrecall.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-precisionrecall.hdf5
rename to tests/data/nonsep-precisionrecall.hdf5
diff --git a/bob/measure/data/nonsep-roc.hdf5 b/tests/data/nonsep-roc.hdf5
similarity index 100%
rename from bob/measure/data/nonsep-roc.hdf5
rename to tests/data/nonsep-roc.hdf5
diff --git a/bob/measure/data/test-1.txt b/tests/data/test-1.txt
similarity index 100%
rename from bob/measure/data/test-1.txt
rename to tests/data/test-1.txt
diff --git a/bob/measure/data/test-2.txt b/tests/data/test-2.txt
similarity index 100%
rename from bob/measure/data/test-2.txt
rename to tests/data/test-2.txt
diff --git a/bob/measure/data/test-cmc.hdf5 b/tests/data/test-cmc.hdf5
similarity index 100%
rename from bob/measure/data/test-cmc.hdf5
rename to tests/data/test-cmc.hdf5
diff --git a/bob/measure/data/test0-open-set.hdf5 b/tests/data/test0-open-set.hdf5
similarity index 100%
rename from bob/measure/data/test0-open-set.hdf5
rename to tests/data/test0-open-set.hdf5
diff --git a/bob/measure/data/test1-open-set.hdf5 b/tests/data/test1-open-set.hdf5
similarity index 100%
rename from bob/measure/data/test1-open-set.hdf5
rename to tests/data/test1-open-set.hdf5
diff --git a/bob/measure/data/test2-open-set.hdf5 b/tests/data/test2-open-set.hdf5
similarity index 100%
rename from bob/measure/data/test2-open-set.hdf5
rename to tests/data/test2-open-set.hdf5
diff --git a/bob/measure/data/test_m1.txt b/tests/data/test_m1.txt
similarity index 100%
rename from bob/measure/data/test_m1.txt
rename to tests/data/test_m1.txt
diff --git a/bob/measure/data/test_m2.txt b/tests/data/test_m2.txt
similarity index 100%
rename from bob/measure/data/test_m2.txt
rename to tests/data/test_m2.txt
diff --git a/bob/measure/data/two-cols.hdf5 b/tests/data/two-cols.hdf5
similarity index 100%
rename from bob/measure/data/two-cols.hdf5
rename to tests/data/two-cols.hdf5
diff --git a/bob/measure/test_error.py b/tests/test_error.py
similarity index 99%
rename from bob/measure/test_error.py
rename to tests/test_error.py
index 43d011214c8bae170680b7bd4c1a18d49f02454e..98b2767e6a6cf2d2993496460be54403ff90ed5f 100644
--- a/bob/measure/test_error.py
+++ b/tests/test_error.py
@@ -12,7 +12,7 @@ import numpy
 import pkg_resources
 import pytest
 
-from . import (
+from bob.measure import (
     calibration,
     cmc,
     correctly_classified_negatives,
diff --git a/bob/measure/test_load.py b/tests/test_load.py
similarity index 96%
rename from bob/measure/test_load.py
rename to tests/test_load.py
index fe2cdd21c92cc5eb3ef5b7ac654a941964edd5f4..4329ba6daed899e9ce5c5cc7d17cf580d364867c 100644
--- a/bob/measure/test_load.py
+++ b/tests/test_load.py
@@ -9,7 +9,7 @@ import h5py
 import numpy
 import pkg_resources
 
-from . import load
+from bob.measure import load
 
 
 def _F(f):
diff --git a/bob/measure/test_script.py b/tests/test_script.py
similarity index 98%
rename from bob/measure/test_script.py
rename to tests/test_script.py
index 15580ca312247ced054216b9b7bff9453888df0a..a4e0ef3f9b99c903d1393c877bf4c45128636bfa 100644
--- a/bob/measure/test_script.py
+++ b/tests/test_script.py
@@ -7,9 +7,8 @@ import pkg_resources
 
 from click.testing import CliRunner
 
-from bob.extension.scripts.click_helper import assert_click_runner_result
-
-from .script import commands
+from bob.io.base.testing_utils import assert_click_runner_result
+from bob.measure.script import commands
 
 
 def _F(f):
diff --git a/bob/measure/test_utils.py b/tests/test_utils.py
similarity index 88%
rename from bob/measure/test_utils.py
rename to tests/test_utils.py
index 1c10886d31314362b83fc88dcd9fac7d13fe8dd3..f7eb26d2154eea1bc5779b132c20fafef5319ff5 100644
--- a/bob/measure/test_utils.py
+++ b/tests/test_utils.py
@@ -3,7 +3,7 @@
 
 import numpy
 
-from .utils import confidence_for_indicator_variable
+from bob.measure.utils import confidence_for_indicator_variable
 
 
 def test_confidence_interval():
diff --git a/version.txt b/version.txt
deleted file mode 100644
index eacbabb50dce45cd154e6bb9285bc1542e662ea9..0000000000000000000000000000000000000000
--- a/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-6.0.3b0