B
    ›³ëbº@  ã               @   s¾   d Z ddlZddlZddlZddlZddlZdZdZdZ	G dd„ dƒZ
G dd„ dƒZG d	d
„ d
ƒZyddlZddlZW n ek
rŠ   Y n0X G dd„ dejƒZG dd„ dƒZG dd„ dƒZdS )zˆSerial and Parallel classes to execute build tasks.

The Jobs class provides a higher level interface to start,
stop, and wait on jobs.
é    Né   zBuild interrupted.c               @   s$   e Zd Zdd„ Zdd„ Zdd„ ZdS )ÚInterruptStatec             C   s
   d| _ d S )NF)Úinterrupted)Úself© r   ú(lib/python3.7/site-packages/SCons/Job.pyÚ__init__5   s    zInterruptState.__init__c             C   s
   d| _ d S )NT)r   )r   r   r   r   Úset8   s    zInterruptState.setc             C   s   | j S )N)r   )r   r   r   r   Ú__call__;   s    zInterruptState.__call__N)Ú__name__Ú
__module__Ú__qualname__r   r	   r
   r   r   r   r   r   4   s   r   c               @   s@   e Zd ZdZdd„ Zdd„ fdd„Zdd	„ Zd
d„ Zdd„ ZdS )ÚJobsz~An instance of this class initializes N jobs, and provides
    methods for starting, stopping, and waiting on all N jobs.
    c             C   sj   d| _ |dkrLt}|dkrt}yt|||ƒ| _ || _W n tk
rJ   Y nX | j dkrft|ƒ| _ d| _dS )aó  
        Create 'num' jobs using the given taskmaster.

        If 'num' is 1 or less, then a serial job will be used,
        otherwise a parallel job with 'num' worker threads will
        be used.

        The 'num_jobs' attribute will be set to the actual number of jobs
        allocated.  If more than one job is requested but the Parallel
        class can't do it, it gets reset to 1.  Wrapping interfaces that
        care should check the value of 'num_jobs' after initialization.
        Né   )ÚjobÚexplicit_stack_sizeÚdefault_stack_sizeÚParallelZnum_jobsÚ	NameErrorÚSerial)r   ÚnumÚ
taskmasterÚ
stack_sizer   r   r   r   D   s    


zJobs.__init__c               C   s   d S )Nr   r   r   r   r   Ú<lambda>a   ó    zJobs.<lambda>c             C   s,   |   ¡  z| j ¡  W d|ƒ  |  ¡  X dS )ap  Run the jobs.

        postfunc() will be invoked after the jobs has run. It will be
        invoked even if the jobs are interrupted by a keyboard
        interrupt (well, in fact by a signal such as either SIGINT,
        SIGTERM or SIGHUP). The execution of postfunc() is protected
        against keyboard interrupts and is guaranteed to run to
        completion.N)Ú_setup_sig_handlerr   ÚstartÚ_reset_sig_handler)r   Zpostfuncr   r   r   Úruna   s
    	zJobs.runc             C   s
   | j  ¡ S )z6Returns whether the jobs were interrupted by a signal.)r   r   )r   r   r   r   Úwere_interruptedq   s    zJobs.were_interruptedc             C   sž   | t  ¡ fdd„}t tj|¡| _t tj|¡| _yt tj|¡| _W n t	k
rZ   Y nX | jdks„| jdks„t
| dƒrš| jdkršd}tj tjj|¡ dS )a‰  Setup an interrupt handler so that SCons can shutdown cleanly in
        various conditions:

          a) SIGINT: Keyboard interrupt
          b) SIGTERM: kill or system shutdown
          c) SIGHUP: Controlling shell exiting

        We handle all of these cases by stopping the taskmaster. It
        turns out that it's very difficult to stop the build process
        by throwing asynchronously an exception such as
        KeyboardInterrupt. For example, the python Condition
        variables (threading.Condition) and queues do not seem to be
        asynchronous-exception-safe. It would require adding a whole
        bunch of try/finally block and except KeyboardInterrupt all
        over the place.

        Note also that we have to be careful to handle the case when
        SCons forks before executing another process. In that case, we
        want the child to exit immediately.
        c             S   s4   t  ¡ |kr&|jj ¡  |jj ¡  n
t  d¡ d S )Né   )ÚosÚgetpidr   r   Ústopr   r	   Ú_exit)ZsignumÚstackr   Z	parentpidr   r   r   ÚhandlerŠ   s    z(Jobs._setup_sig_handler.<locals>.handlerNÚ
old_sighupzŽOverwritting previous signal handler which was not installed from Python. Will not be able to reinstate and so will return to default handler.)r!   r"   ÚsignalÚSIGINTÚ
old_sigintÚSIGTERMÚold_sigtermÚSIGHUPr'   ÚAttributeErrorÚhasattrÚSConsÚWarningsÚwarnZSConsWarning)r   r&   Úmsgr   r   r   r   u   s    zJobs._setup_sig_handlerc             C   sŠ   | j dk	r| j ntj}| jdk	r&| jntj}t tj|¡ t tj|¡ y(| jdk	rZ| jntj}t tj|¡ W n tk
r„   Y nX dS )zfRestore the signal handlers to their previous state (before the
         call to _setup_sig_handler().N)	r*   r(   ÚSIG_DFLr,   r)   r+   r'   r-   r.   )r   Zsigint_to_useZsigterm_to_user   r   r   r      s    zJobs._reset_sig_handlerN)	r   r   r   Ú__doc__r   r   r   r   r   r   r   r   r   r   ?   s   (r   c               @   s    e Zd ZdZdd„ Zdd„ ZdS )r   z÷This class is used to execute tasks in series, and is more efficient
    than Parallel, but is only appropriate for non-parallel builds. Only
    one instance of this class should be in existence at a time.

    This class is not thread safe.
    c             C   s   || _ tƒ | _dS )aŽ  Create a new serial job given a taskmaster.

        The taskmaster's next_task() method should return the next task
        that needs to be executed, or None if there are no more tasks. The
        taskmaster's executed() method will be called for each task when it
        is successfully executed, or failed() will be called if it failed to
        execute (e.g. execute() raised an exception).N)r   r   r   )r   r   r   r   r   r   ²   s    	zSerial.__init__c          	   C   s´   x¤| j  ¡ }|dkrP y| ¡  | ¡ r0| ¡  W n^ tk
r   |  ¡ r|ytjj	|j
d td‚W q„   | ¡  Y q„X n| ¡  | ¡  Y n
X | ¡  | ¡  qW | j  ¡  dS )zîStart the job. This will begin pulling tasks from the taskmaster
        and executing them, and return when there are no more tasks. If a task
        fails to execute (i.e. execute() raises an exception), then the job will
        stop.Nr   )Úerrstr)r   Ú	next_taskÚprepareÚneeds_executeÚexecuteÚ	Exceptionr   r0   ÚErrorsÚ
BuildErrorÚtargetsÚinterrupt_msgÚexception_setÚfailedÚexecutedÚpostprocessÚcleanup)r   Útaskr   r   r   r   ¾   s(    
zSerial.startN)r   r   r   r5   r   r   r   r   r   r   r   ª   s   r   c                   s(   e Zd ZdZ‡ fdd„Zdd„ Z‡  ZS )ÚWorkerzÙA worker thread waits on a task to be posted to its request queue,
        dequeues the task, executes it, and posts a tuple including the task
        and a boolean indicating whether the task executed successfully. c                s.   t ƒ  ¡  d| _|| _|| _|| _|  ¡  d S )NT)Úsuperr   ZdaemonÚrequestQueueÚresultsQueuer   r   )r   rH   rI   r   )Ú	__class__r   r   r   ð   s    
zWorker.__init__c             C   sv   xp| j  ¡ }|d krP y*|  ¡ r6tjj|jd td‚| ¡  W n   | 	¡  d}Y nX d}| j
 ||f¡ qW d S )Nr   )r6   FT)rH   Úgetr   r0   r<   r=   r>   r?   r:   r@   rI   Úput)r   rE   Úokr   r   r   r   ø   s    

z
Worker.run)r   r   r   r5   r   r   Ú__classcell__r   r   )rJ   r   rF   ë   s   rF   c               @   s8   e Zd ZdZdd„ Zdd„ Zdd„ Zdd	„ Zd
d„ ZdS )Ú
ThreadPoolzCThis class is responsible for spawning and managing worker threads.c       	   
   C   s   t  d¡| _t  d¡| _yt |d ¡}W nˆ tk
