From 013233c041d0e7dc78b07f94916c5897461dccc1 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Mon, 25 Aug 2025 08:31:12 +0300 Subject: [PATCH] scm: clean up scm running Split run_script / run_repl more clearly; update callers. --- mu/mu-cmd-server.cc | 5 +- mu/mu-cmd.cc | 5 +- scm/mu-scm.cc | 119 ++++++++++++++++++++------------------------ scm/mu-scm.hh | 35 +++++++++---- 4 files changed, 84 insertions(+), 80 deletions(-) diff --git a/mu/mu-cmd-server.cc b/mu/mu-cmd-server.cc index 280ec4e3..407ba2b9 100644 --- a/mu/mu-cmd-server.cc +++ b/mu/mu-cmd-server.cc @@ -125,12 +125,13 @@ maybe_listen_path(const Mu::Store& store, const Mu::Options& opts) #ifdef BUILD_SCM if (!opts.scm.socket_path) return {}; - const auto res = Mu::Scm::run(store, opts, false/*!block*/); + const auto socket_path{*opts.scm.socket_path}; + const auto res = Mu::Scm::run_repl(store, opts, socket_path); if (!res) { mu_warning("failed to start scm socket: {}", res.error().what()); return {}; } else - return *opts.scm.socket_path; + return socket_path; #endif /*BUILD_SCM*/ return {}; } diff --git a/mu/mu-cmd.cc b/mu/mu-cmd.cc index 522d7316..f358311b 100644 --- a/mu/mu-cmd.cc +++ b/mu/mu-cmd.cc @@ -73,7 +73,10 @@ cmd_scm(const Store& store, const Options& opts) return Err(Error::Code::InvalidArgument, "scm/guile is not available in this build"); #else - return Mu::Scm::run(store, opts, true/*blocking*/); + if (opts.scm.script_path) + return Mu::Scm::run_script(store, opts, *opts.scm.script_path); + else + return Mu::Scm::run_repl(store, opts, opts.scm.socket_path.value_or("")); #endif /*BUILD_SCM*/ } diff --git a/scm/mu-scm.cc b/scm/mu-scm.cc index 40718c20..63cb4044 100644 --- a/scm/mu-scm.cc +++ b/scm/mu-scm.cc @@ -133,7 +133,7 @@ std::thread scm_worker; } static Result -prepare_run(const Mu::Options& opts) +prepare_run(const Mu::Options& opts, StrVec& args) { // do a checks _before_ entering guile, so we get a bit more civilized // error message. @@ -147,37 +147,11 @@ prepare_run(const Mu::Options& opts) else return Err(path.error()); - if (opts.scm.script_path) { - const auto path{opts.scm.script_path->c_str()}; - if (const auto res = ::access(path, R_OK); res != 0) { - return Err(Error::Code::InvalidArgument, - "cannot read '{}': {}", path, ::strerror(errno)); - } - } + args = {"mu", "-l", mu_scm_path}; return Ok(); } -static void -prepare_script(const Options& opts, StrVec& args) -{ - static std::string cmd; // keep alive - - // XXX: couldn't get another combination of -l/-s/-e/-c to work - // a) invokes `main' with arguments, and - // b) exits (rather than drop to a shell) - // but, what works is to manually specify (main ....) - cmd = "(main " + quote(*opts.scm.script_path); - for (const auto& scriptarg : opts.scm.params) - cmd += " " + quote(scriptarg); - cmd += ")"; - - args.emplace_back("-l"); - args.emplace_back(*opts.scm.script_path); - args.emplace_back("-c"); - args.emplace_back(cmd); -} - static void maybe_remove_socket_path() { @@ -197,25 +171,6 @@ maybe_remove_socket_path() } } -static void -prepare_shell(const Options& opts, StrVec& args) -{ - // drop us into an interactive shell/repl or start listening on a domain socket. - if (opts.scm.listen && opts.scm.socket_path) { - mu_scm_socket_path = *opts.scm.socket_path; - g_setenv(SOCKET_PATH_ENV, mu_scm_socket_path.c_str(), 1); - mu_info("setting up socket-path {}", mu_scm_socket_path); - ::atexit(maybe_remove_socket_path); //opportunistic cleanup - } - else - g_unsetenv(SOCKET_PATH_ENV); - - args.emplace_back("--no-auto-compile"); - args.emplace_back("-l"); - args.emplace_back(mu_scm_repl_path); -} - - struct ModMuData { const Mu::Store& store; const Mu::Options& opts; }; static void @@ -253,36 +208,69 @@ run_scm(const Mu::Store& store, const Mu::Options& opts) } Result -Mu::Scm::run(const Mu::Store& store, const Mu::Options& opts, bool blocking) +Mu::Scm::run_repl(const Mu::Store& store, const Mu::Options& opts, + const std::string& socket_path) { - if (const auto res = prepare_run(opts); !res) + if (const auto res = prepare_run(opts, scm_args); !res) return Err(res.error()); - scm_args = {"mu", "-l", mu_scm_path}; + scm_args.emplace_back("--no-auto-compile"); + scm_args.emplace_back("-l"); + scm_args.emplace_back(mu_scm_repl_path); - // do env stuff _before_ starting guile / threads. - if (opts.scm.script_path) - prepare_script(opts, scm_args); - else - prepare_shell(opts, scm_args); + if (!socket_path.empty()) { + mu_scm_socket_path = socket_path; + g_setenv(SOCKET_PATH_ENV, mu_scm_socket_path.c_str(), 1); + mu_info("setting up socket-path {}", mu_scm_socket_path); + ::atexit(maybe_remove_socket_path); //opportunistic cleanup - // in the non-blocking case, we start guile in a - // background thread; otherwise it will block. - if (!blocking) { + // if a socket-path is provided, run in a background thread + // and offer a REPL on a Unix domain socket on said socket_path auto worker = std::thread([&](){ -#ifdef HAVE_PTHREAD_SETNAME_NP - pthread_setname_np(pthread_self(), "mu-scm"); -#endif /*HAVE_PTHREAD_SETNAME_NP*/ + set_thread_name("mu-scm"); run_scm(store, opts); }); worker.detach(); - } else + } else { // otherwise, a normal, interactive shell + g_unsetenv(SOCKET_PATH_ENV); run_scm(store, opts); + } return Ok(); - } +Result +Mu::Scm::run_script(const Mu::Store& store, const Mu::Options& opts, + const std::string& script_path) +{ + if (script_path.empty()) + return Err(Error::Code::InvalidArgument, "missing script path"); + + if (const auto res = ::access(script_path.c_str(), R_OK); res != 0) + return Err(Error::Code::InvalidArgument, + "cannot read '{}': {}", script_path, ::strerror(errno)); + + if (const auto res = prepare_run(opts, scm_args); !res) + return Err(res.error()); + + // XXX: couldn't get another combination of -l/-s/-e/-c to work + // a) invokes `main' with arguments, and + // b) exits (rather than drop to a shell) + // but, what works is to manually specify (main ....) + std::string cmd = "(main " + quote(script_path); + for (const auto& scriptarg : opts.scm.params) + cmd += " " + quote(scriptarg); + cmd += ")"; + + scm_args.emplace_back("-l"); + scm_args.emplace_back(script_path); + scm_args.emplace_back("-c"); + scm_args.emplace_back(cmd); + + run_scm(store, opts); + + return Ok(); +} #ifdef BUILD_TESTS @@ -325,10 +313,9 @@ test_scm_script() } Mu::Options opts{}; - opts.scm.script_path = join_paths(MU_SCM_SRCDIR, "mu-scm-test.scm"); - { - const auto res = Mu::Scm::run(*store, opts, false /*blocks*/); + const auto script_path{join_paths(MU_SCM_SRCDIR, "mu-scm-test.scm")}; + const auto res = Mu::Scm::run_script(*store, opts, script_path); assert_valid_result(res); } } diff --git a/scm/mu-scm.hh b/scm/mu-scm.hh index 840a76bb..e2133315 100644 --- a/scm/mu-scm.hh +++ b/scm/mu-scm.hh @@ -40,23 +40,36 @@ */ namespace Mu::Scm { /** - * Start a guile REPL or program + * Start a Guile/SCM REPL * - * Initialize the Scm sub-system, then start a REPL or run a script, - * based on the configuration. - * - * Unless 'blocking' is false or there is some pre-guile error, this - * method never returns. If blocking is false, it runs in the - * background. + * Initialize the Scm sub-system, then start a REPL, based on the + * configuration. * * @param store a Store object - * @param opts options - * @param blocking whether to block (or run in the background) + * @param opts options; opts.scm.script_path must be Nothing + * @param socket_path if non-empty, run in the background on the + * socket path (Unix domain socket); otherwise, run an + * interactive shell in blocking mode. * * @return Ok() or some error */ - Result run(const Store& store, const Options& opts, - bool blocking=true); + Result run_repl(const Store& store, const Options& opts, + const std::string& socket_path = {}); + + /** + * Run a Guile/SCM script + * + * Initialize the Scm sub-system, then start run a script, + * based on the configuration. + * + * @param store a Store object + * @param opts options; opts.scm.script_path must set + * + * @return Ok() or some error + */ + Result run_script(const Store& store, const Options& opts, + const std::string& script_path); + /** * Helpers