summaryrefslogtreecommitdiff
path: root/SConstruct
diff options
context:
space:
mode:
authorThomas Tanner <trtanner@btinternet.com>2015-10-13 15:24:41 +0100
committerThomas Tanner <trtanner@btinternet.com>2015-10-13 15:24:41 +0100
commitec07d3d4d9ec2245f0b23edf6746f4ae1e5dfce4 (patch)
tree36398767aee155f703c255511bbe0e6b8c7919c4 /SConstruct
parent939993aefe4b0e2928835d2594713e96383c38ce (diff)
Updated scons build a bit to produce helpful-ish git tag for organizer.
Use r"..." strings in template file to make it easier to copy/paste paths from windows clipboard. Changed organizer.pro to not produce strange messages if you opened it because it couldn't find a .hg directory
Diffstat (limited to 'SConstruct')
-rw-r--r--SConstruct524
1 files changed, 524 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct
new file mode 100644
index 00000000..eb3da432
--- /dev/null
+++ b/SConstruct
@@ -0,0 +1,524 @@
+import distutils.sysconfig
+import os
+import re
+import sys
+
+def setup_config_variables():
+ """ Set up defaults values and load the configuration settings """
+ # Take a sensible default for pythonpath
+ must_be_specified = 'Must be specified ***'
+
+ boostpath = must_be_specified
+ if 'BOOSTPATH' in os.environ:
+ boostpath = os.environ['BOOSTPATH']
+
+ lootpath = must_be_specified
+ if 'LOOTPATH' in os.environ:
+ lootpath = os.environ['LOOTPATH']
+
+ pythonpath = must_be_specified
+ if 'PYTHONPATH' in os.environ:
+ pythonpath = os.environ['PYTHONPATH']
+ else:
+ pythonpath = distutils.sysconfig.EXEC_PREFIX
+
+ # Take qtdir from the os environment if set
+ qtdir = must_be_specified
+ if 'QTDIR' in os.environ:
+ qtdir = os.environ['QTDIR']
+ elif 'QT4DIR' in os.environ:
+ qtdir = os.environ['QT4DIR']
+
+ sevenzippath = must_be_specified
+ if 'SEVENZIPPATH' in os.environ:
+ sevenzippath = os.environ['SEVENZIPPATH']
+
+ zlibpath = must_be_specified
+ if 'ZLIBPATH' in os.environ:
+ zlibpath = os.environ['ZLIBPATH']
+
+ vars = Variables('scons_configure.py')
+ vars.AddVariables(
+ PathVariable('BOOSTPATH', 'Set to point to your boost directory',
+ boostpath, PathVariable.PathIsDir),
+ PathVariable('LOOTPATH', 'Set to point to your LOOT API directory',
+ lootpath, PathVariable.PathIsDir),
+ ('MSVC_VERSION', 'Version of msvc, defaults to latest installed'),
+ PathVariable('PYTHONPATH', 'Path to python install', pythonpath,
+ PathVariable.PathIsDir),
+ PathVariable('QTDIR', 'Path to the version of QT to use', qtdir,
+ PathVariable.PathIsDir),
+ PathVariable('SEVENZIPPATH', 'Path to 7zip sources', sevenzippath,
+ PathVariable.PathIsDir),
+ PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath,
+ PathVariable.PathIsDir).
+ PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath,
+ PathVariable.PathIsDir),
+ PathVariable('ZLIBPATH', 'Path to zlib install', zlibpath,
+ PathVariable.PathIsDir)
+ )
+
+ return vars
+
+def add_moc_files(self, files):
+ """
+ The QT4 tool only sets up moc for .cpp files. If there's a header file
+ with no corresponding source we have to do it ourselves. This makes it
+ easier.
+ Note: I could just supress the scanning and moc all the HEADER files...
+ """
+ targets = []
+ for file in self.Flatten([files]):
+ contents = file.get_contents()
+ target = str(file)[:-1] + 'cpp'
+ if not os.path.exists(target):
+ if 'Q_OBJECT' in file.get_contents():
+ header = file.name
+ target = self['QT5_XMOCHPREFIX'] + header[:-2] + \
+ self['QT5_XMOCHSUFFIX']
+ targets.append(self.ExplicitMoc5(target, header))
+ return targets
+
+def link_emitter_wrapper(target, source, env):
+ # This could be better written!
+ if '/DEBUG' in env['LINKFLAGS']:
+ name = str(target[0])[:-3] + 'pdb'
+ target.append(name)
+ return (target, source)
+
+def shlib_emitter_wrapper(target, source, env):
+ if 'WINDOWS_EMBED_MANIFEST' in env:
+ env.AppendUnique(LINKFLAGS = '/MANIFEST')
+ return link_emitter_wrapper(target, source, env)
+
+def fixup_qt4():
+ import SCons.Tool
+ oldpath = sys.path
+ sys.path = SCons.Tool.DefaultToolpath + sys.path
+ import qt4
+ def my_qrc_path(head, prefix, tail, suffix):
+ return "%s%s%s" % (prefix, tail, suffix)
+ qt4.__qrc_path = my_qrc_path
+ sys.path = oldpath
+
+metadata = re.compile(r'Q_PLUGIN_METADATA\(IID *".*" *FILE *"(.*)"\)')
+
+def json_emitter(target, source, env):
+ depends = []
+ for s in source:
+ match = metadata.search(s.get_contents())
+ if match:
+ depends += [ match.group(1) ]
+ env.Depends(target, depends)
+ return target, source
+
+def fixup_qt5():
+ import SCons.Tool
+ oldpath = sys.path
+ sys.path = SCons.Tool.DefaultToolpath + sys.path
+ import qt5
+ def my_qrc_path(head, prefix, tail, suffix):
+ return "%s%s%s" % (prefix, tail, suffix)
+ qt5.__qrc_path = my_qrc_path
+ sys.path = oldpath
+
+ qt_env['BUILDERS']['Moc5'].builder.emitter = json_emitter
+
+moduleDefines = {
+ '3Support' : [ 'QT_QT3SUPPORT_LIB', 'QT3_SUPPORT' ],
+ 'Core' : [ 'QT_CORE_LIB' ],
+ 'Declarative' : [ 'QT_DECLARATIVE_LIB' ],
+ 'Gui' : [ 'QT_GUI_LIB' ],
+ 'Network' : [ 'QT_NETWORK_LIB' ],
+ 'OpenGL' : [ 'QT_OPENGL_LIB' ],
+ 'Script' : [ 'QT_SCRIPT_LIB' ],
+ 'Sql' : [ 'QT_SQL_LIB' ],
+ 'Svg' : [ 'QT_SVG_LIB' ],
+ 'WebKit' : [ 'QT_WEBKIT_LIB' ],
+ 'Xml' : [ 'QT_XML_LIB' ],
+ 'XmlPatterns' : [ 'QT_XMLPATTERNS_LIB' ],
+# qt5
+ 'Qml' : [ 'QT_QML_LIB' ],
+ 'QuickWidgets' : [ 'QT_QUICKWIDGETS_LIB', 'QT_WIDGETS_LIB', 'QT_QUICK_LIB' ],
+ 'Widgets' : [ 'QT_WIDGETS_LIB' ],
+ 'WebKitWidgets' : [ 'QT_WEBKITWIDGETS_LIB' ],
+ 'WinExtras' : [ 'QT_WINEXTRAS_LIB' ]
+}
+
+staticModules = [
+ 'UiTools',
+]
+
+def get_qt_lib_info(env):
+ " Deal with the various QT naming conventions. sigh "
+
+ # For QT4, the libraries are QTlibname{,d}4
+ # For QT5, they are QT5libname{,d}
+
+ prefix = 'QT'
+ suffix = 'd' if env['CONFIG'] == 'debug' else ''
+
+ _suffix = suffix # Because they can't be consistent within a version,
+ # let alone between
+ version = env['QT_MAJOR_VERSION']
+ if version <= 4:
+ suffix += str(version)
+ else:
+ prefix += str(version)
+ return prefix, suffix, _suffix
+
+def EnableQtModules(self, *modules):
+ """ Enable the specified QT modules, mainly by adding defines
+ and libraries
+ """
+ self.AppendUnique(QT_USED_MODULES = modules)
+ for module in modules:
+ try:
+ self.AppendUnique(CPPDEFINES = moduleDefines[module])
+ if self['CONFIG'] == 'debug':
+ if module == 'Declarative':
+ self.AppendUnique(CPPDEFINES = 'QT_DECLARATIVE_DEBUG')
+ elif module == 'Qml':
+ self.AppendUnique(CPPDEFINES = 'QT_QML_DEBUG')
+ except:
+ print 'module', module, 'has no -D'
+ pass
+
+ if "Assistant" in modules:
+ self.AppendUnique(CPPPATH = [
+ os.path.join('$QTDIR', 'include', 'QtAssistant')
+ ])
+ modules.remove('Assistant')
+ modules.append('AssistantClient')
+
+ prefix, suffix, _suffix = get_qt_lib_info(self)
+ self.AppendUnique(LIBS = [
+ prefix + lib + suffix for lib in modules if lib not in staticModules
+ ])
+
+ self.PrependUnique(LIBS = [
+ lib + _suffix for lib in modules if lib in staticModules
+ ])
+
+ if 'OpenGL' in modules:
+ self.AppendUnique(LIBS = [ 'opengl32' ])
+
+ self.AppendUnique(CPPPATH = [
+ os.path.join('$QTDIR', 'include', 'QT' + module) for module in modules
+ ])
+
+def DisableQtModules(self, *modules):
+ """ Disable the specified QT modules similar to enabling them """
+ for module in modules:
+ try:
+ self['QT_USED_MODULES'].remove(module)
+ self['CPPDEFINES'].remove(moduleDefines[module])
+ if self['CONFIG'] == 'debug' and module == 'Declarative':
+ self['CPPDEFINES'].remove('QT_DECLARATIVE_DEBUG')
+ except:
+ pass
+
+
+ if "Assistant" in modules:
+ self['CPPPATH'].remove(os.path.join('$QTDIR', 'include', 'QtAssistant'))
+ modules.remove('Assistant')
+ modules.append('AssistantClient')
+
+ prefix, suffix, _suffix = get_qt_lib_info(self)
+
+ for lib in modules:
+ self['LIBS'].remove(prefix + lib + suffix)
+
+ if 'OpenGL' in modules:
+ self['LIBS'].remove('opengl32')
+
+ for module in modules:
+ self['CPPPATH'].remove(os.path.join('$QTDIR', 'include', 'QT' + module))
+
+
+# Create base environment
+vars = setup_config_variables()
+env = Environment(variables = vars, TARGET_ARCH = 'x86')
+
+# I'd really like to validate for unexpected settings in 'variables', but scons
+# appears to throw them away
+#ok = True
+#for key, value in vars.UnknownVariables():
+# print "unknown variable in scons_configure.py: %s=%s" % (key, value)
+# ok = False
+#if not ok:
+# sys.exit(1)
+
+# Patch scons to realise it's generating PDB files if /DEBUG is set
+env.AppendUnique(PROGEMITTER = [ link_emitter_wrapper ])
+# Ditto + windows_embed_manifest doesn't generate a manifest file
+env.AppendUnique(SHLIBEMITTER = [ shlib_emitter_wrapper ])
+
+# Work out where to find boost libraries
+libdir = os.path.join(env['BOOSTPATH'], 'lib32-msvc-' + env['MSVC_VERSION'])
+if not os.path.exists(libdir):
+ libdir = os.path.join(env['BOOSTPATH'], 'stage', 'lib')
+env.AppendUnique(LIBPATH = libdir)
+
+# Process command line to find out what/where we're building
+config = ARGUMENTS.get('CONFIG', 'debug')
+env['CONFIG'] = config
+
+# I think this is a bug in scons. It returns 12.0 for studio 13
+# This needs to match the QT specified version or bad things will happen!
+msvs_version = int(env['MSVS_VERSION'].split('.')[0]) + 2000
+if msvs_version > 2010:
+ msvs_version += 1
+
+# Need more work on the qt version stuff
+#build-ModOrganizer-Desktop_Qt_5_4_1_MSVC2013_OpenGL_32bit-Debug
+build_dir = 'scons-ModOrganizer-QT_%s_for_MSVS%d_32bit-%s' % (
+ #os.path.basename(env['QTDIR']).replace('.', '_'),
+ '5_4_1_OpenGL',
+ msvs_version,
+ config.title())
+
+# Put the sconsign file somewhere sane
+env.SConsignFile(os.path.join(build_dir, '.sconsign.dblite'))
+
+#this doesn't seem to work
+#env.VariantDir('build/$CONFIG', 'source')
+#env.VariantDir('build/$CONFIG', 'source', duplicate = 0)
+
+# Ripped off from qmake.conf
+
+# Compiler defines. Note that scons uses the same variables for C and C++
+# defines and include paths so be careful if you mix languages.
+
+# A note: QT puts _MSC_VER into the compile line, but I can see no earthly
+# reason for this.
+
+env.AppendUnique(CPPDEFINES = [
+ 'UNICODE',
+ 'WIN32',
+ 'NOMINMAX' # Nukes boost all over the place
+])
+
+# Default warning level.
+env['WARNING_LEVEL'] = 3
+
+# C compiler flags
+env.AppendUnique(CPPFLAGS = [
+ '/Zm200',
+ #'/Zc:wchar_t-', # 4 v 5
+ '/Zc:wchar_t',
+ '/W$WARNING_LEVEL'
+])
+
+# C++ compiler flags
+env.AppendUnique(CXXFLAGS = [
+ '/w34100',
+ '/w34189',
+ '/EHsc', # STL on
+ '/GR' # RTTI on
+])
+
+"""
+# qmake.conf
+#QMAKE_CFLAGS_RELEASE = -O2 -MD
+#QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MD -Zi
+#QMAKE_CFLAGS_LTCG = -GL
+#QMAKE_CFLAGS_MP = -MP
+
+QMAKE_LFLAGS_RELEASE = /INCREMENTAL:NO
+QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO = /DEBUG /OPT:REF
+QMAKE_LFLAGS_LTCG = /LTCG
+
+# project
+"""
+
+env['WINDOWS_EMBED_MANIFEST'] = True
+
+
+# Seriously, the linker doesn't apply these by default?
+env.AppendUnique(LINKFLAGS = [
+ '/DYNAMICBASE',
+ '/NXCOMPAT'
+])
+
+# Display full path in messages. Sadly even this isn't good enough for Creator
+# env.AppendUnique(CPPFLAGS = [ '/FC' ])
+
+if env['CONFIG'] == 'debug':
+ env.AppendUnique(CPPFLAGS = [ '/MDd', '/Z7' ])
+ env.AppendUnique(LINKFLAGS = [ '/DEBUG' ]) #, '/OPT:REF'])
+else:
+ env.AppendUnique(CPPFLAGS = [ '/O2', '/MD' ])
+ env.AppendUnique(LINKFLAGS = [ '/OPT:REF', '/OPT:ICF' ])
+
+# /OPT:REF removes unreferenced code
+# for release, use /OPT:ICF (comdat folding: coalesce identical blocks of code)
+
+# We have to make the install path absolute. *sigh*. But it appears to have to
+# be in build_dir...
+env['INSTALL_PATH'] = os.path.join(os.getcwd(), build_dir, '_ModOrganizer')
+
+# Create the environment for QT
+qt_env = env.Clone()
+
+# If you don't do this for the release build, QT gets very upset...
+if qt_env['CONFIG'] != 'debug':
+ qt_env.AppendUnique(CPPDEFINES = [ 'QT_NO_DEBUG' ])
+
+# Better way of working this out
+qt_env['QT_MAJOR_VERSION'] = 5 #int(os.path.basename(env['QTDIR']).split('.')[0])
+
+qt_env.Tool('qt%d' % qt_env['QT_MAJOR_VERSION'])
+
+# FIXME See if I can work out how to get official scons qt to work. Appears
+# to only work with QT5
+fixup_qt5()
+
+qt_env.AddMethod(add_moc_files, 'AddExtraMoc')
+
+# A very strange rune which QT sets
+qt_env['EXE_MANIFEST_DEPENDENCY'] =\
+ '/MANIFESTDEPENDENCY:"' + ' '.join(("type='win32'",
+ "name='Microsoft.Windows.Common-Controls'",
+ "version='6.0.0.0'",
+ "publicKeyToken='6595b64144ccf1df'",
+ "language='*'",
+ "processorArchitecture='*'")) + '"'
+
+# Not sure how necessary this is. Moreover it says msvc but seems to expect
+# the msvs number
+qt_env.AppendUnique(CPPPATH = [
+ os.path.join(env['QTDIR'], 'mkspecs', 'win32-msvc%d' % msvs_version)
+])
+
+qt_env.AddMethod(EnableQtModules)
+qt_env.AddMethod(DisableQtModules)
+
+# This is a hack. we should redirect uic to uic4/uic5 and fix the scripts
+def Uicc5(self, *args, **kwargs):
+ return self.Uic5(*args, **kwargs)
+
+qt_env.AddMethod(Uicc5, 'Uic')
+
+# Enable the base libraries.
+qt_env.EnableQt5Modules([], debug = qt_env['CONFIG'] == 'debug')
+
+# Causes too many problems if you don't do this
+qt_env['QT%d_MOCCPPPATH' % qt_env['QT_MAJOR_VERSION']] = '$CPPPATH'
+# Yechhh. Note: the the _VER depends on ms compiler version (and could
+# be dragged from the qmake conf file or preferrably from ms compiler)
+qt_env['QT%d_MOCDEFINES' % qt_env['QT_MAJOR_VERSION']] =\
+ '${_defines(QT5_MOCDEFPREFIX, MOCDEFINES+CPPDEFINES, QT5_MOCDEFSUFFIX, __env__)}'
+qt_env['MOCDEFINES'] = [
+ '_MSC_VER=1800',
+]
+# QTCreator appears to add these automatically. Some of these look moderately
+# dangerous if you're attempting to cross-compile
+# only for qt4?
+if qt_env['QT_MAJOR_VERSION'] <= 4:
+ qt_env.AppendUnique(CPPDEFINES = [
+ 'QT_DLL',
+ 'QT_HAVE_MMX',
+ 'QT_HAVE_3DNOW',
+ 'QT_HAVE_SSE',
+ 'QT_HAVE_MMXEXT',
+ 'QT_HAVE_SSE2',
+ 'QT_THREAD_SUPPORT'
+ ])
+
+# It also adds this to the end of the include path.
+# -I"c:\Apps\Qt\4.8.6\include\ActiveQt"
+
+# Export environment. Rename it first to encourage instant Clone() calls
+Export('env qt_env')
+
+# And away we go
+libs_to_install = env.SConscript('source/SConscript',
+ variant_dir = build_dir,
+ duplicate = 0)
+libs_to_install = sorted(set(filter(lambda x: x is not None,
+ env.Flatten(libs_to_install))))
+# There are some odd implicit dependencies
+if qt_env['QT_MAJOR_VERSION'] > 4:
+ libs_to_install += [
+ 'Multimedia',
+ 'MultimediaWidgets',
+ 'OpenGL',
+ 'Positioning',
+ 'PrintSupport',
+ 'Quick',
+ 'Sensors',
+ 'WebChannel',
+ ]
+
+# Finally, set up rules to install the DLLs.
+
+dll_path = os.path.join('$INSTALL_PATH', 'DLLs')
+
+prefix, suffix, suffix_ = get_qt_lib_info(qt_env)
+
+dlls_to_install = []
+dlls_to_install = [
+ os.path.join(env['QTDIR'], 'bin', prefix + lib + suffix + '.dll')
+ for lib in libs_to_install
+]
+
+if env['CONFIG'] == 'debug':
+ dlls_to_install += [
+ os.path.join(env['QTDIR'], 'bin', prefix + lib + suffix + '.pdb')
+ for lib in libs_to_install
+ ]
+
+# There is something wrong with webkit4 and/or this build as it seems
+# to need to live in the same directory as mod organiser.
+if 'WebKit' in libs_to_install and qt_env['QT_MAJOR_VERSION'] == 4:
+ libname = prefix + 'WebKit' + suffix
+ env.Install(env['INSTALL_PATH'],
+ os.path.join(env['QTDIR'], 'bin', libname + '.dll'))
+ if env['CONFIG'] == 'debug':
+ env.Install(env['INSTALL_PATH'],
+ os.path.join(env['QTDIR'], 'bin', libname + '.pdb'))
+
+if qt_env['QT_MAJOR_VERSION'] > 4:
+ # Guesswork a bit.
+ dlls_to_install += [
+ os.path.join(env['QTDIR'], 'bin', 'icu%s53.dll' % lib)
+ for lib in ('dt','in', 'uc')
+ ]
+
+ platform_dlls = []
+ if env['CONFIG'] == 'debug':
+ platform_dlls += [ 'qwindowsd.dll', 'qwindowsd.pdb' ]
+ else:
+ platform_dlls += [ 'qwindows.dll' ]
+
+ # Note: Appears to work fine in DLLs or at the top level, but I'm all for
+ # keeping the top directory a bit clean
+ env.Install(os.path.join(dll_path, 'platforms'),
+ [ os.path.join(env['QTDIR'], 'plugins', 'platforms', dll)
+ for dll in platform_dlls ])
+
+ image_dlls = []
+ for image in ('dds', 'gif', 'jpeg', 'tga'):
+ if env['CONFIG'] == 'debug':
+ image_dlls += [ 'q' + image + 'd.dll', 'q' + image + 'd.pdb' ]
+ else:
+ image_dlls += [ 'q' + image + '.dll' ]
+ env.Install(os.path.join(dll_path, 'imageformats'),
+ [ os.path.join(env['QTDIR'], 'plugins', 'imageformats', dll)
+ for dll in image_dlls ])
+
+# Build your own?
+dlls_to_install += [
+ os.path.join('tools', 'static_data', 'dlls', '7z.dll')
+]
+
+env.Install(dll_path, dlls_to_install)
+
+# And loot which goes somewhere else (maybe this should be done in loot_cli?)
+env.Install(os.path.join('${INSTALL_PATH}', 'loot'),
+ os.path.join('${LOOTPATH}', 'loot32.dll'))
+
+# also pythondll and zip, boost python dll (all for python proxy!)
+# the dll we can drag from the python install
+# the zip i'm not sure about.