I wanted to write a scons build system which can build my project on both Windows and POSIX platforms. Since my project ultimately builds Windows executables, it needs some cross-compiling. Since SCons doesn't have built-in support for it, I had to add it myself. For this purpose, I wrote this tool definition for MinGW. It's based on the original MinGW tool definition which comes with SCons 0.96.90. My windres patch is also applied.
On top of what the original tool definition does, this tool searches for MinGW using more than one common prefixes and sets Windows prefixes and suffixes for output files.
To use it, save crossmingw.py in a directory called scons-tools and use:
1 env.Tool('crossmingw', toolpath = ['scons-tools'])
A complete usage example can be found in NSIS CVS repository. The tool is located under SCons/Tools. It's used in SCons/Config/gnu.
crossmingw.py
1 import os
2 import os.path
3 import string
4
5 import SCons.Action
6 import SCons.Builder
7 import SCons.Tool
8 import SCons.Util
9
10 # This is what we search for to find mingw:
11 prefixes = SCons.Util.Split("""
12 mingw32-
13 i386-mingw32msvc-
14 i486-mingw32msvc-
15 i586-mingw32msvc-
16 i686-mingw32msvc-
17 """)
18
19 def find(env):
20 for prefix in prefixes:
21 # First search in the SCons path and then the OS path:
22 if env.WhereIs(prefix + 'gcc') or SCons.Util.WhereIs(prefix + 'gcc'):
23 return prefix
24
25 return ''
26
27 def shlib_generator(target, source, env, for_signature):
28 cmd = SCons.Util.CLVar(['$SHLINK', '$SHLINKFLAGS'])
29
30 dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
31 if dll: cmd.extend(['-o', dll])
32
33 cmd.extend(['$SOURCES', '$_LIBDIRFLAGS', '$_LIBFLAGS'])
34
35 implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
36 if implib: cmd.append('-Wl,--out-implib,'+implib.get_string(for_signature))
37
38 def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')
39 if def_target: cmd.append('-Wl,--output-def,'+def_target.get_string(for_signature))
40
41 return [cmd]
42
43 def shlib_emitter(target, source, env):
44 dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
45 no_import_lib = env.get('no_import_lib', 0)
46
47 if not dll:
48 raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")
49
50 if not no_import_lib and \
not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'):
51
52 # Append an import library to the list of targets.
53 target.append(env.ReplaceIxes(dll,
54 'SHLIBPREFIX', 'SHLIBSUFFIX',
55 'LIBPREFIX', 'LIBSUFFIX'))
56
57 # Append a def file target if there isn't already a def file target
58 # or a def file source. There is no option to disable def file
59 # target emitting, because I can't figure out why someone would ever
60 # want to turn it off.
61 def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')
62 def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')
63 if not def_source and not def_target:
64 target.append(env.ReplaceIxes(dll,
65 'SHLIBPREFIX', 'SHLIBSUFFIX',
66 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX'))
67
68 return (target, source)
69
70
71 shlib_action = SCons.Action.CommandGenerator(shlib_generator)
72
73 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
74
75 res_builder = SCons.Builder.Builder(action=res_action, suffix='.o',
76 source_scanner=SCons.Tool.SourceFileScanner)
77 SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
78
79 def generate(env):
80 mingw_prefix = find(env)
81
82 if mingw_prefix:
83 dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc'))
84
85 # The mingw bin directory must be added to the path:
86 path = env['ENV'].get('PATH', [])
87 if not path:
88 path = []
89 if SCons.Util.is_String(path):
90 path = string.split(path, os.pathsep)
91
92 env['ENV']['PATH'] = string.join([dir] + path, os.pathsep)
93
94 # Most of mingw is the same as gcc and friends...
95 gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas']
96 for tool in gnu_tools:
97 SCons.Tool.Tool(tool)(env)
98
99 #... but a few things differ:
100 env['CC'] = mingw_prefix + 'gcc'
101 env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
102 env['CXX'] = mingw_prefix + 'g++'
103 env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
104 env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
105 env['SHLINKCOM'] = shlib_action
106 env.Append(SHLIBEMITTER = [shlib_emitter])
107 # This line isn't required and breaks C++ linking
108 #env['LINK'] = mingw_prefix + 'g++'
109 env['AS'] = mingw_prefix + 'as'
110 env['AR'] = mingw_prefix + 'ar'
111 env['RANLIB'] = mingw_prefix + 'ranlib'
112 env['WIN32DEFPREFIX'] = ''
113 env['WIN32DEFSUFFIX'] = '.def'
114 env['SHOBJSUFFIX'] = '.o'
115 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
116
117 env['RC'] = mingw_prefix + 'windres'
118 env['RCFLAGS'] = SCons.Util.CLVar('')
119 env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)'
120 env['RCINCPREFIX'] = '--include-dir '
121 env['RCINCSUFFIX'] = ''
122 env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET'
123 env['BUILDERS']['RES'] = res_builder
124
125 # Some setting from the platform also have to be overridden:
126 env['OBJPREFIX'] = ''
127 env['OBJSUFFIX'] = '.o'
128 env['LIBPREFIX'] = 'lib'
129 env['LIBSUFFIX'] = '.a'
130 env['SHOBJPREFIX'] = '$OBJPREFIX'
131 env['SHOBJSUFFIX'] = '$OBJSUFFIX'
132 env['PROGPREFIX'] = ''
133 env['PROGSUFFIX'] = '.exe'
134 env['LIBPREFIX'] = ''
135 env['LIBSUFFIX'] = '.lib'
136 env['SHLIBPREFIX'] = ''
137 env['SHLIBSUFFIX'] = '.dll'
138 env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
139 env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ]
140
141 def exists(env):
142 return find(env)
