Skip to content

Commit 1b75d88

Browse files
authored
Merge pull request #53 from gilzoide/feature/package-path-settings
Package Path project settings
2 parents b1069c1 + 8f55760 commit 1b75d88

File tree

7 files changed

+164
-15
lines changed

7 files changed

+164
-15
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
- Optional support for `res://` and `user://` relative paths in package searchers, `loadfile` and `dofile`.
77
Open the `GODOT_LOCAL_PATHS` library to activate this behavior.
88
- `LuaState.LoadMode` enum for specifying the Lua load mode: text, binary or any
9-
- `LuaState.do_buffer` and `LuaState.load_buffer` for loading Lua code from possibly binary chunks
9+
- `LuaState.do_buffer` and `LuaState.load_buffer` methods for loading Lua code from possibly binary chunks
10+
- `LuaState.package_path` and `LuaState.package_cpath` properties for accessing the value of Lua's [`package.path`](https://www.lua.org/manual/5.4/manual.html#pdf-package.path) and [`package.cpath`](https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath)
11+
- `LuaState.get_lua_exec_dir` static method to get the executable directory used to replace "!" when setting `package_path` and `package_cpath` properties.
12+
When running in the Godot editor, it returns the globalized version of `res://` path.
13+
Otherwise, it returns the base directory of the executable.
14+
- Advanced project settings for setting the `LuaScriptLanguage` state's `package_path` and `package_cpath` properties
1015

1116
### Changed
1217
- The GDExtension is now marked as reloadable

src/LuaState.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@
2828
#include "utils/module_names.hpp"
2929

3030
#include <godot_cpp/core/binder_common.hpp>
31-
#include <godot_cpp/classes/file_access.hpp>
31+
#include <godot_cpp/classes/engine.hpp>
32+
#include <godot_cpp/classes/os.hpp>
33+
#include <godot_cpp/classes/project_settings.hpp>
34+
#include <luaconf.h>
3235

