cca8bbeeed916ac1d4b16da3cacb583361de703a
[kai/samba.git] / buildtools / wafsamba / samba_headers.py
1 # specialist handling of header files for Samba
2
3 import Build, re, Task, TaskGen, shutil, sys
4 from samba_utils import *
5
6
7 def header_install_path(header, header_path):
8     '''find the installation path for a header, given a header_path option'''
9     if not header_path:
10         return ''
11     if not isinstance(header_path, list):
12         return header_path
13     for (p1, dir) in header_path:
14         for p2 in TO_LIST(p1):
15             if fnmatch.fnmatch(header, p2):
16                 return dir
17     # default to current path
18     return ''
19
20
21 re_header = re.compile('^\s*#\s*include[ \t]*"([^"]+)"', re.I | re.M)
22
23 # a dictionary mapping source header paths to public header paths
24 header_map = {}
25
26 def find_suggested_header(hpath):
27     '''find a suggested header path to use'''
28     base = os.path.basename(hpath)
29     ret = []
30     for h in header_map:
31         if os.path.basename(h) == base:
32             ret.append('<%s>' % header_map[h])
33             ret.append('"%s"' % h)
34     return ret
35
36 def create_public_header(task):
37     '''create a public header from a private one, output within the build tree'''
38     src = task.inputs[0].abspath(task.env)
39     tgt = task.outputs[0].bldpath(task.env)
40
41     if os.path.exists(tgt):
42         os.unlink(tgt)
43
44     relsrc = os_path_relpath(src, task.env.TOPDIR)
45
46     infile  = open(src, mode='r')
47     outfile = open(tgt, mode='w')
48     linenumber = 0
49
50     search_paths = [ '', task.env.RELPATH ]
51     for i in task.env.EXTRA_INCLUDES:
52         if i.startswith('#'):
53             search_paths.append(i[1:])
54
55     for line in infile:
56         linenumber += 1
57
58         # allow some straight substitutions
59         if task.env.public_headers_replace and line.strip() in task.env.public_headers_replace:
60             outfile.write(task.env.public_headers_replace[line.strip()] + '\n')
61             continue
62
63         # see if its an include line
64         m = re_header.match(line)
65         if m is None:
66             outfile.write(line)
67             continue
68
69         # its an include, get the header path
70         hpath = m.group(1)
71         if hpath.startswith("bin/default/"):
72             hpath = hpath[12:]
73
74         # some are always allowed
75         if task.env.public_headers_skip and hpath in task.env.public_headers_skip:
76             outfile.write(line)
77             continue
78
79         # work out the header this refers to
80         found = False
81         for s in search_paths:
82             p = os.path.normpath(os.path.join(s, hpath))
83             if p in header_map:
84                 outfile.write("#include <%s>\n" % header_map[p])
85                 found = True
86                 break
87         if found:
88             continue
89
90         # try to be nice to the developer by suggesting an alternative
91         suggested = find_suggested_header(hpath)
92         outfile.close()
93         os.unlink(tgt)
94         sys.stderr.write("%s:%u:Error: unable to resolve public header %s (maybe try one of %s)\n" % (
95             os.path.relpath(src, os.getcwd()), linenumber, hpath, suggested))
96         raise Utils.WafError("Unable to resolve header path '%s' in public header '%s' in directory %s" % (
97             hpath, relsrc, task.env.RELPATH))
98     infile.close()
99     outfile.close()
100
101
102 def PUBLIC_HEADERS(bld, public_headers, header_path=None, public_headers_install=True):
103     '''install some headers
104
105     header_path may either be a string that is added to the INCLUDEDIR,
106     or it can be a dictionary of wildcard patterns which map to destination
107     directories relative to INCLUDEDIR
108     '''
109     bld.SET_BUILD_GROUP('final')
110     if not bld.env.build_public_headers:
111         bld.env.build_public_headers = ''
112
113     # create the public header in the given path
114     # in the build tree
115     for h in TO_LIST(public_headers):
116         inst_path = header_install_path(h, header_path)
117         if h.find(':') != -1:
118             s = h.split(":")
119             h_name =  s[0]
120             inst_name = s[1]
121         else:
122             h_name =  h
123             inst_name = os.path.basename(h)
124         relpath1 = os_path_relpath(bld.srcnode.abspath(), bld.curdir)
125         relpath2 = os_path_relpath(bld.curdir, bld.srcnode.abspath())
126         targetdir = os.path.normpath(os.path.join(relpath1, bld.env.build_public_headers, inst_path))
127         if not os.path.exists(os.path.join(bld.curdir, targetdir)):
128             raise Utils.WafError("missing source directory %s for public header %s" % (targetdir, inst_name))
129         target = os.path.join(targetdir, inst_name)
130
131         # the source path of the header, relative to the top of the source tree
132         src_path = os.path.normpath(os.path.join(relpath2, h_name))
133
134         # the install path of the header, relative to the public include directory
135         target_path = os.path.normpath(os.path.join(inst_path, inst_name))
136
137         header_map[src_path] = target_path
138
139         t = bld.SAMBA_GENERATOR('HEADER_%s/%s/%s' % (relpath2, inst_path, inst_name),
140                                 group='prototypes',
141                                 rule=create_public_header,
142                                 source=h_name,
143                                 target=target)
144         t.env.RELPATH = relpath2
145         t.env.TOPDIR  = bld.srcnode.abspath()
146         if not bld.env.public_headers_list:
147             bld.env.public_headers_list = []
148         bld.env.public_headers_list.append(os.path.join(inst_path, inst_name))
149         if public_headers_install:
150             bld.INSTALL_FILES('${INCLUDEDIR}',
151                               target,
152                               destname=os.path.join(inst_path, inst_name), flat=True)
153 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS