# .travis.yml based on
# https://github.com/DRMacIver/hypothesis/blob/master/.travis.yml
# Setting the language to C overrides our CC environment variable
# and disables ccache.
# Setting it to python overrides our TRAVIS_PYTHON_VERSION (which
# we're only using in hopes of being able to go *back* to using
# python: one day so making the migration simpler). "shell" and
# "minimal" are aliases.
language: shell
dist: xenial
group: travis_latest
os:
  - linux
  - osx
osx_image: xcode11.4


env:
    global:
        - BUILD_RUNTIMES=$HOME/.runtimes
        - PYTHONHASHSEED=8675309
        - PYTHONUNBUFFERED=1
        - PYTHONDONTWRITEBYTECODE=1
        - PIP_UPGRADE_STRATEGY=eager
        # Don't get warnings about Python 2 support being deprecated. We
        # know. The env var works for pip 20.
        - PIP_NO_PYTHON_VERSION_WARNING=1
        - PIP_NO_WARN_SCRIPT_LOCATION=1
        - CC="ccache gcc"
        - CCACHE_NOCPP2=true
        - CCACHE_SLOPPINESS=file_macro,time_macros,include_file_ctime,include_file_mtime
        - CCACHE_NOHASHDIR=true
        - BUILD_LIBS=$HOME/.libs
        - GEVENTSETUP_EV_VERIFY=2
        # Disable some warnings produced by libev especially and also some Cython generated code.
        # Note that changing the value of these variables invalidates configure caches
        - CFLAGS="-Ofast -pipe -Wno-strict-aliasing -Wno-comment"
        - CPPFLAGS="-I$BUILD_LIBS/include -DEV_VERIFY=3"
        - LDFLAGS="-L$BUILD_LIBS/lib"
        - LD_LIBRARY_PATH="$BUILD_LIBS/lib"
        # Uploading built wheels for releases.
        # TWINE_PASSWORD is encrypted and stored directly in the
        # travis repo settings.
        - TWINE_USERNAME="__token__"

    # Note that this list is again *manually* expanded
    # for the 'build-gevent' stage. The benefit of doing this (instead of
    # only listing a single version here, or using a different 'language')
    # is that we get separate caches per version. This keeps them small,
    # and prevents stepping on each other when we change Python minor
    # versions.

    # We do it here, rather than with the python: keyword
    # because the way travis handles pypy is pretty messed up.

    # Recall that unless otherwise specified, jobs inherit
    # the first entry in this list.
    jobs:
      - TRAVIS_PYTHON_VERSION=2.7
      - TRAVIS_PYTHON_VERSION=3.5
      - TRAVIS_PYTHON_VERSION=3.6
      - TRAVIS_PYTHON_VERSION=3.7
      - TRAVIS_PYTHON_VERSION=3.8
      - TRAVIS_PYTHON_VERSION=pypy2.7
      - TRAVIS_PYTHON_VERSION=pypy3.6
      - TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3

cache:
  pip: true
  directories:
    - $HOME/.venv
    - $HOME/.runtimes
    - $HOME/.wheelhouse
    - $HOME/.ccache
    - $BUILD_LIBS
    - $HOME/Library/Caches/pip

before_cache:
    - rm -f $HOME/.cache/pip/log/debug.log
    # Store the configure caches. Having a cache can speed up c-ares
    # configure from 2-3 minutes to 20 seconds.
    - mkdir -p $BUILD_LIBS
    - if [ -f deps/c-ares/config.cache ]; then cp deps/c-ares/config.cache $BUILD_LIBS/config.cache.ares ; fi
    - if [ -f deps/libev/config.cache ];  then cp deps/libev/config.cache $BUILD_LIBS/config.cache.libev ; fi
    - if [ -f deps/libuv/config.cache ];  then cp deps/libuv/config.cache $BUILD_LIBS/config.cache.libuv ; fi

