# encoding: utf-8
"""
The ``yade.libVersions`` module tracks versions of all libraries it was compiled with. Example usage is as follows::
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>`.
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.
.. hint:: The safest way to compare versions is to use builtin python tuple comparison e.g. ``if(cgalVer > (4,9,0) and cgalVer < (5,1,1)):``.
"""
# all C++ functions are accessible now:
from yade._libVersions import *
import yade.config
[docs]
def getArchitecture():
"""
:return: string containing processor architecture name, as reported by ``uname -m`` call or from ``CMAKE_HOST_SYSTEM_PROCESSOR`` cmake variable.
"""
return 'amd64'
[docs]
def getLinuxVersion():
"""
:return: string containing linux release and version, preferably the value of ``PRETTY_NAME`` from file ``/etc/os-release``.
"""
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)
if(ret==""): ret="Unknown"
return ret
[docs]
def getVersion(libName):
"""
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
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
:return: tuple in format ``(major, minor, patchlevel)`` if ``libName`` exists. Otherwise it returns ``None``.
.. 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.
"""
cppVer = getAllVersionsCpp()
cmakeVer = getAllVersionsCmake()
if(libName == 'openblas'):
print("Warning: openblas has no properly defined version in header files, the obtained version is ",cppVer[libName])
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.")
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]
#raise RuntimeError("Could not find library version of ",libName)
return None
[docs]
def getAllVersionsCmake():
"""
This function returns library versions as provided by cmake during compilation.
: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 *
In [1]: getAllVersionsCmake()
.. 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.
"""
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",'3','28','3','3.28.3')
# 1. compiler
try:
addVer('compiler' ,'13.2.0'.split('.')[0],'13.2.0'.split('.')[1],'13.2.0'.split('.')[2],'/usr/bin/c++ 13.2.0')
except:
pass
addVer("clang",'','','','')
# 2. boost
addVer("boost",'1','83','0','108300')
# 3. qt
addVer("qt" ,'','','','..')
# 4. freeglut
addVer("freeglut" ,'3','0','0','3.0.0')
try:
glutVerStr=''
glutVerNum=glutVerStr.split('.')
addVer("glut", glutVerNum[0] , glutVerNum[1] , glutVerNum[2] , glutVerStr )
except:
pass
# 5. qglviewer - I don't know how to detect it
# 6. python
addVer("python",'3','12','3','3.12.3')
# 7. matplotlib
addVer("matplotlib" ,'3','6','3', '3.6.3')
# 8. eigen
addVer("eigen" ,'3','4','0','3.4.0')
# 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" ,'9','1','0','9.1.0')
# 13. cgal
addVer("cgal" ,'','','','')
# 14. suitesparse
addVer("suitesparse",'7','6','1','7.6.1')
# 15. openblas - I don't know how to detect it
# 16. metis - I don't know how to detect it
# 17. mpi - This one is based on https://cmake.org/cmake/help/v3.10/module/FindMPI.html
addVer("mpi" ,'3','1','0', '3.1')
# 18. numpy
addVer("numpy" ,'1','26','4', '1.26.4')
# Note: these below are getting the version currently installed, not the one with which yade was compiled. Maybe this will need to be changed.
# 19. ipython
addVer("ipython" ,'8','20','0', '8.20.0')
# 20. sphinx,
addVer("sphinx" ,'7','2','6', '7.2.6')
# 21. clp
# Note: this can be fixed in the same way as forced detection of freeglut, with file cMake/FindMissingVersions.cmake
addVer("clp",'1','17','9','1.17.9')
addVer("coinutils",'2','11','4','2.11.4')
# 22. PyMPI
addVer("mpi4py" ,'3','1','5', '3.1.5')
# 23. MPFR
addVer("mpfr",'','','','')
addVer("mpc",'','','','')
# 24. mpmath
# a simple check of mpmath is a following python command:
# import mpmath ; mpmath.mp.dps=50 ; mpmath.acos(0)
try:
addVer("mpmath",'1.2.1'.split('.')[0],'1.2.1'.split('.')[1],'1.2.1'.split('.')[2],'1.2.1')
except:
pass
# 25. tkinter
addVer("tkinter" ,'8','6','0', '8.6')
# 26. pygraphviz
addVer("pygraphviz" ,'1','7','0', '1.7')
# 27. Xlib
addVer("Xlib" ,'0','33','0', '(0,33)')
return ret
[docs]
def printAllVersions(rstFormat=False):
"""
This function prints a nicely formatted table with library versions.
: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.
As an example the table below actually reflects with what libraries this documentation was compiled:
.. ipython::
In [1]: printAllVersions()
.. note:: For convenience at startup ``from yade.libVersions import printAllVersions`` is executed, so that this function is readily accessible.
"""
# there will be three columns: library , cmake , C++
headers = ["library","cmake","C++"]
longest = [None,None,None]
cmakeVer = getAllVersionsCmake()
cppVer = getAllVersionsCpp()
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)
longest[2] = max(longest[2],len(val[1]))
for i in range(3): longest[i]+=2
sep = '| '+'-'*longest[0]+' | '+'-'*longest[1]+' | '+'-'*longest[2]+' |\n'
lines="|"
if(rstFormat):
sep = '+-'+'-'*longest[0]+'-+-'+'-'*longest[1]+'-+-'+'-'*longest[2]+'-+'
lines = sep+'\n|'
# nice python formatting guide: https://pyformat.info/
for i in range(3):
lines +=" "+(('{:'+str(longest[i])+'}').format(headers[i]))+" |"
lines+='\n'
if(rstFormat):
lines += sep.replace('-','=')+'\n'
else:
lines += sep
for libName in sorted(namesSet):
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'
if(rstFormat):
lines+= sep+'\n'
if(rstFormat==False): print("\n```")
#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)
# print yade config dir relative to
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)
print("Yade precision : "+str(yade.math.getDigits2(1))+" bits, "+str(yade.math.getDigits10(1))+" decimal places, with"+("" if yade.config.highPrecisionMpmath else "out")+" mpmath, "+yade.config.highPrecisionName)
if(len(yade.math.getRealHPCppDigits10()) > 1):
print("Yade RealHP<…> : "+str(yade.math.getRealHPCppDigits10())+" decimal digits in C++, "+str(yade.math.getRealHPPythonDigits10())+" decimal digits accessible from python")
if(rstFormat==False): print("```")
print("\nLibraries used :\n")
print(lines)
if(rstFormat==False): print("```")
print("Linux version : "+str(getLinuxVersion()))
print("Architecture : "+str(getArchitecture()))
try:
print("Little endian : "+str(yade.math.isThisSystemLittleEndian()))
except Exception as e:
print("Little endian : unknown")
if(rstFormat==False): print("```")
print("")
[docs]
def getAllVersions(rstFormat=False):
"""
:return: ``str`` - this function returns the result of :yref:`printAllVersions(rstFormat)<yade.libVersions.printAllVersions>` call inside a string variable.
"""
import sys,io
origStdOut = sys.stdout
newStdOut = io.StringIO()
sys.stdout = newStdOut
printAllVersions(rstFormat)
sys.stdout = origStdOut
return newStdOut.getvalue()
"""
GITLAB format:
| header 1 | header 2 |
| -------- | -------- |
| cell 1 | cell 2 |
| cell 3 | cell 4 |
reStructuredText format:
+----------+----------+
| header 1 | header 2 |
+==========+==========+
| cell 1 | cell 2 |
+----------+----------+
| cell 3 | cell 4 |
+----------+----------+
"""