U
    ee                     @   sh  d dl m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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mZmZmZmZ d dlm Z  d d	l!m"Z"m#Z# d d
l$m%Z%m&Z&m'Z'm(Z(m)Z) d dl*m+Z+m,Z,m-Z-m.Z. dd Z/dd Z0G dd dZ1d!ddZ2dd Z3d"ddZ4d#ddZ5dd Z6d$ddZ7d%dd Z8dS )&    )suppressN)environ)abspathjoinrealpathdirname
expanduserexists)
AndroidNDK)ArchARMArchARMv7_aArchAarch_64Archx86
Archx86_64)infowarninginfo_notify	info_mainshprint)get_package_name)CythonRecipeRecipe)check_ndk_versioncheck_target_apicheck_ndk_apiRECOMMENDED_NDK_APIRECOMMENDED_TARGET_API)current_directory
ensure_dirBuildInterruptingExceptionrmdirc                 C   s   t t| ddddrDtt| dddd}|ddjdd}nt t| d	ddrtt| d	dd}|ddjdd}nFt t| d	d
rtt| d	d
}|djdd}ntddd|S )Nzcmdline-toolsZlatestbin
avdmanagerlisttargetutf-8
Ztoolsandroidz@Could not find `android` or `sdkmanager` binaries in Android SDKz0Make sure the path to the Android SDK is correct)Zinstructions)r	   r   shCommandstdoutdecodesplitr   )sdk_dirr"   targetsr'    r/   :/tmp/pip-unpacked-wheel-h4dze4ss/pythonforandroid/build.pyget_targets   s    r1   c                 C   s6   t | }dd |D }dd |D }dd |D }|S )Nc                 S   s   g | ]}t d |r|qS )z^ *API level: )rematch.0sr/   r/   r0   
<listcomp>1   s      z&get_available_apis.<locals>.<listcomp>c                 S   s   g | ]}t d |qS )z[0-9]+)r2   findallr4   r/   r/   r0   r7   2   s     c                 S   s   g | ]}|rt |d  qS )r   )intr4   r/   r/   r0   r7   3   s      )r1   )r-   r.   apisr/   r/   r0   get_available_apis/   s
    r;   c                   @   sp  e Zd ZdZdZdZe ZdZ	dZ
dZdZdZdZdZdZdZdZdZdZdZedd Zedd Zed	d
 Zedd Zedd Zedd Zdd Zdd Zdd Zedd Z e j!dd Z edd Z"e"j!dd Z"edd Z#e#j!dd Z#ed d! Z$e$j!d"d! Z$d#d$ Z%d%d& Z&d'd( Z'd)d* Z(d+d, Z)d-d. Z*d/d0 Z+d1d2 Z,d7d3d4Z-d8d5d6Z.dS )9ContextzA build context. If anything will be built, an instance this class
    will be instantiated and used to hold all the build state.FNautoc                 C   s   t | jdS )z3Where packages are downloaded before being unpackedpackages)r   storage_dirselfr/   r/   r0   packages_patha   s    zContext.packages_pathc                 C   s   t | jdS )NZ	templates)r   root_dirr@   r/   r/   r0   templates_dirf   s    zContext.templates_dirc                 C   s    t | jd| jjj}t| |S )zd
        where Android libs are cached after build
        but before being placed in dists
        Zlibs_collectionsr   	build_dir	bootstrapdistributionnamer   rA   	directoryr/   r/   r0   libs_dirj   s
    zContext.libs_dirc                 C   s    t | jd| jjj}t| |S )NZjavaclassesrE   rJ   r/   r/   r0   javaclass_dirv   s
    zContext.javaclass_dirc                 C   s    t | jd| jjj}t| |S )NZaarsrE   rJ   r/   r/   r0   aars_dir~   s    zContext.aars_dirc                 C   s   t | jd}t| |S )Nzpython-installs)r   rF   r   rJ   r/   r/   r0   python_installs_dir   s    zContext.python_installs_dirc                 C   s   t | j| jjj|S N)r   rO   rG   rH   rI   rA   archr/   r/   r0   get_python_install_dir   s    zContext.get_python_install_dirc                 C   s<   t || _d| jkrtdt| jd| _t| jd| _dS )zhCalculates all the storage and build dirs, and makes sure
        the directories exist where necessary. zPstorage dir path cannot contain spaces, please specify a path with --storage-dirbuilddistsN)r   r?   
