diff --git a/.azure-pipelines/wheel-builder.yml b/.azure-pipelines/wheel-builder.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_LmF6dXJlLXBpcGVsaW5lcy93aGVlbC1idWlsZGVyLnltbA==
--- /dev/null
+++ b/.azure-pipelines/wheel-builder.yml
@@ -0,0 +1,160 @@
+trigger: none
+pr: none
+
+jobs:
+    - job: 'macOS'
+      pool:
+          vmImage: 'macOS-10.14'
+      strategy:
+          matrix:
+              Python27:
+                  python.version: '2.7'
+                  PYTHON_DOWNLOAD_URL: "https://www.python.org/ftp/python/2.7.16/python-2.7.16-macosx10.6.pkg"
+                  PYTHON_BIN_PATH: /Library/Frameworks/Python.framework/Versions/2.7/bin/python
+              Python3:
+                  python.version: '3.4'
+                  PYTHON_DOWNLOAD_URL: "https://www.python.org/ftp/python/3.7.3/python-3.7.3-macosx10.6.pkg"
+                  PYTHON_BIN_PATH: /Library/Frameworks/Python.framework/Versions/3.7/bin/python3
+      steps:
+          - script: |
+              set -e
+              set -x
+
+              curl "$PYTHON_DOWNLOAD_URL" -o python.pkg
+              sudo installer -pkg python.pkg -target /
+            displayName: Download and install Python
+
+          - script: $PYTHON_BIN_PATH -m pip install -U virtualenv
+            displayName: Install virtualenv
+          - script: $PYTHON_BIN_PATH -m virtualenv .venv
+            displayName: Create virtualenv
+          - script: .venv/bin/pip install -U wheel
+            displayName: Update wheel to the latest version
+          - script: .venv/bin/pip install cffi six
+            displayName: Install our Python dependencies
+
+          - script: |
+              set -e
+              set -x
+
+              REGEX="3\.([0-9])*"
+              if [[ "$PYTHON_VERSION" =~ $REGEX ]]; then
+                  PY_LIMITED_API="--build-option --py-limited-api=cp3${BASH_REMATCH[1]}"
+              fi
+
+              .venv/bin/pip wheel bcrypt --no-use-pep517 --wheel-dir=wheelhouse --no-binary bcrypt --no-deps $PY_LIMITED_API
+            displayName: Build the wheel
+          - script: .venv/bin/pip install --no-index -f wheelhouse bcrypt
+            displayName: Test installing the wheel
+          - script: |
+              .venv/bin/python -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
+            displayName: Test the installed wheel
+          - task: PublishBuildArtifacts@1
+            inputs:
+                pathToPublish: wheelhouse/
+                artifactName: bcrypt-macos-python$(python.version)
+
+    - job: 'manylinux1'
+      pool:
+          vmImage: 'ubuntu-16.04'
+      container: 'pyca/cryptography-manylinux1:x86_64'
+      strategy:
+          matrix:
+              Python27m:
+                  PYTHON_VERSION: 'cp27-cp27m'
+              Python27mu:
+                  PYTHON_VERSION: 'cp27-cp27mu'
+              Python3m:
+                  PYTHON_VERSION: 'cp34-cp34m'
+      steps:
+          - script: /opt/python/$PYTHON_VERSION/bin/python -m virtualenv .venv
+            displayName: Create virtualenv
+          - script: .venv/bin/pip install cffi six
+            displayName: Install our Python dependencies
+          - script: |
+              set -e
+              set -x
+
+              REGEX="cp3([0-9])*"
+              if [[ "$PYTHON_VERSION" =~ $REGEX ]]; then
+                  PY_LIMITED_API="--build-option --py-limited-api=cp3${BASH_REMATCH[1]}"
+              fi
+              .venv/bin/pip wheel bcrypt --no-use-pep517 --no-binary bcrypt --no-deps --wheel-dir=tmpwheelhouse $PY_LIMITED_API
+            displayName: Build the wheel
+          - script: auditwheel repair tmpwheelhouse/bcrypt*.whl -w wheelhouse/
+            displayName: Run auditwheel
+          - script: .venv/bin/pip install bcrypt --no-index -f wheelhouse/
+            displayName: Test installing the wheel
+          - script: |
+              .venv/bin/python -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
+            displayName: Test the installed wheel
+          - task: PublishBuildArtifacts@1
+            inputs:
+                pathToPublish: wheelhouse/
+                artifactName: bcrypt-manylinux1-$(PYTHON_VERSION)
+
+    - job: 'windows'
+      pool:
+          vmImage: 'windows-2019'
+      container: $[variables.containerImage]
+      strategy:
+          matrix:
+              Python27-x86:
+                  containerImage: 'pyca/cryptography-runner-windows:py27-x86'
+                  PYTHON_VERSION: '27'
+                  WINDOWS_ARCH: 'x86'
+              Python27-x86-64:
+                  containerImage: 'pyca/cryptography-runner-windows:py27-x86_64'
+                  PYTHON_VERSION: '27'
+                  WINDOWS_ARCH: 'x86_64'
+              Python34-x86:
+                  containerImage: 'pyca/cryptography-runner-windows:py34-x86'
+                  PYTHON_VERSION: '34'
+                  WINDOWS_ARCH: 'x86'
+              Python34-x86-64:
+                  containerImage: 'pyca/cryptography-runner-windows:py34-x86_64'
+                  PYTHON_VERSION: '34'
+                  WINDOWS_ARCH: 'x86_64'
+              Python35-x86:
+                  containerImage: 'pyca/cryptography-runner-windows:py35-x86'
+                  PYTHON_VERSION: '35'
+                  WINDOWS_ARCH: 'x86'
+              Python35-x86-64:
+                  containerImage: 'pyca/cryptography-runner-windows:py35-x86_64'
+                  PYTHON_VERSION: '35'
+                  WINDOWS_ARCH: 'x86_64'
+              Python36-x86:
+                  containerImage: 'pyca/cryptography-runner-windows:py3-x86'
+                  PYTHON_VERSION: '36'
+                  WINDOWS_ARCH: 'x86'
+              Python36-x86-64:
+                  containerImage: 'pyca/cryptography-runner-windows:py3-x86_64'
+                  PYTHON_VERSION: '36'
+                  WINDOWS_ARCH: 'x86_64'
+              Python37-x86:
+                  containerImage: 'pyca/cryptography-runner-windows:py3-x86'
+                  PYTHON_VERSION: '37'
+                  WINDOWS_ARCH: 'x86'
+              Python37-x86-64:
+                  containerImage: 'pyca/cryptography-runner-windows:py3-x86_64'
+                  PYTHON_VERSION: '37'
+                  WINDOWS_ARCH: 'x86_64'
+      steps:
+          - script: '"C:/Python%PYTHON_VERSION%/Scripts/pip" install wheel cffi six'
+            displayName: Install wheel and our Python dependencies
+          - script: |
+              C:/Python%PYTHON_VERSION%/Scripts/pip wheel bcrypt --no-use-pep517 --wheel-dir=wheelhouse --no-binary bcrypt
+            displayName: Build the wheel
+          - script: '"C:/Python%PYTHON_VERSION%/Scripts/pip" install -f wheelhouse bcrypt --no-index'
+            displayName: Test installing the wheel
+          - script: |
+              "C:/Python%PYTHON_VERSION%/python" -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
+            displayName: Test the installed wheel
+          - script: mkdir bcrypt-wheelhouse
+            displayName: Create a directory for placing the final wheel in
+          - script: move wheelhouse\bcrypt*.whl bcrypt-wheelhouse\
+            displayName: Move the bcrypt wheel into the final wheel house
+          - task: PublishBuildArtifacts@1
+            inputs:
+                pathToPublish: bcrypt-wheelhouse/
+                artifactName: bcrypt-windows-$(WINDOWS_ARCH)-python$(PYTHON_VERSION)
diff --git a/.jenkins/Jenkinsfile-wheel-builder b/.jenkins/Jenkinsfile-wheel-builder
deleted file mode 100644
index 08bdbdd29ce80b0282162360728760f600b7af49_LmplbmtpbnMvSmVua2luc2ZpbGUtd2hlZWwtYnVpbGRlcg==..0000000000000000000000000000000000000000
--- a/.jenkins/Jenkinsfile-wheel-builder
+++ /dev/null
@@ -1,152 +0,0 @@
-def configs = [
-    [
-        label: 'windows',
-        versions: ['py26', 'py27', 'py34', 'py35', 'py36'],
-    ],
-    [
-        label: 'windows64',
-        versions: ['py26', 'py27', 'py34', 'py35', 'py36'],
-    ],
-    [
-        label: 'sierra',
-        versions: ['py26', 'py27', 'py34', 'py35', 'py36'],
-    ],
-    [
-        label: 'docker',
-        imageName: 'quay.io/pypa/manylinux1_x86_64',
-        versions: [
-            'cp26-cp26m', 'cp26-cp26mu',
-            'cp27-cp27m', 'cp27-cp27mu', 'cp33-cp33m',
-            'cp34-cp34m', 'cp35-cp35m', 'cp36-cp36m'
-        ],
-    ],
-    [
-        label: 'docker',
-        imageName: 'quay.io/pypa/manylinux1_i686',
-        versions: [
-            'cp26-cp26m', 'cp26-cp26mu',
-            'cp27-cp27m', 'cp27-cp27mu', 'cp33-cp33m',
-            'cp34-cp34m', 'cp35-cp35m', 'cp36-cp36m'
-        ],
-    ],
-]
-
-
-def build(version, label, imageName) {
-    try {
-        timeout(time: 30, unit: 'MINUTES') {
-            if (label.contains("windows")) {
-                def pythonPath = [
-                    py26: "C:\\Python26\\python.exe",
-                    py27: "C:\\Python27\\python.exe",
-                    py33: "C:\\Python33\\python.exe",
-                    py34: "C:\\Python34\\python.exe",
-                    py35: "C:\\Python35\\python.exe",
-                    py36: "C:\\Python36\\python.exe"
-                ]
-                bat """
-                    wmic qfe
-                    @set PATH="C:\\Python27";"C:\\Python27\\Scripts";%PATH%
-                    @set PYTHON="${pythonPath[version]}"
-
-                    virtualenv -p %PYTHON% .release
-                    call .release\\Scripts\\activate
-                    pip install wheel virtualenv
-                    pip wheel bcrypt --wheel-dir=wheelhouse --no-binary bcrypt
-                    pip install -f wheelhouse bcrypt --no-index
-                    python -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
-                """
-            } else if (label.contains("sierra")) {
-                def pythonPath = [
-                    py26: "/usr/bin/python2.6",
-                    py27: "/Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7",
-                    py34: "/Library/Frameworks/Python.framework/Versions/3.4/bin/python3.4",
-                    py35: "/Library/Frameworks/Python.framework/Versions/3.5/bin/python3.5",
-                    py36: "/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6",
-                ]
-                ansiColor {
-                    sh """#!/usr/bin/env bash
-                        set -xe
-                        # output the list of things we've installed as a point in time check of how up
-                        # to date the builder is
-                        /usr/sbin/system_profiler SPInstallHistoryDataType
-
-                        # Jenkins logs in as a non-interactive shell, so we don't even have /usr/local/bin in PATH
-                        export PATH="/usr/local/bin:\${PATH}"
-                        export PATH="/Users/jenkins/.pyenv/shims:\${PATH}"
-
-                        printenv
-
-                        virtualenv .venv -p ${pythonPath[version]}
-                        source .venv/bin/activate
-                        pip install -U wheel # upgrade wheel to latest before we use it to build the wheel
-                        pip wheel bcrypt --wheel-dir=wheelhouse --no-binary bcrypt
-                        pip install -f wheelhouse bcrypt --no-index
-                        python -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
-                    """
-                }
-            } else if (label.contains("docker")) {
-                linux32 = ""
-                if (imageName.contains("i686")) {
-                    linux32 = "linux32"
-                }
-                sh """#!/usr/bin/env bash
-                    set -x -e
-                    # Because we are doing this as root in the container, but we write to a mounted dir that is outside the container
-                    # we need to make sure we set these files writable such that the jenkins user can delete them afterwards
-                    mkdir -p tmpwheelhouse
-                    mkdir -p wheelhouse
-                    chmod -R 777 tmpwheelhouse
-                    chmod -R 777 wheelhouse
-
-                    $linux32 /opt/python/$version/bin/pip install cffi six
-                    $linux32 /opt/python/$version/bin/pip wheel --no-binary bcrypt --no-deps bcrypt -w tmpwheelhouse/
-                    $linux32 auditwheel repair tmpwheelhouse/bcrypt*.whl -w wheelhouse/
-                    $linux32 /opt/python/$version/bin/pip install bcrypt --no-index -f wheelhouse/
-                    $linux32 /opt/python/$version/bin/python -c "import bcrypt;password = b'super secret password';hashed = bcrypt.hashpw(password, bcrypt.gensalt());bcrypt.checkpw(password, hashed)"
-                """
-            }
-            archiveArtifacts artifacts: "**/wheelhouse/bcrypt*.whl"
-        }
-    } finally {
-        deleteDir()
-    }
-
-}
-
-def builders = [:]
-for (config in configs) {
-    def label = config["label"]
-    def versions = config["versions"]
-
-    for (_version in versions) {
-        def version = _version
-
-        if (label.contains("docker")) {
-            def imageName = config["imageName"]
-            def combinedName = "${imageName}-${version}"
-            builders[combinedName] = {
-                node(label) {
-                    stage(combinedName) {
-                        def buildImage = docker.image(imageName)
-                        buildImage.pull()
-                        buildImage.inside("-u root") {
-                            build(version, label, imageName)
-                        }
-                    }
-                }
-            }
-        } else {
-            def combinedName = "${label}-${version}"
-            builders[combinedName] = {
-                node(label) {
-                    stage(combinedName) {
-                        build(version, label, "")
-                    }
-                }
-            }
-        }
-    }
-}
-
-parallel builders
diff --git a/.travis.yml b/.travis.yml
index 08bdbdd29ce80b0282162360728760f600b7af49_LnRyYXZpcy55bWw=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_LnRyYXZpcy55bWw= 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,5 @@
 
 matrix:
   include:
