1if(NOT ANDROID_PACKAGE_NAME) 2 set(ANDROID_PACKAGE_NAME "com.github.stevenlovegrove.pangolin") 3endif() 4 5if(NOT ANDROID_DEFERRED_ENTRY_SO) 6 set(ANDROID_DEFERRED_ENTRY_SO "libpangolin.so") 7endif() 8 9# Configure build environment to automatically generate APK's instead of executables. 10if(ANDROID AND NOT TARGET apk) 11 # virtual targets which we'll add apks and push actions to. 12 add_custom_target( apk ) 13 add_custom_target( push ) 14 add_custom_target( run ) 15 16 # Reset output directories to be in binary folder (rather than source) 17 set(LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_CURRENT_BINARY_DIR}) 18 set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}) 19 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}) 20 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}) 21 22 macro( create_android_manifest_xml filename prog_name package_name activity_name) 23 file( WRITE ${filename} 24"<?xml version=\"1.0\" encoding=\"utf-8\"?> 25<!-- BEGIN_INCLUDE(manifest) --> 26<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" 27 package=\"${package_name}.${prog_name}\" 28 android:versionCode=\"1\" 29 android:versionName=\"1.0\"> 30 31 <!-- This is the platform API where NativeActivity was introduced. --> 32 <uses-sdk android:minSdkVersion=\"14\" /> 33 <uses-feature android:glEsVersion=\"0x00020000\" /> 34 <uses-feature android:name=\"android.hardware.camera\" /> 35 <uses-permission android:name=\"android.permission.CAMERA\"/> 36 <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/> 37 <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/> 38 39 <!-- This .apk has no Java code itself, so set hasCode to false. --> 40 <application android:label=\"${activity_name}\" android:hasCode=\"false\"> 41 42 <!-- Our activity is the built-in NativeActivity framework class. 43 This will take care of integrating with our NDK code. --> 44 <activity android:name=\"android.app.NativeActivity\" 45 android:label=\"${activity_name}\" 46 android:screenOrientation=\"landscape\" 47 android:configChanges=\"orientation|keyboard|keyboardHidden\" 48 android:theme=\"@android:style/Theme.NoTitleBar.Fullscreen\" 49 > 50 <!-- Tell NativeActivity the name of our .so --> 51 <meta-data android:name=\"android.app.lib_name\" 52 android:value=\"${prog_name}_start\" /> 53 <intent-filter> 54 <action android:name=\"android.intent.action.MAIN\" /> 55 <category android:name=\"android.intent.category.LAUNCHER\" /> 56 </intent-filter> 57 </activity> 58 </application> 59 60</manifest> 61<!-- END_INCLUDE(manifest) -->" ) 62 endmacro() 63 64 macro( create_bootstrap_library prog_name package_name) 65 set(bootstrap_cpp "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_start.cpp" ) 66 file( WRITE ${bootstrap_cpp} 67"#include <android/native_activity.h> 68#include <android/log.h> 69#include <dlfcn.h> 70#include <errno.h> 71#include <stdlib.h> 72#include <cstdio> 73 74#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, \"AndroidUtils.cmake\", __VA_ARGS__)) 75#define LIB_PATH \"/data/data/${package_name}.${prog_name}/lib/\" 76 77void * load_lib(const char * l) { 78 void * handle = dlopen(l, RTLD_NOW | RTLD_GLOBAL); 79 if (!handle) LOGE( \"dlopen('%s'): %s\", l, strerror(errno) ); 80 return handle; 81} 82 83void ANativeActivity_onCreate(ANativeActivity * app, void * ud, size_t udsize) { 84 #include \"${prog_name}_shared_load.h\" 85 86 // Look for standard entrypoint in user lib 87 void (*stdentrypoint)(ANativeActivity*, void*, size_t); 88 *(void **) (&stdentrypoint) = dlsym(load_lib( LIB_PATH \"lib${prog_name}.so\"), \"ANativeActivity_onCreate\"); 89 if (stdentrypoint) { 90 (*stdentrypoint)(app, ud, udsize); 91 }else{ 92 // Look for deferred load entry point 93 void (*exdentrypoint)(ANativeActivity*, void*, size_t, const char*); 94 *(void **) (&exdentrypoint) = dlsym(load_lib( LIB_PATH \"lib${prog_name}.so\"), \"DeferredNativeActivity_onCreate\"); 95 if (!exdentrypoint) { 96 // Look in specific shared lib 97 *(void **) (&exdentrypoint) = dlsym(load_lib( LIB_PATH \"${ANDROID_DEFERRED_ENTRY_SO}\"), \"DeferredNativeActivity_onCreate\"); 98 } 99 if(exdentrypoint) { 100 (*exdentrypoint)(app, ud, udsize, LIB_PATH \"lib${prog_name}.so\" ); 101 }else{ 102 LOGE( \"Unable to find compatible entry point\" ); 103 } 104 } 105}" ) 106 add_library( "${prog_name}_start" SHARED ${bootstrap_cpp} ) 107 target_link_libraries( "${prog_name}_start" android log ) 108 add_dependencies( ${prog_name} "${prog_name}_start" ) 109 endmacro() 110 111 macro( android_update android_project_name) 112 # Find which android platforms are available. 113 execute_process( 114 COMMAND android list targets -c 115 OUTPUT_VARIABLE android_target_list 116 ) 117 118 # Pick first platform from this list. 119 string(REGEX MATCH "^[^\n]+" android_target "${android_target_list}" ) 120 message(STATUS "Android Target: ${android_target}") 121 122 if( NOT "${android_target}" STREQUAL "" ) 123 # Generate ant build scripts for making APK 124 execute_process( 125 COMMAND android update project --name ${android_project_name} --path . --target ${android_target} --subprojects 126 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 127 ) 128 else() 129 message( FATAL_ERROR "No Android SDK platforms found. Please install an Android platform SDK. On Linux, run 'android'." ) 130 endif() 131 endmacro() 132 133 # Override add_executable to build android .so instead! 134 macro( add_executable prog_name) 135 set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}) 136 add_library( ${prog_name} SHARED ${ARGN} ) 137 138 # Add required link libs for android 139 target_link_libraries(${prog_name} log android ) 140 141 # Create manifest required for APK 142 create_android_manifest_xml( 143 "${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml" "${prog_name}" 144 "${ANDROID_PACKAGE_NAME}" "${prog_name}" 145 ) 146 147 # Create library that will launch this program and load shared libs 148 create_bootstrap_library( ${prog_name} ${ANDROID_PACKAGE_NAME} ) 149 150 # Generate ant build system for APK 151 android_update( ${prog_name} ) 152 153 # Target to invoke ant build system for APK 154 set( APK_FILE "${CMAKE_CURRENT_BINARY_DIR}/bin/${prog_name}-debug.apk" ) 155 add_custom_command( 156 OUTPUT ${APK_FILE} 157 COMMAND ant debug 158 DEPENDS ${prog_name} 159 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 160 ) 161 162 # Target to install on device 163 add_custom_target( ${prog_name}-apk 164 DEPENDS ${APK_FILE} 165 ) 166 add_dependencies(apk ${prog_name}-apk) 167 168 # Target to install on device 169 add_custom_target( ${prog_name}-push 170 COMMAND adb install -r ${APK_FILE} 171 DEPENDS ${APK_FILE} 172 ) 173 add_dependencies(push ${prog_name}-push) 174 175 # install and run on device 176 add_custom_target( ${prog_name}-run 177 COMMAND adb shell am start -n ${ANDROID_PACKAGE_NAME}.${prog_name}/android.app.NativeActivity 178 DEPENDS ${prog_name}-push 179 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 180 ) 181 add_dependencies(run ${prog_name}-run) 182 183 # Flag to package dependent libs 184 set_property(TARGET ${prog_name} APPEND PROPERTY MAKE_APK 1 ) 185 186 # Clear shared library loading header 187 file( WRITE "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_shared_load.h" "") 188 endmacro() 189 190 macro( package_with_target prog_name lib_path ) 191 # Mark lib_path as dependent of prog_name 192 set_property(TARGET ${prog_name} APPEND PROPERTY IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE ${lib_path} ) 193 194 # If prog_name is to be packaged, add file copy command to package .so's. 195 get_target_property( package_dependent_libs ${prog_name} MAKE_APK ) 196 if( package_dependent_libs ) 197 get_filename_component(target_filename ${lib_path} NAME) 198 file( APPEND ${depend_file} "load_lib(LIB_PATH \"${target_filename}\" );\n") 199 add_custom_command(TARGET ${prog_name} POST_BUILD 200 COMMAND ${CMAKE_COMMAND} -E copy_if_different 201 ${lib_path} "${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_NDK_ABI_NAME}/" 202 ) 203 endif() 204 endmacro() 205 206 macro( add_to_depend_libs prog_name depend_file lib_name ) 207 # Recursively Process dependents of lib_name 208 get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_RELEASE) 209 if(NOT TARGET_LIBS) 210 get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_NOCONFIG) 211 endif() 212 if(NOT TARGET_LIBS) 213 get_target_property(TARGET_LIBS ${lib_name} IMPORTED_LINK_INTERFACE_LIBRARIES_DEBUG) 214 endif() 215 216 foreach(SUBLIB ${TARGET_LIBS}) 217 if(SUBLIB) 218 add_to_depend_libs( ${prog_name} ${depend_file} ${SUBLIB} ) 219 endif() 220 endforeach() 221 222 # Check if lib itself is an external shared library 223 if("${lib_name}" MATCHES "\\.so$") 224 package_with_target( ${prog_name} ${lib_name} ) 225 endif() 226 227 # Check if lib itself is an internal shared library 228 get_target_property(TARGET_LIB ${lib_name} LOCATION) 229 if("${TARGET_LIB}" MATCHES "\\.so$") 230 package_with_target( ${prog_name} ${TARGET_LIB} ) 231 endif() 232 endmacro() 233 234 macro( target_link_libraries prog_name) 235 # _target_link_libraries corresponds to original 236 _target_link_libraries( ${prog_name} ${ARGN} ) 237 238 # Recursively process dependencies 239 set(depend_file "${CMAKE_CURRENT_BINARY_DIR}/${prog_name}_shared_load.h" ) 240 foreach( LIB ${ARGN} ) 241 add_to_depend_libs( ${prog_name} ${depend_file} ${LIB} ) 242 endforeach() 243 endmacro() 244 245endif() 246