File: DoConf.py

package info (click to toggle)
aap 1.072-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 4,980 kB
  • ctags: 2,160
  • sloc: python: 15,113; makefile: 61; sh: 31
file content (341 lines) | stat: -rw-r--r-- 12,216 bytes parent folder | download | duplicates (2)
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# Part of the A-A-P recipe executive: configure command handling

# Copyright (C) 2002-2003 Stichting NLnet Labs
# Permission to copy and use this file is specified in the file COPYING.
# If this file is missing you can find it here: http://www.a-a-p.org/COPYING

import Global
from Util import *
from Message import *
from Conftest import CheckFunc, CheckHeader, CheckType, CheckLib, CheckBuilder


class ConfContext:
    """
    Context for configure tests.  Used for the _conf scope.
    """
    def __init__(self, vardict):
        """
        The "_conf" scope is used for variables, so that things like $CC and
        $LIBS can use the value from the _conf scope.
        """
        self.vardict = vardict
        self.havedict = {}
        self.headerfilename = "confdefs.h"
        self.headerclean = 0        # set to 1 when headerfilename has been
                                    # cleared
        self.default_lang = None

    def Display(self, msg):
        msg_info(Global.globals, msg, msgm_cont)

    def Log(self, msg):
        msg_log(Global.globals, msg, msgm_cont)

    def AppendLIBS(self, lib_name_list):
        from Scope import find_recdict

        # Find the recdict where "LIBS" is defined and obtain the old value.
        # Also obtain the old value of $_conf.LIBS.
        rd = find_recdict(Global.globals, "LIBS")
        if rd:
            oldval = rd.get("LIBS")
        else:
            oldval = None
        conf_oldval = Global.globals["_conf"].data.get("LIBS")

        # Append library/libraries to the old value.
        if oldval:
            newval = oldval
        else:
            newval = ""
        for k in lib_name_list:
            if newval:
                newval = newval + " "
            newval = newval + ("-l%s" % k)

        # Set the new value in the same scope.  Also set $_conf.LIBS, so that
        # ":conf write recipe" will use the new value.
        if rd:
            rd["LIBS"] = newval
        Global.globals["_conf"].data["LIBS"] = newval

        return [rd, oldval, conf_oldval]

    def SetLIBS(self, newval):
        # Get the recdict, and old values from the list that AppendLIBS()
        # returned.
        rd = newval[0]
        oldval = newval[1]
        conf_oldval = newval[2]

        # Get the current value of $LIBS in ret_oldval and store the value from
        # newval.
        if rd:
            ret_oldval = rd.get("LIBS")
            rd["LIBS"] = oldval
        else:
            ret_oldval = None

        # Get the current value of $_conf.LIBS in ret_conf_oldval and store
        # the value from newval.
        ret_conf_oldval = Global.globals["_conf"].get("LIBS")
        if conf_oldval is None:
            del Global.globals["_conf"].data["LIBS"]
        else:
            Global.globals["_conf"].data["LIBS"] = conf_oldval

        return [rd, ret_oldval, ret_conf_oldval]


    def BuildProg(self, text, ext):
        self.Log("\n")          # add a line break after "Checking..."

        # source -> object
        src = "conftest" + ext
        trg = "conftest" + Global.globals["_no"].OBJSUF
        res = self.RunAction("compile", text, src, trg)

        if not res:
            # object -> program
            src = trg
            if ext == ".cpp":
                src = src + "{buildaction = cxx_build}"
            trg = "conftest" + Global.globals["_no"].EXESUF
            res = self.RunAction("build", None, src, trg)

        try_delete(trg)
        return res

    def CompileProg(self, text, ext):
        self.Log("\n")          # add a line break after "Checking..."

        # source -> object
        src = "conftest" + ext
        trg = "conftest" + Global.globals["_no"].OBJSUF
        res = self.RunAction("compile", text, src, trg)
        try_delete(trg)
        return res

    def RunAction(self, action, text, src, trg):
        """
        Run action "action" with source "src" and target "trg".
        When "text" is not None write it to "src".
        Afterwards "src" is always deleted.
        Returns an empty string for success, an error message for failure.
        """
        save_message = Global.globals.get("MESSAGE")
        Global.globals["MESSAGE"] = ""
        save_sys_cmd_log = Global.sys_cmd_log
        Global.sys_cmd_log = " "

        try:
            from Commands import aap_do
            if text:
                f = open(src, "w")
                f.write(text)
                f.close()
            aap_do(0, Global.globals, "%s {target = %s} %s"
                                                          % (action, trg, src))
            msg = ""

            # If there is no error but there is output, log it (e.g., for a
            # warning).
            if Global.sys_cmd_log != " ": 
                msg_log(Global.globals, Global.sys_cmd_log)

        except UserError:
            msg = ((_("Failed to %s test program:") % action)
                                               + Global.sys_cmd_log)

        if save_message is None:
            del Global.globals["MESSAGE"]
        else:
            Global.globals["MESSAGE"] = save_message
        Global.sys_cmd_log = save_sys_cmd_log

        try_delete(src)
        return msg



def init_conf_dict(confdict):
    """
    Called to init a ConfContext() object and add it to the "_conf" scope
    "confdict".
    """
    # "confdict" is used for context.vardict, so that the variables are
    # available as $_conf.VAR.
    context = ConfContext(confdict)

    # _conf.context is used to access the ConfContext object
    confdict["context"] = context

    # _conf.have is a shortcut to _conf.context.havedict
    confdict["have"] = context.havedict


