Added -fPIC compile option as required for python extensions

Added config directory to be determined through platformdirs package
Added pysetup directory with setup.py script and additional code to make
   properly assembled python wheel distribution.
   To make wheel run: python3 pysetup/setup.py bdist_wheel
   When wheel installed into the system the 'coriolis' command available.
   Running coriolis from python package folder implemented
       through preloading of required *.so files in proper order
		(no need to install shared libraries in system folders).
This commit is contained in:
Serge Rabyking 2023-05-26 17:33:25 +01:00
parent 41e57a7512
commit 2046a1501f
5 changed files with 470 additions and 6 deletions

View File

@ -84,10 +84,10 @@
set(DEBUG_FLAGS "-g") set(DEBUG_FLAGS "-g")
if(CYGWIN) if(CYGWIN)
set(ADDITIONAL_FLAGS "-D_GLIBCXX_USE_C99") set(ADDITIONAL_FLAGS "-D_GLIBCXX_USE_C99")
set(CXX_STANDARD "gnu++11") set(CXX_STANDARD "gnu++11 -fPIC")
else() else()
set(ADDITIONAL_FLAGS "-Wl,--no-undefined") set(ADDITIONAL_FLAGS "-Wl,--no-undefined")
set(CXX_STANDARD "c++11") set(CXX_STANDARD "c++11 -fPIC")
endif() endif()
#set(CMAKE_C_FLAGS_DEBUG " -Wall -fsanitize=address ${ADDITIONAL_FLAGS} ${DEBUG_FLAGS}" CACHE STRING "C Compiler Debug options." FORCE) #set(CMAKE_C_FLAGS_DEBUG " -Wall -fsanitize=address ${ADDITIONAL_FLAGS} ${DEBUG_FLAGS}" CACHE STRING "C Compiler Debug options." FORCE)
set(CMAKE_C_FLAGS_DEBUG " -Wall ${ADDITIONAL_FLAGS} ${DEBUG_FLAGS}" CACHE STRING "C Compiler Debug options." FORCE) set(CMAKE_C_FLAGS_DEBUG " -Wall ${ADDITIONAL_FLAGS} ${DEBUG_FLAGS}" CACHE STRING "C Compiler Debug options." FORCE)

View File

@ -282,8 +282,10 @@ def staticInitialization ( quiet=True ):
elif coriolisTop: elif coriolisTop:
sysConfDir = os.path.join(coriolisTop,'etc/coriolis2') sysConfDir = os.path.join(coriolisTop,'etc/coriolis2')
else: else:
raise ErrorMessage( 1, [ 'Cannot locate the directoty holding the configuration files.' import platformdirs
, 'The path is something ending by <.../etc/coriolis2>.'] ) sysConfDir = os.path.join(platformdirs.user_config_dir(), 'coriolis2')
#raise ErrorMessage( 1, [ 'Cannot locate the directoty holding the configuration files.'
# , 'The path is something ending by <.../etc/coriolis2>.'] )
if not quiet: print( ' - "{}"'.format( sysConfDir )) if not quiet: print( ' - "{}"'.format( sysConfDir ))
initTechno( quiet ) initTechno( quiet )
return return
@ -308,8 +310,10 @@ def setSysConfDir ( quiet=False ):
elif coriolisTop: elif coriolisTop:
sysConfDir = os.path.join(coriolisTop,'etc/coriolis2') sysConfDir = os.path.join(coriolisTop,'etc/coriolis2')
else: else:
raise ErrorMessage( 1, [ 'Cannot locate the directoty holding the configuration files.' import platformdirs
, 'The path is something ending by <.../etc/coriolis2>.'] ) sysConfDir = os.path.join(platformdirs.user_config_dir(), 'coriolis2')
#raise ErrorMessage( 1, [ 'Cannot locate the directoty holding the configuration files.'
# , 'The path is something ending by <.../etc/coriolis2>.'] )
if not quiet: print( ' - "{}"'.format( sysConfDir )) if not quiet: print( ' - "{}"'.format( sysConfDir ))
sys.path.append( sysConfDir ) sys.path.append( sysConfDir )
return return

View File

