本文主要记录下在日常开发过程中, 使用 github.com/yuin/gopher-lua 过程中需要注意的地方。
后续遇到其他的需要注意的事项再补充。
1、加载LUA_PATH环境变量
在实际开发中,我们会将一些公共的、可重复使用的代码封装起来,假如我们只是一些简单的处理,全部写在一个文件是没有问题的,维护起来也并不是很麻烦。但是当我们的需求变得复杂起来,或者需求调整的时候,我们还是将所有功能都写在同一个文件的时候,就变得不合理起来,后期的维护更是灾难性的,这个时候就需要将一些公共的、重复使用的代码,抽离出来,根据功能的不同分类,当做一个个模块,在使用的时候使用 require
导入进来,这样才合理。
既然我们需要将自定义的 module 导入进来,那么我们肯定是需要设计环境变量的。
最重要的地方就是下面这点:
// 重点就是这句, 先设置环境变量, 再创建 lua 的 虚拟机, 这样环境变量才生效!!! setLuaLibPath() lvm := newLuaVm(pluginPath, context.Background())
接下来再详细看演示的demo。
先看看代码目录分层,有一个整体认识。
--go_call_lua_test |---go.mod |---main.go |---plugin.lua |---lib/lua/test.lua
main.go 代码
package main import ( "context" "errors" "fmt" lua "github.com/yuin/gopher-lua" "go.uber.org/atomic" luar "layeh.com/gopher-luar" "os" "path/filepath" ) func getExeDir() (string, string) { exePath, _ := os.Executable() exeDir, exeName := filepath.Split(exePath) return exeDir, exeName } func absPath(fp string) string { exeAbsDir, _ := getExeDir() //绝对路径 if !filepath.IsAbs(fp) { return filepath.Join(exeAbsDir, fp) } else { return fp } } func setLuaLibPath() { //设置lua环境变量 pathStr := "" LuaLibPath := []string{"./lib/lua/?.lua"} for _, path := range LuaLibPath { pathStr += ";" + absPath(path) } pathStr += ";;" os.Setenv("LUA_PATH", pathStr) } type LuaVm struct { *lua.LState path string Loaded atomic.Bool } func newLuaVm(path string, ctx context.Context) *LuaVm { lv := &LuaVm{ path: path, LState: lua.NewState(), } lv.SetContext(ctx) if err := lv.load(); err != nil { panic(fmt.Errorf("lvm error=%s", err)) } return lv } func (lv *LuaVm) load() error { //加载工具 下面是将 go 提供的功能当做模块 给 lua 使用 //lv.SetGlobal("FILE", luar.New(lv.LState, &test.FileTool{})) if len(lv.path) == 0 { return fmt.Errorf("plugin file empty") } if err := lv.DoFile(lv.path); err != nil { return err } lv.Loaded.Store(true) return nil } func (lv *LuaVm) CallLua() (result string, err error) { if err := lv.CallByParam(lua.P{ Fn: lv.GetGlobal("PluginTest"), NRet: 2, Protect: true, }, luar.New(lv.LState, "I'm coming go!")); err != nil { fmt.Printf("PrimaryIn error=%s", err) } retCode, _ := lv.Get(-1).(lua.LNumber) lv.Pop(1) retPkt, _ := lv.Get(-1).(lua.LString) lv.Pop(1) if retCode != 0 { return "", errors.New("untreated") } return string(retPkt), nil } func main() { pluginPath := "./plugin.lua" // 重点就是这句, 先设置环境变量, 再创建 lua 的 虚拟机, 这样环境变量才生效!!! setLuaLibPath() lvm := newLuaVm(pluginPath, context.Background()) ret, err := lvm.CallLua() fmt.Println("ret --> ", ret) fmt.Println("err --> ", err) }
plugin.lua 代码
local TestLib = require("test") function PluginTest(param) print(param) print("coming PluginTest") TestLib.CallTestFunc() return "I'm coming lua!" end
test.lua 代码
justTest = {} function justTest.CallTestFunc() print("coming justTest.CallTestFunc") end -- 一定要 return, 否则不生效 return justTest
编译后执行结果:
I'm coming go! coming PluginTest coming justTest.CallTestFunc ret --> I'm coming lua! err --> <nil>