diff --git a/.travis.yml b/.travis.yml index 1dda25b66..8540ede79 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,47 +1,388 @@ branches: only: - - master - -matrix: + - master +env: + global: + - BRAINIAK_REPO=danielsuo/brainiak + - TWINE_USERNAME=brainiak-bot + - AWS_ACCESS_KEY_ID="AKIAJKBW6H2VKKQDHILQ" + - AWS_DEFAULT_REGION="us-east-1" + - secure: JWCpO0caWN4M0xaGPqKMPPHg8WB6sybPQPcCFtXc+dwsD3mu+mDv7lGV3l/5sRoDqIi87uhXBtfnPVKao7AdPaLZoZc8ktBP3b7QwG49U479odN1OuxZcdLBmQBPZlAnqyH7wbM0xhkskr6Yl8gkKWynDfyod1GWbr/plcGRuX82unvuq5tkX8gNSdJ4vHbHGGcvgUbDkyEJdQz7y96qJn7VUpD4FhujO7bGyGVB2ZVcAcEyHuQZiyPxknrXSE0dnk0IeEH77DY40SLpPf5WG4fl/gGYLlccw2ZmBOAOYYE/nq9HF+/b5gnYp+0zLUCgA1tK9uC1UdT3IE24hr8XJMdMIog1BSVjv5k31RjFi0mwCVaasiCN/IOYvwrg+yod5UjXBag7f/Wjo3AEoUSByFERTr+Y7Gs+uCHQKT8Jw4mCTwmI+3JnH+4ZcuMbIQUr10uaocmpNuWhA1IVMTewIKjaAgY9qWy2X5e7X5/faYXlUhV68QiA5TB4MCvA+jeq84+bIk00HBnOadL/iAKFVhBlbkqe8OZWNbsBKFERWEVJFIoOLMg43NORWRs54Mi3eie4bNxWUwwPsxHR4FoBSeWjm2yorDbUIFDK57xZH3jUFu11xSXW3x9BqGWdgQg71j/P1nVKGMoHvPUBX0wO2CLQnSl6katSwgM6aLeJl1U= + - secure: ZkEtQyYsMAiomNX0UCgF8ExvCIselUY9z0cSdbatmiSSHRlKxjBk9wS1CQM+Y9+Eeri7aUkS1PbCrz3vNVbTtj9VEZJjxpFNDzmKziIDBJm6DCE5k0JV7dQbKS1jPrX1/+Az1mMCy+EN6ZtA2WUXCLZsqkmG9rdEUJMBM5SYFBCfTzrTWxwqJZU46ErKrdn5d+ywi9O4Cu7/i2JjRpEejC2iYWSDNEgqHvxDo6lbG5sVSn2pns4Dzqwh3n9zLxqWeQalvgisjcGuBoRLTyCEeJNgDL43PkdgqflyanleLzlDCvTowfrqWZF7Xn8TmkZslunlRe3zMBaiDHxg1h60rju4inXzIahHL7keFDB32QWBZb33jf2BZRStjjJDgO8jvPMBmhxwV5LLzSuW0cHxEm+f5O6eWPQjT4dX04dGMabYU7iMgR5t0+uZDqeW17J4py75UXC3u4KWkzWKvRIe78nEXSE4F1SSiV9eQWdBtkJcF0i3UQzQW5quKUWcYp2sw4SEyz/zCc+CLCtjheIg39rrWVSRKL3t4iCebxyVU/YLgOpBW2BVYtY4vUsyLma7Mr+rcVLZggcU4qnoz3Q6jgDwPP1jeNC9RZNw/Tida9hQgykmqNM9OPkgrRVgL3g0NfYMdysupqezplywjvIDODh11rg8+Cjk6nRb28S4wVQ= +jobs: include: - - os: linux - dist: trusty - sudo: required - language: python - python: 3.4 - addons: &linux_addons - apt: &apt - packages: - - build-essential libgomp1 libmpich-dev mpich - - - os: linux - dist: trusty - sudo: required - language: python - python: 3.5 - addons: *linux_addons - - - os: osx - osx_image: xcode7.3 - language: generic - env: &macos_env - - CC=/usr/local/opt/llvm/bin/clang - - CXX=/usr/local/opt/llvm/bin/clang++ - - LDFLAGS="-L/usr/local/opt/llvm/lib - -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" - - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" - - HOMEBREW_NO_AUTO_UPDATE=1 - before_install: &macos_before_install - - brew install llvm mpich python3 - - - os: osx - osx_image: xcode8 - language: generic - env: *macos_env - before_install: *macos_before_install - -install: - - python3 -m pip install -U pip - -script: - - ./pr-check.sh + - stage: test + language: generic + - os: linux + dist: trusty + sudo: required + language: python + python: '3.4' + before_install: + - python3 -m pip install -U pip awscli twine + script: + - ./bin/pr-check.sh + addons: + apt: + packages: + - build-essential libgomp1 libmpich-dev mpich + - os: linux + dist: trusty + sudo: required + language: python + python: '3.5' + before_install: + - python3 -m pip install -U pip awscli twine + script: + - ./bin/pr-check.sh + addons: + apt: + packages: + - build-essential libgomp1 libmpich-dev mpich + - os: linux + dist: trusty + sudo: required + language: python + python: '3.6' + before_install: + - python3 -m pip install -U pip awscli twine + script: + - ./bin/pr-check.sh + addons: + apt: + packages: + - build-essential libgomp1 libmpich-dev mpich + - os: osx + osx_image: xcode7.3 + language: generic + env: + - CC=/usr/local/opt/llvm/bin/clang + - CXX=/usr/local/opt/llvm/bin/clang++ + - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" + - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" + - HOMEBREW_NO_AUTO_UPDATE=1 + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + install: + - brew install llvm mpich + script: + - ./bin/pr-check.sh + - os: osx + osx_image: xcode8 + language: generic + env: + - CC=/usr/local/opt/llvm/bin/clang + - CXX=/usr/local/opt/llvm/bin/clang++ + - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" + - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" + - HOMEBREW_NO_AUTO_UPDATE=1 + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + install: + - brew install llvm mpich + script: + - ./bin/pr-check.sh + - stage: build + language: generic + if: branch = master + - os: linux + dist: trusty + sudo: required + language: python + python: '3.4' + before_install: + - python3 -m pip install -U pip awscli twine + if: branch = master + install: + - ./bin/build-dist.sh + script: + - ./bin/test-wheels.sh + after_script: + - aws s3 cp dist s3://brainiak/$TRAVIS_COMMIT/dist --recursive --acl=public-read + - os: osx + osx_image: xcode7.3 + language: generic + env: + - CC=/usr/local/opt/llvm/bin/clang + - CXX=/usr/local/opt/llvm/bin/clang++ + - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" + - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" + - HOMEBREW_NO_AUTO_UPDATE=1 + - ARCHFLAGS="-arch x86_64" + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master + install: + - brew install llvm mpich + - VERSIONS="3.4.4" ./bin/install-python-macos.sh + - VERSIONS="3.4.4" ./bin/build-dist-macos.sh + before_script: + - brew uninstall -f --ignore-dependencies llvm mpich + - unset CC + - unset CXX + - unset LDFLAGS + - unset CPPFLAGS + - unset ARCHFLAGS + script: + - VERSIONS="3.4.4" ./bin/test-wheels-macos.sh + after_script: + - aws s3 cp dist s3://brainiak/$TRAVIS_COMMIT/dist --recursive --acl=public-read + - os: osx + osx_image: xcode7.3 + language: generic + env: + - CC=/usr/local/opt/llvm/bin/clang + - CXX=/usr/local/opt/llvm/bin/clang++ + - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" + - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" + - HOMEBREW_NO_AUTO_UPDATE=1 + - ARCHFLAGS="-arch x86_64" + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master + install: + - brew install llvm mpich + - VERSIONS="3.5.3" ./bin/install-python-macos.sh + - VERSIONS="3.5.3" ./bin/build-dist-macos.sh + before_script: + - brew uninstall -f --ignore-dependencies llvm mpich + - unset CC + - unset CXX + - unset LDFLAGS + - unset CPPFLAGS + - unset ARCHFLAGS + script: + - VERSIONS="3.5.3" ./bin/test-wheels-macos.sh + after_script: + - aws s3 cp dist s3://brainiak/$TRAVIS_COMMIT/dist --recursive --acl=public-read + - os: osx + osx_image: xcode7.3 + language: generic + env: + - CC=/usr/local/opt/llvm/bin/clang + - CXX=/usr/local/opt/llvm/bin/clang++ + - LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS" + - CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS" + - HOMEBREW_NO_AUTO_UPDATE=1 + - ARCHFLAGS="-arch x86_64" + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master + install: + - brew install llvm mpich + - VERSIONS="3.6.0" ./bin/install-python-macos.sh + - VERSIONS="3.6.0" ./bin/build-dist-macos.sh + before_script: + - brew uninstall -f --ignore-dependencies llvm mpich + - unset CC + - unset CXX + - unset LDFLAGS + - unset CPPFLAGS + - unset ARCHFLAGS + script: + - VERSIONS="3.6.0" ./bin/test-wheels-macos.sh + after_script: + - aws s3 cp dist s3://brainiak/$TRAVIS_COMMIT/dist --recursive --acl=public-read + - stage: s3 + language: generic + if: branch = master + - os: linux + dist: trusty + sudo: required + language: python + python: '3.4' + before_install: + - python3 -m pip install -U pip awscli twine + if: branch = master + env: TWINE_REPOSITORY_URL=https://test.pypi.org/legacy + install: aws s3 cp s3://brainiak/$TRAVIS_COMMIT/dist dist --recursive + script: + - ./bin/test-wheels.sh + after_script: + - twine upload dist/* + - os: osx + osx_image: xcode7.3 + language: generic + env: + - HOMEBREW_NO_AUTO_UPDATE=1 + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + - aws s3 cp s3://brainiak/$TRAVIS_COMMIT/dist dist --recursive + if: branch = master + install: + - VERSIONS="3.4.4" ./bin/install-python-macos.sh + script: + - VERIONS="3.4.4" ./bin/test-wheels-macos.sh + after_script: + - twine upload dist/* + - os: osx + osx_image: xcode7.3 + language: generic + env: + - HOMEBREW_NO_AUTO_UPDATE=1 + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + - aws s3 cp s3://brainiak/$TRAVIS_COMMIT/dist dist --recursive + if: branch = master + install: + - VERSIONS="3.5.3" ./bin/install-python-macos.sh + script: + - VERIONS="3.5.3" ./bin/test-wheels-macos.sh + after_script: + - twine upload dist/* + - os: osx + osx_image: xcode7.3 + language: generic + env: + - HOMEBREW_NO_AUTO_UPDATE=1 + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + - aws s3 cp s3://brainiak/$TRAVIS_COMMIT/dist dist --recursive + if: branch = master + install: + - VERSIONS="3.6.0" ./bin/install-python-macos.sh + script: + - VERIONS="3.6.0" ./bin/test-wheels-macos.sh + after_script: + - twine upload dist/* + - stage: testpypi + language: generic + if: branch = master and repo = danielsuo/brainiak + - os: linux + dist: trusty + sudo: required + language: python + python: '3.4' + before_install: + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak + env: + - TWINE_REPOSITORY_URL=https://pypi.python.org/pypi + - PYPI_REPOSITORY_URL=https://testpypi.python.org + script: ./bin/test-wheels.sh + after_script: + - 'true' + - os: osx + osx_image: xcode7.3 + language: generic + env: + - TWINE_REPOSITORY_URL=https://pypi.python.org/pypi + - PYPI_REPOSITORY_URL=https://testpypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak + install: + - VERSIONS="3.4.4" ./bin/install-python-macos.sh + script: + - VERIONS="3.4.4" ./bin/test-wheels-macos.sh + after_script: + - 'true' + - os: osx + osx_image: xcode7.3 + language: generic + env: + - TWINE_REPOSITORY_URL=https://pypi.python.org/pypi + - PYPI_REPOSITORY_URL=https://testpypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak + install: + - VERSIONS="3.5.3" ./bin/install-python-macos.sh + script: + - VERIONS="3.5.3" ./bin/test-wheels-macos.sh + after_script: + - 'true' + - os: osx + osx_image: xcode7.3 + language: generic + env: + - TWINE_REPOSITORY_URL=https://pypi.python.org/pypi + - PYPI_REPOSITORY_URL=https://testpypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak + install: + - VERSIONS="3.6.0" ./bin/install-python-macos.sh + script: + - VERIONS="3.6.0" ./bin/test-wheels-macos.sh + after_script: + - 'true' + - stage: pypi + language: generic + if: branch = master and repo = danielsuo/brainiak and tag IS present + - os: linux + dist: trusty + sudo: required + language: python + python: '3.4' + before_install: + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak and tag IS present + env: + - PYPI_REPOSITORY_URL=https://pypi.python.org + install: + - ./bin/test-wheels.sh + - os: osx + osx_image: xcode7.3 + language: generic + env: + - PYPI_REPOSITORY_URL=https://pypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak and tag IS present + install: + - VERSIONS="3.4.4" ./bin/install-python-macos.sh + script: + - VERIONS="3.4.4" ./bin/test-wheels-macos.sh + - os: osx + osx_image: xcode7.3 + language: generic + env: + - PYPI_REPOSITORY_URL=https://pypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak and tag IS present + install: + - VERSIONS="3.5.3" ./bin/install-python-macos.sh + script: + - VERIONS="3.5.3" ./bin/test-wheels-macos.sh + - os: osx + osx_image: xcode7.3 + language: generic + env: + - PYPI_REPOSITORY_URL=https://pypi.python.org + before_install: + - brew update + - brew install python3 + - python3 -m pip install -U pip awscli twine + if: branch = master and repo = danielsuo/brainiak and tag IS present + install: + - VERSIONS="3.6.0" ./bin/install-python-macos.sh + script: + - VERIONS="3.6.0" ./bin/test-wheels-macos.sh diff --git a/bin/README.md b/bin/README.md new file mode 100755 index 000000000..82d85fba8 --- /dev/null +++ b/bin/README.md @@ -0,0 +1,70 @@ +# Wheel to-dos + +# TODO +- Investigate [caching](https://docs.travis-ci.com/user/caching) for pip / brew and for getting wheels from job to job +- Problematic issues with installs + - `pip install` hangs + - `brew` hangs when updating, or unable to download bottle -> forces compilation + - `apt` unreachable + - Python packages incorrectly installed / files not found (e.g., six) +- Deal with versions and tagging (pypi only accepts non-dev tags) +- Recreate and encrypt keys for brainiak/brainiak +- Update documentation +- End-to-end test +- Get things working on Jenkins +- Verify using other MPI (e.g., Intel MPI, mpich3) +- Build for other OSs (e.g., xcode[VERSION], 32-bit Linux, Windows) + - Rename MacOS wheels for multiple versions? +- Test on other python distros (e.g., conda)? +- Change quiet to no progress bar (merged, but not released in pip. See [here](https://github.com/pypa/pip/pull/4194/commits/0124945031e93236c2300eb45c2f962768be62d8)) + +# To verify +- Upload to S3 even for PRs (custom deploy) + - NOTE: untrusted builds don't have access to encrypted environment variables or deploy +- Download from S3 and install +- Pick up from the correct travis commit +- Upload to TestPyPI +- Upload wheels to PyPI (uncomment) +- Only upload to PyPI on tagged master +- Only need to build mpi4py occasionally; not with every commit, and not with every job +- mpi4py should always commit to s3://brainiak/.whl + +# Completed +- Fix stages; it appears that everything just gets lumped into a single stage +- Test-with-deps: test source with dependencies + can be installed (doesn't check install now) +- Test-wout-deps: test binary without dependencies + can be installed (doesn't check tests now) +- Confirm stages that run only on master commit to main don't run for PRs other branchs +- Clean up buckets appropriately +- Change upload / download to $TRAVIS_COMMIT/.whl +- Test local install (```pip``` vs. ```python setup.py```, editable vs. not) +- Only deploy on tag (uncomment) +- Deploy only on master (uncomment in ```.travis.yml```) +- Default implementation should grab wheels matching current brainiak version +- Allow setup.py to take argument for mpi4py wheel location +- Modify ```setup.py``` to handle conditional MPI install +- Move all scripts to bin +- Upload wheels to S3 +- Split test and build stages +- Add bdist exception for editable installs +- Enforce BrainIAK dependencies while installing dev requirements +- Add back quiet / silent flags +- Refactor build and test scripts +- Consolidate python version numbers across various scripts +- Rationalize variables; different scripts call the same things different things +- Resolve coverage running pytest as module +- Split wheel build, test install, test run into three travis stages +- Separate install python and build wheels in MacOS +- Create brainiak/manylinux Docker image +- Have dev and prod S3 buckets for testing wheels +- Add LICENSE into whl file +- Checkout mpi4py tag so we always create the same wheel +- Figure out ```pr-check.sh```, other testing scripts, and documentation + - Change ```pr-check.sh``` to optionally take a ```PYTHON``` argument, using ```python3``` if unspecified + - Change ```pr-check.sh``` to optionally install a wheel (or automatically find the wheel based on the ```PYTHON```argument) insead of from source +- Restore ```.travis.yml``` and make sure previous tests working +- Linux build script +- Linux test script +- MacOS build script +- MacOS test script +- ~~Export PATH and clean up logic~~ +- ~~Package documentation [link](http://python-packaging.readthedocs.io/en/latest/non-code-files.html)~~ diff --git a/bin/build-dist-macos.sh b/bin/build-dist-macos.sh new file mode 100755 index 000000000..8159a6f47 --- /dev/null +++ b/bin/build-dist-macos.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +# Cause the script to exit if a single command fails. +set -e + +# Show explicitly which commands are currently running. +set -x + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd) +WHEEL_DIR=$SCRIPT_DIR/../dist +mkdir -p $WHEEL_DIR + +# git clone -q https://bitbucket.org/mpi4py/mpi4py +# pushd mpi4py +# git checkout 3.0.0 +# popd + +for VERSION in $VERSIONS +do + MAJOR=${VERSION%.*} + # Replicate readlink -f + PYTHON_DIR=$(dirname $(realpath $(which python$MAJOR))) + PYTHON=$PYTHON_DIR/python$MAJOR + DELOCATE=$(dirname $PYTHON)/delocate-wheel + + git clean -f -f -x -d -q -e dist + + # Use virtual environments because we want to install later + pushd .. + $PYTHON -m venv venv + source venv/bin/activate + popd + + # pushd mpi4py + # git clean -f -f -x -d -q + # $PYTHON setup.py -q bdist_wheel -d $WHEEL_DIR + # $DELOCATE $WHEEL_DIR/*.whl + # popd + + $PYTHON -m pip install . + $PYTHON setup.py bdist_wheel -d $WHEEL_DIR + $DELOCATE $WHEEL_DIR/*.whl + + # Build source distribution + if [ $MAJOR == '3.4' ] + then + $PYTHON setup.py sdist + fi + + deactivate + rm -rf ../venv +done + +# rm -rf mpi4py diff --git a/bin/build-dist-manylinux1.sh b/bin/build-dist-manylinux1.sh new file mode 100755 index 000000000..c17e3d3d8 --- /dev/null +++ b/bin/build-dist-manylinux1.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# Caust the script to exit if a single command fails. +set -e + +# Show explicitly which commands are currently running. +set -x + +# Install dependencies +yum install -y -q \ + mpich2-devel + +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd) +WHEEL_DIR=$SCRIPT_DIR/../dist +mkdir -p $WHEEL_DIR +mkdir -p tmp + +# - Use the brainiak source stored in /brainiak via docker run command +# - Use head of mpi4py (we can pick some more suitable tag) +# git clone -q https://bitbucket.org/mpi4py/mpi4py /mpi4py +# pushd /mpi4py +# git checkout 3.0.0 +# popd + +for VERSION in cp34-cp34m cp35-cp35m cp36-cp36m; do + PYTHON=/opt/python/$VERSION/bin/python + $PYTHON -m pip install -U pip wheel + + # pushd /mpi4py + # git clean -f -f -x -d -q + # $PYTHON setup.py -q bdist_wheel -d tmp + # auditwheel repair tmp/*.whl -w $WHEEL_DIR + # popd + + git clean -f -f -x -d -q -e dist + $PYTHON -m pip install -q . + $PYTHON setup.py -q bdist_wheel -d tmp + auditwheel repair tmp/*.whl -w $WHEEL_DIR + + # Build source distribution + if [ $VERSION = "cp34-cp34m" ] + then + $PYTHON setup.py -q sdist + fi +done + +# rm -rf /mpi4py +rm -rf tmp diff --git a/bin/build-dist.sh b/bin/build-dist.sh new file mode 100755 index 000000000..7d661298f --- /dev/null +++ b/bin/build-dist.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker run --rm -w /src -v `pwd`:/src -ti brainiak/manylinux ./bin/build-dist-manylinux1.sh diff --git a/bin/build-wheel.sh b/bin/build-wheel.sh new file mode 100644 index 000000000..86bb02b1e --- /dev/null +++ b/bin/build-wheel.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + + +$PYTHON -m pip install -q . +$PYTHON setup.py -q bdist_wheel -d tmp + + platform="unknown" + unamestr="$(uname)" + if [[ "$unamestr" == "Linux" ]] + then + auditwheel repair tmp/*.whl -w $WHEEL_DIR + elif [[ "$unamestr" == "Darwin" ]] + then + $DELOCATE tmp/*.whl + mv tmp/*.whl $WHEEL_DIR + else + echo "Unrecognized platform." + exit 1 + fi diff --git a/bin/generate-yaml.py b/bin/generate-yaml.py new file mode 100755 index 000000000..dcff7a06e --- /dev/null +++ b/bin/generate-yaml.py @@ -0,0 +1,318 @@ +#!/usr/bin/env python3 + +# TODO: the first job in a stage must be part of the job dictinoary, not an +# element in the job array. This results in a dummy job being created + +# NOTE: we use deep copies even though we could rely on YAML references + +# Stages: (Travis capitalizes first letter and lowers the rest) +# - Test: test source and source distribution, dependencies +# - Build: build wheels and test +# - deploy to s3 +# - S3: download from s3 and test +# - deploy to testpypi +# - Testpypi: download wheels from test PyPI and test +# - deploy to pypi +# - Pypi: download wheels from PyPI and test + +import yaml +import copy + +from collections import OrderedDict + +# Ensure yaml works with OrderedDict +from yaml import Loader, Dumper +from yaml.representer import SafeRepresenter + +_mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG + + +def dict_representer(dumper, data): + return dumper.represent_dict(data.items()) + + +def dict_constructor(loader, node): + return OrderedDict(loader.construct_pairs(node)) + + +Dumper.add_representer(OrderedDict, dict_representer) +Loader.add_constructor(_mapping_tag, dict_constructor) + +Dumper.add_representer(str, + SafeRepresenter.represent_str) + +travis = '.travis.yml' +repo = 'danielsuo/brainiak' +twine_username = 'brainiak-bot' +versions = ['3.4.4', '3.5.3', '3.6.0'] +majors = [version[:-2] for version in versions] + +# S3 configuration +bucket = 'brainiak' +region = 'us-east-1' +access_key_id = 'AKIAJKBW6H2VKKQDHILQ' +secret_access_key = 'A1wrWjyfpCAPCYfu/Y4JpKOgjaAbZTubDfNur1K4rXqLWsi5JHWW9UUcmVXHHZGxy5wM56dTa5Y5smarjNno+KU21ioZ9u4LKthbMq/aDtLc9bMXbWJ+k1fu+jJT5yZ174NwrYFtyOrkwRcJR7ttfBIapY31IgCCkNQ6NtzFLFsf0rNEaW1K0lZIj8k0MvD5aJ77Pi06zRRZdwTibAu27w+FHQzDYTRfPGcutlS/3zfdvBEWC7FpZK772bJFfUsSZ3tUy8BBLhQztnssC3jCvIv4zFkeG7PnZULPjq4f/0EfvvNt7aF2cxsdbwG16L2Ia++/aS98qgA9+f5u+LB83rt7fWmxSyc47kRmyrXKipv9o/mDjZXW7OmlqHVgwRUBkZ6suPwrrv1ZBAbYCk8uNk5wGt69OyJDsyegEoKSSGkYDhQZ8I5JgbiB1myJf2wUVyyd8g71U0/W0CtboqCXiHYZWPIhyIYzN6n044IoNpWleusIAABqp2TU/zSAM+sOjqJqZ59mNVYU5hpPUGVJPuoZ9TW63oOX1q/eO5XSnl2asoNFjjooTr3A38YQ5PdWz+IbIlBJL35pZgnzxOkyskNIYuwOTexLqx7G4nZj9wgDxUUL8UA48wmiu8MnfNxBeZcnmxPqRPVQM3qf9nhpaM3OaX3Cs/OO5leHK/BSPPg=' +secret_access_key_env = 'ZkEtQyYsMAiomNX0UCgF8ExvCIselUY9z0cSdbatmiSSHRlKxjBk9wS1CQM+Y9+Eeri7aUkS1PbCrz3vNVbTtj9VEZJjxpFNDzmKziIDBJm6DCE5k0JV7dQbKS1jPrX1/+Az1mMCy+EN6ZtA2WUXCLZsqkmG9rdEUJMBM5SYFBCfTzrTWxwqJZU46ErKrdn5d+ywi9O4Cu7/i2JjRpEejC2iYWSDNEgqHvxDo6lbG5sVSn2pns4Dzqwh3n9zLxqWeQalvgisjcGuBoRLTyCEeJNgDL43PkdgqflyanleLzlDCvTowfrqWZF7Xn8TmkZslunlRe3zMBaiDHxg1h60rju4inXzIahHL7keFDB32QWBZb33jf2BZRStjjJDgO8jvPMBmhxwV5LLzSuW0cHxEm+f5O6eWPQjT4dX04dGMabYU7iMgR5t0+uZDqeW17J4py75UXC3u4KWkzWKvRIe78nEXSE4F1SSiV9eQWdBtkJcF0i3UQzQW5quKUWcYp2sw4SEyz/zCc+CLCtjheIg39rrWVSRKL3t4iCebxyVU/YLgOpBW2BVYtY4vUsyLma7Mr+rcVLZggcU4qnoz3Q6jgDwPP1jeNC9RZNw/Tida9hQgykmqNM9OPkgrRVgL3g0NfYMdysupqezplywjvIDODh11rg8+Cjk6nRb28S4wVQ=' +twine_password = 'JWCpO0caWN4M0xaGPqKMPPHg8WB6sybPQPcCFtXc+dwsD3mu+mDv7lGV3l/5sRoDqIi87uhXBtfnPVKao7AdPaLZoZc8ktBP3b7QwG49U479odN1OuxZcdLBmQBPZlAnqyH7wbM0xhkskr6Yl8gkKWynDfyod1GWbr/plcGRuX82unvuq5tkX8gNSdJ4vHbHGGcvgUbDkyEJdQz7y96qJn7VUpD4FhujO7bGyGVB2ZVcAcEyHuQZiyPxknrXSE0dnk0IeEH77DY40SLpPf5WG4fl/gGYLlccw2ZmBOAOYYE/nq9HF+/b5gnYp+0zLUCgA1tK9uC1UdT3IE24hr8XJMdMIog1BSVjv5k31RjFi0mwCVaasiCN/IOYvwrg+yod5UjXBag7f/Wjo3AEoUSByFERTr+Y7Gs+uCHQKT8Jw4mCTwmI+3JnH+4ZcuMbIQUr10uaocmpNuWhA1IVMTewIKjaAgY9qWy2X5e7X5/faYXlUhV68QiA5TB4MCvA+jeq84+bIk00HBnOadL/iAKFVhBlbkqe8OZWNbsBKFERWEVJFIoOLMg43NORWRs54Mi3eie4bNxWUwwPsxHR4FoBSeWjm2yorDbUIFDK57xZH3jUFu11xSXW3x9BqGWdgQg71j/P1nVKGMoHvPUBX0wO2CLQnSl6katSwgM6aLeJl1U=' + +data = OrderedDict() + +data['branches'] = OrderedDict({ + 'only': ['master'] +}) + +data['env'] = OrderedDict({ + 'global': [ + 'BRAINIAK_REPO=%s' % repo, + 'TWINE_USERNAME=%s' % twine_username, + 'AWS_ACCESS_KEY_ID="%s"' % access_key_id, + 'AWS_DEFAULT_REGION="%s"' % region, + { + 'secure': twine_password + }, + { + 'secure': secret_access_key_env + } + ] +}) + +data['jobs'] = OrderedDict({'include': []}) +jobs = data['jobs']['include'] + +conditions = { + 'pr': 'branch = master', + 'master': 'branch = master and repo = %s' % repo, + 'tag': 'branch = master and repo = %s and tag IS present' % repo +} + +actions = { + 'download-s3': 'aws s3 cp s3://brainiak/$TRAVIS_COMMIT/dist dist --recursive', + + # TODO: make this for master only + 'upload-s3': 'aws s3 cp dist s3://brainiak/$TRAVIS_COMMIT/dist --recursive --acl=public-read', + + # TODO: make this for master only / possibly only for tag + # 'upload-testpypi': 'if [ ! -z $TRAVIS_TAG ]; then twine upload dist/*; fi', + 'upload-testpypi': 'twine upload dist/*', + + # TODO: populate this command + 'upload-pypi': 'true' +} + +linux = OrderedDict({ + 'os': 'linux', + 'dist': 'trusty', + 'sudo': 'required', + 'language': 'python', + 'python': '3.4', + 'before_install': ['python3 -m pip install -U pip awscli twine'] +}) + +macos = OrderedDict({ + 'os': 'osx', + 'osx_image': 'xcode7.3', + 'language': 'generic', + 'env': ['HOMEBREW_NO_AUTO_UPDATE=1'], + 'before_install': [ + 'brew update', + 'brew install python3', + 'python3 -m pip install -U pip awscli twine' + ] +}) + +############################################################################### +# Stage: Test +############################################################################### +jobs.append(OrderedDict({'stage': 'test', 'language': 'generic'})) + +# Linux +test_linux = copy.deepcopy(linux) +test_linux['script'] = ['./bin/pr-check.sh'] +test_linux['addons'] = { + 'apt': { + 'packages': ['build-essential libgomp1 libmpich-dev mpich'] + } +} + +for major in majors: + block = copy.deepcopy(test_linux) + block['python'] = major + jobs.append(block) + +# MacOS +macos_build_env = [ + 'CC=/usr/local/opt/llvm/bin/clang', + 'CXX=/usr/local/opt/llvm/bin/clang++', + 'LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib $LDFLAGS"', + 'CPPFLAGS="-I/usr/local/opt/llvm/include $CPPFLAGS"', + 'HOMEBREW_NO_AUTO_UPDATE=1' +] + +test_macos = copy.deepcopy(macos) +test_macos['env'] = macos_build_env +test_macos['install'] = ['brew install llvm mpich'] +test_macos['script'] = ['./bin/pr-check.sh'] + +for osx in ['xcode7.3', 'xcode8']: + block = copy.deepcopy(test_macos) + block['osx_image'] = osx + jobs.append(block) + +############################################################################### +# Stage: Build +############################################################################### +jobs.append(OrderedDict({ + 'stage': 'build', + 'language': 'generic', + 'if': conditions['pr'] +})) + +# Linux +build_linux = copy.deepcopy(linux) +build_linux['if'] = conditions['pr'] +build_linux['install'] = ['./bin/build-dist.sh'] +build_linux['script'] = ['./bin/test-wheels.sh'] +build_linux['after_script'] = [actions['upload-s3']] +jobs.append(build_linux) + +# MacOS +build_macos_env = copy.deepcopy(test_macos['env']) +build_macos_env.extend(['ARCHFLAGS="-arch x86_64"']) + +build_macos = copy.deepcopy(macos) +build_macos['if'] = conditions['pr'] +build_macos['env'] = build_macos_env +build_macos['install'] = ['brew install llvm mpich'] + +for version in versions: + block = copy.deepcopy(build_macos) + + block['install'].extend([ + 'VERSIONS="%s" ./bin/install-python-macos.sh' % version, + 'VERSIONS="%s" ./bin/build-dist-macos.sh' % version + ]) + + block['before_script'] = [ + 'brew uninstall -f --ignore-dependencies llvm mpich', + 'unset CC', + 'unset CXX', + 'unset LDFLAGS', + 'unset CPPFLAGS', + 'unset ARCHFLAGS' + ] + + block['script'] = [ + 'VERSIONS="%s" ./bin/test-wheels-macos.sh' % version + ] + + block['after_script'] = [actions['upload-s3']] + + jobs.append(block) + +############################################################################### +# Stage: S3 +############################################################################### +jobs.append(OrderedDict({ + 'stage': 's3', + 'language': 'generic', + 'if': conditions['pr'] +})) + +# Linux +s3_linux = copy.deepcopy(linux) +s3_linux['if'] = conditions['pr'] +s3_linux['env'] = 'TWINE_REPOSITORY_URL=https://test.pypi.org/legacy' +s3_linux['install'] = actions['download-s3'] +s3_linux['script'] = ['./bin/test-wheels.sh'] +s3_linux['after_script'] = [actions['upload-testpypi']] +jobs.append(s3_linux) + +# MacOS +s3_macos = copy.deepcopy(macos) +s3_macos['if'] = conditions['pr'] +s3_macos['before_install'].append(actions['download-s3']) + +for version in versions: + block = copy.deepcopy(s3_macos) + block['install'] = [ + 'VERSIONS="%s" ./bin/install-python-macos.sh' % version + ] + + block['script'] = [ + 'VERIONS="%s" ./bin/test-wheels-macos.sh' % version + ] + + block['after_script'] = [actions['upload-testpypi']] + + jobs.append(block) + +############################################################################### +# Stage: Testpypi +############################################################################### +jobs.append(OrderedDict({ + 'stage': 'testpypi', + 'language': 'generic', + 'if': conditions['master'] +})) + +testpypi_linux = copy.deepcopy(linux) +testpypi_linux['if'] = conditions['master'] +testpypi_linux['env'] = [ + 'TWINE_REPOSITORY_URL=https://pypi.python.org/pypi', + 'PYPI_REPOSITORY_URL=https://testpypi.python.org' +] +testpypi_linux['script'] = './bin/test-wheels.sh' +testpypi_linux['after_script'] = [actions['upload-pypi']] +jobs.append(testpypi_linux) + +testpypi_macos = copy.deepcopy(build_macos) +testpypi_macos['if'] = conditions['master'] +testpypi_macos['env'] = copy.deepcopy(testpypi_linux['env']) + +for version in versions: + block = copy.deepcopy(testpypi_macos) + block['install'] = [ + 'VERSIONS="%s" ./bin/install-python-macos.sh' % version + ] + + block['script'] = [ + 'VERIONS="%s" ./bin/test-wheels-macos.sh' % version + ] + + block['after_script'] = [actions['upload-pypi']] + + jobs.append(block) + +############################################################################### +# Stage: Pypi +############################################################################### +jobs.append(OrderedDict({ + 'stage': 'pypi', + 'language': 'generic', + 'if': conditions['tag'] +})) + +pypi_linux = copy.deepcopy(linux) +pypi_linux['if'] = conditions['tag'] +pypi_linux['env'] = ['PYPI_REPOSITORY_URL=https://pypi.python.org'] +pypi_linux['install'] = ['./bin/test-wheels.sh'] +jobs.append(pypi_linux) + +pypi_macos = copy.deepcopy(macos) +pypi_macos['if'] = conditions['tag'] +pypi_macos['env'] = copy.deepcopy(pypi_linux['env']) + +for version in versions: + block = copy.deepcopy(pypi_macos) + block['install'] = [ + 'VERSIONS="%s" ./bin/install-python-macos.sh' % version + ] + + block['script'] = [ + 'VERIONS="%s" ./bin/test-wheels-macos.sh' % version + ] + + jobs.append(block) + +with open(travis, 'w') as yml: + yaml.dump(data, yml, default_flow_style=False) diff --git a/bin/install-python-macos.sh b/bin/install-python-macos.sh new file mode 100755 index 000000000..7540bd55f --- /dev/null +++ b/bin/install-python-macos.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -ex + +MACPYTHON_URL=https://www.python.org/ftp/python +DOWNLOAD_DIR=python_downloads +mkdir -p $DOWNLOAD_DIR + +for VERSION in $VERSIONS +do + PKG="python-$VERSION-macosx10.6.pkg" + MAJOR=${VERSION%.*} + + # Install Python. + INST_PATH=$DOWNLOAD_DIR/$PKG + curl $MACPYTHON_URL/$VERSION/$PKG > $INST_PATH + sudo installer -pkg $INST_PATH -target / + + pip$MAJOR install -q -U pip delocate + + # NOTE: https://stackoverflow.com/questions/41691327/ssl-sslerror-ssl-certificate-verify-failed-certificate-verify-failed-ssl-c + if [[ $MAJOR > "3.5" ]] + then + sudo /Applications/Python\ $MAJOR/Install\ Certificates.command + fi +done diff --git a/bin/install-python-manylinux1.sh b/bin/install-python-manylinux1.sh new file mode 100755 index 000000000..4562fdb31 --- /dev/null +++ b/bin/install-python-manylinux1.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +for VERSION in $VERSIONS +do + # Download source + wget https://github.com/python/cpython/archive/v$VERSION.tar.gz + tar zxf v$VERSION + + # Install + pushd cpython-$VERSION + ./configure --enable-shared + make -j + make install + popd + + PYTHON=python${VERSION%.*} + $PYTHON -m pip install -U pip wheel + + # Clean up + rm -rf cpython-$VERSION v$VERSION +done diff --git a/bin/install-test-deps-manylinux1.sh b/bin/install-test-deps-manylinux1.sh new file mode 100755 index 000000000..37a020238 --- /dev/null +++ b/bin/install-test-deps-manylinux1.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -ex + +# Install and configure ssh so we can ssh locally for MPI tests +yum install -y -q openssh-server mpich2 +service sshd start + +ssh-keygen -f ~/.ssh/id_rsa -t rsa -N '' -b 4096 +cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys +chmod 600 ~/.ssh/authorized_keys + +echo "Host *" >> ~/.ssh/config +echo " StrictHostKeyChecking no" >> ~/.ssh/config diff --git a/bin/install-wheel.sh b/bin/install-wheel.sh new file mode 100644 index 000000000..1329677cd --- /dev/null +++ b/bin/install-wheel.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + + diff --git a/pr-check.sh b/bin/pr-check.sh similarity index 54% rename from pr-check.sh rename to bin/pr-check.sh index 045d73e4e..ca39e351b 100755 --- a/pr-check.sh +++ b/bin/pr-check.sh @@ -16,7 +16,7 @@ # Check readiness for pull request -set -e +set -ex if [ ! -f brainiak/__init__.py ] then @@ -27,7 +27,11 @@ fi basedir=$(pwd) function create_venv_venv { + if [ ! -z $PYTHON ]; then + $PYTHON -m venv ../$1 + else python3 -m venv ../$1 + fi } function activate_venv_venv { @@ -90,11 +94,14 @@ else fi # Check if running in an sdist -if git ls-files --error-unmatch pr-check.sh 2> /dev/null +if [ ! -z $WHEEL_DIR ] || [ ! -z $PYPI_REPOSITORY_URL ] +then + dist_mode='--bdist-mode' +elif git ls-files --error-unmatch pr-check.sh 2> /dev/null then git clean -Xf . else - sdist_mode="--sdist-mode" + dist_mode="--sdist-mode" fi venv=$(mktemp -u brainiak_pr_venv_XXXXX) || \ @@ -111,34 +118,75 @@ $activate_venv $venv || { # brainiak will also be installed together with the developer dependencies, but # we install it first here to check that installation succeeds without the # developer dependencies. -python3 -m pip install $ignore_installed -U -e . || \ - exit_with_error_and_venv "Failed to install BrainIAK." -# install developer dependencies -python3 -m pip install $ignore_installed -U -r requirements-dev.txt || \ - exit_with_error_and_venv "Failed to install development requirements." +if [ ! -z $WHEEL_DIR ] +then + platform="unknown" + unamestr="$(uname)" + if [[ "$unamestr" == "Linux" ]] + then + platform="manylinux" + elif [[ "$unamestr" == "Darwin" ]] + then + platform="macosx" + else + echo "Unrecognized platform." + exit 1 + fi + + MAJOR=$(python3 -c "import sys; v = sys.version_info; print(v[0] * 10 + v[1])") + # MPI4PY_WHEEL=$(find $WHEEL_DIR -type f | grep cp$MAJOR | grep mpi4py | grep $platform) + BRAINIAK_WHEEL=$(find $WHEEL_DIR -type f | grep cp$MAJOR | grep brainiak | grep $platform) + + # python3 -m pip install -q $MPI4PY_WHEEL + python3 -m pip install -q $BRAINIAK_WHEEL + + # We don't want to install brainiak from source + { echo $BRAINIAK_WHEEL; tail -n +2 requirements-dev.txt; } | python3 -m pip install -q -r /dev/stdin +elif [ ! -z $PYPI_REPOSITORY_URL ] +then + python3 -m pip install -q -i $PYPI_REPOSITORY_URL brainiak + + tail -n +2 requirements-dev.txt | python3 -m pip install --index-url $PYPI_REPOSITORY_URL \ + --extra-index-url="https://pypi.python.org/pypi" brainiak -q -r /dev/stdin +else + python3 -m pip install $ignore_installed -U -e . || \ + exit_with_error_and_venv "Failed to install BrainIAK." -# static analysis -./run-checks.sh || \ - exit_with_error_and_venv "run-checks failed" + # install developer dependencies + python3 -m pip install $ignore_installed -U -r requirements-dev.txt || \ + exit_with_error_and_venv "Failed to install development requirements." +fi + +# Only run source checks if we aren't in binary mode +if [ ${dist_mode:-default} != '--bdist-mode' ] +then + # static analysis + ./bin/run-checks.sh || \ + exit_with_error_and_venv "run-checks failed" +fi # run tests -./run-tests.sh $sdist_mode || \ +./bin/run-tests.sh $dist_mode || \ exit_with_error_and_venv "run-tests failed" -# build documentation -cd docs -export THEANO_FLAGS='device=cpu,floatX=float64,blas.ldflags=-lblas' - -if [ ! -z $SLURM_NODELIST ] +# Only build documentation if we aren't in binary mode +if [ ${dist_mode:-default} != '--bdist-mode' ] then - make_wrapper="srun -n 1" + # build documentation + cd docs + export THEANO_FLAGS='device=cpu,floatX=float64,blas.ldflags=-lblas' + + if [ ! -z $SLURM_NODELIST ] + then + make_wrapper="srun -n 1" + fi + $make_wrapper make || { + cd - + exit_with_error_and_venv "make docs failed" + } + cd - fi -$make_wrapper make || { - cd - - exit_with_error_and_venv "make docs failed" -} -cd - $deactivate_venv $remove_venv $venv diff --git a/run-checks.sh b/bin/run-checks.sh similarity index 99% rename from run-checks.sh rename to bin/run-checks.sh index 6df6cfe9f..92612f7a1 100755 --- a/run-checks.sh +++ b/bin/run-checks.sh @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e +set -ex set -o pipefail flake8 --config setup.cfg brainiak diff --git a/bin/run-manylinux1.sh b/bin/run-manylinux1.sh new file mode 100755 index 000000000..7b886a5c8 --- /dev/null +++ b/bin/run-manylinux1.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker run --rm -w /src -v `pwd`:/src -ti brainiak/manylinux /bin/bash diff --git a/run-tests.sh b/bin/run-tests.sh similarity index 55% rename from run-tests.sh rename to bin/run-tests.sh index fc1b18da5..fd5d78139 100755 --- a/run-tests.sh +++ b/bin/run-tests.sh @@ -14,14 +14,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e +set -ex # When installing from sdist, the pip freeze output cannot be used to # detect editable mode. Use the "--sdist-mode" flag. -sdist_mode=$1 +dist_mode=$1 python3 -m pip freeze | grep -qi /brainiak \ - || [ ${sdist_mode:-default} = "--sdist-mode" ] \ + || [ ${dist_mode:-default} = "--sdist-mode" ] \ + || [ ${dist_mode:-default} = "--bdist-mode" ] \ || { echo "You must install brainiak in editable mode"` `" before calling "$(basename "$0") @@ -31,33 +32,41 @@ python3 -m pip freeze | grep -qi /brainiak \ # Define MKL env variable that is required by Theano to run with MKL 2018 export MKL_THREADING_LAYER=GNU -mpi_command=mpiexec -if [ ! -z $SLURM_NODELIST ] -then +if [ -z $mpi_command ]; then + mpi_command=mpiexec +elif [ ! -z $SLURM_NODELIST ]; then mpi_command=srun fi -$mpi_command -n 2 coverage run -m pytest -coverage combine +if [ ${dist_mode:-default} = "--bdist-mode" ] +then + $mpi_command -n 2 $(which pytest) -# Travis error workaround -coverage_report=$(mktemp -u coverage_report_XXXXX) || { - echo "mktemp -u error" >&2; - exit 1; -} +# Only run coverage for editable or source distribution +else + $mpi_command -n 2 $(which coverage) run -m pytest + coverage combine -set +e -coverage report > $coverage_report -report_exit_code=$? + # Travis error workaround + coverage_report=$(mktemp -u coverage_report_XXXXX) || { + echo "mktemp -u error" >&2; + exit 1; + } -coverage html -coverage xml + set +e + coverage report > $coverage_report + report_exit_code=$? -cat $coverage_report -rm $coverage_report + coverage html + coverage xml -if [ $report_exit_code = 2 ] -then - echo "ERROR: Coverage too low." + cat $coverage_report + rm $coverage_report + + if [ $report_exit_code = 2 ] + then + echo "ERROR: Coverage too low." + fi + exit $report_exit_code fi -exit $report_exit_code + diff --git a/bin/test-wheels-macos.sh b/bin/test-wheels-macos.sh new file mode 100755 index 000000000..4f96e7d7b --- /dev/null +++ b/bin/test-wheels-macos.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash + +# Caust the script to exit if a single command fails. +set -e + +# Show explicitly which commands are currently running. +set -x + +if [ -z $PYPI_REPOSITORY_URL ] +then + SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd) + WHEEL_DIR=$SCRIPT_DIR/../dist + + # Test whether we can install without any dependencies + # TODO: delete once we have setup.py setup correctly + for VERSION in $VERSIONS + do + MAJOR=${VERSION%.*} + PYTHON_DIR=$(dirname $(realpath $(which python$MAJOR))) + PYTHON=$PYTHON_DIR/python$MAJOR + WHEEL_VERSION=$(echo $MAJOR | tr -d '.') + + git clean -f -f -x -d -q -e dist + + pushd .. + $PYTHON -m venv venv + source venv/bin/activate + popd + + # Find the appropriate wheel by grepping for the Python version. + # MPI4PY_WHEEL=$(find $WHEEL_DIR -type f -maxdepth 1 -print | grep "$WHEEL_VERSION" | grep mpi4py | grep macosx) + + # TODO: this will actually pick up both wheels since brainiak is in the path + BRAINIAK_WHEEL=$(find $WHEEL_DIR -type f -maxdepth 1 -print | grep "$WHEEL_VERSION" | grep brainiak | grep macosx) + + # $PYTHON -m pip install $MPI4PY_WHEEL + python3 -m pip install $BRAINIAK_WHEEL + + deactivate + rm -rf ../venv + done +fi + +brew install mpich + +# Test packages +for VERSION in $VERSIONS +do + # TODO: refactor this out + MAJOR=${VERSION%.*} + PYTHON_DIR=$(dirname $(realpath $(which python$MAJOR))) + PYTHON=$PYTHON_DIR/python$MAJOR + git clean -f -f -x -d -q -e dist + + if [ -z $PYPI_REPOSITORY_URL ] + then + WHEEL_DIR=$WHEEL_DIR PYTHON=$PYTHON $SCRIPT_DIR/pr-check.sh + else + PYTHON=$PYTHON $SCRIPT_DIR/pr-check.sh + fi +done diff --git a/bin/test-wheels-manylinux1.sh b/bin/test-wheels-manylinux1.sh new file mode 100755 index 000000000..c0a722a9f --- /dev/null +++ b/bin/test-wheels-manylinux1.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Caust the script to exit if a single command fails. +set -e + +# Show explicitly which commands are currently running. +set -x + +# If we're downloading from repository, skip +if [ -z $PYPI_REPOSITORY_URL ] +then + SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE:-$0}")"; pwd) + WHEEL_DIR=$SCRIPT_DIR/../dist + + # Test whether we can install without any dependencies + # TODO: delete once we have setup.py setup correctly + for VERSION in cp34-cp34m cp35-cp35m cp36-cp36m; do + # MPI4PY_WHEEL=$(find $WHEEL_DIR -type f | grep $VERSION | grep mpi4py | grep manylinux) + BRAINIAK_WHEEL=$(find $WHEEL_DIR -type f | grep $VERSION | grep brainiak | grep manylinux) + + PYTHON=/opt/python/${VERSION}/bin/python3 + + git clean -f -f -x -d -q -e dist + + $PYTHON -m venv venv + source venv/bin/activate + + # $PYTHON -m pip install -q $MPI4PY_WHEEL + $PYTHON -m pip install -q $BRAINIAK_WHEEL + + deactivate + rm -rf venv + done +fi + +# Separate tests into a separate loop because we want to make sure +# we can install brainiak without installing mpich, but require mpiexec during tests +./bin/install-test-deps-manylinux1.sh +for VERSION in cp34-cp34m cp35-cp35m cp36-cp36m; do + git clean -f -f -x -d -q -e dist + + if [ -z $PYPI_REPOSITORY_URL ] + then + mpi_command=mpiexec.hydra \ + WHEEL_DIR=$WHEEL_DIR \ + PYTHON=/opt/python/$VERSION/bin/python \ + $SCRIPT_DIR/pr-check.sh + else + mpi_command=mpiexec.hydra \ + PYTHON=/opt/python/$VERSION/bin/python \ + $SCRIPT_DIR/pr-check.sh + fi +done diff --git a/bin/test-wheels.sh b/bin/test-wheels.sh new file mode 100755 index 000000000..d149ec536 --- /dev/null +++ b/bin/test-wheels.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +docker run --rm -w /src -v `pwd`:/src -ti brainiak/manylinux ./bin/test-wheels-manylinux1.sh diff --git a/requirements-dev.txt b/requirements-dev.txt index 2a887129e..55c4e0005 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,6 +5,7 @@ flake8-print mypy numdifftools pytest +pyyaml restructuredtext-lint setuptools_scm sphinx diff --git a/setup.cfg b/setup.cfg index 80bfe24c9..67b78f40f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,3 +19,6 @@ parallel = True [coverage:report] fail_under = 90 + +[metadata] +license_file = LICENSE diff --git a/setup.py b/setup.py index c207b7258..e77f31f5b 100644 --- a/setup.py +++ b/setup.py @@ -6,12 +6,49 @@ import sys import setuptools from copy import deepcopy +from subprocess import check_call, CalledProcessError +import urllib.request assert sys.version_info >= (3, 4), ( "Please use Python version 3.4 or higher, " "lower versions are not supported" ) +# Attempt to install mpi4py +try: + result = check_call([sys.executable, '-m', 'pip', 'install', 'mpi4py']) +except CalledProcessError as e: + # MacOS: mpi4py-3.0.0-cp34-cp34m-macosx_10_6_intel.whl + # Linux: mpi4py-3.0.1a0-cp34-cp34m-manylinux1_x86_64.whl + + base_url = 'https://s3.amazonaws.com/brainiak/.whl/%s' + mpi4py_version = '3.0.0' + + # Determine wheel file name + # TODO: These templates may change / may need to become more advanced + wheel_file = 'mpi4py-%(mpi4py)s-cp%(py)d-cp%(py)dm-%(dist)s.whl' + wheel_file = wheel_file % { + 'mpi4py': mpi4py_version, + 'py': sys.version_info[0] * 10 + sys.version_info[1], + 'dist': 'macosx_10_6_intel' if sys.platform == 'darwin' + else 'manylinux1_x86_64' + } + + wheel_url = base_url % wheel_file + + # Download mpi4py wheel + local_filename, headers = urllib.request.urlretrieve(wheel_url, wheel_file) + + # Install mpi4py wheel + result = check_call([sys.executable, '-m', 'pip', + 'install', local_filename]) + + # TODO: Need better clean-up mechanism + os.system('rm -f %s' % local_filename) + + if result != 0: + sys.exit('ERROR: failed to install mpi4py') + here = os.path.abspath(os.path.dirname(__file__)) # Get the long description from the README file @@ -113,7 +150,10 @@ def finalize_options(self): setup( name='brainiak', - use_scm_version=True, + + # TODO: Change this back + # use_scm_version=True, + version='0.2.2', setup_requires=[ 'cython', 'numpy',