samba_bundled.py avoid inefficient string concatenations
[samba.git] / buildtools / wafsamba / samba_cross.py
1 # functions for handling cross-compilation
2
3 import os, sys, re, shlex
4 from waflib import Utils, Logs, Options, Errors, Context
5 from waflib.Configure import conf
6 from wafsamba import samba_utils
7
8 real_Popen = None
9
10 ANSWER_UNKNOWN = (254, "")
11 ANSWER_NO      = (1, "")
12 ANSWER_OK      = (0, "")
13
14 cross_answers_incomplete = False
15
16
17 def add_answer(ca_file, msg, answer):
18     '''add an answer to a set of cross answers'''
19     try:
20         f = open(ca_file, 'a')
21     except:
22         Logs.error("Unable to open cross-answers file %s" % ca_file)
23         sys.exit(1)
24     (retcode, retstring) = answer
25     # if retstring is more than one line then we probably
26     # don't care about its actual content (the tests should
27     # yield one-line output in order to comply with the cross-answer
28     # format)
29     retstring = retstring.strip()
30     if len(retstring.split('\n')) > 1:
31         retstring = ''
32     answer = (retcode, retstring)
33
34     if answer == ANSWER_OK:
35         f.write('%s: OK\n' % msg)
36     elif answer == ANSWER_UNKNOWN:
37         f.write('%s: UNKNOWN\n' % msg)
38     elif answer == ANSWER_NO:
39         f.write('%s: NO\n' % msg)
40     else:
41         if retcode == 0:
42             f.write('%s: "%s"\n' % (msg, retstring))
43         else:
44             f.write('%s: (%d, "%s")\n' % (msg, retcode, retstring))
45     f.close()
46
47
48 def cross_answer(ca_file, msg):
49     '''return a (retcode,retstring) tuple from a answers file'''
50     try:
51         f = open(ca_file, 'r')
52     except:
53         return ANSWER_UNKNOWN
54     for line in f:
55         line = line.strip()
56         if line == '' or line[0] == '#':
57             continue
58         if line.find(':') != -1:
59             a = line.split(':', 1)
60             thismsg = a[0].strip()
61             if thismsg != msg:
62                 continue
63             ans = a[1].strip()
64             if ans == "OK" or ans == "YES":
65                 f.close()
66                 return ANSWER_OK
67             elif ans == "UNKNOWN":
68                 f.close()
69                 return ANSWER_UNKNOWN
70             elif ans == "FAIL" or ans == "NO":
71                 f.close()
72                 return ANSWER_NO
73             elif ans[0] == '"':
74                 f.close()
75                 return (0, ans.strip('"'))
76             elif ans[0] == "'":
77                 f.close()
78                 return (0, ans.strip("'"))
79             else:
80                 m = re.match('\(\s*(-?\d+)\s*,\s*\"(.*)\"\s*\)', ans)
81                 if m:
82                     f.close()
83                     return (int(m.group(1)), m.group(2))
84                 else:
85                     raise Errors.WafError("Bad answer format '%s' in %s" % (line, ca_file))
86     f.close()
87     return ANSWER_UNKNOWN
88
89
90 class cross_Popen(Utils.subprocess.Popen):
91     '''cross-compilation wrapper for Popen'''
92     def __init__(*k, **kw):
93         (obj, args) = k
94         use_answers = False
95         ans = ANSWER_UNKNOWN
96
97         # Three possibilities:
98         #   1. Only cross-answers - try the cross-answers file, and if
99         #      there's no corresponding answer, add to the file and mark
100         #      the configure process as unfinished.
101         #   2. Only cross-execute - get the answer from cross-execute
102         #   3. Both - try the cross-answers file, and if there is no
103         #      corresponding answer - use cross-execute to get an answer,
104         #       and add that answer to the file.
105         if '--cross-answers' in args:
106             # when --cross-answers is set, then change the arguments
107             # to use the cross answers if available
108             use_answers = True
109             i = args.index('--cross-answers')
110             ca_file = args[i+1]
111             msg     = args[i+2]
112             ans = cross_answer(ca_file, msg)
113
114         if '--cross-execute' in args and ans == ANSWER_UNKNOWN:
115             # when --cross-execute is set, then change the arguments
116             # to use the cross emulator
117             i = args.index('--cross-execute')
118             newargs = shlex.split(args[i+1])
119             newargs.extend(args[0:i])
120             if use_answers:
121                 p = real_Popen(newargs,
122                                stdout=Utils.subprocess.PIPE,
123                                stderr=Utils.subprocess.PIPE)
124                 ce_out, ce_err = p.communicate()
125                 ans = (p.returncode, samba_utils.get_string(ce_out))
126                 add_answer(ca_file, msg, ans)
127             else:
128                 args = newargs
129
130         if use_answers:
131             if ans == ANSWER_UNKNOWN:
132                 global cross_answers_incomplete
133                 cross_answers_incomplete = True
134                 add_answer(ca_file, msg, ans)
135             (retcode, retstring) = ans
136             args = ['/bin/sh', '-c', "echo -n '%s'; exit %d" % (retstring, retcode)]
137         real_Popen.__init__(*(obj, args), **kw)
138
139
140 @conf
141 def SAMBA_CROSS_ARGS(conf, msg=None):
142     '''get exec_args to pass when running cross compiled binaries'''
143     if not conf.env.CROSS_COMPILE:
144         return []
145
146     global real_Popen
147     if real_Popen is None:
148         real_Popen  = Utils.subprocess.Popen
149         Utils.subprocess.Popen = cross_Popen
150
151     ret = []
152
153     if conf.env.CROSS_EXECUTE:
154         ret.extend(['--cross-execute', conf.env.CROSS_EXECUTE])
155
156     if conf.env.CROSS_ANSWERS:
157         if msg is None:
158             raise Errors.WafError("Cannot have NULL msg in cross-answers")
159         ret.extend(['--cross-answers', os.path.join(Context.launch_dir, conf.env.CROSS_ANSWERS), msg])
160
161     if ret == []:
162         raise Errors.WafError("Cannot cross-compile without either --cross-execute or --cross-answers")
163
164     return ret
165
166 @conf
167 def SAMBA_CROSS_CHECK_COMPLETE(conf):
168     '''check if we have some unanswered questions'''
169     global cross_answers_incomplete
170     if conf.env.CROSS_COMPILE and cross_answers_incomplete:
171         raise Errors.WafError("Cross answers file %s is incomplete" % conf.env.CROSS_ANSWERS)
172     return True