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)
7 # Utilities for generating format independent output. Using these
8 # will help in generation of documentation in at minimum plain/console
20 # The current output target. Defaults to console.
21 output-target = console ;
23 # The current output type. Defaults to plain. Other possible values are "html".
27 .whitespace = [ string.whitespace ] ;
31 .redirect-append = ">>" ;
34 .redirect-out = "| TYPE SYS$INPUT /OUT=" ;
35 .redirect-append = "| APPEND/NEW SYS$INPUT " ;
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.
44 target # The target file or device; file or "console".
45 type ? # The type of output; "plain" or "html".
49 if $(output-target) != $(target)
51 output-target = $(target) ;
52 output-type = $(type) ;
53 if $(output-type) = html
56 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"
60 "<body link=\"#0000ff\" vlink=\"#800080\">"
73 # Generate a section with a description. The type of output can be controlled by
74 # the value of the 'output-type' variable.
77 name # The name of the section.
78 description * # A number of description lines.
81 if $(output-type) = plain
83 lines [ split-at-words $(name): ] ;
86 else if $(output-type) = html
88 name = [ escape-html $(name) ] ;
89 text <h3>$(name)</h3> <p> ;
95 while $(description) && [ string.is-whitespace $(description[1]) ] { description = $(description[2-]) ; }
98 while $(description) && (
99 $(pre) = " $(description[1])" ||
100 ( $(pre) < [ string.chars [ MATCH "^([$(.whitespace)]*)" : " $(description[1])" ] ] )
102 { paragraph += $(description[1]) ; description = $(description[2-]) ; }
103 while [ string.is-whitespace $(paragraph[-1]) ] { paragraph = $(paragraph[1--2]) ; }
105 if $(output-type) = plain
107 lines $(paragraph) "" : " " " " ;
109 else if $(output-type) = html
118 while $(description) && ! [ string.is-whitespace $(description[1]) ]
119 { paragraph += $(description[1]) ; description = $(description[2-]) ; }
120 if $(paragraph[1]) = :: && ! $(paragraph[2])
124 if $(paragraph[1]) = ::
126 if $(output-type) = plain
128 lines $(paragraph[2-]) "" : " " " " ;
131 else if $(output-type) = html
134 lines $(paragraph[2-]) ;
140 local p = [ MATCH "(.*)(::)$" : $(paragraph[-1]) ] ;
141 local pws = [ MATCH "([ ]*)$" : $(p[1]) ] ;
142 p = [ MATCH "(.*)($(pws))($(p[2]))$" : $(paragraph[-1]) ] ;
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
150 lines [ split-at-words " " $(paragraph) ] : " " " " ;
153 else if $(output-type) = html
155 text </p> <p> [ escape-html $(paragraph) ] ;
160 if $(output-type) = plain
162 lines [ split-at-words " " $(paragraph) ] : " " " " ;
165 else if $(output-type) = html
167 text </p> <p> [ escape-html $(paragraph) ] ;
173 if $(output-type) = html
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.
185 if $(output-type) = plain
188 else if $(output-type) = html
195 # Generate an item in a list. The type of output can be controlled by the value
196 # of the 'output-type' variable.
199 item + # The item to list.
202 if $(output-type) = plain
204 lines [ split-at-words "*" $(item) ] : " " " " ;
206 else if $(output-type) = html
208 text <li> [ escape-html $(item) ] </li> ;
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.
218 if $(output-type) = plain
222 else if $(output-type) = html
229 # Split the given text into separate lines, word-wrapping to a margin. The
230 # default margin is 78 characters.
232 rule split-at-words (
233 text + # The text to split.
234 : margin ? # An optional margin, default is 78.
238 text = [ string.words $(text:J=" ") ] ;
239 text = $(text:J=" ") ;
241 local char-match-1 = ".?" ;
242 local char-match = "" ;
245 char-match = $(char-match)$(char-match-1) ;
246 margin = [ numbers.decrement $(margin) ] ;
252 # divide s into the first X characters and the rest
253 s = [ MATCH "^($(char-match))(.*)" : $(text) ] ;
257 # split the first half at a space
258 t = [ MATCH "^(.*)[\\ ]([^\\ ]*)$" : $(s[1]) ] ;
270 text = $(t[2])$(s[2]) ;
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>.
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.
290 if $(output-type) = plain
292 text $(outdent)$(text[1]) $(indent)$(text[2-]) ;
294 else if $(output-type) = html
296 local indent-chars = [ string.chars $(indent) ] ;
298 for local c in $(indent-chars)
300 if $(c) = " " { c = " " ; }
301 else if $(c) = " " { c = " " ; }
302 indent = $(indent)$(c) ;
304 local html-text = [ escape-html $(text) : " " ] ;
305 text $(html-text[1])<br> $(indent)$(html-text[2-])<br> ;
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.
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
325 prefix-body-suffix ?= body ;
326 if $(output-target) = console
334 for local s in $(strings)
340 if ! $($(output-target).did-action)
342 $(output-target).did-action = yes ;
343 $(output-target).text-prefix = ;
344 $(output-target).text-body = ;
345 $(output-target).text-suffix = ;
347 nl on $(output-target) = "
349 text-redirect on $(output-target) = $(.redirect-append) ;
352 text-redirect on $(output-target) = $(.redirect-out) ;
354 text-content on $(output-target) = ;
356 text-action $(output-target) ;
358 if $(overwrite) && $(output-target) != console
360 check-for-update $(output-target) ;
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) ;
371 # Outputs the text to the current targets, after word-wrapping it.
373 rule wrapped-text ( text + )
375 local lines = [ split-at-words $(text) ] ;
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,
385 text + # The text to escape.
386 : space ? # What to replace spaces with, defaults to " ".
392 local html = $(text[1]) ;
394 html = [ regex.replace $(html) "&" "&" ] ;
395 html = [ regex.replace $(html) "<" "<" ] ;
396 html = [ regex.replace $(html) ">" ">" ] ;
399 html = [ regex.replace $(html) " " "$(space)" ] ;
401 html-text += $(html) ;
403 return $(html-text) ;
407 # Outputs the text strings collected by the text rule to the output file.
409 actions quietly text-action
411 @($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) "$(<)"
416 actions quietly text-action
418 @($(STDOUT):E=$(text-content:J=$(nl))) $(text-redirect) $(<:W)
426 .scanner = [ class.new print-scanner ] ;
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.
437 rule check-for-update ( target )
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) ;
453 class print-scanner : scanner
460 return "(One match...)" ;
463 rule process ( target : matches * : binding )
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) ] ;
471 dir = [ path.make $(dir) ] ;
473 local file = [ path.native [ path.join $(dir) $(base:G=) ] ] ;
474 local actual-content ;
477 actual-content = [ SHELL "type \"$(file)\" 2>nul" ] ;
479 else if [ os.name ] = VMS
481 actual-content = [ SHELL "PIPE TYPE $(file:W) 2>NL:" ] ;
485 actual-content = [ SHELL "cat \"$(file)\" 2>/dev/null" ] ;
487 if $(text-content:J=$(nl)) != $(actual-content)
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 ;
504 # VP, 2004-12-03 The following test fails for some reason, so commenting it
506 #assert.result "one two three" "&<>" :
507 # escape-html "one two three" "&<>" ;