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