-    - python: 2.6
-      env: TOXENV=py26 CC=gcc
     - python: 2.7
       env: TOXENV=py27 CC=gcc
@@ -9,10 +7,8 @@
     - python: 2.7
       env: TOXENV=py27 CC=gcc
-    - python: 3.3
-      env: TOXENV=py33 CC=gcc
     - python: 3.4
       env: TOXENV=py34 CC=gcc
     - python: 3.5
       env: TOXENV=py35 CC=gcc
     - python: 3.6
       env: TOXENV=py36 CC=gcc
@@ -13,8 +9,14 @@
     - python: 3.4
       env: TOXENV=py34 CC=gcc
     - python: 3.5
       env: TOXENV=py35 CC=gcc
     - python: 3.6
       env: TOXENV=py36 CC=gcc
+    - python: 3.6
+      env: TOXENV=py36 CC=gcc
+    - python: 3.7
+      env: TOXENV=py37 CC=gcc
+      dist: xenial
+      sudo: true
     - python: pypy
       env: TOXENV=pypy CC=gcc
@@ -19,6 +21,4 @@
     - python: pypy
       env: TOXENV=pypy CC=gcc
-    - python: 2.6
-      env: TOXENV=py26 CC=clang
     - python: 2.7
       env: TOXENV=py27 CC=clang
