tools-deps

Discuss tools.deps.alpha, tools.build, and the clj/clojure command-line scripts! See also #depstar #clj-new
2021-01-15T06:02:15.006500Z

I understand -X lets you call a specific function from some random namespace and pass it arguments. So you can configure an alias like:

:new {:extra-deps {seancorfield/clj-new {:mvn/version "RELEASE"}}
        :exec-fn clj-new/create
        :exec-args {:template lib}}
And then call clj -X:new and clj will run the create function from clj-new namespace and pass it as argument {:template lib}

2021-01-15T06:02:50.007100Z

It seems -M is just the new -A where your alias had a :main-opts [] defined.

2021-01-15T06:02:59.007300Z

And I don't know what has become of -A

seancorfield 2021-01-15T06:06:13.008200Z

-A will stop running :main-opts at some point and will be used just for starting a REPL (with various aliases). At least, that's what I understand @alexmiller to have said about it.

seancorfield 2021-01-15T06:07:07.009300Z

(and you'll get deprecation warnings right now if you try to use -A with any main opts -- either :main-opts or via the command line)

seancorfield 2021-01-15T06:10:42.009700Z

@didibus ^ does that answer your "question"? ๐Ÿ™‚

seancorfield 2021-01-15T06:11:24.010300Z

(and, to be clear, -M has expanded from respecting just :main-opts to respecting "everything")

2021-01-15T06:35:01.010800Z

I see, so -A will be if you just want to like bring more or less deps in your repl sa you start it?

2021-01-15T06:35:23.011200Z

Or like on anything, since I assume you can combo like -M and -A

2021-01-15T06:36:40.012200Z

Oh, so -M will be smart? So like you can use -M for alias that define an exec-fn ? So I guess only when its ambiguous you need to explicitly use -X ?

seancorfield 2021-01-15T06:37:41.012400Z

What?

seancorfield 2021-01-15T06:37:55.012800Z

-X invokes a function. -M invokes a main.

seancorfield 2021-01-15T06:38:39.013700Z

So -X looks for :exec-fn or a function on the command-line. -M runs :main-opts (or main options on the command-line).

seancorfield 2021-01-15T06:38:44.013900Z

There's no overlap there.

seancorfield 2021-01-15T06:39:51.015100Z

Re: -A, yes, my understanding, based on what Alex has said, is that -A will at some point simply stop running :main-opts and will instead always start a REPL (with all the dependencies and classpath stuff set up per whatever aliases you specify).

seancorfield 2021-01-15T06:41:33.016700Z

Once -A stops running main options, there will be no overlap there either: * -X -- invoke a function with a single hash map argument * -M -- invoke clojure.main with various options (`-m`, -e presumably) * -A -- run a REPL

2021-01-15T06:42:54.017500Z

Ok, maybe I didn't get what you meant by: (and, to be clear,ย -Mย has expanded from respecting justย :main-optsย to respecting "everything")

2021-01-15T06:43:03.017700Z

What is everything?

seancorfield 2021-01-15T06:43:27.018100Z

(at the risk of sounding like a broken record, I still think expanding -R for the REPL option and just plain deprecating -A would have been better but...)

seancorfield 2021-01-15T06:43:59.018600Z

"everything" = dependencies and class paths and jvm options etc

seancorfield 2021-01-15T06:44:44.019400Z

-R and -C and -O used to be individual options for resolve args, classpath args, and jvm options.

2021-01-15T06:44:56.019700Z

Oh I see, ok I just assumed that haha, I guess I missed the time when -M did not respect those

2021-01-15T06:45:43.020600Z

Didn't -A respect all those as well?

seancorfield 2021-01-15T06:45:56.020900Z

It used to be, prior to this big change, that -A was "all things" and all the other options just dealt with a specific thing (and there was no -X but there was a -M).

seancorfield 2021-01-15T06:46:12.021300Z

-A still does respect all of those.

seancorfield 2021-01-15T06:46:20.021600Z

At some point it will ignore main opts.

2021-01-15T06:48:40.023300Z

Ah ok I see. I feel like -A should become: run any alias fully. If the alias has a :main-opts run that, if it doesn't but has an exec run that, if not just start a repl with the extra-deps and all

seancorfield 2021-01-15T06:49:06.024Z

Well, no, that's not what it is and not what it's going to be. That ship has sailed too ๐Ÿ™‚

๐Ÿ˜ญ 1
2021-01-15T06:49:15.024300Z

Its strange to provide in your README like: add this alias, run with -X, or add this alias but this one remember to run with -M

2021-01-15T06:49:53.025200Z

Like what's the point of configuring anything in the deps.edn file, if I still need to remember some of it at the command line ๐Ÿ˜›

seancorfield 2021-01-15T06:50:29.025600Z

I suspect, over time, most tools will change to -X execution because it is more powerful/more convenient.

seancorfield 2021-01-15T06:51:09.026500Z

clj-new switched, depstar switched. I see deps-deploy switched. When tools.build arrives, I bet it's going to be all -X style stuff.

2021-01-15T06:52:06.027600Z

It depends though, passing args to -X is tricky, and not unix like at all, so if you make a CLI, like take clj-kondo for example, if I want to use it from clj -X then the argument I would pass it need to be formatted differently than when I'd call it with clj-kondo executable

seancorfield 2021-01-15T06:52:50.028300Z

-X is "native" Clojure. -M is artificial. The latter is all strings that need to be parsed and turned into Clojure data. The former already is Clojure data.

alexmiller 2021-01-15T13:57:18.074400Z

bleh on those words. -X is function execution, -M is clojure.main. They are both Clojure.

seancorfield 2021-01-15T17:35:07.083700Z

I just meant in terms of -X calling a Clojure function directly -- and being idiomatic in terms of the arg being a single hash map with keys and values from the command-line -- whereas -M is like a traditional JVM process, via -main, and passing strings that Clojure needs to parse. Hence "native" in quotes ๐Ÿ™‚

seancorfield 2021-01-15T06:54:17.030100Z

With -main (and -M) everyone needs to write their own ad hoc argument parsing (even if they build on tools.cli). There's no consistency there at all. With -X, it's absolutely consistent and all tools will work the same way: they'll call a Clojure function with a hash map built as EDN.

2021-01-15T06:54:21.030300Z

I guess I see pros/cons for each. While -X is native Clojure, it is not native bash or powershell, so now that side of the interface needs to do the mapping.

seancorfield 2021-01-15T06:54:57.030800Z

You seem to presume there's a real standard in command-line arguments already... which there really isn't...

2021-01-15T06:55:19.031100Z

True, but I'm talking at least the formatting

seancorfield 2021-01-15T06:55:41.031500Z

How do you pass a vector of integers to a command-line program?

seancorfield 2021-01-15T06:56:13.032100Z

Dead easy with -X. Not easy with -M, even with tools.cli (which, remember, I maintain these days!).

2021-01-15T06:56:39.032600Z

Right, but then, a very common use case is to pass a string, and with -X you need to do this shenanigan: '"hi there"'

seancorfield 2021-01-15T06:57:41.033700Z

I think that's actually less common that you might imagine. And for a lot of "string" arguments, it's actually fine to pass a symbol and have the function decide to handle symbols as strings if necessary.

2021-01-15T06:58:14.034400Z

For tooling, I think -X makes more sense, but for distribution I feel -M might make more sense. Like if you want to use tools.deps to distribute your command line tools to users.

seancorfield 2021-01-15T06:59:05.035100Z

Having used this new stuff for a while I don't agree. I think you will change your mind in due course.

seancorfield 2021-01-15T06:59:21.035500Z

Right now, it's just "different" and you're resisting that change.

2021-01-15T07:01:45.038200Z

I personally prefer -X a lot. I do feel now though I keep having to open my deps.edn to remember if that particular alias I have to call with -M or -X though ๐Ÿ˜›, that's my biggest problem. But I was wondering for -X what it mean for CLIs, it still seems to me if you're puporsely making a CLI tool, you wouldn't want it to take EDN as input, that would throw of most users. Like say I made a grep in Clojure. And then you'd probably use -M with it.

seancorfield 2021-01-15T07:02:32.039200Z

Having maintained tools.cli for a while and having written and maintained a lot of command-line stuff over the decades, I hate having to deal with strings and ad hoc parsing and inventing semantics for all that stuff over and over and over again...

2021-01-15T07:03:45.039800Z

So you'd go as far as saying that if someone were to use Clojure for a CLI, it might be a good idea they just take EDN as args?

โœ”๏ธ 1
seancorfield 2021-01-15T07:03:56.040Z

Yes, very much so.

seancorfield 2021-01-15T07:04:36.041200Z

It's consistent. It's provides access to all of Clojure's data types. You can provide structured data as arguments really easily.

2021-01-15T07:05:05.042Z

How easy is that? Is it just:

(defn -main
  [& {:keys [:option1 :option2]}]
  ...)
Would this mimic exactly clj -X ?

seancorfield 2021-01-15T07:05:25.042500Z

Alex was saying that they're looking at a way to pass Vars directly, so you don't have to then resolve symbols, which will make this even more powerful.

seancorfield 2021-01-15T07:05:58.043100Z

-main can't mimic -X -- it must take a sequence of strings because that's what Java does.

seancorfield 2021-01-15T07:06:25.043500Z

public static void main( String[] );

seancorfield 2021-01-15T07:06:38.044100Z

(or whatever nasty syntax Java requires)

2021-01-15T07:06:42.044200Z

So its not possible to make a CLI with Clojure that takes the same argument format as when launched with -X ? I mean, its got to be no since clj does it ๐Ÿ˜›

alexmiller 2021-01-15T13:58:51.074600Z

of course it is - that's what -X calls

seancorfield 2021-01-15T07:07:05.044800Z

I don't understand your question.

seancorfield 2021-01-15T07:07:10.045100Z

clj is a shell script.

2021-01-15T07:09:45.047600Z

Like say I'm writing a Command Line Application. Lets say an implementation of ls but in Clojure. And I'm going to distribute this to people as say a Graal Native Binary. Or as some UbrJar that I wrap a bash script around to launch it. Now say I would like it the arguments to this Clojure based ls would be similar to when called with -X. I want it to be EDN. So I want to do: ls :verbose true for example

2021-01-15T07:10:38.048400Z

I guess I'm asking... Will tools.deps expose the parser it uses for -X so I can use it in place of tools.cli or something like that?

seancorfield 2021-01-15T07:11:14.048700Z

I can't answer that, I'm not part of the Clojure core team.

vlaaad 2021-01-15T07:11:41.049300Z

The parser is clojure.edn/read-string

seancorfield 2021-01-15T07:13:21.051300Z

It's more than that: there's a clojure.run.exec ns (currently) that strings all of that together including the function invocation.

2021-01-15T07:13:44.051900Z

Like, (read-string (reduce str args)) ?

vlaaad 2021-01-15T07:14:02.052300Z

Found it!

seancorfield 2021-01-15T07:15:28.054400Z

That's very much an implementation detail right now -- it's some pretty sketchy code ๐Ÿ˜

2021-01-15T07:15:31.054500Z

Well, either them releasing it separately, or someone replicating it in a lib would be neat. Then you'd be able to make a CLI and the UX would be the same either used with -X or from the command line directly.

borkdude 2021-01-15T07:16:36.057900Z

Btw for some CLIs I support an โ€”opts argument where you can pass all args as one EDN map. I find this more ergonomic in some cases than passing separate args with their own paths into the map which then also need individual quoting

seancorfield 2021-01-15T07:16:42.058100Z

Since -X specifies which function to call (either directly or via :exec-fn in aliases) it is pretty much integral to how these arguments are handled.

seancorfield 2021-01-15T07:17:19.058800Z

I mean, basically, -X is tied to deps.edn etc so the question you're asking about GraalVM binaries doesn't really make sense.

vlaaad 2021-01-15T07:18:35.060100Z

More like (apply hash-map (map edn/read-string args))

vlaaad 2021-01-15T07:19:32.062300Z

Every string arg at CLI is parsed to edn separately

2021-01-15T07:19:52.063100Z

Ya, I mean that's not a bad idea, even if not exactly what tools.deps does for -X (I don't know if it is or not), but for my next CLI such a simple parsing of args could make sense, I might try it out

