2016-03-26 15:32:11 +03:00
#! /usr/bin/env python
# encoding: utf-8
# DC 2008
2018-01-31 12:48:43 +03:00
# Thomas Nagy 2016-2018 (ita)
2016-03-26 15:32:11 +03:00
2018-01-31 12:48:43 +03:00
import os , re , traceback
2016-03-26 15:32:11 +03:00
from waflib import Utils , Logs , Errors
from waflib . Tools import fc , fc_config , fc_scan , ar , ccroot
from waflib . Configure import conf
from waflib . TaskGen import after_method , feature
@conf
def find_ifort ( conf ) :
fc = conf . find_program ( ' ifort ' , var = ' FC ' )
conf . get_ifort_version ( fc )
conf . env . FC_NAME = ' IFORT '
@conf
def ifort_modifier_win32 ( self ) :
v = self . env
v . IFORT_WIN32 = True
v . FCSTLIB_MARKER = ' '
v . FCSHLIB_MARKER = ' '
v . FCLIB_ST = v . FCSTLIB_ST = ' %s .lib '
v . FCLIBPATH_ST = v . STLIBPATH_ST = ' /LIBPATH: %s '
v . FCINCPATH_ST = ' /I %s '
v . FCDEFINES_ST = ' /D %s '
v . fcprogram_PATTERN = v . fcprogram_test_PATTERN = ' %s .exe '
v . fcshlib_PATTERN = ' %s .dll '
v . fcstlib_PATTERN = v . implib_PATTERN = ' %s .lib '
v . FCLNK_TGT_F = ' /out: '
v . FC_TGT_F = [ ' /c ' , ' /o ' , ' ' ]
v . FCFLAGS_fcshlib = ' '
v . LINKFLAGS_fcshlib = ' /DLL '
v . AR_TGT_F = ' /out: '
v . IMPLIB_ST = ' /IMPLIB: %s '
v . append_value ( ' LINKFLAGS ' , ' /subsystem:console ' )
if v . IFORT_MANIFEST :
v . append_value ( ' LINKFLAGS ' , [ ' /MANIFEST ' ] )
@conf
def ifort_modifier_darwin ( conf ) :
fc_config . fortran_modifier_darwin ( conf )
@conf
def ifort_modifier_platform ( conf ) :
dest_os = conf . env . DEST_OS or Utils . unversioned_sys_platform ( )
ifort_modifier_func = getattr ( conf , ' ifort_modifier_ ' + dest_os , None )
if ifort_modifier_func :
ifort_modifier_func ( )
@conf
def get_ifort_version ( conf , fc ) :
"""
Detects the compiler version and sets ` ` conf . env . FC_VERSION ` `
"""
version_re = re . compile ( r " \ bIntel \ b.* \ bVersion \ s*(?P<major> \ d*) \ .(?P<minor> \ d*) " , re . I ) . search
if Utils . is_win32 :
cmd = fc
else :
cmd = fc + [ ' -logo ' ]
out , err = fc_config . getoutput ( conf , cmd , stdin = False )
match = version_re ( out ) or version_re ( err )
if not match :
conf . fatal ( ' cannot determine ifort version. ' )
k = match . groupdict ( )
conf . env . FC_VERSION = ( k [ ' major ' ] , k [ ' minor ' ] )
def configure ( conf ) :
"""
Detects the Intel Fortran compilers
"""
if Utils . is_win32 :
2018-01-31 12:48:43 +03:00
compiler , version , path , includes , libdirs , arch = conf . detect_ifort ( )
2016-03-26 15:32:11 +03:00
v = conf . env
v . DEST_CPU = arch
v . PATH = path
v . INCLUDES = includes
v . LIBPATH = libdirs
v . MSVC_COMPILER = compiler
try :
v . MSVC_VERSION = float ( version )
2018-01-31 12:48:43 +03:00
except ValueError :
2016-03-26 15:32:11 +03:00
v . MSVC_VERSION = float ( version [ : - 3 ] )
conf . find_ifort_win32 ( )
conf . ifort_modifier_win32 ( )
else :
conf . find_ifort ( )
conf . find_program ( ' xiar ' , var = ' AR ' )
conf . find_ar ( )
conf . fc_flags ( )
conf . fc_add_flags ( )
conf . ifort_modifier_platform ( )
all_ifort_platforms = [ ( ' intel64 ' , ' amd64 ' ) , ( ' em64t ' , ' amd64 ' ) , ( ' ia32 ' , ' x86 ' ) , ( ' Itanium ' , ' ia64 ' ) ]
""" List of icl platforms """
@conf
def gather_ifort_versions ( conf , versions ) :
"""
List compiler versions by looking up registry keys
"""
2019-06-03 11:40:55 +03:00
version_pattern = re . compile ( r ' ^...?.? \ ....?.? ' )
2016-03-26 15:32:11 +03:00
try :
all_versions = Utils . winreg . OpenKey ( Utils . winreg . HKEY_LOCAL_MACHINE , ' SOFTWARE \\ Wow6432node \\ Intel \\ Compilers \\ Fortran ' )
2018-01-31 12:48:43 +03:00
except OSError :
2016-03-26 15:32:11 +03:00
try :
all_versions = Utils . winreg . OpenKey ( Utils . winreg . HKEY_LOCAL_MACHINE , ' SOFTWARE \\ Intel \\ Compilers \\ Fortran ' )
2018-01-31 12:48:43 +03:00
except OSError :
2016-03-26 15:32:11 +03:00
return
index = 0
while 1 :
try :
version = Utils . winreg . EnumKey ( all_versions , index )
2018-01-31 12:48:43 +03:00
except OSError :
2016-03-26 15:32:11 +03:00
break
index + = 1
if not version_pattern . match ( version ) :
continue
targets = { }
for target , arch in all_ifort_platforms :
2018-01-31 12:48:43 +03:00
if target == ' intel64 ' :
targetDir = ' EM64T_NATIVE '
else :
targetDir = target
2016-03-26 15:32:11 +03:00
try :
Utils . winreg . OpenKey ( all_versions , version + ' \\ ' + targetDir )
icl_version = Utils . winreg . OpenKey ( all_versions , version )
path , type = Utils . winreg . QueryValueEx ( icl_version , ' ProductDir ' )
2018-01-31 12:48:43 +03:00
except OSError :
2016-03-26 15:32:11 +03:00
pass
else :
2018-01-31 12:48:43 +03:00
batch_file = os . path . join ( path , ' bin ' , ' ifortvars.bat ' )
2016-03-26 15:32:11 +03:00
if os . path . isfile ( batch_file ) :
targets [ target ] = target_compiler ( conf , ' intel ' , arch , version , target , batch_file )
for target , arch in all_ifort_platforms :
try :
icl_version = Utils . winreg . OpenKey ( all_versions , version + ' \\ ' + target )
path , type = Utils . winreg . QueryValueEx ( icl_version , ' ProductDir ' )
2018-01-31 12:48:43 +03:00
except OSError :
2016-03-26 15:32:11 +03:00
continue
else :
2018-01-31 12:48:43 +03:00
batch_file = os . path . join ( path , ' bin ' , ' ifortvars.bat ' )
2016-03-26 15:32:11 +03:00
if os . path . isfile ( batch_file ) :
targets [ target ] = target_compiler ( conf , ' intel ' , arch , version , target , batch_file )
major = version [ 0 : 2 ]
versions [ ' intel ' + major ] = targets
@conf
def setup_ifort ( conf , versiondict ) :
"""
Checks installed compilers and targets and returns the first combination from the user ' s
options , env , or the global supported lists that checks .
: param versiondict : dict ( platform - > dict ( architecture - > configuration ) )
: type versiondict : dict ( string - > dict ( string - > target_compiler )
: return : the compiler , revision , path , include dirs , library paths and target architecture
: rtype : tuple of strings
"""
platforms = Utils . to_list ( conf . env . MSVC_TARGETS ) or [ i for i , j in all_ifort_platforms ]
desired_versions = conf . env . MSVC_VERSIONS or list ( reversed ( list ( versiondict . keys ( ) ) ) )
for version in desired_versions :
try :
targets = versiondict [ version ]
except KeyError :
continue
for arch in platforms :
try :
cfg = targets [ arch ]
except KeyError :
continue
cfg . evaluate ( )
if cfg . is_valid :
compiler , revision = version . rsplit ( ' ' , 1 )
return compiler , revision , cfg . bindirs , cfg . incdirs , cfg . libdirs , cfg . cpu
conf . fatal ( ' ifort: Impossible to find a valid architecture for building %r - %r ' % ( desired_versions , list ( versiondict . keys ( ) ) ) )
@conf
def get_ifort_version_win32 ( conf , compiler , version , target , vcvars ) :
# FIXME hack
try :
conf . msvc_cnt + = 1
except AttributeError :
conf . msvc_cnt = 1
batfile = conf . bldnode . make_node ( ' waf-print-msvc- %d .bat ' % conf . msvc_cnt )
batfile . write ( """ @echo off
set INCLUDE =
set LIB =
call " %s " % s
echo PATH = % % PATH % %
echo INCLUDE = % % INCLUDE % %
echo LIB = % % LIB % % ; % % LIBPATH % %
""" % (vcvars,target))
sout = conf . cmd_and_log ( [ ' cmd.exe ' , ' /E:on ' , ' /V:on ' , ' /C ' , batfile . abspath ( ) ] )
batfile . delete ( )
lines = sout . splitlines ( )
if not lines [ 0 ] :
lines . pop ( 0 )
MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
for line in lines :
if line . startswith ( ' PATH= ' ) :
path = line [ 5 : ]
MSVC_PATH = path . split ( ' ; ' )
elif line . startswith ( ' INCLUDE= ' ) :
MSVC_INCDIR = [ i for i in line [ 8 : ] . split ( ' ; ' ) if i ]
elif line . startswith ( ' LIB= ' ) :
MSVC_LIBDIR = [ i for i in line [ 4 : ] . split ( ' ; ' ) if i ]
if None in ( MSVC_PATH , MSVC_INCDIR , MSVC_LIBDIR ) :
conf . fatal ( ' ifort: Could not find a valid architecture for building (get_ifort_version_win32) ' )
# Check if the compiler is usable at all.
# The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
env = dict ( os . environ )
env . update ( PATH = path )
compiler_name , linker_name , lib_name = _get_prog_names ( conf , compiler )
fc = conf . find_program ( compiler_name , path_list = MSVC_PATH )
# delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
if ' CL ' in env :
del ( env [ ' CL ' ] )
try :
conf . cmd_and_log ( fc + [ ' /help ' ] , env = env )
except UnicodeError :
2018-01-31 12:48:43 +03:00
st = traceback . format_exc ( )
2016-03-26 15:32:11 +03:00
if conf . logger :
conf . logger . error ( st )
conf . fatal ( ' ifort: Unicode error - check the code page? ' )
2018-01-31 12:48:43 +03:00
except Exception as e :
2016-03-26 15:32:11 +03:00
Logs . debug ( ' ifort: get_ifort_version: %r %r %r -> failure %s ' , compiler , version , target , str ( e ) )
conf . fatal ( ' ifort: cannot run the compiler in get_ifort_version (run with -v to display errors) ' )
else :
Logs . debug ( ' ifort: get_ifort_version: %r %r %r -> OK ' , compiler , version , target )
finally :
conf . env [ compiler_name ] = ' '
return ( MSVC_PATH , MSVC_INCDIR , MSVC_LIBDIR )
class target_compiler ( object ) :
"""
Wraps a compiler configuration ; call evaluate ( ) to determine
whether the configuration is usable .
"""
def __init__ ( self , ctx , compiler , cpu , version , bat_target , bat , callback = None ) :
"""
: param ctx : configuration context to use to eventually get the version environment
: param compiler : compiler name
: param cpu : target cpu
: param version : compiler version number
: param bat_target : ?
: param bat : path to the batch file to run
: param callback : optional function to take the realized environment variables tup and map it ( e . g . to combine other constant paths )
"""
self . conf = ctx
self . name = None
self . is_valid = False
self . is_done = False
self . compiler = compiler
self . cpu = cpu
self . version = version
self . bat_target = bat_target
self . bat = bat
self . callback = callback
def evaluate ( self ) :
if self . is_done :
return
self . is_done = True
try :
2018-01-31 12:48:43 +03:00
vs = self . conf . get_ifort_version_win32 ( self . compiler , self . version , self . bat_target , self . bat )
2016-03-26 15:32:11 +03:00
except Errors . ConfigurationError :
self . is_valid = False
return
if self . callback :
vs = self . callback ( self , vs )
self . is_valid = True
( self . bindirs , self . incdirs , self . libdirs ) = vs
def __str__ ( self ) :
return str ( ( self . bindirs , self . incdirs , self . libdirs ) )
def __repr__ ( self ) :
return repr ( ( self . bindirs , self . incdirs , self . libdirs ) )
@conf
def detect_ifort ( self ) :
return self . setup_ifort ( self . get_ifort_versions ( False ) )
@conf
def get_ifort_versions ( self , eval_and_save = True ) :
"""
: return : platforms to compiler configurations
: rtype : dict
"""
dct = { }
self . gather_ifort_versions ( dct )
return dct
def _get_prog_names ( self , compiler ) :
if compiler == ' intel ' :
compiler_name = ' ifort '
linker_name = ' XILINK '
lib_name = ' XILIB '
else :
# assumes CL.exe
compiler_name = ' CL '
linker_name = ' LINK '
lib_name = ' LIB '
return compiler_name , linker_name , lib_name
@conf
def find_ifort_win32 ( conf ) :
# the autodetection is supposed to be performed before entering in this method
v = conf . env
path = v . PATH
compiler = v . MSVC_COMPILER
version = v . MSVC_VERSION
compiler_name , linker_name , lib_name = _get_prog_names ( conf , compiler )
v . IFORT_MANIFEST = ( compiler == ' intel ' and version > = 11 )
# compiler
fc = conf . find_program ( compiler_name , var = ' FC ' , path_list = path )
# before setting anything, check if the compiler is really intel fortran
env = dict ( conf . environ )
2018-01-31 12:48:43 +03:00
if path :
env . update ( PATH = ' ; ' . join ( path ) )
2016-03-26 15:32:11 +03:00
if not conf . cmd_and_log ( fc + [ ' /nologo ' , ' /help ' ] , env = env ) :
conf . fatal ( ' not intel fortran compiler could not be identified ' )
v . FC_NAME = ' IFORT '
if not v . LINK_FC :
conf . find_program ( linker_name , var = ' LINK_FC ' , path_list = path , mandatory = True )
if not v . AR :
conf . find_program ( lib_name , path_list = path , var = ' AR ' , mandatory = True )
v . ARFLAGS = [ ' /nologo ' ]
# manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
if v . IFORT_MANIFEST :
conf . find_program ( ' MT ' , path_list = path , var = ' MT ' )
v . MTFLAGS = [ ' /nologo ' ]
try :
conf . load ( ' winres ' )
except Errors . WafError :
Logs . warn ( ' Resource compiler not found. Compiling resource file is disabled ' )
#######################################################################################################
##### conf above, build below
@after_method ( ' apply_link ' )
@feature ( ' fc ' )
def apply_flags_ifort ( self ) :
"""
Adds additional flags implied by msvc , such as subsystems and pdb files : :
def build ( bld ) :
bld . stlib ( source = ' main.c ' , target = ' bar ' , subsystem = ' gruik ' )
"""
if not self . env . IFORT_WIN32 or not getattr ( self , ' link_task ' , None ) :
return
is_static = isinstance ( self . link_task , ccroot . stlink_task )
subsystem = getattr ( self , ' subsystem ' , ' ' )
if subsystem :
subsystem = ' /subsystem: %s ' % subsystem
flags = is_static and ' ARFLAGS ' or ' LINKFLAGS '
self . env . append_value ( flags , subsystem )
if not is_static :
for f in self . env . LINKFLAGS :
d = f . lower ( )
if d [ 1 : ] == ' debug ' :
pdbnode = self . link_task . outputs [ 0 ] . change_ext ( ' .pdb ' )
self . link_task . outputs . append ( pdbnode )
if getattr ( self , ' install_task ' , None ) :
self . pdb_install_task = self . add_install_files ( install_to = self . install_task . install_to , install_from = pdbnode )
break
@feature ( ' fcprogram ' , ' fcshlib ' , ' fcprogram_test ' )
@after_method ( ' apply_link ' )
def apply_manifest_ifort ( self ) :
"""
Enables manifest embedding in Fortran DLLs when using ifort on Windows
See : http : / / msdn2 . microsoft . com / en - us / library / ms235542 ( VS .80 ) . aspx
"""
if self . env . IFORT_WIN32 and getattr ( self , ' link_task ' , None ) :
# it seems ifort.exe cannot be called for linking
self . link_task . env . FC = self . env . LINK_FC
if self . env . IFORT_WIN32 and self . env . IFORT_MANIFEST and getattr ( self , ' link_task ' , None ) :
out_node = self . link_task . outputs [ 0 ]
man_node = out_node . parent . find_or_declare ( out_node . name + ' .manifest ' )
self . link_task . outputs . append ( man_node )
self . env . DO_MANIFEST = True
2018-01-31 12:48:43 +03:00