@@ -23,10 +23,8 @@
     - python: 2.7
       env: TOXENV=py27 CC=clang
-    - python: 3.3
-      env: TOXENV=py33 CC=clang
     - python: 3.4
       env: TOXENV=py34 CC=clang
     - python: 3.5
       env: TOXENV=py35 CC=clang
     - python: 3.6
       env: TOXENV=py36 CC=clang
@@ -27,9 +25,13 @@
     - python: 3.4
       env: TOXENV=py34 CC=clang
     - python: 3.5
       env: TOXENV=py35 CC=clang
     - python: 3.6
       env: TOXENV=py36 CC=clang
+    - python: 3.7
+      env: TOXENV=py37 CC=clang
+      dist: xenial
+      sudo: true
     - python: pypy
       env: TOXENV=pypy CC=clang
     - python: 2.7
@@ -37,14 +39,6 @@
     - env: TOXENV=packaging
     - python: 3.5
       env: TOXENV=py3pep8
-    - language: generic
-      os: osx
-      osx_image: xcode7.3
-      env: TOXENV=py27
-    - language: generic
-      os: osx
-      osx_image: xcode7.3
-      env: TOXENV=py35
 
 install: .travis/install.sh
 
@@ -53,3 +47,17 @@
 branches:
   only:
     - master