3336
namespace luagdextension {
3437

@@ -166,6 +169,58 @@ LuaTable *LuaState::get_registry() const {
166169
return memnew(LuaTable(lua_state.registry()));
167170
}
168171

172+
String LuaState::get_package_path() const {
173+
if (auto package = lua_state.get<sol::optional<sol::table>>("package")) {
174+
return package->get<String>("path");
175+
}
176+
else {
177+
ERR_FAIL_V_MSG("", "LUA_PACKAGE library is not opened");
178+
}
179+
}
180+
181+
String LuaState::get_package_cpath() const {
182+
if (auto package = lua_state.get<sol::optional<sol::table>>("package")) {
183+
return package->get<String>("cpath");
184+
}
185+
else {
186+
ERR_FAIL_V_MSG("", "LUA_PACKAGE library is not opened");
187+
}
188+
}
189+
190+
void LuaState::set_package_path(const String& path) {
191+
if (auto package = lua_state.get<sol::optional<sol::table>>("package")) {
192+
package->set("path",
193+
path.replace(";;", LUA_PATH_SEP LUA_PATH_DEFAULT LUA_PATH_SEP)
194+
.rstrip(LUA_PATH_SEP)
195+
.lstrip(LUA_PATH_SEP)
196+
.replace(LUA_EXEC_DIR, get_lua_exec_dir())
197+
);
198+
}
199+
else {
200+
ERR_FAIL_MSG("LUA_PACKAGE library is not opened");
201+
}
202+
}
203+
204+
void LuaState::set_package_cpath(const String& cpath) {
205+
if (auto package = lua_state.get<sol::optional<sol::table>>("package")) {
206+
package->set("cpath",
207+
cpath.replace(";;", LUA_PATH_SEP LUA_CPATH_DEFAULT LUA_PATH_SEP)
208+
.rstrip(LUA_PATH_SEP)
209+
.lstrip(LUA_PATH_SEP)
210+
.replace(LUA_EXEC_DIR, get_lua_exec_dir())
211+
);
212+
}
213+
else {
214+
ERR_FAIL_MSG("LUA_PACKAGE library is not opened");
215+
}
216+
}
217+
218+
String LuaState::get_lua_exec_dir() {
219+
return Engine::get_singleton()->is_editor_hint()
220+
? ProjectSettings::get_singleton()->globalize_path("res://")
221+
: OS::get_singleton()->get_executable_path().get_base_dir();
222+
}
223+
169224
LuaState *LuaState::find_lua_state(lua_State *L) {
170225
L = sol::main_thread(L);
171226
if (LuaState **ptr = valid_states.getptr(L)) {
@@ -219,10 +274,17 @@ void LuaState::_bind_methods() {
219274
ClassDB::bind_method(D_METHOD("do_file", "filename", "mode", "env"), &LuaState::do_file, DEFVAL(LOAD_MODE_ANY), DEFVAL(nullptr));
220275
ClassDB::bind_method(D_METHOD("get_globals"), &LuaState::get_globals);
221276
ClassDB::bind_method(D_METHOD("get_registry"), &LuaState::get_registry);
277+
ClassDB::bind_method(D_METHOD("get_package_path"), &LuaState::get_package_path);
278+
ClassDB::bind_method(D_METHOD("get_package_cpath"), &LuaState::get_package_cpath);
279+
ClassDB::bind_method(D_METHOD("set_package_path", "path"), &LuaState::set_package_path);
280+
ClassDB::bind_method(D_METHOD("set_package_cpath", "cpath"), &LuaState::set_package_cpath);
281+
ClassDB::bind_static_method(LuaState::get_class_static(), D_METHOD("get_lua_exec_dir"), &LuaState::get_lua_exec_dir);
222282

223283
// Properties
224284
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "globals", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE, LuaTable::get_class_static()), "", "get_globals");
225285
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "registry", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE, LuaTable::get_class_static()), "", "get_registry");
286+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "package_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_package_path", "get_package_path");
287+
ADD_PROPERTY(PropertyInfo(Variant::STRING, "package_cpath", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_package_cpath", "get_package_cpath");
226288
}
227289

228290
LuaState::operator String() const {

src/LuaState.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,14 @@ class LuaState : public RefCounted {
113113
LuaTable *get_globals() const;
114114
LuaTable *get_registry() const;
115115

116+
String get_package_path() const;
117+
String get_package_cpath() const;
118+
void set_package_path(const String& path);
119+
void set_package_cpath(const String& cpath);
120+
116121
operator String() const;
117122

123+
static String get_lua_exec_dir();
118124
static LuaState *find_lua_state(lua_State *L);
119125

120126
protected:

src/luaopen/local_paths.cpp

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,12 @@
2121
*/
2222

2323
#include "../LuaTable.hpp"
24+
#include "../generated/package_searcher.h"
2425
#include "../utils/convert_godot_lua.hpp"
2526
#include "../utils/load_fileaccess.hpp"
26-
#include "sol/forward.hpp"
27-
#include "sol/load_result.hpp"
2827

29-
#include <godot_cpp/classes/engine.hpp>
3028
#include <godot_cpp/classes/file_access.hpp>
31-
#include <godot_cpp/classes/os.hpp>
32-
#include <godot_cpp/classes/project_settings.hpp>
33-
34-
#include "../generated/package_searcher.h"
29+
#include <luaconf.h>
3530

3631
using namespace luagdextension;
3732

@@ -43,15 +38,11 @@ static int l_searchpath(lua_State *L) {
4338
if (!sep.is_empty()) {
4439
name = name.replace(sep, rep);
4540
}
46-
47-
String execdir_repl = Engine::get_singleton()->is_editor_hint()
48-
? ProjectSettings::get_singleton()->globalize_path("res://")
49-
: OS::get_singleton()->get_executable_path().get_base_dir();
5041

51-
PackedStringArray path_list = path.split(";", false);
42+
PackedStringArray path_list = path.split(LUA_PATH_SEP, false);
5243
PackedStringArray not_found_list;
5344
for (const String& path_template : path_list) {
54-
String filename = path_template.replace("?", name).replace("!", execdir_repl);
45+
String filename = path_template.replace(LUA_PATH_MARK, name);
5546
if (FileAccess::file_exists(filename)) {
5647
sol::stack::push(L, filename);
5748
return 1;

src/script-language/LuaScriptLanguage.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "../utils/function_wrapper.hpp"
3333

3434
#include <godot_cpp/classes/engine.hpp>
35+
#include <godot_cpp/classes/project_settings.hpp>
3536
#include <godot_cpp/classes/reg_ex.hpp>
3637
#include <godot_cpp/classes/reg_ex_match.hpp>
3738
#include <godot_cpp/classes/resource_loader.hpp>
@@ -40,6 +41,19 @@
4041

4142
namespace luagdextension {
4243

44+
constexpr char LUA_PATH_SETTING[] = "lua_gdextension/lua_script_language/package_path";
45+
constexpr char LUA_CPATH_SETTING[] = "lua_gdextension/lua_script_language/package_c_path";
46+
constexpr char LUA_CPATH_WINDOWS_SETTING[] = "lua_gdextension/lua_script_language/package_c_path.windows";
47+
constexpr char LUA_CPATH_MACOS_SETTING[] = "lua_gdextension/lua_script_language/package_c_path.macos";
48+
49+
static void add_project_setting(ProjectSettings *project_settings, const String& setting_name, const Variant& initial_value, bool is_basic = false) {
50+
if (!project_settings->has_setting(setting_name)) {
51+
project_settings->set_setting(setting_name, initial_value);
52+
}
53+
project_settings->set_initial_value(setting_name, initial_value);
54+
project_settings->set_as_basic(setting_name, is_basic);
55+
}
56+
4357
String LuaScriptLanguage::_get_name() const {
4458
return "Lua";
4559
}
@@ -57,6 +71,17 @@ void LuaScriptLanguage::_init() {
5771
LuaScriptMethod::register_lua(state);
5872
LuaScriptProperty::register_lua(state);
5973
LuaScriptSignal::register_lua(state);
74+
75+
// Register project settings
76+
ProjectSettings *project_settings = ProjectSettings::get_singleton();
77+
add_project_setting(project_settings, LUA_PATH_SETTING, "res://?.lua;res://?/init.lua");
78+
add_project_setting(project_settings, LUA_CPATH_SETTING, "!/?.so;!/loadall.so");
79+
add_project_setting(project_settings, LUA_CPATH_WINDOWS_SETTING, "!/?.dll;!/loadall.dll");
80+
add_project_setting(project_settings, LUA_CPATH_MACOS_SETTING, "!/?.dylib;!/loadall.dylib");
81+
82+
// Apply project settings (package.path, package.cpath)
83+
lua_state->set_package_path(project_settings->get_setting_with_override(LUA_PATH_SETTING));
84+
lua_state->set_package_cpath(project_settings->get_setting_with_override(LUA_CPATH_SETTING));
6085
}
6186

6287
String LuaScriptLanguage::_get_type() const {
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
extends RefCounted
2+
3+
var lua_state
4+
var initial_package_path
5+
var initial_package_cpath
6+
7+
8+
func _init():
9+
lua_state = LuaState.new()
10+
lua_state.open_libraries()
11+
initial_package_path = lua_state.package_path
12+
initial_package_cpath = lua_state.package_cpath
13+
14+
15+
func test_package_path() -> bool:
16+
var package_path = "res://?.lua"
17+
lua_state.package_path = package_path
18+
assert(lua_state.package_path == package_path)
19+
assert(lua_state.globals.package.path == package_path)
20+
assert(lua_state.globals.package.cpath != package_path)
21+
return true
22+
23+
24+
func test_package_cpath() -> bool:
25+
var package_cpath = "!/?.so"
26+
lua_state.package_cpath = package_cpath
27+
package_cpath = package_cpath.replace("!", LuaState.get_lua_exec_dir())
28+
assert(lua_state.package_cpath == package_cpath)
29+
assert(lua_state.globals.package.path != package_cpath)
30+
assert(lua_state.globals.package.cpath == package_cpath)
31+
return true
32+
33+
34+
func test_package_path_default() -> bool:
35+
lua_state.package_path = ""
36+
assert(lua_state.package_path == "")
37+
lua_state.package_path = ";;"
38+
assert(lua_state.package_path.replace("\\", "/") == initial_package_path.replace("\\", "/"), "Expected '%s', got '%s'" % [initial_package_path.replace("\\", "/"), lua_state.package_path.replace("\\", "/")])
39+
lua_state.package_path = "prefix;;"
40+
assert(lua_state.package_path.replace("\\", "/") == "prefix;" + initial_package_path.replace("\\", "/"), "Expected '%s', got '%s'" % ["prefix;" + initial_package_path.replace("\\", "/"), lua_state.package_path.replace("\\", "/")])
41+
lua_state.package_path = ";;suffix"
42+
assert(lua_state.package_path.replace("\\", "/") == initial_package_path.replace("\\", "/") + ";suffix", "Expected '%s', got '%s'" % [initial_package_path.replace("\\", "/") + ";suffix", lua_state.package_path.replace("\\", "/")])
43+
lua_state.package_path = "prefix;;suffix"
44+
assert(lua_state.package_path.replace("\\", "/") == "prefix;" + initial_package_path.replace("\\", "/") + ";suffix", "Expected '%s', got '%s'" % ["prefix;" + initial_package_path.replace("\\", "/") + ";suffix", lua_state.package_path.replace("\\", "/")])
45+
return true
46+
47+
48+
func test_package_cpath_default() -> bool:
49+
lua_state.package_cpath = ""
50+
assert(lua_state.package_cpath == "")
51+
lua_state.package_cpath = ";;"
52+
assert(lua_state.package_cpath.replace("\\", "/") == initial_package_cpath.replace("\\", "/"), "Expected '%s', got '%s'" % [initial_package_path.replace("\\", "/"), lua_state.package_path.replace("\\", "/")])
53+
lua_state.package_cpath = "prefix;;"
54+
assert(lua_state.package_cpath.replace("\\", "/") == "prefix;" + initial_package_cpath.replace("\\", "/"), "Expected '%s', got '%s'" % ["prefix;" + initial_package_cpath.replace("\\", "/"), lua_state.package_path.replace("\\", "/")])
55+
lua_state.package_cpath = ";;suffix"
56+
assert(lua_state.package_cpath.replace("\\", "/") == initial_package_cpath.replace("\\", "/") + ";suffix", "Expected '%s', got '%s'" % [initial_package_cpath.replace("\\", "/") + ";suffix", lua_state.package_path.replace("\\", "/")])
57+
lua_state.package_cpath = "prefix;;suffix"
58+
assert(lua_state.package_cpath.replace("\\", "/") == "prefix;" + initial_package_cpath.replace("\\", "/") + ";suffix", "Expected '%s', got '%s'" % ["prefix;" + initial_package_cpath.replace("\\", "/") + ";suffix", lua_state.package_path.replace("\\", "/")])
59+
return true
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
uid://bsfmpr4en0xje

0 commit comments

Comments
 (0)