1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
#!/usr/bin/env python
"""
Script to build a "binary wheel" for the 'pip' Python package manager for
the LAMMPS python module which includes the shared library file. After a
successful build the script attempts to install the wheel into a system
specific site-packages folder or - failing that - into the corresponding
user site-packages folder. Called from the 'install-python' build target
in the GNU make and CMake based build systems. Can also be called
independently and used to build the wheel without installing it.
"""
from __future__ import print_function
import sys, os, shutil, glob, subprocess
from argparse import ArgumentParser
parser = ArgumentParser(prog='install.py',
description='LAMMPS python package installer script')
parser.add_argument("-p", "--package", required=True,
help="path to the LAMMPS Python package folder")
parser.add_argument("-l", "--lib", required=True,
help="path to the compiled LAMMPS shared library")
parser.add_argument("-n", "--noinstall", action="store_true", default=False,
help="only build a binary wheel. Don't attempt to install it")
parser.add_argument("-w", "--wheeldir", required=False,
help="path to a directory where the created wheel will be stored")
parser.add_argument("-v", "--versionfile", required=True,
help="path to the LAMMPS version.h source file")
parser.add_argument("-f", "--force", action="store_true", required=False, default=False,
help="force installation of LAMMPS Python package")
args = parser.parse_args()
# validate arguments and make paths absolute
if args.package:
if not os.path.exists(args.package):
print("\nERROR: LAMMPS package folder %s does not exist\n" % args.package)
parser.print_help()
sys.exit(1)
else:
args.package = os.path.abspath(args.package)
if ((os.path.basename(args.package) != "lammps")
and ((os.path.basename(os.path.dirname(args.package)) != "python"))):
print("\nERROR: LAMMPS package folder path %s does not end in %s\n"
% (args.package, os.path.join("python", "lammps")))
parser.print_help()
sys.exit(1)
if args.lib:
if not os.path.exists(args.lib):
print("\nERROR: LAMMPS shared library %s does not exist\n" % args.lib)
parser.print_help()
sys.exit(1)
else:
args.lib = os.path.abspath(args.lib)
if args.wheeldir:
if not os.path.exists(args.wheeldir):
print("\nERROR: directory %s to store the wheel does not exist\n" % args.wheeldir)
parser.print_help()
sys.exit(1)
else:
args.wheeldir = os.path.abspath(args.wheeldir)
if args.versionfile:
if not os.path.exists(args.versionfile):
print("\nERROR: LAMMPS version file at %s does not exist\n" % args.versionfile)
parser.print_help()
sys.exit(1)
else:
args.versionfile = os.path.abspath(args.versionfile)
olddir = os.path.abspath('.')
pythondir = os.path.abspath(os.path.join(args.package,'..'))
os.putenv('LAMMPS_VERSION_FILE',os.path.abspath(args.versionfile))
# purge old build folder
if os.path.isdir(os.path.join(olddir, 'build-python')):
print("Cleaning old build directory")
shutil.rmtree(os.path.join(olddir, 'build-python'))
# remove any old wheel files left over from previous calls
print("Purging existing wheels...")
for wheel in glob.glob('lammps-*.whl'):
print("deleting " + wheel)
os.remove(wheel)
# copy python tree to build folder
builddir = shutil.copytree(pythondir, os.path.join(olddir, 'build-python'))
os.chdir(builddir)
# copy shared object to the current folder so that
# it will show up in the installation at the expected location
os.putenv('LAMMPS_SHARED_LIB',os.path.basename(args.lib))
shutil.copy(args.lib,'lammps')
# create a virtual environment for building the wheel
shutil.rmtree('buildwheel', True)
try:
txt = subprocess.check_output([sys.executable, '-m', 'venv', 'buildwheel'], stderr=subprocess.STDOUT, shell=False)
print(txt.decode('UTF-8'))
except subprocess.CalledProcessError as err:
sys.exit("Failed to create a virtual environment: {0}".format(err.output.decode('UTF-8')))
# now run the commands to build the wheel. those must be in a separate script
# and run in subprocess, since this will use the virtual environment and
# there is no simple way to return from that in python.
os.system(sys.executable + ' makewheel.py')
# copy wheel to final location
for wheel in glob.glob('lammps-*.whl'):
if args.wheeldir:
shutil.copy(wheel, args.wheeldir)
else:
shutil.copy(wheel, olddir)
# remove temporary folders and files
os.chdir(olddir)
shutil.rmtree('build-python', True)
# stop here if we were asked not to install the wheel we created
if args.noinstall:
exit(0)
# install the wheel with pip. first try to install in the default environment.
# that will be a virtual environment, if active, or the system folder.
# if in a virtual environment, we must not use the python executable
# that is running this script (configured by cmake), but use "python"
# from the regular system path. The user may have changed to the virtual
# environment *after* running cmake.
# recent versions of pip will automatically drop to use the user folder
# in case the system folder is not writable.
# we use a subprocess so we can catch an exception on failure.
# we need to check whether pip refused to install because of a
# version of the module previously installed with distutils. those
# must be uninstalled manually. We must not ignore this and drop
# back to install into a (forced) user folder.
if "VIRTUAL_ENV" in os.environ:
print("Installing wheel into virtual environment")
py_exe = 'python'
else:
print("Installing wheel into system site-packages folder")
py_exe = sys.executable
try:
if args.force:
txt = subprocess.check_output([py_exe, '-m', 'pip', 'install', '--force-reinstall', '--break-system-packages', wheel], stderr=subprocess.STDOUT, shell=False)
else:
txt = subprocess.check_output([py_exe, '-m', 'pip', 'install', '--force-reinstall', wheel], stderr=subprocess.STDOUT, shell=False)
print(txt.decode('UTF-8'))
sys.exit(0)
except subprocess.CalledProcessError as err:
errmsg = err.output.decode('UTF-8')
if errmsg.find("distutils installed"):
sys.exit(errmsg + "You need to uninstall the LAMMPS python module manually first.\n")
try:
print('Installing wheel into system site-packages folder failed. Trying user folder now')
if args.force:
txt = subprocess.check_output([sys.executable, '-m', 'pip', 'install', '--user', '--force-reinstall', '--break-system-packages', wheel], stderr=subprocess.STDOUT, shell=False)
else:
txt = subprocess.check_output([sys.executable, '-m', 'pip', 'install', '--user', '--force-reinstall', wheel], stderr=subprocess.STDOUT, shell=False)
print(txt.decode('UTF-8'))
except:
sys.exit('Failed to install wheel ' + wheel)
|