+    - /\d+\.\d+\.x/
+    - /\d+\.\d+(\.\d+)?/
+
+notifications:
+irc:
+  channels:
+    # This is set to a secure variable to prevent forks from notifying the
+    # IRC channel whenever they fail a build. This can be removed when travis
+    # implements https://github.com/travis-ci/travis-ci/issues/1094.
+    # The value encrypted here was created via
+    # travis encrypt "irc.freenode.org#cryptography-dev"
+    - secure: "SYCVjMUGx5sJ5iEdgEz1iUG6QC7Ep1CGlAatbW8AmNTCurnJHN1Ezm1ix3mbGDevdeyQC/eC1iOXEEj6jhImUziUAG1YtpgHsJ1H3J5MNU/WUNuj6TGESa9iMq6K2cAAIWGvDR0hx4tO5/p09GevL9GVmc2IelUKuGKNkOVRGrI="
+  use_notice: true
+  skip_join: true
diff --git a/.travis/install.sh b/.travis/install.sh
index 08bdbdd29ce80b0282162360728760f600b7af49_LnRyYXZpcy9pbnN0YWxsLnNo..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_LnRyYXZpcy9pbnN0YWxsLnNo 100755
--- a/.travis/install.sh
+++ b/.travis/install.sh
@@ -3,35 +3,7 @@
 set -e
 set -x
 
-install_pyenv () {
-    git clone https://github.com/yyuu/pyenv.git ~/.pyenv
-    PYENV_ROOT="$HOME/.pyenv"
-    PATH="$PYENV_ROOT/bin:$PATH"
-    eval "$(pyenv init -)"
-}
-
-if [[ "$(uname -s)" == 'Darwin' ]]; then
-    install_pyenv
-    case "${TOXENV}" in
-        py27)
-            curl -O https://bootstrap.pypa.io/get-pip.py
-            python get-pip.py --user
-            ;;
-        py35)
-            pyenv install 3.5.1
-            pyenv global 3.5.1
-            ;;
-    esac
-    pyenv rehash
-    python -m pip install --user virtualenv
-else
-    if [[ "${TOXENV}" == "pypy" ]]; then
-        install_pyenv
-        pyenv install pypy-2.6.0
-        pyenv global pypy-2.6.0
-    fi
-    pip install virtualenv
-fi
+pip install virtualenv
 
 python -m virtualenv ~/.venv
 source ~/.venv/bin/activate
diff --git a/.travis/run.sh b/.travis/run.sh
index 08bdbdd29ce80b0282162360728760f600b7af49_LnRyYXZpcy9ydW4uc2g=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_LnRyYXZpcy9ydW4uc2g= 100755
--- a/.travis/run.sh
+++ b/.travis/run.sh
@@ -3,20 +3,5 @@
 set -e
 set -x
 
-init_pyenv () {
-    PYENV_ROOT="$HOME/.pyenv"
-    PATH="$PYENV_ROOT/bin:$PATH"
-    eval "$(pyenv init -)"
-}
-
-if [[ "$(uname -s)" == "Darwin" ]]; then
-    init_pyenv
-else
-    if [[ "${TOXENV}" == "pypy" ]]; then
-        init_pyenv
-        pyenv global pypy-2.6.0
-    fi
-fi
-
 source ~/.venv/bin/activate
 tox
diff --git a/Jenkinsfile b/Jenkinsfile
deleted file mode 100644
index 08bdbdd29ce80b0282162360728760f600b7af49_SmVua2luc2ZpbGU=..0000000000000000000000000000000000000000
--- a/Jenkinsfile
+++ /dev/null
@@ -1,61 +0,0 @@
-def configs = [
-    [
-        label: 'windows',
-        toxenvs: ['py26', 'py27', 'py33', 'py34', 'py35', 'py36'],
-    ],
-    [
-        label: 'windows64',
-        toxenvs: ['py26', 'py27', 'py33', 'py34', 'py35', 'py36'],
-    ],
-    [
-        label: 'freebsd11',
-        toxenvs: ['py27'],
-    ],
-]
-
-def build(label, toxenv) {
-    try {
-        timeout(time: 5, unit: 'MINUTES') {
-            if (label.startsWith("windows")) {
-                bat """
-                    @set PATH="C:\\Python27";"C:\\Python27\\Scripts";%PATH%
-                    tox -r -e $toxenv
-                """
-            } else {
-                ansiColor('xterm') {
-                    sh "tox -r -e $toxenv --  --color=yes"
-                }
-            }
-        }
-    } finally {
-        deleteDir()
-    }
-
-}
-
-def builders = [:]
-for (config in configs) {
-    def label = config["label"]
-    def toxenvs = config["toxenvs"]
-
-    // We need to use a temporary variable here and then
-    // bind it in the for loop so that it is properly captured
-    // by the closure
-    for (_toxenv in toxenvs) {
-        def toxenv = _toxenv
-        def combinedName = "${label}-${toxenv}"
-
-        builders[combinedName] = {
-            node(label) {
-                stage("Checkout") {
-                    checkout scm
-                }
-                stage(combinedName) {
-                    build(label, toxenv)
-                }
-            }
-        }
-    }
-}
-
-parallel builders
diff --git a/MANIFEST.in b/MANIFEST.in
index 08bdbdd29ce80b0282162360728760f600b7af49_TUFOSUZFU1QuaW4=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_TUFOSUZFU1QuaW4= 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,8 +1,10 @@
 include LICENSE README.rst
 
