1class MRuby::Toolchain::Android 2 3 DEFAULT_ARCH = 'armeabi' # TODO : Revise if arch should have a default 4 5 DEFAULT_TOOLCHAIN = :clang 6 7 DEFAULT_NDK_HOMES = %w{ 8 /usr/local/opt/android-sdk/ndk-bundle 9 /usr/local/opt/android-ndk 10 ~/Android/Sdk/ndk-bundle 11 %LOCALAPPDATA%/Android/android-sdk/ndk-bundle 12 %LOCALAPPDATA%/Android/android-ndk 13 ~/Library/Android/sdk/ndk-bundle 14 ~/Library/Android/ndk 15 } 16 17 TOOLCHAINS = [:clang, :gcc] 18 19 ARCHITECTURES = %w{ 20 armeabi armeabi-v7a arm64-v8a 21 x86 x86_64 22 mips mips64 23 } 24 25 class AndroidNDKHomeNotFound < StandardError 26 def message 27 <<-EOM 28Couldn't find Android NDK Home. 29Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter 30 EOM 31 end 32 end 33 34 class PlatformDirNotFound < StandardError 35 def message 36 <<-EOM 37Couldn't find Android NDK platform directories. 38Set ANDROID_PLATFORM environment variable or set :platform parameter 39 EOM 40 end 41 end 42 43 attr_reader :params 44 45 def initialize(params) 46 @params = params 47 end 48 49 def bin_gcc(command) 50 command = command.to_s 51 52 command = case arch 53 when /armeabi/ then 'arm-linux-androideabi-' 54 when /arm64-v8a/ then 'aarch64-linux-android-' 55 when /x86_64/ then 'x86_64-linux-android-' 56 when /x86/ then 'i686-linux-android-' 57 when /mips64/ then 'mips64el-linux-android-' 58 when /mips/ then 'mipsel-linux-android-' 59 end + command 60 61 gcc_toolchain_path.join('bin', command).to_s 62 end 63 64 def bin(command) 65 command = command.to_s 66 toolchain_path.join('bin', command).to_s 67 end 68 69 def home_path 70 @home_path ||= Pathname( 71 params[:ndk_home] || 72 ENV['ANDROID_NDK_HOME'] || 73 DEFAULT_NDK_HOMES.find { |path| 74 path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%' 75 path.gsub! '\\', '/' 76 path.gsub! '~', Dir.home || '~' 77 File.directory?(path) 78 } || raise(AndroidNDKHomeNotFound) 79 ) 80 end 81 82 def toolchain 83 @toolchain ||= params.fetch(:toolchain){ DEFAULT_TOOLCHAIN } 84 end 85 86 def toolchain_path 87 @toolchain_path ||= case toolchain 88 when :gcc 89 gcc_toolchain_path 90 when :clang 91 home_path.join('toolchains', 'llvm' , 'prebuilt', host_platform) 92 end 93 end 94 95 def gcc_toolchain_path 96 if @gcc_toolchain_path === nil then 97 prefix = case arch 98 when /armeabi/ then 'arm-linux-androideabi-' 99 when /arm64-v8a/ then 'aarch64-linux-android-' 100 when /x86_64/ then 'x86_64-' 101 when /x86/ then 'x86-' 102 when /mips64/ then 'mips64el-linux-android-' 103 when /mips/ then 'mipsel-linux-android-' 104 end 105 106 test = case arch 107 when /armeabi/ then 'arm-linux-androideabi-*' 108 when /arm64-v8a/ then 'aarch64-linux-android-*' 109 when /x86_64/ then 'x86_64-*' 110 when /x86/ then 'x86-*' 111 when /mips64/ then 'mips64el-linux-android-*' 112 when /mips/ then 'mipsel-linux-android-*' 113 end 114 115 gcc_toolchain_version = Dir[home_path.join('toolchains', test)].map{|t| t.match(/-(\d+\.\d+)$/); $1.to_f }.max 116 @gcc_toolchain_path = home_path.join('toolchains', prefix + gcc_toolchain_version.to_s, 'prebuilt', host_platform) 117 end 118 @gcc_toolchain_path 119 end 120 121 def host_platform 122 @host_platform ||= case RUBY_PLATFORM 123 when /cygwin|mswin|mingw|bccwin|wince|emx/i 124 path = home_path.join('toolchains', 'llvm' , 'prebuilt', 'windows*') 125 Dir.glob(path.to_s){ |item| 126 next if File.file?(item) 127 path = Pathname(item) 128 break 129 } 130 path.basename 131 when /x86_64-darwin/i 132 'darwin-x86_64' 133 when /darwin/i 134 'darwin-x86' 135 when /x86_64-linux/i 136 'linux-x86_64' 137 when /linux/i 138 'linux-x86' 139 else 140 raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})" 141 end 142 end 143 144 def arch 145 @arch ||= (params[:arch] || ENV['ANDROID_ARCH'] || DEFAULT_ARCH).to_s 146 end 147 148 def sysroot 149 @sysroot ||= home_path.join('platforms', platform, 150 case arch 151 when /armeabi/ then 'arch-arm' 152 when /arm64-v8a/ then 'arch-arm64' 153 when /x86_64/ then 'arch-x86_64' 154 when /x86/ then 'arch-x86' 155 when /mips64/ then 'arch-mips64' 156 when /mips/ then 'arch-mips' 157 end 158 ).to_s 159 end 160 161 def platform 162 if @platform === nil then 163 @platform = params[:platform] || ENV['ANDROID_PLATFORM'] || nil 164 if @platform === nil 165 Dir.glob(home_path.join('platforms/android-*').to_s){ |item| 166 next if File.file?(item) 167 if @platform === nil 168 @platform = Integer(item.rpartition('-')[2]) 169 else 170 platform = Integer(item.rpartition('-')[2]) 171 @platform = platform > @platform ? platform : @platform 172 end 173 } 174 if @platform === nil 175 raise(PlatformDirNotFound) 176 else 177 @platform = "android-#{@platform}" 178 end 179 end 180 end 181 if Integer(@platform.rpartition('-')[2]) < 21 182 case arch 183 when /arm64-v8a/, /x86_64/, /mips64/ 184 raise NotImplementedError, "Platform (#{@platform}) has no implementation for architecture (#{arch})" 185 end 186 end 187 @platform 188 end 189 190 def armeabi_v7a_mfpu 191 @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s 192 end 193 194 def armeabi_v7a_mfloat_abi 195 @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s 196 end 197 198 def no_warn_mismatch 199 if %W(soft softfp).include? armeabi_v7a_mfloat_abi 200 '' 201 else 202 ',--no-warn-mismatch' 203 end 204 end 205 206 def cc 207 case toolchain 208 when :gcc then bin_gcc('gcc') 209 when :clang then bin('clang') 210 end 211 end 212 213 def ar 214 case toolchain 215 when :gcc then bin_gcc('ar') 216 when :clang then bin_gcc('ar') 217 end 218 end 219 220 def ctarget 221 flags = [] 222 223 case toolchain 224 when :gcc 225 case arch 226 when /armeabi-v7a/ then flags += %W(-march=armv7-a) 227 when /armeabi/ then flags += %W(-march=armv5te) 228 when /arm64-v8a/ then flags += %W(-march=armv8-a) 229 when /x86_64/ then flags += %W(-march=x86-64) 230 when /x86/ then flags += %W(-march=i686) 231 when /mips64/ then flags += %W(-march=mips64r6) 232 when /mips/ then flags += %W(-march=mips32) 233 end 234 when :clang 235 case arch 236 when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi) 237 when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi) 238 when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android) 239 when /x86_64/ then flags += %W(-target x86_64-none-linux-android) 240 when /x86/ then flags += %W(-target i686-none-linux-android) 241 when /mips64/ then flags += %W(-target mips64el-none-linux-android) 242 when /mips/ then flags += %W(-target mipsel-none-linux-android) 243 end 244 end 245 246 case arch 247 when /armeabi-v7a/ then flags += %W(-mfpu=#{armeabi_v7a_mfpu} -mfloat-abi=#{armeabi_v7a_mfloat_abi}) 248 when /armeabi/ then flags += %W(-mtune=xscale -msoft-float) 249 when /arm64-v8a/ then flags += %W() 250 when /x86_64/ then flags += %W() 251 when /x86/ then flags += %W() 252 when /mips64/ then flags += %W(-fmessage-length=0) 253 when /mips/ then flags += %W(-fmessage-length=0) 254 end 255 256 flags 257 end 258 259 def cflags 260 flags = [] 261 262 flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}") 263 flags += ctarget 264 case toolchain 265 when :gcc 266 when :clang 267 flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument) 268 end 269 flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes) 270 271 flags 272 end 273 274 def ldflags 275 flags = [] 276 277 flags += %W(--sysroot="#{sysroot}") 278 279 flags 280 end 281 282 def ldflags_before_libraries 283 flags = [] 284 285 case toolchain 286 when :gcc 287 case arch 288 when /armeabi-v7a/ then flags += %W(-Wl#{no_warn_mismatch}) 289 end 290 when :clang 291 flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}") 292 case arch 293 when /armeabi-v7a/ then flags += %W(-target armv7-none-linux-androideabi -Wl,--fix-cortex-a8#{no_warn_mismatch}) 294 when /armeabi/ then flags += %W(-target armv5te-none-linux-androideabi) 295 when /arm64-v8a/ then flags += %W(-target aarch64-none-linux-android) 296 when /x86_64/ then flags += %W(-target x86_64-none-linux-android) 297 when /x86/ then flags += %W(-target i686-none-linux-android) 298 when /mips64/ then flags += %W(-target mips64el-none-linux-android) 299 when /mips/ then flags += %W(-target mipsel-none-linux-android) 300 end 301 end 302 flags += %W(-no-canonical-prefixes) 303 304 flags 305 end 306end 307 308MRuby::Toolchain.new(:android) do |conf, params| 309 android = MRuby::Toolchain::Android.new(params) 310 311 toolchain android.toolchain 312 313 [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc| 314 cc.command = android.cc 315 cc.flags = android.cflags 316 end 317 318 conf.archiver.command = android.ar 319 conf.linker.command = android.cc 320 conf.linker.flags = android.ldflags 321 conf.linker.flags_before_libraries = android.ldflags_before_libraries 322end 323