libVersions.py.in 10.5 KB
Newer Older
1 2
# encoding: utf-8
"""
3
The ``yade.libVersions`` module tracks versions of all libraries it was compiled with. Example usage is as follows::
4

5 6 7 8 9 10 11 12 13 14 15
	from yade.libVersions import *
	if(getVersion('cgal') > (4,9,0)):

	else:


To obtain a list of all libraries use the function :yref:`yade.libVersions.printAllVersions`.

All libraries listed in :ref:`prerequisites<prerequisites>` are detected by this module.

.. note:: If we need a version of some library not listed in :ref:`prerequisites<prerequisites>`, then it must also be added to :ysrc:`that list<doc/sphinx/installation.rst>`.
Janek Kozicki's avatar
Fix #89  
Janek Kozicki committed
16 17 18 19 20 21

When adding a new version please have a look at these three files: 

1. :ysrc:`py/_libVersions.cpp`: detection of versions from ``#include`` files by C++.
2. :ysrc:`py/libVersions.py.in`: python module which is constructed by cmake during compilation. All ``*.in`` files are processed by cmake.
3. :ysrc:`cMake/FindMissingVersions.cmake`: forced detection of library with undetectable version.
22 23 24 25
"""

# all C++ functions are accessible now:
from yade._libVersions import *
26
import yade.config
27

28 29
def getLinuxVersion():
	"""
30
	:return: string containing linux release and version, preferably the value of ``PRETTY_NAME`` from file ``/etc/os-release``.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
	"""
	ret=""
	try:
		import os
		listDir = os.listdir("/etc")
		once = ("os-release" in listDir)
		for f in listDir:
			if((once and f=="os-release") or ((not once) and f.endswith("elease"))):
				with open(os.path.join("/etc", f), 'r') as fin:
					lines=""
					for line in fin:
						if(line.startswith("PRETTY_NAME")):
							try:
								ret=(line.split('"')[1])
							except Exception as e:
								ret=(line)
							break
						lines+=line
					if(ret==""): ret=("\n"+lines)
	except Exception as e:
		print("Error: cannot find file /etc/os-release. Caught exception:",e)
Janek Kozicki's avatar
Janek Kozicki committed
52
	if(ret==""): ret="Unknown"
53 54 55
	return ret


56 57
def getVersion(libName):
	"""
58
	This function returns the tuple ``(major, minor, patchlevel)`` with library version number. The ``yade --test`` in file :ysrc:`py/tests/libVersions.py` tests that this
59 60 61 62
	version is the same as detected by cmake and C++. If only one of those could detect the library version, then this number is used.

	:param string libName: the name of the library

Janek Kozicki's avatar
Janek Kozicki committed
63
	:return: tuple in format ``(major, minor, patchlevel)`` if ``libName`` exists. Otherwise it returns ``None``.
64

65
	.. note:: library openblas has no properly defined version in header files, this function will return ``(0,0,0)`` for openblas. Parsing the version string would be unreliable. The mpi version detected by cmake sometimes is different than version detected by C++, this needs further investigation.
66
	"""
67 68
	cppVer   = getAllVersionsCpp()
	cmakeVer = getAllVersionsCmake()
69
	if(libName == 'openblas'):
Janek Kozicki's avatar
Janek Kozicki committed
70
		print("Warning: openblas has no properly defined version in header files, the obtained version is ",cppVer[libName])
71 72 73
	if((libName == 'mpi' ) and (cppVer[libName][0] != cmakeVer[libName][0])):
		print('\033[91m'+" Warning: mpi versions are different. Can you help with file py/libVersions.py.in?"+'\033[0m')
		print("C++ is: " , cppVer[libName], " and cmake is: ",cmakeVer[libName], ", will return the C++ one.")
74 75 76 77
	if((libName in cppVer) and (len(cppVer[libName])==2)):
		return cppVer[libName][0]
	if((libName in cmakeVer) and (len(cmakeVer[libName])==2)):
		return cmakeVer[libName][0]
Janek Kozicki's avatar
Janek Kozicki committed
78 79
	#raise RuntimeError("Could not find library version of ",libName)
	return None
80

81
def getAllVersionsCmake():
82
	"""
83
	This function returns library versions as provided by cmake during compilation.
84

85 86 87 88 89 90 91 92 93
	:return: dictionary in following format: ``{ "libName" : [ (major, minor, patchlevel) , "versionString" ] }``


	As an example the dict below reflects what libraries this documentation was compiled with (here are only those detected by `CMAKE <https://cmake.org>`_):

	.. ipython::
	
	   In [1]: from yade.libVersions import *

94
	   In [1]: getAllVersionsCmake()
95

96
	.. note:: Please add here detection of other libraries when yade starts using them or if you discover how to extract from cmake a version which I didn't add here.
97 98 99

	"""

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
	ret={}
	def addVer(name,v1,v2,v3,ver):
		try:
			ret.update({ name : [ ( int(v1) , int(v2) , int(v3) ) , ver ]})
		except:
			pass
	# 0.cmake
	addVer("cmake",'${CMAKE_MAJOR_VERSION}','${CMAKE_MINOR_VERSION}','${CMAKE_PATCH_VERSION}','${CMAKE_VERSION}')
	# 1. compiler
	try:
		addVer("gcc"  ,'${CMAKE_CXX_COMPILER_VERSION}'.split('.')[0],'${CMAKE_CXX_COMPILER_VERSION}'.split('.')[1],'${CMAKE_CXX_COMPILER_VERSION}'.split('.')[2],'${CMAKE_CXX_COMPILER_VERSION}')
	except:
		pass
	addVer("clang",'${CLANG_VERSION_MAJOR}','${CLANG_VERSION_MINOR}','${CLANG_VERSION_PATCHLEVEL}','${CLANG_VERSION}')
	# 2. boost
	addVer("boost",'${Boost_MAJOR_VERSION}','${Boost_MINOR_VERSION}','${Boost_SUBMINOR_VERSION}','${Boost_VERSION}')
	# 3. qt
