U
    ee>                     @   s  d dl mZmZmZmZmZmZmZmZ d dl	Z	d dl
Z
d dlmZ d dlZd dlZd dlZd dlZd dlmZ d dlmZmZmZmZmZ d dlmZ d dlZzd dlmZ W n  ek
r   d dlmZ Y nX d dlm Z m!Z!m"Z"m#Z#m$Z$m%Z% d d	l&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, d d
l&m-Z. ej/0 Z1e1j2Z3ej/4e1 G dd de5Z6G dd de6dZ7G dd de8Z9G dd de7Z:G dd de7Z;G dd de7Z<G dd de<Z=G dd de=Z>G dd de<Z?G dd de7Z@d d! ZAdS )"    )basenamedirnameexistsisdirisfilejoinrealpathsplitN)match)urlretrieve)listdirunlinkenvironcurdirwalk)stdout)urlparse)loggerinfowarningdebugshprint	info_main)current_directory
ensure_dirBuildInterruptingExceptionrmdirmovetouch)load_sourcec                       s   e Zd Z fddZ  ZS )
RecipeMetac                    sF   |dkr4d|kr| d|d< d|kr4| d|d< t | |||S )NRecipeurl_urlversion_version)popsuper__new__)clsnamebasesdct	__class__ ;/tmp/pip-unpacked-wheel-h4dze4ss/pythonforandroid/recipe.pyr(   !   s    zRecipeMeta.__new__)__name__
__module____qualname__r(   __classcell__r/   r/   r-   r0   r        s   r    c                   @   s  e Zd ZdZdZdZdZdZg Zg Z	g Z
g Zg ZdgZi ZdZdZdd Zdd Zed	d
 Zedd Zedd ZdNddZdOddZdd Zdd Zedd Zedd Zdd Zdd Zdd  Zd!d" Z d#d$ Z!d%d& Z"d'd( Z#d)d* Z$d+d, Z%dPd.d/Z&d0d1 Z'd2d3 Z(dQd4d5Z)d6d7 Z*d8d9 Z+d:d; Z,d<d= Z-d>d? Z.dRd@dAZ/dBdC Z0dDdE Z1dSdFdGZ2e3dHdI Z4e3dJdK Z5e3dLdM Z6dS )Tr!   NZarmeabiFz
c++_sharedc                 C   s   t |jdj| jdS Nzlib{name}.so)r*   )r   Zndk_lib_dirformatstl_lib_nameselfarchr/   r/   r0   get_stl_library   s    zRecipe.get_stl_libraryc                 C   s0   | j |jdj| jds,| || | d S r5   )ctxhas_libr:   r6   r7   install_libsr;   r8   r/   r/   r0   install_stl_lib   s
     zRecipe.install_stl_libc                 C   s   d| j  }t|| jS )NZVERSION_)r*   r   getr%   r9   keyr/   r/   r0   r$      s    
zRecipe.versionc                 C   s   d| j  }t|| jS )NZURL_)r*   r   r@   r#   rA   r/   r/   r0   r"      s    
z
Recipe.urlc                 C   s   | j dkrdS | j j| jdS )zA property returning the url of the recipe with ``{version}``
        replaced by the :attr:`url`. If accessing the url, you should use this
        property, *not* access the url directly.N)r$   )r"   r6   r$   r9   r/   r/   r0   versioned_url   s    
zRecipe.versioned_urlc           
   
   C   s  |sdS t d| j| |r(t||}t|}|jdkrdd }t|rRt| d}d}zzdgt	_