def doconf(line_nr, recdict, optiondict, argdictlist):
    """
    Do the configure checks.  Implementation of the ":conf" command.
    """
    from Work import getrpstack
    from Process import recipe_error
    rpstack = getrpstack(recdict, line_nr)

    # Get the ConfContext object from the _conf scope.
    context = recdict["_conf"].context

    command = argdictlist[0]["name"]

    # Init the configure stuff when not done already.
    # TODO: Is it possible that the file is truncated with an ":execute"
    # command?
    if not context.headerclean and command != "init":
        _init_conf(context)

    if command in [ "header", "function", "type", "lib" ]:
        #
        # :conf header stdlib.h ...
        # :conf function snprintf ...
        # :conf type size_t ...
        # :conf lib iconv,iconv_open ...
        #
        if len(argdictlist) < 2:
            recipe_error(rpstack, _('":conf %s" requires at least one more argument') % command)
        found = 0
        for i in range(1, len(argdictlist)):
            testarg = argdictlist[i]["name"]
            headerarg = argdictlist[i].get("header")
            langarg = argdictlist[i].get("language")
            if not langarg:
                langarg = context.default_lang

            if command == "header":
                msg = CheckHeader(context, testarg,
                                        header = headerarg, language = langarg)
            elif command == "type":
                fallbackarg = argdictlist[i].get("fallback")
                msg = CheckType(context, testarg, fallback = fallbackarg,
                                        header = headerarg, language = langarg)
            elif command == "lib":
                call = argdictlist[i].get("call")
                comma = string.find(testarg, ",")
                if comma < 0:
                    if not call:
                        recipe_error(rpstack,
                                _('":conf lib" requires an argument in the form libname,funcname.'))
                    lib_name = testarg
                    func_name = None
                else:
                    lib_name = testarg[0:comma]
                    func_name = testarg[comma+1:]
                msg = CheckLib(context, lib_name, func_name, call = call,
                                        header = headerarg, language = langarg)
            else: # command == "function"
                msg = CheckFunc(context, testarg,
                                        header = headerarg, language = langarg)

            if not msg:
                found = 1
                if optiondict.get("oneof"):
                    break
            elif optiondict.get("required"):
                recipe_error(rpstack, _('required %s "%s" not found.')
                                                          % (command, testarg))
        if not found and optiondict.get("oneof"):
            from Dictlist import dictlist2str
            recipe_error(rpstack,
                    _('None of the %ss found for ":conf {oneof} %s"')
                    % (command,
                       dictlist2str(argdictlist, Expand(0, Expand.quote_aap))))

    elif command == "write":
        #
        # :conf write header config.h
        # :conf write recipe config.aap
        #
        from Dictlist import dictlist_expand
        dictlist_expand(argdictlist)
        if len(argdictlist) != 3:
            recipe_error(rpstack, _('":conf write" requires two arguments'))
        what = argdictlist[1]["name"]
        fname = argdictlist[2]["name"]

        if what == "header":
            # We copy confdefs.h to the target file.  This makes sure the same
            # file that was used for testing is used.
            from CopyMove import remote_copy_move
            remote_copy_move(rpstack, recdict, 1,
                             [ {"name" : context.headerfilename} ],
                             { "name" : fname }, {}, 0, errmsg = 1)
        elif what == "recipe":
            try:
                f = open(fname, "w")
            except StandardError, e:
                recipe_error(rpstack, _('Could not create recipe "%s": %s')
                                                             % (fname, str(e)))
            try:
                f.write("# Generated by Aap.  You are not supposed to edit this file.\n\n")
                for k in context.vardict.keys():
                    if not k in ["have", "context"] and context.vardict[k]:
                        f.write("%s = %s\n" % (k, context.vardict[k]))
                f.close()
            except StandardError, e:
                recipe_error(rpstack, _('Could not write to recipe "%s": %s')
                                                             % (fname, str(e)))
            else:
                msg_info(recdict, 'Written config recipe "%s"' % fname)

        else:
            recipe_error(rpstack, _('Unsupported argument for ":conf write": "%s"') % what)

    elif command == "init":
        #
        # :conf init
        #
        if len(argdictlist) > 1:
            recipe_error(rpstack, _('Too many arguments for ":conf init"'))
        _init_conf(context)

    elif command == "language":
        #
        # :conf language C++
        #
        if len(argdictlist) != 2:
            recipe_error(rpstack, _('":conf language" requires one argument'))
        context.default_lang = argdictlist[1]["name"]
        # check for a valid language
        msg = CheckBuilder(context, language = context.default_lang)
        if msg:
            msg_warning(recdict, _('Cannot compile a simple %s program: %s')
                                                 % (context.default_lang, msg))

    else:
        recipe_error(rpstack, _('Unsupported :conf argument: "%s"') % command)


def _init_conf(context):
    """
    Init the configure stuff.  This makes sure the "headerfilename" is empty.
    """
    try:
        f = open(context.headerfilename, "w+")
        f.write("/* Generated by Aap.  You are not supposed to edit this file. */\n\n")
        f.close()
    except StandardError, e:
        msg_warning(Global.globals, _("Could not make %s empty: %s")
                                        % (context.headerfilename, str(e)))
    context.headerclean = 1


def cleanup(recdict):
    """
    Cleanup after doing configure checks.
    """
    # Get the ConfContext object from the _conf scope.
    context = recdict["_conf"].context

    try_delete(context.headerfilename)

# vim: set sw=4 et sts=4 tw=79 fo+=l: