setup.py 9.05 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/env python3
#
#  Copyright (C) 2016 Codethink Limited
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU Lesser General Public
#  License as published by the Free Software Foundation; either
#  version 2 of the License, or (at your option) any later version.
#
#  This library is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
#  Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public
#  License along with this library. If not, see <http://www.gnu.org/licenses/>.
#
#  Authors:
#        Tristan Van Berkom <tristan.vanberkom@codethink.co.uk>
20

21 22
import os
import shutil
23
import subprocess
24
import sys
25
import versioneer
26

27 28
if sys.version_info[0] != 3 or sys.version_info[1] < 4:
    print("BuildStream requires Python >= 3.4")
29 30
    sys.exit(1)

31 32
try:
    from setuptools import setup, find_packages
33
    from setuptools.command.easy_install import ScriptWriter
34 35 36 37 38
except ImportError:
    print("BuildStream requires setuptools in order to build. Install it using"
          " your package manager (usually python3-setuptools) or via pip (pip3"
          " install setuptools).")
    sys.exit(1)
39

40

41 42 43
##################################################################
# Bubblewrap requirements
##################################################################
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
REQUIRED_BWRAP_MAJOR = 0
REQUIRED_BWRAP_MINOR = 1
REQUIRED_BWRAP_PATCH = 2


def exit_bwrap(reason):
    print(reason +
          "\nBuildStream requires Bubblewrap (bwrap) for"
          " sandboxing the build environment. Install it using your package manager"
          " (usually bwrap or bubblewrap)")
    sys.exit(1)


def bwrap_too_old(major, minor, patch):
    if major < REQUIRED_BWRAP_MAJOR:
        return True
    elif major == REQUIRED_BWRAP_MAJOR:
        if minor < REQUIRED_BWRAP_MINOR:
            return True
        elif minor == REQUIRED_BWRAP_MINOR:
            return patch < REQUIRED_BWRAP_PATCH
        else:
            return False
    else:
        return False


71 72 73 74 75
def assert_bwrap():
    platform = os.environ.get('BST_FORCE_BACKEND', '') or sys.platform
    if platform.startswith('linux'):
        bwrap_path = shutil.which('bwrap')
        if not bwrap_path:
76 77 78 79 80 81 82
            exit_bwrap("Bubblewrap not found")

        version_bytes = subprocess.check_output([bwrap_path, "--version"]).split()[1]
        version_string = str(version_bytes, "utf-8")
        major, minor, patch = map(int, version_string.split("."))
        if bwrap_too_old(major, minor, patch):
            exit_bwrap("Bubblewrap too old")
83 84


85
##################################################################
86
# OSTree version requirements
87
##################################################################
88 89 90 91
REQUIRED_OSTREE_YEAR = 2017
REQUIRED_OSTREE_RELEASE = 8


92
def exit_ostree(reason):
93 94 95
    print(reason +
          "\nBuildStream requires OSTree >= v{}.{} with Python bindings. "
          .format(REQUIRED_OSTREE_YEAR, REQUIRED_OSTREE_RELEASE) +
96 97 98
          "Install it using your package manager (usually ostree or gir1.2-ostree-1.0).")
    sys.exit(1)

99 100 101 102 103 104 105 106 107 108 109 110 111 112

def assert_ostree_version():
    platform = os.environ.get('BST_FORCE_BACKEND', '') or sys.platform
    if platform.startswith('linux'):
        try:
            import gi
        except ImportError:
            print("BuildStream requires PyGObject (aka PyGI). Install it using"
                  " your package manager (usually pygobject3 or python-gi).")
            sys.exit(1)

        try:
            gi.require_version('OSTree', '1.0')
            from gi.repository import OSTree
113
        except ValueError:
114 115 116 117 118 119 120 121 122 123
            exit_ostree("OSTree not found")

        try:
            if OSTree.YEAR_VERSION < REQUIRED_OSTREE_YEAR or \
               (OSTree.YEAR_VERSION == REQUIRED_OSTREE_YEAR and
                OSTree.RELEASE_VERSION < REQUIRED_OSTREE_RELEASE):
                exit_ostree("OSTree v{}.{} is too old."
                            .format(OSTree.YEAR_VERSION, OSTree.RELEASE_VERSION))
        except AttributeError:
            exit_ostree("OSTree is too old.")
124

