1 class MRuby::Toolchain::Android
3 DEFAULT_ARCH = 'armeabi' # TODO : Revise if arch should have a default
5 DEFAULT_TOOLCHAIN = :clang
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
17 TOOLCHAINS = [:clang, :gcc]
20 armeabi armeabi-v7a arm64-v8a
25 class AndroidNDKHomeNotFound < StandardError
28 Couldn't find Android NDK Home.
29 Set ANDROID_NDK_HOME environment variable or set :ndk_home parameter
34 class PlatformDirNotFound < StandardError
37 Couldn't find Android NDK platform directories.
38 Set ANDROID_PLATFORM environment variable or set :platform parameter
45 def initialize(params)
50 command = command.to_s
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-'
61 gcc_toolchain_path.join('bin', command).to_s
65 command = command.to_s
66 toolchain_path.join('bin', command).to_s
70 @home_path ||= Pathname(
72 ENV['ANDROID_NDK_HOME'] ||
73 DEFAULT_NDK_HOMES.find { |path|
74 path.gsub! '%LOCALAPPDATA%', ENV['LOCALAPPDATA'] || '%LOCALAPPDATA%'
76 path.gsub! '~', Dir.home || '~'
78 } || raise(AndroidNDKHomeNotFound)
83 @toolchain ||= params.fetch(:toolchain){ DEFAULT_TOOLCHAIN }
87 @toolchain_path ||= case toolchain
91 home_path.join('toolchains', 'llvm' , 'prebuilt', host_platform)
95 def gcc_toolchain_path
96 if @gcc_toolchain_path === nil then
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-'
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-*'
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)
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)
131 when /x86_64-darwin/i
140 raise NotImplementedError, "Unknown host platform (#{RUBY_PLATFORM})"
145 @arch ||= (params[:arch] || ENV['ANDROID_ARCH'] || DEFAULT_ARCH).to_s
149 @sysroot ||= home_path.join('platforms', platform,
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'
162 if @platform === nil then
163 @platform = params[:platform] || ENV['ANDROID_PLATFORM'] || nil
165 Dir.glob(home_path.join('platforms/android-*').to_s){ |item|
166 next if File.file?(item)
168 @platform = Integer(item.rpartition('-')[2])
170 platform = Integer(item.rpartition('-')[2])
171 @platform = platform > @platform ? platform : @platform
175 raise(PlatformDirNotFound)
177 @platform = "android-#{@platform}"
181 if Integer(@platform.rpartition('-')[2]) < 21
183 when /arm64-v8a/, /x86_64/, /mips64/
184 raise NotImplementedError, "Platform (#{@platform}) has no implementation for architecture (#{arch})"
191 @armeabi_v7a_mfpu ||= (params[:mfpu] || 'vfpv3-d16').to_s
194 def armeabi_v7a_mfloat_abi
195 @armeabi_v7a_mfloat_abi ||= (params[:mfloat_abi] || 'softfp').to_s
199 if %W(soft softfp).include? armeabi_v7a_mfloat_abi
202 ',--no-warn-mismatch'
208 when :gcc then bin_gcc('gcc')
209 when :clang then bin('clang')
215 when :gcc then bin_gcc('ar')
216 when :clang then bin_gcc('ar')
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)
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)
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)
262 flags += %W(-MMD -MP -D__android__ -DANDROID --sysroot="#{sysroot}")
267 flags += %W(-gcc-toolchain "#{gcc_toolchain_path}" -Wno-invalid-command-line-argument -Wno-unused-command-line-argument)
269 flags += %W(-fpic -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes)
277 flags += %W(--sysroot="#{sysroot}")
282 def ldflags_before_libraries
288 when /armeabi-v7a/ then flags += %W(-Wl#{no_warn_mismatch})
291 flags += %W(-gcc-toolchain "#{gcc_toolchain_path.to_s}")
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)
302 flags += %W(-no-canonical-prefixes)
308 MRuby::Toolchain.new(:android) do |conf, params|
309 android = MRuby::Toolchain::Android.new(params)
311 toolchain android.toolchain
313 [conf.cc, conf.cxx, conf.objc, conf.asm].each do |cc|
314 cc.command = android.cc
315 cc.flags = android.cflags
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