@@ -339,17 +339,17 @@ def archive_compiled_code(project_dir):
339
339
outfile = shutil .make_archive (outfile , 'zip' , project_dir )
340
340
return outfile
341
341
342
-
343
- def compile_dex (apkfile , filtercfg ):
342
+ def compile_dex (apkfile , filtercfg , dynamic_register ):
344
343
show_logging (level = logging .INFO )
345
344
346
345
d = auto_vm (apkfile )
347
346
dx = analysis .Analysis (d )
348
347
349
348
method_filter = MethodFilter (filtercfg , d )
350
349
351
- compiler = Dex2C (d , dx )
350
+ compiler = Dex2C (d , dx , dynamic_register )
352
351
352
+ native_method_prototype = {}
353
353
compiled_method_code = {}
354
354
errors = []
355
355
@@ -372,20 +372,85 @@ def compile_dex(apkfile, filtercfg):
372
372
errors .append ('%s:%s' % (full_name , str (e )))
373
373
continue
374
374
375
- if code :
376
- compiled_method_code [method_triple ] = code
375
+ if code [0 ]:
376
+ compiled_method_code [method_triple ] = code [0 ]
377
+ native_method_prototype [jni_longname ] = code [1 ]
377
378
378
- return compiled_method_code , errors
379
+ return compiled_method_code , native_method_prototype , errors
379
380
380
381
def is_apk (name ):
381
382
return name .endswith ('.apk' )
382
383
383
- def dcc_main (apkfile , filtercfg , outapk , do_compile = True , project_dir = None , source_archive = 'project-source.zip' ):
384
+ def write_dummy_dynamic_register (project_dir ):
385
+ source_dir = os .path .join (project_dir , 'jni' , 'nc' )
386
+ if not os .path .exists (source_dir ):
387
+ os .makedirs (source_dir )
388
+
389
+ filepath = os .path .join (source_dir , 'DynamicRegister.cpp' )
390
+ with open (filepath , 'w' ) as fp :
391
+ fp .write ('#include "DynamicRegister.h"\n \n const char *dynamic_register_compile_methods(JNIEnv *env) { return nullptr; }' )
392
+
393
+ def write_dynamic_register (project_dir , compiled_methods , method_prototypes ):
394
+ source_dir = os .path .join (project_dir , 'jni' , 'nc' )
395
+ if not os .path .exists (source_dir ):
396
+ os .makedirs (source_dir )
397
+
398
+ export_list = {}
399
+
400
+ # Make export list
401
+ for method_triple in sorted (compiled_methods .keys ()):
402
+ full_name = JniLongName (* method_triple )
403
+ if not full_name in method_prototypes :
404
+ raise Exception ('Method %s prototype info could not be found' % full_name )
405
+
406
+ class_path = method_triple [0 ][1 :- 1 ].replace ('.' , '/' )
407
+ method_name = method_triple [1 ]
408
+ method_signature = method_triple [2 ]
409
+ method_native_name = full_name
410
+ method_native_prototype = method_prototypes [full_name ]
411
+
412
+ if not class_path in export_list :
413
+ export_list [class_path ] = [] # methods
414
+
415
+ export_list [class_path ].append ((method_name , method_signature , method_native_name , method_native_prototype ))
416
+
417
+ if len (export_list ) == 0 :
418
+ logger .info ('No export methods' )
419
+ return
420
+
421
+ # Generate extern block and export block
422
+ extern_block = []
423
+ export_block = ['\n jclass clazz;\n ' ]
424
+ export_block_template = 'clazz = env->FindClass("%s");\n if (clazz == nullptr)\n return "Class not found: %s";\n '
425
+ export_block_template += 'const JNINativeMethod export_method_%d[] = {\n %s\n };\n '
426
+ export_block_template += 'env->RegisterNatives(clazz, export_method_%d, %d);\n '
427
+ export_block_template += 'env->DeleteLocalRef(clazz);\n '
428
+
429
+ for index , class_path in enumerate (sorted (export_list .keys ())):
430
+ methods = export_list [class_path ]
431
+
432
+ extern_block .append ('\n ' .join (['extern %s;' % method [3 ] for method in methods ]))
433
+
434
+ export_methods = ',\n ' .join (['{"%s", "%s", (void *)%s}' % (method [0 ], method [1 ], method [2 ]) for method in methods ])
435
+ export_block .append (export_block_template % (class_path , class_path , index , export_methods , index , len (methods )))
436
+
437
+ export_block .append ('return nullptr;\n ' )
438
+
439
+ # Write DynamicRegister.cpp
440
+ filepath = os .path .join (source_dir , 'DynamicRegister.cpp' )
441
+ with open (filepath , 'w' ) as fp :
442
+ fp .write ('#include "DynamicRegister.h"\n \n ' )
443
+ fp .write ('\n ' .join (extern_block ))
444
+ fp .write ('\n \n const char *dynamic_register_compile_methods(JNIEnv *env) {' )
445
+ fp .write ('\n ' .join (export_block ))
446
+ fp .write ('}' )
447
+
448
+ def dcc_main (apkfile , filtercfg , outapk , do_compile = True , project_dir = None , source_archive = 'project-source.zip' , dynamic_register = False ):
384
449
if not os .path .exists (apkfile ):
385
450
logger .error ("file %s is not exists" , apkfile )
386
451
return
387
452
388
- compiled_methods , errors = compile_dex (apkfile , filtercfg )
453
+ compiled_methods , method_prototypes , errors = compile_dex (apkfile , filtercfg , dynamic_register )
389
454
390
455
if errors :
391
456
logger .warning ('================================' )
@@ -400,11 +465,22 @@ def dcc_main(apkfile, filtercfg, outapk, do_compile=True, project_dir=None, sour
400
465
if not os .path .exists (project_dir ):
401
466
shutil .copytree ('project' , project_dir )
402
467
write_compiled_methods (project_dir , compiled_methods )
468
+
469
+ if dynamic_register :
470
+ write_dynamic_register (project_dir , compiled_methods , method_prototypes )
471
+ else :
472
+ write_dummy_dynamic_register (project_dir )
403
473
else :
404
474
project_dir = make_temp_dir ('dcc-project-' )
405
475
shutil .rmtree (project_dir )
406
476
shutil .copytree ('project' , project_dir )
407
477
write_compiled_methods (project_dir , compiled_methods )
478
+
479
+ if dynamic_register :
480
+ write_dynamic_register (project_dir , compiled_methods , method_prototypes )
481
+ else :
482
+ write_dummy_dynamic_register (project_dir )
483
+
408
484
src_zip = archive_compiled_code (project_dir )
409
485
shutil .move (src_zip , source_archive )
410
486
@@ -428,6 +504,7 @@ def dcc_main(apkfile, filtercfg, outapk, do_compile=True, project_dir=None, sour
428
504
parser .add_argument ('-o' , '--out' , nargs = '?' , help = 'Output APK file name' )
429
505
parser .add_argument ('--sign' , action = 'store_true' , default = False , help = 'Sign apk' )
430
506
parser .add_argument ('--filter' , default = 'filter.txt' , help = 'Method filter configure file' )
507
+ parser .add_argument ('--dynamic-register' , action = 'store_true' , default = False , help = 'Export native methods using RegisterNatives' )
431
508
parser .add_argument ('--no-build' , action = 'store_true' , default = False , help = 'Do not build the compiled code' )
432
509
parser .add_argument ('--source-dir' , help = 'The compiled cpp code output directory.' )
433
510
parser .add_argument ('--project-archive' , default = 'project-source.zip' , help = 'Archive the project directory' )
@@ -439,6 +516,7 @@ def dcc_main(apkfile, filtercfg, outapk, do_compile=True, project_dir=None, sour
439
516
filtercfg = args ['filter' ]
440
517
do_compile = not args ['no_build' ]
441
518
source_archive = args ['project_archive' ]
519
+ dynamic_register = args ['dynamic_register' ]
442
520
443
521
if args ['source_dir' ]:
444
522
project_dir = args ['source_dir' ]
@@ -460,7 +538,7 @@ def dcc_main(apkfile, filtercfg, outapk, do_compile=True, project_dir=None, sour
460
538
APKTOOL = dcc_cfg ['apktool' ]
461
539
462
540
try :
463
- dcc_main (infile , filtercfg , outapk , do_compile , project_dir , source_archive )
541
+ dcc_main (infile , filtercfg , outapk , do_compile , project_dir , source_archive , dynamic_register )
464
542
except Exception as e :
465
543
logger .error ("Compile %s failed!" % infile , exc_info = True )
466
544
finally :
0 commit comments