+include pyproject.toml
+
 include tox.ini .coveragerc
 include src/build_bcrypt.py
 
 recursive-include src/_csrc *
 recursive-include tests *.py
 
@@ -3,8 +5,8 @@
 include tox.ini .coveragerc
 include src/build_bcrypt.py
 
 recursive-include src/_csrc *
 recursive-include tests *.py
 
-exclude requirements.txt tasks.py .travis.yml wheel-scripts Jenkinsfile
+exclude requirements.txt release.py .travis.yml azure-pipelines.yml
 
@@ -10,7 +12,5 @@
 
-exclude .jenkins
-recursive-exclude .jenkins *
-
-recursive-exclude wheel-scripts *
+exclude .azure-pipelines
+recursive-exclude .azure-pipelines *
 
 prune .travis
diff --git a/README.rst b/README.rst
index 08bdbdd29ce80b0282162360728760f600b7af49_UkVBRE1FLnJzdA==..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_UkVBRE1FLnJzdA== 100644
--- a/README.rst
+++ b/README.rst
@@ -2,9 +2,9 @@
 ======
 
 .. image:: https://img.shields.io/pypi/v/bcrypt.svg
-    :target: https://pypi.python.org/pypi/bcrypt/
+    :target: https://pypi.org/project/bcrypt/
     :alt: Latest Version
 
 .. image:: https://travis-ci.org/pyca/bcrypt.svg?branch=master
     :target: https://travis-ci.org/pyca/bcrypt
 
@@ -6,9 +6,12 @@
     :alt: Latest Version
 
 .. image:: https://travis-ci.org/pyca/bcrypt.svg?branch=master
     :target: https://travis-ci.org/pyca/bcrypt
 
-Modern password hashing for your software and your servers
+.. image:: https://dev.azure.com/pyca/bcrypt/_apis/build/status/bcrypt-CI?branchName=master
+    :target: https://dev.azure.com/pyca/bcrypt/_build/latest?definitionId=8&branchName=master
+
+Good password hashing for your software and your servers
 
 
 Installation
@@ -34,6 +37,11 @@
 
     $ sudo yum install gcc libffi-devel python-devel
 
+Alternatives
+============
+
+While bcrypt remains a good choice for password storage depending on your specific use case you may also want to consider using scrypt (either via `standard library`_ or `cryptography`_) or argon2id via `argon2_cffi`_.
+
 Changelog
 =========
 
@@ -37,6 +45,24 @@
 Changelog
 =========
 
+3.1.6
+-----
+
+* Added support for compilation on Haiku.
+
+3.1.5
+-----
+
+* Added support for compilation on AIX.
+* Dropped Python 2.6 and 3.3 support.
+* Switched to using ``abi3`` wheels for Python 3. If you are not getting a
+  wheel on a compatible platform please upgrade your ``pip`` version.
+
+3.1.4
+-----
+
+* Fixed compilation with mingw and on illumos.
+
 3.1.3
 -----
 * Fixed a compilation issue on Solaris.
@@ -158,7 +184,7 @@
 -------------
 
 This library should be compatible with py-bcrypt and it will run on Python
-2.6+, 3.3+, and PyPy 2.6+.
+2.7, 3.4+, and PyPy 2.6+.
 
 C Code
 ------
@@ -172,3 +198,6 @@
 identify a vulnerability, we ask you to contact us privately.
 
 .. _`same security policy as cryptography`: https://cryptography.io/en/latest/security/
