~ubuntu-branches/ubuntu/quantal/enigmail/quantal-security

« back to all changes in this revision

Viewing changes to build/ConfigStatus.py

  • Committer: Package Import Robot
  • Author(s): Chris Coulson
  • Date: 2013-09-13 16:02:15 UTC
  • mfrom: (0.12.16)
  • Revision ID: package-import@ubuntu.com-20130913160215-u3g8nmwa0pdwagwc
Tags: 2:1.5.2-0ubuntu0.12.10.1
* New upstream release v1.5.2 for Thunderbird 24

* Build enigmail using a stripped down Thunderbird 17 build system, as it's
  now quite difficult to build the way we were doing previously, with the
  latest Firefox build system
* Add debian/patches/no_libxpcom.patch - Don't link against libxpcom, as it
  doesn't exist anymore (but exists in the build system)
* Add debian/patches/use_sdk.patch - Use the SDK version of xpt.py and
  friends
* Drop debian/patches/ipc-pipe_rename.diff (not needed anymore)
* Drop debian/patches/makefile_depth.diff (not needed anymore)

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)