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
4 import SCons.Action
5 import SCons.Builder
6 import SCons.Tool
7 import SCons.Util
8
9 # This is what we search for to find mingw:
10 prefixes = SCons.Util.Split("""
11 mingw32-
12 i386-mingw32msvc-
13 i486-mingw32msvc-
14 i586-mingw32msvc-
15 i686-mingw32msvc-
16 i686-pc-mingw32-
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 \
51 not env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX'):
52
53 # Append an import library to the list of targets.
54 target.append(env.ReplaceIxes(dll,
55 'SHLIBPREFIX', 'SHLIBSUFFIX',
56 'LIBPREFIX', 'LIBSUFFIX'))
57
58 # Append a def file target if there isn't already a def file target
59 # or a def file source. There is no option to disable def file
60 # target emitting, because I can't figure out why someone would ever
61 # want to turn it off.
62 def_source = env.FindIxes(source, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')
63 def_target = env.FindIxes(target, 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX')
64 if not def_source and not def_target:
65 target.append(env.ReplaceIxes(dll,
66 'SHLIBPREFIX', 'SHLIBSUFFIX',
67 'WIN32DEFPREFIX', 'WIN32DEFSUFFIX'))
68
69 return (target, source)
70
71
72 shlib_action = SCons.Action.CommandGenerator(shlib_generator)
73 # in Scons 2.1 the line above needs to be replace with:
74 #shlib_action = SCons.Action.Action(shlib_generator, generator=1)
75
76 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
77
78 res_builder = SCons.Builder.Builder(action=res_action, suffix='.o',
79 source_scanner=SCons.Tool.SourceFileScanner)
80 SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
81
82 def generate(env):
83 mingw_prefix = find(env)
84
85 if mingw_prefix:
86 dir = os.path.dirname(env.WhereIs(mingw_prefix + 'gcc') or SCons.Util.WhereIs(mingw_prefix + 'gcc'))
87
88 # The mingw bin directory must be added to the path:
89 path = env['ENV'].get('PATH', [])
90 if not path:
91 path = []
92 if SCons.Util.is_String(path):
93 path = path.split(os.pathsep)
94
95 env['ENV']['PATH'] = os.pathsep.join([dir] + path)
96
97 # Most of mingw is the same as gcc and friends...
98 gnu_tools = ['gcc', 'g++', 'gnulink', 'ar', 'gas']
99 for tool in gnu_tools:
100 SCons.Tool.Tool(tool)(env)
101
102 #... but a few things differ:
103 env['CC'] = mingw_prefix + 'gcc'
104 env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
105 env['CXX'] = mingw_prefix + 'g++'
106 env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
107 env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -shared')
108 env['SHLINKCOM'] = shlib_action
109 env.Append(SHLIBEMITTER = [shlib_emitter])
110 # This line isn't required and breaks C++ linking
111 #env['LINK'] = mingw_prefix + 'g++'
112 env['AS'] = mingw_prefix + 'as'
113 env['AR'] = mingw_prefix + 'ar'
114 env['RANLIB'] = mingw_prefix + 'ranlib'
115 env['WIN32DEFPREFIX'] = ''
116 env['WIN32DEFSUFFIX'] = '.def'
117 env['SHOBJSUFFIX'] = '.o'
118 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
119
120 env['RC'] = mingw_prefix + 'windres'
121 env['RCFLAGS'] = SCons.Util.CLVar('')
122 env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET)} $)'
123 env['RCINCPREFIX'] = '--include-dir '
124 env['RCINCSUFFIX'] = ''
125 env['RCCOM'] = '$RC $RCINCFLAGS $RCINCPREFIX $SOURCE.dir $RCFLAGS -i $SOURCE -o $TARGET'
126 env['BUILDERS']['RES'] = res_builder
127
128 # Some setting from the platform also have to be overridden:
129 env['OBJPREFIX'] = ''
130 env['OBJSUFFIX'] = '.o'
131 env['LIBPREFIX'] = 'lib'
132 env['LIBSUFFIX'] = '.a'
133 env['SHOBJPREFIX'] = '$OBJPREFIX'
134 env['SHOBJSUFFIX'] = '$OBJSUFFIX'
135 env['PROGPREFIX'] = ''
136 env['PROGSUFFIX'] = '.exe'
137 env['LIBPREFIX'] = ''
138 env['LIBSUFFIX'] = '.lib'
139 env['SHLIBPREFIX'] = ''
140 env['SHLIBSUFFIX'] = '.dll'
141 env['LIBPREFIXES'] = [ '$LIBPREFIX' ]
142 env['LIBSUFFIXES'] = [ '$LIBSUFFIX' ]
143
144 def exists(env):
145 return find(env)
