opam

What does eval $(opam env) do, does it activate a opam environment?


I keep seeing

eval $(opam env)

e.g. when I try to install coq:

# brew install opam  # for mac
# for ubuntu
conda install -c conda-forge opam
opam init
# if doing local env?
# eval $(opam env)

# - install coq
# local install
#opam switch create . 4.12.1
#eval $(opam env)
#opam repo add coq-released https://coq.inria.fr/opam/released
#opam install coq

# If you want a single global (wrt conda) coq installation (for say your laptop):
opam switch create 4.12.1
opam switch 4.12.1
opam repo add coq-released https://coq.inria.fr/opam/released
opam install coq

but I never know what it does (nor no one I talk to knows) and when I google it this comes up instead: What is the use of eval `opam config env`?

cross posted: https://discuss.ocaml.org/t/what-does-eval-opam-env-do-does-it-activate-a-opam-environment/9990


Clarification

Note that

eval `opam config env`

might be very similar. In the sense that I think the dashes '' are also command substitution as in $(...) but not sure. Which might make What is the use of eval `opam config env`? very related (though I don't know the difference for sure)


Solution

  • tldr: it activates your opam context according to your current switch -- similar to how a python virtual env is activated in python but for opam.

    What eval $(opam env) does is command substitute $(opam env) i.e. run opam env in a subshell (that's what $( ) does) return the string it outputted and then give it to eval to evaluate as bash code. Command substitution is usually done to next the output of commands in bash. In python eval would interpret the input string to it as literal python code to evaluate, parse, run here similarly it would do the same but assume it's bash (is my guess).

    What does opam env does? It returns the current bash env variables for the current swithc (i.e. siwth approximately euqal to opam environment). So therefore, doing:

    eval $(opam env)
    

    does this:

    1. first runs in a subshell because thats what $(cmd) does. It's command substitution. Usually used to nest commands.
    2. then it's output (so the output string of $(opam env)) is given to eval. opam env returns a string of env variables needed to "activate: the current opam env (similar to how you activate virtual envs in python).
    3. finally eval evaluates the string it receives from the command substitution i.e. it parses the string as a bash command and runs it.

    For compeleteness see the output of opam env:

    (iit_synthesis) brandomiranda~ ❯ opam env
    OPAM_SWITCH_PREFIX='/Users/brandomiranda/.opam/4.12.1'; export OPAM_SWITCH_PREFIX;
    CAML_LD_LIBRARY_PATH='/Users/brandomiranda/.opam/4.12.1/lib/stublibs:/Users/brandomiranda/.opam/4.12.1/lib/ocaml/stublibs:/Users/brandomiranda/.opam/4.12.1/lib/ocaml'; export CAML_LD_LIBRARY_PATH;
    OCAML_TOPLEVEL_PATH='/Users/brandomiranda/.opam/4.12.1/lib/toplevel'; export OCAML_TOPLEVEL_PATH;
    PATH='/Users/brandomiranda/.opam/4.12.1/bin:/Users/brandomiranda/miniconda/envs/iit_synthesis/bin:/Users/brandomiranda/miniconda/condabin:/usr/local/bin:/Users/brandomiranda/.opam/4.12.1/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin'; export PATH;
    

    Comment on backard ticks:

    Please note : The back ticks shown around opam env after eval are essential. They change the order of application, which is very important. The back ticks tells the system to first evaluate opam env (which returns a string of commands) and then eval executes those commands in the string. Executing them doesn’t return anything, but it initializes the Opam environment behind the scenes. ref: https://ocaml.org/docs/up-and-running


    also see:


    All details that lead to this answer:

    From the man page for opam env:

    This is most usefully used as eval $(opam env) to have further shell commands be evaluated in the proper opam context.
    

    see: https://opam.ocaml.org/doc/man/opam-env.html


    Slightly more details:

    Finally, to get all the pieces together, what is the difference between command substitution (e.g. $( ) or ) and evaluating an expression (e.g. eval string or eval $(cmd) ) (in bash mainly). Ref: https://unix.stackexchange.com/a/23116/55620. My understanding is that $( ) runs a command in a subshells and outputs the string so that it's string is an input to another command e.g. eval. So to my understanding you can think of $(cmd) as replacing that expression and pasting the string of it back to to the command line for another commadn e.g. eval.  While eval will take in the string and evaluate the expression in it instead. So what eval $(cmd) does is first evaluate $(cmd) replace it with the output string of running cmd in a subshell and then feeding it to eval -- where eval will pretend that input is code and evaluate it, parse and run it. So $(...) is usually done to nest command inside other commands. 

    The only puzzling thing to me is why $(cmd) without a cmd2 (eg eval) in the front interprets the strings of $(cmd) as bash commands to run instead of just evaluting e.g. (iit_synthesis) brandomiranda~ ❯ $(echo v=1) zsh: command not found: v=1

    but

    (iit_synthesis) brandomiranda~ ❯ v=1 (iit_synthesis) brandomiranda~ ❯ echo $v 1

    does evaluate my string v=1 and then run it. 

    Thus what eval $(opam env) does is command substitute $(opam env) i.e. run opam env in a subshell return the string it outputted and then given to eval to evaluate as bash code. In python eval would interpret the input string to it as literal python code to evaluate, parse, run here similarly it would do the same but assume its bash (is my guess)


    Long answer with all details:

    toolchain = In software, a toolchain is a set of programming tools that is used to perform a complex software development task or to create a software product, which is typically another computer program or a set of related programs. https://www.google.com/search?q=toolchain&oq=toolchain&aqs=chrome..69i57j0i512l9.209j0j7&sourceid=chrome&ie=UTF-8

    (iit_synthesis) brandomiranda~ ❯ opam env OPAM_SWITCH_PREFIX='/Users/brandomiranda/.opam/4.12.1'; export OPAM_SWITCH_PREFIX; CAML_LD_LIBRARY_PATH='/Users/brandomiranda/.opam/4.12.1/lib/stublibs:/Users/brandomiranda/.opam/4.12.1/lib/ocaml/stublibs:/Users/brandomiranda/.opam/4.12.1/lib/ocaml'; export CAML_LD_LIBRARY_PATH; OCAML_TOPLEVEL_PATH='/Users/brandomiranda/.opam/4.12.1/lib/toplevel'; export OCAML_TOPLEVEL_PATH; PATH='/Users/brandomiranda/.opam/4.12.1/bin:/Users/brandomiranda/miniconda/envs/iit_synthesis/bin:/Users/brandomiranda/miniconda/condabin:/usr/local/bin:/Users/brandomiranda/.opam/4.12.1/bin:/opt/homebrew/bin:/usr/bin:/bin:/usr/sbin:/sbin'; export PATH; so opam env outputs a bunch of paths that are needed for the toolchain to work...perhaps here toolchain == opam? Based on What is the use of eval `opam config env` or eval $(opam env) and their difference? (question for eval opam config env ) I am inferring that what it means is that these are the bash env variables needed to "activate the ocaml/opam environment". Concluded that because opam env outputs a bunch of env variables. 

    So running opam env by itself only prints the output to the terminal the output of the command opam env

    $ usually refers that the identifier is a variable. e.g. $x is the variable x while x in the terminal is literally the string x (or command x if it exists).

    $(command) or command is command substitution. So doing $(command) runs the output (e.g. the script that would have been printed to the terminal after running command) of command. I assume it's called command substitution because we substitute command with the output of it and try to run it. e.g. it says: In computing, command substitution is a facility that allows a command to be run and its output to be pasted back on the command line as arguments to another command in the wikipedia article so yes. https://en.wikipedia.org/wiki/Command_substitution

    opam env = opam-env - Prints appropriate shell variable assignments to stdout. Returns the bindings for the environment variables set in the current switch, e.g. PATH, in a format intended to be evaluated by a shell. https://opam.ocaml.org/doc/man/opam-env.html note that same opam man page says This is most usefully used as eval $(opam env) to have further shell commands be evaluated in the proper opam context.

    Finally, to get all the pieces together, what is the difference between command substitution (e.g. $( ) or ) and evaluating an expression (e.g. eval string or eval $(cmd) ) (in bash mainly). Ref: https://unix.stackexchange.com/a/23116/55620. My understanding is that $( ) runs a command in a subshells and outputs the string so that it's string is an input to another command e.g. eval. So to my understanding you can think of $(cmd) as replacing that expression and pasting the string of it back to to the command line for another commadn e.g. eval.  While eval will take in the string and evaluate the expression in it instead. So what eval $(cmd) does is first evaluate $(cmd) replace it with the output string of running cmd in a subshell and then feeding it to eval -- where eval will pretend that input is code and evaluate it, parse and run it. So $(...) is usually done to nest command inside other commands. 

    The only puzzling thing to me is why $(cmd) without a cmd2 (eg eval) in the front interprets the strings of $(cmd) as bash commands to run instead of just evaluting e.g. (iit_synthesis) brandomiranda~ ❯ $(echo v=1) zsh: command not found: v=1

    but

    (iit_synthesis) brandomiranda~ ❯ v=1 (iit_synthesis) brandomiranda~ ❯ echo $v 1

    does evaluate my string v=1 and then run it. 

    Thus what eval $(opam env) does is command substitute $(opam env) i.e. run opam env in a subshell return the string it outputted and then given to eval to evaluate as bash code. In python eval would interpret the input string to it as literal python code to evaluate, parse, run here similarly it would do the same but assume its bash (is my guess)


    Eval vs $( ) details:

    Playground 1

    (iit_synthesis) brandomiranda~ ❯ clear
    (iit_synthesis) brandomiranda~ ❯ $(echo v=1)
    zsh: command not found: v=1
    (iit_synthesis) brandomiranda~ ❯ v=1
    (iit_synthesis) brandomiranda~ ❯ echo $v
    1
    

    Playground 2

    (iit_synthesis) brandomiranda~ ❯ echo vv=2
    vv=2
    (iit_synthesis) brandomiranda~ ❯ $(echo vv=2)
    zsh: command not found: vv=2
    (iit_synthesis) brandomiranda~ ❯ eval $(echo vv=2)
    (iit_synthesis) brandomiranda~ ❯ echo $vv
    2