-
Notifications
You must be signed in to change notification settings - Fork 35
[200_69] Fix gfproject.json merge logic #695
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3940,21 +3940,67 @@ find_gfproject_json (const char* gf_lib) { | |
|
|
||
| static json | ||
| load_gfproject_config (const char* gf_lib) { | ||
| fs::path config_path= find_gfproject_json (gf_lib); | ||
| if (config_path.empty ()) { | ||
| // Find both gfproject.json files if they exist | ||
| std::error_code ec; | ||
| fs::path cwd= fs::current_path (ec); | ||
| fs::path local_path= cwd / "gfproject.json"; | ||
| bool local_exists= !ec && fs::exists (local_path, ec) && fs::is_regular_file (local_path, ec); | ||
|
|
||
| fs::path lib_path= fs::path (gf_lib) / "gfproject.json"; | ||
| bool lib_exists= fs::exists (lib_path, ec) && fs::is_regular_file (lib_path, ec); | ||
|
|
||
| // If neither exists, return empty object | ||
| if (!local_exists && !lib_exists) { | ||
| return json::object (); | ||
| } | ||
| try { | ||
| std::ifstream file (config_path); | ||
| if (!file.is_open ()) { | ||
| return json::object (); | ||
| } | ||
| json config; | ||
| file >> config; | ||
| return config; | ||
| } catch (...) { | ||
| return json::object (); | ||
|
|
||
| // Start with lib config as base (only tools section will be merged) | ||
| json merged= json::object (); | ||
| json lib_tools= json::object (); | ||
| if (lib_exists) { | ||
| try { | ||
| std::ifstream lib_file (lib_path); | ||
| if (lib_file.is_open ()) { | ||
| json lib_config; | ||
| lib_file >> lib_config; | ||
| if (lib_config.contains ("tools")) { | ||
| lib_tools= lib_config["tools"]; | ||
| } | ||
| } | ||
| } catch (...) { | ||
| // Ignore parse errors for lib config | ||
| } | ||
| } | ||
|
|
||
| // Get local tools (local takes precedence) | ||
| json local_tools= json::object (); | ||
| if (local_exists) { | ||
| try { | ||
| std::ifstream local_file (local_path); | ||
| if (local_file.is_open ()) { | ||
| json local_config; | ||
| local_file >> local_config; | ||
| if (local_config.contains ("tools")) { | ||
| local_tools= local_config["tools"]; | ||
| } | ||
| } | ||
| } catch (...) { | ||
| // Ignore parse errors for local config | ||
| } | ||
| } | ||
|
|
||
| // Merge tools: lib as base, local overrides (except "help" which is always internal) | ||
| json merged_tools= lib_tools; | ||
| for (auto& [key, value] : local_tools.items ()) { | ||
| // "help" tool is never merged - always use internal implementation | ||
| if (key == "help") { | ||
| continue; | ||
| } | ||
| merged_tools[key]= value; | ||
| } | ||
| merged["tools"]= merged_tools; | ||
|
|
||
| return merged; | ||
| } | ||
|
|
||
| static string | ||
|
|
@@ -3989,25 +4035,17 @@ goldfish_run_tool (s7_scheme* sc, const char* gf_lib, const string& command, | |
| json tool_config= config["tools"][command]; | ||
|
|
||
| // Check if tool has implementation (organization and module) | ||
| // If not complete, fall back to internal implementation (return -1) | ||
| if (!tool_config.contains ("organization") || !tool_config.contains ("module")) { | ||
| cerr << "Error: Tool '" << command << "' is not fully implemented (missing organization or module)." | ||
| << endl; | ||
| s7_close_output_port (sc, s7_current_error_port (sc)); | ||
| s7_set_current_error_port (sc, old_port); | ||
| if (gc_loc != -1) s7_gc_unprotect_at (sc, gc_loc); | ||
| return 1; | ||
| return -1; // Fall back to internal implementation | ||
| } | ||
|
|
||
| string org = tool_config["organization"]; | ||
| string module = tool_config["module"]; | ||
| string tool_root= find_tool_root_by_command (gf_lib, command); | ||
|
|
||
| if (tool_root.empty ()) { | ||
| cerr << "Error: tools/" << command << "/" << org << " directory not found." << endl; | ||
| s7_close_output_port (sc, s7_current_error_port (sc)); | ||
| s7_set_current_error_port (sc, old_port); | ||
| if (gc_loc != -1) s7_gc_unprotect_at (sc, gc_loc); | ||
| return 1; | ||
| return -1; // Fall back to internal implementation | ||
| } | ||
|
|
||
| s7_add_to_load_path (sc, tool_root.c_str ()); | ||
|
|
@@ -5315,8 +5353,9 @@ repl_for_community_edition (s7_scheme* sc, int argc, char** argv) { | |
| } | ||
|
|
||
| // 处理动态注册的工具(从 gfproject.json 加载) | ||
| // 注意:"help" 命令始终使用内部实现,不从 gfproject.json 加载 | ||
| json gfproject_config = load_gfproject_config (gf_lib); | ||
| if (gfproject_config.contains ("tools") && gfproject_config["tools"].contains (command)) { | ||
| if (command != "help" && gfproject_config.contains ("tools") && gfproject_config["tools"].contains (command)) { | ||
| int tool_ret = goldfish_run_tool (sc, gf_lib, command, errmsg, old_port, gc_loc); | ||
| if (tool_ret != -1) { | ||
| // Tool was found and executed (or failed with an error) | ||
|
|
@@ -5325,6 +5364,15 @@ repl_for_community_edition (s7_scheme* sc, int argc, char** argv) { | |
| // If tool_ret == -1, tool config exists but execution failed, fall through to check other commands | ||
| } | ||
|
Comment on lines
5357
to
5365
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
json gfproject_config = load_gfproject_config (gf_lib);
if (command != "help" && gfproject_config.contains ("tools") && gfproject_config["tools"].contains (command)) {
int tool_ret = goldfish_run_tool (sc, gf_lib, command, gfproject_config, errmsg, old_port, gc_loc);Then update |
||
|
|
||
| // 处理 help 命令(始终使用内部实现) | ||
| if (command == "help") { | ||
| display_help (); | ||
| s7_close_output_port (sc, s7_current_error_port (sc)); | ||
| s7_set_current_error_port (sc, old_port); | ||
| if (gc_loc != -1) s7_gc_unprotect_at (sc, gc_loc); | ||
| return 0; | ||
| } | ||
|
|
||
| // 处理 eval 子命令 | ||
| if (command == "eval") { | ||
| if (argc < command_index + 1) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,23 +35,79 @@ | |
| ) ;export | ||
| (begin | ||
|
|
||
| (define (find-gfproject-path) | ||
| "Search for gfproject.json in current directory" | ||
| (define (find-gfproject-path kind) | ||
| "Search for gfproject.json - 'local or 'lib" | ||
| (let ((cwd (getcwd))) | ||
| (if cwd | ||
| (let ((test-path (path->string (path-join (path cwd) (path "gfproject.json"))))) | ||
| (if (file-exists? test-path) | ||
| test-path | ||
| #f)) | ||
| #f))) | ||
| (cond | ||
| ((eq? kind 'local) | ||
| (if cwd | ||
| (let ((test-path (path->string (path-join (path cwd) (path "gfproject.json"))))) | ||
| (if (file-exists? test-path) | ||
| test-path | ||
| #f)) | ||
| #f)) | ||
| ((eq? kind 'lib) | ||
| ;; First try GF_LIB environment variable | ||
| (let ((gf-lib (getenv "GF_LIB"))) | ||
| (if gf-lib | ||
| (let ((lib-path (path->string (path-join (path gf-lib) (path "gfproject.json"))))) | ||
| (if (file-exists? lib-path) | ||
| lib-path | ||
| #f)) | ||
| ;; Fallback: search from executable location upward | ||
| (let ((exe-path (executable))) | ||
| (if exe-path | ||
| (let search ((dir (path-dirname (path exe-path)))) | ||
| (if dir | ||
| (let ((gfproject-path (path->string (path-join dir (path "gfproject.json"))))) | ||
| (if (file-exists? gfproject-path) | ||
| gfproject-path | ||
| (search (path-dirname dir)))) | ||
| #f)) | ||
| #f))))) | ||
|
Comment on lines
+58
to
+67
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The fallback search walks up the directory tree by repeatedly calling (let search ((dir (path-dirname (path exe-path))))
(if dir
(let ((parent (path-dirname dir)))
(if (or (not parent)
(string=? (path->string dir) (path->string parent)))
#f
(let ((gfproject-path (path->string (path-join dir (path "gfproject.json")))))
(if (file-exists? gfproject-path)
gfproject-path
(search parent)))))
#f)) |
||
| (else #f)))) | ||
|
|
||
| (define (json-merge-tools lib-tools local-tools) | ||
| "Merge two tools JSON objects, local takes precedence (except 'help')" | ||
| (cond | ||
| ((json-null? lib-tools) | ||
| (if (json-null? local-tools) | ||
| (make-json) | ||
| local-tools)) | ||
| ((json-null? local-tools) | ||
| lib-tools) | ||
| (else | ||
| ;; Start with lib tools as base, then add local tools (except "help") | ||
| (let ((merged (make-json))) | ||
| ;; Add lib tools (except "help") | ||
| (for-each | ||
| (lambda (key) | ||
| (when (not (string=? key "help")) | ||
| (json-set! merged key (json-ref lib-tools key)))) | ||
| (json-keys lib-tools)) | ||
| ;; Add local tools (except "help"), overwriting lib values | ||
| (for-each | ||
| (lambda (key) | ||
| (when (not (string=? key "help")) | ||
| (json-set! merged key (json-ref local-tools key)))) | ||
| (json-keys local-tools)) | ||
| merged))))) | ||
|
|
||
| (define (load-gfproject) | ||
| "Load and parse gfproject.json, return tools object" | ||
| (let ((path (find-gfproject-path))) | ||
| (if path | ||
| (let ((content (path-read-text path))) | ||
| (string->json content)) | ||
| (string->json "{}")))) | ||
| "Load and merge gfproject.json from local and lib, local takes precedence" | ||
| (let ((local-path (find-gfproject-path 'local)) | ||
| (lib-path (find-gfproject-path 'lib))) | ||
| (let ((lib-config (if lib-path | ||
| (let ((content (path-read-text lib-path))) | ||
| (string->json content)) | ||
| (make-json))) | ||
| (local-config (if local-path | ||
| (let ((content (path-read-text local-path))) | ||
| (string->json content)) | ||
| (make-json)))) | ||
| (let ((lib-tools (json-ref lib-config "tools")) | ||
| (local-tools (json-ref local-config "tools"))) | ||
| (json-merge-tools lib-tools local-tools))))) | ||
|
Comment on lines
96
to
+110
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The fix is to wrap the merged tools back into a config object: (define (load-gfproject)
"Load and merge gfproject.json from local and lib, local takes precedence"
(let ((local-path (find-gfproject-path 'local))
(lib-path (find-gfproject-path 'lib)))
(let ((lib-config (if lib-path
(string->json (path-read-text lib-path))
(make-json)))
(local-config (if local-path
(string->json (path-read-text local-path))
(make-json))))
(let ((merged (make-json)))
(json-set! merged "tools"
(json-merge-tools (json-ref lib-config "tools")
(json-ref local-config "tools")))
merged)))) |
||
|
|
||
| (define (get-tool-description tools tool-name lang) | ||
| "Get description for a tool in specified language" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tool_ret == -1The comment says "tool config exists but execution failed", but
-1is now returned for several non-failure conditions: missingorganization/modulefields, or missingtool_root. A more accurate comment would be: