制作第三方包 ============== PyPi wheel 制作 ---------------- `PyPi `_ wheel 非常容易制作, 只需要正确的书写 :file:`setup.py` 文件即可,之后运行下述命令: .. code-block:: bash python3 setup.py sdist bdist_wheel 然后, :file:`*.tar.gz` 和 :file:`.whl` 就会暴露在 :file:`./dist` 目录下。 `能否正确书写` :file:`setup.py` 决定了包制作过程的成败, :file:`setup.py` 样例如下所示: .. code-block:: python from distutils.extension import Extension from pathlib import Path from Cython.Build import cythonize from setuptools import setup, find_packages setup( name='gvasp', description='A quick post-process for resolve or assistant the VASP calculations', author='hui_zhou', long_description=Path("./README.md").read_text(), long_description_content_type='text/markdown', version='0.0.1', license='GPL-3.0', python_requires='>=3.9', packages=find_packages(), install_requires=[ 'Cython', 'lxml', 'matplotlib', 'numpy', 'pandas', 'pymatgen', 'pymatgen-analysis-diffusion', 'pyyaml', 'scipy'], ext_modules=cythonize([Extension(name='gvasp.lib._dos', sources=['extension/_dos/_dos.pyx']), Extension(name='gvasp.lib._file', sources=['extension/_file/_file.cpp', 'extension/_file/_lib.cpp'])], language_level=3), include_dirs=['/usr/lib/gcc/x86_64-linux-gnu/11/include', '/home/hzhou/anaconda3/include', '/home/hzhou/anaconda3/Library/include'], include_package_data=True, package_data={"gvasp": ["*.json", "*.yaml", "INCAR", "pot.tgz"]}, entry_points={'console_scripts': ['gvasp = gvasp.main:main']} ) where - *name*, *author*, *description*, *version* and *license* are package metadata - *long_description* are *long_description_content_type* are related description in `PyPi `_ (can write only once, before upload the package) - *packages* will search the module (including the :file:`__init__.py`) under **.** directory - *install_requires* is the dependency of the package, will installed when the package installed - *ext_modules* and *include_dirs* are related to the :code:`C/C++ extensions` - *package_data* is the data you want to including (which under the module); for other data (not in module), can write :file:`MANIFEST.in` to including them, like :download:`this <./MANIFEST.in>` - *entry_points* specify a alias **gvasp** to represent the :code:`python3 gvasp/main.py` In fact, generate the :file:`*.whl` is the first step for :code:`Linux` platform, because `PyPi `_ will check the :code:`tag` of :file:`*.whl` file, only \*manylinux_* field in name can be accept according to `PEP rules `_ (:code:`PEP 513 (manylinux1)`, :code:`PEP 571 (manylinux2010)`, :code:`PEP 599 (manylinux2014)` and :code:`PEP 600 (manylinux_x_y)`). So one want to upload the package to PyPi should *repair* the wheel to have the `manylinux` field. Luckily, by `docker image `_ and `auditwheel tool `_, one can easily `repair` the `wheel`. For example, following such steps: 1. pull the docker image, i.e., `manylinux_2_28_x86_64` .. code-block:: bash docker pull quay.io/pypa/manylinux_2_28_x86_64 2. start and attach a container .. code-block:: bash docker run -it quay.io/pypa/manylinux_2_28_x86_64 "/bin/bash" 3. transfer the source code to docker container .. code-block:: bash docker cp local_path container_id:docker_path 4. recompile the package and obtain the \*.whl .. code-block:: bash $python3 setup.py bdist_wheel 5. repair the \*.whl .. code-block:: bash auditwheel repair *.whl Finally, a new :file:`wheel` with the `manylinux` field will occur in the wheelhouse directory. Then you can upload the `wheel` to `PyPi `_ use such command: .. code-block:: bash twine upload dist/* Conda 包制作 -------------- Relative `PyPi `_ package production, production of conda package is very disgusting!!! Because you will meat the dependency problem every where. Although, the conda package actually only need write :file:`meta.yaml` and :file:`build.sh` (at least for me), like this: .. code-block:: yaml package: name: gvasp version: 0.0.1 source: path: . requirements: build: - {{ compiler('c') }} - {{ compiler('cxx') }} host: - python - Cython - setuptools run: - python - numpy - Cython - lxml - matplotlib - pandas - pymatgen - pymatgen-analysis-diffusion - pyyaml - scipy about: home: https://github.com/Rasic2/gvasp license: GPL-3.0 and this: .. code-block:: bash export CFLAGS="${CFLAGS} -isysroot ${CONDA_BUILD_SYSROOT}" export CXXFLAGS="${CXXFLAGS} -isysroot ${CONDA_BUILD_SYSROOT}" $PYTHON -m pip install . --no-deps -vv Firstly, we talk about the :file:`meta.yaml`. * `package` section represents the package information * `source` section manage how to get the package (`git`, `pypi`, `local` or `other`), here we use `local` (we suggest that you mkdir a new directory (like :file:`conda`), and put the necessary source and data in there, including :file:`meta.yaml` and the :file:`bash.sh` below) * `requirements` is very very disgusting, because they have three different part, i.e., :code:`build`, :code:`host`, :code:`run`. * :code:`build` represents the **system infrastructure**, so you can put `revision control systems (Git, SVN)`, `make tools (GNU make, Autotool, CMake)` and `compilers (real cross, pseudo-cross, or native when not cross-compiling)`, and `any source pre-processors` there. For example, we put :code:`C/C++ compilers` in this section. * :code:`host` is responsible for the :code:`setup.py`, in there, we use **Cython**, **setuptools** and inner module of **python**, so we put them in this section. * :code:`run` is simple, only equal to the `install_requires`, (noted that `pymatgen-\* packages` not in default channels, so we add the :code:`conda-forge` as the optional) * Actually, in the package production, conda will make a new directory under the `envs/**/conda-bld/package_name`. Under the directory, three directory will be made, i.e. :code:`_build_env`, :code:`_placeholder_placeholder_` and :code:`work`, where the compiler in :code:`build` section will download and installed in `_build_env`. The `_placeholder_placeholder_` directory manage the conda environment, for example, it will install the python, setuptools, Cython here, basically same to a new conda environment. The `work` dir is the copy of your source code, and the `real` build work will happen here, for example, :code:`compile` and :code:`package`. Then we can talk about the :file:`build.sh`: * Bacause of use Cython, we redefine of the :code:`CFLAGS` and :code:`CXXFLAGS`, detailed information can see `here `_. * env :code:`$PYTHON` represents the python version in `_placeholder_placeholder` directory, don't use the pure `python` command. Here, we can use command below to process the real package production: .. code-block:: bash conda-build . -c conda-forge **.** represents the directory including the :file:`meta.yaml` and use the :code:`conda-forge` channel because of `pymatgen-*` packages. After that, in :file:`conda-bld/linux-64` directory, the :file:`package.tar.bz2` has been written (`bin`, `info` and `lib` directory in it). .. note:: *bin* directory occur because we use **entry_points**; *info* directory store the **recipes** and **metadata**, *lib* is the real built package. Finally, we can use :code:`Anaconda` command to upload the package: .. code-block:: bash Anaconda upload *.tar.bz2 Install package can do this: .. code-block:: bash conda install -c hui_zhou -c conda-forge gvasp .. attention:: When install the package, noticed that we used the compilers in **conda-forge** channel, so we particularly add this channel to install the package, otherwise conflicts will occur. 第三方包安装 ============ - 安装 conda 包 .. code-block:: bash conda install package 重新安装同版本的包时,加入参数 :code:`--force-reinstall`