在ide环境下编译luajit源码

        四年前第一次听说luajit(Just-In-Time Compiler for Lua.),所谓个Just-In-Time也就是运行时编译器,说白了就是一个lua的高效版本,据说执行效率是lua的数十倍,并且全面兼容lua5.1版本。由于项目内核的lua版本刚好是5.14,所以当即决定把源码下载下来研究一下,无奈四年前项目进度太赶了,下载了之后连编译都编译不过(不通过luajit自带的msvbuild.bat脚本编译),更别提将它嵌入到项目中去了,再加上网上相关技术帖少之又少,也没有一些成功上线项目案例,所以就没继续往下研究了。这个月初,通过一个朋友了解到,网易的超级大作《天域》项目服务端逻辑层完全是由lua实现的,采用的就是luajit,这再一次激起了我的好奇心,我决定这一次一定要把luajit集成到项目中去(逻辑层全面lua话的好处实在太多了)。网上的所有帖子几乎全是在介绍自动化编译luajit脚本,但自动化编译无法帮助我们理解luajit的项目结构,本文主要就是介绍Luajit源码的在vs ide下手动编译流程。

        luajit所有的源码都在src目录下, src目录包含一个msvbuild.bat自动编译脚本,如果只是想直接使用luajit,那运行一下这个脚本,就能自动生成luajit.exe的可执行文件、lua51.dll的动态链接库以及lua51.lib的静态链接库。然而我们的目标是要手动编译luajit,并且能够将源码嵌入到项目中去,这样才能针对luajit源码做定制化修改。

        首先我们来分析下msvbuild.bat到底做了哪些工作。         

        编译的前半段主要是做了一些脚本本地环境变量的设置,其中红框部分的编译选项需要注意一下,优化级别O2,并且一定要添加 _CRT_SECURE_NO_DEPRECATE或者_CRT_SECURE_NO_WARNINGS宏,否则会编译报错。DASM是什么不在本文进行讨论,感兴趣的可以看一下官方介绍以及相关技术帖子,这里直贴两个链接:

        http://luajit.org/dynasm.html

        http://www.cppblog.com/pwq1989/archive/2013/11/30/204508.aspx   

        我们暂时可以将dynasm理解成生成中间文件的工具。 其中32位环境需要将DASC变量设置vm_x86.dasc,64位环境需要DASC变量设置dysm_x64.lua。LJDLLNAME和LJLIBNAME分别是静态库和动态库名。ALL_LIB是生成临时文件时所需要的源文件。                                                                                               

        这一步很简单,就是将host下minilua.c文件编译并链接成可执行文件minlua.exe。于是我们首先要建立一个工程,这个工程只包含minilua.c这个文件,编译连接生成minilua.exe.

        minilua是一个工具,作用是为了生成另外一些中间文件。而这个中间文件是啥呢,

没错,就是buildvm_arch.h这个头文件了。当然了,这个头文件也不是luajit相关的头文件,而是为了生成buildvm这个中间工具所需要的头文件。

于是我们需要再建立一个buildvm工程,将src下所有buildvm开头的源文件都纳入到该工程中。

编译buildvm前需要通过minilua生成buildvm_arch.h,因此需要在buildvm工程设置生成前事件:

$(OutDir)\MiniLua.exe $(SolutionDir)dynasm/dynasm.lua -LN -D WIN -D JIT -D FFI -o $(SolutionDir)Buildvm/src/buildvm_arch.h archdasc/vm_x86.dasc

编译后链接生成buildvm.exe。

这还没完,我们发现脚本中利用buildvm生成了很多头文件,因此我们还需要对buildvm工程设置链接后事件:

以下是脚本的翻译版本,将生成的头文件放入到src目录下。

@setlocal
 
cd /d ..\src
@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c
$(OutDir)\buildvm.exe -m peobj -o $(OutDir)\lj_vm.obj

$(OutDir)\buildvm.exe -m bcdef -o $(SolutionDir)src\lj_bcdef.h %ALL_LIB%

$(OutDir)\buildvm.exe -m ffdef -o $(SolutionDir)src\lj_ffdef.h %ALL_LIB%

$(OutDir)\buildvm.exe -m libdef -o $(SolutionDir)src\lj_libdef.h %ALL_LIB%

$(OutDir)\buildvm.exe -m recdef -o $(SolutionDir)src\lj_recdef.h %ALL_LIB%

$(OutDir)\buildvm.exe -m vmdef -o $(SolutionDir)src\jit\vmdef.lua %ALL_LIB%

$(OutDir)\buildvm.exe -m folddef -o $(SolutionDir)src\lj_folddef.h lj_opt_fold.c

@endlocal
 

