Small docstring update for vars.Add
[scons.git] / test / Libs / SharedLibraryIxes.py
blob55fcd27ee27317564da4e3411139ac6b53165eea
1 #!/usr/bin/env python
3 # MIT License
5 # Copyright The SCons Foundation
7 # Permission is hereby granted, free of charge, to any person obtaining
8 # a copy of this software and associated documentation files (the
9 # "Software"), to deal in the Software without restriction, including
10 # without limitation the rights to use, copy, modify, merge, publish,
11 # distribute, sublicense, and/or sell copies of the Software, and to
12 # permit persons to whom the Software is furnished to do so, subject to
13 # the following conditions:
15 # The above copyright notice and this permission notice shall be included
16 # in all copies or substantial portions of the Software.
18 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
19 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
20 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 """
27 Test that we can build shared libraries and link against shared
28 libraries that have non-standard library prefixes and suffixes.
29 """
31 import re
32 import sys
33 import TestSCons
35 test = TestSCons.TestSCons()
37 test.write('SConstruct', """
38 import sys
39 isCygwin = sys.platform == 'cygwin'
40 isWindows = sys.platform == 'win32'
41 isMingw = False
42 if isWindows:
43 import SCons.Tool.MSCommon as msc
44 if not msc.msvc_exists():
45 # We can't seem to find any MSVC version, so we assume
46 # that MinGW is installed instead. Accordingly, we use the
47 # standard gcc/g++ conventions for lib prefixes and suffixes
48 # in the following...
49 isWindows = False
50 isMingw = True
52 DefaultEnvironment(tools=[]) # test speedup
53 env = Environment()
55 # Make sure that the shared library can be located at runtime.
56 env.Append(RPATH=['.'])
57 env.Append(LIBPATH=['.'])
59 # We first bake the LIBSUFFIXES, so that it will not change as a
60 # side-effect of changing SHLIBSUFFIX.
61 env['LIBSUFFIXES'] = list(map( env.subst, env.get('LIBSUFFIXES', [])))
63 weird_prefixes = ['libXX', 'libYY']
65 if isWindows:
66 weird_suffixes = ['.xxx', '.yyy', '.xxx.dll', '.yyy.dll']
67 env.Append(CCFLAGS = '/MD')
68 elif env['PLATFORM'] == 'darwin':
69 weird_suffixes = ['.xxx.dylib', '.yyy.dylib']
70 else:
71 weird_suffixes = ['.xxx.so', '.yyy.so']
73 shlibprefix = env.subst('$SHLIBPREFIX')
74 shlibsuffix = env.subst('$SHLIBSUFFIX')
76 progprefix = env.subst('$PROGPREFIX')
77 progsuffix = env.subst('$PROGSUFFIX')
79 goo_obj = env.SharedObject(source='goo.c')
80 foo_obj = env.SharedObject(source='foo.c')
81 prog_obj = env.SharedObject(source='prog.c')
84 # The following functions define all the different ways that one can
85 # use to link against a shared library.
87 def nodeInSrc(source, lib, libname):
88 return (source+lib, '')
90 def pathInSrc(source, lib, libname):
91 return (source+list(map(str,lib)), '')
93 def nodeInLib(source, lib, libname):
94 return (source, lib)
96 def pathInLib(source, lib, libname):
97 return (source, list(map(str,lib)))
99 def nameInLib(source, lib, libname):
100 # NOTE: libname must contain both the proper prefix and suffix.
102 # When using non-standard prefixes and suffixes, one has to
103 # provide the full name of the library since scons can not know
104 # which of the non-standard extension to use.
106 # Note that this is not necessarily SHLIBPREFIX and
107 # SHLIBSUFFIX. These are the ixes of the target library, not the
108 # ixes of the library that we are linking against.
109 return (source, libname)
111 libmethods = [nodeInSrc, pathInSrc, nodeInLib, pathInLib]
112 # We skip the nameInLib test for MinGW and Cygwin...they would fail, due to
113 # the Tool's internal naming conventions
114 if not isMingw and not isCygwin:
115 libmethods.extend([nameInLib])
117 def buildAndlinkAgainst(builder, target, source, method, lib, libname, **kw):
118 '''Build a target using a given builder while linking against a given
119 library using a specified method for linking against the library.'''
121 # On Windows, we have to link against the .lib file.
122 if isWindows:
123 for l in lib:
124 if str(l)[-4:] == '.lib':
125 lib = [l]
126 break
127 # If we use MinGW or Cygwin and create a SharedLibrary, we get two targets: a DLL,
128 # and the import lib created by the "--out-implib" parameter. We always
129 # want to link against the second one, in order to prevent naming issues
130 # for the linker command line...
131 if (isMingw or isCygwin) and len(lib) > 1:
132 lib = lib[1:]
134 # Apply the naming method to be tested and call the specified Builder.
135 (source, LIBS) = method(source, lib, libname)
136 #build = builder(target=target, source=source, LIBS=LIBS, **kw)
137 kw = kw.copy()
138 kw['target'] = target
139 kw['source'] = source
140 kw['LIBS'] = LIBS
141 build = builder(**kw)
143 # Check that the build target depends on at least one of the
144 # library target.
145 found_dep = False
146 children = build[0].children()
147 for l in lib:
148 if l in children:
149 found_dep = True
150 break;
151 assert found_dep, \
152 "One of %s not found in %s, method=%s, libname=%s, shlibsuffix=%s" % \
153 (list(map(str,lib)), list(map(str, build[0].children())), method.__name__, libname, shlibsuffix)
154 return build
156 def prog(i,
157 goomethod, goolibprefix, goolibsuffix,
158 foomethod, foolibprefix, foolibsuffix):
159 '''Build a program
161 The program links against a shared library foo which itself links
162 against a shared library goo. The libraries foo and goo can use
163 arbitrary library prefixes and suffixes.'''
165 goo_name = goolibprefix+'goo'+str(i)+goolibsuffix
166 foo_name = foolibprefix+'foo'+str(i)+foolibsuffix
167 prog_name = progprefix+'prog'+str(i)+progsuffix
169 print('Prog: %d, %s, %s, %s' % (i, goo_name, foo_name, prog_name))
171 # On Windows, we have to link against the .lib file.
172 if isWindows:
173 goo_libname = goolibprefix+'goo'+str(i)+'.lib'
174 foo_libname = foolibprefix+'foo'+str(i)+'.lib'
175 else:
176 goo_libname = goo_name
177 foo_libname = foo_name
179 goo_lib = env.SharedLibrary(
180 goo_name, goo_obj, SHLIBSUFFIX=goolibsuffix)
181 foo_lib = buildAndlinkAgainst(
182 env.SharedLibrary, foo_name, foo_obj,
183 goomethod, goo_lib, goo_libname, SHLIBSUFFIX=foolibsuffix)
184 prog = buildAndlinkAgainst(env.Program, prog_name, prog_obj,
185 foomethod, foo_lib, foo_libname)
189 # Create the list of all possible permutations to test.
191 i = 0
192 tests = []
193 prefixes = [shlibprefix] + weird_prefixes
194 suffixes = [shlibsuffix] + weird_suffixes
195 for foolibprefix in prefixes:
196 for foolibsuffix in suffixes:
197 for foomethod in libmethods:
198 for goolibprefix in prefixes:
199 for goolibsuffix in suffixes:
200 for goomethod in libmethods:
201 tests.append(
203 goomethod, goolibprefix, goolibsuffix,
204 foomethod, foolibprefix, foolibsuffix))
205 i = i + 1
208 # Pseudo-randomly choose 200 tests to run out of the possible
209 # tests. (Testing every possible permutation would take too long.)
211 import random
212 random.seed(123456)
213 try:
214 random.shuffle(tests)
215 except AttributeError:
216 pass
218 for i in range(200):
219 prog(*tests[i])
221 """)
223 test.write('goo.c', r"""
224 #include <stdio.h>
226 #ifdef _WIN32
227 #define EXPORT __declspec( dllexport )
228 #else
229 #define EXPORT
230 #endif
232 EXPORT void
233 goo(void)
235 printf("goo.c\n");
237 """)
239 test.write('foo.c', r"""
240 #include <stdio.h>
242 void goo(void);
244 #ifdef _WIN32
245 #define EXPORT __declspec( dllexport )
246 #else
247 #define EXPORT
248 #endif
250 EXPORT void
251 foo(void)
253 goo();
254 printf("foo.c\n");
256 """)
258 test.write('prog.c', r"""
259 #include <stdio.h>
261 void foo(void);
264 main(int argc, char *argv[])
266 argv[argc++] = "--";
267 foo();
268 printf("prog.c\n");
269 return 0;
271 """)
273 test.run(arguments = '.',
274 stderr=TestSCons.noisy_ar,
275 match=TestSCons.match_re_dotall)
277 tests = re.findall(r'Prog: (\d+), (\S+), (\S+), (\S+)', test.stdout())
278 expected = "goo.c\nfoo.c\nprog.c\n"
280 for t in tests:
281 if sys.platform != 'cygwin':
282 test.must_exist(t[1])
283 test.must_exist(t[2])
284 else:
285 # Cygwin turns libFoo.xxx into cygFoo.xxx
286 for f in t[1:2]:
287 test.must_exist(re.sub('^lib', 'cyg', f))
289 test.must_exist(t[3])
290 test.run(program = test.workpath(t[3]), stdout=expected)
292 test.pass_test()
294 # Local Variables:
295 # tab-width:4
296 # indent-tabs-mode:nil
297 # End:
298 # vim: set expandtab tabstop=4 shiftwidth=4: