Closed haruki7049 closed 1 month ago
This may not be why CFiggers uses declare-binscript
, but I do it because it's easier to debug things (I can edit the script in-place) and it doesn't rely on a user having to compile anything.
I could remember it all wrong, but there was a change because of the loading of local modules. For tools, I am also inclined to use the declare-binscript
. But for the release build of my apps, I strictly use declare-executable
with compile-time configuration.
It can be my Janet-lang's mannerisms :-D.
I understood that it is one of the debugging methods, is that correct?
Hi @haruki7049 —great question. The answer is, Janet LSP strategically runs on the user's install of Janet. This does two things that are useful for the LSP and are preferable to having an executable:
It makes Janet LSP fully cross-platform WITHOUT needing anybody (either me or the user of Janet LSP) to compile a bunch of binaries. If I were to make Janet LSP executable-first, then either I would need to compile the binary for all the different platforms (Windows, Mac, Linux, ARM, RISC-V) or else the user would have to do so upon downloading it (which can be a problem if the user doesn't have access to a C compiler, like Windows users by default unless they have installed Visual Studio/the MSVC or MINGW toolchains). I first ran into this because I want to distribute Janet LSP along with Janet++, my Janet support extension for VS Code. I didn't want to package five different executables in order to support all the different platforms—so instead, I distribute a .jimage file and have the user's own Janet execute it. That's essentially the same pattern as installing a binscript, I just don't "install" it beyond the extension itself.
It means that the LSP will always have the same access to Janet (same version, same env vars, same installed modules) as the user's own copy of Janet. This is particularly important for an LSP, since each user wants to get hover documentation and diagnostic warnings in their editor that will match what they would get by running janet
at their own command line. If a user is running Janet 1.33.0 and doesn't have the newer functions added in Janet 1.35.0, then Janet LSP will similarly not recognize those functions just the way they would expect. If the user has a custom install path set up for their Janet, they expect the modules installed in their custom library path to be recognized in their editor. By running Janet LSP as a binscript, they will be. And if the user's install of Janet ever changes—increments the version, changes the install path, changes env variables, whatever—the LSP will instantly reflect that no matter what (because it's just using the user's Janet to begin with). By piggy-backing on the user's Janet install, I'm making the LSP behave much more like the user might expect WITHOUT having to do a bunch of fancy detection stuff to make an executable pretend like it is the same as their install.
For other examples of installing Janet CLI tooling as binscripts, see ianthehenry's Judge and the first-party jpm tool, both of which install binscripts rather than executables. It's a very well-supported paradigm! 🙂 Of course, the caveat with this approach is that we have to assume that the end user has a copy of Janet installed. But given that Janet LSP is editor tooling for Janet, and is literally useless to anyone who is not a Janet programmer, I find this a safe assumption to make! 😁
Two other notes:
You are of course very free to compile Janet LSP into an executable if you so prefer. That's why I've left those lines in project.janet
, just commented out—uncomment those lines and comment out the declare-binscript
form, then run jpm build
(and optionally jpm install
) and you're off to the races. Be aware that you'll lose some of the benefits of running the LSP with your Janet install—diagnostics will be less reliable, the LSP may not detect installed third-party modules, and the Janet LSP executable won't stay in sync when you update your Janet version (the binscript will, since it's just running your Janet install to begin with!).
You should be aware that in general compiling an executable binary with Janet is basically just sticking a copy of the Janet runtime that you have installed plus a compiled image of the program together into the executable bundle. So the run time characteristics of an executable vs a script or .jimage is not wildly different. You'll get slightly faster startup time vs compiling a script (you skip the need to read in, parse, and compile the script file—that all happened when you compiled the executable) but once the program begins running it's almost identical regardless. So Janet executables are great for distribution (you can give a copy of the compiled executable to someone else and they don't need Janet installed for it to work, as long as their architecture matches and any shared libraries are present). But compiling an executable out of Janet code gives you only minor performance benefits (or maybe the more positive way of saying it is, there's very little performance downside to running scripts via the Janet executable as opposed to compiling self-contained binaries 😉).
Thanks for the very thorough explanation. I am a little bit bad at English so I had to use Deepl to read it.
Two things I understand now.
Are these two points correct?
This is a bit off topic, but I am currently working on a library that uses Nix, a package manager and build system, to build a project for Janet-lang.
buildJanetPackage currently only supports installation in binary format using declare-executable
, and does not support installation in script format using declare-binscript
, or installation of the .jimage
of the build result.
From what you have said, I have learned about the effective use of declare-binscript
and .jimage
. So we will actively support both of them in the Nix library.
I think specifically for janet-lsp, the following point mentioned by @CFiggers is additionally relevant:
It means that the LSP will always have the same access to Janet (same version, same env vars, same installed modules) as the user's own copy of Janet.
That's not the same point as either debuggability or for cross-platform matters.
Regarding Nix / Guix and Janet, there are some discussions about that on the Janet Zulip instance as well as at the main Janet repository:
I close this issue once my questions have been answered. Thanks for all your answers.
My apologies to CFiggers for my poor manners.
Sorry!
My apologies to CFiggers for my poor manners.
Sorry!
No poor manners detected, friend! No apology needed. Actually, I'm not even sure what you're referring to.
I am smiling at how well you guys seem to get along....
No poor manners detected, friend! No apology needed. Actually, I'm not even sure what you're referring to.
You are most gracious.
In retrospect, I think I should have waited for you to respond before my barging in. Too quick to trigger (^^;
One last breach, if I may (^^;
@haruki7049 Please feel free to join us at the Zulip instance if you wish.
I created my account for janet.zulipchat.com
:)
https://janet.zulipchat.com/#user/758940
I like binary formats, so I am wondering why @CFiggers are using
declare-binscript
.I am new to Janet-lang and do not know the manner in which it is done in Janet-lang. If your reasons for using declare-binscript include Janet-lang's mannerisms, I would be glad to know.