接下来就要上主菜了,生成luajit.exe以及luajit的动态链接库与静态链接库。我们首先建立两个工程,一个工程用来生成luajit.exe,一个工程用来生成luajit.dll与luajit.lib,将src所有以l开头的文件(包括刚才自动生成的文件)纳入到工程去(ljamalg.c除外,另外生成库文件的工程不需要包含luajit.c这个main函数入口文件)。

尝试直接编译发现,编译可以通过,但是链接出了问题,仔细一看才发现原来buildvm刚才还生成了一个lj_vm.obj,一些接口的实现时放在这个obj文件里的,因此我们需要设置一下链接输入文件,将lj_vm.obj设置进去。

最后进行编译链接,就会得到我们想要的可执行文件以及库文件了。

       下一步的目标是在linux环境下通过CMake编译,难度要比Windows端大很多,不过程序员的目标不就是要不断地走出舒适圈吗。

        最后给一个简单的结论吧,我将luajit成功地嵌入到了项目中去,在项目写了一个lua测试函数对两个版本(lua版本与luajit版本)进行lua性能测试,结论果然是,Luajit的执行效率真的要快过lua30倍,当然了,这个测试函数很简单,luajit在性能上也有很多踩坑点,这需要一点点地去探索,这个探索的过程往往也是需要交学费的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译 Luajit 可以分为两个步骤:首先是交叉编译 Luajit 库,然后是在 Android 项目中使用该库。 1. 交叉编译 Luajit 库 首先需要下载 Luajit,可以从官网或 GitHub 上下载。 然后需要配置交叉编译环境,比如使用 Android NDK 的话,可以使用以下命令生成交叉编译工具链: ``` $NDK_HOME/build/tools/make_standalone_toolchain.py --arch=arm --api=21 --stl=libc++ --install-dir=/path/to/toolchain ``` 其中 `$NDK_HOME` 是 Android NDK 的路径,`--arch` 指定目标架构,`--api` 指定目标 API 版本,`--stl` 指定 C++ STL 库,`--install-dir` 指定生成的工具链存放路径。 接下来进入 Luajit源码目录,执行以下命令进行交叉编译: ``` make HOST_CC="gcc -m32" CROSS=arm-linux-androideabi- TARGET_FLAGS="-march=armv7-a -mfloat-abi=softfp -Wl,--fix-cortex-a8" TARGET_SYS=Linux PREFIX=/path/to/install ``` 其中 `HOST_CC` 指定使用 32 位的 gcc 编译主机代码,`CROSS` 指定交叉编译工具链前缀,`TARGET_FLAGS` 指定目标架构和 ABI,`TARGET_SYS` 指定目标系统,`PREFIX` 指定安装路径。 执行以上命令后,会在指定的安装路径下生成编译好的 Luajit 库文件和头文件。 2. 在 Android 项目中使用 Luajit 库 在 Android 项目中使用 Luajit 库可以分为以下几个步骤: - 将编译好的 Luajit 库和头文件复制到 Android 项目中; - 在 Android.mk 中添加 Luajit 库的编译选项; - 在 Java 代码中加载 Luajit 库和执行 Lua 脚本。 具体实现可以参考以下代码示例: Android.mk: ``` LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := luajit LOCAL_SRC_FILES := libluajit.a LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include include $(PREBUILT_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := mylib LOCAL_SRC_FILES := mylib.cpp LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_STATIC_LIBRARIES := luajit include $(BUILD_SHARED_LIBRARY) ``` 其中 `LOCAL_MODULE` 指定模块名称,`LOCAL_SRC_FILES` 指定源文件,`LOCAL_EXPORT_C_INCLUDES` 和 `LOCAL_C_INCLUDES` 指定头文件路径,`LOCAL_STATIC_LIBRARIES` 指定依赖库。 mylib.cpp: ```c++ #include <jni.h> #include <string.h> #include "lua.hpp" extern "C" { JNIEXPORT jstring JNICALL Java_com_example_myapp_MainActivity_runLua(JNIEnv* env, jobject thiz); } JNIEXPORT jstring JNICALL Java_com_example_myapp_MainActivity_runLua(JNIEnv* env, jobject thiz) { lua_State* L = luaL_newstate(); luaL_openlibs(L); luaL_dostring(L, "print('Hello, world!')"); lua_close(L); return env->NewStringUTF("Lua script executed"); } ``` 其中 `runLua` 方法会执行 Lua 脚本并返回结果。 MainActivity.java: ```java package com.example.myapp; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.widget.TextView; public class MainActivity extends AppCompatActivity { static { System.loadLibrary("mylib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView textView = findViewById(R.id.text_view); String result = runLua(); textView.setText(result); } public native String runLua(); } ``` 其中 `System.loadLibrary("mylib")` 会加载编译好的库文件,`runLua` 方法会调用 C++ 代码执行 Lua 脚本并返回结果。 以上就是在 Android 中编译和使用 Luajit 库的方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值