diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml new file mode 100644 index 00000000..7f877686 --- /dev/null +++ b/.github/workflows/build_wheels.yml @@ -0,0 +1,55 @@ +name: Build wheels + +on: workflow_dispatch + +jobs: + build_sdist: + name: Build source distribution + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history, necessary for git describe in setup.py + + - name: Build sdist + run: pipx run build --sdist + + - uses: actions/upload-artifact@v4 + with: + name: cibw-sdist + path: dist/*.tar.gz + + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + # macos-13 is an intel runner, macos-14 is apple silicon + os: [ubuntu-latest, macos-13, macos-14] + + steps: + - uses: actions/setup-python@v5 + with: + python-version: 3.8 + if: runner.os == 'macOS' && runner.arch == 'ARM64' + + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Fetch full history, necessary for git describe in setup.py + + - name: Checkout charm_src # is this best practice? should we use a submodule? + uses: actions/checkout@v4 + with: + repository: charmplusplus/charm + path: charm_src/charm + fetch-depth: 0 # Fetch full history, necessary for commit_str parsing in setup.py + + - name: Build wheels + env: + CIBW_SKIP: "*-manylinux_i686 *-manylinux_ppc64le *-manylinux_s390x *musllinux_*" + uses: pypa/cibuildwheel@v2.20.0 + + - uses: actions/upload-artifact@v4 + with: + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} + path: ./wheelhouse/*.whl diff --git a/.github/workflows/upload_pypi.yml b/.github/workflows/upload_pypi.yml new file mode 100644 index 00000000..82dcc76e --- /dev/null +++ b/.github/workflows/upload_pypi.yml @@ -0,0 +1,39 @@ +name: Publish wheels to pypi + +on: + workflow_dispatch: + inputs: + workflow_id: + description: "The workflow ID to pull wheels from" + required: true + type: string + +jobs: + publish: + name: Publish Python 🐍 distribution 📦 to PyPI + runs-on: ubuntu-latest + + environment: + name: pypi + url: https://pypi.org/p/charm4py + + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: dawidd6/action-download-artifact@v2 + with: + # download all the wheels + path: unmerged + run_id: ${{ github.event.inputs.workflow_id }} + workflow: build_wheels.yml + + - name: Merge files to dist + run: | + mkdir dist + mv unmerged/*/*.whl dist + mv unmerged/*/*.tar.gz dist + + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/upload_testpypi.yml b/.github/workflows/upload_testpypi.yml new file mode 100644 index 00000000..fc3c23a6 --- /dev/null +++ b/.github/workflows/upload_testpypi.yml @@ -0,0 +1,41 @@ +name: Publish wheels to testpypi + +on: + workflow_dispatch: + inputs: + workflow_id: + description: "The workflow ID to pull wheels from" + required: true + type: string + +jobs: + publish_test: + name: Publish Python 🐍 distribution 📦 to TestPyPI + runs-on: ubuntu-latest + + environment: + name: testpypi + url: https://test.pypi.org/p/charm4py + + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: dawidd6/action-download-artifact@v2 + with: + # download all the wheels + path: unmerged + run_id: ${{ github.event.inputs.workflow_id }} + workflow: build_wheels.yml + + - name: Merge files to dist + run: | + mkdir dist + mv unmerged/*/*.whl dist + mv unmerged/*/*.tar.gz dist + + - name: Publish distribution 📦 to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ diff --git a/docs/install.rst b/docs/install.rst index 2f079513..011fbfa6 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -7,59 +7,58 @@ Install Charm4py runs on Linux, macOS, Windows, Raspberry Pi, and a wide variety of clusters and supercomputer environments (including many supercomputers in the TOP500). -Charm4py runs on Python 3.4+. Charm4py has been tested with the +Charm4py runs on Python 3.7+. Charm4py has been tested with the following Python implementations: CPython (most common implementation) and PyPy_. .. _PyPy: https://pypy.org +Installing Charm4Py binaries (via pip) +--------------------------------------- -Installing Charm4Py on a laptop/personal machine ------------------------------------------------- +The easiest way to install Charm4Py is via pip. Currently, pip wheels are available for Linux and macOS. -This install process covers the installation of Charm4Py on a laptop or personal machine, as opposed to a cluster. +To install the latest release of Charm4Py, run:: -Before installing, you need the following prerequisites: - - CPython: numpy, greenlet and cython (``pip3 install 'numpy>=1.10.0' cython greenlet``) - - PyPy: none + $ pip install charm4py -You can get these prerequisites by running the following command:: +This will install the latest stable release of Charm4Py, using the default underlying Charm++ build +(see the `Charm++ manual`_ for more information on the different builds of Charm++). If you want to +use a specific Charm++ build, you can install and build Charm4Py from source. Note that the source distribution +is available via "pip install", but the standard from source build process is via "git clone", as outlined below. - $ pip3 install -r requirements.txt +Installing Charm4Py from source +------------------------------------------------------------ + +This install process covers the installation of Charm4Py from source. The first step is to clone the Charm4py repository from Git:: $ git clone https://github.com/charmplusplus/charm4py.git $ cd charm4py -Next, create a folder called charm_src in the charm4py repo, and then clone the Charm++ repo -into that folder:: +Next, clone the Charm++ repo into charm_src:: - $ mkdir charm_src && cd charm_src - $ git clone https://github.com/charmplusplus/charm.git + $ git clone https://github.com/charmplusplus/charm.git charm_src/charm -Once this is done, there are two ways to build Charm4py. The first way is to change back up -into the Charm4Py directory and run the install script:: +Once this is done, there are two ways to build Charm4py. The first is to simply run the installation +from the Charm4py root. This method will use the default Charm++ backend:: $ cd .. - $ python3 setup.py install + $ pip install . -The other option is to manually build Charm++ before building Charm4py. To do this, change to -the charm directory and run the following build command:: +The other option is to manually build Charm++ before building Charm4py. This may be necessary +if you want to configure Charm++ differently from the default. To do this, change to +the charm directory and run the following build command, then build Charm4Py:: $ cd charm - $ ./build charm4py netlrts-- -j --with-production - -For building on a laptop, you must use a netlrts build of Charm4Py. Check the Charm++ documentation -to identify the correct os and architecture command to pass into the build command. The -j option -is a cmake option that launches N threads for the make. - -Then, return to the charm4py directory and run setup.py:: - + $ ./build charm4py -j --with-production $ cd ../.. - $ python3 setup.py install + $ pip install . +Finally, if necessary, when installing dependencies or when running the install script, add the --user +option to the Python command to complete the installation without permission errors. After building, you can run Charm4py examples. One example you can try is array_hello.py, which can be run as follows:: @@ -67,22 +66,23 @@ array_hello.py, which can be run as follows:: $ cd examples/hello $ python -m charmrun.start +p2 array_hello.py -Installing Charm4Py on a cluster machine ----------------------------------------- +Choosing your target architecture when building from source +------------------------------------------------------------ -To install Charm4Py on a cluster machine, you will generally follow the same steps as above, but -with the following changes. First, when building Charm++, use the MPI build instead of the netlrts -build:: +When building from source, as described above, you must chose the appropriate target architecture. - $ ./build charm4py mpi-- -j --with-production - -Next, pass in the MPI option to the python setup script:: - - $ python3 setup.py install --mpi +For building on a laptop or personal machine, you must use a netlrts build of Charm4Py. +For example, to build for a personal machine running macOS with ARM processors, using 4 cmake +threads, you would run:: + + $ ./build charm4py netlrts-darwin-arm -j4 --with-production -Finally, if necessary, when installing dependencies or when running the install script, add the --user -option to the Python command to complete the installation without permission errors. +To install Charm4Py on a cluster machine, you will generally want to chose a different backend. +For example, to use Charm4Py with MPI, build the Charm backend as follows:: + $ ./build charm4py mpi-- -j --with-production +Check the Charm++ documentation to identify the correct os and architecture command +to pass into the build command. .. _manual: https://charm.readthedocs.io/en/latest/charm++/manual.html#installing-charm diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..472a059d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +[build-system] +requires = ["setuptools>=43.0.0", "cython>=3.0.0", "numpy>=1.10.0"] +build-backend = "setuptools.build_meta" + +[project] +description = 'Charm4py Parallel Programming Framework' +authors = [ + { name = "Juan Galvez", email = "jjgalvez@illinois.edu" }, + { name = "Maya Taylor", email = "mayat4@illinois.edu" }, +] +maintainers = [{ name = "Maya Taylor", email = "mayat4@illinois.edu" }] +name = "charm4py" +dynamic = ["version"] +readme = { file = "README.rst", content-type = "text/x-rst" } +keywords = [ + "parallel", + "parallel programming", + "distributed computing", + "distributed", + "hpc", + "HPC", + "runtime", +] +requires-python = ">=3.7" +dependencies = ["numpy>=1.10.0", "greenlet>=3.0.0"] +classifiers = [ + 'Intended Audience :: Developers', + 'License :: Free for non-commercial use', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: POSIX', + 'Operating System :: POSIX :: Linux', + 'Operating System :: Microsoft :: Windows', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Topic :: System :: Distributed Computing', + 'Topic :: System :: Clustering', +] + +[project.urls] +Documentation = "https://charm4py.readthedocs.io" +Repository = "https://github.com/charmplusplus/charm4py" + +[project.scripts] +charmrun = 'charmrun.start:start' diff --git a/requirements.txt b/requirements.txt index 4cb33f6c..f313d9c6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,3 @@ -cython>=3.0.0 -numpy +numpy>=1.10.0 greenlet>=3.0.0 - -# Required for the charm++ build: -cmake +cython>=3.0.0 diff --git a/setup.py b/setup.py index ab3665e5..25b7cd0e 100644 --- a/setup.py +++ b/setup.py @@ -118,7 +118,13 @@ def check_libcharm_version(charm_src_dir): def check_cffi(): try: import cffi - version = tuple(int(v) for v in cffi.__version__.split('.')) + version_str = cffi.__version__.split('.') + + # pypy3.9 returns version string like '1.17.0.dev0' + if (len(version_str) > 3): + version_str = version_str[:3] + + version = tuple(int(v) for v in version_str) if version < (1, 7): raise DistutilsSetupError('Charm4py requires cffi >= 1.7. ' 'Installed version is ' + cffi.__version__) @@ -369,42 +375,12 @@ def install(self): additional_setup_keywords['cffi_modules'] = 'charm4py/charmlib/charmlib_cffi_build.py:ffibuilder' -with open('README.rst', 'r') as f: - long_description = f.read() - - setuptools.setup( - name='charm4py', version=charm4py_version, - author='Juan Galvez and individual contributors', - author_email='jjgalvez@illinois.edu', - description='Charm4py Parallel Programming Framework', - long_description=long_description, - url='https://github.com/charmplusplus/charm4py', - keywords='parallel parallel-programming distributed distributed-computing hpc HPC runtime', packages=setuptools.find_packages(), package_data={ 'charm4py': ['libcharm_version'], }, - entry_points={ - 'console_scripts': [ - 'charmrun = charmrun.start:start', - ], - }, - install_requires=['numpy>=1.10.0', 'greenlet>=3.0.0', 'cython>=3.0.0', 'cmake'], - python_requires='~=3.6', - classifiers=[ - 'Intended Audience :: Developers', - 'License :: Free for non-commercial use', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: Linux', - 'Operating System :: Microsoft :: Windows', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Topic :: System :: Distributed Computing', - 'Topic :: System :: Clustering', - ], ext_modules=extensions, cmdclass = {'build_py': custom_build_py, 'build_ext': custom_build_ext,