t||| W nd tk
r } zF|d7 }|d	kr td
|| t| |d9 }W Y 
W qZW 5 d}~X Y nX W 5 tt	_
X qqZ|S |jdkrt|sr|dr|dd }| jr`t| t|$ ttjd ttjddd| W 5 Q R X nttjdd|| t|v | jrttjdddd| j ttjd| j tdd}	|	rttjd ttjdd ttjdddddd W 5 Q R X |S dS ) zA
        (internal) Download an ``url`` to a ``target``.
        NDownloading {} from {})httphttpsc                 S   sV   |dkrd | | }nd | | d t| }dtkrRtd | t  d S )Nr   z	{0} bytesz{0:.2f}%g      Y@CIz- Download {})r6   floatr   r   writeflush)indexZblksizesizeZprogressionr/   r/   r0   report_hook   s    z)Recipe.download_file.<locals>.report_hookr      )z
User-agentzWget/1.0   z0Download failed: {}; retrying in {} second(s)...   )gitzgit+filezgit+sshzgit+httpz	git+httpszgit+   initremoteaddoriginclonez--recursivefetchz--depth1checkoutbranchz--show-currentpullz--recurse-submodules	submoduleupdatez--init)r   r6   r*   r   r   schemer   r   url_orig_headers
url_opener
addheadersr   OSErrorr   rJ   timesleepr   
startswithr$   r   r   r   shrR   )
r9   r"   targetcwd
parsed_urlrN   attemptssecondser\   r/   r/   r0   download_file   s\    



"


 zRecipe.download_filec              
   C   sL   t d| |r|n| |}t|  |}ttjdd|dd|dd dS )	z
        Apply a patch from the current recipe directory into the current
        build directory.

        .. versionchanged:: 0.6.0
            Add ability to apply patch from any dir via kwarg `build_dir`'''
        zApplying patch {}z-tz-dz-p1z-i
   )_tailN)r   r6   get_build_dirr   get_recipe_dirr   rh   patch)r9   filenamer:   	build_dirr/   r/   r0   apply_patch   s      zRecipe.apply_patchc                 C   s:   t d|| t|  |}t| j|}t|| d S )NzCopy {} to {})r   r6   r   rs   rv   shutilcopy)r9   ru   destr/   r/   r0   	copy_file   s    zRecipe.copy_filec              	   C   sl   t d|| t|  |}t| j|}t|d}| }W 5 Q R X t|d}|| W 5 Q R X d S )NzAppend {} to {}rbab)r   r6   r   rs   rv   openreadrJ   )r9   ru   rz   fddatar/   r/   r0   append_file  s    zRecipe.append_filec                 C   s   | j j}|ddd S )z=The name of the recipe, the same as the folder containing it..rQ   )r.   r2   r	   )r9   modnamer/   r/   r0   r*     s    zRecipe.namec                 C   s2   g }| j jD ] }| jr"|j| jkr|| q|S )zKReturn archs of self.ctx that are valid build archs
        for the Recipe.)r<   archsr:   append)r9   resultr:   r/   r/   r0   filtered_archs  s
    zRecipe.filtered_archsc                 C   sl   g }| j j}| jD ]2}t|ttfr|D ]}||kr(||  qq(q| jD ]}||krL|| qLt|S )zChecks what recipes are being built to see which of the alternative
        and optional dependencies are being used,
        and returns a list of these.)	r<   Zrecipe_build_orderdepends
isinstancetuplelistr   opt_dependssorted)r9   recipesZbuilt_recipesrecipealternativer/   r/   r0   check_recipe_choices  s    


zRecipe.check_recipe_choicesc                    s    fdd|D S )z_Given a list of recipe names, returns those that are also in
        self.opt_depends.
        c                    s   g | ]}| j kr|qS r/   )r   ).0r   rC   r/   r0   
<listcomp>1  s     
 z2Recipe.get_opt_depends_in_list.<locals>.<listcomp>r/   )r9   r   r/   rC   r0   get_opt_depends_in_list-  s    zRecipe.get_opt_depends_in_listc                 C   s&   |   }t| jjd|d|| jjS )zGiven the arch name, returns the directory where it will be
        built.

        This returns a different directory depending on what
        alternative or optional dependencies are being built.
        other_buildsz{}__ndk_target_{})get_dir_namer   r<   rv   r6   ndk_api)r9   r:   dir_namer/   r/   r0   get_build_container_dir3  s
    
 zRecipe.get_build_container_dirc                 C   s   |   }d| jg| }|S )N-)r   r   r*   )r9   choicesr   r/   r/   r0   r   >  s    zRecipe.get_dir_namec                 C   s   t | || jS )zeGiven the arch name, returns the directory where the
        downloaded/copied package will be built.)r   r   r*   r8   r/   r/   r0   rr   C  s    zRecipe.get_build_dirc                 C   s:   | j jdk	r(t| j j| j}t|r(|S t| j jd| jS )zf
        Returns the local recipe directory or defaults to the core recipe
        directory.
        Nr   )r<   local_recipesr   r*   r   root_dir)r9   Zlocal_recipe_dirr/   r/   r0   rs   I  s
    zRecipe.get_recipe_dirc                 C   sR   t d| j td| j }|d k	rFtd| j| j d S |   d S )NzDownloading {}
P4A_{}_DIRz+P4A_{}_DIR is set, skipping download for {})r   r6   r*   r   r@   lowerr   download)r9   user_dirr/   r/   r0   download_if_necessaryV  s     zRecipe.download_if_necessaryc           
   	   C   sV  | j d krtd| j d S | j}i }ttjtdB D ]p}t| |d rZt	| |d nd }t
d| d |}|r|rtd|| j|d}|d}|r:|||< q:tt| jj| j tt| jj| jv ttj|jd d	 d
}d}d|}t|rt|rt|s.ttj| n^| D ]P\}}t||}	|	|kr6td||	 td|| td|| jq6d}|r8td| j| ttjd| | | j| t| t|rHt|rH| D ]P\}}t||}	|	|krtd||	 td|| td|| jqntd| j W 5 Q R X d S )Nz%Skipping {} download as no URL is set)md5sha512blake2bsumz^(.+)#z=([0-9a-f]{32,})$z2Received {}sum from both the {} recipe and its urlrO   rQ   r   utf-8Tz.mark-{}z* Generated {}sum: {}z* Expected {}sum: {}z>Generated {0}sum does not match expected {0}sum for {1} recipeFrE   z-fz${} download already cached, skipping)r"   r   r6   r*   rD   sethashlibalgorithms_guaranteedhasattrgetattrr
   
ValueErrorgroupr   r   r<   packages_pathr   r   rh   r   r   decoder   r   Zrmitemsalgsumr   ro   r   )
r9   r"   Zexpected_digestsalgZexpected_digestmaru   Zdo_downloadZmarker_filenameZcurrent_digestr/   r/   r0   r   _  s    
  






 

 
zRecipe.downloadc              
   C   s~  t d| j| | |}td| j }|d k	rtd| j  t| 	|r`d S t
| t| ttjd|| 	| d S | jd krtd| j d S ttj| jjd d d}td|}|r|d	}t| | 	|}t|rt|s`t| jj| j|}t|r|d
rzt| W n tjtjfk
r\   Y nX dd l }|!|d}	|	j"d j#$dd }
|
t|kr
t%|
| nh|drt&d| t&d|jd$dd $dd }
|
t|kr
t%|
| nt'd|nRt|rPt| t(|D ]&}|dkr&ttjdt||| q&nt'd|ntd| j W 5 Q R X d S )NzUnpacking {} for {}r   z%P4A_{}_DIR exists, symlinking instead-az#Skipping {} unpack as no URL is setr   r   z$^(.+)#[a-z0-9_]{3,}=([0-9a-f]{32,})$rO   z.zipr   r/)z.tar.gzz.tgzz.tar.bz2z.tbz2z.tar.xzz.txzZxftf
zNCould not extract {} download, it must be .zip, .tar.gz or .tar.bz2 or .tar.xz)z.gitz-Rvz0Given path is neither a file nor a directory: {}z {} is already unpacked, skipping))r   r6   r*   r   r   r@   r   r   r   rr   r   r   r   rh   cpr"   r   rD   r   r   r
   r   r   r   r   r<   r   r   endswithZunzipErrorReturnCode_1ZErrorReturnCode_2zipfileZipFilefilelistru   r	   r   tar	Exceptionr   )r9   r:   rv   r   ru   r   Zdirectory_nameZextraction_filenamer   filehZroot_directoryentryr/   r/   r0   unpack  s    

 


  



zRecipe.unpackTc                 C   s"   |dkr| j d }|j|d}|S )z2Return the env specialized for the recipe
        Nr   )with_flags_in_cc)r   Zget_env)r9   r:   r   envr/   r/   r0   get_recipe_env  s    
zRecipe.get_recipe_envc                 C   sB   d |jdd}t| |r,t| |  ntd | j| dS )zRun any pre-build tasks for the Recipe. By default, this checks if
        any prebuild_archname methods exist for the archname of the current
        architecture, and runs them if so.zprebuild_{}r   _z{} has no {}, skippingN)r6   r:   replacer   r   r   r*   )r9   r:   Zprebuildr/   r/   r0   prebuild_arch  s    
zRecipe.prebuild_archc                 C   s   |  |j}tt|dS )N.patched)rr   r:   r   r   )r9   r:   rv   r/   r/   r0   
is_patched  s    zRecipe.is_patchedc                 C   s   | j rtd| j|j | |r8td| j dS |r@|n
| |j}| j D ]H}t|tt	frz|\}}||| dszqR| j
|j| j|jd|j|d qRtt|d dS )zApply any patches for the Recipe.

        .. versionchanged:: 0.6.0
            Add ability to apply patches from any dir via kwarg `build_dir`zApplying patches for {}[{}]z{} already patched, skippingN)r:   r   )r$   r:   )rv   r   )patchesr   r6   r*   r:   r   rr   r   r   r   rw   r$   r   r   )r9   r:   rv   rt   Zpatch_checkr/   r/   r0   apply_patches  s(     

 zRecipe.apply_patchesc                 C   s&   | j r"tdd | |jD  S dS )zShould perform any necessary test and return True only if it needs
        building again. Per default we implement a library test, in case that
        we detect so.

        c                 s   s   | ]}t |V  qd S N)r   r   libr/   r/   r0   	<genexpr>   s    z&Recipe.should_build.<locals>.<genexpr>T)built_librariesallget_librariesr:   r8   r/   r/   r0   should_build  s
    

zRecipe.should_buildc                 C   s&   d |j}t| |r"t| |  dS )zRun any build tasks for the Recipe. By default, this checks if
        any build_archname methods exist for the archname of the current
        architecture, and runs them if so.zbuild_{}N)r6   r:   r   r   )r9   r:   buildr/   r/   r0   
build_arch%  s    
zRecipe.build_archc                 C   s2   | j s
dS dd | |D }| j|f|  dS )zThis method is always called after `build_arch`. In case that we
        detect a library recipe, defined by the class attribute
        `built_libraries`, we will copy all defined libraries into the
         right location.
        Nc                 S   s   g | ]}| d r|qS ).so)r   r   r/   r/   r0   r   5  s    
 z,Recipe.install_libraries.<locals>.<listcomp>)r   r   r>   )r9   r:   Zshared_libsr/   r/   r0   install_libraries-  s    zRecipe.install_librariesc                 C   s6   d |j}t| |r"t| |  | jr2| | dS )zRun any post-build tasks for the Recipe. By default, this checks if
        any postbuild_archname methods exist for the archname of the
        current architecture, and runs them if so.
        zpostbuild_{}N)r6   r:   r   r   need_stl_sharedr?   )r9   r:   Z	postbuildr/   r/   r0   postbuild_arch:  s
    
zRecipe.postbuild_archc                 C   s   |  | dS )zCopies the recipe data into a build dir for the given arch. By
        default, this unpacks a downloaded recipe. You should override
        it (or use a Recipe subclass with different behaviour) if you
        want to do something else.
        N)r   r8   r/   r/   r0   prepare_build_dirF  s    zRecipe.prepare_build_dirc                 C   s|   |dkrt | jjd| j}n
| |}t|d }t|rF|| |sZtd	| j |D ]}t
| q^t
| jj dS )a  Deletes all the build information of the recipe.

        If arch is not None, only this arch dir is deleted. Otherwise
        (the default) all builds for all archs are deleted.

        By default, this just deletes the main build dir. If the
        recipe has e.g. object files biglinked, or .so files stored
        elsewhere, you should override this method.

        This method is intended for testing purposes, it may have
        strange results. Rebuild everything if this seems to happen.

        Nr   z-*z@Attempted to clean build for {} but found no existing build dirs)r   r<   rv   r*   r   globr   r   r   r6   r   python_installs_dir)r9   r:   base_dirdirs	directoryr/   r/   r0   clean_buildN  s    


zRecipe.clean_buildc                 G   s<   | j |j}|std d S ||f }ttjf|  d S )Nz1install_libs called with no libraries to install!)r<   get_libs_dirr:   r   r   rh   r   )r9   r:   libslibs_dirargsr/   r/   r0   r>   n  s    
zRecipe.install_libsc                    s   t t fdd|S )Nc                    s   j  j| S r   )r<   r=   r:   )r   r:   r9   r/   r0   <lambda>w      z!Recipe.has_libs.<locals>.<lambda>)r   map)r9   r:   r   r/   r   r0   has_libsv  s    zRecipe.has_libsc                 C   st   t  }| js|S | j D ]T\}}|sRt| |||}|dkrdt| ||}nt| j||}|| q|S )a/  Return the full path of the library depending on the architecture.
        Per default, the build library path it will be returned, unless
        `get_libraries` has been called with kwarg `in_context` set to
        True.

        .. note:: this method should be used for library recipes only
        >    r   N)r   r   r   r   rr   r<   r   rV   )r9   Z	arch_nameZ
in_contextZrecipe_libsr   rel_pathabs_pathr/   r/   r0   r   y  s    zRecipe.get_librariesc                 C   sL   g }|j d k	r|t|j  |jr6|t|jd |t|jd |S )Nr   )r   r   r   Zstorage_dirr   r   )r)   r<   recipe_dirsr/   r/   r0   r     s    
zRecipe.recipe_dirsc                 c   sT   d}|  |D ]@}|rt|rt|D ]&}||kr4q&t||}t|r&|V  q&qd S )N)__pycache__)r   r   r   r   r   )r)   r<   Zforbidden_dirsrecipes_dirr*   fnr/   r/   r0   list_recipes  s    
zRecipe.list_recipesc                 C   s   |  }t| dsi | _|| jkr,| j| S d}| |D ]T}t|sHq:t|D ]0}|  |krPt||d}t|r||} qd}qP|dk	r: qq:td|t	d||}t
tjdkrttjd  |j}||_|| j|  < |S )z5Returns the Recipe with the given name, if it exists.r   Nz__init__.pyzRecipe does not exist: {}zpythonforandroid.recipes.{}rO   )r   r   r   r   r   r   r   r   r6   import_recipelenr   handlersremoveHandlerr   r<   )r)   r*   r<   Zrecipe_filer   Z	subfoldermodr   r/   r/   r0   
get_recipe  s4    


zRecipe.get_recipe)N)N)NT)N)N)F)7r1   r2   r3   r#   r%   Zmd5sumZ	sha512sumZ
blake2bsumr   	conflictsr   r   Zpython_dependsr   r   r   r7   r;   r?   propertyr$   r"   rD   ro   rw   r{   r   r*   r   r   r   r   r   rr   rs   r   r   r   r   r   r   r   r   r   r   r   r   r   r>   r   r   classmethodr   r   r   r/   r/   r/   r0   r!   +   st   



F
	

		AI



 

	
r!   )	metaclassc                   @   s   e Zd ZdZdZdd ZdS )IncludedFilesBehaviourz]Recipe mixin class that will automatically unpack files included in
    the recipe directory.Nc                 C   sF   | j d krtdt| | ttjdt|  | j | | d S )Nz8IncludedFilesBehaviour failed: no src_filename specifiedr   )	src_filenamer   r   rr   r   rh   r   r   rs   r8   r/   r/   r0   r     s    
z(IncludedFilesBehaviour.prepare_build_dir)r1   r2   r3   __doc__r  r   r/   r/   r/   r0   r    s   r  c                       s>   e Zd ZdZdZdd Zdd Zdd Zd fdd	Z  Z	S )BootstrapNDKRecipea  A recipe class for recipes built in an Android project jni dir with
    an Android.mk. These are not cached separatly, but built in the
    bootstrap's own building directory.

    To build an NDK project which is not part of the bootstrap, see
    :class:`~pythonforandroid.recipe.NDKRecipe`.

    To link with python, call the method :meth:`get_recipe_env`
    with the kwarg *with_python=True*.
    Nc                 C   s   |   S r   )get_jni_dirr8   r/   r/   r0   r     s    z*BootstrapNDKRecipe.get_build_container_dirc                 C   s,   | j d krtd| jt| || j S )Nz:{} recipe doesn't define a dir_name, but this is necessary)r   r   r6   r*   r   r   r8   r/   r/   r0   rr     s
    
z BootstrapNDKRecipe.get_build_dirc                 C   s   t | jjjdS NZjni)r   r<   	bootstraprv   rC   r/   r/   r0   r    s    zBootstrapNDKRecipe.get_jni_dirTFc                    sV   t  ||}|s|S | jj|j|d< | jj|j|d< d| jjj|d< |S )NZPYTHON_INCLUDE_ROOTZPYTHON_LINK_ROOTz -lpython{}ZEXTRA_LDLIBS)	r'   r   r<   python_recipeinclude_rootr:   	link_rootr6   link_version)r9   r:   r   Zwith_pythonr   r-   r/   r0   r     s    z!BootstrapNDKRecipe.get_recipe_env)NTF)
r1   r2   r3   r  r   r   rr   r  r   r4   r/   r/   r-   r0   r    s   r  c                       s<   e Zd ZdZg Zdd Zdd Zdd Z fdd	Z  Z	S )
	NDKRecipezAA recipe class for any NDK project not included in the bootstrap.c                 C   s.   |  |}| jD ]}tt||s dS qdS )NTF)get_lib_dirgenerated_librariesr   r   )r9   r:   lib_dirr   r/   r/   r0   r     s
    

zNDKRecipe.should_buildc                 C   s   t | |jdd|jS )Nobjlocalr   rr   r:   r8   r/   r/   r0   r  
  s    zNDKRecipe.get_lib_dirc                 C   s   t | |jdS r  r  r8   r/   r/   r0   r    s    zNDKRecipe.get_jni_dirc              	      s   t  | | |}t| |jT ttt	| j
jddd| j
jrLdnd dt| j
j d|j f|d|i W 5 Q R X d S )	Nz	ndk-buildzV=1z
NDK_DEBUG=rZ   0zAPP_PLATFORM=android-zAPP_ABI=_env)r'   r   r   r   rr   r:   r   rh   Commandr   r<   Zndk_dirZbuild_as_debuggablestrr   )r9   r:   
extra_argsr   r-   r/   r0   r     s    
zNDKRecipe.build_arch)
r1   r2   r3   r  r  r   r  r  r   r4   r/   r/   r-   r0   r    s   	r  c                       s   e Zd ZdZdZdZdZg ZdgZ fddZ	d fdd	Z
ed	d
 Zedd Zedd Zd fdd	Zdd Z fddZdddZdd Zdd Z  ZS )PythonRecipeNTFpython3c                    s>   t  j|| d| jkr:| j}|d tt|}|| _d S )Nr  )r'   __init__r   r   r   r   )r9   r   kwargsr   r-   r/   r0   r  E  s    

zPythonRecipe.__init__c                    sz   t  j|d | j}tt| jjd}|D ]H}tt|ddd}|r,t|d |}t|r,td	| t
| q,d S )N)r:   *r   zpython*site-packagesr   z
Deleted {})r'   r   folder_namer   r   r<   r   r   r   r6   r   )r9   r:   r*   Zpython_install_dirsZpython_installZsite_packages_dirrv   r-   r/   r0   r   R  s    zPythonRecipe.clean_buildc                 C   sD   d | jjj}|dkr,t|| j}|jS | jj}d |jS d S )Nzhost{}Zhostpython3python{})r6   r<   r  r*   r!   r   Z
python_exer$   )r9   Z	host_namer  r/   r/   r0   real_hostpython_location_  s    z%PythonRecipe.real_hostpython_locationc                 C   s   | j s| jS | jjS r   ) call_hostpython_via_targetpythonr   r<   
hostpythonrC   r/   r/   r0   hostpython_locationi  s    z PythonRecipe.hostpython_locationc                 C   s   | j }|dkr| j}|S )z5The name of the build folders containing this recipe.N)site_packages_namer*   )r9   r*   r/   r/   r0   r  o  s    zPythonRecipe.folder_namec                    s  t  ||}d|d< d|d< | js|d  d| jj|j7  < |d  d| jj|j| jjj	7  < g }|
tt| jd	 |
t|d
 d tt| jd t r| fddt D 7 }t|d
krd|krd||d g |d< nd||d< |S )NrZ   PYTHONNOUSERSITEzen_GB.UTF-8LANGCFLAGSz -I{}LDFLAGSz -L{} -lpython{}Libr   r  r   c                    s$   g | ]}t t |rt |qS r/   )r   r   )r   dZbuilddirr/   r0   r     s    z/PythonRecipe.get_recipe_env.<locals>.<listcomp>
PYTHONPATH:)r'   r   r!  r6   r<   r  r	  r:   r
  r  r   r   r   r#  r   r   r   )r9   r:   r   r   Zhppathr-   r+  r0   r   w  s,    
zPythonRecipe.get_recipe_envc                 C   s2   | j }| j||r td dS td| dS )Nz.Python package already exists in site-packagesFz,{} apparently isn't already in site-packagesT)r  r<   Zhas_packager   r6   )r9   r:   r*   r/   r/   r0   r     s    zPythonRecipe.should_buildc                    s   t  | | | dS )zYInstall the Python module by calling setup.py install with
        the target Python dir.N)r'   r   install_python_packager8   r-   r/   r0   r     s    zPythonRecipe.build_archc                 C   s   |dkr| j }|dkr | |}td| j  t| j}| }t| 	|j
F t|dddd| j|j
df| jd|i | jr| | W 5 Q R X dS )	zvAutomate the installation of a Python package (or a cython
        package where the cython components are pre-built).Nz Installing {} into site-packagessetup.pyinstall-O2	--root={}z--install-lib=.r  )r*   r   r   r6   rh   r  r#  ry   r   rr   r:   r   r<   Zget_python_install_dirsetup_extra_argsinstall_in_hostpythoninstall_hostpython_package)r9   r:   r*   r   is_dirr"  Zhpenvr/   r/   r0   r.    s$    

z#PythonRecipe.install_python_packagec                 C   s"   t  }tt| jdd|d< |S )Nr)  r  r,  )r   ry   r   r   r   r9   r:   r   r/   r/   r0   get_hostrecipe_env  s    zPythonRecipe.get_hostrecipe_envc              	   C   sF   |  |}t| j}t|ddddt| jdf| jd|i d S )Nr/  r0  r1  r2  z--install-lib=Lib/site-packagesr  )r8  rh   r  r   r   r6   r   r3  )r9   r:   r   Zreal_hostpythonr/   r/   r0   r5    s    

z'PythonRecipe.install_hostpython_package)N)NT)NNT)r1   r2   r3   r$  r!  r4  Zinstall_in_targetpythonr3  r   r  r   r   r   r#  r  r   r   r   r.  r8  r5  r4   r/   r/   r-   r0   r    s(   
	

 
r  c                       s<   e Zd ZdZdZdd Zdd Z fddZd	d
 Z  Z	S )CompiledComponentsPythonRecipeF	build_extc                 C   s$   t | | | | | | dS zBuild any cython components, then install the Python module by
        calling setup.py install with the target Python dir.
        N)r!   r   build_compiled_componentsr.  r8   r/   r/   r0   r     s    
z)CompiledComponentsPythonRecipe.build_archc                 C   s   t d| j | |}t| j}t| |j	j | j
rPt|ddd|d t|d| jdf| jd|i tdd	 }ttj|d
dd|d dd|d	 W 5 Q R X d S )Nz"Building compiled components in {}r/  clean--allr  -vr  zbuild/lib.*r   z-namez"*.o"-execSTRIP{};)r   r6   r*   r   rh   r  r#  r   rr   r:   r4  r   	build_cmdr3  r   find)r9   r:   r   r"  rv   r/   r/   r0   r<    s$    
   z8CompiledComponentsPythonRecipe.build_compiled_componentsc                    s&   |  |}| || t | d S r   )r8  rebuild_compiled_componentsr'   r5  r7  r-   r/   r0   r5    s    
z9CompiledComponentsPythonRecipe.install_hostpython_packagec                 C   sP   t d| j t| j}t|ddd|d t|d| jdf| jd|i d S )Nz$Rebuilding compiled components in {}r/  r=  r>  r?  r@  r  )	r   r6   r*   rh   r  r   r   rE  r3  )r9   r:   r   r"  r/   r/   r0   rG    s    z:CompiledComponentsPythonRecipe.rebuild_compiled_components)
r1   r2   r3   pre_build_extrE  r   r<  r5  rG  r4   r/   r/   r-   r0   r9    s   r9  c                   @   s   e Zd ZdZdZdZdS )!CppCompiledComponentsPythonRecipez% Extensions that require the cxx-stl FTN)r1   r2   r3   r  r!  r   r/   r/   r/   r0   rI    s   rI  c                       sZ   e Zd ZdZdZg ZdZdd Zdd Zddd	Z	d
d Z
dddZd fdd	Z  ZS )CythonRecipeFTc                 C   s$   t | | | | | | dS r;  )r!   r   build_cython_componentsr.  r8   r/   r/   r0   r     s    
zCythonRecipe.build_archc              	   C   s   t d| j | |}t| |j t| j	j
}t|dd|d tdtt t d| j d}z t|dd	d
f| jd|i W n0 tjk
r   t  t d| j d}Y nX |r| j|d t|dd	d
f| j|ddd nt d | j	js| || W 5 Q R X d S )Nz$Cythonizing anything necessary in {}z-czimport sys; print(sys.path)r?  z	cwd is {}zFTrying first build of {} to get cython files: this is expected to failFr/  r:  r@  r  z#{} first build failed (as expected)T)r      )r  rq   Z	_criticalzGFirst build appeared to complete correctly, skipping manualcythonising.)r   r6   r*   r   r   rr   r:   rh   r  r<   r"  r   r   r   r   r3  r   printcythonize_buildZwith_debug_symbolsstrip_object_files)r9   r:   r   r"  Zmanually_cythoniser/   r/   r0   rK    s:    

 

z$CythonRecipe.build_cython_componentsNc                 C   s|   |d kr|  |j}t|V td ttjddddddd|d		 ttjdddd|d
 dd ddd|d	
 W 5 Q R X d S )NzStripping object filesr   -iname*.sorA  z/usr/bin/echorC  rD  r?  rB   r   z--strip-unneeded)rr   r:   r   r   r   rh   rF  r	   )r9   r:   r   rv   r/   r/   r0   rO  '  s"    
      zCythonRecipe.strip_object_filesc                 C   s   |}| |r"|t|d d  }td| | }d|krN|d |d< nd|kr\|d= d|krn|d td| jj	j
dd }t|d	|f| jd
|i d S )NrO   zCythonize {}Z
CYTHONPATHr,  r%  r  r   r   z\-cimport sys; from Cython.Compiler.Main import setuptools_main; sys.exit(setuptools_main());r  )rg   r   r   r6   ry   r&   rh   r  r<   r  major_minor_version_stringr	   r   cython_args)r9   r   rv   ru   Zshort_filenameZcyenvZpython_commandr/   r/   r0   cythonize_file3  s*    

zCythonRecipe.cythonize_filer   c              	   C   sX   | j std d S td tdD ]0\}}}t|dD ]}| ||t|| q8q"d S )Nz+Running cython cancelled per recipe settingz Running cython where appropriater   z*.pyx)	cythonizer   r   fnmatchfilterrU  r   )r9   r   rv   rootdirnames	filenamesru   r/   r/   r0   rN  F  s    zCythonRecipe.cythonize_buildc                    s   t  ||}|d d| j|jd| jj dt| jjj	dd|j  |d< |d d |d< d	|d
< | jj
rd|d< t| |jd| j}||d< t| |S )Nr(  z -L{} z -L{}r  r  CCz -sharedLDSHAREDZNOTNONEZLIBLINKrZ   ZCOPYLIBSz
objects_{}ZLIBLINK_PATH)r'   r   r6   r<   r   r:   r   r   r  rv   Z	copy_libsr   r*   r   )r9   r:   r   r   Zliblink_pathr-   r/   r0   r   O  s(    


zCythonRecipe.get_recipe_env)N)r   )T)r1   r2   r3   rH  rV  rT  r!  r   rK  rO  rU  rN  r   r4   r/   r/   r-   r0   rJ    s    

	rJ  c                       sX   e Zd ZdZ fddZ fddZdd Zdd	 Zed
d Z	dd Z
dd Z  ZS )TargetPythonRecipezClass for target python recipes. Sets ctx.python_recipe to point to
    itself, so as to know later what kind of Python was built or used.c                    s   d | _ t j|| d S r   )Z_ctxr'   r  )r9   r   r  r-   r/   r0   r  k  s    zTargetPythonRecipe.__init__c                    s   t  | | | j_d S r   )r'   r   r<   r  r8   r-   r/   r0   r   o  s    z TargetPythonRecipe.prebuild_archc                 C   s   t ddS )z1The root directory from which to include headers.%Not implemented in TargetPythonRecipeNNotImplementedErrorr8   r/   r/   r0   r	  s  s    zTargetPythonRecipe.include_rootc                 C   s   t dd S )Nr_  r`  rC   r/   r/   r0   r
  w  s    zTargetPythonRecipe.link_rootc                 C   s0   ddl m} ddd || jjd d D S )Nr   )LooseVersionr   c                 S   s   g | ]}t |qS r/   )r  )r   vr/   r/   r0   r   }  s     zATargetPythonRecipe.major_minor_version_string.<locals>.<listcomp>rQ   )Zdistutils.versionrb  r   r$   )r9   rb  r/   r/   r0   rS  z  s    z-TargetPythonRecipe.major_minor_version_stringc                 C   s   t d| dS )z
        Create a packaged python bundle in the target directory, by
        copying all the modules and standard library to the right
        place.
        z*{} does not implement create_python_bundleN)ra  r6   )r9   dirnr:   r/   r/   r0   create_python_bundle  s    z'TargetPythonRecipe.create_python_bundlec                 C   st   t tj|dd}|jdddd }|D ]@}t|\}}|d}t|dkrVq.t|t||d	 d
  q.dS )zRecursively renames all files named XXX.cpython-...-linux-gnu.so"
        to "XXX.so", i.e. removing the erroneous architecture name
        coming from the local system.
        rP  rQ  r   r   Nr   r   rQ   r   r   )	r   rh   rF  r   r   r	   r   r   r   )r9   rd  Zpy_so_filesZfilensfilenZfile_dirnameZfile_basenamepartsr/   r/   r0   reduce_object_file_names  s    
z+TargetPythonRecipe.reduce_object_file_names)r1   r2   r3   r  r  r   r	  r
  r   rS  re  rh  r4   r/   r/   r-   r0   r^  g  s   
r^  c              	   C   s0   t |d}tt| | }W 5 Q R X | S )z$Calculate the digest of a file.
    r|   )r~   r   r   r   	hexdigest)r   rf  r   digestr/   r/   r0   r     s    r   )Bos.pathr   r   r   r   r   r   r   r	   r   r   rer
   rh   rx   rW  urllib.requesturllibr   osr   r   r   r   r   sysr   re   r   ImportErrorurllib.parseZpythonforandroid.loggerr   r   r   r   r   r   Zpythonforandroid.utilr   r   r   r   r   r   r   r   requestbuild_openerrb   rc   ra   install_openertyper    r!   objectr  r  r  r  r9  rI  rJ  r^  r   r/   r/   r/   r0   <module>   sL   (  
     "&# ,)n/