ValueErrorr   rF   dist_dir)rA   r?   r/   r/   r0   
setup_dirs   s
    

zContext.setup_dirsc                 C   sB   t | j t | j t | j t t| jd t t| jd d S )NZbootstrap_buildsZother_builds)r   r?   rF   rX   r   r@   r/   r/   r0   ensure_dirs   s
    


zContext.ensure_dirsc                 C   s   | j dkrtd| j S )zThe Android API being targeted.NzcTried to access android_api but it has not been set - this should not happen, something went wrong!)_android_apirW   r@   r/   r/   r0   android_api   s    
zContext.android_apic                 C   s
   || _ d S rP   )r[   rA   valuer/   r/   r0   r\      s    c                 C   s   | j dkrtd| j S )zThe API number compile againstNz_Tried to access ndk_api but it has not been set - this should not happen, something went wrong!)_ndk_apirW   r@   r/   r/   r0   ndk_api   s    
zContext.ndk_apic                 C   s
   || _ d S rP   )r_   r]   r/   r/   r0   r`      s    c                 C   s   | j dkrtd| j S )zThe path to the Android SDK.Nz_Tried to access sdk_dir but it has not been set - this should not happen, something went wrong!)_sdk_dirrW   r@   r/   r/   r0   r-      s    
zContext.sdk_dirc                 C   s
   || _ d S rP   )ra   r]   r/   r/   r0   r-      s    c                 C   s   | j dkrtd| j S )zThe path to the Android NDK.Nz_Tried to access ndk_dir but it has not been set - this should not happen, something went wrong!)_ndk_dirrW   r@   r/   r/   r0   ndk_dir   s    
zContext.ndk_dirc                 C   s
   || _ d S rP   )rb   r]   r/   r/   r0   rc      s    c              	   C   s  |    | jrdS d}|r|}|dkr2tdd}|dkrFtdd}|dkrtttddddd}d	d
 |D }|rtdddd |D  td|d  t	d |d }|dkrt
dt|| _d}|r|}td| n4dtkrtd }td| ntdt t}t|}|| _| jD ]}t|| q8t| j}	tddtt|	 ||	krtd| nt
d|d}
|r|}
td |
dkrtdd}
|
dk	rtd|
 |
dkrtdd}
|
dk	rtd|
 |
dkr6tdd}
|
dk	r6td |
 |
dkrtttddddd!}|rtd"dd#d |D  td$|d  t	d% |d }
|
dkrt
d&t|
| _t|
 d}|r|}td' n@d(tkrtd(d}td) n t| jt}t	d*|| jt t|}|| _t|| j t| j| _td+| _| js^td, zt !d-d.d/d0g W n  t j"k
r   t	d1 Y nX d2| jj#| j| j d3td4g| j$d4< dS )5zChecks that build dependencies exist and sets internal variables
        for the Android SDK etc.

        ..warning:: This *must* be called before trying any build stuff

        NZ
ANDROIDSDKZANDROID_HOME~z
.buildozerr'   platformzandroid-sdk-*c                 S   s   g | ]}| d s|qS ))z.bz2z.gz)endswithr5   dr/   r/   r0   r7      s    
z5Context.prepare_build_environment.<locals>.<listcomp>z,Found possible SDK dirs in buildozer dir: {}, c                 s   s   | ]}| tjd  V  qdS Nr,   osseprg   r/   r/   r0   	<genexpr>   s     z4Context.prepare_build_environment.<locals>.<genexpr>zWill attempt to use SDK at {}r   zThis SDK lookup is intended for debug only, if you use python-for-android much you should probably maintain your own SDK download.z+Android SDK dir was not specified, exiting.z2Getting Android API version from user argument: {}Z
ANDROIDAPIz+Found Android API target in $ANDROIDAPI: {}z@Android API target was not set manually, using the default of {}zAvailable Android APIs are ({})z1Requested API target {} is available, continuing.zORequested API target {} is not available, install it with the SDK android tool.z'Getting NDK dir from from user argumentZ
ANDROIDNDKz Found NDK dir in $ANDROIDNDK: {}ZNDK_HOMEzFound NDK dir in $NDK_HOME: {}ZANDROID_NDK_HOMEz&Found NDK dir in $ANDROID_NDK_HOME: {}zandroid-ndk-r*z,Found possible NDK dirs in buildozer dir: {}c                 s   s   | ]}| tjd  V  qdS rj   rl   rg   r/   r/   r0   ro   4  s     zWill attempt to use NDK at {}zThis NDK lookup is intended for debug only, if you use python-for-android much you should probably maintain your own NDK download.z!Android NDK dir was not specifiedzGGetting NDK API version (i.e. minimum supported API) from user argumentZNDKAPIz#Found Android API target in $NDKAPIzfNDK API target was not set manually, using the default of {} = min(android-api={}, default ndk-api={})ccachezAccache is missing, the build will not be optimized in the future.python3-mZcythonz--helpzuCython for python3 missing. If you are building for  a python 3 target (which is the default) then THINGS WILL BREAK.:z/toolsPATH)%rZ   _build_env_preparedr   getglobr   r   r   formatr   r   r   r-   r   r9   r\   archsr   r;   mapstrrc   r   minr   r`   r   r
   ndkshutilwhichrp   
