diff --git a/__init__.py b/__init__.py index 607cb6c..da44a56 100644 --- a/__init__.py +++ b/__init__.py @@ -1,6 +1,7 @@ """ SConsProject + The sconsproject package proposes a way to easily create the compilation system of your project with the minimum of information. It's an helper around SCons. diff --git a/autoconf/_external.py b/autoconf/_external.py index 4a054a2..1cf8a11 100644 --- a/autoconf/_external.py +++ b/autoconf/_external.py @@ -86,7 +86,6 @@ def postconfigure(self, project, env, level): ''' Particular case, which allow to add things after all libraries checks. ''' - env.AppendUnique( LIBS = self.getLibs(env) ) return True def check(self, project, conf): diff --git a/autoconf/cdk.py b/autoconf/cdk.py new file mode 100644 index 0000000..d56e11c --- /dev/null +++ b/autoconf/cdk.py @@ -0,0 +1,4 @@ +from _external import * + +# CDK is a framework for ncurses +cdk = LibWithHeaderChecker('cdk', ['cdk.h'], 'c') diff --git a/autoconf/fmod.py b/autoconf/fmod.py new file mode 100644 index 0000000..1ff0791 --- /dev/null +++ b/autoconf/fmod.py @@ -0,0 +1,5 @@ +from _external import * + +fmod = LibWithHeaderChecker( 'fmod', 'fmod.h', 'c' ) + + diff --git a/autoconf/ncurses.py b/autoconf/ncurses.py new file mode 100644 index 0000000..72fd889 --- /dev/null +++ b/autoconf/ncurses.py @@ -0,0 +1,3 @@ +from _external import * + +ncurses = LibWithHeaderChecker('ncurses', ['ncurses.h'], 'c') diff --git a/autoconf/poco_foundation.py b/autoconf/poco_foundation.py index 42782ef..3915cb5 100644 --- a/autoconf/poco_foundation.py +++ b/autoconf/poco_foundation.py @@ -1,5 +1,5 @@ from _external import * from poco_xml import * -poco_foundation = HeaderChecker( 'poco_foundation', 'Poco/Foundation.h', 'c++', dependencies=[poco_xml] ) +poco_foundation = LibWithHeaderChecker( 'poco_foundation', ['Poco/Foundation.h'], 'c++', dependencies=[poco_xml] ) diff --git a/autoconf/poco_net.py b/autoconf/poco_net.py index 2d0737e..c5e7c59 100644 --- a/autoconf/poco_net.py +++ b/autoconf/poco_net.py @@ -6,7 +6,7 @@ windows = os.name.lower() == "nt" and sys.platform.lower().startswith("win") if windows: - poco_net = HeaderChecker( 'poco_net', 'Poco/Net/Net.h', 'c++', dependencies=[poco_foundation, winsock2] ) + poco_net = LibWithHeaderChecker( 'poco_net', 'Poco/Net/Net.h', 'c++', dependencies=[poco_foundation, winsock2] ) else: - poco_net = HeaderChecker( 'poco_net', 'Poco/Net/Net.h', 'c++', dependencies=[poco_foundation] ) + poco_net = LibWithHeaderChecker( 'poco_net', 'Poco/Net/Net.h', 'c++', dependencies=[poco_foundation] ) diff --git a/autoconf/poco_xml.py b/autoconf/poco_xml.py index 5b4416d..93fa0d9 100644 --- a/autoconf/poco_xml.py +++ b/autoconf/poco_xml.py @@ -1,4 +1,4 @@ from _external import * -poco_xml = HeaderChecker( 'poco_xml', 'Poco/XML/XML.h', 'c++' ) +poco_xml = LibWithHeaderChecker( 'poco_xml', 'Poco/XML/XML.h', 'c++' ) diff --git a/autoconf/qt5.py b/autoconf/qt5.py new file mode 100644 index 0000000..8660cf5 --- /dev/null +++ b/autoconf/qt5.py @@ -0,0 +1,154 @@ +from _external import * +import os +import SCons.Util + +def unique(list): + return dict.fromkeys(list).keys() + +def subdirs(files): + dirs = unique(map(os.path.dirname, files)) + dirs.sort() + return dirs + +def locateQt5Command(env, command, bindir): + #print 'locateQt5Command:', command + suffixes = [ + '-qt5', + '5', + '', + ] + progs = [command+s for s in suffixes] + for prog in progs: + path = env.WhereIs(prog, path=bindir) + if path: + return path + for prog in progs: + path = env.WhereIs(prog) + if path: + return path + + msg = 'Qt5 command "' + command + '" not found. Tried: ' + str(progs) + '.' + #raise Exception(msg) + print 'Warning: ', msg + return command + +class Qt5Checker(LibWithHeaderChecker): + ''' + Qt5 checker + ''' + allUiFiles = [] + + def __init__( self, + modules = [ + 'QtCore', + 'QtGui', + 'QtOpenGL', + 'QtWidgets', + 'QtNetwork', + 'QtPrintSupport', + 'QtWebKit', + 'QtWebKitWidgets', + ], + uiFiles = [], + defines = ['QT_NO_KEYWORDS'], + useLocalIncludes = True ): + self.name = 'qt5' + postfix = '' if not windows else '5' + for m in modules: + realName = m + postfix + if realName not in self.libs: + self.libs.append( realName ) + self.uiFiles =self.getAbsoluteCwd(uiFiles) + self.defines = defines[:] + self.useLocalIncludes = useLocalIncludes + + def setModules(self, modules): + self.libs = modules[:] + + def declareUiFiles(self, uiFiles): + self.uiFiles.extend( self.getAbsoluteCwd(uiFiles) ) + + def initEnv(self, project, env): + # use qt scons tool + env.Tool('qt') + + def initOptions(self, project, opts): + LibWithHeaderChecker.initOptions(self, project, opts) + opts.Add( 'bindir_'+self.name, 'Base directory for '+self.name, '${_join_if_basedir_not_empty( dir_'+self.name+ ', "bin" )}' ) + return True + + def configure(self, project, env): + env.EnableQtEmmitters() + + bindir = '$bindir_'+self.name + moc = locateQt5Command(env, 'moc', bindir) + uic = locateQt5Command(env, 'uic', bindir) + rcc = locateQt5Command(env, 'rcc', bindir) + lupdate = locateQt5Command(env, 'lupdate', bindir) + lrelease = locateQt5Command(env, 'lrelease', bindir) + + # specific part for Qt5 + env.Replace( + # suffixes/prefixes for the headers / sources to generate + QT_UICDECLPREFIX = 'ui_', + QT_UICDECLSUFFIX = '.h', + QT_UICIMPLPREFIX = 'ui_', + QT_UICIMPLSUFFIX = '$CXXFILESUFFIX', + QT_MOCHPREFIX = 'moc_', + QT_MOCHSUFFIX = '$CXXFILESUFFIX', + QT_MOCCXXPREFIX = '', + QT_MOCCXXSUFFIX = '.moc', + QT_UISUFFIX = '.ui', + + # Qt commands + # command to generate header from a .ui file + QT_UICCOM = [ + SCons.Util.CLVar('$QT_UIC $QT_UICDECLFLAGS -o ${TARGETS[0]} $SOURCE'), + ], + ) + + env.SetDefault( + QT_MOC = moc, + QT_UIC = uic, + QT5_RCC = rcc, + QT5_LUPDATE = lupdate, + QT5_LRELEASE = lrelease, + ) + + # don't need emitter with qt5 + env['BUILDERS']['Uic'].emitter = None + + if env['mode'] != 'debug' : + env.AppendUnique( CPPDEFINES = 'QMLJSDEBUGGER' ) + env.AppendUnique( CPPDEFINES = 'QT_DECLARATIVE_DEBUG' ) # QtQuick 1 + #env.AppendUnique( CPPDEFINES = 'QT_QML_DEBUG' ) # QtQuick 2 + + return BaseLibChecker.configure(self, project, env) + + def check(self, project, conf): + conf.env.AppendUnique( CPPDEFINES = self.defines ) + result = True + for mod in self.getLibs(conf.env): + r = self.CheckLibWithHeader( conf, [mod], header=[mod+'/'+mod], language='c++' ) + if not r: + print 'error: ',mod + result &= r + return result + + def postconfigure(self, project, env, level): + ''' + Add things for ui files after all libs check. + ''' + if len(self.uiFiles): + for ui in self.uiFiles: + # do not redeclare a ui file + if ui not in Qt5Checker.allUiFiles: + env.Uic( ui ) + Qt5Checker.allUiFiles.append( ui ) + if self.useLocalIncludes: + env.AppendUnique( CPPPATH=subdirs(self.uiFiles) ) + return True + +qt5 = Qt5Checker + + diff --git a/autoconf/qtmain.py b/autoconf/qtmain.py index 0b2d334..5c85ec4 100644 --- a/autoconf/qtmain.py +++ b/autoconf/qtmain.py @@ -1,3 +1,7 @@ from _external import * -qtmain = LibChecker( 'qtmain' ) +if windows: + qtmain = LibChecker( 'qtmain' ) +else: + qtmain = ObjectChecker( 'qtmain' ) + diff --git a/autoconf/qxorm.py b/autoconf/qxorm.py old mode 100755 new mode 100644 index 0048aae..1c0f36a --- a/autoconf/qxorm.py +++ b/autoconf/qxorm.py @@ -1,13 +1,13 @@ from _external import * from boost import * from boost_serialization import * -from qt4 import * +from qt5 import * qxorm = LibWithHeaderChecker( 'QxOrm', 'QxOrm.h', 'c++', name='qxorm', - dependencies= [ boost, boost_serialization, qt4(modules=[ 'QtCore', 'QtGui', 'QtSql' ] ) ], + dependencies= [ boost, boost_serialization, qt5(modules=[ 'QtCore', 'QtGui', 'QtSql' ] ) ], ) diff --git a/autoconf/sconsProject.py b/autoconf/sconsProject.py index babfb5f..28f67ed 100755 --- a/autoconf/sconsProject.py +++ b/autoconf/sconsProject.py @@ -30,7 +30,7 @@ def configure(self, project, env): env.AppendUnique( CPPDEFINES = 'WINDOWS' ) env.AppendUnique( CPPDEFINES = '_WINDOWS' ) env.AppendUnique( CPPDEFINES = '__WINDOWS__' ) - env.AppendUnique( CPPDEFINES = 'WIN'+str(env['osbits']) ) +# env.AppendUnique( CPPDEFINES = 'WIN'+str(env['osbits']) ) else: env.AppendUnique( CPPDEFINES = 'UNIX' ) env.AppendUnique( CPPDEFINES = '__UNIX__' ) diff --git a/compiler/clang.py b/compiler/clang.py index e41ccea..9ac599a 100755 --- a/compiler/clang.py +++ b/compiler/clang.py @@ -6,6 +6,8 @@ name = 'clang' ccBin = 'clang' cxxBin = 'clang++' +arBin = 'ar' +ranlibBin = 'ranlib' linkBin = ccBin linkxxBin = cxxBin ccVersionStr = 'unknown' diff --git a/compiler/gcc.py b/compiler/gcc.py index 9eaa4e5..618b5da 100755 --- a/compiler/gcc.py +++ b/compiler/gcc.py @@ -11,6 +11,8 @@ cxxBin = 'g++' linkBin = ccBin linkxxBin = cxxBin +arBin = 'ar' +ranlibBin = 'ranlib' ccVersionStr = 'unknown' ccVersion = [0,0,0] cxxVersionStr = 'unknown' @@ -23,6 +25,10 @@ CC['define'] = '-D' +CC['bigobj'] = '' +CC['multithreadedlib'] = '' +CC['multithreaded_static_lib'] = '' +CC['singlethreadedlib'] = '' CC['optimize'] = ['-O3'] #, '-flto']#, #'-finline-limit=700', @@ -72,6 +78,9 @@ CC['cover'] = ['-fprofile-arcs', '-ftest-coverage'] CC['linkcover'] = ['-lgcov'] +# @see visual.py +CC['wchar'] = [] + ##### -fprofile-arcs # Instrument arcs during compilation. For each function of your program, GCC creates a program flow graph, then finds a spanning tree for the graph. Only arcs that are not on the spanning tree have to be instrumented: the compiler adds code to count the number of times that these arcs are executed. When an arc is the only exit or only entrance to a block, the instrumentation code can be added to the block; otherwise, a new basic block must be created to hold the instrumentation code. # diff --git a/compiler/visual.py b/compiler/visual.py index f4a469e..e944896 100755 --- a/compiler/visual.py +++ b/compiler/visual.py @@ -4,6 +4,8 @@ cxxBin = 'cl' linkBin = 'link' linkxxBin = 'link' +arBin = '' +ranlibBin = '' ccVersionStr = 'unknown' ccVersion = [0,0,0] @@ -15,7 +17,9 @@ CC['exceptionsEnabled'] = '/EHsc' #'/GX' CC['multithreadedlib'] = '/MD' +CC['multithreaded_static_lib'] = '/MT' CC['singlethreadedlib'] = '/ML' +CC['bigobj'] = '/bigobj' CC['optimize'] =['/O2','/Ox','/GA','/GL'] CC['nooptimize'] =['/Od'] @@ -70,6 +74,8 @@ CC['ssse3'] = ['/arch:SSSE3'] CC['sse4'] = ['/arch:SSE4'] +# needed for compatibility with qt5 precompiled library +CC['wchar'] = ['/Zc:wchar_t-'] def retrieveVersion(ccBinArg): import subprocess diff --git a/project/__init__.py b/project/__init__.py index 9536b21..e2cd556 100755 --- a/project/__init__.py +++ b/project/__init__.py @@ -516,8 +516,11 @@ def help_format(env, opt, help, default, actual, aliases): opts.Add('aliases', 'A list of custom aliases.', []) opts.Add('jobs', 'Parallel jobs', '1') opts.Add(SCons.Script.BoolVariable('check_libs', 'Enable/Disable lib checking', True)) + opts.Add('SHLIBSUFFIX', 'Specify the shared libraries suffix', '.dll' if self.windows else( '.dylib' if self.macos else '.so' ) ) opts.Add('CC', 'Specify the C Compiler', self.compiler.ccBin) opts.Add('CXX', 'Specify the C++ Compiler', self.compiler.cxxBin) + opts.Add('AR', 'Specify the C Compiler', self.compiler.arBin) + opts.Add('RANLIB', 'Specify the C++ Compiler', self.compiler.ranlibBin) opts.Add('SCRIPTTESTXX', 'Specify the script test binary', "nosetests") opts.Add('SCRIPTTESTFLAGS', 'Specify the script test flags', "--detailed-errors --process-timeout=60 --nocapture") @@ -1078,7 +1081,7 @@ def declareTarget(self, localEnv, target, targetName=None): def StaticLibrary( self, target, - sources=[], dirs=[], libraries=[], includes=[], + sources=[], precsrc='', precinc='', dirs=[], libraries=[], includes=[], env=None, localEnvFlags={}, replaceLocalEnvFlags={}, externEnvFlags={}, globalEnvFlags={}, dependencies=[], installDir=None, installAs=None, install=True, headers=[], localHeaders=[], @@ -1152,6 +1155,12 @@ def StaticLibrary( self, target, sourcesFiles = self.getAbsoluteCwd( sourcesFiles ) + #adding precompiled headers + if precinc and self.windows: + localEnv['PCHSTOP'] = self.getRealAbsoluteCwd() + '/' + precinc + localEnv.Append( CPPFLAGS = [ '/FI' + self.getRealAbsoluteCwd() + '/' + precinc, '/Zm135' ] ) + localEnv['PCH'] = localEnv.PCH( precsrc )[0] + # create the target dstLib = localEnv.StaticLibrary( target=target, source=sourcesFiles ) @@ -1198,7 +1207,7 @@ def StaticLibrary( self, target, def SharedLibrary( self, target, - sources=[], dirs=[], libraries=[], includes=[], + sources=[], precsrc='', precinc='', dirs=[], libraries=[], includes=[], env=None, localEnvFlags={}, replaceLocalEnvFlags={}, externEnvFlags={}, globalEnvFlags={}, dependencies=[], installDir=None, installAs=None, install=True, headers=[], localHeaders=[], @@ -1266,6 +1275,12 @@ def SharedLibrary( self, target, sourcesFiles = self.getAbsoluteCwd( sourcesFiles ) + #adding precompiled headers + if precinc and self.windows: + localEnv['PCHSTOP'] = self.getRealAbsoluteCwd() + '/' + precinc + localEnv.Append( CPPFLAGS = [ '/FI' + self.getRealAbsoluteCwd() + '/' + precinc, '/Zm135' ] ) + localEnv['PCH'] = localEnv.PCH( precsrc )[0] + #print "target:", target localEnv['PDB'] = str(target) + '.pdb' # create the target @@ -1318,7 +1333,7 @@ def SharedLibrary( self, target, return dstLibInstall def Program( self, target, - sources=[], dirs=[], libraries=[], includes=[], + sources=[], dirs=[], libraries=[], includes=[], rc_files = [], precsrc = [], precinc = [], env=None, localEnvFlags={}, replaceLocalEnvFlags={}, externEnvFlags={}, globalEnvFlags={}, dependencies=[], installDir=None, install=True, headers=[], localHeaders=[], @@ -1381,9 +1396,19 @@ def Program( self, target, sourcesFiles = self.getAbsoluteCwd( sourcesFiles ) + # Add rc files (windows only) + if self.windows: + for rc in rc_files: + print rc + # sourcesFiles.append( localEnv.RES( rc ) ); + + if precinc and self.windows: + localEnv['PCHSTOP'] = self.getRealAbsoluteCwd() + '/' + precinc + localEnv.Append( CPPFLAGS = [ '/FI' + self.getRealAbsoluteCwd() + '/' + precinc, '/Zm135' ] ) + localEnv['PCH'] = localEnv.PCH( precsrc )[0] + # create the target dst = localEnv.Program( target=target, source=sourcesFiles ) - dstInstall = localEnv.Install( installDir if installDir else self.inOutputBin(), dst ) if install else dst localEnv.Alias( target, dstInstall ) self.declareTarget(localEnv, target) diff --git a/tools/__init__.py b/tools/__init__.py index 069c93c..17dad70 100755 --- a/tools/__init__.py +++ b/tools/__init__.py @@ -1,4 +1,4 @@ -import qt4 +import qt5 diff --git a/tools/qt.py b/tools/qt.py index f72a048..618ce1a 100644 --- a/tools/qt.py +++ b/tools/qt.py @@ -64,6 +64,30 @@ class QtdirNotFound(ToolQtWarning): #cxx_suffixes = cplusplus.CXXSuffixes cxx_suffixes = [".c", ".cxx", ".cpp", ".cc"] +# this function replace a given pattern (pat) by s_after inside the file fname +def fileReplace(fname, pat, s_after): + # first, see if the pattern is even in the file. + with open(fname) as f: + if not any(re.search(pat, line) for line in f): + return # pattern does not occur in file so we are done. + + # pattern is in the file, so perform replace operation. + with open(fname) as f: + out_fname = fname + ".tmp" + out = open(out_fname, "w") + for line in f: + out.write(re.sub(pat, s_after, line)) + out.close() + f.close(); + os.remove(fname) + os.rename(out_fname, fname) + +# simplify very long useless includes such as #include "../../../../foo.hpp" with #include "./foo.hpp" +# this is useful because moc generates very long relative includes that implies issues on windows OS. +def simplifyInclude(target, source, env): + fileReplace(target[0].rstr(), "#include\s+\"(\.\./)+", "#include \"" + os.getcwd().replace("\\", "/") + "/" ); + return None + def checkMocIncluded(target, source, env): moc = target[0] cpp = source[0] @@ -107,7 +131,7 @@ def __call__(self, target, source, env): debug = int(env.subst('$QT_DEBUG')) except ValueError: debug = 0 - + # some shortcuts used in the scanner splitext = SCons.Util.splitext objBuilder = getattr(env, self.objBuilderName) @@ -130,16 +154,16 @@ def __call__(self, target, source, env): out_sources = source[:] for obj in SCons.Util.flatten(source): - if not obj.has_builder(): - # binary obj file provided - if debug: - print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) - continue - cpp = obj.sources[0] + if not isinstance(obj, SCons.Node.Node) or not obj.has_builder(): + # binary obj file provided + if debug: + print "scons: qt: '%s' seems to be a binary. Discarded." % str(obj) + continue + cpp = obj.sources[0] if not splitext(str(cpp))[1] in cxx_suffixes: if debug: print "scons: qt: '%s' is no cxx file. Discarded." % str(cpp) - # c or fortran source + # c or fortran source continue #cpp_contents = comment.sub('', cpp.get_text_contents()) cpp_contents = cpp.get_text_contents() @@ -170,8 +194,7 @@ def __call__(self, target, source, env): # (to be included in cpp) moc = env.Moc(cpp) env.Ignore(moc, moc) - if debug: - print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) + print "scons: qt: found Q_OBJECT macro in '%s', moc'ing to '%s'" % (str(cpp), str(moc)) #moc.source_scanner = SCons.Defaults.CScan # restore the original env attributes (FIXME) objBuilder.env = objBuilderEnv @@ -270,6 +293,10 @@ def generate(env): QT_MOCCXXPREFIX = '', QT_MOCCXXSUFFIX = '.moc', QT_UISUFFIX = '.ui', + QT4_LUPDATE = os.path.join('$QT_BINPATH','lupdate'), + QT4_LRELEASE = os.path.join('$QT_BINPATH','lrelease'), + QT4_LUPDATECOM = '$QT4_LUPDATE $SOURCE -ts $TARGET', + QT4_LRELEASECOM = '$QT4_LRELEASE $SOURCE', # Commands for the qt support ... # command to generate header, implementation and moc-file @@ -289,8 +316,7 @@ def generate(env): # declarated in a cpp file QT_MOCFROMCXXCOM = [ CLVar('$QT_MOC $QT_MOCFROMCXXFLAGS -o ${TARGETS[0]} $SOURCE'), - Action(checkMocIncluded,None), - ] + Action(checkMocIncluded,None), Action(simplifyInclude)] ) # ... and the corresponding builders @@ -316,6 +342,20 @@ def generate(env): mocBld.suffix[cxx] = '$QT_MOCCXXSUFFIX' # register the builders + # Translation builder + tsbuilder = Builder( + action = SCons.Action.Action('$QT4_LUPDATECOM'), #,'$QT4_LUPDATECOMSTR'), + multi=1 + ) + qmbuilder = Builder( + action = SCons.Action.Action('$QT4_LRELEASECOM'),# , '$QT4_LRELEASECOMSTR'), + src_suffix = '.ts', + suffix = '.qm', + single_source = True + ) + + env['BUILDERS']['Ts'] = tsbuilder + env['BUILDERS']['Qm'] = qmbuilder env['BUILDERS']['Uic'] = uicBld env['BUILDERS']['Moc'] = mocBld static_obj, shared_obj = SCons.Tool.createObjBuilders(env)