99#include <stdlib.h>
1010#include <unistd.h>
1111#include <dirent.h>
12+ #include <dlfcn.h>
13+ #include <libgen.h>
1214#include <jni.h>
1315#include <sys/stat.h>
1416#include <sys/types.h>
3032
3133#define ENTRYPOINT_MAXLEN 128
3234#define LOG (n , x ) __android_log_write(ANDROID_LOG_INFO, (n), (x))
33- #define LOGP (x ) LOG("python", (x))
3435#define P4A_MIN_VER 11
36+ static void LOGP (const char * fmt , ...) {
37+ va_list args ;
38+ va_start (args , fmt );
39+ __android_log_vprint (ANDROID_LOG_INFO , "python" , fmt , args );
40+ va_end (args );
41+ }
3542
3643static PyObject * androidembed_log (PyObject * self , PyObject * args ) {
3744 char * logstr = NULL ;
@@ -69,14 +76,125 @@ int dir_exists(char *filename) {
6976}
7077
7178int file_exists (const char * filename ) {
72- FILE * file ;
73- if ((file = fopen (filename , "r" ))) {
74- fclose (file );
75- return 1 ;
76- }
77- return 0 ;
79+ return access (filename , F_OK ) == 0 ;
7880}
7981
82+ static void get_dirname (const char * path , char * dir , size_t size ) {
83+ strncpy (dir , path , size - 1 );
84+ dir [size - 1 ] = '\0' ;
85+ char * last_slash = strrchr (dir , '/' );
86+ if (last_slash ) * last_slash = '\0' ;
87+ else dir [0 ] = '\0' ;
88+ }
89+
90+ // strip "lib" prefix and "bin.so" suffix
91+ static void get_exe_name (const char * filename , char * out , size_t size ) {
92+ size_t len = strlen (filename );
93+ if (len < 7 ) { // too short to be valid
94+ strncpy (out , filename , size - 1 );
95+ out [size - 1 ] = '\0' ;
96+ return ;
97+ }
98+
99+ const char * start = filename ;
100+ if (strncmp (filename , "lib" , 3 ) == 0 ) start += 3 ;
101+ size_t start_len = strlen (start );
102+
103+ if (start_len > 6 ) {
104+ size_t copy_len = start_len - 6 ; // remove "bin.so"
105+ if (copy_len >= size ) copy_len = size - 1 ;
106+ strncpy (out , start , copy_len );
107+ out [copy_len ] = '\0' ;
108+ } else {
109+ strncpy (out , start , size - 1 );
110+ out [size - 1 ] = '\0' ;
111+ }
112+ }
113+
114+ char * setup_symlinks () {
115+ Dl_info info ;
116+ char lib_path [512 ];
117+ char * interpreter = NULL ;
118+
119+ if (!(dladdr ((void * )setup_symlinks , & info ) && info .dli_fname )) {
120+ LOGP ("symlinking failed: failed to get libdir" );
121+ return interpreter ;
122+ }
123+
124+ strncpy (lib_path , info .dli_fname , sizeof (lib_path ) - 1 );
125+ lib_path [sizeof (lib_path ) - 1 ] = '\0' ;
126+
127+ char native_lib_dir [512 ];
128+ get_dirname (lib_path , native_lib_dir , sizeof (native_lib_dir ));
129+ if (native_lib_dir [0 ] == '\0' ) {
130+ LOGP ("symlinking failed: could not determine lib directory" );
131+ return interpreter ;
132+ }
133+
134+ const char * files_dir_env = getenv ("ANDROID_APP_PATH" );
135+ char bin_dir [512 ];
136+
137+ snprintf (bin_dir , sizeof (bin_dir ), "%s/.bin" , files_dir_env );
138+ if (mkdir (bin_dir , 0755 ) != 0 && errno != EEXIST ) {
139+ LOGP ("Failed to create .bin directory" );
140+ return interpreter ;
141+ }
142+
143+ DIR * dir = opendir (native_lib_dir );
144+ if (!dir ) {
145+ LOGP ("Failed to open native lib dir" );
146+ return interpreter ;
147+ }
148+
149+ struct dirent * entry ;
150+ while ((entry = readdir (dir )) != NULL ) {
151+ const char * name = entry -> d_name ;
152+ size_t len = strlen (name );
153+
154+ if (len < 7 ) continue ;
155+ if (strcmp (name + len - 6 , "bin.so" ) != 0 ) continue ; // only bin.so at end
156+
157+ // get cleaned executable name
158+ char exe_name [128 ];
159+ get_exe_name (name , exe_name , sizeof (exe_name ));
160+
161+ char src [512 ], dst [512 ];
162+ snprintf (src , sizeof (src ), "%s/%s" , native_lib_dir , name );
163+ snprintf (dst , sizeof (dst ), "%s/%s" , bin_dir , exe_name );
164+
165+ // interpreter found?
166+ if (strcmp (exe_name , "python" ) == 0 ) {
167+ interpreter = strdup (dst );
168+ }
169+
170+ struct stat st ;
171+ if (lstat (dst , & st ) == 0 ) continue ; // already exists
172+ if (symlink (src , dst ) == 0 ) {
173+ LOGP ("symlink: %s -> %s" , name , exe_name );
174+ } else {
175+ LOGP ("Symlink failed" );
176+ }
177+ }
178+
179+ closedir (dir );
180+
181+ // append bin_dir to PATH
182+ const char * old_path = getenv ("PATH" );
183+ char new_path [1024 ];
184+ if (old_path && strlen (old_path ) > 0 ) {
185+ snprintf (new_path , sizeof (new_path ), "%s:%s" , old_path , bin_dir );
186+ } else {
187+ snprintf (new_path , sizeof (new_path ), "%s" , bin_dir );
188+ }
189+ setenv ("PATH" , new_path , 1 );
190+
191+ // set lib path
192+ setenv ("LD_LIBRARY_PATH" , native_lib_dir , 1 );
193+
194+ return interpreter ;
195+ }
196+
197+
80198/* int main(int argc, char **argv) { */
81199int main (int argc , char * argv []) {
82200
@@ -147,10 +265,11 @@ int main(int argc, char *argv[]) {
147265 LOGP ("Warning: no p4a_env_vars.txt found / failed to open!" );
148266 }
149267
150- LOGP ("Changing directory to the one provided by ANDROID_ARGUMENT" );
151- LOGP (env_argument );
268+ LOGP ("Changing directory to '%s'" , env_argument );
152269 chdir (env_argument );
153270
271+ char * interpreter = setup_symlinks ();
272+
154273#if PY_MAJOR_VERSION < 3
155274 Py_NoSiteFlag = 1 ;
156275#endif
@@ -257,12 +376,20 @@ int main(int argc, char *argv[]) {
257376 "sys.path.append('%s/site-packages')" ,
258377 python_bundle_dir );
259378
260- PyRun_SimpleString ("import sys\n"
261- "sys.argv = ['notaninterpreterreally']\n"
262- "from os.path import realpath, join, dirname" );
379+ PyRun_SimpleString ("import sys, os\n"
380+ "from os.path import realpath, join, dirname" );
381+
382+ char buf_exec [512 ];
383+ char buf_argv [512 ];
384+ snprintf (buf_exec , sizeof (buf_exec ), "sys.executable = '%s'\n" , interpreter );
385+ snprintf (buf_argv , sizeof (buf_argv ), "sys.argv = ['%s']\n" , interpreter );
386+ PyRun_SimpleString (buf_exec );
387+ PyRun_SimpleString (buf_argv );
388+
263389 PyRun_SimpleString (add_site_packages_dir );
264390 /* "sys.path.append(join(dirname(realpath(__file__)), 'site-packages'))") */
265391 PyRun_SimpleString ("sys.path = ['.'] + sys.path" );
392+ PyRun_SimpleString ("os.environ['PYTHONPATH'] = ':'.join(sys.path)" );
266393 }
267394
268395 PyRun_SimpleString (
@@ -280,23 +407,12 @@ int main(int argc, char *argv[]) {
280407 " androidembed.log(l.replace('\\x00', ''))\n"
281408 " self.__buffer = lines[-1]\n"
282409 "sys.stdout = sys.stderr = LogFile()\n"
283- "print('Android path', sys.path)\n"
284- "# import os\n"
285- "# print('os.environ is', os.environ)\n"
286410 "print('Android kivy bootstrap done. __name__ is', __name__)" );
287411
288412#if PY_MAJOR_VERSION < 3
289413 PyRun_SimpleString ("import site; print site.getsitepackages()\n" );
290414#endif
291415
292- LOGP ("AND: Ran string" );
293-
294- /* run it !
295- */
296- LOGP ("Run user program, change dir and execute entrypoint" );
297-
298- /* Get the entrypoint, search the .pyc then .py
299- */
300416 char * dot = strrchr (env_entrypoint , '.' );
301417 char * ext = ".pyc" ;
302418 if (dot <= 0 ) {
@@ -345,7 +461,6 @@ int main(int argc, char *argv[]) {
345461 LOGP (entrypoint );
346462 return -1 ;
347463 }
348-
349464 /* run python !
350465 */
351466 ret = PyRun_SimpleFile (fd , entrypoint );
@@ -361,34 +476,16 @@ int main(int argc, char *argv[]) {
361476
362477 LOGP ("Python for android ended." );
363478
364- /* Shut down: since regular shutdown causes issues sometimes
365- (seems to be an incomplete shutdown breaking next launch)
366- we'll use sys.exit(ret) to shutdown, since that one works.
367-
368- Reference discussion:
369-
370- https://github.com/kivy/kivy/pull/6107#issue-246120816
371- */
372- char terminatecmd [256 ];
373- snprintf (
374- terminatecmd , sizeof (terminatecmd ),
375- "import sys; sys.exit(%d)\n" , ret
376- );
377- PyRun_SimpleString (terminatecmd );
378-
379- /* This should never actually be reached, but we'll leave the clean-up
380- * here just to be safe.
381- */
382479#if PY_MAJOR_VERSION < 3
383480 Py_Finalize ();
384481 LOGP ("Unexpectedly reached Py_FinalizeEx(), but was successful." );
385482#else
386- if (Py_FinalizeEx () != 0 ) // properly check success on Python 3
483+ if (Py_FinalizeEx () != 0 ) { // properly check success on Python 3
387484 LOGP ("Unexpectedly reached Py_FinalizeEx(), and got error!" );
388- else
389- LOGP ("Unexpectedly reached Py_FinalizeEx(), but was successful." );
485+ }
390486#endif
391487
488+ exit (ret );
392489 return ret ;
393490}
394491
0 commit comments