OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
binji
2012/10/03 22:52:13
add license header
Sam Clegg
2012/10/04 22:57:57
Done.
| |
2 import os | |
3 import Queue | |
4 import shlex | |
5 import subprocess | |
6 import sys | |
7 import threading | |
8 import multiprocessing | |
9 import time | |
binji
2012/10/03 22:52:13
sort imports alphabetically
Sam Clegg
2012/10/04 22:57:57
Done.
| |
10 | |
11 """Python wrapper around gcc to make it behave a little | |
12 more like cl.exe WRT to parallel building. | |
13 """ | |
14 | |
15 verbose = int(os.environ.get('NACL_GCC_VERBOSE', '0')) | |
16 show_commands = int(os.environ.get('NACL_GCC_SHOW_COMMANDS', '0')) | |
17 cores = int(os.environ.get('NACL_GCC_CORES', '0')) | |
18 stop_on_error = False | |
19 | |
20 def RunGCC(cmd, basename): | |
21 cmdstring = subprocess.list2cmdline(cmd) | |
22 if show_commands: | |
binji
2012/10/03 22:52:13
nit: move down to use of logmsg
Sam Clegg
2012/10/04 22:57:57
Done.
| |
23 logmsg = cmdstring | |
24 else: | |
25 logmsg = basename | |
26 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
27 stdout, stderr = p.communicate() | |
28 p.wait() | |
29 stdout = logmsg + '\n' + stdout | |
30 return (p.returncode, stdout, stderr) | |
31 | |
32 | |
33 def BuildSerial(base_cmd, outpath, files): | |
binji
2012/10/03 22:52:13
Is this necessary? Why not always use BuildParalle
Sam Clegg
2012/10/04 22:57:57
Just here for debugging. This code path is not us
binji
2012/10/04 23:27:31
Looks like it will still run if cores==1... but it
| |
34 final_result = 0 | |
35 | |
36 for filename in files: | |
37 cmd, basename = MakeCommand(base_cmd, outpath, filename) | |
38 rtn, stdout, stderr = RunGCC(cmd, basename) | |
39 sys.stderr.write(stdout) | |
40 sys.stderr.write(stderr) | |
41 if rtn: | |
42 final_result = rtn | |
43 if stop_on_error: | |
44 break | |
45 | |
46 return final_result | |
47 | |
48 | |
49 q = Queue.Queue() | |
binji
2012/10/03 22:52:13
IMO, it is nicer to make these local to BuildParal
Sam Clegg
2012/10/04 22:57:57
Done.
| |
50 outq = Queue.Queue() | |
51 | |
52 | |
53 def Worker(): | |
54 while not q.empty(): | |
binji
2012/10/03 22:52:13
and Worker.running?
Sam Clegg
2012/10/04 22:57:57
Done.
| |
55 item = q.get(False) | |
56 if not item: | |
57 break | |
58 results = RunGCC(item[0], item[1]) | |
59 outq.put(results) | |
60 | |
61 | |
62 def MakeCommand(base_cmd, outpath, filename): | |
63 basename = os.path.basename(filename) | |
64 out = os.path.join(outpath, os.path.splitext(basename)[0] + '.obj') | |
binji
2012/10/03 22:52:13
use .o instead of .obj?
Sam Clegg
2012/10/04 22:57:57
Unfortunately we can't in this case as we need the
binji
2012/10/04 23:27:31
ok, comment would be nice. :)
| |
65 return (base_cmd + ['-c', filename, '-o', out], basename) | |
66 | |
67 | |
68 def BuildParallel(base_cmd, outpath, files): | |
69 pool = [] | |
70 | |
71 for filename in files: | |
72 cmd, basename = MakeCommand(base_cmd, outpath, filename) | |
73 q.put((cmd, basename)) | |
74 | |
75 Worker.running = True | |
76 for i in xrange(cores): | |
77 t = threading.Thread(target=Worker) | |
78 t.start() | |
79 | |
80 results = 0 | |
81 Trace("waiting for %d results" % len(files)) | |
82 final_result = 0 | |
83 while results < len(files): | |
84 results += 1 | |
85 rtn, stdout, stderr = outq.get() | |
86 sys.stderr.write(stdout) | |
binji
2012/10/03 22:52:13
why write stdout from compiler to stderr?
Sam Clegg
2012/10/04 22:57:57
Good question. Visual studio doesn't display stdo
binji
2012/10/04 23:27:31
OK, might add a comment then.
Sam Clegg
2012/10/05 21:11:19
Done.
| |
87 sys.stderr.write(stderr) | |
88 if rtn: | |
89 final_result = rtn | |
90 if stop_on_error: | |
91 # stop all workers | |
92 Worker.running = False | |
93 break | |
94 | |
95 return final_result | |
96 | |
97 | |
98 def Log(msg): | |
99 sys.stderr.write(str(msg) + '\n') | |
100 sys.stderr.flush() | |
101 | |
102 | |
103 def Trace(msg): | |
104 if verbose: | |
105 Log(msg) | |
106 | |
107 | |
108 def main(args): | |
109 global cores | |
110 if args[0][0] == '@': | |
111 rspfile = args[0][1:] | |
112 args = shlex.split(open(rspfile).read()) | |
113 | |
114 # find the last occurance of '--' in the argument | |
binji
2012/10/03 22:52:13
nit: s/occurance/occurrence/
Sam Clegg
2012/10/04 22:57:57
Done.
| |
115 # list an use that to signify the start of the | |
binji
2012/10/03 22:52:13
s/an/and/
Sam Clegg
2012/10/04 22:57:57
Done.
| |
116 # list of sources | |
117 index = list(reversed(args)).index('--') | |
118 index = len(args) - index | |
119 base_cmd = args[:index-1] | |
120 files = args[index:] | |
121 | |
122 # remove -o <path> from base_cmd | |
123 index = base_cmd.index('-o') | |
124 outpath = base_cmd[index+1] | |
125 del base_cmd[index+1] | |
126 del base_cmd[index] | |
127 | |
128 if not cores: | |
129 cores = multiprocessing.cpu_count() | |
130 | |
131 cores = min(cores, len(files)) | |
132 | |
133 Trace("compiling %d sources using %d threads" % (len(files), cores)) | |
134 | |
135 if cores == 1: | |
136 rtn = BuildSerial(base_cmd, outpath, files) | |
137 else: | |
138 rtn = BuildParallel(base_cmd, outpath, files) | |
139 | |
140 Trace("done build of %d sources" % (len(files))) | |
binji
2012/10/03 22:52:13
what about when the build fails?
Sam Clegg
2012/10/04 22:57:57
Well spotted. Amazingly (you are not going to bel
binji
2012/10/04 23:27:31
Haha, I actually meant the Trace is displaying tha
Sam Clegg
2012/10/05 21:11:19
Done.
| |
141 | |
142 | |
143 if __name__ == '__main__': | |
144 main(sys.argv[1:]) | |
OLD | NEW |