clj-on-windows

chaos 2022-10-08T08:06:10.625659Z

Hi @seancorfield, we all agree the .exe file is a much better solution to go with, but still it does not address the quoting issue though, it can fail on nested quotes (as per below example). I have an idea which has the potential to elegantly solve the quoting issue irrespective of OS and executable type that I need to put to trial. Quoting should be seen as an inherent incidental complexity that we should be looking to address rather than getting defeated by. My aim here is to get first class support for Clojure on windows that I see as fundamental to the wider Clojure reach, especially for newcomers. Asking people to move to WSL is IMHO a step towards the wrong direction. Window users should not be forced to unix conventions. WSL comes with its own challenges (interop between win programs and WSL programs, need to install an X server on Win10, need to configure port forwarding when opening a port to the outside world etc). Thanks Quoting failure example on bash (success):

$ deps -Sdeps "{:aliases {:demo {:jvm-opts [\"-Dx=\\\"y z\\\"\"]}}}" -e "1"
WARNING: Implicit use of clojure.main with options is deprecated, use -M
1
on the cmd prompt (failure):
> deps -Sdeps "{:aliases {:demo {:jvm-opts [\"-Dx=\\\"y z\\\"\"]}}}" -e "1"
Error while parsing option "--config-data {:aliases {:demo {:jvm-opts [\"-Dx=\\y": java.lang.RuntimeException: Unsupported escape character: \y

borkdude 2022-10-08T08:35:24.475499Z

Yeah, I was just going to say: deps.clj does not solve any quoting issues. Quoting issues are an interplay of shell-specific quoting rules and software requiring users to use quotes on the command line (in the case of clj: quoted EDN forms). This is one of the reasons I started writing https://github.com/babashka/cli#clojure-cli to offer the ability to create CLIs with minimal effort

borkdude 2022-10-08T08:42:33.796449Z

When going with deps.exe -> clj.exe some things have already been mentioned but something else hasn't: • Mentioned: GraalVM native-image could go away. I personally don't see this happening as Oracle offers enterprise support for this and it's adopted quite widely by all kinds of Java frameworks like Spring etc. as well • Not mentioned: GraalVM native-image supports the most popular CPU architectures but not all of them. I recently had a question from someone who ran on PowerPC. I told him he could still use the .jar instead of the .exe and the issue was solved. But this may be a good requirement to study when replacing all of the things with a single other thing. I don't see a problem with an additional solution like deps.exe. You can simply rename deps.exe to clj.exe today and then you have a working installation.

chaos 2022-10-08T08:56:32.025599Z

Thanks @borkdude. The other think to consider when going with deps.exe (or any other tooling) is installation (the original topic of this discussion). Windows users in corporate environments are most likely to be behind Web Application Firewall nowadays and have restricted installation rights on their machines. This rules out using package managers such as Scoop whose range of packages they support is immense and most likely to be barred. Perhaps it is better to consider an MSI solution in this case (I believe @danielmartincraig is looking into this), that sysadmin can reference in their setup scripts.

borkdude 2022-10-08T08:57:26.835289Z

@chaos deps.exe doesn't have to be installed: you can just place it anywhere on your machine and it should work

chaos 2022-10-08T09:01:15.448589Z

