,9Mc @sQdZdZddklZddkZddkZddkZddkZddkZ ddk Z ddk Z e i i Z e i iZe i iZe i iZe i iZe i iZe i iZeZdfdYZgZdZdZd fd YZ d e fd YZ!d e fdYZ"dZ#dfdYZ$dS(s Generic Taskmaster module for the SCons build engine. This module contains the primary interface(s) between a wrapping user interface and the SCons build engine. There are two key classes here: Taskmaster This is the main engine for walking the dependency graph and calling things to decide what does or doesn't need to be built. Task This is the base class for allowing a wrapping interface to decide what does or doesn't actually need to be done. The intention is for a wrapping interface to subclass this as appropriate for different types of behavior it may need. The canonical example is the SCons native Python interface, which has Task subclasses that handle its specific behavior, like printing "`foo' is up to date" when a top-level target doesn't need to be built, and handling the -c option by removing targets as its "build" action. There is also a separate subclass for suppressing this output when the -q option is used. The Taskmaster instantiates a Task object for each (set of) target(s) that it decides need to be evaluated and/or built. s=src/engine/SCons/Taskmaster.py 4629 2010/01/17 22:23:21 sconsi(tchainNtStatscBseZdZdZRS(sN A simple class for holding statistics about the disposition of a Node by the Taskmaster. If we're collecting statistics, each Node processed by the Taskmaster gets one of these attached, in which case the Taskmaster records its decision each time it processes the Node. (Ideally, that's just once per Node.) cCsCd|_d|_d|_d|_d|_d|_d|_dS(sp Instantiates a Taskmaster.Stats object, initializing all appropriate counters to zero. iN(t consideredtalready_handledtproblemt child_failedt not_builtt side_effectstbuild(tself((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt__init__Vs      (t__name__t __module__t__doc__R (((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRNssp%(considered)3d %(already_handled)3d %(problem)3d %(child_failed)3d %(not_built)3d %(side_effects)3d %(build)3d cCs>tidx'tD]}t|iit|GHqWdS(NcSstt|t|S((tcmptstr(tatb((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytns(t StatsNodestsorttfmttstatst__dict__R(tn((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt dump_statsmstTaskcBseZdZdZddZdZdZdZdZdZ d Z d Z e Z d Z d Zd ZdZdZeZdZdZdZddZdZdZRS(s Default SCons build engine task. This controls the interaction of the actual building of node and the rest of the engine. This is expected to handle all of the normally-customizable aspects of controlling a build, so any given application *should* be able to do what it wants by sub-classing this class and overriding methods as appropriate. If an application needs to customze something by sub-classing Taskmaster (or some other build engine class), we should first try to migrate that functionality into this class. Note that it's generally a good idea for sub-classes to call these methods explicitly to update state, etc., rather than roll their own interaction with Taskmaster from scratch. cCs2||_||_||_||_|idS(N(ttmttargetsttoptnodet exc_clear(R RRRR((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyR s     RcCs'd}||d||ii|fS(Ns %-20s %s %s t:(Rt trace_node(R tmethodRt descriptionR((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt trace_messagescCsdS(s Hook to allow the calling interface to display a message. This hook gets called as part of preparing a task for execution (that is, a Node to be built). As part of figuring out what Node should be built next, the actually target list may be altered, along with a message describing the alteration. The calling interface can subclass Task and provide a concrete implementation of this method to see those messages. N((R tmessage((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytdisplays cCs|ii}|o |i|id|in|i|iio#|i|iid|i_n|i di }|i x<|i D].}|i x|i D]}|i qWqWdS(s Called just before the task is executed. This is mainly intended to give the target Nodes a chance to unlink underlying files and make all necessary directories before the Action is actually called to build the targets. sTask.prepare()iN(RttracetwriteR$Rtexception_raiseR%R&tNoneRt get_executortpreparetget_action_targetsR(R tTtexecutorttts((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyR,s         cCs|iS(s>Fetch the target being built or updated by this task. (R(R ((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt get_targetscCs'dd}tiitii|tS(Ns;Direct use of the Taskmaster.Task class will be deprecated s in a future release.(tSConstWarningstwarntTaskmasterNeedsExecuteWarningtTrue(R tmsg((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt needs_executescCsf|ii}|o |i|id|inyid}x@|iD]5}|io|it|i qFd}PqFW|p|idi nWnt j o6t i d}tii|id|intiij o nhtiij o nMtj o@}tii|}|id|_t i |_ |nXdS(s Called to execute the task. This method is called from multiple threads in a parallel build, so only do thread safe stuff here. Do thread unsafe stuff in prepare(), executed() or failed(). sTask.execute()iiN(RR'R(R$RRtretrieve_from_cachet set_statet NODE_EXECUTEDtbuiltRt SystemExittsystexc_infoR3tErrorst ExplicitExittcodet UserErrort BuildErrort Exceptiontconvert_to_BuildError(R R.teverything_was_cachedR0t exc_valuetet buildError((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytexecutes4     #cCs|ii}|o |i|id|inxV|iD]K}|itjo2x|iD]}|i t q`W|i t q=q=WdS(s Called when the task has been successfully executed and the Taskmaster instance doesn't want to call the Node's callback methods. s!Task.executed_without_callbacks()N( RR'R(R$RRt get_statetNODE_EXECUTINGRR;t NODE_NO_STATER<(R R.R0t side_effect((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytexecuted_without_callbackss   cCs|ii}|o |i|id|inxt|iD]i}|itjoFx|iD]}|i t q`W|i t |i |i n|iq=WdS(sL Called when the task has been successfully executed and the Taskmaster instance wants to call the Node's callback methods. This may have been a do-nothing operation (to preserve build order), so we must check the node's state before deciding whether it was "built", in which case we call the appropriate Node method. In any event, we always call "visited()", which will handle any post-visit actions that must take place regardless of whether or not the target was an actual built target or a source Node. sTask.executed_with_callbacks()N(RR'R(R$RRRMRNRR;ROR<t push_to_cacheR=tvisited(R R.R0RP((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytexecuted_with_callbacks s     cCs|idS(s Default action when a task fails: stop the build. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). N(t fail_stop(R ((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytfailed'scCsx|ii}|o |i|id|in|ii|id|ii|iig|_d|_ dS(sI Explicit stop-the-build failure. This sets failure status on the target nodes and all of their dependent parent nodes. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). sTask.failed_stop()cSs |itS((R;t NODE_FAILED(R((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRAsiN( RR'R(R$Rtwill_not_buildRtstopt current_topR(R R.((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRU1s   cCsP|ii}|o |i|id|in|ii|iddS(sM Explicit continue-the-build failure. This sets failure status on the target nodes and all of their dependent parent nodes. Note: Although this function is normally invoked on nodes in the executing state, it might also be invoked on up-to-date nodes when using Configure(). sTask.failed_continue()cSs |itS((R;RW(R((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRZsN(RR'R(R$RRXR(R R.((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt fail_continueLs  cCs|ii}|o |i|id|in|i|_xE|iD]:}|iit x|i D]}|it qmWqJWdS(s Marks all targets in a task ready for execution. This is used when the interface needs every target Node to be visited--the canonical example being the "scons -c" option. sTask.make_ready_all()N( RR'R(R$RRt out_of_datet disambiguateR;RNR(R R.R0R1((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytmake_ready_all\s     cCsk|ii}|o |i|id|ing|_t}x|iD]}y9|ii |i p|i o |i }Wn<t j o0}tiid|d|id|inX|p|ii|t}qLqLW|oFxn|iD]4}|itx|iD]}|itqWqWn,x(|iD]}|i|itqFWdS(s Marks all targets in a task ready for execution if any target is not current. This is the default behavior for building only what's necessary. sTask.make_ready_current()RterrstrtfilenameN(RR'R(R$RR\tFalseRR]t make_readyt has_buildert always_buildt is_up_to_datetEnvironmentErrorR3RAREtstrerrorR`tappendR7R;RNRRStNODE_UP_TO_DATE(R R.tneeds_executingR0ReRJR1((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytmake_ready_currentls8   ,     c Cs!|ii}|o |i|id|int|i}|ii}h}x~|D]v}|io8|o |i|id|dn|i |nx+|iD] }|i |dd||s    cCs5x.|io#|i}g|_|i|qWdS(s Stops Taskmaster processing by not returning a next candidate. Note that we have to clean-up the Taskmaster candidate list because the cycle detection depends on the fact all nodes have been processed somehow. N(RtRXR*(R Rt((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytno_next_candidatebs    cCsx|iD]}|ittfjptt|t|ift|idjptt|t|ifxC|iD]8}|i djp"tt|t||i fqWq WdS(s Validate the content of the pending_children set. Assert if an internal error is found. This function is used strictly for debugging the taskmaster by checking that no invariants are violated. It is not used in normal operation. The pending_children set is used to detect cycles in the dependency graph. We call a "pending child" a child that is found in the "pending" state when checking the dependencies of its parent node. A pending child can occur when the Taskmaster completes a loop through a cycle. For example, lets imagine a graph made of three node (A, B and C) making a cycle. The evaluation starts at node A. The taskmaster first consider whether node A's child B is up-to-date. Then, recursively, node B needs to check whether node C is up-to-date. This leaves us with a dependency graph looking like: Next candidate Node A (Pending) --> Node B(Pending) --> Node C (NoState) ^ | | | +-------------------------------------+ Now, when the Taskmaster examines the Node C's child Node A, it finds that Node A is in the "pending" state. Therefore, Node A is a pending child of node C. Pending children indicate that the Taskmaster has potentially loop back through a cycle. We say potentially because it could also occur when a DAG is evaluated in parallel. For example, consider the following graph: Node A (Pending) --> Node B(Pending) --> Node C (Pending) --> ... | ^ | | +----------> Node D (NoState) --------+ / Next candidate / The Taskmaster first evaluates the nodes A, B, and C and starts building some children of node C. Assuming, that the maximum parallel level has not been reached, the Taskmaster will examine Node D. It will find that Node C is a pending child of Node D. In summary, evaluating a graph with a cycle will always involve a pending child at one point. A pending child might indicate either a cycle or a diamond-shaped DAG. Only a fraction of the nodes ends-up being a "pending child" of another node. This keeps the pending_children set small in practice. We can differentiate between the two cases if we wait until the end of the build. At this point, all the pending children nodes due to a diamond-shaped DAG will have been properly built (or will have failed to build). But, the pending children involved in a cycle will still be in the pending state. The taskmaster removes nodes from the pending_children set as soon as a pending_children node moves out of the pending state. This also helps to keep the pending_children set small. iN( Rntstatet NODE_PENDINGRNtAssertionErrorRt StateStringtlenRoRs(R RRx((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt_validate_pending_childrenpsG 5 cCsd|S(NsTaskmaster: %s ((R R%((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyR$scCs*dt|i|itt|fS(Ns<%-10s %-3s %s>(RRMRstreprR(R R((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyR!sc Csd|_|i}|o|id|idnxr|i}|djo-|o|i|iddndSn|i}|i}toJt |dpt |_ t i |n|i }|id|_nd}|o'|i|id|i|n|tjo|itnQ|tjoC|o|id|_n|o|i|idq:q:n|i}y|i}Wntj oitid}tii||i}tii|f|_|o|i|idn|Snitj o\}ti|_|o|id|_n|o|i|id |n|SnXg} t} g} t } xt!|i"|D]} | i}|o'|i|id |i| n|tjo| i | n6|tjo| i#| n|t$jo t%} n|t&jo| i | qqW| i'|i(i)|i*| | otx!|i+D]}|it$qW|o|i,d|_,n|o'|i|id |i|q:q:n| ox| D]z} |o|i-d|_-n|i.| i/||_.|o9|i|id |i|t0t1| fqqW|o8x5| D])}|i|id |i|qWn|i2| B|_2q:nt }x>|i3D]0}|it&jo|i4|t%}qqW|o"|o|i5d|_5q:q:n|o|i6d|_6n|o'|i|id|i|n|Sq:dS(sO Finds the next node that is ready to be built. This is *the* main guts of the DAG walk. We loop through the list of candidates, looking for something that has no un-built children (i.e., that is a leaf Node or has dependencies that are all leaf Nodes or up-to-date). Candidate Nodes are re-scanned (both the target Node itself and its sources, which are always scanned in the context of a given target) to discover implicit dependencies. A Node that must wait for some children to be built will be put back on the candidates list after the children have finished building. A Node that has been put back on the candidates list in this way may have itself (or its sources) re-scanned, in order to handle generated header files (e.g.) and the implicit dependencies therein. Note that this method does not do any signature calculation or up-to-date check itself. All of that is handled by the Task class. This is purely concerned with the dependency graph walk. s sLooking for a node to evaluatesNo candidate anymore.Ris) Considering node %s and its children:s! already handled (executed)s SystemExits- exception %s while scanning children. s s ****** %s s% adjusted ref count: %s, child %ss- adding %s to the pending children set sEvaluating %s N(7R*t ready_excR'R(R$RR]RMt CollectStatsthasattrRRRRhRR!ROR;RRR+tget_all_childrenR>R?R@R3RARBRCRFRRmRaRtget_all_prerequisitesRRWR7RNRRtRRR-RRRstadd_to_waiting_parentsRRRntget_action_side_effectstadd_to_waiting_s_eRR(R R.RRtSR/tchildrenRIRJtchildren_not_visitedtchildren_pendingtchildren_not_readytchildren_failedtchildt childstateRtpctwait_side_effectstse((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt_find_next_ready_nodes        '      '       '.   cCs|i}|djodSn|ii}|i||||ij|}y|iWnti|_ nX|i o|i |i nd|_ |S(s Returns the next task to be executed. This simply asks for the next Node to be evaluated, and then wraps it in the specific Task subclass with which we were initialized. N( RR*R+tget_all_targetsRRRbR?R@RR}(R Rttlistttask((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyt next_tasks  !  cCsdS(N(R*(R((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRsc Cse|i}|i}t|}||}|o8x5|D])}|i|id|i|q6Wnyxy|i}Wn<tj o0t|o|d}|i |qPnX|||i }t|_ ||B}||}xL|D]D} | i d| _ |o'|i|id|i| qqWqmWnt j onX||_dS(s Perform clean-up about nodes that will never be built. Invokes a user defined function on all of these nodes (including all of their parents). s6 removing node %s from the pending children set iis8 removing parent %s from the pending children set N( R'RnRmR(R$R!RtAttributeErrorRtremoveRoRstKeyError( R tnodest node_funcR.Rntto_visitRRRwRx((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRXs@           #cCs|i|_dS(s5 Stops the current build completely. N(RR(R ((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRYscCs|ipdSntd|i}td|}|pdSnd}xm|D]e\}}|o+|dtitt|dd}qU|d|t|t|if}qUWt i i |dS( s. Check for dependency cycles. NcSs|t|gtfS((RRm(R((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRscSs|dp|ditjS(ii(RMR<(R0((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyRssFound dependency cycle(s): s s -> s s> Internal Error: no cycle found for node %s (%s) in state %s ( RntmaptfiltertstringtjoinRRRRMR3RARD(R tnclisttgenuine_cyclestdescRtcycle((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pytcleanups  +%N(R R R R*R RRRR$R!RRRXRYR(((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pyR)s $  O   5 (%R t __revision__t itertoolsRtoperatorRR?t tracebackt SCons.ErrorsR3t SCons.NodetSCons.WarningsRRtno_stateROtpendingRRRNt up_to_dateRiRR<RVRWR*RRRRRRRRRR(((s5install/lib/scons-1.2.0.d20100117/SCons/Taskmaster.pys1s6