]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #!/usr/bin/python |
2 | ||
3 | # Copyright 2002, 2003 Dave Abrahams | |
4 | # Copyright 2002, 2003, 2004, 2005 Vladimir Prus | |
5 | # Copyright 2012 Jurko Gospodnetic | |
6 | # Distributed under the Boost Software License, Version 1.0. | |
7 | # (See accompanying file LICENSE_1_0.txt or copy at | |
8 | # http://www.boost.org/LICENSE_1_0.txt) | |
9 | ||
10 | import BoostBuild | |
11 | import re | |
12 | ||
13 | ||
14 | def test_basic(): | |
15 | t = BoostBuild.Tester(pass_d0=False) | |
16 | __write_appender(t, "appender.jam") | |
17 | t.write("a.cpp", "") | |
18 | t.write("b.cxx", "") | |
19 | t.write("c.tui", "") | |
20 | t.write("d.wd", "") | |
21 | t.write("e.cpp", "") | |
22 | t.write("x.l", "") | |
23 | t.write("y.x_pro", "") | |
24 | t.write("z.cpp", "") | |
25 | t.write("lib/c.cpp", "int bar() { return 0; }\n") | |
26 | t.write("lib/jamfile.jam", "my-lib auxilliary : c.cpp ;") | |
27 | t.write("jamroot.jam", | |
28 | r"""import appender ; | |
29 | ||
30 | import "class" : new ; | |
31 | import generators ; | |
32 | import type ; | |
33 | ||
34 | ||
35 | ################################################################################ | |
36 | # | |
37 | # We use our own custom EXE, LIB & OBJ target generators as using the regular | |
38 | # ones would force us to have to deal with different compiler/linker specific | |
39 | # 'features' that really have nothing to do with this test. For example, IBM XL | |
40 | # C/C++ for AIX, V12.1 (Version: 12.01.0000.0000) compiler exits with a non-zero | |
41 | # exit code and thus fails our build when run with a source file using an | |
42 | # unknown suffix like '.marked_cpp'. | |
43 | # | |
44 | ################################################################################ | |
45 | ||
46 | type.register MY_EXE : my_exe ; | |
47 | type.register MY_LIB : my_lib ; | |
48 | type.register MY_OBJ : my_obj ; | |
49 | ||
50 | appender.register compile-c : C : MY_OBJ ; | |
51 | appender.register compile-cpp : CPP : MY_OBJ ; | |
52 | appender.register link-lib composing : MY_OBJ : MY_LIB ; | |
53 | appender.register link-exe composing : MY_OBJ MY_LIB : MY_EXE ; | |
54 | ||
55 | ||
56 | ################################################################################ | |
57 | # | |
58 | # LEX --> C | |
59 | # | |
60 | ################################################################################ | |
61 | ||
62 | type.register LEX : l ; | |
63 | ||
64 | appender.register lex-to-c : LEX : C ; | |
65 | ||
66 | ||
67 | ################################################################################ | |
68 | # | |
69 | # /--> tUI_H --\ | |
70 | # tUI --< >--> CPP | |
71 | # \------------/ | |
72 | # | |
73 | ################################################################################ | |
74 | ||
75 | type.register tUI : tui ; | |
76 | type.register tUI_H : tui_h ; | |
77 | ||
78 | appender.register ui-to-cpp : tUI tUI_H : CPP ; | |
79 | appender.register ui-to-h : tUI : tUI_H ; | |
80 | ||
81 | ||
82 | ################################################################################ | |
83 | # | |
84 | # /--> X1 --\ | |
85 | # X_PRO --< >--> CPP | |
86 | # \--> X2 --/ | |
87 | # | |
88 | ################################################################################ | |
89 | ||
90 | type.register X1 : x1 ; | |
91 | type.register X2 : x2 ; | |
92 | type.register X_PRO : x_pro ; | |
93 | ||
94 | appender.register x1-x2-to-cpp : X1 X2 : CPP ; | |
95 | appender.register x-pro-to-x1-x2 : X_PRO : X1 X2 ; | |
96 | ||
97 | ||
98 | ################################################################################ | |
99 | # | |
100 | # When the main target type is NM_EXE, build OBJ from CPP-MARKED and not from | |
101 | # anything else, e.g. directly from CPP. | |
102 | # | |
103 | ################################################################################ | |
104 | ||
105 | type.register CPP_MARKED : marked_cpp : CPP ; | |
106 | type.register POSITIONS : positions ; | |
107 | type.register NM.TARGET.CPP : target_cpp : CPP ; | |
108 | type.register NM_EXE : : MY_EXE ; | |
109 | ||
110 | appender.register marked-to-target-cpp : CPP_MARKED : NM.TARGET.CPP ; | |
111 | appender.register cpp-to-marked-positions : CPP : CPP_MARKED POSITIONS ; | |
112 | ||
113 | class nm::target::cpp-obj-generator : generator | |
114 | { | |
115 | rule __init__ ( id ) | |
116 | { | |
117 | generator.__init__ $(id) : NM.TARGET.CPP : MY_OBJ ; | |
118 | generator.set-rule-name appender.appender ; | |
119 | } | |
120 | ||
121 | rule requirements ( ) | |
122 | { | |
123 | return <main-target-type>NM_EXE ; | |
124 | } | |
125 | ||
126 | rule run ( project name ? : properties * : source : multiple ? ) | |
127 | { | |
128 | if [ $(source).type ] = CPP | |
129 | { | |
130 | local converted = [ generators.construct $(project) : NM.TARGET.CPP | |
131 | : $(properties) : $(source) ] ; | |
132 | if $(converted) | |
133 | { | |
134 | return [ generators.construct $(project) : MY_OBJ : | |
135 | $(properties) : $(converted[2]) ] ; | |
136 | } | |
137 | } | |
138 | } | |
139 | } | |
140 | generators.register [ new nm::target::cpp-obj-generator target-obj ] ; | |
141 | generators.override target-obj : all ; | |
142 | ||
143 | ||
144 | ################################################################################ | |
145 | # | |
146 | # A more complex test case scenario with the following generators: | |
147 | # 1. WHL --> CPP, WHL_LR0, H, H(%_symbols) | |
148 | # 2. DLP --> CPP | |
149 | # 3. WD --> WHL(%_parser) DLP(%_lexer) | |
150 | # 4. A custom generator of higher priority than generators 1. & 2. that helps | |
151 | # disambiguate between them when generating CPP files from WHL and DLP | |
152 | # sources. | |
153 | # | |
154 | ################################################################################ | |
155 | ||
156 | type.register WHL : whl ; | |
157 | type.register DLP : dlp ; | |
158 | type.register WHL_LR0 : lr0 ; | |
159 | type.register WD : wd ; | |
160 | ||
161 | local whale-generator-id = [ appender.register whale : WHL : CPP WHL_LR0 H | |
162 | H(%_symbols) ] ; | |
163 | local dolphin-generator-id = [ appender.register dolphin : DLP : CPP ] ; | |
164 | appender.register wd : WD : WHL(%_parser) DLP(%_lexer) ; | |
165 | ||
166 | class wd-to-cpp : generator | |
167 | { | |
168 | rule __init__ ( id : sources * : targets * ) | |
169 | { | |
170 | generator.__init__ $(id) : $(sources) : $(targets) ; | |
171 | } | |
172 | ||
173 | rule run ( project name ? : property-set : source ) | |
174 | { | |
175 | local new-sources = $(source) ; | |
176 | if ! [ $(source).type ] in WHL DLP | |
177 | { | |
178 | local r1 = [ generators.construct $(project) $(name) : WHL : | |
179 | $(property-set) : $(source) ] ; | |
180 | local r2 = [ generators.construct $(project) $(name) : DLP : | |
181 | $(property-set) : $(source) ] ; | |
182 | new-sources = [ sequence.unique $(r1[2-]) $(r2[2-]) ] ; | |
183 | } | |
184 | ||
185 | local result ; | |
186 | for local i in $(new-sources) | |
187 | { | |
188 | local t = [ generators.construct $(project) $(name) : CPP : | |
189 | $(property-set) : $(i) ] ; | |
190 | result += $(t[2-]) ; | |
191 | } | |
192 | return $(result) ; | |
193 | } | |
194 | } | |
195 | generators.override $(__name__).wd-to-cpp : $(whale-generator-id) ; | |
196 | generators.override $(__name__).wd-to-cpp : $(dolphin-generator-id) ; | |
197 | generators.register [ new wd-to-cpp $(__name__).wd-to-cpp : : CPP ] ; | |
198 | ||
199 | ||
200 | ################################################################################ | |
201 | # | |
202 | # Declare build targets. | |
203 | # | |
204 | ################################################################################ | |
205 | ||
206 | # This should not cause two CPP --> MY_OBJ constructions for a.cpp or b.cpp. | |
207 | my-exe a : a.cpp b.cxx obj_1 obj_2 c.tui d.wd x.l y.x_pro lib//auxilliary ; | |
208 | my-exe f : a.cpp b.cxx obj_1 obj_2 lib//auxilliary ; | |
209 | ||
210 | # This should cause two CPP --> MY_OBJ constructions for z.cpp. | |
211 | my-obj obj_1 : z.cpp ; | |
212 | my-obj obj_2 : z.cpp ; | |
213 | ||
214 | nm-exe e : e.cpp ; | |
215 | """) | |
216 | ||
217 | t.run_build_system() | |
218 | t.expect_addition("bin/$toolset/debug/" * BoostBuild.List("a.my_exe " | |
219 | "a.my_obj b.my_obj c.tui_h c.cpp c.my_obj d_parser.whl d_lexer.dlp " | |
220 | "d_parser.cpp d_lexer.cpp d_lexer.my_obj d_parser.lr0 d_parser.h " | |
221 | "d_parser.my_obj d_parser_symbols.h x.c x.my_obj y.x1 y.x2 y.cpp " | |
222 | "y.my_obj e.marked_cpp e.positions e.target_cpp e.my_obj e.my_exe " | |
223 | "f.my_exe obj_1.my_obj obj_2.my_obj")) | |
224 | t.expect_addition("lib/bin/$toolset/debug/" * BoostBuild.List("c.my_obj " | |
225 | "auxilliary.my_lib")) | |
226 | t.expect_nothing_more() | |
227 | ||
228 | folder = "bin/$toolset/debug" | |
229 | t.expect_content_lines("%s/obj_1.my_obj" % folder, " Sources: 'z.cpp'") | |
230 | t.expect_content_lines("%s/obj_2.my_obj" % folder, " Sources: 'z.cpp'") | |
231 | t.expect_content_lines("%s/a.my_obj" % folder, " Sources: 'a.cpp'") | |
232 | ||
233 | lines = t.stdout().splitlines() | |
234 | source_lines = [x for x in lines if re.match("^ Sources: '", x)] | |
235 | if not __match_count_is(source_lines, "'z.cpp'", 2): | |
236 | BoostBuild.annotation("failure", "z.cpp must be compiled exactly " | |
237 | "twice.") | |
238 | t.fail_test(1) | |
239 | if not __match_count_is(source_lines, "'a.cpp'", 1): | |
240 | BoostBuild.annotation("failure", "a.cpp must be compiled exactly " | |
241 | "once.") | |
242 | t.fail_test(1) | |
243 | t.cleanup() | |
244 | ||
245 | ||
246 | def test_generated_target_names(): | |
247 | """ | |
248 | Test generator generated target names. Unless given explicitly, target | |
249 | names should be determined based on their specified source names. All | |
250 | sources for generating a target need to have matching names in order for | |
251 | Boost Build to be able to implicitly determine the target's name. | |
252 | ||
253 | We use the following target generation structure with differently named | |
254 | BBX targets: | |
255 | /---> BB1 ---\ | |
256 | AAA --<----> BB2 ---->--> CCC --(composing)--> DDD | |
257 | \---> BB3 ---/ | |
258 | ||
259 | The extra generator at the end is needed because generating a top-level | |
260 | CCC target directly would requires us to explicitly specify a name for it. | |
261 | The extra generator needs to be composing in order not to explicitly | |
262 | request a specific name for its CCC source target based on its own target | |
263 | name. | |
264 | ||
265 | We also check for a regression where only the first two sources were | |
266 | checked to see if their names match. Note that we need to try out all file | |
267 | renaming combinations as we do not know what ordering Boost Build is going | |
268 | to use when passing in those files as generator sources. | |
269 | ||
270 | """ | |
271 | jamfile_template = """\ | |
272 | import type ; | |
273 | type.register AAA : _a ; | |
274 | type.register BB1 : _b1 ; | |
275 | type.register BB2 : _b2 ; | |
276 | type.register BB3 : _b3 ; | |
277 | type.register CCC : _c ; | |
278 | type.register DDD : _d ; | |
279 | ||
280 | import appender ; | |
281 | appender.register aaa-to-bbX : AAA : BB1%s BB2%s BB3%s ; | |
282 | appender.register bbX-to-ccc : BB1 BB2 BB3 : CCC ; | |
283 | appender.register ccc-to-ddd composing : CCC : DDD ; | |
284 | ||
285 | ddd _xxx : _xxx._a ; | |
286 | """ | |
287 | ||
288 | t = BoostBuild.Tester(pass_d0=False) | |
289 | __write_appender(t, "appender.jam") | |
290 | t.write("_xxx._a", "") | |
291 | ||
292 | def test_one(t, rename1, rename2, rename3, status): | |
293 | def f(rename): | |
294 | if rename: return "(%_x)" | |
295 | return "" | |
296 | ||
297 | jamfile = jamfile_template % (f(rename1), f(rename2), f(rename3)) | |
298 | t.write("jamroot.jam", jamfile, wait=False) | |
299 | ||
300 | # Remove any preexisting targets left over from a previous test run | |
301 | # so we do not have to be careful about tracking which files have been | |
302 | # newly added and which preexisting ones have only been modified. | |
303 | t.rm("bin") | |
304 | ||
305 | t.run_build_system(status=status) | |
306 | ||
307 | if status: | |
308 | t.expect_output_lines("*.bbX-to-ccc: source targets have " | |
309 | "different names: cannot determine target name") | |
310 | else: | |
311 | def suffix(rename): | |
312 | if rename: return "_x" | |
313 | return "" | |
314 | name = "bin/$toolset/debug/_xxx" | |
315 | e = t.expect_addition | |
316 | e("%s%s._b1" % (name, suffix(rename1))) | |
317 | e("%s%s._b2" % (name, suffix(rename2))) | |
318 | e("%s%s._b3" % (name, suffix(rename3))) | |
319 | e("%s%s._c" % (name, suffix(rename1 and rename2 and rename3))) | |
320 | e("%s._d" % name) | |
321 | t.expect_nothing_more() | |
322 | ||
323 | test_one(t, False, False, False, status=0) | |
324 | test_one(t, True , False, False, status=1) | |
325 | test_one(t, False, True , False, status=1) | |
326 | test_one(t, False, False, True , status=1) | |
327 | test_one(t, True , True , False, status=1) | |
328 | test_one(t, True , False, True , status=1) | |
329 | test_one(t, False, True , True , status=1) | |
330 | test_one(t, True , True , True , status=0) | |
331 | t.cleanup() | |
332 | ||
333 | ||
334 | def __match_count_is(lines, pattern, expected): | |
335 | count = 0 | |
336 | for x in lines: | |
337 | if re.search(pattern, x): | |
338 | count += 1 | |
339 | if count > expected: | |
340 | return False | |
341 | return count == expected | |
342 | ||
343 | ||
344 | def __write_appender(t, name): | |
345 | t.write(name, | |
346 | r"""# Copyright 2012 Jurko Gospodnetic | |
347 | # Distributed under the Boost Software License, Version 1.0. | |
348 | # (See accompanying file LICENSE_1_0.txt or copy at | |
349 | # http://www.boost.org/LICENSE_1_0.txt) | |
350 | ||
351 | # Support for registering test generators that construct their targets by | |
352 | # simply appending their given input data, e.g. list of sources & targets. | |
353 | ||
354 | import "class" : new ; | |
355 | import generators ; | |
356 | import modules ; | |
357 | import sequence ; | |
358 | ||
359 | rule register ( id composing ? : source-types + : target-types + ) | |
360 | { | |
361 | local caller-module = [ CALLER_MODULE ] ; | |
362 | id = $(caller-module).$(id) ; | |
363 | local g = [ new generator $(id) $(composing) : $(source-types) : | |
364 | $(target-types) ] ; | |
365 | $(g).set-rule-name $(__name__).appender ; | |
366 | generators.register $(g) ; | |
367 | return $(id) ; | |
368 | } | |
369 | ||
370 | if [ modules.peek : NT ] | |
371 | { | |
372 | X = ")" ; | |
373 | ECHO_CMD = (echo. ; | |
374 | } | |
375 | else | |
376 | { | |
377 | X = \" ; | |
378 | ECHO_CMD = "echo $(X)" ; | |
379 | } | |
380 | ||
381 | local appender-runs ; | |
382 | ||
383 | # We set up separate actions for building each target in order to avoid having | |
384 | # to iterate over them in action (i.e. shell) code. We have to be extra careful | |
385 | # though to achieve the exact same effect as if doing all the work in just one | |
386 | # action. Otherwise Boost Jam might, under some circumstances, run only some of | |
387 | # our actions. To achieve this we register a series of actions for all the | |
388 | # targets (since they all have the same target list - either all or none of them | |
389 | # get run independent of which target actually needs to get built), each | |
390 | # building only a single target. Since all our actions use the same targets, we | |
391 | # can not use 'on-target' parameters to pass data to a specific action so we | |
392 | # pass them using the second 'sources' parameter which our actions then know how | |
393 | # to interpret correctly. This works well since Boost Jam does not automatically | |
394 | # add dependency relations between specified action targets & sources and so the | |
395 | # second argument, even though most often used to pass in a list of sources, can | |
396 | # actually be used for passing in any type of information. | |
397 | rule appender ( targets + : sources + : properties * ) | |
398 | { | |
399 | appender-runs = [ CALC $(appender-runs:E=0) + 1 ] ; | |
400 | local target-index = 0 ; | |
401 | local target-count = [ sequence.length $(targets) ] ; | |
402 | local original-targets ; | |
403 | for t in $(targets) | |
404 | { | |
405 | target-index = [ CALC $(target-index) + 1 ] ; | |
406 | local appender-run = $(appender-runs) ; | |
407 | if $(targets[2])-defined | |
408 | { | |
409 | appender-run += [$(target-index)/$(target-count)] ; | |
410 | } | |
411 | append $(targets) : $(appender-run:J=" ") $(t) $(sources) ; | |
412 | } | |
413 | } | |
414 | ||
415 | actions append | |
416 | { | |
417 | $(ECHO_CMD)-------------------------------------------------$(X) | |
418 | $(ECHO_CMD)Appender run: $(>[1])$(X) | |
419 | $(ECHO_CMD)Appender run: $(>[1])$(X)>> "$(>[2])" | |
420 | $(ECHO_CMD)Target group: $(<:J=' ')$(X) | |
421 | $(ECHO_CMD)Target group: $(<:J=' ')$(X)>> "$(>[2])" | |
422 | $(ECHO_CMD) Target: '$(>[2])'$(X) | |
423 | $(ECHO_CMD) Target: '$(>[2])'$(X)>> "$(>[2])" | |
424 | $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X) | |
425 | $(ECHO_CMD) Sources: '$(>[3-]:J=' ')'$(X)>> "$(>[2])" | |
426 | $(ECHO_CMD)=================================================$(X) | |
427 | $(ECHO_CMD)-------------------------------------------------$(X)>> "$(>[2])" | |
428 | } | |
429 | """) | |
430 | ||
431 | ||
432 | test_basic() | |
433 | test_generated_target_names() |