seancorfield 2021-01-15T07:20:13.063800Z

If you "fix" the values of :exec-fn, :exec-args and do not accept a function to invoke, then yes you could have a standardized command-line based on keywords and EDN values (and I think there's code in t.d.a directly that handles that -- the clj-exec thing is just a copy/variant specifically to support CLI invocation via -X).

vlaaad 2021-01-15T07:20:36.064Z

Yeah, if the target audience is not clojurists, it might be better to use something like tools.cli

borkdude 2021-01-15T07:21:03.065Z

Right. If you want to support the CLI for JVM invocation via deps.edn you can also add an -X one apart from the Unix style

borkdude 2021-01-15T07:21:39.065700Z

Itโ€™s not an either/or

2021-01-15T07:22:29.066800Z

That's what I mean. -X is starting a new convention for Clojure CLI applications in how their command line args are specified. But I would find it weird if a command line app takes different style of arguments based on how it is invoked. So if I'm to make one and use -X, it be nice if other ways to start it I can also just pass it -X style arguments.

2021-01-15T07:22:48.066900Z

Like taking an -X that then takes an EDN string?

2021-01-15T07:23:13.067100Z

Oh you mean, in the CLI, have a -main and another function for -X ?

seancorfield 2021-01-15T07:23:16.067300Z

-X :keyword some-edn :another-keyword more-edn

vlaaad 2021-01-15T07:23:33.067500Z

-X is a bit more complicated since it has to deal with defaults and overrides, but I think this approach is very nice because you are able to have the same api both for -X and -M invocations

seancorfield 2021-01-15T07:24:06.068Z

I'd be quite happy if all Clojure tooling worked like -X ๐Ÿ™‚

โค๏ธ 1
borkdude 2021-01-15T07:24:25.068200Z

Yes, both

borkdude 2021-01-15T07:25:10.069800Z

And please donโ€™t deprecate one over the other wink wink, there is more than one build tool

2021-01-15T07:25:38.070600Z

Ya, you can do that, extra work though. It be nice if you could just only have the -X one, and use it even for -main somehow

seancorfield 2021-01-15T07:25:50.070900Z

(beer's empty, battery's nearly dead, and I have to get up and go have my brain swabbed in the morning to see whether or not I have COVID, so I'll bid y'all adieu)

๐Ÿคž 4
๐Ÿ™ 2
2021-01-15T07:28:39.071200Z

Ya, it quite neat

2021-01-15T07:29:41.071400Z

At first I was thinking, ok but users might find :option ... weird, but Sean is right, CLIs are already pretty inconsistent here, some take -o --option --o and all that

2021-01-15T07:31:28.071600Z

Also I guess in theory, the EDX parsing can even return a map of symbol to value, so you can probably even use this to parse -o which I think is a valid symbol

vlaaad 2021-01-15T07:33:38.071800Z

haha yeah

vlaaad 2021-01-15T07:34:44.072100Z

so is -h , -? and --help ๐Ÿ™‚

2021-01-15T07:34:47.072300Z

I'm off as well, nice day everyone

vlaaad 2021-01-15T07:35:21.072400Z

I experimented a bit with it some time before the -X got released: https://vlaaad.github.io/tools-cli-in-10-lines-of-code

2021-01-15T07:35:40.072700Z

Nice lol. So the only annoying bit is if you need to take a string as the value, it has to be: app -option '"the long string"'

vlaaad 2021-01-15T07:35:54.072900Z

yeah

2021-01-15T07:37:07.073100Z

Oh neat, I will definitely bookmark and read this blog, thanks

2021-01-15T07:37:14.073300Z

Off to bed now though, gn

alexmiller 2021-01-15T14:01:26.075100Z

clojure.run.exec should currently be considered a changing implementation detail

alexmiller 2021-01-15T14:01:44.075500Z

(and it has been actively and regularly changing)

alexmiller 2021-01-15T14:05:22.078100Z

@didibus @seancorfield I do think that ultimately most of the code there should be cleaned up and included in clojure (like clojure.main). right now, clj has to sneak that into your classpath and I'd prefer for it to rely on getting via your clojure dep. But the current "api" is pretty dirty and is doing half of clj's parsing job. until that's cleaner, not ready to do that yet.

borkdude 2021-01-15T14:07:17.079800Z

I would consider a same kind of API for babashka scripts when this is considered done. I'm not 100% convinced of the way "nested" args are passed in, since that results in a lot of command line quotes for basic things like strings. Passing in an entire config map feels more natural to me, but maybe the design choice was: we don't want to decide how nested args should be merged, we only support assoc-in?

borkdude 2021-01-15T14:08:00.080500Z

How would you for example add an element to a vector inside an arg map? {:a {:foos [1 2]}} and now you want to add 3?

alexmiller 2021-01-15T14:08:41.081Z

not something we were trying to support

alexmiller 2021-01-15T14:09:03.081500Z

the idea is that you're mostly assembling maps, not updating them

borkdude 2021-01-15T14:10:10.082300Z

{:some-tool {:scan-paths ["src"]}} now add "test" to :scan-paths, this is maybe a better example

alexmiller 2021-01-15T14:10:44.082800Z

I await your ask clojure request :)