Indeed, though there is a high chance that WAFs nowadays are most likely to screen and potentially prevent any .exe files from downloading from untrusted sources from the net ... :(

borkdude 2022-10-08T09:02:18.649949Z

We've recently seen this with babashka and submitting it to a Microsoft link unflagged it rather quickly

borkdude 2022-10-08T09:03:06.181679Z

But this is definitely one of the things to add to the consideration matrix

chaos 2022-10-08T09:03:56.925979Z

Yes, I've noticed that. The case I'm referring to is not due to a false positive alert by the antivirus, but rather to a WAF policy to prevent all .exes from downloading, unless they are explicitly whitelisted ... :(

borkdude 2022-10-08T09:06:23.622579Z

I see

borkdude 2022-10-08T09:08:31.573529Z

So maybe with all things considered the powershell solution is an optimum in some sense? Or are there some corporate environments that also block powershell stuff? I think I've heard that before

chaos 2022-10-08T09:22:52.502779Z

I think if there is an installer endorsed by MS, then it should be fine, e.g. an MSI package or a PS module as we have now. It is easier for sysadmins who create the corporate windows build to include it in their build configuration, rather than trying to put something ad hoc in place. It is also much easier to convince them to open a WAF policy to allow users to download it themselves, since the installation will be registered and easy for them to audit (as oppose to dropping a random .exe file somewhere). PS is ok for installation purposes, but definitely not developer friendly (it is unfortunate that the (un)official Clojure windows script is written in PS 😞, it causes much pain and is irritating slow to start from the command prompt, but it is what it is). I would personally rather prefer an MSI/.exe solution or maybe a standalone .bat that calls java (a la @ericdallo's https://github.com/ericdallo/deps-bin perhaps), though I am not sure how well a standalone`.bat` would interact with a WAF.

👍 1
borkdude 2022-10-08T09:36:32.932249Z

OK, well, as always, if anyone from the core team wants to borrow anything from deps.clj, feel free to do so without my permission and if you need my help, just ping me

lread 2022-10-08T14:00:57.320089Z

One detail on the topic of quoting: a powershell module gets ultra tricky when you are shelling out from a program. You have to invoke the module with powershell. The quoting complexity of this scenario was a wall I decided to stop banging my head against and when I switched to deps.exe. Also when shelling to powershell, the naive (like me) can all too easily not realize there needs to be special handling to capture and return the exit code. For me, this meant some failing tests on Windows native were going unnoticed on CI.

chaos 2022-10-08T16:20:35.484509Z

Hi @lee, indeed quoting gets twice as complicated when you try to invoke PS from another shell/process, because one needs to quote the args according to the outer shell’s convention while also accounting for the PS quoting rules. On top of that, java applies its own complicated quoting rules on windows just before calling to the win32 CreateProcess API, so it all becomes too complicated for a single person to reason about it reliably. Wrt to the exit status, I have raised another question to ask clojure alongside this one, proposing a “simple” fix. There is a straightforward workaround as you have mentioned, but is well hidden from the average user’s field of view.

seancorfield 2022-10-08T16:24:35.661539Z

@borkdude I guess I misunderstood some of what I heard you say about deps.clj and quoting then -- I thought it made life easier in that area? Does it really require the same triple quoting that is shown here https://clojure.org/reference/deps_and_cli#quoting ?

borkdude 2022-10-08T16:35:04.135659Z

@seancorfield Yes, it doesn't matter if you call an .exe or whatever, passing command line arguments doesn't change

borkdude 2022-10-08T16:35:22.561699Z

I've never said that deps.clj made quoting easier

seancorfield 2022-10-08T16:35:35.989299Z

Thanks for the clarification. That makes PS as a whole suck even more from my p.o.v. 😐

borkdude 2022-10-08T16:35:58.012599Z

It does make shelling out easier: shelling out to an .exe is easier than shelling out to a powershell module

borkdude 2022-10-08T16:36:47.823179Z

> That makes PS as a whole suck even more from my p.o.v. Why is that?

seancorfield 2022-10-08T16:38:05.269699Z

Because of the quoting required. It's so much more complex than macOS/Linux.

seancorfield 2022-10-08T16:39:52.267019Z

(and, as mentioned above, the steady stream of complaints from beginners that following READMEs, books, and tutorials "doesn't work" on PS, even when they've managed to install the Clojure module -- I was hoping that weird quoting stuff was just an artifact of using a PS script rather than inherent to PS itself)

borkdude 2022-10-08T16:44:49.575319Z

cmd.exe and powershell also have different quoting rules, but even in bash working with quotes gets cumbersome if you throw some $(inline-command ...) in the mix

chaos 2022-10-08T16:51:50.829399Z

I think I have a “good” idea about how to manage argument quoting uniformly across the different OSs and this can also support any shell extensions the user would like to apply locally, but I’d like to get closure of this discussion first before getting roasted again with yet another suggestion 😨

borkdude 2022-10-08T18:13:27.254429Z

Well, I for one am curious about your idea :)

chaos 2022-10-08T18:15:21.437779Z

so, we follow the good example set forward by the lisp reader, and create a pseudo reader literal, eg #clj/'

chaos 2022-10-08T18:16:21.297359Z

we then take control of arguments escaping by disallowing any characters that are designated special by any shell quoting mechanisms

chaos 2022-10-08T18:17:30.167599Z

e.g. if you want to create an edn with embeded quotes, we will use something like “#clj/' {:aliases {’Ddemo’D …}}”

chaos 2022-10-08T18:17:39.487909Z

’D is hte mnemonic for ”

borkdude 2022-10-08T18:18:18.937769Z

# means comment in bash...?

chaos 2022-10-08T18:18:52.203399Z

thus when this argument is parsed by the tool deps, it will be identified as a special reader argument (starting with #clj/‘) and all occurances of ’<letter> will be converted to the actual character

chaos 2022-10-08T18:19:13.030749Z

let me get the example i used earlier in the thread, should be straightforward to understand I think

chaos 2022-10-08T18:21:26.674749Z

so this one:

deps -Sdeps "{:aliases {:demo {:jvm-opts [\"-Dx=\\\"y z\\\"\"]}}}" -e "1"
becomes
deps -Sdeps "#clj/' {:aliases {:demo {:jvm-opts ['Dx='S'Dy z'S'D'D]}}}" -e "1"

chaos 2022-10-08T18:21:49.021809Z

so we avoid using any of the escape seaquences in the argument and we define our own reader literal to parse the argument

chaos 2022-10-08T18:22:09.122929Z

’D = Double Quote, ’S for slash

chaos 2022-10-08T18:22:24.480919Z

so ' is the escape character

chaos 2022-10-08T18:24:31.995199Z

does it make some sense?

chaos 2022-10-08T18:24:49.461059Z

this is akin athink to reversion of control, we take control of quoting

borkdude 2022-10-08T18:25:47.571449Z

Probably just reading the args from a file would be a better solution? deps -Sdeps @foo.edn

chaos 2022-10-08T18:26:14.329969Z

i think there is an issue there that you can’t put these as a single command liner on a web page

chaos 2022-10-08T18:26:55.742339Z

we want to keep things as they stand, but handle quoting in such a way that doesn’t cause an issue

borkdude 2022-10-08T18:29:31.562929Z

I've done a similar but different suggestion here: https://ask.clojure.org/index.php/11585/convention-bypassing-parsing-reduce-quotes-arguments-passed

borkdude 2022-10-08T18:30:23.802519Z

but then I wrote babashka.cli which deals with this problem now

chaos 2022-10-08T18:35:53.924789Z

yes it is exactly on the same spirit. The above is more clojurian since using a “pseudo” reader literal, and specifies what the escaped characters are simply by using ‘<letter>’. This can be easily implemented as a clojure library with no extra dependencies required, and if ’required by say the tools deps, it just a matter of calling a function on each and every argument to convert them into the end arguments. Very simple spec and easy to implement (famous last words, I need to test this out, I have already a list of characters across the different shells that need escaping, such as the backtick from PS)

chaos 2022-10-08T18:36:29.357519Z

and I have also thought how to make dead simple for a user to convert from one format to another without really needing any extra options on the command line

borkdude 2022-10-08T18:36:32.042769Z

Does it support the inline JSON example in the ask issue?

borkdude 2022-10-08T18:38:17.607509Z

I'm not immediately convinced that this idea is dead simple and more clojurian, but I don't want to roast your idea ;)

chaos 2022-10-08T18:39:30.769119Z

yes this is the thing, until i have something to demonstrate, it’s all up in the air. So this is why we keep this a secret 🙂

chaos 2022-10-08T18:41:17.614249Z

sorry no it wont work with the above jason, since it need the command to be escaped before hand, but you can $(bb <escape> command) like “clj/' $(bb <escapecmdcallingtothequatinglibrary> file)” and this will do it

chaos 2022-10-08T18:42:20.581089Z

so bb willl use this new tiny library to quote the json file before inserting it in “clj/' …”

chaos 2022-10-08T18:44:39.251279Z

the new libraries job should be dead simple,:if the string starts with “#clj/‘“, get the rest of the string starting after “#clj/” and convert all occurrences of ’<letter> to that character designated by the <letter>, and return that string

borkdude 2022-10-08T18:45:57.971819Z

And what if you are arguments have single quotes?

chaos 2022-10-08T18:46:19.391639Z

you escape them with ‘’

chaos 2022-10-08T18:46:35.033609Z

so any single quote character in the string is escapes as quote quote

borkdude 2022-10-08T18:46:58.762289Z

could work

chaos 2022-10-08T18:47:25.204909Z

e.g. “‘quoted string’” -> “#clj/' ‘’quoted string’’”

chaos 2022-10-08T18:49:08.193189Z

another case is what about the $ charracter which is special in bash. Well we designate ’R as the escape characger for dollaR

chaos 2022-10-08T18:49:33.601419Z

if you want it as literal, use that, if you want to invoke the shell subcommand, then just use $(…)

borkdude 2022-10-08T18:50:15.363149Z

you might as well base64 escape your edn :P

chaos 2022-10-08T18:50:28.087869Z

nope, this is really horrible

chaos 2022-10-08T18:50:36.219459Z

this is what powershell does

chaos 2022-10-08T18:50:45.030859Z

you don’t have any idea at all what you are running

chaos 2022-10-08T18:51:15.808159Z

try run cider on windows and you get a horrible powershell comand line of cider starting the nrepl server, you have no idea what cider has run

borkdude 2022-10-08T18:52:27.838099Z

It was a joke. What does powershell really do?

chaos 2022-10-08T18:52:47.159539Z

just use base64 decoding on that line

chaos 2022-10-08T18:53:35.973409Z

so you can call PS command with a single basey64 encoded string, which is the command to run and the arguments

chaos 2022-10-08T18:53:53.436519Z

cider uses this to bypass all this really complex quoting mechanism

chaos 2022-10-08T18:54:25.775559Z

then if you get an error with cider nrepl starting, good luck figurring out what might have gone wrong,

chaos 2022-10-08T18:55:27.296849Z

I need to look into this in cider and somehow either decode the nrepl starting command on the message displayed, or add an additional message with the decoded string

chaos 2022-10-08T18:58:17.675919Z

so to recap, the idea to me looks dead simple, it handles arguments uniformaly across all archs, and uses the familiar reader conditional to denote something special is happening. I would even argue it makes the quoted string even easier for a human to understand, only because it uses mnemonic characters like ’S ’D ’R or whatever to designate the quoted chars. Where do you think btw, this. might not be as simple as it seems?

chaos 2022-10-08T18:58:51.757099Z

I’m just trying to find a counterexample, because it looks to me too good to be true

borkdude 2022-10-08T19:00:36.448209Z

I think the idea could work, but the downside is that you will have to convert normal EDN to this notation either in your head or programmatically, when you want to copy paste stuff

chaos 2022-10-08T19:00:41.135039Z

(I have also though about how to help with inputing this expressions and debuging them, all without the calling application which is using this library having to add additional arguments such as --quote-expression or --debug-expression to their list of args)

chaos 2022-10-08T19:00:48.553189Z

so

chaos 2022-10-08T19:00:57.480989Z

here is the answer

chaos 2022-10-08T19:03:40.123589Z

a call like “#clj/‘’” will instruct the library to ask the user to input the unquoted expression. so the user will run say deps “#clj/‘’” and they get a prompt like, please enter the expression to encode:, the user inputs the expression, the tool displays the encoded line, the program runs as expected with the encoded line

chaos 2022-10-08T19:04:21.628609Z

so the user can immediately see what the expression they would llike to quote looks like quoted, copy paste to “#clj/' …” next time they run the command

borkdude 2022-10-08T19:05:06.751039Z

I have to run now. Thanks for sharing those ideas

chaos 2022-10-08T19:05:16.940499Z

thanks for keeping an open mind!

chaos 2022-10-08T19:05:21.320419Z

have a great evening

borkdude 2022-10-08T19:05:31.084499Z

you too!

chaos 2022-10-08T19:05:45.359619Z

(and being so approachable to everyone)

chaos 2022-11-12T20:24:52.528989Z

Hi @borkdude, I've created a draft library of my idea to make argument quoting cross-platform as discussed above which I believe can solve the quoting issue. Do you think you could have a look and provide some feedback about its potential usefulness? I've tested it out with deps.clj. Basically, it solves the quoting issue by disallowing the superset of symbols that are known to be special across all platforms to appear on the command line, and provides a simple two char syntax starting with % to escape those characters. It is an opt-in solution that can be mixed with standards arguments. It borrows the idea of edn tags argument syntax in any way imaginable, such as reading from files (there is an example in the code where an argq/file tag is trivially defined to read the argument from a file). As such it should be easy to create a new tag (e.g. argq/shell) to run a a command with babashka.process and pass the result as an argument value (should address the aforemention clojure question). This is the first draft, and I am hoping to get for some early feedback that could either reveal any fatal fallacies that I might have overlooked or steer the development towards the right direction, thanks https://github.com/ikappaki/argq.alpha

borkdude 2022-11-12T20:29:13.078839Z

I think it would be better to gather feedback from multiple people so you could try posting it either here or in a channel with more people.

chaos 2022-11-12T20:30:13.651649Z

Sure, thanks!

borkdude 2022-11-12T20:30:56.999109Z

note that % also means something in Windows: it denotes environment variables, but you probably know this

chaos 2022-11-12T20:32:19.404899Z

Indeed, I don't think they have any special meaning inside quotes but tis is relevant nevertheless to the readme file where I document the special characters

borkdude 2022-11-12T20:32:27.802159Z

My personal impression is that such an alien syntax would scare people away even further and we as CLI writers should strive to not impose any quoting (or bespoke syntax) on people

borkdude 2022-11-12T20:32:50.709999Z

but if it will help someone in the status quo, why not :)

chaos 2022-11-12T20:34:34.700419Z

I think the quoting part has the potential to solve the issue with publishing command line invocationd on web pages, so it can have some niche market

chaos 2022-11-12T20:35:18.866319Z

I've also added some tooling to help with writing and decoding the syntax to address that concern (how helpful that turns out to be is another matter :))

borkdude 2022-11-12T20:35:37.731629Z

I think reading command line arguments from a file would be an easier solution

borkdude 2022-11-12T20:36:10.500579Z

That said, please gather feedback from other people as well

chaos 2022-11-12T20:38:35.778629Z

Thanks! you are right about the % characters btw used as an escape char on windows, this is one of the fallacies I haven't considered!

chaos 2022-11-20T08:24:45.626969Z

Hi @borkdude, I have introduced a https://github.com/ikappaki/argq.alpha, trying to address in good faith any concerns with the library as they come in starting with what you said above wrt to potentially introducing yet another alien syntax layer. Let me know what you think, if you happen have the time to look into it. I have also changed the tagged literals to make them more descriptive (e.g. #clj/esc -- for escape -- instead of #clj/*), switched the escape character from % to * , and introduced a study case for the ask clojure question on how to pass the json output of a command as an argument to clojure -X. In addition I asked for feedback from the channel participants as suggested. I understand you might be busy or uninterested in this, thus don't feel obliged to answer or look into it. My aim is to hook this library up with either the clojure tools scripts or clojure.tools.deps.alpha, if it is proven to solve the cross platform quoting issue beyond any concerns. Thanks

lread 2022-10-08T17:35:59.867789Z

@chaos I’m sorry that you feel roasted, but I also know from personal experience that it can be hard on the ego to invest a bunch of time into some idea and then have it critiqued and/or rejected. But in the end, personally, I’m ultimately fine with it. I admire the time and care that the Clojure team take, and very much respect its benevolent dictatorship.

lread 2022-10-08T17:38:43.250349Z

And remember that just because an idea is not adopted, doesn’t mean it is not a good idea.

chaos 2022-10-08T17:45:48.247269Z

@lee personally I’m fine with this either way, rejected or approved. I think these discussions are done in good faith by everyone for the greater Clojure good, and it brings everybody else to the same page with regards to the other people’s perspective and experiences. I could as well be technically totally wrong, that’s why i put these ideas forward for “consideration” rather than demanding to be implemented 🙂 It’s very good to get roasted, this means the ideas are put to a good test by highly respectable people like yourself 🙂 What I would have been disappointed with is not wanting to discuss ideas that someone genuinely believes are for the greater good.

lread 2022-10-08T17:48:10.013509Z

Coolio, well glad you are here @chaos, and nice to see someone representing the Windows native cause who is actually using Windows native!

👍 2
seancorfield 2022-10-08T00:53:43.695989Z

@chaos According to the last State of Clojure survey analysis, 90% of Clojure users are on macOS/Linux, 5% are on Windows (using cmd.exe or Powershell), and 5% are on Windows/WSL2. Just offering a data point. I'm in that latter 5% and very happy with WSL2 as the best option since all the books/tutorials out there "just work", whereas that other 5% is full of users who try to follow instructions from books/tutorials and project READMEs and run into errors because of the peculiar argument quoting issues on cmd.exe and (different) on Powershell. It doesn't matter how easy the PS module install is made, it isn't going to fix those quoting issues so those beginners are just going to be even more frustrated: they can "easily" get clojure installed on Powershell but then it "doesn't work" (because they haven't read the section on Windows-specific argument quoting on http://clojure.org) and all the project READMEs have "the wrong instructions". I'm already fielding a steady stream of "bug reports" on clj-new and deps-new from PS users and I have to point each of them to http://clojure.org piece about quoting arguments. I agree with @lee that it was a good alpha experiment but the UX is still pretty horrible even if you can defeat the PS installer stuff 😐 I think having a simple, downloadable .exe file is the way to go on Windows because that also addresses the quoting issues (right @borkdude?). p.s., if you've posted on http://ask.clojure.org, there's really no need to raise it here and beg the core team to consider it, since they'll definitely see it on Ask and respond there (Ask is permanent, Slack isn't -- despite us currently having a complimentary Pro account).