--!A cross-platform build utility based on Lua -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Copyright (C) 2015-present, Xmake Open Source Community. -- -- @author ruki -- @file find_ndk.lua -- -- imports import("core.base.option") import("core.base.global") import("core.project.config") import("core.cache.detectcache") import("lib.detect.find_directory") -- get triple function _get_triple(arch) local triples = { ["armv5te"] = "arm-linux-androideabi" -- deprecated , ["armv7-a"] = "arm-linux-androideabi" -- deprecated , ["armeabi"] = "arm-linux-androideabi" -- removed in ndk r17 , ["armeabi-v7a"] = "arm-linux-androideabi" , ["arm64-v8a"] = "aarch64-linux-android" , ["riscv64"] = "riscv64-linux-android" , i386 = "i686-linux-android" -- deprecated , x86 = "i686-linux-android" , x86_64 = "x86_64-linux-android" , mips = "mips-linux-android" -- removed in ndk r17 , mips64 = "mips64-linux-android" -- removed in ndk r17 } return triples[arch] end -- find ndk directory function _find_ndkdir(sdkdir) -- get ndk directory if not sdkdir then sdkdir = os.getenv("ANDROID_NDK_HOME") or os.getenv("ANDROID_NDK_ROOT") if not sdkdir and config.get("android_sdk") then local ndkbundle = path.join(config.get("android_sdk"), "ndk-bundle") if os.isdir(ndkbundle) then sdkdir = ndkbundle end end if not sdkdir and is_host("macosx") then sdkdir = find_directory("NDK", "/Applications/AndroidNDK*.app/Contents") if not sdkdir then sdkdir = find_directory("*", "~/Library/Android/sdk/ndk") end if not sdkdir then sdkdir = "~/Library/Android/sdk/ndk-bundle" end end end -- get ndk directory if sdkdir and os.isdir(sdkdir) then return path.translate(sdkdir) end end -- find the sdk version of ndk function _find_ndk_sdkver(sdkdir, bindir, sysroot, arch) -- uses llvm stl? local use_llvm = false local ndk_cxxstl = config.get("ndk_cxxstl") if ndk_cxxstl then -- we uses c++_static/c++_shared instead of llvmstl_static/llvmstl_shared if ndk_cxxstl:startswith("c++") or ndk_cxxstl:startswith("llvmstl") then use_llvm = true end elseif bindir and bindir:find("llvm", 1, true) then use_llvm = true end -- get triple local triple = _get_triple(arch) assert(triple, "no triple found for arch %s (wrong arch?)", arch) -- try to select the best compatible version local sdkver = "16" if use_llvm or arch == "arm64-v8a" or arch == "riscv64" then sdkver = (arch == "riscv64") and "36" or "21" end if sysroot then if os.isdir(path.join(sysroot, "usr", "lib", triple, sdkver)) then return sdkver end end if os.isdir(path.join(sdkdir, "platforms", "android-" .. sdkver)) then return sdkver end -- find the max version local sdkver_max = 0 local sdkver_dir_pattern if sysroot and os.isdir(path.join(sysroot, "usr", "lib")) then sdkver_dir_pattern = path.join(sysroot, "usr", "lib", triple, "*") else sdkver_dir_pattern = path.join(sdkdir, "platforms", "android-*") end for _, sdkdir in ipairs(os.dirs(sdkver_dir_pattern)) do -- get version local filename = path.filename(sdkdir) local version, count = filename:gsub("android%-", "") if count > 0 then -- get the max version local sdkver_now = tonumber(version) if sdkver_now > sdkver_max then sdkver_max = sdkver_now end end end -- get the max sdk version return sdkver_max > 0 and tostring(sdkver_max) or nil end -- find the toolchains version of ndk function _find_ndk_toolchains_ver(bindir) return bindir:match("%-(%d*%.%d*)[/\\]") end -- find sysroot directory function _find_ndk_sysroot(sdkdir) -- get sysroot above ndk r22 -- @see https://github.com/android/ndk/wiki/Changelog-r22 local prebuilt = (is_host("macosx") and "darwin" or os.host()) .. "-x86_64" local sysroot_r22 = path.join(sdkdir, "toolchains", "llvm", "prebuilt", prebuilt, "sysroot") if os.isdir(sysroot_r22) then return sysroot_r22 end -- get sysroot above ndk r14 -- @see https://android.googlesource.com/platform/ndk/+/master/docs/UnifiedHeaders.md local sysroot_r14 = path.join(sdkdir, "sysroot") if os.isdir(sysroot_r14) then return sysroot_r14 end end -- find the ndk toolchain function _find_ndk(opt) opt = opt or {} local arch = opt.arch local sdkdir = opt.sdkdir local ndk_sdkver = opt.ndk_sdkver local ndk_toolchains_ver = opt.ndk_toolchains_ver local compiler = opt.compiler -- find ndk root directory sdkdir = _find_ndkdir(sdkdir) if not sdkdir then return end -- get cross local crosses = { ["armv5te"] = "arm-linux-androideabi-" -- deprecated , ["armv7-a"] = "arm-linux-androideabi-" -- deprecated , ["armeabi"] = "arm-linux-androideabi-" -- removed in ndk r17 , ["armeabi-v7a"] = "arm-linux-androideabi-" , ["arm64-v8a"] = "aarch64-linux-android-" , ["riscv64"] = "riscv64-linux-android-" , i386 = "i686-linux-android-" -- deprecated , x86 = "i686-linux-android-" , x86_64 = "x86_64-linux-android-" , mips = "mips-linux-android-" -- removed in ndk r17 , mips64 = "mips64-linux-android-" -- removed in ndk r17 } local cross = crosses[arch] -- get gcc toolchain sub-directory local gcc_toolchain_subdirs = { ["armv5te"] = "arm-linux-androideabi-*" , ["armv7-a"] = "arm-linux-androideabi-*" , ["armeabi"] = "arm-linux-androideabi-*" , ["armeabi-v7a"] = "arm-linux-androideabi-*" , ["arm64-v8a"] = "aarch64-linux-android-*" , ["riscv64"] = "riscv64-linux-android-*" , i386 = "x86-*" , x86 = "x86-*" , x86_64 = "x86_64-*" , mips = "mipsel-linux-android-*" , mips64 = "mips64el-linux-android-*" } local gcc_toolchain_subdir = gcc_toolchain_subdirs[arch] or "arm-linux-androideabi-*" -- find the binary directory local llvm_toolchain local prebuilt = (is_host("macosx") and "darwin" or os.host()) .. "-x86_64" local bindir = find_directory("bin", path.join(sdkdir, "toolchains", "llvm", "prebuilt", prebuilt)) -- larger than ndk r16 if bindir and compiler ~= "gcc" then llvm_toolchain = path.directory(bindir) else bindir = find_directory("bin", path.join(sdkdir, "toolchains", gcc_toolchain_subdir, "prebuilt", "*")) end if not bindir then return end -- find the gcc toolchain local gcc_toolchain = find_directory("bin", path.join(sdkdir, "toolchains", gcc_toolchain_subdir, "prebuilt", "*")) if gcc_toolchain then gcc_toolchain = path.directory(gcc_toolchain) end -- find the toolchains version local toolchains_ver = ndk_toolchains_ver or _find_ndk_toolchains_ver(gcc_toolchain or bindir) -- find sysroot directory local sysroot = _find_ndk_sysroot(sdkdir) -- find the sdk version local sdkver = ndk_sdkver or _find_ndk_sdkver(sdkdir, bindir, sysroot, arch) -- get ndk version, e.g. r16b, .. local ndkver = nil if sysroot then local ndk_version_header = path.join(sysroot, "usr/include/android/ndk-version.h") if os.isfile(ndk_version_header) then local ndk_version_info = io.readfile(ndk_version_header) if ndk_version_info then ndk_version_info = ndk_version_info:match("#define __NDK_MAJOR__ (%d+)") if ndk_version_info then ndkver = tonumber(ndk_version_info) end end end end return {ndkver = ndkver, sdkdir = sdkdir, bindir = bindir, cross = cross, sdkver = sdkver, llvm_toolchain = llvm_toolchain, -- >= ndk r22 gcc_toolchain = gcc_toolchain, toolchains_ver = toolchains_ver, sysroot = sysroot} end -- find ndk toolchains -- -- @param sdkdir the ndk directory -- @param opt the argument options -- e.g. {arch = "[armeabi|armeabi-v7a|arm64-v8a]", verbose = true, force = false, sdkver = 19, toolchains_ver = "4.9"} -- -- @return the ndk toolchains. e.g. {bindir = .., cross = ..} -- -- @code -- -- local toolchain = find_ndk("/xxx/android-ndk-r10e") -- local toolchain = find_ndk("/xxx/android-ndk-r10e", {arch = "arm64-v8a"}) -- -- @endcode -- -- find Android NDK SDK -- -- @param sdkdir the NDK SDK directory (optional) -- @param opt the options, e.g. {verbose = true, force = false} -- @return the SDK info table {sdkdir, bindir, cross, sdkver, ...} -- function main(sdkdir, opt) -- init arguments opt = opt or {} -- attempt to load cache first local key = "detect.sdks.find_ndk" local cacheinfo = detectcache:get(key) or {} if not opt.force and cacheinfo.ndk and cacheinfo.ndk.sdkdir and os.isdir(cacheinfo.ndk.sdkdir) then return cacheinfo.ndk end -- get arch local arch = opt.arch or config.get("arch") or "armeabi-v7a" -- find ndk local ndk = _find_ndk({sdkdir = sdkdir or config.get("ndk") or global.get("ndk"), arch = arch, ndk_sdkver = opt.sdkver or config.get("ndk_sdkver"), ndk_toolchains_ver = opt.toolchains_ver or config.get("ndk_toolchains_ver"), compiler = opt.compiler}) if ndk and ndk.sdkdir then config.set("ndk", ndk.sdkdir, {force = true, readonly = true}) config.set("ndkver", ndk.ndkver, {force = true, readonly = true}) config.set("ndk_sdkver", ndk.sdkver, {force = true, readonly = true}) config.set("ndk_toolchains_ver", ndk.toolchains_ver, {force = true, readonly = true}) if opt.verbose or option.get("verbose") then local extra = "" if ndk.sdkver then extra = " (sdk: " .. ndk.sdkver .. ")" end cprint("checking for Android NDK ... ${color.success}%s%s", ndk.sdkdir, extra) end else if opt.verbose or option.get("verbose") then cprint("checking for Android NDK ... ${color.nothing}${text.nothing}") end end -- save to cache cacheinfo.ndk = ndk or false detectcache:set(key, cacheinfo) return ndk end