Add a PyMOL builder to SCons and generalize PYMOL_PATH setup.
[thesis.git] / site_cons / site_tools / asymptote.py
1 import os.path
2 import re
3 import SCons.Action
4 import SCons.Scanner
5 import SCons.Script
6 import SCons.Util
7 import doctest
8
9
10 double_quoted_string_re = re.compile(r'"([^"]*)"', re.M)
11
12 # SCons' LaTeX scanner doesn't understand \asyinclude{}, so keep track
13 # of all Asymptote graphics for phony target creation.
14 asyfigs = []
15
16 def asymptote_scan(node, env, path, arg=None):
17     """
18     >>> this_dir = os.path.dirname(__file__)
19     >>> src_dir = os.path.join(this_dir, '..', '..', 'src')
20     >>> class node (object):
21     ...     def __init__(self, path):
22     ...         self.path = path
23     ...         self.abspath = os.path.abspath(self.path)
24     ...         if os.path.isfile(self.path):
25     ...             self.dir = node(os.path.dirname(path))
26     ...     def get_text_contents(self):
27     ...         return open(self.path, 'r').read()
28     ...     def get_contents(self):
29     ...         return self.get_text_contents()
30     ...     def srcnode(self):
31     ...         return self
32     >>> for p in asymptote_scan(
33     ...         node(os.path.join(src_dir, 'figures', 'cantilever-sim', 'v-dep.asy')),
34     ...         None, None, None):
35     ...     print p
36     v-dep.d/v_dep_127_8
37     v-dep.d/v_dep_27_8
38     v-dep.d/v_dep_127_30
39     v-dep.d/v_dep_27_30
40     v-dep.d/v_dep_0.1_1
41     v-dep.d/v_dep_0.1_30
42     v-dep.d/v_dep_127_8.fit.dat
43     v-dep.d/v_dep_27_8.fit.dat
44     v-dep.d/v_dep_127_30.fit.dat
45     v-dep.d/v_dep_27_30.fit.dat
46     v-dep.d/v_dep_0.1_1.fit.dat
47     v-dep.d/v_dep_0.1_30.fit.dat
48     """
49     try:
50         contents = node.get_text_contents()
51     except AttributeError:
52         contents = node.get_contents() # for older versions of SCons, fall back on binary read
53     ret = []
54     for string in double_quoted_string_re.findall(contents):
55         if string in ret:
56             continue # we've already added this dependency
57         if len(string) == 0:
58             continue # empty string not much of a dependency ;)
59         p = os.path.join(node.dir.srcnode().abspath, string)
60         if os.path.exists(p) and os.path.isfile(p): # probably an include file
61             ret.append(string)
62         elif string.endswith('.dat'): # marker for auto-generated include files
63             ret.append(string)
64     return ret
65
66 def asymptote_emitter(target, source, env):
67     assert str(source[0]).endswith('.asy'), str(source[0])
68     filebase = str(source[0])[:-len('.asy')]
69     target.extend(['%s%s' % (filebase, ext)
70                    for ext in ['.tex', '.pre', '_0.pdf']])
71     source.append(SCons.Script.Alias('asytools'))
72     # side effect, keep track of all asymptote graphics.
73     asyfigs.append(target[0])
74     return target, source
75
76 AsymptoteAction = None
77
78 def generate(env):
79     """Add Builders and construction variables for Asymptote to an Environment."""
80     global AsymptoteAction
81     if AsymptoteAction is None:
82         AsymptoteAction = SCons.Action.Action('$ASYMPTOTECOM', '$ASYMPTOTECOMSTR')
83
84     #import pdf
85     #pdf.generate(env)
86
87     env['BUILDERS']['Asymptote'] = SCons.Script.Builder(
88         action=AsymptoteAction, suffix='.tex', src_suffix = '.asy',
89         emitter=asymptote_emitter)
90
91     env['ASYMPTOTE'] = 'asy'
92     env['ASYMPTOTEFLAGS'] = SCons.Util.CLVar(
93         '-tex pdflatex -inlineimage -inlinetex')
94     env['ASYMPTOTECOM']  = 'cd ${TARGET.dir} && $ASYMPTOTE $ASYMPTOTEFLAGS ${SOURCE.filebase}'
95     env.Append(SCANNERS=SCons.Scanner.Base(
96             function=asymptote_scan,
97             name='Asymptote',
98             skeys=['.asy']))
99
100 def exists(env):
101     return env.Detect('asymptote')
102
103 if __name__ == '__main__':
104     doctest.testmod()