+.. _`standard library`: https://docs.python.org/3/library/hashlib.html#hashlib.scrypt
+.. _`argon2_cffi`: https://argon2-cffi.readthedocs.io
+.. _`cryptography`: https://cryptography.io/en/latest/hazmat/primitives/key-derivation-functions/#cryptography.hazmat.primitives.kdf.scrypt.Scrypt
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_YXp1cmUtcGlwZWxpbmVzLnltbA==
--- /dev/null
+++ b/azure-pipelines.yml
@@ -0,0 +1,89 @@
+trigger:
+  branches:
+    include:
+      - "master"
+  tags:
+    include:
+      - "*"
+
+jobs:
+- job: 'mac'
+  pool:
+    vmImage: 'macOS-10.14'
+  strategy:
+    matrix:
+      Python27:
+        python.version: '2.7'
+        TOXENV: py27
+      Python34:
+        python.version: '3.4'
+        TOXENV: py34
+      Python35:
+        python.version: '3.5'
+        TOXENV: py35
+      Python36:
+        python.version: '3.6'
+        TOXENV: py36
+      Python37:
+        python.version: '3.7'
+        TOXENV: py37
+  steps:
+  - task: UsePythonVersion@0
+    inputs:
+      versionSpec: '$(python.version)'
+      architecture: 'x64'
+
+  - script: pip install tox
+    displayName: 'Install tox'
+
+  - script: tox
+    displayName: 'Run tests'
+- job: 'win'
+  pool:
+    vmImage: 'windows-2019'
+  container: $[variables.containerImage]
+  strategy:
+    matrix:
+      Python27-x86:
+        TOXENV: py27
+        containerImage: 'pyca/cryptography-runner-windows:py27-x86'
+        PYTHON_DIR: 'Python27'
+      Python27-x86-64:
+        TOXENV: py27
+        containerImage: 'pyca/cryptography-runner-windows:py27-x86_64'
+        PYTHON_DIR: 'Python27'
+      Python34-x86:
+        TOXENV: py34
+        containerImage: 'pyca/cryptography-runner-windows:py34-x86'
+        PYTHON_DIR: 'Python34'
+      Python34-x86-64:
+        TOXENV: py34
+        containerImage: 'pyca/cryptography-runner-windows:py34-x86_64'
+        PYTHON_DIR: 'Python34'
+      Python35-x86:
+        TOXENV: py35
+        containerImage: 'pyca/cryptography-runner-windows:py35-x86'
+        PYTHON_DIR: 'Python35'
+      Python35-x86-64:
+        TOXENV: py35
+        containerImage: 'pyca/cryptography-runner-windows:py35-x86_64'
+        PYTHON_DIR: 'Python35'
+      Python36-x86:
+        TOXENV: py36
+        containerImage: 'pyca/cryptography-runner-windows:py3-x86'
+        PYTHON_DIR: 'Python36'
+      Python36-x86-64:
+        TOXENV: py36
+        containerImage: 'pyca/cryptography-runner-windows:py3-x86_64'
+        PYTHON_DIR: 'Python36'
+      Python37-x86:
+        TOXENV: py37
+        containerImage: 'pyca/cryptography-runner-windows:py3-x86'
+        PYTHON_DIR: 'Python37'
+      Python37-x86-64:
+        TOXENV: py37
+        containerImage: 'pyca/cryptography-runner-windows:py3-x86_64'
+        PYTHON_DIR: 'Python37'
+  steps:
+  - script: "C:/%PYTHON_DIR%/Scripts/tox"
+    displayName: 'Run tests'
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_cHlwcm9qZWN0LnRvbWw=
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,10 @@
+[build-system]
+# Must be kept in sync with `setup_requirements` in `setup.py`
+requires = [
+    "setuptools>=40.8.0",
+    "wheel",
+    "cffi>=1.1; python_implementation != 'PyPy'",
+]
+# Point to the setuptools' PEP517 build backend explicitly to
+# disable Pip's fallback guessing
+build-backend = "setuptools.build_meta"
diff --git a/release.py b/release.py
new file mode 100644
index 0000000000000000000000000000000000000000..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_cmVsZWFzZS5weQ==
--- /dev/null
+++ b/release.py
@@ -0,0 +1,102 @@
+# This file is dual licensed under the terms of the Apache License, Version
+# 2.0, and the BSD License. See the LICENSE file in the root of this repository
+# for complete details.
+
+from __future__ import absolute_import, division, print_function
+
+import getpass
+import glob
+import os
+import subprocess
+import tempfile
+import time
+import zipfile
+
+from azure.devops.connection import Connection
+from azure.devops.v5_1.build.models import Build
+
+import click
+
+from msrest.authentication import BasicAuthentication
+
+
+def run(*args, **kwargs):
+    print("[running] {0}".format(list(args)))
+    subprocess.check_call(list(args), **kwargs)
+
+
+def wait_for_build_completed_azure(build_client, build_id):
+    while True:
+        build = build_client.get_build("bcrypt", build_id)
+        if build.finish_time is not None:
+            break
+        time.sleep(3)
+
+
+def download_artifacts_azure(build_client, build_id):
+    artifacts = build_client.get_artifacts("bcrypt", build_id)
+    paths = []
+    for artifact in artifacts:
+        contents = build_client.get_artifact_content_zip(
+            "bcrypt", build_id, artifact.name
+        )
+        with tempfile.NamedTemporaryFile() as f:
+            for chunk in contents:
+                f.write(chunk)
+            f.flush()
+            with zipfile.ZipFile(f.name) as z:
+                for name in z.namelist():
+                    if not name.endswith(".whl"):
+                        continue
+                    p = z.open(name)
+                    out_path = os.path.join(
+                        os.path.dirname(__file__),
+                        "dist",
+                        os.path.basename(name),
+                    )
+                    with open(out_path, "wb") as f:
+                        f.write(p.read())
+                    paths.append(out_path)
+    return paths
+
+
+def build_wheels_azure(version):
+    token = getpass.getpass("Azure personal access token: ")
+    credentials = BasicAuthentication("", token)
+    connection = Connection(
+        base_url="https://dev.azure.com/pyca", creds=credentials
+    )
+    build_client = connection.clients.get_build_client()
+    [definition] = build_client.get_definitions(
+        "bcrypt", "bcrypt-wheel-builder"
+    )
+    build_description = Build(
+        definition=definition,
+    )
+    build = build_client.queue_build(
+        project="bcrypt", build=build_description
+    )
+    wait_for_build_completed_azure(build_client, build.id)
+    return download_artifacts_azure(build_client, build.id)
+
+
+@click.command()
+@click.argument("version")
+def release(version):
+    """
+    ``version`` should be a string like '0.4' or '1.0'.
+    """
+    run("git", "tag", "-s", version, "-m", "{0} release".format(version))
+    run("git", "push", "--tags")
+
+    run("python", "setup.py", "sdist")
+
+    packages = glob.glob("dist/bcrypt-{0}*".format(version))
+    run("twine", "upload", "-s", *packages)
+
+    azure_wheel_paths = build_wheels_azure(version)
+    run("twine", "upload", *azure_wheel_paths)
+
+
+if __name__ == "__main__":
+    release()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c2V0dXAuY2Zn
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,2 @@
+[metadata]
+license_file = LICENSE
diff --git a/setup.py b/setup.py
index 08bdbdd29ce80b0282162360728760f600b7af49_c2V0dXAucHk=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c2V0dXAucHk= 100644
--- a/setup.py
+++ b/setup.py
@@ -54,6 +54,7 @@
     author=__about__["__author__"],
     author_email=__about__["__email__"],
 
+    python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
     setup_requires=[CFFI_DEPENDENCY],
     install_requires=[
         CFFI_DEPENDENCY,
@@ -61,7 +62,7 @@
     ],
     extras_require={
         "tests": [
-            "pytest>=3.2.1",
+            "pytest>=3.2.1,!=3.3.0",
         ],
     },
     tests_require=[
@@ -65,7 +66,7 @@
         ],
     },
     tests_require=[
-        "pytest>=3.2.1",
+        "pytest>=3.2.1,!=3.3.0",
     ],
 
     package_dir={"": "src"},
