]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/tools/build/src/util/print.jam
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / tools / build / src / util / print.jam
1 # Copyright 2003 Douglas Gregor
2 # Copyright 2002, 2003, 2005 Rene Rivera
3 # Copyright 2002, 2003, 2004, 2005 Vladimir Prus
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
6
7 # Utilities for generating format independent output. Using these
8 # will help in generation of documentation in at minimum plain/console
9 # and html.
10
11 import modules ;
12 import numbers ;
13 import string ;
14 import regex ;
15 import "class" ;
16 import scanner ;
17 import path ;
18 import os ;
19
20 # The current output target. Defaults to console.
21 output-target = console ;
22
23 # The current output type. Defaults to plain. Other possible values are "html".
24 output-type = plain ;
25
26 # Whitespace.
27 .whitespace = [ string.whitespace ] ;
28
29 # Redirect
30 .redirect-out = ">" ;
31 .redirect-append = ">>" ;
32 if [ os.name ] = VMS
33 {
34 .redirect-out = "| TYPE SYS$INPUT /OUT=" ;
35 .redirect-append = "| APPEND/NEW SYS$INPUT " ;
36 }
37
38 # Set the target and type of output to generate. This sets both the destination
39 # output and the type of docs to generate to that output. The target can be
40 # either a file or "console" for echoing to the console. If the type of output
41 # is not specified it defaults to plain text.
42 #
43 rule output (
44 target # The target file or device; file or "console".
45 type ? # The type of output; "plain" or "html".
46 )
47 {
48 type ?= plain ;
49 if $(output-target) != $(target)
50 {
51 output-target = $(target) ;
52 output-type = $(type) ;
53 if $(output-type) = html
54 {
55 text
56 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
57 "<html>"
58 "<head>"
59 "</head>"
60 "<body link=\"#0000ff\" vlink=\"#800080\">"
61 : true
62 : prefix ;
63 text
64 "</body>"
65 "</html>"
66 :
67 : suffix ;
68 }
69 }
70 }
71
72
73 # Generate a section with a description. The type of output can be controlled by
74 # the value of the 'output-type' variable.
75 #
76 rule section (
77 name # The name of the section.
78 description * # A number of description lines.
79 )
80 {
81 if $(output-type) = plain
82 {
83 lines [ split-at-words $(name): ] ;
84 lines ;
85 }
86 else if $(output-type) = html
87 {
88 name = [ escape-html $(name) ] ;
89 text <h3>$(name)</h3> <p> ;
90 }
91 local pre = ;
92 while $(description)
93 {
94 local paragraph = ;
95 while $(description) && [ string.is-whitespace $(description[1]) ] { description = $(description[2-]) ; }
96 if $(pre)
97 {
98 while $(description) && (
99 $(pre) = " $(description[1])" ||
100 ( $(pre) < [ string.chars [ MATCH "^([$(.whitespace)]*)" : " $(description[1])" ] ] )
101 )
102 { paragraph += $(description[1]) ; description = $(description[2-]) ; }
103 while [ string.is-whitespace $(paragraph[-1]) ] { paragraph = $(paragraph[1--2]) ; }
104 pre = ;
105 if $(output-type) = plain
106 {
107 lines $(paragraph) "" : " " " " ;
108 }
109 else if $(output-type) = html
110 {
111 text <blockquote> ;
112 lines $(paragraph) ;
113 text </blockquote> ;
114 }
115 }
116 else
117 {
118 while $(description) && ! [ string.is-whitespace $(description[1]) ]
119 { paragraph += $(description[1]) ; description = $(description[2-]) ; }
120 if $(paragraph[1]) = :: && ! $(paragraph[2])
121 {
122 pre = " " ;
123 }
124 if $(paragraph[1]) = ::
125 {
126 if $(output-type) = plain
127 {
128 lines $(paragraph[2-]) "" : " " " " ;
129 lines ;
130 }
131 else if $(output-type) = html
132 {
133 text <blockquote> ;
134 lines $(paragraph[2-]) ;
135 text </blockquote> ;
136 }
137 }
138 else
139 {
140 local p = [ MATCH "(.*)(::)$" : $(paragraph[-1]) ] ;
141 local pws = [ MATCH "([ ]*)$" : $(p[1]) ] ;
142 p = [ MATCH "(.*)($(pws))($(p[2]))$" : $(paragraph[-1]) ] ;
143 if $(p[3]) = ::
144 {
145 pre = [ string.chars [ MATCH "^([$(.whitespace)]*)" : " $(p[1])" ] ] ;
146 if ! $(p[2]) || $(p[2]) = "" { paragraph = $(paragraph[1--2]) $(p[1]): ; }
147 else { paragraph = $(paragraph[1--2]) $(p[1]) ; }
148 if $(output-type) = plain
149 {
150 lines [ split-at-words " " $(paragraph) ] : " " " " ;
151 lines ;
152 }
153 else if $(output-type) = html
154 {
155 text </p> <p> [ escape-html $(paragraph) ] ;
156 }
157 }
158 else
159 {
160 if $(output-type) = plain
161 {
162 lines [ split-at-words " " $(paragraph) ] : " " " " ;
163 lines ;
164 }
165 else if $(output-type) = html
166 {
167 text </p> <p> [ escape-html $(paragraph) ] ;
168 }
169 }
170 }
171 }
172 }
173 if $(output-type) = html
174 {
175 text </p> ;
176 }
177 }
178
179
180 # Generate the start of a list of items. The type of output can be controlled by
181 # the value of the 'output-type' variable.
182 #
183 rule list-start ( )
184 {
185 if $(output-type) = plain
186 {
187 }
188 else if $(output-type) = html
189 {
190 text <ul> ;
191 }
192 }
193
194
195 # Generate an item in a list. The type of output can be controlled by the value
196 # of the 'output-type' variable.
197 #
198 rule list-item (
199 item + # The item to list.
200 )
201 {
202 if $(output-type) = plain
203 {
204 lines [ split-at-words "*" $(item) ] : " " " " ;
205 }
206 else if $(output-type) = html
207 {
208 text <li> [ escape-html $(item) ] </li> ;
209 }
210 }
211
212
213 # Generate the end of a list of items. The type of output can be controlled by
214 # the value of the 'output-type' variable.
215 #
216 rule list-end ( )
217 {
218 if $(output-type) = plain
219 {
220 lines ;
221 }
222 else if $(output-type) = html
223 {
224 text </ul> ;
225 }
226 }
227
228
229 # Split the given text into separate lines, word-wrapping to a margin. The
230 # default margin is 78 characters.
231 #
232 rule split-at-words (
233 text + # The text to split.
234 : margin ? # An optional margin, default is 78.
235 )
236 {
237 local lines = ;
238 text = [ string.words $(text:J=" ") ] ;
239 text = $(text:J=" ") ;
240 margin ?= 78 ;
241 local char-match-1 = ".?" ;
242 local char-match = "" ;
243 while $(margin) != 0
244 {
245 char-match = $(char-match)$(char-match-1) ;
246 margin = [ numbers.decrement $(margin) ] ;
247 }
248 while $(text)
249 {
250 local s = "" ;
251 local t = "" ;
252 # divide s into the first X characters and the rest
253 s = [ MATCH "^($(char-match))(.*)" : $(text) ] ;
254
255 if $(s[2])
256 {
257 # split the first half at a space
258 t = [ MATCH "^(.*)[\\ ]([^\\ ]*)$" : $(s[1]) ] ;
259 }
260 else
261 {
262 t = $(s) ;
263 }
264
265 if ! $(t[2])
266 {
267 t += "" ;
268 }
269
270 text = $(t[2])$(s[2]) ;
271 lines += $(t[1]) ;
272 }
273 return $(lines) ;
274 }
275
276
277 # Generate a set of fixed lines. Each single item passed in is output on a
278 # separate line. For console this just echos each line, but for html this will
279 # split them with <br>.
280 #
281 rule lines (
282 text * # The lines of text.
283 : indent ? # Optional indentation prepended to each line after the first.
284 outdent ? # Optional indentation to prepend to the first line.
285 )
286 {
287 text ?= "" ;
288 indent ?= "" ;
289 outdent ?= "" ;
290 if $(output-type) = plain
291 {
292 text $(outdent)$(text[1]) $(indent)$(text[2-]) ;
293 }
294 else if $(output-type) = html
295 {
296 local indent-chars = [ string.chars $(indent) ] ;
297 indent = "" ;
298 for local c in $(indent-chars)
299 {
300 if $(c) = " " { c = "&nbsp;" ; }
301 else if $(c) = " " { c = "&nbsp;&nbsp;&nbsp;&nbsp;" ; }
302 indent = $(indent)$(c) ;
303 }
304 local html-text = [ escape-html $(text) : "&nbsp;" ] ;
305 text $(html-text[1])<br> $(indent)$(html-text[2-])<br> ;
306 }
307 }
308
309
310 # Output text directly to the current target. When doing output to a file, one
311 # can indicate if the text should be output to "prefix" it, as the "body"
312 # (default), or "suffix" of the file. This is independant of the actual
313 # execution order of the text rule. This rule invokes a singular action, one
314 # action only once, which does the build of the file. Therefore actions on the
315 # target outside of this rule will happen entirely before and/or after all
316 # output using this rule.
317 #
318 rule text (
319 strings * # The strings of text to output.
320 : overwrite ? # True to overwrite the output (if it is a file).
321 : prefix-body-suffix ? # Indication to output prefix, body, or suffix (for
322 # a file).
323 )
324 {
325 prefix-body-suffix ?= body ;
326 if $(output-target) = console
327 {
328 if ! $(strings)
329 {
330 ECHO ;
331 }
332 else
333 {
334 for local s in $(strings)
335 {
336 ECHO $(s) ;
337 }
338 }
339 }
340 if ! $($(output-target).did-action)
341 {
342 $(output-target).did-action = yes ;
343 $(output-target).text-prefix = ;
344 $(output-target).text-body = ;
345 $(output-target).text-suffix = ;
346
347 nl on $(output-target) = "
348 " ;
349 text-redirect on $(output-target) = $(.redirect-append) ;
350 if $(overwrite)
351 {
352 text-redirect on $(output-target) = $(.redirect-out) ;
353 }
354 text-content on $(output-target) = ;
355
356 text-action $(output-target) ;
357
358 if $(overwrite) && $(output-target) != console
359 {
360 check-for-update $(output-target) ;
361 }
362 }
363 $(output-target).text-$(prefix-body-suffix) += $(strings) ;
364 text-content on $(output-target) =
365 $($(output-target).text-prefix)
366 $($(output-target).text-body)
367 $($(output-target).text-suffix) ;
368 }
369
370
371 # Outputs the text to the current targets, after word-wrapping it.
372 #
373 rule wrapped-text ( text + )
374 {
375 local lines = [ split-at-words $(text) ] ;
376 text $(lines) ;
377 }
378
379
380 # Escapes text into html/xml printable equivalents. Does not know about tags and
381 # therefore tags fed into this will also be escaped. Currently escapes space,
382 # "<", ">", and "&".
383 #
384 rule escape-html (
385 text + # The text to escape.
386 : space ? # What to replace spaces with, defaults to " ".
387 )
388 {
389 local html-text = ;
390 while $(text)
391 {
392 local html = $(text[1]) ;
393 text = $(text[2-]) ;
394 html = [ regex.replace $(html) "&" "&amp;" ] ;
395 html = [ regex.replace $(html) "<" "&lt;" ] ;
396 html = [ regex.replace $(html) ">" "&gt;" ] ;
397 if $(space)
398 {
399 html = [ regex.replace $(html) " " "$(space)" ] ;
400 }
401 html-text += $(html) ;
402 }
403 return $(html-text) ;
404 }
405
406
407 # Outputs the text strings collected by the text rule to the output file.
408 #
409 actions quietly text-action
410 {
411 @($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) "$(<)"
412 }
413
414 if [ os.name ] = VMS
415 {
416 actions quietly text-action
417 {
418 @($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) $(<:W)
419 }
420 }
421
422 rule get-scanner ( )
423 {
424 if ! $(.scanner)
425 {
426 .scanner = [ class.new print-scanner ] ;
427 }
428 return $(.scanner) ;
429 }
430
431
432 # The following code to update print targets when their contents change is a
433 # horrible hack. It basically creates a target which binds to this file
434 # (print.jam) and installs a scanner on it which reads the target and compares
435 # its contents to the new contents that we are writing.
436 #
437 rule check-for-update ( target )
438 {
439 local scanner = [ get-scanner ] ;
440 local file = [ path.native [ modules.binding $(__name__) ] ] ;
441 local g = [ MATCH <(.*)> : $(target:G) ] ;
442 local dependency-target = $(__file__:G=$(g:E=)-$(target:G=)-$(scanner)) ;
443 DEPENDS $(target) : $(dependency-target) ;
444 SEARCH on $(dependency-target) = $(file:D) ;
445 ISFILE $(dependency-target) ;
446 NOUPDATE $(dependency-target) ;
447 base on $(dependency-target) = $(target) ;
448 scanner.install $(scanner) : $(dependency-target) ;
449 return $(dependency-target) ;
450 }
451
452
453 class print-scanner : scanner
454 {
455 import path ;
456 import os ;
457
458 rule pattern ( )
459 {
460 return "(One match...)" ;
461 }
462
463 rule process ( target : matches * : binding )
464 {
465 local base = [ on $(target) return $(base) ] ;
466 local nl = [ on $(base) return $(nl) ] ;
467 local text-content = [ on $(base) return $(text-content) ] ;
468 local dir = [ on $(base) return $(LOCATE) ] ;
469 if $(dir)
470 {
471 dir = [ path.make $(dir) ] ;
472 }
473 local file = [ path.native [ path.join $(dir) $(base:G=) ] ] ;
474 local actual-content ;
475 if [ os.name ] = NT
476 {
477 actual-content = [ SHELL "type \"$(file)\" 2>nul" ] ;
478 }
479 else if [ os.name ] = VMS
480 {
481 actual-content = [ SHELL "PIPE TYPE $(file:W) 2>NL:" ] ;
482 }
483 else
484 {
485 actual-content = [ SHELL "cat \"$(file)\" 2>/dev/null" ] ;
486 }
487 if $(text-content:J=$(nl)) != $(actual-content)
488 {
489 ALWAYS $(base) ;
490 }
491 }
492 }
493
494
495 rule __test__ ( )
496 {
497 import assert ;
498
499 assert.result one two three : split-at-words one two three : 5 ;
500 assert.result "one two" three : split-at-words one two three : 8 ;
501 assert.result "one two" three : split-at-words one two three : 9 ;
502 assert.result "one two three" : split-at-words one two three ;
503
504 # VP, 2004-12-03 The following test fails for some reason, so commenting it
505 # out.
506 #assert.result "one&nbsp;two&nbsp;three" "&amp;&lt;&gt;" :
507 # escape-html "one two three" "&<>" ;
508 }