Use environment variables in F# directives - f#

In FSI, is it possible to run something like
#I #"%APPDATA%/npm/node_modules/blabla/bin/"
instead of
#I #"C:/Users/username/AppData/Roaming/npm/node_modules/blabla/bin/"
Any other options to make it not sensitive to username?

Not likely you can use environment variables in #I FSI directives.
However you may make your package installation agnostic of user name by using the trick with __SOURCE_DIRECTORY__ built-in identifier similar to one I've described in this SO answer:
create a file anchorfsi.fsx with the single line of code
#I __SOURCE_DIRECTORY__
and put it into the directory %APPDATA% points to
add to the command line starting your FSI the term
--load:%APPDATA%\anchorfsi.fsx
Now you can use relative paths in your #r directives.
Just for illustration I put into directory associated with my user profile a folder testlib containing FSharp.Data.dll. The snip below shows how it gets referenced from FSI using the outlined above technique:

Related

Cannot Load Deedle in FSharp

I have the most basic of question that should be easy but proving to be a pain. I have a f# FSX file and would like to include Deedle.
I ran the install package and it put it in C:\Users\myName.nuget\packages
Under this directory, the deedle.fsx is in deedle\2.0.4
Now, some of the documentation I have read instructs to do this at the top of the FSX file:
"#I "../../packages/Deedle"
"#load "Deedle.fsx"
Issue#1. That relative path for #I does not work, so I use an explicit path. That resolves that issue.
Issue#2. I cannot get the load statement to find the deedle.fsx file. It appends a bin/net45 to the path.
Therefore, the question is is there a trick I am missing in using this library and how I am referencing it for use?
Thx,
Marc.
try this in your fsx file
#r #"C:\Users\myName\.nuget\packages\deedle\2.0.4\lib\net45\deedle.dll"
change myName to your username.

Require dll using string path in F#

Is there a way to do this in F#:
let fakeToolsPath = "D:\tools\FAKE\tools\FakeLib.dll"
#r fakeToolsPath
the fake tools are on a different path depending on the build agent that builds the code, so I need to be able to set it dynamically, from an environment variable or some config file.
Three ideas, in order of increasing hackiness - you'll be the judge which one makes most sense in your scenario:
In .fsx script, you can use __SOURCE_DIRECTORY__ to get the directory where the script is located. If your dll is always located in the same directory relative to the script, you can use that as a "hook" to get to it.
There's a command-line --reference argument to fsi.exe that should do what you want. If you're using fake.exe instead, you can use --fsiargs to pass it in (take a look at the link for details).
If everything else fails, create a symlink as a separate build step in your CI job configuration and just hardcode the path in the script.

ocaml: Is there an environment variable to set the search path for #use and #load?

I've been wondering, if there is an environment variable to set the search path for #use and #load for the ocaml toplevel.
What I think I know so far:
I can use findlib instead of "raw" #use and #load. findlib looks at some environment variables for the search path.
I can set a search path with -I.
Experiments seem to indicate that CAML_LD_LIBRARY_PATH does not influence #use (script loading) and #load (byte code file loading).
(updated) I can use #directory to add the desired path - but unfortunately this only takes a string literal, so i can't pass something I read from the environment at run time. (Update: I forgot to mention #directory originally and why it doesn't fit my use case).
What I want to do:
Run ocaml programs as scripts
Point ocaml to script libraries and script fragments with an environment variable
Avoid, in some scenarios, to create a full findlib library.
Presently I'm not using ocaml as interpreter, but a wrapper that adds a path to the invocation. I want to avoid the wrapper.
(I hope the questions makes sense now, after you know my use case)
So: Is there an environment variable to set the search path for #use and #load without resorting to a wrapper?
More Details
What I'm currently doing:
#!/usr/bin/env oscript2
#use "MyScript"
#load "SomeModule.cmo"
(* ... more ocaml *)
oscript2 is a wrapper around ocaml that basically sets the search paths for #use and #load, but finally executes the toplevel ocaml withe something like
exec ocaml -I .... ...some-byte-code-modules... "$#"
MyScript and SomeModule.cmo live outside of the "normal" Ocaml search path. The actual location might change, but after login (and working through the profie scripts) there is an environment variable (today it's OSCRIPTLOAD_PATH) that tells me, where alle loadable byte code and ocaml script files might live.
This works well, a variant of that setup has been in use for years (at least 7).
The one thing that bothers me there, is: The wrapper, the simple fact of it's presence, looks homebrew, so I'd like to avoid it, to make a better impression on potential future users of the script collection. What I'd like to do
#!/usr/bin/env ocaml
#use "MyScript"
#load "SomeModule.cmo"
(* ... more ocaml *)
and have ocaml itself pick up the search path from some environment variable (I'm free to change the variable name, that is under my control, but I don't want to install script and byte code libs into the default search path, and, as already stated, I' asking if I can do that without findlib).
Basically (as already stated at the very beginning) I'm looking for an environment variable that controls the search path for #use and #load. I'm not looking for toplevel directives and not for wrappers that retrofit this feature. (Thanks everyone for those suggestions, but unfortunately I've already gone that road, it's feasible, but here I'm looking for an alternative purely for cosmetic reasons).
Recent research didn't bring up that such a variable exists, but I thought I'd be asking here, before giving up on it.
From inside the OCaml toplevel you can use the #directory "foo";; primitive to add an include directory.
Here's a shell script that runs the OCaml toplevel while adding a directory to the search path taken from an environment variable named EXTRA_OCAML_DIR.
#!/bin/sh
ocaml -I "$EXTRA_OCAML_DIR" "$#"
If you run this instead of ocaml, you will have a directory in the load path specified by an environment variable.
It seems a little obvious, but maybe it will spark an idea that is more helpful.

F# scripting: Add new directory via #I

How could I make this work?
#I (__SOURCE_DIRECTORY__ + #"\bin\Release")
And second questions. Is it possible to do something like:
let path = __SOURCE_DIRECTORY__ + #"\bin\Release"
#I path
?
You can't. The #I and #r commands are really pre-processor instructions that are executed before the code is dynamically compiled by fsi, so they only support string literals. This is logical if you think about it, as the referenced assemblies maybe need to compile the code.
However, the #r and #I command always take relative paths as being from the scripts location, so this
#I #".\bin\Release"
should work just fine (note the dot to ensure it’s a relative not an absolute path).
Note that runtimes relative paths are from the fsi working directory (normally the temp directory) so you do need to use __SOURCE_DIRECTORY__ when loading files from you’re scripts.

questions about fsi

how to change the working directory of fsi in VS.
how to auto load DLLs (e.g. the powerpack) at the startup of fsi.
how to auto open namespaces at the startup of fsi.
Thanks!
There is no direct way to specify this, but you can use the --use:file.fsx command line parameter, which allows you to specify an FSX file that will be loaded by F# interactive when it starts. You can specify the parameter in Tools -> Options -> F# Tools.
To do the three things you asked for, your file.fsx should look like this:
// 3. Open some namespace
open System
// 1. Change the current directory
Environment.CurrentDirectory <- "C:\\Temp"
// 2. Reference an assembly (e.g. PowerPack)
#r #"FSharp.PowerPack.dll"
See F# Interactive Options for a description of all of the available command line options. As detailed on that page, you can use the -r option to reference a particular DLL. There aren't built-in options for the other two questions you asked, but as Tomas mentioned, you can put them into a file which you #use.

Resources