File: compiler.py

package info (click to toggle)
distcc 3.1-6.1
  • links: PTS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 4,568 kB
  • ctags: 3,009
  • sloc: ansic: 19,204; python: 6,177; sh: 4,023; makefile: 862; perl: 52
file content (193 lines) | stat: -rwxr-xr-x 7,487 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# benchmark -- automated system for testing distcc correctness
# and performance on various source trees.

# Copyright (C) 2002, 2003 by Martin Pool
# Copyright 2008 Google Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
# USA.

import commands
import os
import shutil
import stat
import sys
import tempfile

import buildutil

STANDARD_CC_NAMES = ['cc', 'gcc']
STANDARD_CXX_NAMES = ['cxx', 'c++', 'g++' ]

def _find_executable(name):
    (rs, output) = commands.getstatusoutput('which "%s"' % name)
    if rs:
        sys.exit("Could not determine location of '%s'" % name)
    return output.strip()

class CompilerSpec:
    """Describes a compiler/make setup.

    Used to define different situations such as local compilation, and
    various degrees of parallelism."""

    def __init__(self, where, cc, cxx, prefix='', make_opts='',
                 pump_cmd='', num_hosts=1, host_opts='',
                 name=None):
        """Constructor:

        Args:
          where: 'local', 'dist', 'lzo', or 'pump'
          cc: location of the C compiler
          cxx: location of the C++
          prefix: a string, either 'distcc ' or ''
          make_opts: options to make, such as '-j120'
          host_opts: for appending to hosts in DISTCC_HOSTS
                     such as ',lzo,cpp'
          name: a string
        """
        self.where = where
        self.real_cc = _find_executable(cc)
        self.real_cxx = _find_executable(cxx)
        self.cc = prefix + self.real_cc
        self.cxx = prefix + self.real_cxx
        self.make_opts = make_opts
        self.host_opts = host_opts
        self.pump_cmd = pump_cmd
        self.num_hosts = num_hosts
        self.host_opts = host_opts
        self.name = name or (self.pump_cmd + self.real_cc + "__" +
                             self.make_opts).replace(' ', '_')

    def prepare_shell_script_farm(self, farm_dir, masquerade):
        """Prepare farm directory for masquerading.

        Assume the compiler is not local. Each standard name, such as
        'cc', is used for form a shell script, named 'cc', that
        contains the line 'distcc /my/path/gcc "$@"', where
        '/my/path/gcc' is the value of the compiler.gcc field.

        If the compiler is local, then the same procedure is followed
        except that 'distcc' is omitted from the command line.
        """
        assert os.path.isdir(farm_dir)
        assert os.path.isabs(farm_dir)

        def make_shell_script(name, compiler_path, where):
            fd = open(os.path.join(farm_dir, name), 'w')
            fd.write('#!/bin/sh\n%s%s "$@"'
                     % (where != 'local' and 'distcc ' or '',
                        compiler_path))
            fd.close()
            os.chmod(os.path.join(farm_dir, name),
                     stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)

        for generic_name in STANDARD_CC_NAMES:
            make_shell_script(generic_name, self.real_cc, self.where)

        for generic_name in STANDARD_CXX_NAMES:
            make_shell_script(generic_name, self.real_cxx, self.where)

        # Make shell wrapper to help manual debugging.
        fd = open(masquerade, 'w')
        fd.write("""\
#!/bin/sh
# Execute $@, but force 'cc' and 'cxx'" to be those in the farm of
# masquerading scripts.  Each script in turn executes 'distcc' with the actual
# compiler specified with the benchmark.py command.
PATH=%s:"$PATH" "$@"
""" % farm_dir)
        fd.close()
        os.chmod(masquerade,
                 stat.S_IXUSR | stat.S_IRUSR | stat.S_IWUSR)


def default_compilers(cc, cxx):
    return [parse_compiler_opt('local,h1,j1', cc, cxx),
            parse_compiler_opt('dist,h10,j20', cc, cxx),
            parse_compiler_opt('dist,h10,j40', cc, cxx),
            parse_compiler_opt('pump,h10,j20', cc, cxx),
            parse_compiler_opt('pump,h10,j40', cc, cxx),
            ]

def parse_compiler_opt(optarg, cc, cxx):
    """Parse command-line specification of a compiler (-c/--compiler).

    XXX: I don't really know what the best syntax for this is.  For
    the moment, it is "local", "dist", "lzo", or "pump", followed by ",h"
    and the number of hosts to use, followed by ",j" and the number
    of jobs to use (for the -j option to make).
    """
    where, hosts, jobs = optarg.split(',')
    if hosts.startswith("h"):
      hosts = int(hosts[1:])
      if not os.getenv("DISTCC_HOSTS"):
        raise ValueError, "You must set DISTCC_HOSTS before running benchmarks"
      max_hosts = buildutil.count_hosts(os.getenv("DISTCC_HOSTS"))
      if hosts > max_hosts:
        print ("Warning: can't use %d hosts: DISTCC_HOSTS only has %d" %
               (hosts, max_hosts))
        hosts = max_hosts
    else:
      raise ValueError, ("invalid compiler option: "
                         "expecting '...,h<NUMBER OF HOSTS>,...', found %s"
                         % `hosts`)
    if jobs.startswith("j"):
      jobs = int(jobs[1:])
    else:
      raise ValueError, ("invalid compiler option: "
                         "expecting '...,j<NUMBER OF JOBS>', found %s"
                         % `jobs`)
    if where == 'local':
        return CompilerSpec(where=where,
                            name='local_%02d' % jobs,
                            cc=cc,
                            cxx=cxx,
                            num_hosts=1,
                            make_opts='-j%d' % jobs)
    elif where == 'dist':
        return CompilerSpec(where=where,
                            name='dist_h%02d_j%02d' % (hosts, jobs),
                            cc=cc,
                            cxx=cxx,
                            prefix='distcc ',
                            num_hosts=hosts,
                            make_opts='-j%d' % jobs)
    elif where == 'lzo':
        return CompilerSpec(where=where,
                            name='lzo_h%02d_j%02d' % (hosts, jobs),
                            cc=cc,
                            cxx=cxx,
                            prefix='distcc ',
                            num_hosts=hosts,
                            host_opts=",lzo",
                            make_opts='-j%d' % jobs)
    elif where == 'pump':
        return CompilerSpec(where=where,
                            name='pump_h%02d_j%02d' % (hosts, jobs),
                            cc=cc,
                            cxx=cxx,
                            prefix='distcc ',
                            pump_cmd='pump ',
                            num_hosts=hosts,
                            host_opts=",cpp,lzo",
                            make_opts='-j%d' % jobs)
    else:
      raise ValueError, ("invalid compiler option: don't understand %s"
                         % `where`)


def prepare_shell_script_farm(compiler, farm_dir, masquerade):
    compiler.prepare_shell_script_farm(farm_dir, masquerade)