subprocesscheck_outputCalledProcessErrorZllvm_bin_direnv)rA   Zuser_sdk_dirZuser_ndk_dirZuser_android_apiZuser_ndk_apir-   Zpossible_dirsr\   rR   r:   rc   r`   r/   r/   r0   prepare_build_environment   s        











    




     

z!Context.prepare_build_environmentc                 C   s   g | _ d| _d | _d | _d | _d | _d | _d | _d| _d| _	d| _
t| t| t| t| t| f| _ttt| _| jdd  | jdd  | jdd  d | _d S )NFzorg.kivy.android.PythonActivityzorg.kivy.android.PythonServiceLDFLAGSZ	ARCHFLAGSCFLAGS)include_dirsru   ra   rb   r[   r_   r}   Zlocal_recipes	copy_libsZactivity_class_nameZservice_class_namer   r   r   r   r   ry   r   r   __file__rC   r   poppython_reciper@   r/   r/   r0   __init__i  s,    zContext.__init__c                    sx   | j }t }|D ]*  fdd|D }|D ]}|| q*qt|| _ | j sTtdtdddd | j D  d S )Nc                    s   g | ]}|j  kr|qS r/   rR   r5   rR   rI   r/   r0   r7     s     
 z%Context.set_archs.<locals>.<listcomp>z*Asked to compile for no Archs, so failing.z(Will compile for the following archs: {}ri   c                 s   s   | ]}|j V  qd S rP   r   r   r/   r/   r0   ro     s     z$Context.set_archs.<locals>.<genexpr>)ry   setaddr#   r   r   rx   r   )rA   Z
arch_namesZ	all_archsZ	new_archsZmatchingr3   r/   r   r0   	set_archs  s    
zContext.set_archsc                 C   s0   |st d| |_|| _| j  | jj| _d S )Nz!None is not allowed for bootstrap)	TypeErrorctxrG   prepare_build_dirrF   bootstrap_build_dir)rA   rG   r/   r/   r0   prepare_bootstrap  s    
zContext.prepare_bootstrapc                 C   s   | j   d S rP   )rG   Zprepare_dist_dirr@   r/   r/   r0   prepare_dist  s    zContext.prepare_distc                 C   s   |  |jS )zWReturns the location of site-packages in the python-install build
        dir.
        )rS   rR   rQ   r/   r/   r0   get_site_packages_dir  s    zContext.get_site_packages_dirc                 C   s   t t| j| t| j|S )zThe libs dir for a given arch.)r   r   rL   rQ   r/   r/   r0   get_libs_dir  s    zContext.get_libs_dirc                 C   s   t t| ||S rP   )r	   r   r   )rA   rR   libr/   r/   r0   has_lib  s    zContext.has_libc                 C   s  | ddks| ddkrf| ddk rftj|s:dS zttj|}W n tk
rd   Y dS X zt|| }W n tk
r   Y nX t	|dd p|}|
dd}| |}tt||ptt||d ptt||d	 ptt||d
 ptt||d S )N/r   \z://FZsite_packages_name.z.pyz.pyc.soz-*.egg)findrm   pathr	   r   r   rW   r   
get_recipegetattrreplacer   r   rw   )rA   rI   rR   recipeZsite_packages_dirr/   r/   r0   has_package  s2    
zContext.has_packagec                 C   s   |  || S rP   )r   )rA   rI   rR   r/   r/   r0   not_has_package  s    zContext.not_has_package)N)N)/__name__
__module____qualname____doc__Zbuild_as_debuggablewith_debug_symbolsr   copyr   rC   r?   rF   rH   rX   Zuse_setup_pyrp   r}   rG   r   recipe_build_orderZsymlink_bootstrap_filesZjava_build_toolpropertyrB   rD   rL   rM   rN   rO   rS   rY   rZ   r\   setterr`   r-   rc   r   r   r   r   r   r   r   r   r   r   r/   r/   r/   r0   r<   7   sv   














 #
