]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | # Copyright 2003 Dave Abrahams |
2 | # Copyright 2004 Vladimir Prus | |
3 | # Distributed under the Boost Software License, Version 1.0. | |
1e59de90 TL |
4 | # (See accompanying file LICENSE.txt or copy at |
5 | # https://www.bfgroup.xyz/b2/LICENSE.txt) | |
7c673cae FG |
6 | |
7 | # Print a stack backtrace leading to this rule's caller. Each argument | |
8 | # represents a line of output to be printed after the first line of the | |
9 | # backtrace. | |
10 | # | |
11 | rule backtrace ( skip-frames prefix messages * : * ) | |
12 | { | |
13 | local frame-skips = 5 9 13 17 21 25 29 33 37 41 45 49 53 57 61 65 69 73 77 81 ; | |
14 | local drop-elements = $(frame-skips[$(skip-frames)]) ; | |
15 | if ! ( $(skip-frames) in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ) | |
16 | { | |
17 | ECHO "warning: backtrace doesn't support skipping $(skip-frames) " | |
18 | "frames; using 1 instead." ; | |
19 | drop-elements = 5 ; | |
20 | } | |
21 | ||
22 | local args = $(.args) ; | |
23 | if $(.user-modules-only) | |
24 | { | |
25 | local bt = [ nearest-user-location ] ; | |
26 | if $(bt) | |
27 | { | |
28 | ECHO $(prefix) at $(bt) ; | |
29 | } | |
30 | for local n in $(args) | |
31 | { | |
32 | if $($(n))-is-defined | |
33 | { | |
34 | ECHO $(prefix) $($(n)) ; | |
35 | } | |
36 | } | |
37 | } | |
38 | else | |
39 | { | |
40 | # Get the whole backtrace, then drop the initial quadruples | |
41 | # corresponding to the frames that must be skipped. | |
42 | local bt = [ BACKTRACE ] ; | |
43 | bt = $(bt[$(drop-elements)-]) ; | |
44 | ||
45 | while $(bt) | |
46 | { | |
47 | local m = [ MATCH ^(.+)\\.$ : $(bt[3]) ] ; | |
11fdf7f2 | 48 | ECHO "$(bt[1]):$(bt[2]):" "in" $(bt[4]) "from module" $(m) ; |
7c673cae FG |
49 | |
50 | # The first time through, print each argument on a separate line. | |
51 | for local n in $(args) | |
52 | { | |
53 | if $($(n))-is-defined | |
54 | { | |
55 | ECHO $(prefix) $($(n)) ; | |
56 | } | |
57 | } | |
58 | args = ; # Kill args so that this never happens again. | |
59 | ||
60 | # Move on to the next quadruple. | |
61 | bt = $(bt[5-]) ; | |
62 | } | |
63 | } | |
64 | } | |
65 | ||
66 | .args ?= messages 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; | |
67 | .disabled ?= ; | |
68 | .last-error-$(.args) ?= ; | |
69 | ||
70 | ||
71 | # try-catch -- | |
72 | # | |
73 | # This is not really an exception-handling mechanism, but it does allow us to | |
74 | # perform some error-checking on our error-checking. Errors are suppressed after | |
75 | # a try, and the first one is recorded. Use catch to check that the error | |
76 | # message matched expectations. | |
77 | ||
78 | # Begin looking for error messages. | |
79 | # | |
80 | rule try ( ) | |
81 | { | |
82 | .disabled += true ; | |
83 | .last-error-$(.args) = ; | |
84 | } | |
85 | ||
86 | ||
87 | # Stop looking for error messages; generate an error if an argument of messages | |
88 | # is not found in the corresponding argument in the error call. | |
89 | # | |
90 | rule catch ( messages * : * ) | |
91 | { | |
92 | .disabled = $(.disabled[2-]) ; # Pop the stack. | |
93 | ||
94 | import sequence ; | |
95 | ||
96 | if ! $(.last-error-$(.args))-is-defined | |
97 | { | |
98 | error-skip-frames 3 expected an error, but none occurred ; | |
99 | } | |
100 | else | |
101 | { | |
102 | for local n in $(.args) | |
103 | { | |
104 | if ! $($(n)) in $(.last-error-$(n)) | |
105 | { | |
106 | local v = [ sequence.join $($(n)) : " " ] ; | |
107 | v ?= "" ; | |
108 | local joined = [ sequence.join $(.last-error-$(n)) : " " ] ; | |
109 | ||
110 | .last-error-$(.args) = ; | |
111 | error-skip-frames 3 expected \"$(v)\" in argument $(n) of error | |
112 | : got \"$(joined)\" instead ; | |
113 | } | |
114 | } | |
115 | } | |
116 | } | |
117 | ||
118 | ||
119 | rule error-skip-frames ( skip-frames messages * : * ) | |
120 | { | |
121 | if ! $(.disabled) | |
122 | { | |
11fdf7f2 | 123 | backtrace $(skip-frames) "error:" $(messages) : $(2) : $(3) : $(4) : $(5) |
7c673cae FG |
124 | : $(6) : $(7) : $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) |
125 | : $(15) : $(16) : $(17) : $(18) : $(19) ; | |
126 | EXIT ; | |
127 | } | |
128 | else if ! $(.last-error-$(.args)) | |
129 | { | |
130 | for local n in $(.args) | |
131 | { | |
132 | # Add an extra empty string so that we always have something in the | |
133 | # event of an error. | |
134 | .last-error-$(n) = $($(n)) "" ; | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | if --no-error-backtrace in [ modules.peek : ARGV ] | |
140 | { | |
141 | .no-error-backtrace = true ; | |
142 | } | |
143 | ||
144 | ||
145 | # Print an error message with a stack backtrace and exit. | |
146 | # | |
147 | rule error ( messages * : * ) | |
148 | { | |
149 | if $(.no-error-backtrace) | |
150 | { | |
151 | local first-printed ; | |
152 | # Print each argument on a separate line. | |
153 | for local n in $(.args) | |
154 | { | |
155 | if $($(n))-is-defined | |
156 | { | |
157 | if ! $(first-printed) | |
158 | { | |
11fdf7f2 | 159 | ECHO "error:" $($(n)) ; |
7c673cae FG |
160 | first-printed = true ; |
161 | } | |
162 | else | |
163 | { | |
164 | ECHO $($(n)) ; | |
165 | } | |
166 | } | |
167 | } | |
168 | EXIT ; | |
169 | } | |
170 | else | |
171 | { | |
172 | error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : | |
173 | $(8) : $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) | |
174 | : $(17) : $(18) : $(19) ; | |
175 | } | |
176 | } | |
177 | ||
178 | ||
179 | # Same as 'error', but the generated backtrace will include only user files. | |
180 | # | |
181 | rule user-error ( messages * : * ) | |
182 | { | |
183 | .user-modules-only = 1 ; | |
184 | error-skip-frames 3 $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : | |
185 | $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : | |
186 | $(18) : $(19) ; | |
187 | } | |
188 | ||
189 | ||
190 | # Print a warning message with a stack backtrace and exit. | |
191 | # | |
192 | rule warning | |
193 | { | |
11fdf7f2 | 194 | backtrace 2 "warning:" $(1) : $(2) : $(3) : $(4) : $(5) : $(6) : $(7) : $(8) : |
7c673cae FG |
195 | $(9) : $(10) : $(11) : $(12) : $(13) : $(14) : $(15) : $(16) : $(17) : |
196 | $(18) : $(19) ; | |
197 | } | |
198 | ||
199 | ||
200 | # Convert an arbitrary argument list into a list with ":" separators and quoted | |
201 | # elements representing the same information. This is mostly useful for | |
202 | # formatting descriptions of arguments with which a rule was called when | |
203 | # reporting an error. | |
204 | # | |
205 | rule lol->list ( * ) | |
206 | { | |
207 | local result ; | |
208 | local remaining = 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ; | |
209 | while $($(remaining)) | |
210 | { | |
211 | local n = $(remaining[1]) ; | |
212 | remaining = $(remaining[2-]) ; | |
213 | ||
214 | if $(n) != 1 | |
215 | { | |
216 | result += ":" ; | |
217 | } | |
218 | result += \"$($(n))\" ; | |
219 | } | |
220 | return $(result) ; | |
221 | } | |
222 | ||
223 | ||
224 | # Return the file:line for the nearest entry in backtrace which correspond to a | |
225 | # user module. | |
226 | # | |
227 | rule nearest-user-location ( ) | |
228 | { | |
229 | local bt = [ BACKTRACE ] ; | |
230 | ||
231 | local result ; | |
232 | while $(bt) && ! $(result) | |
233 | { | |
234 | local m = [ MATCH ^(.+)\\.$ : $(bt[3]) ] ; | |
11fdf7f2 | 235 | local user-modules = "([Jj]amroot(.jam|.v2|)|([Jj]amfile(.jam|.v2|)|user-config.jam|site-config.jam|project-config.jam|project-root.jam)" ; |
7c673cae FG |
236 | |
237 | if [ MATCH $(user-modules) : $(bt[1]:D=) ] | |
238 | { | |
11fdf7f2 | 239 | result = "$(bt[1]):$(bt[2])" ; |
7c673cae FG |
240 | } |
241 | bt = $(bt[5-]) ; | |
242 | } | |
243 | return $(result) ; | |
244 | } | |
245 | ||
246 | ||
247 | # If optimized rule is available in Jam, use it. | |
248 | if NEAREST_USER_LOCATION in [ RULENAMES ] | |
249 | { | |
250 | rule nearest-user-location ( ) | |
251 | { | |
252 | local r = [ NEAREST_USER_LOCATION ] ; | |
11fdf7f2 | 253 | return "$(r[1]):$(r[2])" ; |
7c673cae FG |
254 | } |
255 | } | |
256 | ||
257 | ||
258 | rule __test__ ( ) | |
259 | { | |
260 | # Show that we can correctly catch an expected error. | |
261 | try ; | |
262 | { | |
263 | error an error occurred : somewhere ; | |
264 | } | |
265 | catch an error occurred : somewhere ; | |
266 | ||
267 | # Show that unexpected errors generate real errors. | |
268 | try ; | |
269 | { | |
270 | try ; | |
271 | { | |
272 | error an error occurred : somewhere ; | |
273 | } | |
274 | catch an error occurred : nowhere ; | |
275 | } | |
276 | catch expected \"nowhere\" in argument 2 ; | |
277 | ||
278 | # Show that not catching an error where one was expected is an error. | |
279 | try ; | |
280 | { | |
281 | try ; | |
282 | { | |
283 | } | |
284 | catch ; | |
285 | } | |
286 | catch expected an error, but none occurred ; | |
287 | } |