使用Clang对lua源码进行交叉编译

什么是交叉编译

在一个平台上编译生成其他平台可运行的程序, 我们称之为交叉编译

c语言为例, 在Mac平台编译出来的程序只能在Mac平台运行, 将其拷贝到Windows平台则无法识别, 解决这个问题的办法就是将源代码移至Windows平台 然后重新编译一次 这种做法属于原始的常规编译

那能不能在Mac平台编译生成能在Windows平台运行的程序呢, 可以, 只要借助交叉编译工具即可实现, 而这种非常规编译手段也叫做交叉编译

关于交叉编译实操, 详见之前的文章《android设备上如何运行C语言原生程序

针对C/C++语言目前主流的交叉编译有:

  • GCC: GNU旗下的一款编译工具
  • CLANG: 苹果主导编写的一款基于LLVM的编译工具
  • MSVC: 微软旗下的一款编译工具

为什么使用Clang

相较之下, Clang编译速度更快, 占用内存更小, 错误提示更加人性化

Clang常规使用

我们先以一个C语言小程序来示范clang的编译流程

  1. 编写c程序代码 文件名为main,c

    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>

    int main(int argc, char** argv){
    printf("Hello World!\n");

    return 0;
    }
  2. 编译程序源码为当前平台可执行文件

    1
    clang main.c -o main

    -o:指定输出执行文件路径和程序名称

  3. 编译时顺带打印编译日志

    添加一个-v即可

    1
    clang main.c -o main -v
  4. 编译生成预处理文件

    使用-E, 区分大小写

    1
    clang -E main.c -o main
  5. 编译生成汇编代码

    使用-s, 区分大小写

    1
    clang -S main.c -o main
  6. 编译生成obj文件

    使用-c, 区分大小写

    1
    clang -c main.c -o main

Clang交叉编译

  1. 指定cpu架构

    使用-arch, 比如我们要编译生成arm64位架构平台可执行的程序

    1
    clang  main.c -o main -arch arm64

    正常情况下 上述指令会执行失败, 那是因为clang默认会使用本平台的SDK进行链接编译, 而本平台的SDK如果不支持arm的话自然无法编译 目前大部分电脑用的cpu依然还是x86架构的

    既然如此 倘若我们需要编译能在iPhone上运行的程序, 那么就需要指定可以支持armSDK来进行编译, 比如iphoneOS_SDK

  2. 指定编译SDK

    clang中, 通过来-isysroot指定编译SDK, 如下:

    1
    clang -isysroot  /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk main.c -o main -arch arm64

    编译成功后 生成的可执行文件即可在苹果手机上运行

  3. 指定操作系统

    1
    clang  main.c -o main -target x86_64-apple-darwin-eabi

    这里使用-target同时指定cpu架构和操作系统,-target的参数一共分为四部分:

    • arch:cpu架构 比如arm,x86_64
    • vendor: 工具连提供厂商 比如pc, apple,nvidia,ibm,等
    • os: 操作系统 比如darwin,linux ,win32
    • abi:应用程序二进制接口, 描述了程序在目标平台的运行规则, 比如eabi, androidabi ,gnueabi

    每部分用横杆隔开, 合起来就是arch-vendor-os-abi

示例

  1. Mac编译android平台程序

    这里我使用的是ndk包下的clang进行编译

    1
    ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=armv7-none-linux-androideabi16 --sysroot=/Users/songjian/Library/Android/sdk/ndk/21.0.6113669/toolchains/llvm/prebuilt/darwin-x86_64/sysroot main.c -o androidExc
  2. Mac编译iphone平台程序

    1
    clang -isysroot  /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.5.sdk main.c -o main -arch arm64

lua源码交叉编译

接下来 我们在Mac平台上编译生成android上可执行的lua程序

由于lua源码文件比较多, 手敲命令编译太过繁琐, 因此我这里使用gradle+cmake构建工具进行快速编译

其中build.gradle配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apply plugin: 'c'
apply plugin: 'com.android.application'

android {
compileSdkVersion 30
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
defaultConfig {
//需要限制最小库版本不能太低 不然有些c库可能缺失
minSdkVersion 21
targetSdkVersion 30

}

}

这里 我将所有lua源码放置在了lua目录当中

CMakeLists.txt配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 限定cmake最小版本号 当前使用的版本必须在这个之上
cmake_minimum_required(VERSION 3.10.2)

# 描述工程名称
project("cproject")

# 自定义变量 使用${变量名}进行引用
set(EXEC_LUA "lua")
set(EXEC_LUAC "luac")

#获取指定目录下源文件列表,保存到 `DIR_SRCS` 变量中
aux_source_directory(${PROJECT_SOURCE_DIR}/src/main/c/lua DIR_SRCS )

#lua有两个主程序 单独抽出来
set(LUA_MAIN ${PROJECT_SOURCE_DIR}/src/main/c/lua/lua.c)
set(LUAC_MAIN ${PROJECT_SOURCE_DIR}/src/main/c/lua/luac.c)


#排除文件
list(REMOVE_ITEM DIR_SRCS ${PROJECT_SOURCE_DIR}/src/main/c/lua2/luac.c)
list(REMOVE_ITEM DIR_SRCS ${PROJECT_SOURCE_DIR}/src/main/c/lua2/luac.c)


# 生成可执行文件
add_executable(${EXEC_LUA}
${DIR_SRCS} ${LUA_MAIN})

add_executable(${EXEC_LUAC}
${DIR_SRCS} ${LUAC_MAIN})

编译工程后我们在build目录可以查看到生成的程序:

image-20220929105030969

将其pushroot过的android设备上即可正常运行

关于工程配置, 如有疑问 可参考文章《使用Android Studio进行C/C++的开发

附加

  1. Mac平台上查看可执行文件的架构

    1
    file 程序名 或 lipo –info 程序名
  2. 如果你的电脑安装了xcode, 那么可以使用xcrun来查看sdk路径

    查看当前平台SDK路径:

    1
    xcrun --show-sdk-path

    查看iphone平台SDK路径:

    1
    xcrun --sdk iphoneos --show-sdk-path

    甚至可以用xcrun配合clang简化编译指令:

    1
    2
    3
    xcrun -sdk iphoneos clang -arch arm64 -o main main.c
    或者
    clang -isysroot `xcrun --sdk iphoneos --show-sdk-path` main.c -o main -arch arm64

    以及本机平台

    1
    clang -isysroot `xcrun --show-sdk-path` main.c -o main -arch x86_64
  3. 目前iphoneOS_SDK支持的cpu架构有

    1
    2
    3
    4
    arm64
    armv7s
    armv7
    armv6

    x86系列处理器暂不支持

总结

关于GCC和Clang的使用对比:

GCC 会针对每一个编译主机和目标架构提供一套完整的套件,包含了二进制、头文件和库等。所以通常使用起来比较简单

Clang 是复用一套编译系统去负责多个目标的编译任务,通过 -target选项来区分, 各个平台对应的头文件和库需要自己单独准备 编译的时候将路径通过参数告知给Clang, 使用起来相对会麻烦一些

本文为作者原创转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%