117
	addVer("qt"   ,'${Used_QT_VERSION_MAJOR}','${Used_QT_VERSION_MINOR}','${Used_QT_VERSION_PATCH}','${Used_QT_VERSION_MAJOR}.${Used_QT_VERSION_MINOR}.${Used_QT_VERSION_PATCH}')
Janek Kozicki's avatar
Fix #89  
Janek Kozicki committed
118 119
	# 4. freeglut
	addVer("freeglut" ,'${FREEGLUT_VERSION_MAJOR}','${FREEGLUT_VERSION_MINOR}','${FREEGLUT_VERSION_PATCH}','${FREEGLUT_VERSION_STR}')
120 121 122 123 124
	# 5. qglviewer  - I don't know how to detect it
	# 6. python
	addVer("python",'${PYTHON_VERSION_MAJOR}','${PYTHON_VERSION_MINOR}','${PYTHON_VERSION_PATCH}','${PYTHON_VERSION_STRING}')
	# 7. matplotlib - I don't know how to detect it
	# 8. eigen
125
	addVer("eigen" ,'${EIGEN3_WORLD_VERSION}','${EIGEN3_MAJOR_VERSION}','${EIGEN3_MINOR_VERSION}','${EIGEN3_VERSION}')
126 127 128 129 130 131 132 133 134 135 136
	# 9. gdb        - I don't know how to detect it
	# 10. sqlite3   - I don't know how to detect it
	# 11. loki      - I don't know how to detect it
	# 12. vtk
	addVer("vtk"   ,'${VTK_MAJOR_VERSION}','${VTK_MINOR_VERSION}','${VTK_BUILD_VERSION}','${VTK_VERSION}')
	# 13. cgal
	addVer("cgal"  ,'${CGAL_MAJOR_VERSION}','${CGAL_MINOR_VERSION}','${CGAL_BUGFIX_VERSION}','${CGAL_VERSION}')
	# 14. suitesparse
	addVer("suitesparse",'${SUITESPARSE_MAIN_VERSION}','${SUITESPARSE_SUB_VERSION}','${SUITESPARSE_SUBSUB_VERSION}','${SUITESPARSE_VERSION}')
	# 15. openblas  - I don't know how to detect it
	# 16. metis     - I don't know how to detect it
137 138
	# 17. mpi       - This one is based on https://cmake.org/cmake/help/v3.10/module/FindMPI.html
	addVer("mpi"   ,'${MPI_CXX_VERSION_MAJOR}','${MPI_CXX_VERSION_MINOR}','0', '${MPI_CXX_VERSION}')
Janek Kozicki's avatar
Janek Kozicki committed
139 140
	# 18. numpy
	addVer("numpy" ,'${NUMPY_VERSION_MAJOR}','${NUMPY_VERSION_MINOR}','${NUMPY_VERSION_PATCH}', '${NUMPY_VERSION}')
Janek Kozicki's avatar
Janek Kozicki committed
141
	# Note: these below are getting the version currently installed, not the one with which yade was compiled. Maybe this will need to be changed.
Janek Kozicki's avatar
Janek Kozicki committed
142
	# 19. ipython
143 144 145 146 147 148 149
	try:
		import IPython
		ipver=IPython.__version__
		ipvertab=IPython.__version__.split('.')
		addVer("ipython", ipvertab[0] , ipvertab[1] , ipvertab[2] , ipver )
	except:
		pass
150 151 152 153 154 155 156
	# 20. sphinx, https://www.sphinx-doc.org/en/master/extdev/appapi.html#checking-the-sphinx-version
	try:
		import sphinx
		spver=sphinx.version_info
		addVer("sphinx", spver[0] , spver[1] , spver[2] , '%i.%i.%i-%s-%i'%spver )
	except:
		pass
Vasileios Angelidakis's avatar
Vasileios Angelidakis committed
157
	# 21. clp
Janek Kozicki's avatar
Fix #89  
Janek Kozicki committed
158
	# Note: this can be fixed in the same way as forced detection of freeglut, with file cMake/FindMissingVersions.cmake