125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
###########################################
# List the pre-built man pages to install #
###########################################
#
# Man pages are automatically generated however it was too difficult
# to integrate with setuptools as a step of the build (FIXME !).
#
# To update the man pages in tree before a release, you need to
# ensure you have the 'click_man' package installed, and run:
#
# python3 setup.py --command-packages=click_man.commands man_pages
#
# Then commit the result.
#
def list_man_pages():
    bst_dir = os.path.dirname(os.path.abspath(__file__))
    man_dir = os.path.join(bst_dir, 'man')
    man_pages = os.listdir(man_dir)
    return [os.path.join('man', page) for page in man_pages]


147
#####################################################
148
#                Conditional Checks                 #
149
#####################################################
150 151 152 153 154
#
# Because setuptools... there is no way to pass an option to
# the setup.py explicitly at install time.
#
# So screw it, lets just use an env var.
155 156 157 158 159 160
bst_install_entry_points = {
    'console_scripts': [
        'bst-artifact-receive = buildstream._artifactcache.pushreceive:receive_main'
    ],
}

161 162 163 164 165
if not os.environ.get('BST_ARTIFACTS_ONLY', ''):
    assert_bwrap()
    assert_ostree_version()
    bst_install_entry_points['console_scripts'] += [
        'bst = buildstream._frontend:cli'
166 167
    ]

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
#####################################################
#    Monkey-patching setuptools for performance     #
#####################################################
#
# The template of easy_install.ScriptWriter is inefficient in our case as it
# imports pkg_resources. Patching the template only doesn't work because of the
# old string formatting used (%). This forces us to overwrite the class function
# as well.
#
# The patch was inspired from https://github.com/ninjaaron/fast-entry_points
# which we believe was also inspired from the code from `setuptools` project.
TEMPLATE = '''\
# -*- coding: utf-8 -*-
import sys

from {0} import {1}

if __name__ == '__main__':
    sys.exit({2}())'''


189 190 191 192
# Modify the get_args() function of the ScriptWriter class
# Note: the pylint no-member warning has been disabled as the functions: get_header(),
# ensure_safe_name() and _get_script_args() are all members of this class.
# pylint: disable=no-member
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
@classmethod
def get_args(cls, dist, header=None):
    if header is None:
        header = cls.get_header()
    for name, ep in dist.get_entry_map('console_scripts').items():
        cls._ensure_safe_name(name)
        script_text = TEMPLATE.format(ep.module_name, ep.attrs[0], '.'.join(ep.attrs))
        args = cls._get_script_args('console', name, header, script_text)
        for res in args:
            yield res


ScriptWriter.get_args = get_args


208 209 210
#####################################################
#             Main setup() Invocation               #
#####################################################
211
setup(name='BuildStream',
212 213 214 215
      # Use versioneer
      version=versioneer.get_version(),
      cmdclass=versioneer.get_cmdclass(),

216
      description='A framework for modelling build pipelines in YAML',
217
      license='LGPL',
218
      packages=find_packages(exclude=('tests', 'tests.*')),
Tristan Maat's avatar
Tristan Maat committed
219
      package_data={'buildstream': ['plugins/*/*.py', 'plugins/*/*.yaml',
220
                                    'data/*.yaml', 'data/*.sh.in']},
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
      data_files=[
          # This is a weak attempt to integrate with the user nicely,
          # installing things outside of the python package itself with pip is
          # not recommended, but there seems to be no standard structure for
          # addressing this; so just installing this here.
          #
          # These do not get installed in developer mode (`pip install --user -e .`)
          #
          # The completions are ignored by bash unless it happens to be installed
          # in the right directory; this is more like a weak statement that we
          # attempt to install bash completion scriptlet.
          #
          ('share/man/man1', list_man_pages()),
          ('share/bash-completion/completions', [
              os.path.join('buildstream', 'data', 'bst')
          ])
      ],
238
      install_requires=[
239
          'setuptools',
240
          'psutil',
241 242
          'ruamel.yaml',
          'pluginbase',
243
          'Click',
244
          'blessings',
245
          'jinja2 >= 2.10',
246
      ],
247
      entry_points=bst_install_entry_points,
248
      setup_requires=['pytest-runner'],
249
      tests_require=['pep8',
250 251
                     # Pin coverage to 4.2 for now, we're experiencing
                     # random crashes with 4.4.2
252
                     'coverage == 4.4.0',
253
                     'pytest-datafiles',
254
                     'pytest-env',
255
                     'pytest-pep8',
256
                     'pytest-pylint',
257
                     'pytest-cov',
258 259
                     # Provide option to run tests in parallel, less reliable
                     'pytest-xdist',
260
                     'pytest >= 3.1.0'],
261
      zip_safe=False)