@ -0,0 +1,38 @@
# preload shared libraries
import os, ctypes
__libs=[]
__lib_folder=os.path.join(os.path.dirname(os.path.realpath(__file__)),'..','libs')
for name in (
"libdef.so",
"libpyflute.so",
"libcoloquinte.so",
"libhurricane.so",
"libutils.so",
"libpycrlconst.so",
"libconfiguration.so",
"libpytypemanager.so",
"libflute.so",
"liblef.so",
"libdefzlib.so",
"libisobar.so",
"libintervalTree.so",
"libviewer.so",
"liblefzlib.so",
"libanalog.so",
"libcrlcore.so",
"libtutorial.so",
"libequinox.so",
"libpycrlcore.so",
"liblibmanager.so",
"libetesian.so",
"libsolstice.so",
"libunicorn.so",
"libanabatic.so",
"libkatana.so",
"libpyanabatic.so",
"libbora.so",
"libpybora.so",
):
__libs.append(ctypes.CDLL(os.path.join(__lib_folder,name)))

View File

@ -0,0 +1,245 @@
#!/usr/bin/env python3
#
# This file is part of the Coriolis Software.
# Copyright (c) Sorbonne Université 2015-2021, All Rights Reserved
#
# +-----------------------------------------------------------------+
# | C O R I O L I S |
# | C o r i o l i s - Generic Program Launcher |
# | |
# | Author : Jean-Paul CHAPUT |
# | E-mail : Jean-Paul.Chaput@lip6.fr |
# | =============================================================== |
# | Python : "./src/cgt.py" |
# +-----------------------------------------------------------------+
try:
import sys
import os.path
import optparse
from coriolis import helpers
helpers.loadUserSettings()
from coriolis import Cfg, Hurricane, Viewer, CRL, Etesian, Anabatic, \
Katana, Bora, Tutorial, Unicorn
except Exception as e:
helpers.io.showPythonTrace( sys.argv[0], e )
sys.exit(2)
def setCgtBanner ( banner ):
banner.setName('cgt')
banner.setPurpose('Coriolis Graphical Tool')
return banner
def credits ():
s = ''
s += ' Tool Credits\n'
s += ' Hurricane .................... Remy Escassut & Christian Masson\n'
s += ' Etesian - Placer .............................. Gabriel Gouvine\n'
s += ' Knik - Global Router ............................ Damien Dupuis\n'
s += ' Kite - Detailed Router ....................... Jean-Paul Chaput\n\n'
s += ' Contributors\n'
s += ' Sophie Belloeil, Hugo Clement, Marek Sroka, Wu Yifei\n'
s += ' Coloquinte software credits (used by Etesian)\n'
s += ' Author ........................................ Gabriel Gouvine\n'
s += ' FLUTE software credits (used by Knik)\n'
s += ' Author ........................................ Chris C. N. CHU\n'
s += ' Prof. Ident. ............................ Iowa State University\n'
s += ' URL ........................ http://home.eng.iastate.edu/~cnchu\n\n'
return s
def runScript ( scriptPath, editor ):
try:
sys.stdout.flush()
sys.stderr.flush()
kw = { }
if editor: kw[ 'editor' ] = editor
sys.path.insert( 0, os.path.dirname(scriptPath) )
module = __import__( os.path.basename(scriptPath), globals(), locals() )
if not 'scriptMain' in module.__dict__:
print( '[ERROR] Script module is missing function scriptMain().' )
print( ' "{}"'.format( scriptPath ))
return
if not callable( module.__dict__['scriptMain'] ):
print( '[ERROR] Script module symbol scriptMain is not callable (not a function?).' )
print( ' "{}"'.format( scriptPath ))
return
module.__dict__['scriptMain']( **kw )
except ImportError as e:
#module = str(e).split()[-1]
#print( '[ERROR] The "{}" script cannot be loaded.'.format(os.path.basename(scriptPath)) )
#print( ' Please check your design hierarchy or the Python syntax.' )
#print( ' Error was:' )
#print( ' {}\n'.format(e))
helpers.io.catch( e )
except Exception as e:
helpers.io.catch( e )
return
def run():
try:
usage = str(setCgtBanner(CRL.Banner()))
usage += '\ncgt [options]'
parser = optparse.OptionParser(usage)
parser.add_option( '--no-init' , action='store_true', dest='noInit' , help='Do not load any initialization.')
parser.add_option( '-c', '--cell' , type='string' , dest='cell' , help='The name of the cell to load, without extension.')
parser.add_option( '--acm-sigda-89' , type='string' , dest='acmSigdaName' , help='An ACM/SIGDA 89 bench name to load, without extension.')
parser.add_option( '--blif' , type='string' , dest='blifName' , help='A Blif (Yosys) design name to load, without extension.')
parser.add_option( '--ispd-05' , type='string' , dest='ispd05name' , help='An ISPD 05 bench (placement) name to load, without extension.')
parser.add_option( '--script' , type='string' , dest='script' , help='Run a Python or Stratus script.')
parser.add_option( '-v', '--verbose' , action='store_true', dest='verbose' , help='First level of verbosity.')
parser.add_option( '-V', '--very-verbose' , action='store_true', dest='veryVerbose' , help='Second level of verbosity.')
parser.add_option( '-i', '--info' , action='store_true', dest='info' , help='Display lots of informational messages.')
parser.add_option( '--paranoid' , action='store_true', dest='paranoid' , help='Display everything that *may be* suspicious...')
parser.add_option( '-b', '--bug' , action='store_true', dest='bug' , help='Display bug related messages.')
parser.add_option( '--show-conf' , action='store_true', dest='showConf' , help='Display Kite configuration.')
parser.add_option( '-D', '--core-dump' , action='store_true', dest='coreDump' , help='Enable core-dump when a crash occurs.')
parser.add_option( '-L', '--log-mode' , action='store_true', dest='logMode' , help='Disable ANSI escape sequences in console output.')
parser.add_option( '-t', '--text' , action='store_true', dest='textMode' , help='Run in command line mode.')
parser.add_option( '-K', '--use-katana' , action='store_true', dest='useKatana' , help='Use Katana instead of Knik/Kite router.')
parser.add_option( '-m', '--margin' , type='float' , dest='margin' , help='Percentage of free area to add to the minimal placement area.')
parser.add_option( '-P', '--place' , action='store_true', dest='place' , help='Run the analytical placer (Etesian).')
parser.add_option( '-G', '--global-route' , action='store_true', dest='globalRoute' , help='Run the global router (Knik).')
parser.add_option( '-g', '--load-global' , action='store_true', dest='loadGlobal' , help='Reload a global routing from disk.')
parser.add_option( '--save-global' , action='store_true', dest='saveGlobal' , help='Save the global routing solution.')
parser.add_option( '--htracks-local' , type='int' , dest='hTracksLocal' , help='The amount of horizontal tracks reserved for the GCell local routing.')
parser.add_option( '--vtracks-local' , type='int' , dest='vTracksLocal' , help='The amount of vertical tracks reserved for the GCell local routing .')
parser.add_option( '--events-limit' , type='int' , dest='eventsLimit' , help='The maximum number of iterations (events) that the router is allowed to perform.')
parser.add_option( '-R', '--detail-route' , action='store_true', dest='detailRoute' , help='Run the detailed router (Kite).')
parser.add_option( '-M', '--dump-measures' , action='store_true', dest='dumpMeasures' , help='Dump some statistical measurements on the disk.')
parser.add_option( '-s', '--save-design' , type='string' , dest='saveDesign' , help='Save the routed design.')
parser.add_option( '--top-routing-layer', type='string' , dest='topRoutingLayer', help='Sets the top (upper) routing layer.')
parser.add_option( '--vst-use-concat' , action='store_true', dest='vstUseConcat' , help='The VST driver will use "&" (concat) in PORT MAP.')
(options, args) = parser.parse_args()
args.insert(0, 'cgt')
useKatana = False
flags = 0
if options.noInit:
flags |= CRL.AllianceFramework.NoPythonInit
af = CRL.AllianceFramework.create( flags )
if helpers.io.isVL(2): print( af.getEnvironment().getPrint() )
Cfg.Configuration.pushDefaultPriority(Cfg.Parameter.Priority.CommandLine)
if options.coreDump: Cfg.getParamBool ('misc.catchCore' ).setBool(False)
if options.verbose: Cfg.getParamBool ('misc.verboseLevel1').setBool(True)
if options.veryVerbose: Cfg.getParamBool ('misc.verboseLevel2').setBool(True)
if options.info: Cfg.getParamBool ('misc.info' ).setBool(True)
if options.paranoid: Cfg.getParamBool ('misc.paranoid' ).setBool(True)
if options.bug: Cfg.getParamBool ('misc.bug' ).setBool(True)
if options.logMode: Cfg.getParamBool ('misc.logMode' ).setBool(True)
if options.showConf: Cfg.getParamBool ('misc.showConf' ).setBool(True)
if options.margin: Cfg.getParamPercentage('etesian.spaceMargin').setPercentage(options.margin)
if options.hTracksLocal: Cfg.getParamInt ('katana.hTracksReservedLocal').setInt(options.hTracksLocal)
if options.vTracksLocal: Cfg.getParamInt ('katana.vTracksReservedLocal').setInt(options.vTracksLocal)
if options.eventsLimit: Cfg.getParamInt ('katana.eventsLimit' ).setInt(options.eventsLimit)
if options.topRoutingLayer: Cfg.getParamString ('anabatic.topRoutingLayer' ).setString(options.topRoutingLayer)
if options.useKatana: useKatana = True
loadGlobal = options.loadGlobal
saveGlobal = options.saveGlobal
globalRoute = options.globalRoute
detailRoute = options.detailRoute
runEtesianTool = options.place
Cfg.Configuration.popDefaultPriority()
cell = None
if options.acmSigdaName:
cell = CRL.AcmSigda.load(options.acmSigdaName)
elif options.ispd05name:
cell = CRL.Ispd05.load(options.ispd05name)
elif options.blifName:
cell = CRL.Blif.load(options.blifName)
elif options.cell:
cell = af.getCell(options.cell, CRL.Catalog.State.Views)
else:
runEtesianTool = False
loadGlobal = False
saveGlobal = False
globalRoute = False
detailRoute = False
if not options.textMode:
# Run in graphic mode.
ha = Viewer.HApplication.create(args)
Viewer.Graphics.enable()
unicorn = Unicorn.UnicornGui.create()
unicorn.setApplicationName ('cgt')
unicorn.registerTool (Etesian.GraphicEtesianEngine.grab())
#unicorn.registerTool (Kite.GraphicKiteEngine.grab())
unicorn.registerTool (Katana.GraphicKatanaEngine.grab())
unicorn.registerTool (Bora.GraphicBoraEngine.grab())
unicorn.registerTool (Tutorial.GraphicTutorialEngine.grab())
#unicorn.setAnonNetSelectable(False)
unicorn.setLayerVisible ("grid" , False);
unicorn.setLayerVisible ("text.instance" , False);
unicorn.setLayerVisible ("text.component", False);
if options.script:
runScript(options.script,unicorn)
setCgtBanner(unicorn.getBanner())
#print( unicorn.getBanner() )
#print( credits() )
if cell: unicorn.setCell(cell)
unicorn.show()
ha.qtExec()
else:
# Run in command line mode.
if options.script: runScript(options.script,None)
kiteSuccess = True
if runEtesianTool:
etesian = Etesian.EtesianEngine.create(cell)
#if options.showConf: etesian.printConfiguration()
etesian.place()
if detailRoute and not (loadGlobal or globalRoute): globalRoute = True
runKiteTool = loadGlobal or globalRoute or detailRoute
if useKatana and runKiteTool:
runKiteTool = False
katana = Katana.KatanaEngine.create( cell )
#katana.printConfiguration ()
katana.digitalInit ()
#katana.runNegociatePreRouted()
katana.runGlobalRouter ( Katana.Flags.NoFlags )
katana.loadGlobalRouting ( Anabatic.EngineLoadGrByNet )
katana.layerAssign ( Anabatic.EngineNoNetLayerAssign )
katana.runNegociate ( Katana.Flags.NoFlags )
kiteSuccess = katana.isDetailedRoutingSuccess()
#katana.finalizeLayout()
katana.destroy()
if options.saveDesign:
views = CRL.Catalog.State.Physical
if options.vstUseConcat: views |= CRL.Catalog.State.VstUseConcat
if options.saveDesign != cell.getName():
cell.setName(options.saveDesign)
views |= CRL.Catalog.State.Logical
af.saveCell(cell, views)
sys.exit(not kiteSuccess)
except Exception as e:
helpers.io.showPythonTrace( sys.argv[0], e )
sys.exit(0)

