Link external libraries in Android Studio

I wanted to make a game which was built in C++ and uses Lua for scripting purposes. For that, I made my own Lua Engine. For Windows, I simply included the Lua Header Files, the Lua library and the Lua dll. This works perfectly fine.

Now I want to import the same project for Android. The problem I am facing is that when building, it gives ‘undefined reference’ to all the Lua calls that I make. It is able to include the Lua header files, but it’s still not compiling. Now I assume that’s because of not linking the lib and .dll file. Can anyone suggest me a solution to this!?

1 Like

This is what I have done so far:

Step 1: I created static libraries for android-lua.

#lua

LIB_VERSION:=lua-5.2.2

LOCAL_PATH:= $(call my-dir)
LIB_ROOT_REL:= ../../$(LIB_VERSION)
LIB_ROOT_ABS:= $(LOCAL_PATH)/../../$(LIB_VERSION)

include $(CLEAR_VARS)

LOCAL_CFLAGS := -O2 -Wall -DLUA_COMPAT_ALL -D"getlocaledecpoint() ='.'"

LOCAL_SRC_FILES := \
 $(LIB_ROOT_REL)/src/lapi.c \
 $(LIB_ROOT_REL)/src/lauxlib.c \
 $(LIB_ROOT_REL)/src/lbaselib.c \
 $(LIB_ROOT_REL)/src/lbitlib.c \
 $(LIB_ROOT_REL)/src/lcode.c \
 $(LIB_ROOT_REL)/src/lcorolib.c \
 $(LIB_ROOT_REL)/src/lctype.c \
 $(LIB_ROOT_REL)/src/ldblib.c \
 $(LIB_ROOT_REL)/src/ldebug.c \
 $(LIB_ROOT_REL)/src/ldo.c \
 $(LIB_ROOT_REL)/src/ldump.c \
 $(LIB_ROOT_REL)/src/lfunc.c \
 $(LIB_ROOT_REL)/src/lgc.c \
 $(LIB_ROOT_REL)/src/linit.c \
 $(LIB_ROOT_REL)/src/liolib.c \
 $(LIB_ROOT_REL)/src/llex.c \
 $(LIB_ROOT_REL)/src/lmathlib.c \
 $(LIB_ROOT_REL)/src/lmem.c \
 $(LIB_ROOT_REL)/src/loadlib.c \
 $(LIB_ROOT_REL)/src/lobject.c \
 $(LIB_ROOT_REL)/src/lopcodes.c \
 $(LIB_ROOT_REL)/src/loslib.c \
 $(LIB_ROOT_REL)/src/lparser.c \
 $(LIB_ROOT_REL)/src/lstate.c \
 $(LIB_ROOT_REL)/src/lstring.c \
 $(LIB_ROOT_REL)/src/lstrlib.c \
 $(LIB_ROOT_REL)/src/ltable.c \
 $(LIB_ROOT_REL)/src/ltablib.c \
 $(LIB_ROOT_REL)/src/ltm.c \
 $(LIB_ROOT_REL)/src/lua.c \
 $(LIB_ROOT_REL)/src/lundump.c \
 $(LIB_ROOT_REL)/src/lutf8lib.c \
 $(LIB_ROOT_REL)/src/lvm.c \
 $(LIB_ROOT_REL)/src/lzio.c \

LOCAL_C_INCLUDES += \
 $(LIB_ROOT_ABS)/src \

LOCAL_LDLIBS += -llog

LOCAL_MODULE := liblua

include $(BUILD_STATIC_LIBRARY)

I used ndk-build on this Android.mk file. This gave me liblua.a

Step 2: In the main Android.mk, I try to include this module.

include $(CLEAR_VARS)
LOCAL_MODULE    := liblua
LOCAL_SRC_FILES := liblua.a
include $(PREBUILT_STATIC_LIBRARY)

LOCAL_STATIC_LIBRARIES :=   cocos2dx_static \
                            liblua

This is working now!

Did you see the difference? I would test with static linking.

I didn’t used shared linking, but I used static linking for Firebase libraries and this worked for me.

Edit: Another idea is to change the name from myliblua to liblua for the reference.

Thanks it’s working now! Finally! I had to change the architecture too because I was building for different and cocos had a different one. By the way, a different question, but do you know where all the files in the Resources folder end up in Android!? I want to access a file but I don’t know where it goes in the APK!

Sure, the Resources are in the assets-folder within the APK.

Will you access them with Java or C++?

Java: You can access them with getAssets() from Context. The result is a AssetManager which can be used to open every file there.
C++: If it’s a text file -> FileUtils::getInstance()->getStringFromFile(“filename.ext”)

I want to access them using C++. And it’s a script file, so I need the whole file instead of just a string.

My fault. Your introduction said, that you are using lua.

But the C++ code should be the same for Android and iOS, if the Lua-script is within the Resources folder.

I didn’t use Lua, so I can’t really help with that. I found the following code on this page:

[...]
// Load the script file into Lua
auto file = FileUtils::getInstance()->fullPathForFilename("TestScript.lua");
luaL_loadfile(LuaState, file.c_str());
[...]

Maybe this is helpful for your problem.

Okay so I figured the reason. luaL_loadfile uses some file I/O calls which are not supported when running on Android. So instead of that I used this method which worked:

string contents = FileUtils::getInstance()->getStringFromFile(f);
if (luaL_loadbuffer (L, contents.c_str(), contents.size(), f.c_str()) || lua_pcall(L, 0, 0, 0))
{
    printf("%s\n", lua_tostring(L, -1));
}

But after I solve one, another problem comes. I can run all the Lua script files from my C++ code inside Android, but I can’t run any Lua code from inside another Lua code. the require() command in Lua script files now fails, apparently for the same reason as above. Does anyone know any solution to this!?

I get the following error in Lua whenever I try to require (“Pokemon”):

[string "Charmander.lua"]:1: module 'Pokemon' not found:
	no field package.preload['Pokemon']
	no file '/usr/local/share/lua/5.3/Pokemon.lua'
	no file '/usr/local/share/lua/5.3/Pokemon/init.lua'
	no file '/usr/local/lib/lua/5.3/Pokemon.lua'
	no file '/usr/local/lib/lua/5.3/Pokemon/init.lua'
	no file './Pokemon.lua'
	no file './Pokemon/init.lua'
	no file '/usr/local/lib/lua/5.3/Pokemon.so'
	no file '/usr/local/lib/lua/5.3/loadall.so'
	no file './Pokemon.so'

Okay, so I am able to read files using C++ code, so in order to read files from Lua, I am making a C function and then registering every script file.

    L = luaL_newstate();
	luaL_openlibs(L);

    registerFunction("requireLuaModule", [](lua_State *L)
        {
            string lfilename = lua_tostring(L, 1);
            string contents = FileUtils::getInstance()->getStringFromFile(lfilename);
            lua_pushstring(L, contents.c_str());
            return 1;
        }, this);

    string contents = FileUtils::getInstance()->getStringFromFile(filename);

	if (luaL_loadbuffer (L, contents.c_str(), contents.size(), filename.c_str()) || lua_pcall(L, 0, 0, 0))
	{
		printf("%s\n", lua_tostring(L, -1));
	}

and in Lua, I do this:
Pokemon = load(requireLuaModule("Pokemon.lua"))();

This works perfectly. But my only concern is the extra overhead. Is there is a better solution than this!? Moreover, depending on the platform I am building (Windows), I can comment the extra C++ code, but I can’t do the same in Lua code. And this step is unnecessary when building for Windows because there require() works perfectly. Any suggestion on how to tell Lua which to use: require() [Windows] or requireLuaModule() [Android]!?