r<   Fc                    s  t d|  |r2tt|}t dd|  fdd| D }td |D ]}|  qP jD ]*}td|j td |D ] }t	|
|j ||j qtd	 |D ],}td
|j|j || || qtd |D ]J}td|j|j ||r|| ntd|j || qtd  jsVt | ntd td |D ]$}td|j|j || qjqdtd  jD ]}t ||||d qd S )NzRecipe build order is {}zQThe requirements ({}) were not found as recipes, they will be installed with pip.ri   c                    s   g | ]}t | qS r/   r   r   r5   rI   r   r/   r0   r7     s     z!build_recipes.<locals>.<listcomp>z# Downloading recipes z"# Building all recipes for arch {}z# Unpacking recipesz# Prebuilding recipeszPrebuilding {} for {}z# Building recipeszBuilding {} for {}z%{} said it is already built, skippingz# Biglinking object fileszAContext's python recipe found, skipping biglink (will this work?)z# Postbuilding recipeszPostbuilding {} for {}z # Installing pure Python modules)ignore_setup_py)r   rx   sortedr   r   r   Zdownload_if_necessaryry   rR   r   get_build_container_dirr   rI   Zprebuild_archZapply_patchesZshould_buildZ
build_archr   Zinstall_librariesr   biglinkr   Zpostbuild_archrun_pymodules_install)Zbuild_orderZpython_modulesr   project_dirZignore_project_setup_pyrecipesr   rR   r/   r   r0   build_recipes  sf    


   r   c                 C   s$   | d k	o"t t| dp"t t| dS )Nzsetup.pyzpyproject.toml)r	   r   )r   r/   r/   r0   project_has_setup_py  s    r   c                 C   s  |pi }t | td td tjt| jddddgt|d}tt |	dd	}W 5 Q R X t| t
d
d}||dd	 W 5 Q R X ztd tjtj| |}tjtj| jdddd ttj| jddD d d}g }t|D ]r}	tj||	}
tjtj||	stj|
r\t|
tj||	 nt|
tj||	 ||	 qt|}td ttjddt| jddddd d d | |ddt|d td g }tt|t| D ]Z}	||	 tj||	}
tj|
rBt|
tj||	 nt|
tj||	 q td t|| D ]6}	tj||	}
tj|
rt |
 n
