# 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 }, { 'mkdir' : 1, 'force' : 1}, 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: