Rick
Rick

Reputation: 451

How can I load a lua script relative to the currently loaded script?

I'm loading a lua script that tries to import a secondary lua script. The issue is that I load the initial script (Main.lua) in C++ and specify a path to this Lua, then when the Main.lua tries to run dofile on "Other.lua" it can't find it...

I get...

cannot open Other.lua: No such file or directory

main.cpp

#include "sol.hpp"
int main()
{
      sol::state m_lua;
      m_lua.open_libraries(sol::lib::base);
      sol::protected_function_result result = m_lua.script_file("../../../Desktop/Main.lua");
      if (!result.valid())
      {
         sol::error err = result;
         std::string what = err.what();
         std::cout << "Failed to load script: Test.lua - " << what << std::endl;
      }
   }
}

Main.lua

print("Main Loaded");
dofile("Other.lua");

Other.lua - Stored in the same folder as "Main.lua"

print("Other Loaded");

Upvotes: 0

Views: 1972

Answers (2)

Coal
Coal

Reputation: 344

If you're using LuaRocks, your configuration might have overriden lua's path default config, that will cause issues (it happens often, really)

There isn't any good guides about how to manage this, and as you get more advanced with Lua, you will face this kind of issues, so I had to write this for you and other people that might be experiencing these kind of issues.

There's many procedures you can take to fix this (nested relative imports can be pure madness)

  1. You can simply assign the current path to package.path

    package.path = package.path .. ';' .. ('./?.lua')

    package.path = package.path .. ';' .. ('./?/init.lua')

    . is the current directory, and ? gets replaced by the name of the script you passed to require(), this way it will forcedly have to search in the current directory as well.

In some projects, you will have to manage importing from nested folders and even importing from folders behind

Lua makes it especially hard to manage imports, in example, if you used require at a script which was already required from the root folder, you will have to index the script you want to load starting from the root folder

Instead of something like require(libs.classes.functions.public.dev.MyFunction) you can also do this:

  1. If the script is imported from an already nested script, you can use (...) in the root of the script to get the path of imports (only in Lua 5.1 and above)

    > current_path = (...) will equal to the path of the current script based on the relative path (if the script is already nested)

    It will return something similar to this: root.lib.classes.currentscriptname

    The last name will equal to the script name from where (...) was used.

  2. If you want to import from the script current folder, use (...):match('(.-)[^%.]+$'), this will allow you to import from the current script parent directory, that is, the folder where the current script is located at, not where the main script is located at.

    So: BASE = (...):match('(.-)[^%.]+$') and then require(BASE .. 'myLib') will import myLib from the folder that holds the current script.

In example, assuming you ran main.lua, located at main/, then imported a script which is called loader.lua, located at main/lib/classes, imagine you want to load two scripts:

  1. assets.lua which is located at main/lib
  2. bullet.lua which is located at main/lib/classes

You can do the following:

At your main.lua script:

-- main.lua
-- From Lua 5.1 and above

-- Since this script is not nested
-- this will only contain the name of the current script (without '.lua'), which is "main"
local CURRENT_PATH = (...) 

LIBS_PATH = CURRENT_PATH .. '.lib' -- This will hold "main.lib"

At your loader.lua script:

-- loader.lua
-- From Lua 5.1 and above

-- This will contain "main.lib.classes.", which already has a dot at the end
local CURRENT_PATH = (...):match('(.-)[^%.]+$')

-- SCRIPT_PATH will contain "main.lib.classes.loader"
local SCRIPT_PATH = (...)

-- Import from main/lib/assets.lua
local assets = require(LIB_PATH .. '.assets')
-- Import from current directory (main/lib/classes)
local bullet = require(BASE .. 'assets')

This will allow you to load both scripts from loader.lua without much hassle and without making an unnecessarily long path.

Upvotes: 1

DarkWiiPlayer
DarkWiiPlayer

Reputation: 7046

Just pass Main.lua the path where it should load the Other.lua script from C++ and prepend that to the filename before passing it to dofile in Lua.

Upvotes: 1

Related Questions