@@ -76,6 +77,8 @@
     zip_safe=False,
 
     classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "License :: OSI Approved :: Apache Software License",
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Programming Language :: Python :: 2",
@@ -79,6 +82,5 @@
         "Programming Language :: Python :: Implementation :: CPython",
         "Programming Language :: Python :: Implementation :: PyPy",
         "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.6",
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
@@ -83,6 +85,5 @@
         "Programming Language :: Python :: 2.7",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.3",
         "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
@@ -86,6 +87,7 @@
         "Programming Language :: Python :: 3.4",
         "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
+        "Programming Language :: Python :: 3.7",
     ],
 
     ext_package="bcrypt",
diff --git a/src/_csrc/blf.c b/src/_csrc/blf.c
index 08bdbdd29ce80b0282162360728760f600b7af49_c3JjL19jc3JjL2JsZi5j..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c3JjL19jc3JjL2JsZi5j 100644
--- a/src/_csrc/blf.c
+++ b/src/_csrc/blf.c
@@ -15,10 +15,7 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
+ * 3. The name of the author may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
diff --git a/src/_csrc/blf.h b/src/_csrc/blf.h
index 08bdbdd29ce80b0282162360728760f600b7af49_c3JjL19jc3JjL2JsZi5o..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c3JjL19jc3JjL2JsZi5o 100644
--- a/src/_csrc/blf.h
+++ b/src/_csrc/blf.h
@@ -14,10 +14,7 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *      This product includes software developed by Niels Provos.
- * 4. The name of the author may not be used to endorse or promote products
+ * 3. The name of the author may not be used to endorse or promote products
  *    derived from this software without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
diff --git a/src/_csrc/portable_endian.h b/src/_csrc/portable_endian.h
index 08bdbdd29ce80b0282162360728760f600b7af49_c3JjL19jc3JjL3BvcnRhYmxlX2VuZGlhbi5o..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c3JjL19jc3JjL3BvcnRhYmxlX2VuZGlhbi5o 100644
--- a/src/_csrc/portable_endian.h
+++ b/src/_csrc/portable_endian.h
@@ -94,6 +94,10 @@
 
 #   include <sys/endian.h>
 
+#elif defined(__HAIKU__)
+
+#   include <endian.h>
+
 #elif defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
 
 #   include <sys/endian.h>
@@ -180,6 +184,35 @@
 #   define be64toh(x) BE_64(x)
 #   define le64toh(x) LE_64(x)
 
+#elif defined _AIX      /* AIX is always big endian */
+#       define be64toh(x) (x)
+#       define be32toh(x) (x)
+#       define be16toh(x) (x)
+#       define le32toh(x)                              \
+         ((((x) & 0xff) << 24) |                 \
+           (((x) & 0xff00) << 8) |                \
+           (((x) & 0xff0000) >> 8) |              \
+           (((x) & 0xff000000) >> 24))
+#       define   le64toh(x)                               \
+         ((((x) & 0x00000000000000ffL) << 56) |   \
+          (((x) & 0x000000000000ff00L) << 40) |   \
+          (((x) & 0x0000000000ff0000L) << 24) |   \
+          (((x) & 0x00000000ff000000L) << 8)  |   \
+          (((x) & 0x000000ff00000000L) >> 8)  |   \
+          (((x) & 0x0000ff0000000000L) >> 24) |   \
+          (((x) & 0x00ff000000000000L) >> 40) |   \
+          (((x) & 0xff00000000000000L) >> 56))
+#       ifndef htobe64
+#               define htobe64(x) be64toh(x)
+#       endif
+#       ifndef htobe32
+#               define htobe32(x) be32toh(x)
+#       endif
+#       ifndef htobe16
+#               define htobe16(x) be16toh(x)
+#       endif
+
+
 #else
 
 #   error platform not supported
diff --git a/src/bcrypt/__about__.py b/src/bcrypt/__about__.py
index 08bdbdd29ce80b0282162360728760f600b7af49_c3JjL2JjcnlwdC9fX2Fib3V0X18ucHk=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c3JjL2JjcnlwdC9fX2Fib3V0X18ucHk= 100644
--- a/src/bcrypt/__about__.py
+++ b/src/bcrypt/__about__.py
@@ -26,7 +26,7 @@
 __summary__ = "Modern password hashing for your software and your servers"
 __uri__ = "https://github.com/pyca/bcrypt/"
 
-__version__ = "3.1.3"
+__version__ = "3.1.7.dev1"
 
 __author__ = "The Python Cryptographic Authority developers"
 __email__ = "cryptography-dev@python.org"
diff --git a/src/bcrypt/__init__.py b/src/bcrypt/__init__.py
index 08bdbdd29ce80b0282162360728760f600b7af49_c3JjL2JjcnlwdC9fX2luaXRfXy5weQ==..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_c3JjL2JjcnlwdC9fX2luaXRfXy5weQ== 100644
--- a/src/bcrypt/__init__.py
+++ b/src/bcrypt/__init__.py
@@ -22,8 +22,7 @@
 
 import six
 
-from bcrypt import _bcrypt
-
+from . import _bcrypt
 from .__about__ import (
     __author__, __copyright__, __email__, __license__, __summary__, __title__,
     __uri__, __version__,
@@ -134,7 +133,9 @@
             "Warning: bcrypt.kdf() called with only {0} round(s). "
             "This few is not secure: the parameter is linear, like PBKDF2.")
             .format(rounds),