Vasileios Angelidakis's avatar
Vasileios Angelidakis committed
159
	addVer("clp",'${CLP_VERSION_MAJOR}','${CLP_VERSION_MINOR}','${CLP_VERSION_RELEASE}','${CLP_VERSION}')
160 161 162 163 164 165 166 167 168 169
	# 22. PyMPI
	try:
		import mpi4py
		mpi4ver=mpi4py.__version__
		mpi4vertab=mpi4py.__version__.split('.')
		addVer("mpi4py", mpi4vertab[0] , mpi4vertab[1] , mpi4vertab[2] , mpi4ver )
	except:
		pass

	return ret
170 171 172 173 174

def printAllVersions(rstFormat=False):
	"""
	This function prints a nicely formatted table with library versions.

Janek Kozicki's avatar
Janek Kozicki committed
175
	:param bool rstFormat: whether to print table using the reStructuredText formatting. Defaults to ``False`` and prints using `Gitlab markdown rules <https://gitlab.com/help/user/markdown>`_ so that it is easy to paste into gitlab discussions.
176 177 178 179 180 181 182

	As an example the table below actually reflects with what libraries this documentation was compiled:

	.. ipython::
	
	   In [1]: printAllVersions()

183 184
	.. note:: For convenience at startup ``from yade.libVersions import printAllVersions`` is executed, so that this function is readily accessible.

185 186
	"""

187 188 189
	# there will be three columns: library , cmake , C++
	headers  = ["library","cmake","C++"]
	longest  = [None,None,None]
190 191
	cmakeVer = getAllVersionsCmake()
	cppVer   = getAllVersionsCpp()
192 193 194 195 196 197 198 199 200 201 202
	namesSet = set()
	for i in range(3): longest[i] = len(headers[i])
	for key,val in cmakeVer.items():
		longest[0] = max(longest[0],len(key))
		if(len(val)==2):
			namesSet.add(key)
			longest[1] = max(longest[1],len(val[1]))
	for key,val in cppVer.items():
		longest[0] = max(longest[0],len(key))
		if(len(val)==2):
			namesSet.add(key)
Janek Kozicki's avatar
Janek Kozicki committed
203
			longest[2] = max(longest[2],len(val[1]))
204 205 206 207 208 209

	for i in range(3): longest[i]+=2

	sep = '| '+'-'*longest[0]+' | '+'-'*longest[1]+' | '+'-'*longest[2]+' |\n'
	lines="|"
	if(rstFormat):
210 211
		sep = '+-'+'-'*longest[0]+'-+-'+'-'*longest[1]+'-+-'+'-'*longest[2]+'-+'
		lines = sep+'\n|'
212 213
	# nice python formatting guide: https://pyformat.info/
	for i in range(3):
214
		lines +=" "+(('{:'+str(longest[i])+'}').format(headers[i]))+" |"
215
	lines+='\n'
216 217 218 219
	if(rstFormat):
		lines += sep.replace('-','=')+'\n'
	else:
		lines += sep
220
	for libName in sorted(namesSet):
221 222 223 224 225 226 227 228 229 230
		lines+="| "+(('{:'+str(longest[0])+'}').format(libName))+" |"
		if (libName in cmakeVer) and (len(cmakeVer[libName])==2):
			lines+=" "+(('{:'+str(longest[1])+'}').format(cmakeVer[libName][1]))+" |"
		else:
			lines+=" "+(('{:'+str(longest[1])+'}').format(' '                 ))+" |"
		if (libName in cppVer) and (len(cppVer[libName])==2):
			lines+=" "+(('{:'+str(longest[2])+'}').format(cppVer[libName][1]  ))+" |"
		else:
			lines+=" "+(('{:'+str(longest[2])+'}').format(' '                 ))+" |"
		lines+='\n'
231 232
		if(rstFormat):
			lines+= sep+'\n'
233

234
	if(rstFormat==False): print("\n```")
235 236 237 238 239 240 241
	#print("Yade revision  : ",yade.config.revision)
	print("Yade version   : ",yade.config.version)
	#print("Yade prefix    : ",yade.config.prefix)
	#print("Yade suffix    : ",yade.config.suffix)
	feats = ""
	for f in yade.config.features: feats+=" "+f
	print("Yade features  :",feats)
242 243 244 245 246 247 248 249
	# print yade config dir relative to ${HOME}
	confD = yade.config.confDir
	try:
		import os
		confD = "~/"+os.path.relpath(yade.config.confDir,os.environ['HOME'])
	except Exception as e:
		pass
	print("Yade config dir:",confD)
250
	if(rstFormat==False): print("```")
251
	print("\nLibraries used :\n")
252
	print(lines)
253
	print("Linux version: "+str(getLinuxVersion()))
254
	print("")
255

256

257 258 259 260 261 262 263 264 265 266 267
"""
GITLAB format:

| header 1 | header 2 |
| -------- | -------- |
| cell 1   | cell 2   |
| cell 3   | cell 4   |


reStructuredText format:

268 269 270 271 272 273 274
+----------+----------+
| header 1 | header 2 |
+==========+==========+
| cell 1   | cell 2   |
+----------+----------+
| cell 3   | cell 4   |
+----------+----------+
275 276
"""