3 # Copyright (c) 2010 The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 # This script looks for some XML tags that describe SCons example
27 # configurations and commands to execute in those configurations, and
28 # uses TestCmd.py to execute the commands and insert the output from
29 # those commands into the XML that we output. This way, we can run a
30 # script and update all of our example documentation output without
31 # a lot of laborious by-hand checking.
33 # An "SCons example" looks like this, and essentially describes a set of
34 # input files (program source files as well as SConscript files):
36 # <scons_example name="ex1">
37 # <file name="SConstruct" printme="1">
42 # int main(void) { printf("foo.c\n"); }
46 # The <file> contents within the <scons_example> tag will get written
47 # into a temporary directory whenever example output needs to be
48 # generated. By default, the <file> contents are not inserted into text
49 # directly, unless you set the "printme" attribute on one or more files,
50 # in which case they will get inserted within a <programlisting> tag.
51 # This makes it easy to define the example at the appropriate
52 # point in the text where you intend to show the SConstruct file.
54 # Note that you should usually give the <scons_example> a "name"
55 # attribute so that you can refer to the example configuration later to
56 # run SCons and generate output.
58 # If you just want to show a file's contents without worry about running
59 # SCons, there's a shorter <sconstruct> tag:
66 # This is essentially equivalent to <scons_example><file printme="1">,
67 # but it's more straightforward.
69 # SCons output is generated from the following sort of tag:
71 # <scons_output example="ex1" os="posix">
72 # <scons_output_command suffix="1">scons -Q foo</scons_output_command>
73 # <scons_output_command suffix="2">scons -Q foo</scons_output_command>
76 # You tell it which example to use with the "example" attribute, and then
77 # give it a list of <scons_output_command> tags to execute. You can also
78 # supply an "os" tag, which specifies the type of operating system this
79 # example is intended to show; if you omit this, default value is "posix".
81 # The generated XML will show the command line (with the appropriate
82 # command-line prompt for the operating system), execute the command in
83 # a temporary directory with the example files, capture the standard
84 # output from SCons, and insert it into the text as appropriate.
85 # Error output gets passed through to your error output so you
86 # can see if there are any problems executing the command.
95 from SConsDoc
import tf
as stf
98 # The available types for ExampleFile entries
100 FT_FILE
= 0 # a physical file (=<file>)
101 FT_FILEREF
= 1 # a reference (=<scons_example_file>)
104 def __init__(self
, type_
=FT_FILE
):
111 return self
.type == FT_FILEREF
118 class ExampleCommand
:
121 self
.environment
= ''
141 def getFileContents(self
, fname
):
143 if fname
== f
.name
and not f
.isFileRef():
148 def readExampleInfos(fpath
, examples
):
149 """ Add the example infos for the file fpath to the
150 global dictionary examples.
154 t
= SConsDoc
.SConsDocTree()
155 t
.parseXmlFile(fpath
)
157 # Parse scons_examples
158 for e
in stf
.findAll(t
.root
, "scons_example", SConsDoc
.dbxid
,
159 t
.xpath_context
, t
.nsmap
):
161 if stf
.hasAttribute(e
, 'name'):
162 n
= stf
.getAttribute(e
, 'name')
163 if n
and n
not in examples
:
168 # Parse file and directory entries
169 for f
in stf
.findAll(e
, "file", SConsDoc
.dbxid
,
170 t
.xpath_context
, t
.nsmap
):
172 if stf
.hasAttribute(f
, 'name'):
173 fi
.name
= stf
.getAttribute(f
, 'name')
174 if stf
.hasAttribute(f
, 'chmod'):
175 fi
.chmod
= stf
.getAttribute(f
, 'chmod')
176 fi
.content
= stf
.getText(f
)
177 examples
[n
].files
.append(fi
)
178 for d
in stf
.findAll(e
, "directory", SConsDoc
.dbxid
,
179 t
.xpath_context
, t
.nsmap
):
181 if stf
.hasAttribute(d
, 'name'):
182 di
.name
= stf
.getAttribute(d
, 'name')
183 if stf
.hasAttribute(d
, 'chmod'):
184 di
.chmod
= stf
.getAttribute(d
, 'chmod')
185 examples
[n
].folders
.append(di
)
188 # Parse scons_example_files
189 for f
in stf
.findAll(t
.root
, "scons_example_file", SConsDoc
.dbxid
,
190 t
.xpath_context
, t
.nsmap
):
191 if stf
.hasAttribute(f
, 'example'):
192 e
= stf
.getAttribute(f
, 'example')
195 fi
= ExampleFile(FT_FILEREF
)
196 if stf
.hasAttribute(f
, 'name'):
197 fi
.name
= stf
.getAttribute(f
, 'name')
198 if stf
.hasAttribute(f
, 'chmod'):
199 fi
.chmod
= stf
.getAttribute(f
, 'chmod')
200 fi
.content
= stf
.getText(f
)
201 examples
[e
].files
.append(fi
)
205 for o
in stf
.findAll(t
.root
, "scons_output", SConsDoc
.dbxid
,
206 t
.xpath_context
, t
.nsmap
):
207 if stf
.hasAttribute(o
, 'example'):
208 n
= stf
.getAttribute(o
, 'example')
212 eout
= ExampleOutput()
213 if stf
.hasAttribute(o
, 'name'):
214 eout
.name
= stf
.getAttribute(o
, 'name')
215 if stf
.hasAttribute(o
, 'tools'):
216 eout
.tools
= stf
.getAttribute(o
, 'tools')
217 if stf
.hasAttribute(o
, 'os'):
218 eout
.os
= stf
.getAttribute(o
, 'os')
219 if stf
.hasAttribute(o
, 'suffix'):
220 eout
.suffix
= stf
.getAttribute(o
, 'suffix')
222 for c
in stf
.findAll(o
, "scons_output_command", SConsDoc
.dbxid
,
223 t
.xpath_context
, t
.nsmap
):
224 oc
= ExampleCommand()
225 if stf
.hasAttribute(c
, 'edit'):
226 oc
.edit
= stf
.getAttribute(c
, 'edit')
227 if stf
.hasAttribute(c
, 'environment'):
228 oc
.environment
= stf
.getAttribute(c
, 'environment')
229 if stf
.hasAttribute(c
, 'output'):
230 oc
.output
= stf
.getAttribute(c
, 'output')
231 if stf
.hasAttribute(c
, 'cmd'):
232 oc
.cmd
= stf
.getAttribute(c
, 'cmd')
234 oc
.cmd
= stf
.getText(c
)
236 eout
.commands
.append(oc
)
238 examples
[n
].outputs
.append(eout
)
240 def readAllExampleInfos(dpath
):
241 """ Scan for XML files in the given directory and
242 collect together all relevant infos (files/folders,
243 output commands) in a map, which gets returned.
246 for path
, dirs
, files
in os
.walk(dpath
):
248 if f
.endswith('.xml'):
249 fpath
= os
.path
.join(path
, f
)
250 if SConsDoc
.isSConsXml(fpath
):
251 readExampleInfos(fpath
, examples
)
255 generated_examples
= os
.path
.join('doc', 'generated', 'examples')
257 def ensureExampleOutputsExist(dpath
):
258 """ Scan for XML files in the given directory and
259 ensure that for every example output we have a
260 corresponding output file in the 'generated/examples'
263 # Ensure that the output folder exists
264 if not os
.path
.isdir(generated_examples
):
265 os
.mkdir(generated_examples
)
267 examples
= readAllExampleInfos(dpath
)
268 for key
, value
in examples
.items():
269 # Process all scons_output tags
270 for o
in value
.outputs
:
271 cpath
= os
.path
.join(generated_examples
,
272 key
+ '_' + o
.suffix
+ '.xml')
273 if not os
.path
.isfile(cpath
):
275 s
= stf
.newXmlTree("screen")
276 stf
.setText(s
, "NO OUTPUT YET! Run the script to generate/update all examples.")
278 stf
.writeTree(s
, cpath
)
280 # Process all scons_example_file tags
281 for r
in value
.files
:
284 content
= value
.getFileContents(r
.name
)
285 fpath
= os
.path
.join(generated_examples
,
286 key
+ '_' + r
.name
.replace("/", "_"))
288 with
open(fpath
, 'w') as f
:
289 f
.write("%s\n" % content
)
293 def createAllExampleOutputs(dpath
):
294 """ Scan for XML files in the given directory and
295 creates all output files for every example in
296 the 'generated/examples' folder.
298 # Ensure that the output folder exists
299 if not os
.path
.isdir(generated_examples
):
300 os
.mkdir(generated_examples
)
302 examples
= readAllExampleInfos(dpath
)
303 total
= len(examples
)
306 if len(sys
.argv
) > 1:
307 examples_to_run
= sys
.argv
[1:]
308 examples
= { k
:v
for k
,v
in examples
.items() if k
in examples_to_run
}
310 for key
, value
in examples
.items():
311 # Process all scons_output tags
312 print("%.2f%s (%d/%d) %s" % (float(idx
+ 1) * 100.0 / float(total
),
313 perc
, idx
+ 1, total
, key
))
315 create_scons_output(value
)
316 # Process all scons_example_file tags
317 for r
in value
.files
:
320 content
= value
.getFileContents(r
.name
)
321 fpath
= os
.path
.join(generated_examples
,
322 key
+ '_' + r
.name
.replace("/", "_"))
324 with
open(fpath
, 'w') as f
:
325 f
.write("%s\n" % content
)
328 def collectSConsExampleNames(fpath
):
329 """ Return a set() of example names, used in the given file fpath.
333 failed_suffixes
= False
336 t
= SConsDoc
.SConsDocTree()
337 t
.parseXmlFile(fpath
)
340 for e
in stf
.findAll(t
.root
, "scons_example", SConsDoc
.dbxid
,
341 t
.xpath_context
, t
.nsmap
):
343 if stf
.hasAttribute(e
, 'name'):
344 n
= stf
.getAttribute(e
, 'name')
347 if n
not in suffixes
:
350 print("Error: Example in file '%s' is missing a name!" % fpath
)
351 failed_suffixes
= True
353 for o
in stf
.findAll(t
.root
, "scons_output", SConsDoc
.dbxid
,
354 t
.xpath_context
, t
.nsmap
):
356 if stf
.hasAttribute(o
, 'example'):
357 n
= stf
.getAttribute(o
, 'example')
359 print("Error: scons_output in file '%s' is missing an example name!" % fpath
)
360 failed_suffixes
= True
362 if n
not in suffixes
:
363 print("Error: scons_output in file '%s' is referencing non-existent example '%s'!" % (fpath
, n
))
364 failed_suffixes
= True
368 if stf
.hasAttribute(o
, 'suffix'):
369 s
= stf
.getAttribute(o
, 'suffix')
371 print("Error: scons_output in file '%s' (example '%s') is missing a suffix!" % (fpath
, n
))
372 failed_suffixes
= True
374 if s
not in suffixes
[n
]:
375 suffixes
[n
].append(s
)
377 print("Error: scons_output in file '%s' (example '%s') is using a duplicate suffix '%s'!" % (fpath
, n
, s
))
378 failed_suffixes
= True
380 return names
, failed_suffixes
382 def exampleNamesAreUnique(dpath
):
383 """ Scan for XML files in the given directory and
384 check whether the scons_example names are unique.
388 for path
, dirs
, files
in os
.walk(dpath
):
390 if f
.endswith('.xml'):
391 fpath
= os
.path
.join(path
, f
)
392 if SConsDoc
.isSConsXml(fpath
):
393 names
, failed_suffixes
= collectSConsExampleNames(fpath
)
396 i
= allnames
.intersection(names
)
398 print("Not unique in %s are: %s" % (fpath
, ', '.join(i
)))
405 # ###############################################################
407 # In the second half of this module (starting here)
408 # we define the variables and functions that are required
409 # to actually run the examples, collect their output and
410 # write it into the files in doc/generated/examples...
411 # which then get included by our UserGuide.
413 # ###############################################################
415 sys
.path
.append(os
.path
.join(os
.getcwd(), 'testing/framework'))
416 sys
.path
.append(os
.path
.join(os
.getcwd(), 'build', 'testing/framework'))
418 scons_py
= os
.path
.join('scripts', 'scons.py')
419 scons_py
= os
.path
.join(os
.getcwd(), scons_py
)
420 scons_lib_dir
= os
.path
.join(os
.getcwd(), 'SCons')
422 os
.environ
['SCONS_LIB_DIR'] = scons_lib_dir
431 # The magick SCons hackery that makes this work.
433 # So that our examples can still use the default SConstruct file, we
434 # actually feed the following into SCons via stdin and then have it
435 # SConscript() the SConstruct file. This stdin wrapper creates a set
436 # of ToolSurrogates for the tools for the appropriate platform. These
437 # Surrogates print output like the real tools and behave like them
438 # without actually having to be on the right platform or have the right
441 # The upshot: The wrapper transparently changes the world out from
442 # under the top-level SConstruct file in an example just so we can get
443 # the command output.
449 import SCons.Defaults
453 platform = '%(osname)s'
461 # Slip our own __str__() method into the EntryProxy class used to expand
462 # $TARGET{S} and $SOURCE{S} to translate the path-name separators from
463 # what's appropriate for the system we're running on to what's appropriate
464 # for the example system.
465 orig = SCons.Node.FS.EntryProxy
466 class MyEntryProxy(orig):
468 return str(self._subject).replace(os.sep, Sep)
469 SCons.Node.FS.EntryProxy = MyEntryProxy
471 # Slip our own RDirs() method into the Node.FS.File class so that the
472 # expansions of $_{CPPINC,F77INC,LIBDIR}FLAGS will have the path-name
473 # separators translated from what's appropriate for the system we're
474 # running on to what's appropriate for the example system.
475 orig_RDirs = SCons.Node.FS.File.RDirs
476 def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs):
477 return [str(x).replace(os.sep, Sep) for x in orig_RDirs(self, pathlist)]
478 SCons.Node.FS.File.RDirs = my_RDirs
481 def __init__(self, fun, *args, **kwargs):
483 self.pending = args[:]
484 self.kwargs = kwargs.copy()
486 def __call__(self, *args, **kwargs):
487 if kwargs and self.kwargs:
488 kw = self.kwargs.copy()
491 kw = kwargs or self.kwargs
493 return self.fun(*self.pending + args, **kw)
495 def Str(target, source, env, cmd=""):
497 for cmd in env.subst_list(cmd, target=target, source=source):
498 result.append(' '.join(map(str, cmd)))
499 return '\\n'.join(result)
502 def __init__(self, tool, variable, func, varlist):
504 if not isinstance(variable, list):
505 variable = [variable]
506 self.variable = variable
508 self.varlist = varlist
509 def __call__(self, env):
512 for v in self.variable:
515 strfunction = orig.strfunction
516 except AttributeError:
517 strfunction = Curry(Str, cmd=orig)
518 # Don't call Action() through its global function name, because
519 # that leads to infinite recursion in trying to initialize the
520 # Default Environment.
521 env[v] = SCons.Action.Action(self.func,
522 strfunction=strfunction,
523 varlist=self.varlist)
525 # This is for the benefit of printing the 'TOOLS'
526 # variable through env.Dump().
527 return repr(self.tool)
529 def Null(target, source, env):
532 def Cat(target, source, env):
533 target = str(target[0])
534 for src in map(str, source):
535 shutil.copy(src, target)
537 def CCCom(target, source, env):
538 def process(source_file, ofp):
539 with open(source_file, "r") as ifp:
540 for line in ifp.readlines():
541 m = re.match(r'#include\s[<"]([^<"]+)[>"]', line)
544 for d in [str(env.Dir('$CPPPATH')), '.']:
545 f = os.path.join(d, include)
546 if os.path.exists(f):
549 elif line[:11] != "STRIP CCCOM":
552 with open(str(target[0]), "w") as fp:
553 for src in map(str, source):
555 fp.write('debug = ' + ARGUMENTS.get('debug', '0') + '\\n')
557 public_class_re = re.compile(r'^public class (\S+)', re.MULTILINE)
559 def JavaCCom(target, source, env):
560 # This is a fake Java compiler that just looks for
561 # public class FooBar
562 # lines in the source file(s) and spits those out
563 # to .class files named after the class.
564 tlist = list(map(str, target))
568 for src in map(str, source):
569 with open(src, "r") as f:
571 classes = public_class_re.findall(contents)
573 for t in [x for x in tlist if x.find(c) != -1]:
574 with open(t, "w") as f:
577 for t in not_copied.keys():
578 with open(t, "w") as f:
581 def JavaHCom(target, source, env):
582 tlist = map(str, target)
583 slist = map(str, source)
584 for t, s in zip(tlist, slist):
587 def JarCom(target, source, env):
588 target = str(target[0])
590 for src in map(str, source):
591 for dirpath, dirnames, filenames in os.walk(src):
592 class_files.extend([ os.path.join(dirpath, f)
593 for f in filenames if f.endswith('.class') ])
594 for cf in class_files:
595 shutil.copy(cf, target)
597 # XXX Adding COLOR, COLORS and PACKAGE to the 'cc' varlist(s) by hand
598 # here is bogus. It's for the benefit of doc/user/command-line.in, which
599 # uses examples that want to rebuild based on changes to these variables.
600 # It would be better to figure out a way to do it based on the content of
601 # the generated command-line, or else find a way to let the example markup
602 # language in doc/user/command-line.in tell this script what variables to
603 # add, but that's more difficult than I want to figure out how to do right
604 # now, so let's just use the simple brute force approach for the moment.
607 'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
608 ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []),
609 ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []),
610 ('tar', 'TARCOM', Null, []),
611 ('zip', 'ZIPCOM', Null, []),
612 ('javac', 'JAVACCOM', JavaCCom, []),
613 ('javah', 'JAVAHCOM', JavaHCom, []),
614 ('jar', 'JARCOM', JarCom, []),
615 ('rmic', 'RMICCOM', Cat, []),
617 'win32' : [('msvc', ['CCCOM', 'SHCCCOM', 'RCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
618 ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []),
619 ('mslib', 'ARCOM', Cat, []),
620 ('tar', 'TARCOM', Null, []),
621 ('zip', 'ZIPCOM', Null, []),
622 ('javac', 'JAVACCOM', JavaCCom, []),
623 ('javah', 'JAVAHCOM', JavaHCom, []),
624 ('jar', 'JARCOM', JarCom, []),
625 ('rmic', 'RMICCOM', Cat, []),
629 toollist = ToolList[platform]
630 filter_tools = '%(tools)s'.split()
632 toollist = [x for x in toollist if x[0] in filter_tools]
634 toollist = [ToolSurrogate(*t) for t in toollist]
636 toollist.append('install')
638 def surrogate_spawn(sh, escape, cmd, args, env):
641 def surrogate_pspawn(sh, escape, cmd, args, env, stdout, stderr):
644 SCons.Defaults.ConstructionEnvironment.update({
645 'PLATFORM' : platform,
647 'SPAWN' : surrogate_spawn,
648 'PSPAWN' : surrogate_pspawn,
651 SConscript('SConstruct')
654 # "Commands" that we will execute in our examples.
655 def command_scons(args
, command
, test
, values
):
662 ce
= command
.environment
663 except AttributeError:
666 for arg
in command
.environment
.split():
667 key
, val
= arg
.split('=')
669 save_vals
[key
] = os
.environ
[key
]
671 delete_keys
.append(key
)
672 os
.environ
[key
] = val
674 test
.write(test
.workpath('WORK/SConstruct_created'), Stdin
% values
)
676 test
.run(interpreter
=sys
.executable
,
678 # We use ToolSurrogates to capture win32 output by "building"
679 # examples using a fake win32 tool chain. Suppress the
680 # warnings that come from the new revamped VS support so
681 # we can build doc on (Linux) systems that don't have
682 # Visual C installed.
683 arguments
='--warn=no-visual-c-missing -f - ' + ' '.join(args
),
684 chdir
=test
.workpath('WORK'),
685 stdin
=Stdin
% values
)
686 os
.environ
.update(save_vals
)
687 for key
in delete_keys
:
690 out
= out
.replace(test
.workpath('ROOT'), '')
691 out
= out
.replace(test
.workpath('WORK/SConstruct'),
692 '/home/my/project/SConstruct')
693 lines
= out
.split('\n')
695 while lines
[-1] == '':
697 # err = test.stderr()
699 # sys.stderr.write(err)
702 def command_touch(args
, command
, test
, values
):
704 t
= int(time
.mktime(time
.strptime(args
[1], '%Y%m%d%H%M')))
711 if not os
.path
.isabs(file):
712 file = os
.path
.join(test
.workpath('WORK'), file)
713 if not os
.path
.exists(file):
714 with
open(file, 'w'):
716 os
.utime(file, times
)
719 def command_edit(args
, c
, test
, values
):
721 add_string
= 'void edit(void) { ; }\n'
723 add_string
= c
.edit
[:]
724 if add_string
[-1] != '\n':
725 add_string
= add_string
+ '\n'
727 if not os
.path
.isabs(file):
728 file = os
.path
.join(test
.workpath('WORK'), file)
729 with
open(file, 'a') as f
:
733 def command_ls(args
, c
, test
, values
):
736 return [' '.join(sorted([x
for x
in os
.listdir(a
) if x
[0] != '.']))]
738 # This should never happen. Pop into debugger
739 import pdb
; pdb
.set_trace()
743 l
.extend(ls(test
.workpath('WORK', a
)))
745 return ls(test
.workpath('WORK'))
747 def command_sleep(args
, c
, test
, values
):
748 time
.sleep(int(args
[0]))
751 'scons' : command_scons
,
752 'touch' : command_touch
,
753 'edit' : command_edit
,
755 'sleep' : command_sleep
,
758 def ExecuteCommand(args
, c
, t
, values
):
760 func
= CommandDict
[args
[0]]
762 func
= lambda args
, c
, t
, values
: []
763 return func(args
[1:], c
, t
, values
)
766 def create_scons_output(e
):
768 The real raison d'etre for this script, this is where we
769 actually execute SCons to fetch the output.
772 # Loop over all outputs for the example
774 # Create new test directory
775 t
= TestCmd
.TestCmd(workdir
='', combine
=1)
778 t
.subdir('ROOT', 'WORK')
779 t
.rootpath
= t
.workpath('ROOT').replace('\\', '\\\\')
782 dir = t
.workpath('WORK', d
.name
)
783 if not os
.path
.exists(dir):
790 # Left-align file's contents, starting on the first
793 data
= f
.content
.split('\n')
800 # Scan first line for the number of spaces
801 # that this block is indented
802 while lines
[0][i
] == ' ':
805 lines
= [l
[i
:] for l
in lines
]
806 path
= f
.name
.replace('__ROOT__', t
.rootpath
)
807 if not os
.path
.isabs(path
):
808 path
= t
.workpath('WORK', path
)
809 dir, name
= os
.path
.split(path
)
810 if dir and not os
.path
.exists(dir):
812 content
= '\n'.join(lines
)
813 content
= content
.replace('__ROOT__', t
.rootpath
)
814 path
= t
.workpath('WORK', path
)
815 t
.write(path
, content
)
816 if hasattr(f
, 'chmod'):
818 os
.chmod(path
, int(f
.chmod
, base
=8))
820 # Regular expressions for making the doc output consistent,
821 # regardless of reported addresses or Python version.
823 # Massage addresses in object repr strings to a constant.
824 address_re
= re
.compile(r
' at 0x[0-9a-fA-F]*>')
826 # Massage file names in stack traces (sometimes reported as absolute
827 # paths) to a consistent relative path.
828 engine_re
= re
.compile(r
' File ".*/SCons/')
830 # Python 2.5 changed the stack trace when the module is read
831 # from standard input from read "... line 7, in ?" to
832 # "... line 7, in <module>".
833 file_re
= re
.compile(r
'^( *File ".*", line \d+, in) \?$', re
.M
)
835 # Python 2.6 made UserList a new-style class, which changes the
836 # AttributeError message generated by our NodeList subclass.
837 nodelist_re
= re
.compile(r
'(AttributeError:) NodeList instance (has no attribute \S+)')
839 # Root element for our subtree
840 sroot
= stf
.newEtreeNode("screen", True)
843 for command
in o
.commands
:
844 content
+= Prompt
[o
.os
]
845 if curchild
is not None:
846 if not command
.output
:
847 # Append content as tail
848 curchild
.tail
= content
850 # Add new child for userinput tag
851 curchild
= stf
.newEtreeNode("userinput")
852 d
= command
.cmd
.replace('__ROOT__', '')
854 sroot
.append(curchild
)
856 content
+= command
.output
+ '\n'
858 if not command
.output
:
859 # Add first text to root
862 # Add new child for userinput tag
863 curchild
= stf
.newEtreeNode("userinput")
864 d
= command
.cmd
.replace('__ROOT__', '')
866 sroot
.append(curchild
)
868 content
+= command
.output
+ '\n'
869 # Execute command and capture its output
870 cmd_work
= command
.cmd
.replace('__ROOT__', t
.workpath('ROOT'))
871 args
= cmd_work
.split()
872 lines
= ExecuteCommand(args
, command
, t
, {'osname':o
.os
, 'tools':o
.tools
})
873 if not command
.output
and lines
:
874 ncontent
= '\n'.join(lines
)
875 ncontent
= address_re
.sub(r
' at 0x700000>', ncontent
)
876 ncontent
= engine_re
.sub(r
' File "SCons/', ncontent
)
877 ncontent
= file_re
.sub(r
'\1 <module>', ncontent
)
878 ncontent
= nodelist_re
.sub(r
"\1 'NodeList' object \2", ncontent
)
879 ncontent
= ncontent
.replace('__ROOT__', '')
880 content
+= ncontent
+ '\n'
881 # Add last piece of content
883 if curchild
is not None:
884 curchild
.tail
= content
889 fpath
= os
.path
.join(generated_examples
,
890 e
.name
+ '_' + o
.suffix
+ '.xml')
891 # Expand Element tree
892 s
= stf
.decorateWithHeader(stf
.convertElementTree(sroot
)[0])
894 stf
.writeTree(s
, fpath
)
899 # indent-tabs-mode:nil
901 # vim: set expandtab tabstop=4 shiftwidth=4: