~ubuntu-branches/ubuntu/oneiric/lightning-extension/oneiric-updates

« back to all changes in this revision

Viewing changes to mozilla/build/ConfigStatus.py

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2012-11-08 10:00:06 UTC
  • mfrom: (1.3.8)
  • Revision ID: package-import@ubuntu.com-20121108100006-xpf89hktfitzuqc3
Tags: 1.9+build1-0ubuntu0.11.10.1
* New upstream stable release to support Thunderbird 17 (CALENDAR_1_9_BUILD1)
  - see LP: #1080212 for USN information

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# This Source Code Form is subject to the terms of the Mozilla Public
 
2
# License, v. 2.0. If a copy of the MPL was not distributed with this
 
3
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
4
 
 
5
# Combined with build/autoconf/config.status.m4, ConfigStatus is an almost
 
6
# drop-in replacement for autoconf 2.13's config.status, with features
 
7
# borrowed from autoconf > 2.5, and additional features.
 
8
 
 
9
from __future__ import with_statement
 
10
from optparse import OptionParser
 
11
import sys, re, os, posixpath, ntpath
 
12
from StringIO import StringIO
 
13
# Standalone js doesn't have virtualenv.
 
14
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'config'))
 
15
from Preprocessor import Preprocessor
 
16
 
 
17
# Basic logging facility
 
18
verbose = False
 
19
def log(string):
 
20
    if verbose:
 
21
        print >>sys.stderr, string
 
22
 
 
23
# We need relpath, but it is introduced in python 2.6
 
24
# http://docs.python.org/library/os.path.html
 
25
def my_relpath(path, start):
 
26
    """
 
27
    Return a relative version of a path
 
28
    from /usr/lib/python2.6/posixpath.py
 
29
    """
 
30
 
 
31
    if not path:
 
32
        raise ValueError("no path specified")
 
33
 
 
34
    start_list = os.path.abspath(start).split(os.path.sep)
 
35
    path_list = os.path.abspath(path).split(os.path.sep)
 
36
 
 
37
    # Work out how much of the filepath is shared by start and path.
 
38
    i = len(os.path.commonprefix([start_list, path_list]))
 
39
 
 
40
    rel_list = [os.path.pardir] * (len(start_list)-i) + path_list[i:]
 
41
    if not rel_list:
 
42
        return os.curdir
 
43
    return os.path.join(*rel_list)
 
44
 
 
45
relpath = getattr(os.path, "relpath", my_relpath)
 
46
 
 
47
def ensureParentDir(file):
 
48
    '''Ensures the directory parent to the given file exists'''
 
49
    dir = os.path.dirname(file)
 
50
    if dir and not os.path.exists(dir):
 
51
        try:
 
52
            os.makedirs(dir)
 
53
        except OSError, error:
 
54
            if error.errno != errno.EEXIST:
 
55
                raise
 
56
 
 
57
class FileAvoidWrite(StringIO):
 
58
    '''file-like object that buffers its output and only writes it to disk
 
59
    if the new contents are different from what the file may already contain.
 
60
    '''
 
61
    def __init__(self, filename):
 
62
        self.filename = filename
 
63
        StringIO.__init__(self)
 
64
 
 
65
    def close(self):
 
66
        buf = self.getvalue()
 
67
        StringIO.close(self)
 
68
        try:
 
69
            file = open(self.filename, 'rU')
 
70
        except IOError:
 
71
            pass
 
72
        else:
 
73
            try:
 
74
                 if file.read() == buf:
 
75
                     log("%s is unchanged" % relpath(self.filename, os.curdir))
 
76
                     return
 
77
            except IOError:
 
78
                pass
 
79
            finally:
 
80
                file.close()
 
81
 
 
82
        log("creating %s" % relpath(self.filename, os.curdir))
 
83
        ensureParentDir(self.filename)
 
84
        with open(self.filename, 'w') as file:
 
85
            file.write(buf)
 
86
 
 
87
    def __enter__(self):
 
88
        return self
 
89
    def __exit__(self, type, value, traceback):
 
90
        self.close()
 
91
 
 
92
def shell_escape(s):
 
93
    '''Escape some characters with a backslash, and double dollar signs.
 
94
    '''
 
95
    return re.sub('''([ \t`#$^&*(){}\\|;'"<>?\[\]])''', r'\\\1', str(s)).replace('$', '$$')
 
96
 
 
97
class ConfigEnvironment(object):
 
98
    '''A ConfigEnvironment is defined by a source directory and a build
 
99
    directory. It preprocesses files from the source directory and stores
 
100
    the result in the object directory.
 
101
 
 
102
    There are two types of files: config files and config headers,
 
103
    each treated through a different member function.
 
104
 
 
105
    Creating a ConfigEnvironment requires a few arguments:
 
106
      - topsrcdir and topobjdir are, respectively, the top source and
 
107
        the top object directory.
 
108
      - defines is a list of (name, value) tuples. In autoconf, these are
 
109
        set with AC_DEFINE and AC_DEFINE_UNQUOTED
 
110
      - non_global_defines are a list of names appearing in defines above
 
111
        that are not meant to be exported in ACDEFINES and ALLDEFINES (see
 
112
        below)
 
113
      - substs is a list of (name, value) tuples. In autoconf, these are
 
114
        set with AC_SUBST.
 
115
 
 
116
    ConfigEnvironment automatically defines two additional substs variables
 
117
    from all the defines not appearing in non_global_defines:
 
118
      - ACDEFINES contains the defines in the form -DNAME=VALUE, for use on
 
119
        preprocessor command lines. The order in which defines were given
 
120
        when creating the ConfigEnvironment is preserved.
 
121
      - ALLDEFINES contains the defines in the form #define NAME VALUE, in
 
122
        sorted order, for use in config files, for an automatic listing of
 
123
        defines.
 
124
    and another additional subst variable from all the other substs:
 
125
      - ALLSUBSTS contains the substs in the form NAME = VALUE, in sorted
 
126
        order, for use in autoconf.mk. It includes ACDEFINES, but doesn't
 
127
        include ALLDEFINES.
 
128
 
 
129
    ConfigEnvironment expects a "top_srcdir" subst to be set with the top
 
130
    source directory, in msys format on windows. It is used to derive a
 
131
    "srcdir" subst when treating config files. It can either be an absolute
 
132
    path or a path relative to the topobjdir.
 
133
    '''
 
134
 
 
135
    def __init__(self, topobjdir = '.', topsrcdir = '.',
 
136
                 defines = [], non_global_defines = [], substs = []):
 
137
        self.defines = dict(defines)
 
138
        self.substs = dict(substs)
 
139
        self.topsrcdir = topsrcdir
 
140
        self.topobjdir = topobjdir
 
141
        global_defines = [name for name, value in defines if not name in non_global_defines]
 
142
        self.substs['ACDEFINES'] = ' '.join(["-D%s=%s" % (name, shell_escape(self.defines[name])) for name in global_defines])
 
143
        self.substs['ALLSUBSTS'] = '\n'.join(sorted(["%s = %s" % (name, self.substs[name]) for name in self.substs]))
 
144
        self.substs['ALLDEFINES'] = '\n'.join(sorted(["#define %s %s" % (name, self.defines[name]) for name in global_defines]))
 
145
 
 
146
    def get_relative_srcdir(self, file):
 
147
        '''Returns the relative source directory for the given file, always
 
148
        using / as a path separator.
 
149
        '''
 
150
        assert(isinstance(file, basestring))
 
151
        dir = posixpath.dirname(relpath(file, self.topobjdir).replace(os.sep, '/'))
 
152
        if dir:
 
153
            return dir
 
154
        return '.'
 
155
 
 
156
    def get_top_srcdir(self, file):
 
157
        '''Returns a normalized top_srcdir for the given file: if
 
158
        substs['top_srcdir'] is a relative path, it is relative to the
 
159
        topobjdir. Adjust it to be relative to the file path.'''
 
160
        top_srcdir = self.substs['top_srcdir']
 
161
        if posixpath.isabs(top_srcdir) or ntpath.isabs(top_srcdir):
 
162
            return top_srcdir
 
163
        return posixpath.normpath(posixpath.join(self.get_depth(file), top_srcdir))
 
164
 
 
165
    def get_file_srcdir(self, file):
 
166
        '''Returns the srcdir for the given file, where srcdir is in msys
 
167
        format on windows, thus derived from top_srcdir.
 
168
        '''
 
169
        dir = self.get_relative_srcdir(file)
 
170
        top_srcdir = self.get_top_srcdir(file)
 
171
        return posixpath.normpath(posixpath.join(top_srcdir, dir))
 
172
 
 
173
    def get_depth(self, file):
 
174
        '''Returns the DEPTH for the given file, that is, the path to the
 
175
        object directory relative to the directory containing the given file.
 
176
        Always uses / as a path separator.
 
177
        '''
 
178
        return relpath(self.topobjdir, os.path.dirname(file)).replace(os.sep, '/')
 
179
 
 
180
    def get_input(self, file):
 
181
        '''Returns the input file path in the source tree that can be used
 
182
        to create the given config file or header.
 
183
        '''
 
184
        assert(isinstance(file, basestring))
 
185
        return os.path.normpath(os.path.join(self.topsrcdir, "%s.in" % relpath(file, self.topobjdir)))
 
186
 
 
187
    def create_config_file(self, path):
 
188
        '''Creates the given config file. A config file is generated by
 
189
        taking the corresponding source file and replacing occurences of
 
190
        "@VAR@" by the value corresponding to "VAR" in the substs dict.
 
191
 
 
192
        Additional substs are defined according to the file being treated:
 
193
            "srcdir" for its the path to its source directory
 
194
            "relativesrcdir" for its source directory relative to the top
 
195
            "DEPTH" for the path to the top object directory
 
196
        '''
 
197
        input = self.get_input(path)
 
198
        pp = Preprocessor()
 
199
        pp.context.update(self.substs)
 
200
        pp.context.update(top_srcdir = self.get_top_srcdir(path))
 
201
        pp.context.update(srcdir = self.get_file_srcdir(path))
 
202
        pp.context.update(relativesrcdir = self.get_relative_srcdir(path))
 
203
        pp.context.update(DEPTH = self.get_depth(path))
 
204
        pp.do_filter('attemptSubstitution')
 
205
        pp.setMarker(None)
 
206
        with FileAvoidWrite(path) as pp.out:
 
207
            pp.do_include(input)
 
208
 
 
209
    def create_config_header(self, path):
 
210
        '''Creates the given config header. A config header is generated by
 
211
        taking the corresponding source file and replacing some #define/#undef
 
212
        occurences:
 
213
            "#undef NAME" is turned into "#define NAME VALUE"
 
214
            "#define NAME" is unchanged
 
215
            "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE"
 
216
            "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */"
 
217
            Whitespaces are preserved.
 
218
        '''
 
219
        with open(self.get_input(path), 'rU') as input:
 
220
            ensureParentDir(path)
 
221
            output = FileAvoidWrite(path)
 
222
            r = re.compile('^\s*#\s*(?P<cmd>[a-z]+)(?:\s+(?P<name>\S+)(?:\s+(?P<value>\S+))?)?', re.U)
 
223
            for l in input:
 
224
                m = r.match(l)
 
225
                if m:
 
226
                    cmd = m.group('cmd')
 
227
                    name = m.group('name')
 
228
                    value = m.group('value')
 
229
                    if name:
 
230
                        if name in self.defines:
 
231
                            if cmd == 'define' and value:
 
232
                                l = l[:m.start('value')] + str(self.defines[name]) + l[m.end('value'):]
 
233
                            elif cmd == 'undef':
 
234
                                l = l[:m.start('cmd')] + 'define' + l[m.end('cmd'):m.end('name')] + ' ' + str(self.defines[name]) + l[m.end('name'):]
 
235
                        elif cmd == 'undef':
 
236
                           l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name'):]
 
237
 
 
238
                output.write(l)
 
239
            output.close()
 
240
 
 
241
def config_status(topobjdir = '.', topsrcdir = '.',
 
242
                  defines = [], non_global_defines = [], substs = [],
 
243
                  files = [], headers = []):
 
244
    '''Main function, providing config.status functionality.
 
245
 
 
246
    Contrary to config.status, it doesn't use CONFIG_FILES or CONFIG_HEADERS
 
247
    variables, but like config.status from autoconf 2.6, single files may be
 
248
    generated with the --file and --header options. Several such options can
 
249
    be given to generate several files at the same time.
 
250
 
 
251
    Without the -n option, this program acts as config.status and considers
 
252
    the current directory as the top object directory, even when config.status
 
253
    is in a different directory. It will, however, treat the directory
 
254
    containing config.status as the top object directory with the -n option,
 
255
    while files given to the --file and --header arguments are considered
 
256
    relative to the current directory.
 
257
 
 
258
    The --recheck option, like with the original config.status, runs configure
 
259
    again, with the options given in the "ac_configure_args" subst.
 
260
 
 
261
    The options to this function are passed when creating the
 
262
    ConfigEnvironment, except for files and headers, which contain the list
 
263
    of files and headers to be generated by default. These lists, as well as
 
264
    the actual wrapper script around this function, are meant to be generated
 
265
    by configure. See build/autoconf/config.status.m4.
 
266
 
 
267
    Unlike config.status behaviour with CONFIG_FILES and CONFIG_HEADERS,
 
268
    but like config.status behaviour with --file and --header, providing
 
269
    files or headers on the command line inhibits the default generation of
 
270
    files when given headers and headers when given files.
 
271
 
 
272
    Unlike config.status, the FILE:TEMPLATE syntax is not supported for
 
273
    files and headers. The template is always the filename suffixed with
 
274
    '.in', in the corresponding directory under the top source directory.
 
275
    '''
 
276
 
 
277
    if 'CONFIG_FILES' in os.environ:
 
278
        raise Exception, 'Using the CONFIG_FILES environment variable is not supported. Use --file instead.'
 
279
    if 'CONFIG_HEADERS' in os.environ:
 
280
        raise Exception, 'Using the CONFIG_HEADERS environment variable is not supported. Use --header instead.'
 
281
 
 
282
    parser = OptionParser()
 
283
    parser.add_option('--recheck', dest='recheck', action='store_true',
 
284
                      help='update config.status by reconfiguring in the same conditions')
 
285
    parser.add_option('--file', dest='files', metavar='FILE', action='append',
 
286
                      help='instantiate the configuration file FILE')
 
287
    parser.add_option('--header', dest='headers', metavar='FILE', action='append',
 
288
                      help='instantiate the configuration header FILE')
 
289
    parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
 
290
                      help='display verbose output')
 
291
    parser.add_option('-n', dest='not_topobjdir', action='store_true',
 
292
                      help='do not consider current directory as top object directory')
 
293
    (options, args) = parser.parse_args()
 
294
 
 
295
    # Without -n, the current directory is meant to be the top object directory
 
296
    if not options.not_topobjdir:
 
297
        topobjdir = '.'
 
298
 
 
299
    env = ConfigEnvironment(topobjdir = topobjdir, topsrcdir = topsrcdir,
 
300
                            defines = defines, non_global_defines = non_global_defines,
 
301
                            substs = substs)
 
302
 
 
303
    if options.recheck:
 
304
        # Execute configure from the top object directory
 
305
        if not os.path.isabs(topsrcdir):
 
306
            topsrcdir = relpath(topsrcdir, topobjdir)
 
307
        os.chdir(topobjdir)
 
308
        os.execlp('sh', 'sh', '-c', ' '.join([os.path.join(topsrcdir, 'configure'), env.substs['ac_configure_args'], '--no-create', '--no-recursion']))
 
309
 
 
310
    if options.files:
 
311
        files = options.files
 
312
        headers = []
 
313
    if options.headers:
 
314
        headers = options.headers
 
315
        if not options.files:
 
316
            files = []
 
317
    # Default to display messages when giving --file or --headers on the
 
318
    # command line.
 
319
    if options.files or options.headers or options.verbose:
 
320
        global verbose
 
321
        verbose = True
 
322
    if not options.files and not options.headers:
 
323
        print >>sys.stderr, "creating config files and headers..."
 
324
        files = [os.path.join(topobjdir, f) for f in files]
 
325
        headers = [os.path.join(topobjdir, f) for f in headers]
 
326
 
 
327
    for file in files:
 
328
        env.create_config_file(file)
 
329
    for header in headers:
 
330
        env.create_config_header(header)