diff --git a/bin/CMakeLists.txt b/bin/CMakeLists.txt
index 5a086bfd2d2aad611d411a2c15767c5951f050af..04ab0d0fd6544b37902e922385ebe26f7cb488e9 100644
--- a/bin/CMakeLists.txt
+++ b/bin/CMakeLists.txt
@@ -2,6 +2,7 @@ install(PROGRAMS
   dune-ctest
   duneproject
   dunecontrol
+  dunepackaging.py
   dune-git-whitespace-hook
   rmgenerated.py
   setup-dunepy.py
diff --git a/bin/dunepackaging.py b/bin/dunepackaging.py
new file mode 100755
index 0000000000000000000000000000000000000000..ac038c4494b7a635dea3db9a9fe862f28f93c916
--- /dev/null
+++ b/bin/dunepackaging.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+
+import sys, os, io, getopt, re
+import importlib, subprocess
+import email.utils
+import pkg_resources
+from datetime import date
+
+from dune.common.module import Version, VersionRequirement, Description
+class Data:
+    def __init__(self):
+        description = Description('dune.module')
+        self.name = description.name
+        self.version = str(description.version)
+        self.author_email = description.maintainer[1]
+        self.author = description.author or self.author_email
+        self.description = description.description
+        self.url = description.url
+        self.dune_dependencies = [
+                (dep[0]+str(dep[1])).replace("("," ").replace(")","")
+                for dep in description.depends
+             ]
+
+        self.install_requires = []
+        try:
+            with open('python/setup.py.in', 'r') as setuppyinfile:
+                content = setuppyinfile.read()
+                if content.find('install_requires'):
+                    bracket = content.split('install_requires')[1].split('[')[1].split(']')[0]
+                    self.install_requires = [r.strip('\'"') for r in bracket.split(',')]
+        except FileNotFoundError:
+            pass
+
+def main(argv):
+
+    repositories = ["gitlab", "testpypi", "pypi"]
+    def usage():
+        return 'usage: dunepackaging.py [--upload <'+"|".join(repositories)+'> | -c | --clean | --version 1.0.DATE>]'
+
+    try:
+        opts, args = getopt.getopt(argv, "hc", ["upload=", "clean", "version="])
+    except getopt.GetoptError:
+        print(usage())
+        sys.exit(2)
+
+    sdist = True
+    upload = False
+    repository = "gitlab"
+    clean = False
+    version = None
+    for opt, arg in opts:
+        if opt == '-h':
+            print(usage())
+            sys.exit(2)
+        elif opt in ("--upload"):
+            upload = True
+            if arg != '':
+                repository = arg
+                if repository not in repositories:
+                    print("Specified repository must be one of: " + " ".join(repositories))
+                    sys.exit(2)
+        elif opt in ("-c", "--clean"):
+            clean = True
+        elif opt in ("--version"):
+            version = arg
+
+    # Remove generated files
+    def removeFiles():
+        import glob
+        files = ['setup.py', 'MANIFEST', 'pyproject.toml', 'dist', '_skbuild', '__pycache__']
+        print("Remove generated files: " + ", ".join(files))
+        remove = ['rm', '-rf'] + files
+        subprocess.call(remove)
+
+    if clean:
+        removeFiles()
+        if not upload:
+            sys.exit(2)
+
+    data = Data()
+
+    # defaults
+    if not hasattr(data, 'dune_dependencies'):
+        data.dune_dependencies = []
+
+    if not hasattr(data, 'install_requires'):
+        data.install_requires = []
+
+    # if no version parameter specified, append DATE to version number in package.py
+    if version is None:
+        if not hasattr(data, 'version'):
+            print("No version number specified!")
+            sys.exit(2)
+        version = data.version + '.devDATE'
+
+    # version - replacing "DATE" with yearmonthday string
+    t = date.today()
+    today = t.strftime('%Y%m%d')
+    data.version = version.replace("DATE",today)
+
+    # Generate setup.py
+    print("Generate setup.py")
+    setuppy = '''\
+import sys, os
+from setuptools import find_packages
+from skbuild import setup
+
+with open("README.md", "r") as fh:
+    long_description = fh.read()
+'''
+    setuppy += '''
+setup(
+'''
+    setuppy += '    name="'+data.name+'",\n'
+    setuppy += '    version="'+data.version+'",\n'
+    setuppy += '    author="'+data.author+'",\n'
+    setuppy += '    author_email="'+data.author_email+'",\n'
+    setuppy += '    description="'+data.description+'",\n'
+    setuppy += '    long_description=long_description,\n'
+    setuppy += '    long_description_content_type="text/markdown",\n'
+    if data.url is not None:
+        setuppy += '    url="'+data.url+'",\n'
+    setuppy += '    packages=find_packages(where="python"),\n'
+    setuppy += '    package_dir={"": "python"},\n'
+    setuppy += '    install_requires='+(data.install_requires+data.dune_dependencies).__str__()+','
+    setuppy += '''
+    classifiers=[
+        "Programming Language :: C++",
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: GNU General Public License (GPL)",
+    ],
+    python_requires='>=3.4',
+    cmake_args=[
+        '-DBUILD_SHARED_LIBS=TRUE',
+        '-DDUNE_ENABLE_PYTHONBINDINGS=TRUE',
+        '-DDUNE_PYTHON_INSTALL_LOCATION=none',
+        '-DDUNE_GRID_GRIDTYPE_SELECTOR=ON',
+        '-DALLOW_CXXFLAGS_OVERWRITE=ON',
+        '-DUSE_PTHREADS=ON',
+        '-DCMAKE_BUILD_TYPE=Release',
+        '-DCMAKE_DISABLE_FIND_PACKAGE_LATEX=TRUE',
+        '-DCMAKE_DISABLE_DOCUMENTATION=TRUE',
+        '-DINKSCAPE=FALSE',
+        '-DCMAKE_INSTALL_RPATH='+sys.prefix+'/lib/',
+        '-DCMAKE_MACOSX_RPATH=TRUE',
+    ]
+)
+'''
+    f = open("setup.py", "w")
+    f.write(setuppy)
+    f.close()
+
+    # Generate pyproject.toml
+    print("Generate pyproject.toml")
+    f = open("pyproject.toml", "w")
+    requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja"]
+    requires += data.dune_dependencies
+    f.write("[build-system]\n")
+    f.write("requires = "+requires.__str__()+"\n")
+    f.write("build-backend = 'setuptools.build_meta'\n")
+    f.close()
+
+    # Generate MANIFEST
+    with open('MANIFEST', 'wb') as manifest_file:
+        manifest_file.write("setup.py\n".encode())
+        manifest_file.write("pyproject.toml\n".encode())
+        manifest_file.write(
+             subprocess.check_output(['git', 'ls-files'])
+        )
+
+    # Create source distribution
+    python = sys.executable
+    if sdist:
+        print("Remove dist")
+        remove = ['rm', '-rf', 'dist']
+        subprocess.call(remove)
+
+        # check if we have scikit-build
+        import pkg_resources
+        installed = {pkg.key for pkg in pkg_resources.working_set}
+        if not 'scikit-build' in installed:
+            print("Please install the pip package 'scikit-build' to build the source distribution.")
+            sys.exit(2)
+
+        print("Create source distribution")
+        build = [python, 'setup.py', 'sdist']
+        subprocess.call(build, stdout=subprocess.DEVNULL)
+
+    # Upload to repository
+    if upload:
+        # check if we have twine
+        import pkg_resources
+        installed = {pkg.key for pkg in pkg_resources.working_set}
+        if not 'twine' in installed:
+            print("Please install the pip package 'twine' to upload the source distribution.")
+            sys.exit(2)
+
+        twine = [python, '-m', 'twine', 'upload']
+        twine += ['--repository', repository]
+        twine += ['dist/*']
+        subprocess.call(twine)
+
+        removeFiles()
+
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/dune-common.pc.in b/dune-common.pc.in
index 5965364236baa4a5bc05d5a7a0a15c0b69f62cf4..d78094be3c36a05d3809dda5283774503a429ec5 100644
--- a/dune-common.pc.in
+++ b/dune-common.pc.in
@@ -8,8 +8,8 @@ DEPENDENCIES=@REQUIRES@
 
 Name: @PACKAGE_NAME@
 Version: @VERSION@
-Description: Dune (Distributed and Unified Numerics Environment) common module
-URL: http://dune-project.org/
+Description: @DESCRIPTION@
+URL: @URL@
 Requires: ${DEPENDENCIES}
 Libs: -L${libdir} -ldunecommon
 Cflags: -I${includedir}
diff --git a/dune.module b/dune.module
index f3123c523ae793361f029feba369e7ec2ae2dcdd..e5e93cdb608fa74da1141b6ad749d196bcf89c34 100644
--- a/dune.module
+++ b/dune.module
@@ -1,4 +1,7 @@
 Module: dune-common
 Version: 2.8-git
+Author: The Dune Core developers
 Maintainer: dune-devel@lists.dune-project.org
+Description: Basis infrastructure for all Dune modules
+URL: https://gitlab.dune-project.org/core/dune-common
 Whitespace-Hook: Yes
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index c7e614dd0b6af764bbf6eb57b125b00269216068..0000000000000000000000000000000000000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,3 +0,0 @@
-[build-system]
-requires = ["setuptools", "wheel", "scikit-build", "cmake"]
-build-backend = "setuptools.build_meta"
diff --git a/python/dune/common/module.py b/python/dune/common/module.py
index 8ea0730dc83c1f12ec6af0822ac99c96c170534b..7dfce77beab7e1d5a59f6843bdbebfbe9fcdac59 100644
--- a/python/dune/common/module.py
+++ b/python/dune/common/module.py
@@ -144,6 +144,21 @@ class Description:
         except KeyError:
             self.maintainer = None
 
+        try:
+            self.author = data['author']
+        except KeyError:
+            self.author = None
+
+        try:
+            self.description = data['description']
+        except KeyError:
+            self.description = ''
+
+        try:
+            self.url = data['url']
+        except KeyError:
+            self.url = None
+
         try:
             wshook = data['whitespace-hook'].lower()
             if wshook == 'yes':
@@ -180,6 +195,12 @@ class Description:
         s += 'Version:         ' + str(self.version) + '\n'
         if self.maintainer is not None:
             s += 'Maintainer:      ' + email.utils.formataddr(self.maintainer) + '\n'
+        if self.author is not None:
+            s += 'Author:          ' + self.author + '\n'
+        if self.description is not '':
+            s += 'Description:     ' + self.description + '\n'
+        if self.url is not None:
+            s += 'URL:             ' + self.url + '\n'
         if self.whitespace_hook is not None:
             s += 'Whitespace-Hook: ' + ('Yes' if self.whitespace_hook else 'No') + '\n'
 
diff --git a/python/setup.py.in b/python/setup.py.in
index e52ec0c261d4f644687a3922fd73777020a7c20b..6d5d6f1e6e6eaec3b458795db9c4f3782c17c2c3 100644
--- a/python/setup.py.in
+++ b/python/setup.py.in
@@ -1,6 +1,6 @@
 from setuptools import setup, find_packages
 
-setup(name="dune.common",
+setup(name="dune-common",
     namespace_packages=['dune'],
     description="Python lib for dune",
     version="${DUNE_COMMON_VERSION}",
diff --git a/setup.py b/setup.py
deleted file mode 100644
index c2dfb392b60f1f422671357f62b7e0b55c178166..0000000000000000000000000000000000000000
--- a/setup.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import sys, os
-from setuptools import find_packages
-
-try:
-    from skbuild import setup
-except ImportError:
-    print('Please update pip, you need pip 10 or greater,\n'
-          ' or you need to install the PEP 518 requirements in pyproject.toml yourself', file=sys.stderr)
-    raise
-
-with open("README.md", "r") as fh:
-    long_description = fh.read()
-
-setup(
-    name="dune-common",
-    version="2.8.20201123",
-    author="The Dune Core developers",
-    author_email="dune@lists.dune-project.org",
-    description="""Basis infrastructure classes for all Dune modules""",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    url="https://gitlab.dune-project.org/core/dune-common",
-    packages=find_packages(where="python"),
-    package_dir={"": "python"},
-    install_requires=['numpy', 'mpi4py'],
-    cmake_args=['-DBUILD_SHARED_LIBS=TRUE',
-                '-DDUNE_ENABLE_PYTHONBINDINGS=TRUE',
-                '-DDUNE_PYTHON_INSTALL_LOCATION=none',
-                '-DDUNE_GRID_GRIDTYPE_SELECTOR=ON',
-                '-DALLOW_CXXFLAGS_OVERWRITE=ON',
-                '-DUSE_PTHREADS=ON',
-                '-DCMAKE_BUILD_TYPE=Release',
-                '-DCMAKE_DISABLE_FIND_PACKAGE_LATEX=TRUE',
-                '-DCMAKE_DISABLE_DOCUMENTATION=TRUE',
-                '-DCMAKE_INSTALL_RPATH='+sys.prefix+'/lib/']
-)