# Part of the A-A-P recipe executive: Add default rules and dependencies # 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 string import os.path from Action import find_primary_action from Dictlist import varname2dictlist, dictlist2str from RecPos import RecPos, rpcopy from Util import * from Depend import Depend from Message import * from Process import Process, recipe_error from ParsePos import ParsePos from Scope import get_build_recdict from DoBuild import find_autodep_items from RecPython import srcitem2obj, topdir def doadddef(work, recdict, toplevel): """ Add default dependencies, depending on what was defined in the recipe(s) read. """ # Add a dependency from $SOURCE and $TARGET if appropriate. add_source_target(work, recdict) # Add a comment to the useful targets we add here. caddlist = [] for n, c in [("install", _("install files to directory $PREFIX")), ("uninstall", _("delete installed files from directory $PREFIX")), ("clean", _("delete all generated files that are not distributed")), ("cleanmore", _("delete all generated files")), ("cleanALL", _("delete all generated files, AAPDIR and build-* directories"))]: if not work.find_node(n): caddlist.append((n, c)) # Add "clean" and "cleanmore" target if appropriate. add_clean(work, recdict) # At the toplevel we add more things. if toplevel: # Add the stuff for ports if $PORTNAME is defined. if recdict.get("PORTNAME"): from Port import add_port_defaults add_port_defaults(work) # Add "cleanALL" when not defined. add_optional_target(work, recdict, "cleanALL", [ "clean" ], ":tree . {dirname = AAPDIR}\n" " :del {f}{r} $name\n" ":tree . {dirname = build-.*}\n" " :del {f}{r} $name\n") # Add "install" and friends when not defined. # Also does the # "uninstall" variant. add_install_target(work, recdict, "install", [ "install-platform", "install-shared" ], "@if has_target('install-local'):\n" " :update install-local\n") add_install_target(work, recdict, "install-platform", [ "install-exec", "install-sbin", "install-lib", "install-dll", "install-ltlib", "install-conf" ], "@if has_target('install-platform-local'):\n" " :update install-platform-local\n") add_install_target(work, recdict, "install-shared", ["install-data", "install-man", "install-info", "install-include"], "@if has_target('install-shared-local'):\n" " :update install-shared-local\n") # These targets are all alike. for t in ["exec", "sbin", "lib", "dll", "ltlib", "conf", "data", "man", "info", "include"]: tup = string.upper(t) add_install_target(work, recdict, "install-%s" % t, [], "@if _top.get('INSTALL_%s'):\n" " :update $_top.INSTALL_%s\n" " :do install%s $_top.INSTALL_%s\n" % (tup, tup, t, tup)) # Add the comments after adding the dependencies. for n, c in caddlist: work.get_node(n, 1).set_attributes({"comment" : c}) def add_source_target(work, recdict): """ Add build dependencies for $SOURCE and $TARGET for backwards compatibility. """ # If $SOURCE and $TARGET are defined at the toplevel in "recdict", $TARGET # is one item and there is no dependency with $TARGET as target that # includes build commands, create one from $SOURCE and $TARGET. Can be # used for the main and a child recipe. if recdict.get("TARGET") and recdict.get("SOURCE"): targets = varname2dictlist(recdict, None, "TARGET") if len(targets) == 1: msg_warning(recdict, _("Warning: support for $SOURCE and $TARGET will be removed soon")) target = work.find_node(targets[0]["name"]) if not target or not target.get_first_build_dependency(): sources = varname2dictlist(recdict, None, "SOURCE") add_buildrule([RecPos("Default target")], work, recdict, "program", {}, targets, {}, sources) def add_clean(work, recdict): """ Add "clean" build dependency for the current recipe if there isn't one. Do the same for "cleanmore", but it also depends on "clean". """ for (trg, TRG, srclist) in [("clean", "CLEAN", []), ("cleanmore", "CLEANMORE", [{"name": "clean"}])]: target = work.find_node(trg) if not target or not target.get_recipe_build_dependency(recdict): cmd = ( ("@if _recipe.get('%sFILES'):\n" % TRG) + (" :del {f} $_recipe.%sFILES\n" % TRG) + ("@if _recipe.get('%sDIRS'):\n" % TRG) + (" :del {f}{r} $_recipe.%sDIRS\n" % TRG)) msg_depend(recdict, _('Adding default dependency for "%s"') % trg) rpstack = [RecPos("Default target")] dep = Depend([{"name" : trg}], {}, srclist, work, rpstack, cmd, recdict = recdict) work.add_dependency(rpstack, dep) # Add the generated recipes for automatic dependencies to $CLEANFILES. # Go through all the dependencies and handle the ones defined in this # recipe. # TODO: this doesn't find automatic dependencies from rules and assumes # dependencies are done recursively. for dep in work.dependencies: if (dep.buildrecdict and dep.buildrecdict["_recipe"] is recdict["_recipe"]): for src in dep.sourcelist: node = work.find_node(src["name"]) if node and not node.did_add_clean: node.did_add_clean = 1 ftype = node.get_ftype(recdict) if ftype and ftype != "ignore": action, recipe = find_autodep_items(work, recdict, ftype, node, {}) if action: add_cleanfiles(recdict, recipe.get_name()) # Remove duplicates from CLEANFILES. if recdict["_recipe"].get("CLEANFILES"): recdict["_recipe"]["CLEANFILES"] = rem_dup( recdict["_recipe"]["CLEANFILES"]) def add_install_target(work, recdict, targetname, sourcenames, cmd): """ Call add_optional_target() twice, the second time with "install" changed to "uninstall". """ add_optional_target(work, recdict, targetname, sourcenames, cmd) untargetname = re.sub("install", "uninstall", targetname) unsourcenames = map(lambda x: re.sub("install", "uninstall", x), sourcenames) # In the uninstall command change all "install" to "uninstall". # When using INSTALL.* remove the ":update" line and then double the # command to also use the UNINSTALL variable. uncmd = re.sub("install", "uninstall", cmd) if string.find(uncmd, "INSTALL") >= 0: uncmd = re.sub(":update.*\n", "", uncmd) uncmd = uncmd + re.sub("INSTALL", "UNINSTALL", uncmd) add_optional_target(work, recdict, untargetname, unsourcenames, uncmd) def add_optional_target(work, recdict, targetname, sourcenames, cmd): """ Add build dependency for "targetname" if there isn't one. To be used at the toplevel. """ target = work.find_node(targetname) if target and target.get_dependencies(): msg_depend(recdict, _('Not adding default dependency for "%s"') % targetname) else: rpstack = [RecPos("Default target")] if sourcenames: sourcelist = map(lambda x: {"name": x}, sourcenames) else: sourcelist = [] dep = Depend([{"name" : targetname}], {}, sourcelist, work, rpstack, cmd, recdict = recdict) work.add_dependency(rpstack, dep) def add_buildrule(rpstack, work, recdict, type, cmd_attr, targetlist, build_attr, sourcelist): """ Add a buildrule. Also used for ":totype". "type" is "program", "lib", "ltlib", "dll", "totype" or the first argument of ":produce". "cmd_attr" are extra attributes for the command itself. "targetlist" is a dictlist with the target(s). "build_attr" are extra attributes for the build commands. "sourcelist" is a dictlist with the sources. """ # TODO: implement "cmd_attr". if type != "totype": target = work.find_node(targetlist[0]["name"]) if target and target.get_first_build_dependency(): msg_error(recdict, _("Both a build rule and a dependency with build commands defined for '%s'") % targetlist[0]["name"]) return # Create a node for all items in sourcelist and targetlist, makes # sure the attributes are carried over. work.add_dictlist_nodes(sourcelist) if type != "totype": work.add_dictlist_nodes(targetlist) # When there is no direct build action for the source or there # are multiple sources, compile each source into an object and # build all the objects into the target. installvar = cmd_attr.get("installvar") onestep = cmd_attr.get("onestep") cleanfiles = [] if type == "dll": if installvar is None: installvar = "INSTALL_DLL" elif type == "lib": if installvar is None: installvar = "INSTALL_LIB" elif type == "ltlib": if installvar is None: installvar = "INSTALL_LTLIB" elif type == "program": if installvar is None: installvar = "INSTALL_EXEC" if onestep and type == "ltlib": msg_warning(recdict, _("The 'onestep' attribute is not supported for the :ltlib target."), rpstack = rpstack) onestep = None if onestep: add_onestep_build(rpstack, work, recdict, type, cmd_attr, targetlist, build_attr, sourcelist) else: a = cmd_attr.get("objecttype") if a: objtypes = string.split(a, ',') else: objtypes = None buildaction = cmd_attr.get("buildaction") if type == "dll": if not buildaction: buildaction = "builddll" elif type == "lib": if not buildaction: buildaction = "buildlib" elif type == "ltlib": if not buildaction: buildaction = "buildltlib" elif type == "program": if not buildaction: buildaction = "build" elif type == "totype": objtypes = [ targetlist[0]["name"] ] else: if not buildaction: recipe_error(rpstack, _("Missing buildaction attribute")) if installvar is None: installvar = "INSTALL_EXEC" if not objtypes: # Obtain the supported object types from the buildaction. act = find_primary_action(buildaction) if not act: if type not in ["dll", "lib", "ltlib", "program"]: recipe_error(rpstack, _("Missing objecttype attribute")) else: recipe_error(rpstack, _("No primary %s action defined") % buildaction) objtypes = act.get_in_types() objectsuffix = cmd_attr.get("objectsuffix") if type == "totype" and targetlist[0].get("suffix"): objectsuffix = targetlist[0].get("suffix") objectprefix = cmd_attr.get("objectprefix") if type == "totype" and targetlist[0].get("prefix"): objectprefix = targetlist[0].get("prefix") # Go over all source files and figure out a way to turn each one # into an object file. build_obj = [] for si in sourcelist: node = work.get_node(si["name"], 1, si) in_ftype = node.get_ftype(recdict) if in_ftype in objtypes: # The source is supported by the build action: add it directly. build_obj.append(si) else: # Loop over the types that the build action supports, turn # the source in the first one that is possible. names = '' objname = None for objtype in objtypes: if objtype == "default": continue objname = add_build_one(rpstack, work, recdict, node, si, in_ftype, objtype, objectprefix, objectsuffix, build_attr, cleanfiles) if objname: break if names: names = names + '/' names = names + objtype if objname is None: recipe_error(rpstack, _('Do not know how to make an %s out of "%s"') % (names, si["name"])) build_obj.append({"name" : objname}) if type != "totype": targets_str = dictlist2str(targetlist) if not onestep: # Add a dependency to build the program or library in the form: # targetlist : {buildcheck = xxx} sourcelist # do buildaction {target = $target} $source build_obj_str = dictlist2str(build_obj) cmd = (" :do %s {target = $+target} $source" % buildaction) msg_depend(recdict, _('Adding dependency:\n\t%s : %s\n\t%s') % (targets_str, build_obj_str, cmd)) work.add_dependency(rpstack, Depend(targetlist, build_attr, build_obj, work, rpstack, cmd + '\n', recdict = recdict)) cleanfiles.extend(targetlist) # Add target to INSTALL_EXEC, INSTALL_LIB or INSTALL_DLL. # do that relative to the top directory. if installvar: append2var(recdict, "_top", installvar, topdir(targets_str)) # Add files to be cleaned to _recipe.CLEANFILES. add_cleanfiles(recdict, dictlist2str(cleanfiles, Expand(0))) # Add files to be distributed to _recipe.DISTFILES. Skip the ones with a # {nodist} attribute. l = filter(lambda x: not x.get("nodist"), sourcelist) add_distfiles(recdict, dictlist2str(l, Expand(0))) def add_build_one(rpstack, work, recdict, node, si, in_ftype, objtype, objectprefix, objectsuffix, build_attr, cleanfiles): """ Add build rule to turn one source node into a file of type "objtype". If successful return name of target file, None otherwise. """ sufname = None if not objectsuffix: if objtype == "dllobject": sufname = "DLLOBJSUF" elif objtype == "libobject": sufname = "LIBOBJSUF" elif objtype == "ltobject": sufname = "LTOBJSUF" elif objtype == "object": sufname = "OBJSUF" # Make the object name by prepending $BDIR and changing the # extension to $OBJSUF/$LIBOBJSUF/$DLLOBJSUF/nothing. n = srcitem2obj(recdict, si["name"], attrdict = si, sufname = sufname) if objectprefix: n = os.path.join(os.path.dirname(n), objectprefix + os.path.basename(n)) if objectsuffix: n = n + objectsuffix # If there is no build rule for the object file, add one. target = work.find_node(n) if target and target.get_first_build_dependency(): # There already is a dependency for this target. if len(si) > 1: msg_warning(recdict, _('%s: Ignoring attributes for "%s", a build rule already exists') % (str(rpstack[-1]), si["name"])) else: # Find the route to the object file. route = work.find_route(in_ftype, objtype, use_actions = 1) if not route: return None # Add attributes from ":route" to the target node. work.get_node(n, add = 1, dict = route.targetattr) # Inits to avoid a warning from pychecker. trg = None out_ftype = None # Loop over all steps in the route. for i in range(len(route.steplist)): # Get the action and filename by processing the line. # TODO: catch errors route.rpstack[-1].line_nr = route.lnumlist[i] - 1 fp = ParsePos(route.rpstack, string = "_x = " + route.steplist[i] + '\n') rd = get_build_recdict(recdict, route.recdict, route.rpstack, keep_current_scope = 1, xscope = build_attr.get("scope")) rd["source"] = si["name"] Process(fp, rd, 0) l = string.split(rd["_x"], None, 1) action = l[0] if i == 0: src_dict = si.copy() src = si["name"] dnode = node else: src_dict = {"name" : trg} src = trg in_ftype = out_ftype dnode = work.find_node(src) # Set "depdir" for the auto-depend recipe. Required if the source # file is built twice with different $BDIR. Also add the recipe to # the files to be cleaned now, because $BDIR may change afterwards. if not src_dict.get("depdir"): src_dict["depdir"] = get_var_val_int(recdict, "BDIR") depaction, recipe = find_autodep_items(work, recdict, in_ftype, dnode, src_dict) if depaction: add_cleanfiles(recdict, recipe.get_name()) if i == len(route.steplist) - 1: trg = n out_ftype = objtype else: trg = l[1] out_ftype = route.typelist[i + 1][0] if (not os.path.isabs(trg) and string.find(trg, "build-") != 0): trg = os.path.join(get_var_val_int(recdict, "BDIR"), trg) # Add a dependency to execute the action. cmd = " :do %s {target = %s} %s" % (action, trg, src) msg_depend(recdict, _('Adding dependency:\n\t%s : %s\n\t%s') % (trg, dictlist2str([src_dict]), cmd)) src_dict["filetype"] = in_ftype trg_dict = {"name": trg, "filetype" : out_ftype} work.add_dependency(rpstack, Depend([ trg_dict ], build_attr, [ src_dict ], work, rpcopy(route.rpstack, route.lnumlist[i]), cmd + '\n', recdict = recdict)) cleanfiles.append({"name": trg}) return n def add_onestep_build(rpstack, work, recdict, type, cmd_attr, targetlist, build_attr, sourcelist): """Adds an onestep build""" buildaction = cmd_attr.get("buildaction") if type == "dll": if not buildaction: buildaction = "builddllonestep" elif type == "lib": if not buildaction: buildaction = "buildlibonestep" elif type == "program": if not buildaction: buildaction = "buildonestep" cmd = (" :do %s {target =$+target} $source" % buildaction) work.add_dependency(rpstack, Depend(targetlist, build_attr, sourcelist, work, rpstack, cmd + '\n', recdict = recdict)) # vim: set sw=4 et sts=4 tw=79 fo+=l: