Scons
From WxWiki
SCons is a software construction tool that aims to replace make and autotools, which have been coined to be somewhat of a problem for both end users and developers. SCons is written in Python and is a very easy tool to learn, specially compared to GNU Make.
The latest online manual can be found here.
The basic idea: an example on Windows
SCons has a function ParseConfig for tools like pkg-config and Wx-Config. On windows, one can use Wx-config_Windows_port (tested with revision 44 and MinGW).
A simple SConstruct looks like:
env = Environment() env.ParseConfig('wx-config --cflags --libs') env.StaticLibrary(target = 'my_lib', source = ['foo.cpp']) env.Program(target = 'my_app', source = ['bar.cpp'], LIBS = env['LIBS'] + ['my_lib'], LIBPATH = env['LIBPATH'] + ['.'])
Note that to link my_app with my_lib, I appended my_lib to the LIBS in the environment, instead of just using LIBS=['my_lib'].
Go on to the next section to see how we can adapt this example to work on *nix platforms.
The basic idea on Linux and Mac OS X
The proposed SConstruct file from the last example runs well under Linux for scons v0.98, however the last lines fail on Mac OS X. We will simply remove them, since they are not needed under Linux either if you are not building and linking against you own software library (which is probably the case for more than 90% of the projects out there).
env = Environment() env.ParseConfig('wx-config --cxxflags --libs') env.Program(target = 'my_app', source = ['bar.cpp'])
Please note we have also changed the --cflags parameter to --cxxflags, although they should make no difference under Linux.
More robust Wx-Config usage
Wx-Config enables cross-platform building with relative ease. There are some caveats though with different operating systems and versions of wxWidgets. The following SCons python module was tested for MSVC on windows and gcc on Linux and MacOSX and wxWidgets versions 2.4 and 2.6.
# # SCons python library: wxWidgets configuration # # \author Willem van Engen <wvengen@stack.nl> # \date created 2006/10/16 # \version v 1.3.2.2 2006/11/24 14:26:47 willem Exp # import os, os.path import re import sys import glob # Runs a system command and returns return value and output def SystemBacktick(program): # Run and return result pipe = os.popen(program + ' 2>&1') # TODO: properly redirect stderr to stdout output = pipe.read() retcode = pipe.close() or 0 return [retcode, output] def SystemWXConfig(env, args): if sys.platform == 'win32': return SystemBacktick(env['wxconfig'] + ' --wxcfg=' + env['ENV']['WXCFG'] + ' ' + args + env['wxconfig_postargs']) else: return SystemBacktick(env['wxconfig'] + ' ' + args + env['wxconfig_postargs']) # Check version of wx-config # It succeeds with a warning if version check failed. def CheckWXConfigVersion(context, version): releaseversion = SystemWXConfig(context.env, '--release')[1] try: if float(version) > float(releaseversion.strip()): return False except (ValueError, TypeError): context.Message('version check failed, but ok... ') return True return True # Check if requested components exist in wxWidgets def CheckWXConfigComponents(context, libraries): # set components, method depending on wxWidgets version if CheckWXConfigVersion(context, '2.6'): context.env['wxconfig_postargs'] += ' ' + ','.join(libraries) return SystemWXConfig(context.env, '--libs ')[0] == 0 # version 2.4 or below, only gl is an optional component with special flag if 'gl' in libraries: context.env['wxconfig'] += ' --gl-libs' return SystemWXConfig(context.env, '--libs')[0] == 0 # no optional components can be checked, it should be allright return True # Find wx-config with suitable settings. It tries to use a debug configuration if requested, # but reverts to release or default when that fails. def CheckWXConfigWin(context, version, debug): context.Message('Checking for wxWidgets >= %s... ' % version) # Try to find it in path wx_prog = context.env.WhereIs(context.env['wxconfig']) if wx_prog == None: # You could supply wx-config.exe as a fallback option. #wx_prog = os.path.join('scons', 'wx-config') context.Message('wx-config not found...') return False context.env['wxconfig'] = wx_prog # Some environment variables are required for wx-config to work, check them. if 'WXWIN' not in context.env['ENV']: # TODO: maybe try some default location like "C:\Program Files\wxWidgets-*" or registry context.Message('please set WXWIN in environment... ') return False # If there's no WXCFG, either there is only one config or we try to find out. if 'WXCFG' not in context.env['ENV']: # TODO: try to find one in some sensible order from alternatives # Current guess is: visual studio static, non-unicode # Try debugging version first if requested, else fallback to release if debug: context.env['ENV']['WXCFG'] = 'vc_lib\mswd' if SystemWXConfig(context.env, '--libs')[0] == 0: return CheckWXConfigVersion(context, version) # Non-debug context.env['ENV']['WXCFG'] = 'vc_lib\msw' if SystemWXConfig(context.env, '--libs')[0] == 0: # this is the only configuration: use it return CheckWXConfigVersion(context, version) context.Message('please set WXCFG in environment... ') return False # WXCFG is in environment, nice. # Try a debugging version if requested: postfix WXCFG with 'd' if debug: oldwxcfg = context.env['ENV']['WXCFG'] context.env['ENV']['WXCFG'] += 'd' if SystemWXConfig(context.env, '--libs')[0] == 0: return CheckWXConfigVersion(context, version) # Failed: revert to plain WXCFG, use existing environment context.env['ENV']['WXCFG'] = oldwxcfg if SystemWXConfig(context.env, '--libs')[0] == 0: return CheckWXConfigVersion(context, version) # Everything failed ... return False def CheckWXConfigPosixFind(context, debug): # Find a wx-config compatible pathname # wx*-config --> wx*-[0-9]+\.[0-9]+-config / wx<platform>-<version>-config dbglist = [] rellist = [] cfgre = re.compile('\/wx(\w+?)(d?)-(\d+\.\d+)-config') for dir in context.env['ENV']['PATH'].split(':'): for cfgprog in glob.glob(os.path.join(dir, 'wx*-config')): m = cfgre.search(cfgprog) if m and m.group(1) != 'base': # add to either debug or release list if m.group(2) == '': rellist.append(cfgprog) else: dbglist.append(cfgprog) # TODO: sort on version # Now pick the right one if debug and len(dbglist) > 0: return dbglist[0] if len(rellist) > 0: return rellist[0] # Too bad return False def CheckWXConfigPosix(context, version, debug): # TODO: try several wx-config names context.Message('Checking for wxWidgets >= %s... ' % version) # If supplied wx-config doesn't work, try to find another one if SystemWXConfig(context.env, '--libs')[0] != 0: wx_prog = CheckWXConfigPosixFind(context, debug) if not wx_prog: context.Message('not found... ') return False context.env['wxconfig'] = wx_prog if not debug: return CheckWXConfigVersion(context, version) # use `wx-config --debug` if it's in its help helpoutput = SystemWXConfig(context.env, '--help')[1] if '--debug' in helpoutput: context.Message('--debug') if SystemWXConfig(context.env, '--debug --libs')[0] == 0: context.env['wxconfig'] = context.env['wxconfig'] + ' --debug' return CheckWXConfigVersion(context, version) # If it's plain wx-config, we may need to look for another one for debugging if context.env['wxconfig'] == 'wx-config': wx_prog = CheckWXConfigPosixFind(context, debug) if wx_prog: context.env['wxconfig'] = wx_prog # TODO: possibly warning message when using release instead of debug return CheckWXConfigVersion(context, version) def CheckWXConfig(context, version, components, debug = False): context.env['wxconfig_postargs'] = '' build_platform=context.env['build_platform'] target_platform=context.env['target_platform'] # Get wx-config invocation and check version if build_platform == 'win32' and target_platform == 'win32': res = CheckWXConfigWin(context, version, debug) else: res = CheckWXConfigPosix(context, version, debug) # Make sure we have the required libraries if res: res = CheckWXConfigComponents(context, components) if not res: context.Message('not all components found [' + ','.join(components) + ']... ') context.Result(res) return res def ParseWXConfig(env): build_platform = env['build_platform'] target_platform = env['target_platform'] # Windows doesn't work with ParseConfig (yet) :( if build_platform == 'win32' and target_platform == 'win32': # Use wx-config, yay! # ParseConfig() on windows is broken, so the following is done instead cflags = SystemWXConfig(env, '--cxxflags')[1] env.AppendUnique(CPPFLAGS = cflags.strip().split(' ')) libs = SystemWXConfig(env, '--libs')[1] env.AppendUnique(LINKFLAGS = libs.strip().split(' ')) elif target_platform == 'darwin': # MacOSX doesn't handle '-framework foobar' correctly, do that separately. env.ParseConfig(env['wxconfig'] + ' --cxxflags' + env['wxconfig_postargs']) env.AppendUnique(LINKFLAGS=SystemWXConfig(env, '--libs' + env['wxconfig_postargs'])[1]) else: # Here ParseConfig should really work env.ParseConfig(env['wxconfig'] + ' --cxxflags --libs' + env['wxconfig_postargs'])
To use this module, include something like this in your main SConscript/SConstruct:
### import the module sys.path.append('my_scons_script_directory') from wxconfig import * ### setup variables needed, mainly to support cross-compilation env['build_platform'] = env['PLATFORM'] env['target_platform'] = env['PLATFORM'] # it is recommended to get these from SCons parameters debug = False env['wxconfig'] = 'wx-config' ### detect wxWidgets conf = Configure(env, custom_tests = {'CheckWXConfig' : CheckWXConfig }) # CheckWXConfig(version, componentlist, debug) # version: string with minimum version ('x.y') # componentlist: list of components needed. This was introduced with # wxWidgets 2.6 if I'm correct. You'll usually need # ['adv', 'core', 'base'], in this order. If you use # wxGLCanvas, prepend 'gl' like below. if not conf.CheckWXConfig('2.4', ['gl', 'adv', 'core', 'base'], debug): print 'wxWidgets library not found.' Exit(1) env = conf.Finish() ### target ParseWXConfig(env) env.Program(target = 'my_app', source = ['bar.cpp'])