177
pysetup/setup.py Normal file
View File

@ -0,0 +1,177 @@
import os, sys
import setuptools
def find_coriolis_install(root=None):
installs=[]
if root is None:
root=os.path.join(os.environ['HOME'],'coriolis-2.x')
for r,d,f in os.walk(root):
if "install" in d:
installs.append(os.path.join(r,'install'))
if not installs:
print("Error: Coriolis build not found.")
sys.exit(-1)
if len(installs)>1:
print("Error: Found several Coriolis builds. Work only with one.")
sys.exit(-2)
return installs[0]
VERSION = "2.4.0"
setup_path=os.path.dirname(os.path.realpath(__file__))
coriolis_install_path=find_coriolis_install()
coriolis_bin_path=os.path.join(coriolis_install_path,'bin')
for name in ["lib64","lib"]:
coriolis_lib_path=os.path.join(coriolis_install_path,'lib64')
if os.path.isdir(coriolis_lib_path):
break
coriolis_lib_path=None
if not coriolis_lib_path:
print("Error: lib64 or lib directory not found in Coriolis build.")
sys.exit(-3)
coriolis_python_path=None
for r,d,f in os.walk(coriolis_lib_path):
if "coriolis" in d:
coriolis_python_path=r
break
if not coriolis_python_path:
print("Error: 'coriolis' python module not found in Coriolis build.")
sys.exit(-4)
#for name in (setup_path,coriolis_install_path,coriolis_bin_path,coriolis_lib_path,coriolis_python_path):
# print(name)
def _add_preload_code(path,libs):
with open(path,"rt") as f:
code=f.read().strip()
if not code.startswith("# preload"):
with open(path,"wt") as f:
f.write(
"""# preload shared libraries
import os, ctypes
__libs=[]
__lib_folder=os.path.join(os.path.dirname(os.path.realpath(__file__)),'libs')
for name in (
""")
for lib in libs:
lib=lib.strip()
if not lib:
continue
f.write(f' "{lib}",\n')
f.write(
"""
):
__libs.append(ctypes.CDLL(os.path.join(__lib_folder,name)))
""")
f.write("\n")
f.write(code)
f.write("\n")
#_add_preload_code(os.path.join(setup_path,"coriolis_cmds","__init__.py"),
#"""
#libdef.so
#libpyflute.so
#libcoloquinte.so
#libhurricane.so
#libutils.so
#libpycrlconst.so
#libconfiguration.so
#libpytypemanager.so
#libflute.so
#liblef.so
#libdefzlib.so
#libisobar.so
#libintervalTree.so
#libviewer.so
#liblefzlib.so
#libanalog.so
#libcrlcore.so
#libtutorial.so
#libequinox.so
#libpycrlcore.so
#liblibmanager.so
#libetesian.so
#libsolstice.so
#libunicorn.so
#libanabatic.so
#libkatana.so
#libpyanabatic.so
#libbora.so
#libpybora.so
#""".split())
import sys, ctypes
def _files(folder):
files=[]
for f in os.listdir(folder):
path=os.path.join(folder,f)
if os.path.isfile(path):
files.append(path)
return files
def _print_ordered_preload_libs():
libs=_files(coriolis_lib_path)
libs_ordered=[]
__libs=[]
while libs:
name=libs.pop(0)
if not name.endswith(".so"):
continue
try:
lib=ctypes.CDLL(name)
__libs.append(lib)
libs_ordered.append(name)
except OSError:
libs.append(name)
for path in libs_ordered:
print(f' "{os.path.split(path)[-1]}",')
#_print_ordered_preload_libs()
def _coriolis_packages():
def _packages(prefix,folder):
root=prefix+[folder]
yield '.'.join(root)
for f in os.listdir(os.path.join(coriolis_python_path,*root)):
if f=="__pycache__":
continue
path=os.path.join(coriolis_python_path,*root,f)
if os.path.isdir(path):
yield from _packages(root,f)
yield from _packages([],"coriolis")
with open(os.path.join(coriolis_python_path,"coriolis","__init__.py"),"wt") as f:
f.write("from .cmds import *\n")
setuptools.setup(
name = "coriolis",
version = VERSION,
description = 'Coriolis is a free database, placement tool and routing tool for VLSI design.',
author = ', '.join(["Rémy ESCASSUT & Christian",
"Christophe ALEXANDRE",
"Sophie BELLOEIL",
"Damien DUPUIS",
"Jean-Paul CHAPUT"
]),
#author_email = 'martin@v.loewis.de',
url = 'https://www-soc.lip6.fr/sesi-docs/coriolis2-docs/coriolis2/en/latex/users-guide/UsersGuide.pdf',
long_description = '''
Coriolis provides several tools to perform the layout of VLSI circuits.
Its main components are the Hurricane database, the Etesian placer and
the Katana router, but other tools can use the Hurricane database and
the parsers provided.
''',
install_requires=['platformdirs'],
package_dir={"coriolis":os.path.join(coriolis_python_path,'coriolis'),
"coriolis.libs":coriolis_lib_path,
"coriolis.bin":coriolis_bin_path,
"coriolis.cmds":os.path.join(setup_path,"coriolis_cmds"),
},
packages=list(_coriolis_packages())+["coriolis.libs", "coriolis.bin", "coriolis.cmds"],
package_data={"":["*.so","*.so.*"],"coriolis.bin":['*']},
entry_points = {
'console_scripts': ['coriolis=coriolis.cmds.coriolis:run'],
}
)