|
| 1 | +# An introduction to `cljr` |
| 2 | + |
| 3 | +Start by reading [Deps and CLI Reference Rationale](https://clojure.org/reference/deps_and_cli). |
| 4 | + |
| 5 | +The CLR port of [tools.deps](https://github.com/clojure/tools.deps) is currently located at [clr.tools.deps](https://github.com/clojure/clr.tools.deps). |
| 6 | + |
| 7 | +This repo contains the .NET equivalent of the command line programs `cli` and `clojure`. |
| 8 | + |
| 9 | +## How it works |
| 10 | + |
| 11 | +The main program follows the outline of the `cli` tool. |
| 12 | +The first step is the parse the command-line arguments. |
| 13 | + |
| 14 | +If the arg list contains `-h`, a help message will be printed. |
| 15 | +This will give you the clues for other options. (I need to edit the help text a bit to make it `cljr`-specific.) |
| 16 | + |
| 17 | +At the moment, there is no distinction betwee `cli` and `clojure`. |
| 18 | +(I don't see any difference in the JVM world either, despite what they say in the help text.) |
| 19 | + |
| 20 | +``` |
| 21 | +...> Deps.Cljr.exe -h |
| 22 | +Version: 1.0.0.0 |
| 23 | +
|
| 24 | +You use the Clojure tools('clj' or 'clojure') to run Clojure programs |
| 25 | +on the JVM, e.g.to start a REPL or invoke a specific function with data. The Clojure tools will configure the JVM process by defining a classpath (of desired libraries), an execution environment(JVM options) and specifying a main class and args. |
| 26 | +
|
| 27 | +Using a deps.edn file(or files), you tell Clojure where your source code resides and what libraries you need.Clojure will then calculate the full set of required libraries and a classpath, caching expensive parts of this process for better performance. |
| 28 | +
|
| 29 | +The internal steps of the Clojure tools, as well as the Clojure functions you intend to run, are parameterized by data structures, often maps. Shell command lines are not optimized for passing nested data, so instead you will put the data structures in your deps.edn file and refer to them on the command line via 'aliases' - keywords that name data structures. |
| 30 | +
|
| 31 | +'clj' and 'clojure' differ in that 'clj' has extra support for use as a REPL in a terminal, and should be preferred unless you don't want that support, then use 'clojure'. |
| 32 | +
|
| 33 | +Usage: |
| 34 | +
|
| 35 | + Start a REPL |
| 36 | + clj [clj-opt*] [-Aaliases] |
| 37 | + |
| 38 | + Exec fn(s) |
| 39 | + clojure [clj-opt*] -X[aliases][a / fn *][kpath v]* |
| 40 | + |
| 41 | + Run main |
| 42 | + clojure[clj-opt *] -M[aliases][init-opt *][main-opt][arg*] |
| 43 | + |
| 44 | + Run tool |
| 45 | + clojure [clj-opt*] -T[name | aliases] a/fn[kpath v] kv-map? |
| 46 | + |
| 47 | + Prepare |
| 48 | + clojure[clj-opt*] -P[other exec opts] |
| 49 | +
|
| 50 | +exec-opts: |
| 51 | + -Aaliases Use concatenated aliases to modify classpath |
| 52 | + -X[aliases] Use concatenated aliases to modify classpath or supply exec fn/args |
| 53 | + -M[aliases] Use concatenated aliases to modify classpath or supply main opts |
| 54 | + -P Prepare deps - download libs, cache classpath, but don't exec |
| 55 | +
|
| 56 | +clj-opts: |
| 57 | + -Jopt Pass opt through in java_opts, ex: -J-Xmx512m |
| 58 | + -Sdeps EDN Deps data to use as the last deps file to be merged |
| 59 | + -Spath Compute classpath and echo to stdout only |
| 60 | + -Stree Print dependency tree |
| 61 | + -Scp CP Do NOT compute or cache classpath, use this one instead |
| 62 | + -Srepro Ignore the ~/.clojure/deps.edn config file |
| 63 | + -Sforce Force recomputation of the classpath(don't use the cache) |
| 64 | + -Sverbose Print important path info to console |
| 65 | + -Sdescribe Print environment and command parsing info as data |
| 66 | + -Sthreads Set specific number of download threads |
| 67 | + -Strace Write a trace.edn file that traces deps expansion |
| 68 | + -- Stop parsing dep options and pass remaining arguments to clojure.main |
| 69 | + --version Print the version to stdout and exit |
| 70 | + -version Print the version to stdout and exit |
| 71 | +
|
| 72 | +The following non-standard options are available only in deps.clj: |
| 73 | +
|
| 74 | + -Sdeps-file Use this file instead of deps.edn |
| 75 | + -Scommand A custom command that will be invoked. Substitutions: { { classpath} }, {{main-opts |
| 76 | +}}. |
| 77 | +
|
| 78 | +init - opt: |
| 79 | + -i, --init path Load a file or resource |
| 80 | + -e, --eval string Eval exprs in string; print non-nil values |
| 81 | + --report target Report uncaught exception to "file" (default), "stderr", or "none" |
| 82 | +
|
| 83 | +main-opt: |
| 84 | + -m, --main ns - name Call the -main function from namespace w/args |
| 85 | + -r, --repl Run a repl |
| 86 | + path Run a script from a file or resource |
| 87 | + - Run a script from standard input |
| 88 | + -h, -?, --help Print this help message and exit |
| 89 | +
|
| 90 | +Programs provided by :deps alias: |
| 91 | + -X:deps mvn-pom Generate (or update) pom.xml with deps and paths |
| 92 | + -X:deps list List full transitive deps set and licenses |
| 93 | + -X:deps tree Print deps tree |
| 94 | + -X:deps find-versions Find available versions of a library |
| 95 | + -X:deps prep Prepare all unprepped libs in the dep tree |
| 96 | + -X:deps mvn-install Install a maven jar to the local repository cache |
| 97 | + -X:deps git-resolve-tags Resolve git coord tags to shas and update deps.edn |
| 98 | +
|
| 99 | +For more info, see: |
| 100 | + https://clojure.org/guides/deps_and_cli |
| 101 | + https://clojure.org/reference/repl_and_main |
| 102 | +
|
| 103 | +``` |
| 104 | + |
| 105 | +- If the arg list contains `-h`, `-?`, or `--help`, a help message will be printed. (Above) |
| 106 | +- If the arg list contains `-version` or `--version`, version info will be printed. |
| 107 | + |
| 108 | +``` |
| 109 | +...> Deps.Cljr.exe -version |
| 110 | +ClojureCLR CLI Version: 1.0.0.0 |
| 111 | +``` |
| 112 | + |
| 113 | +- If the arg list contains `-pom` or `-Jwhatever`, it will be ignored, other than printing a warning, "We are the CLR!". Eventually we will come up with a substitute for the `-Jwhatever` that allows passing information to the CLR runtime. Maybe. When I figure out what that might be. |
| 114 | + |
| 115 | +- If the arg list containts `-Sverbose`, you will get extra information printed out, including information on installation and configuration directories that might be helpful in debugging. |
| 116 | + |
| 117 | + |
| 118 | +After parsing the command line, the short-circuits help and version are checked for. This followed by computing a bunch of environmental information: the install, config and cache directories, the tools directory (including making sure there is a `tools.edn` file in there and copying a blank one if necessary). |
| 119 | + |
| 120 | +Then the various aliases (`-X`, `-M`, `-T`, etc.) are computed and used to form the cache key, which is then hashed. This determines the subdirectory in the cache file to look at and store cached info into. |
| 121 | + |
| 122 | +You can see all this by running with `-Sverbose`: |
| 123 | + |
| 124 | +``` |
| 125 | +> Deps.Cljr.exe -Sverbose |
| 126 | +version = 1.0.0.0 |
| 127 | +install_dir = C:\work\clojure\deps.cljr\src\dotnet\Deps.Cljr\bin\Debug\net6.0\ |
| 128 | +config_dir = C:\Users\dmill\.clojure |
| 129 | +config_paths = C:\work\clojure\deps.cljr\src\dotnet\Deps.Cljr\bin\Debug\net6.0\deps.edn C:\Users\dmill\.clojure\deps.edn deps.edn |
| 130 | +cache_dir = C:\Users\dmill\.clojure\.cpcache |
| 131 | +cp_file = C:\Users\dmill\.clojure\.cpcache\D79F9C6847CA7B8A630F9F8E6C23BEE0.cp |
| 132 | +
|
| 133 | +Refreshing classpath |
| 134 | +... |
| 135 | +``` |
| 136 | + |
| 137 | +The string `D79F9C6847CA7B8A630F9F8E6C23BEE0` is the cache key hash. |
| 138 | + |
| 139 | +The next step is to see if the classpath information is stale and refresh if necessary. The classpath is computed by the ClojureCLR function `clojure.tools.deps.script.make-classpath2`. This is run by running a PowerShell script that starts up ClojureCLR and runs this function, with a bunch of arguments passed on the command line. |
| 140 | + |
| 141 | +Here is what is executed: |
| 142 | + |
| 143 | +``` |
| 144 | +run-clojure-main.ps1 -m clojure.tools.deps.script.make-classpath2 --config-user C:\Users\dmill\.clojure\deps.edn --config-project deps.edn --cp-file C:\Users\dmill\.clojure\.cpcache\D79F9C6847CA7B8A630F9F8E6C23BEE0.cp --jvm-file C:\Users\dmill\.clojure\.cpcache\D79F9C6847CA7B8A630F9F8E6C23BEE0.jvm --main-file C:\Users\dmill\.clojure\.cpcache\D79F9C6847CA7B8A630F9F8E6C23BEE0.main --manifest-file C:\Users\dmill\.clojure\.cpcache\D79F9C6847CA7B8A630F9F8E6C23BEE0.manifest |
| 145 | +``` |
| 146 | + |
| 147 | +The script `run-clojure-main.ps1` is trivial: |
| 148 | + |
| 149 | +``` |
| 150 | +clojure.main @args |
| 151 | +``` |
| 152 | + |
| 153 | +Seriously, that's it. |
| 154 | + |
| 155 | +You will note some oddities in the command line arguments, such as the `.jvm` file -- those will eventually be replaced. The arguments passed to `clojure.tools.deps.script.make-classpath2` here are what that program is currently written to accept. |
| 156 | + |
| 157 | +In order for this to work, we must have the `clr.tools.deps` code in the appropriate `bin` subdirectory. This is a dependence that we must build in by hand -- we are building the tool that calculates dependencies and pulls in code, but there is no obvious way to have it bootstrap itself, other than to pull in the code as needed from being hardwired. (to do -- for now, we just bring it in by hand.) |
0 commit comments