rt } z,tdk	rdd|jd  }t	j
 t	j
j|¡ W dd}~X Y n@ tk
r² } z"dt|ƒ }t	j
 t	j
j|¡ W dd}~X Y nX g | _x,t|ƒD ] }t| j| j|ƒ}| j |¡ qÄW dtƒ krüt |¡ dS )zÅCreate the request and reply queues, and 'num' worker threads.

            One must specify the stack size of the worker threads. The
            stack size is specified in kilobytes.
            r   i   NzASetting stack size is unsupported by this version of Python:
    zSetting stack size failed:
    Ú	prev_size)ÚqueueZQueuerH   rI   Ú	threadingr   r.   r   Úargsr0   r1   r2   ZStackSizeWarningÚ
ValueErrorÚstrÚworkersÚrangerF   ÚappendÚlocals)	r   r   r   r   rP   Úer3   Ú_Úworkerr   r   r   r     s$    "$
zThreadPool.__init__c             C   s   | j  |¡ dS )zPut task into request queue.N)rH   rL   )r   rE   r   r   r   rL   1  s    zThreadPool.putc             C   s
   | j  ¡ S )z8Remove and return a result tuple from the results queue.)rI   rK   )r   r   r   r   rK   5  s    zThreadPool.getc             C   s   | j  |df¡ d S )NF)rI   rL   )r   rE   r   r   r   Úpreparation_failed9  s    zThreadPool.preparation_failedc             C   s@   x| j D ]}| j d¡ qW x| j D ]}| d¡ q$W g | _ dS )z}
            Shuts down the thread pool, giving each worker thread a
            chance to shut down gracefully.
            Ng      ð?)rV   rH   rL   Újoin)r   r[   r\   r   r   r   rD   <  s
    	zThreadPool.cleanupN)	r   r   r   r5   r   rL   rK   r]   rD   r   r   r   r   rO     s   rO   c               @   s    e Zd ZdZdd„ Zdd„ ZdS )r   z¹This class is used to execute tasks in parallel, and is somewhat
        less efficient than Serial, but is appropriate for parallel builds.

        This class is thread safe.
        c             C   s(   || _ tƒ | _t||| jƒ| _|| _dS )a±  Create a new parallel job given a taskmaster.

            The taskmaster's next_task() method should return the next
            task that needs to be executed, or None if there are no more
            tasks. The taskmaster's executed() method will be called
            for each task when it is successfully executed, or failed()
            will be called if the task failed to execute (i.e. execute()
            raised an exception).

            Note: calls to taskmaster are serialized, but calls to
            execute() on distinct tasks are not serialized, because
            that is the whole point of parallel jobs: they can execute
            multiple tasks simultaneously. N)r   r   r   rO   ÚtpÚmaxjobs)r   r   r   r   r   r   r   r   `  s    zParallel.__init__c             C   s2  d}xx‚|| j k rŠ| j ¡ }|dkr(P y| ¡  W n$   | ¡  | ¡  | ¡  Y q
X | ¡ rx| j 	|¡ |d7 }q
| 
¡  | ¡  q
W |s–|s–P x~| j ¡ \}}|d8 }|r¼| 
¡  n@|  ¡ rôytjj|jd td‚W n   | ¡  Y nX | ¡  | ¡  | jj ¡ r˜P q˜W qW | j ¡  | j ¡  dS )zúStart the job. This will begin pulling tasks from the
            taskmaster and executing them, and return when there are no
            more tasks. If a task fails to execute (i.e. execute() raises
            an exception), then the job will stop.r   Nr   )r6   )r`   r   r7   r8   r@   rA   rC   r9   r_   rL   rB   rK   r   r0   r<   r=   r>   r?   rI   ÚemptyrD   )r   ZjobsrE   rM   r   r   r   r   u  sH    

 


zParallel.startN)r   r   r   r5   r   r   r   r   r   r   r   Y  s   r   )r5   ZSCons.compatr0   r!   r(   ZSCons.ErrorsZSCons.Warningsr   r   r?   r   r   r   rQ   rR   ÚImportErrorZThreadrF   rO   r   r   r   r   r   Ú<module>   s&   
k;$J