Can I run my command with out lein run? - clojure

I currently run a simple cli I wrote by calling: lein run my-cli-command arg --option
How can I call my command without needing to include lein run? This is what i'm after: my-cli-command arg --option
Do I need to convert it to an binary or executable and if so how?

As far as I know, there's no way to run just my-cli-command arg --option.
You can take lein out of the equation though by creating a Java archive:
lein uberjar
Then run the jar as you would any other:
java -jar target/my-cli-command-standalone.jar arg --option
uberjar will name the jar based on what you've called your project in project.clj, and will create a jar that relies on external dependencies, and one that doesn't (standalone).
Then, as #gary pointed out, you can stick the java - jar ... command in a .bat file, name it whatever you want, then run the bat directly. My bat-Fu is pretty weak, but there's likely a way to pass arguments to the bat and have them passed to the jar so you don't need to hard code the arguments.

As of Clojure 1.9 there are new CLI tools! See this guide for installation. You can create an executable script like this:
#!/usr/local/bin/clojure
(println "Hello World! from" *ns*)
(require '[clojure.walk :as walk])
(walk/postwalk-demo {:woo ::yeah})
Then make the script executable and execute it:
$ chmod +x my_script
$ ./my_script
Hello World! from #object[clojure.lang.Namespace 0x1ebea008 user]
Walked: :woo
Walked: :user/yeah
Walked: [:woo :user/yeah]
Walked: {:woo :user/yeah}
Start-up time seems improved as well. It takes a little over a second to run a trivial script (just print a string) on a recent MBP:
time ./hello
Hello World!
./hello 1.51s user 0.12s system 184% cpu 0.887 total

I've not used it, but I know inlein exists and it looks like what you need.
Inlein is the easiest and fastest way to run Clojure scripts. You only have to inline your dependencies, add in a shebang line, and make the script file executable.
And a minimal example:
#!/usr/bin/env inlein
'{:dependencies [[org.clojure/clojure "1.8.0"]]}
(println "hello world!")

You can use "binary payload" in shell script as described in https://coderwall.com/p/ssuaxa/how-to-make-a-jar-file-linux-executable
Basically you can concatenate a shell script and your uberjar in a single shell script file and execute java in the script specifying that script as the jar file on the classpath - the example comes from the linked post:
Save your runner script in stub.sh:
#!/bin/sh
MYSELF=`which "$0" 2>/dev/null`
[ $? -gt 0 -a -f "$0" ] && MYSELF="./$0"
java=java
if test -n "$JAVA_HOME"; then
java="$JAVA_HOME/bin/java"
fi
exec "$java" $java_args -jar $MYSELF "$#"
exit 1
Then concatenate it with your uberjar:
cat stub.sh my-cli-command-uberjar.jar > my-cli-command && chmod +x my-cli-command
Now you can run it directly:
./my-cli-command args...
There is also a lein plugin automating this process: lein-binplus

Related

Git Bash for Windows not working 'lein repl' command

I'm actually using git bash instead of default windows command shell (cmd).But I cannot run 'lein repl' in git bash. It doesn't show anything and I must press crtl + c to regain control.
Any suggestion?
Thank you.
-- EDIT
I'm running lein instead of lein.bat because I added alias lein=lein.bat in my .bashrc, so I don't think it is a problem.
lein run is working fine in Git Bash and leiningen works fine in windows cmd. The problem is explicity with lein repl.
When I run which lein I got which: no lein in ([MY-PATH-VAR])
First Check if you have installed lein.bat in windows using below command in command prompt
lein --version
Then if lein is available, then in your git bash type the below command
alias lein='lein.bat'
Thein run your lein command it will work for sure...
I had exactly the same problem as ThomasH where calling lein repl from an Emacs shell would get me the introductory messages and a prompt ...
nREPL server started on port 55801 on host 127.0.0.1 - nrepl://127.0.0.1:55801
REPL-y 0.4.4, nREPL 0.8.3
Clojure 1.10.1
Java HotSpot(TM) 64-Bit Server VM 15.0.2+7-27
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
... but seemingly freeze when I try to get it to do anything, like:
user=> (+ 1 2 3 8)
When I open up the Task Manager, I could see the underlying Java program continuously at ~30% CPU for no apparent reason, and the only way I could reliably get out of it was to end the Java process from there.
But through a lot of effort, I finally figured out that the reason this was happening was a shell environment variable TERM=emacs. Whenever you call M-x shell, the function comint-term-environment puts it in for you.
(According to the source code in comint.el, it's done so that it "lets us specify a width". And it also mentions that "Some programs that use terminfo get very confused if TERM is not a valid terminal type". So there you have it - it looks like Java is one of those.)
To resolve this, you can set the variable system-uses-terminfo to 't before calling M-x shell.
(setq system-uses-terminfo t)
After having done so, Java is now finally responsive and I can evaluate things in the repl.
user=> (+ 1 2 3 8)
14
If you have this problem and got fortunate enough to stumble here, I hope this helps! =D
The following worked for me to get this running in Git bash on Windows 10. When I originally got this working, it was freezing, but this post helped me figure out how to fix by including winpty.
If you haven't added the lein.bat file to your Path variable, then do steps 1-3. Otherwise, skip to 4. In the System Properties -> Advanced -> Environment Variables area, select the Path variable
Click Edit and add the directory containing lein.bat to this. Mine was C:\Program Files (x86)\lein - lein.bat was stored inside the lein folder
Click OK until you close out of the prompts
Confirm the update worked by opening a command prompt (windows key, then type cmd)
Enter lein. If you haven't already, it should prompt you to run the install command. Do this if prompted, then enter lein again to make sure it worked (should output help menu)
Close out of Git bash if already open, then open again
Enter alias repl='winpty lein.bat repl'. You can choose what you want the alias to be, I used repl
Type repl (or whatever alias you added). Should take a second, then begin a REPL prompt
Using GitBash as your terminal, you can install the lein script rather than the lein.bat script.
The lein script will run correctly in GitBash (as it is effectively a Unix environment).
I recommend installing the lein script in a directory called bin in your user account directory, e.g.:
c:/Users/your-account/bin
Placing the lein script in bin will allow you to run the lein command anywhere on the GitBash command line.
A Note about Emacs, lein and windows
The lein script will also be called correctly from Emacs, assuming you run Emacs from the GitBash terminal, ie. runemacs. Add Emacs install directory to your user account PATH environment variable and GitBash will find it on the command line.
Emacs installed on Windows works better when run from the GitBash terminal, as you can then use ediff in Emacs, which requires the diff command that GitBash terminal provides.
My recommended Emacs installs for windows
zklhp emacs-w64 optomised
Chocolatey Emacs 64bit
My experience was that lein repl (and I do use the lein bash script) would start up ok, also printing the startup messages of the lein command to the shell, but would then stop to output anything (although the process might be running fine), part. not providing an interactive prompt (except for the first), not echoing user input (except for the first), nor printing evaluation results. When terminating the process with Ctrl-C all my inputs would be passed to the underlying shell (with funny results like $ (+ 3 4) bash: +: command not found).
My solution is to use Git CMD for interactive commands like lein repl, lein figwheel etc., which (unfortunately, as it might seem) works fine. Running a Git sh process inside a different terminal emulation (Cmder) also gives me a working interactive environment.

What is the criteria for entering shell commands using Clojure Java Shell, specifically cd?

I have been trying a number of things to enter cd /home/ics/icsdev using Clojure Java Shell. I've been getting errors, but don't know why. I've read the source and looked for examples.
ics-db.core=> (sh "cmd" "cd /home/ics/icsdev")
IOException error=2,
No such file or directory
java.lang.UNIXProcess.forkAndExec (UNIXProcess.java:-2)
I have also played around with :in and got this:
ics-db.core=> (:in "cd /home/ics/icsdev" (sh "pwd" ))
{:exit 0, :out "/home/ics/projects/clojure/ics-db\n", :err ""}
I wanted pwd to return /home/ics/icsdev.
In contrast, pwd works just fine
ics-db.core=> (sh "pwd")
{:exit 0, :out "/home/ics/projects/clojure/ics-db\n", :err ""}
ics-db.core=>
What am I missing?
Version of lein
Leiningen 2.3.4 on Java 1.7.0_55 OpenJDK Client VM
Clojure 1.5.1
At the (bash) prompt
$ which pwd
/usr/bin/pwd
$ which cd
which: no cd in $PATH
You cannot directly exec a bash built-in like cd as a new process. It is a shell command, not an executable. This is why (sh "pwd") works but (sh "cd" ...) does not.
You can do so indirectly (Clojure REPL prompt)
=> (require '[clojure.java.shell :as shell])
=> (shell/sh "sh" "-c" "cd /etc; pwd")
{:exit 0, :out "/etc\n", :err ""}
But that just sets the directory in that new shell sub-process that has now exited.
Use :dir directive or with-sh-dir if you want to exec (multiple) process from within a different directories.
cd is a builtin in the cmd prompt. So, you can't execute it in this way, and even if you could, it wouldn't matter because it would change the current working directory of the sub-process, and not the JVM that is spawning the subprocess.

Running the "exec" command in Jenkins "Execute Shell"

I'm running Jenkins on a Linux host. I'm automating the build of a C++ application. In order to build the application I need to use the 4.7 version of g++ which includes support for c++11. In order to use this version of g++ I run the following command at a command prompt:
exec /usr/bin/scl enable devtoolset-1.1 bash
So I created a "Execute shell" build step and put the following commands, which properly builds the C++ application on the command prompt:
exec /usr/bin/scl enable devtoolset-1.1 bash
libtoolize
autoreconf --force --install
./configure --prefix=/home/tomcat/.jenkins/workspace/project
make
make install
cd procs
./makem.sh /home/tomcat/.jenkins/workspace/project
The problem is that Jenkins will not run any of the commands after the "exec /usr/bin/scl enable devtoolset-1.1 bash" command, but instead just runs the "exec" command, terminates and marks the build as successful.
Any ideas on how I can re-structure the above so that Jenkins will run all the commands?
Thanks!
At the begining of your "Execute shell" script, execute source /opt/rh/devtoolset-1.1/enable to enable the devtoolet "inside" of your shell.
Which gives:
source /opt/rh/devtoolset-1.1/enable
libtoolize
autoreconf --force --install
./configure --prefix=/home/tomcat/.jenkins/workspace/project
make
make install
cd procs
./makem.sh /home/tomcat/.jenkins/workspace/project
I needed to look up what scl actually does.
Examples
scl enable example 'less --version'
runs command 'less --version' in the environment with collection 'example' enabled
scl enable foo bar bash
runs bash instance with foo and bar Software Collections enabled
So what you are doing is running a bash shell. I guess, that the bash shell returns immediately, since you are in non-interactive mode. exec runs the the command within the shell without creating a new shell. That means if the newly opened bash ends it also ends your shell prematurely. I would suggest to put all your build steps into a bash script (e.g. run_my_build.sh) and call it in the following way.
exec /usr/bin/scl enable devtoolset-1.1 run_my_build.sh
This kind of thing normally works in "find" commands, but may work here. Rather than running two, or three processes, you run one "sh" that executes multiple things, like this:
exec sh -c "thing1; thing2; thing3"
If you require each step to succeed before the next step, replace the semi-colons with double ampersands:
exec sh -c "thing1 && thing2 && thing3"
I have no idea which of your steps you wish to run together, so I am hoping you can adapt the concept to fit your needs.
Or you can put the whole lot into a script and exec that.

how to package shell script into a leiningen project

I have a shell script saved in scripts/shell/record_video.sh that I need to call in one of my projects.
An example of its use in code:
(defn save-mp4 [cam filename duration]
(sh "scripts/shell/record_video.sh" (:i_url cam) filename (str duration)))
how would I be able to jar the project so that the shell script is included if i upload to clojars?
follow up
Thanks #Ankur. The (sh "bash" :in bash-command-string) is super useful. The only reason I needed the .sh file in the first place was because I couldn't quite figure out how to do redirects when the stdout contains something big (like a video).
the file scripts/shell/record_video.sh contains:
SRC=rtsp://$1:554/axis-media/media.amp?resolution=1920x1080
DST=$2
LEN=$3
openRTSP -4 -d $LEN -w 1440 -h 1080 -f 25 -u root pass $SRC > $DST 2> /dev/null
and I did not know how to translate the redirect (>) without making the program memory consumption enormous. The (sh "bash" :in bash-command-string) command allows my function to be written without the shell script:
(defn save-mp4 [cam filename duration]
(let [src (format "rtsp://%s:554/axis-media/media.amp?resolution=1920x1080" (:i_url cam))
cmd (format "openRTSP -4 -d %d -w 1440 -h 1080 -f 25 -u root pass %s > %s 2> /dev/null"
duration src filename)]
(sh "bash" :in cmd)))
Two steps:
Package the shell script as resource.
Read the shell script file using java resource API and use
(sh "bash" :in file-str)
Where file str is the shell script content read using resource API.
You actually have two problems. First how to put a file into a jar file. Second, how to access the file from within the jar file.
The first is simple enough: include the file on the resources directory. All files in that directory are included in the jar file.
The second is a more difficult as sh is going to be looking for the script on disk, not nestled in a jar file. You may have to extract the file using class loader.getresource and them write it to disk to execute it.
There is a discussion on how to read a resource from a jar with the simplest being (clojure.java.io/resource "myscript.js")

Leiningen has problems building a working uberjar

We're trying to build our Clojure project with Leiningen. We've succeeded in creating an uberjar by doing the following:
preconditions:
project.clj file lists dependencies
:main my-project.core in project.clj
a core.clj file with a -main function
(:gen-class :main true) in core.clj
procedure:
ran lein test; completed with no failures
ran lein deps; completed successfully
from project.clj's directory: rain lein uberjar
This created two jar files: My-Project-1.0.0-SNAPSHOT-standalone.jar, and My-Project-1.0.0-SNAPSHOT.jar.
ran java -jar BioClojure-1.0.0-SNAPSHOT-standalone.jar, which resulted in this exception:
Exception in thread "main" java.lang.SecurityException: Invalid signature file digest for Manifest main attributes
My research into this problem has not been fruitful. Apparently, it's a known problem with no good solution. I do not understand the answers there.
What do we need to do to get our uberjar working?
determine which of our dependencies is causing the problem?
remove dependencies from our project?
compile the project some other way?
patch leiningen?
use the suggested command: zip *-standalone.jar -d META-INF/DUMMY.SF (I have no idea what this does)
do something with :uberjar-exclusions in the project.clj file? (if so, what?)
Lein and java versions:
$ lein version
Leiningen 1.6.1 on Java 1.6.0_26 Java HotSpot(TM) 64-Bit Server VM
Update: running the command suggested gives:
$ unzip -l BioClojure-1.0.0-SNAPSHOT-standalone.jar | grep -i -e "\.sf"
49911 08-27-09 15:57 META-INF/RCSB-PDB.SF
0 03-23-10 08:21 META-INF/maven/net.sf.alxa/
0 03-23-10 08:21 META-INF/maven/net.sf.alxa/jlatexmath/
929 03-23-10 08:20 META-INF/maven/net.sf.alxa/jlatexmath/pom.xml
115 03-21-10 14:01 META-INF/maven/net.sf.alxa/jlatexmath/pom.properties
175241 08-17-11 20:25 META-INF/SELFSIGN.SF
0 09-21-09 06:45 META-INF/maven/net.sf.opencsv/
0 09-21-09 06:45 META-INF/maven/net.sf.opencsv/opencsv/
5510 09-21-09 06:44 META-INF/maven/net.sf.opencsv/opencsv/pom.xml
106 09-21-09 06:45 META-INF/maven/net.sf.opencsv/opencsv/pom.properties
My understanding from reading the comments in that issue is that your problem would go away if you add the following to your project.clj
:uberjar-exclusions [#"foo.sf"]
where foo.sf is the particular .sf file you want to ignore from the jar. You can determine this by running:
unzip -l BioClojure-1.0.0-SNAPSHOT-standalone.jar | grep -i -e "\.sf"
The suggested zip command deletes the particular file from the jar (which is of the ZIP format).