before_install:
  - export PATH=$BUILD_RUNTIMES/snakepit/$TRAVIS_PYTHON_VERSION.d/bin:$PATH
  - export G_SITE=$BUILD_RUNTIMES/snakepit/$TRAVIS_PYTHON_VERSION.d/lib/*/site-packages/
  # Restore the configure caches
  - if [ -f $BUILD_LIBS/config.cache.ares ]; then cp $BUILD_LIBS/config.cache.ares deps/c-ares/config.cache ; fi
  - if [ -f $BUILD_LIBS/config.cache.libev ]; then cp $BUILD_LIBS/config.cache.libev deps/libev/config.cache ; fi
  - if [ -f $BUILD_LIBS/config.cache.libuv ]; then cp $BUILD_LIBS/config.cache.libuv deps/libuv/config.cache ; fi
  - |
    if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
      brew install ccache
      export PATH="/usr/local/opt/ccache/libexec:$PATH"
      export CFLAGS="$CFLAGS -Wno-parentheses-equality"
    fi

before_script:
  # Show some details of interest
  - |
    python --version
    python -c 'import greenlet; print(greenlet, greenlet.__version__)'
    python -c 'import gevent.core; print(gevent.core.loop)'
    python -c 'import gevent.ares; print(gevent.ares)'


# Installing is taken care of by the first stage.
install:
  - ls -l $BUILD_RUNTIMES/snakepit/
  - echo $BUILD_RUNTIMES/snakepit/$TRAVIS_PYTHON_VERSION.d
  - ls -l $BUILD_RUNTIMES/snakepit/$TRAVIS_PYTHON_VERSION.d
  - python -c 'import gevent; print(gevent.__version__)'
  - python -c 'from gevent._compat import get_clock_info; print(get_clock_info("perf_counter"))'
script:
  # The generic script for the matrix expansion is to
  # test the defaults.
  - &test-basic python -m gevent.tests

  # Now, the non-default threaded file object.
  # In the past, we included all test files that had a reference to 'subprocess'' somewhere in their
  # text. The monkey-patched stdlib tests were specifically included here.
  # However, we now always also test on AppVeyor (Windows) which only has GEVENT_FILE=thread,
  # so we can save a lot of CI time by reducing the set and excluding the stdlib tests without
  # losing any coverage. Which is good, because coverage fails when run this way.
  - (cd src/gevent/tests && GEVENT_FILE=thread python -mgevent.tests test__*subprocess*.py)


# Submit coverage info
after_success:
  - python -m coverage combine || true
  - python -m coverage report -i || true
  - python -m coveralls || true
  - |
    if [[ -n "$DOCKER_IMAGE" ]]; then
      ls -l wheelhouse
      twine check wheelhouse/*
      if [[ $TRAVIS_TAG ]]; then
        twine upload --skip-existing wheelhouse/*
      fi
    fi


stages:
  - build-gevent
  - test


jobs:
  fast_finish: true
  exclude:
    - os: osx
      env: TRAVIS_PYTHON_VERSION=3.5
    - os: osx
      env: TRAVIS_PYTHON_VERSION=pypy2.7
    - os: osx
      env: TRAVIS_PYTHON_VERSION=pypy3.6
    - os: osx
      env: TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3

  include:
    - &build-gevent
      stage: build-gevent
      install:
        # Install the Python runtime
        - &build-gevent-python time ./scripts/install.sh $TRAVIS_PYTHON_VERSION
        # Install gevent. Yes, this will create different files each time,
        # leading to a fresh cache. But because of CCache stats, we had already been doing
        # that (before we learned about CCACHE_NOSTATS).
        # We don't install using the requirements file for speed (reduced deps) and because an editable
        # install doesn't work in the cache.
        # First, the build dependencies (see setup.cfg)
        # so that we don't have to use build isolation and can better use the cache;
        # Note that we can't use -U for cffi and greenlet on PyPy.
        - &build-gevent-deps pip install -U setuptools wheel twine && pip install -U 'faulthandler; python_version == "2.7" and platform_python_implementation == "CPython"' 'cffi;platform_python_implementation=="CPython"' 'cython<3' 'greenlet;platform_python_implementation=="CPython"'
        # Next, build the wheel *in place*. This helps ccache, and also lets us cache the configure
        # output (pip install uses a random temporary directory, making this difficult)
        - python setup.py bdist_wheel
        - ls -l dist
        - twine check dist/*
        - pip uninstall -y gevent
        - pip install -U --no-compile `ls dist/*whl`[test]
        - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then if [[ $TRAVIS_TAG ]]; then twine upload --skip-existing dist/*; fi; fi;
      script: ccache -s
      before_script: true
      after_success: true
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.8
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.5
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.6
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.7
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=pypy2.7
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=pypy3.6

    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=2.7
      os: osx
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.6
      os: osx
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.7
      os: osx
    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=3.8
      os: osx

    - <<: *build-gevent
      env: TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3
      install:
        # Install the Python runtime
        - *build-gevent-python
        - *build-gevent-deps
        # Install the C dependencies to a known location. This is used
        # to test 'no embed' cases. It might seem like it would prime
        # the CCache for when we *do* embed if we did it as part of
        # the generic build stage, but overall it just seems to slow
        # things down. The Travis caching and CCache is not working as
        # well as we would hope. (XXX: Probably because in the past we
        # used pip install which used temporary directories.)
        - pushd deps/libev && ./configure -C --prefix=$BUILD_LIBS && make install && popd
        - pushd deps/c-ares && ./configure -C --prefix=$BUILD_LIBS && make -j4 install && popd
        - autoconf --version
        - automake --version
        - ls -l deps/libuv/m4
        - pushd deps/libuv && ./autogen.sh && ./configure -C --disable-static --prefix=$BUILD_LIBS && make -j4 install && popd
        # libev builds a manpage each time, and it includes today's date, so it frequently changes.
        # delete to avoid repacking the archive
        - rm -rf $BUILD_LIBS/share/man/
        - ls -l $BUILD_LIBS $BUILD_LIBS/lib $BUILD_LIBS/include
        - python setup.py bdist_wheel
        - pip uninstall -y gevent
        - pip install -U `ls dist/*whl`[test]
        # Test that we're actually linking
        # to the .so file.
        - objdump -p $G_SITE/gevent/libev/_corecffi*so | grep "NEEDED.*libev.so"
        - objdump -p $G_SITE/gevent/libev/corecext*so | grep "NEEDED.*libev.so"
        - objdump -p $G_SITE/gevent/libuv/_corecffi*so | grep "NEEDED.*libuv.so"
      script:
        # Verify that we got non-embedded builds
        - python -c 'import gevent.libev.corecffi as CF; assert not CF.LIBEV_EMBED'
        - python -c 'import gevent.libuv.loop as CF; assert not CF.libuv.LIBUV_EMBED'

    # Ok, now we switch to the test stage. These are all in addition
    # to the jobs created by the matrix (and should override the
    # `script` command).

    # The manylinux builds and tests.
    # These take awhile, so get them started while others proceed in parallel.
    - stage: test
      name: 64-bit manylinux wheels (all Pythons)
      language: python
      services: docker
      env: DOCKER_IMAGE=quay.io/pypa/manylinux2010_x86_64
      install: docker pull $DOCKER_IMAGE
      script: bash scripts/releases/make-manylinux
      before_script: pip install -U twine

    - stage: test
      name: 32-bit manylinux wheels (all Pythons)
      language: python
      services: docker
      env: DOCKER_IMAGE=quay.io/pypa/manylinux2010_i686 PRE_CMD=linux32
      install: docker pull $DOCKER_IMAGE
      script: bash scripts/releases/make-manylinux
      before_script: pip install -U twine

    # Lint the code. Because this is a separate job, even if it fails fast
    # the tests will still run. Put it at the top for fast feedback.
    # We only need to do this on one version, and it should be Python 3, because
    # pylint has stopped updating for Python 2.
    - stage: test
      # We need pylint, since above we're not installing a
      # requirements file.
      install: pip install -U pylint
      script: python -m pylint --limit-inference-results=1 --rcfile=.pylintrc gevent
      env: TRAVIS_PYTHON_VERSION=3.7
      name: lint37

    # Now the various functional test groups.
    # We organize these by interpreter, rather than functional test group,
    # so that it's easy to see which interpreters are in which group.

    # First, default (cpython 2.7). This tests essentially all the groups.

    # Test the c-ares resolver. It's implemented in C, so don't bother with coverage.
    # Also, we don't support it on PyPy for production, so don't bother to test PyPy.
    - &test-ares-jobs
      stage: test
      script: GEVENT_RESOLVER=ares python -mgevent.tests --ignore tests_that_dont_use_resolver.txt
      name: ares27
    # Run dnspython with coverage enabled, it's implemented in python whereas ares is C.
    # PyPy is supported.
    - &test-dnspython-jobs
      script: GEVENT_RESOLVER=dnspython python -mgevent.tests --coverage --ignore tests_that_dont_use_resolver.txt
      name: dnspython27
    # Now test the alternate backends, starting with libuv-cffi, which should be present everywhere
    - &test-libuv-jobs
      script: GEVENT_LOOP=libuv python -mgevent.tests --coverage
      name: libuv27
      # Next the libev-cffi backend, which is only needed on CPython (default on PyPy)
    - &test-libev-jobs
      script: GEVENT_LOOP=libev-cffi python -mgevent.tests --coverage
      name: libev-cffi27
    # No compiled cython modules on CPython, using the default backend. Get coverage here.
    # We should only need to run this for a single Python 2 and a Python 3
    - &test-pure-jobs
      script: PURE_PYTHON=1 python -mgevent.tests --coverage
      name: pure27
    # Run the leaktests; this seems to be extremely slow on Python 3.7
    # XXX: Figure out why. Can we reproduce locally?
    - &test-leak-jobs
      script: GEVENTTEST_LEAKCHECK=1 python -m gevent.tests --ignore tests_that_dont_do_leakchecks.txt
      name: leak27

    # Now, in order of the matrix, tests for particular versions.

    # For the CPython interpreters, unless we have reason to expect
    # different behaviour across the versions (e.g., as measured by coverage)
    # it's sufficient to test the alternate backend library for non-current versions;
    # run the full suite on the current version.

    # 3.6
    - <<: *test-libuv-jobs
      env: TRAVIS_PYTHON_VERSION=3.6
      name: libuv36

    # 3.7
    - <<: *test-libuv-jobs
      env: TRAVIS_PYTHON_VERSION=3.7
      name: libuv37

    # 3.8
    - <<: *test-libuv-jobs
      env: TRAVIS_PYTHON_VERSION=3.8
      name: libuv38
    - <<: *test-libev-jobs
      env: TRAVIS_PYTHON_VERSION=3.8
      name: libev-cffi38
    - <<: *test-ares-jobs
      env: TRAVIS_PYTHON_VERSION=3.8
      name: c-ares resolver on Python 3.8
    - <<: *test-dnspython-jobs
      env: TRAVIS_PYTHON_VERSION=3.8
      name: dnspython resolver on Python 3.8
    - <<: *test-pure-jobs
      env: TRAVIS_PYTHON_VERSION=3.8
      name: pure-Python on Python 3.8

    # 2.7, no-embed. Run the tests that exercise the libraries we
    # linked to.
    - <<: *test-ares-jobs
      # This job exercises both the non-embedded ares resolver
      # and the non-embedded Cython libev loop.
      env: TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3
      name: ares27-noembed
    # These exercise the CFFI versions.
    - <<: *test-libuv-jobs
      env: TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3
      name: libuv27-noembed
    - <<: *test-libev-jobs
      env: TRAVIS_PYTHON_VERSION=2.7 GEVENTSETUP_EMBED=0 GEVENTSETUP_EV_VERIFY=3
      name: libev27-noembed

    # PyPy 2.7
    - <<: *test-dnspython-jobs
      env: TRAVIS_PYTHON_VERSION=pypy2.7
      name: dnspythonpypy27
    - <<: *test-libuv-jobs
      env: TRAVIS_PYTHON_VERSION=pypy2.7
      name: libuvpypy27

notifications:
  email: false
