1from __future__ import print_function
2
3try:
4    import cStringIO
5except ImportError:
6    import io as cStringIO
7
8import sys, os
9import time
10import re
11import glob
12from collections import defaultdict
13from os.path import dirname
14from subprocess import Popen, PIPE
15from distutils import dir_util
16
17def create_all(generated_dir, pymoldir="."):
18    '''
19    Generate various stuff
20    '''
21    create_shadertext(
22            os.path.join(pymoldir, "data", "shaders"),
23            generated_dir,
24            os.path.join(generated_dir, "ShaderText.h"),
25            os.path.join(generated_dir, "ShaderText.cpp"))
26    create_buildinfo(generated_dir, pymoldir)
27
28class openw(object):
29    """
30    File-like object for writing files. File is actually only
31    written if the content changed.
32    """
33    def __init__(self, filename):
34        if os.path.exists(filename):
35            self.out = cStringIO.StringIO()
36            self.filename = filename
37        else:
38            dir_util.mkpath(os.path.dirname(filename))
39            self.out = open(filename, "w")
40            self.filename = None
41    def close(self):
42        if self.out.closed:
43            return
44        if self.filename:
45            with open(self.filename) as handle:
46                oldcontents = handle.read()
47            newcontents = self.out.getvalue()
48            if oldcontents != newcontents:
49                self.out = open(self.filename, "w")
50                self.out.write(newcontents)
51        self.out.close()
52    def __getattr__(self, name):
53        return getattr(self.out, name)
54    def __enter__(self):
55        return self
56    def __exit__(self, *a, **k):
57        self.close()
58    def __del__(self):
59        self.close()
60
61def create_shadertext(shaderdir, shaderdir2, outputheader, outputfile):
62
63    outputheader = openw(outputheader)
64    outputfile = openw(outputfile)
65
66    include_deps = defaultdict(set)
67    ifdef_deps = defaultdict(set)
68
69    # get all *.gs *.vs *.fs *.shared from the two input directories
70    shaderfiles = set()
71    for sdir in [shaderdir, shaderdir2]:
72        for ext in ['gs', 'vs', 'fs', 'shared']:
73            shaderfiles.update(map(os.path.basename,
74                sorted(glob.glob(os.path.join(sdir, '*.' + ext)))))
75
76    varname = '_shader_cache_raw'
77    outputheader.write('extern const char * %s[];\n' % varname)
78    outputfile.write('const char * %s[] = {\n' % varname)
79
80    for filename in sorted(shaderfiles):
81        shaderfile = os.path.join(shaderdir, filename)
82        if not os.path.exists(shaderfile):
83            shaderfile = os.path.join(shaderdir2, filename)
84
85        with open(shaderfile, 'r') as handle:
86            contents = handle.read()
87
88        if True:
89            outputfile.write('"%s", ""\n' % (filename))
90
91            for line in contents.splitlines():
92                line = line.strip()
93
94                # skip blank lines and obvious comments
95                if not line or line.startswith('//') and not '*/' in line:
96                    continue
97
98                # write line, quoted, escaped and with a line feed
99                outputfile.write("\"%s\\n\"\n" % line.replace('\\', '\\\\').replace('"', r'\"'))
100
101                # include and ifdef dependencies
102                if line.startswith('#include'):
103                    include_deps[line.split()[1]].add(filename)
104                elif line.startswith('#ifdef') or line.startswith('#ifndef'):
105                    ifdef_deps[line.split()[1]].add(filename)
106
107            outputfile.write(',\n')
108
109    outputfile.write('0};\n')
110
111    # include and ifdef dependencies
112    for varname, deps in [
113            ('_include_deps', include_deps),
114            ('_ifdef_deps', ifdef_deps)]:
115        outputheader.write('extern const char * %s[];\n' % varname)
116        outputfile.write('const char * %s[] = {\n' % varname)
117        for name, itemdeps in deps.items():
118            outputfile.write('"%s", "%s", 0,\n' % (name, '", "'.join(sorted(itemdeps))))
119        outputfile.write('0};\n')
120
121    outputheader.close()
122    outputfile.close()
123
124def create_buildinfo(outputdir, pymoldir='.'):
125
126    try:
127        sha = Popen(['git', 'rev-parse', 'HEAD'], cwd=pymoldir,
128                stdout=PIPE).stdout.read().strip().decode()
129    except OSError:
130        sha = ''
131
132    with openw(os.path.join(outputdir, 'PyMOLBuildInfo.h')) as out:
133        print('''
134#define _PyMOL_BUILD_DATE %d
135#define _PYMOL_BUILD_GIT_SHA "%s"
136        ''' % (time.time(), sha), file=out)
137
138if __name__ == "__main__":
139    create_shadertext(*sys.argv[1:6])
140    create_buildinfo(dirname(sys.argv[4]), dirname(dirname(sys.argv[1])))
141