-            UserWarning)
+            UserWarning,
+            stacklevel=2,
+        )
 
     key = _bcrypt.ffi.new("uint8_t[]", desired_key_bytes)
     res = _bcrypt.lib.bcrypt_pbkdf(
diff --git a/tasks.py b/tasks.py
deleted file mode 100644
index 08bdbdd29ce80b0282162360728760f600b7af49_dGFza3MucHk=..0000000000000000000000000000000000000000
--- a/tasks.py
+++ /dev/null
@@ -1,120 +0,0 @@
-from __future__ import absolute_import, division, print_function
-
-import getpass
-import io
-import os
-import time
-
-from clint.textui.progress import Bar as ProgressBar
-
-import invoke
-
-import requests
-
-
-JENKINS_URL = "https://jenkins.cryptography.io/job/bcrypt-wheel-builder"
-
-
-def wait_for_build_completed(session):
-    # Wait 20 seconds before actually checking if the build is complete, to
-    # ensure that it had time to really start.
-    time.sleep(20)
-    while True:
-        response = session.get(
-            "{0}/lastBuild/api/json/".format(JENKINS_URL),
-            headers={
-                "Accept": "application/json",
-            }
-        )
-        response.raise_for_status()
-        if not response.json()["building"]:
-            assert response.json()["result"] == "SUCCESS"
-            break
-        time.sleep(0.1)
-
-
-def download_artifacts(session):
-    response = session.get(
-        "{0}/lastBuild/api/json/".format(JENKINS_URL),
-        headers={
-            "Accept": "application/json"
-        }
-    )
-    response.raise_for_status()
-    assert not response.json()["building"]
-    assert response.json()["result"] == "SUCCESS"
-
-    paths = []
-
-    last_build_number = response.json()["number"]
-    for run in response.json()["runs"]:
-        if run["number"] != last_build_number:
-            print(
-                "Skipping {0} as it is not from the latest build ({1})".format(
-                    run["url"], last_build_number
-                )
-            )
-            continue
-
-        response = session.get(
-            run["url"] + "api/json/",
-            headers={
-                "Accept": "application/json",
-            }
-        )
-        response.raise_for_status()
-        for artifact in response.json()["artifacts"]:
-            response = session.get(
-                "{0}artifact/{1}".format(run["url"], artifact["relativePath"]),
-                stream=True
-            )
-            assert response.headers["content-length"]
-            print("Downloading {0}".format(artifact["fileName"]))
-            bar = ProgressBar(
-                expected_size=int(response.headers["content-length"]),
-                filled_char="="
-            )
-            content = io.BytesIO()
-            for data in response.iter_content(chunk_size=8192):
-                content.write(data)
-                bar.show(content.tell())
-            assert bar.expected_size == content.tell()
-            bar.done()
-            out_path = os.path.join(
-                os.path.dirname(__file__),
-                "dist",
-                artifact["fileName"],
-            )
-            with open(out_path, "wb") as f:
-                f.write(content.getvalue())
-            paths.append(out_path)
-    return paths
-
-
-@invoke.task
-def release(version):
-    """
-    ``version`` should be a string like '0.4' or '1.0'.
-    """
-    invoke.run("git tag -s {0} -m '{0} release'".format(version))
-    invoke.run("git push --tags")
-
-    invoke.run("python setup.py sdist")
-
-    invoke.run(
-        "twine upload -s dist/bcrypt-{0}*".format(version)
-    )
-
-    session = requests.Session()
-
-    token = getpass.getpass("Input the Jenkins token: ")
-    response = session.post(
-        "{0}/build?token={1}".format(JENKINS_URL, token),
-        params={
-            "cause": "Building wheels for {0}".format(version)
-        }
-    )
-    response.raise_for_status()
-    wait_for_build_completed(session)
-    paths = download_artifacts(session)
-    invoke.run("twine upload {0}".format(" ".join(paths)))
diff --git a/tests/test_bcrypt.py b/tests/test_bcrypt.py
index 08bdbdd29ce80b0282162360728760f600b7af49_dGVzdHMvdGVzdF9iY3J5cHQucHk=..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_dGVzdHMvdGVzdF9iY3J5cHQucHk= 100644
--- a/tests/test_bcrypt.py
+++ b/tests/test_bcrypt.py
@@ -400,7 +400,9 @@
         b"\x43\x66\x6c\x9b\x09\xef\x33\xed\x8c\x27\xe8\xe8\xf3\xe2\xd8\xe6"
     ]])
 def test_kdf(rounds, password, salt, expected):
-    derived = bcrypt.kdf(password, salt, len(expected), rounds)
+    derived = bcrypt.kdf(
+        password, salt, len(expected), rounds, ignore_few_rounds=True
+    )
     assert derived == expected
 
 
diff --git a/tox.ini b/tox.ini
index 08bdbdd29ce80b0282162360728760f600b7af49_dG94LmluaQ==..0859aefd9f413bca4e8ab7099d26c498ca5d4f25_dG94LmluaQ== 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py26,py27,pypy,py33,py34,py35,py36,pep8,py3pep8,packaging
+envlist = py27,pypy,py34,py35,py36,py37,pep8,py3pep8,packaging
 
 [testenv]
 extras =
@@ -19,7 +19,7 @@
     flake8 .
 
 [testenv:py3pep8]
-basepython = python3.3
+basepython = python3
 deps =
     flake8
     flake8-import-order
@@ -37,6 +37,7 @@
 
 
 [flake8]
+ignore = W504
 exclude = .tox,*.egg
 select = E,W,F,N,I
 application-import-names = bcrypt,tests