t|
 qpW 5 td
 X W 5 Q R X d S )Nz`got setup.py or similar, running project install. (disable this behavior with --ignore-setup-py)z/Contents that will be used for constraints.txt:venvr!   pipfreeze)r   r%   r   z ._tmp_p4a_recipe_constraints.txtwbzCPopulating venv's site-packages with ctx.get_site_packages_dir()...r   c                 S   s   g | ]}| d r|qS )python)
startswith)r5   fr/   r/   r0   r7   F  s   
z'run_setuppy_install.<locals>.<listcomp>r   site-packageszLaunching package install...-c''"'"'z' z0install -c ._tmp_p4a_recipe_constraints.txt -v ._envzRCopying additions resulting from setup.py back into ctx.get_site_packages_dir()...z4Reverting additions to virtualenv's site-packages...)!r   r   r   r   r   rF   r   r   AttributeErrorr+   openwriteencoderm   remover   normpathr   r   listdirr	   isdirr~   copytreecopy2appendr   r(   bashr   rx   r   r    )r   r   r   rR   constraintsfilehZctx_site_packages_dirZvenv_site_packages_dirZcopied_over_contentsr   	full_pathZprevious_venv_contentsZnew_venv_additionsr/   r/   r0   run_setuppy_install  s       
   
         
 

  

r   c              
      s  t d   fdd|D }|r.t|nd}|sV|sJ|dksJt|sVt d dS |rvt dd| t d |dk	rt|r|st d	 tj}ttj	 t
|d
dd tttj} |d< t d t
tjddt|d t d t
tjddt|d t }|_d|_|jd }t|}	|	| |	d  d  7  < tjtj	dddjj dd |	d  |	d< |st d nt d tddJ}
|D ]>}d| }|tkrd|t| }n
d|}|
| qW 5 Q R X t d  t d! t
tjdd" d#d$t|	d |dk	rxt|rx|sxt||	 j n|st d%t|  j s|j! |	j	d& W 5 Q R X dS )'a
   This function will take care of all non-recipe things, by:

        1. Processing them from --requirements (the modules argument)
           and installing them

        2. Installing the user project/app itself via setup.py if
           ignore_setup_py=True

    z;*** PYTHON PACKAGE / PROJECT INSTALL STAGE FOR ARCH: {} ***c                    s   g | ]} | r|qS r/   )r   )r5   mrR   r   r/   r0   r7     s      z)run_pymodules_install.<locals>.<listcomp>Nz6No Python modules and no setup.py to process, skippingzMThe requirements ({}) don't have recipes, attempting to install them with pipri   zVIf this fails, it may mean that the module has compiled components and needs a recipe.zeWill process project install, if it fails then the project may not be compatible for Android install.rr   r   
PYTHONPATHzUpgrade pip to latest versionr   z.source venv/bin/activate && pip install -U pipr   z;Install Cython in case one of the modules needs it to buildzvenv/bin/pip install CythonFr   rs   r   r   r   z0There are no Python modules to install, skippingz7Creating a requirements.txt file for the Python moduleszrequirements.txtwZVERSION_z{}=={}
z{}
z"Installing Python modules with pipzIF THIS FAILS, THE MODULES MAY NEED A RECIPE. A reason for this is often modules compiling native code that is unaware of Android cross-compilation and does not work without additional changes / workarounds.zDvenv/bin/pip install -v --target '{0}' --no-deps -r requirements.txtr   r   z(No setup.py found in project directory: )rF   )"r   rx   r   r   r   r(   r)   Z
hostpythonr   rF   r   dictr   rm   r   r   r   r   r   Z call_hostpython_via_targetpythonZget_recipe_envry   updater   r   Zmajor_minor_version_stringr   r   r   r   rR   r{   r   Zstrip_object_files)r   rR   modulesr   r   Zhost_pythonZbase_envZstandard_recipeZ
recipe_envr   r   modulekeyliner/   r   r0   r     s    

  




  r   c           	   	      s  t d t jjd}t|  fdd jD }|D ]}t||jd|j	}t
|snt d|j	 q6tt|d}t|st d|j	 q6t d	|j	 || ttjd
f|  q6| }|d dt jjdd|j |d< ttt|dst d d S t d t dt |jd  jrFtnt}t|jF |t |jd|dt jjdd|jtjdg|d W 5 Q R X d S )Nz'Collating object files from each recipeZcollated_objectsc                    s   g | ]}t | qS r/   r   r   r   r/   r0   r7     s     zbiglink.<locals>.<listcomp>z
objects_{}z0{} recipe has no biglinkable files dir, skipping*z,{} recipe has no biglinkable files, skippingz#{} recipe has object files, copyingz-rr   z -L{}objlocalz3There seem to be no libraries to biglink, skipping.Z
Biglinkingz	target {}zlibpymodules.sorT   r   )extra_link_dirsr   )r   r   rG   rF   r   r   r   rR   rx   rI   r	   rw   lenr   r   r(   cpZget_envr   r   copylibs_functionbiglink_functionr   Zndk_lib_dirr,   rm   r   r   )	r   rR   Zobj_dirr   r   Zrecipe_obj_dirfilesr   Z
do_biglinkr/   r   r0   r   
  s^    



  
r   c              
   C   sx  |d krg }t d| g }|D ]Z}t|D ]J}tj||}|dsJq,tj|d d d sdq,||d d  q,qg }|D ]J}|d }|d }	|| t|	}
|
	 }|
|d W 5 Q R X qg }|r| }|dkrq||kr|d| q|D ]$}d	|}||kr|| q|d
 }t| d }|j| dd   }t|ddd| f|d|i d S )Nobjs_paths arez.so.o.libsz.orT   )z-Lr   z-L{}CC   z-sharedz-O3z-or   )printrm   r   r   r   rf   r	   r   r   readextendr,   r   insertrx   r(   r)   baker   )soname
objs_pathsr   r   sofilesrK   fnargsZafnZlibsfnfddataZunique_argsadirlinkZcc_nameccr/   r/   r0   r   8  sF    





r   c                    sR  |d krg }t d| td}d g g }|rBd|krB|d }n$dtjkrXtjd }ntd }t	|
d}t| }|D ]}t|D ]}	t||	}	|	dsq|	d d d	 }
t|
sqt|	,}|  d
} fdd|D }W 5 Q R X |rt dd|  |d d  }g }t|
X}|  }|D ]>}|sV q||krdqD| }t d| |d d  D ]}|krq|dr|| | qd| d }t||}d| d }t||}|d }t||}d }t|r
|}nt|r|}|rNt d|d| || || | qt|rt d|d| || | qqqDW 5 Q R X |D ]p}t d| ||}| D ]N}||}|r|d}||kr|kr| kr||d qq||7 }||krtdd| qqqt d ttjf||f  d S )Nr   z3^.*\(NEEDED\)\s+Shared library: \[lib(.*)\.so\]\s*$)czstdc++dlz	python2.7ZsdlZ	sdl_imageZsdl_ttfzr   ZGLESv2ZjpegZpnglogZSDL2ZSDL2_ttfZ
SDL2_imageZ
SDL2_mixerREADELFreadelfz-dr   rk   dirsrT   c                    s$   g | ]}|r| kr|kr|qS r/   r/   )r5   r   Zblacklist_libsZ
found_libsr/   r0   r7     s
    z%copylibs_function.<locals>.<listcomp>zneed libs:
	z
	Zscanningz.ar   r   foundinz(static) inzscanning dependencies forr   z$Failed to locate needed libraries!
	zCopying libraries)r   r2   compilerm   r   r~   r   stripr(   r)   r   r   r   r   rf   r	   r   r   r,   r   r   
splitlinesr3   groupRuntimeErrorr   r   )r   r   r   r   Z	re_needsor   r  destrK   r   Zdirfnr   ZlibsZneeded_libsZstart_needed_libsZfound_sofilesZlibdirsZlibdirr   Zlib_aZ	libpath_aZlib_soZ
libpath_soZplain_soZplainpath_soZsopathZsofileoutr   Zneedsor/   r
  r0   r   i  s    





























r   )F)NN)NF)NN)NN)9
contextlibr   r   rw   rm   r   os.pathr   r   r   r   r   r	   r2   r~   r   r(   Zpythonforandroid.androidndkr
   Zpythonforandroid.archsr   r   r   r   r   Zpythonforandroid.loggerr   r   r   r   r   Zpythonforandroid.pythonpackager   Zpythonforandroid.reciper   r   Z pythonforandroid.recommendationsr   r   r   r   r   Zpythonforandroid.utilr   r   r   r    r1   r;   r<   r   r   r   r   r   r   r   r/   r/   r/   r0   <module>   s@        
D
o  
~.
1