Get absolute path of current source file in Haskell - haskell

Is it possible to get the absolute path of the current source file in Haskell?
I could only find one relevant function: getCurrentDirectory from System.Directory, but it "returns an absolute path to the current directory of the calling process.", not the path of the current file.
(I need it to read sample inputs which are located in the same folder as the source file; If there's any better way to do it, that will be helpful too!)

You can use CPP. If you compile this file
{-# LANGUAGE CPP #-}
main = print __FILE__
it will print the path to the source as you passed it to ghc – which may or may not be the full path, though:
/tmp $ ghc --make mypath.hs
[1 of 1] Compiling Main ( mypath.hs, mypath.o )
Linking mypath ...
/tmp $ ./mypath
"mypath.hs"
/tmp $ ghc --make /tmp/mypath.hs
Linking /tmp/mypath ...
/tmp $ ./mypath
"/tmp/mypath.hs"

As an alternative, the file-embed package can be used here. It uses template haskell to embed files/directories.
This can be very useful to embed resources or configs in the executable. It may not be advisable to read the sample input this way though. data-files in cabal might be better alternative as already pointed out earlier in this thread.

The PseudoMacros package might be useful. According to the description, it provides C-like strings for the current file name etc.
UPDATE
The file name returned by PseudoMacros equals the path passed to ghc (same behaviour as #JoachimBreitner mentioned in his answer), so
import PseudoMacros
main :: IO ()
main = putStrLn ("Hello from " ++ $__FILE__ ++ ", line " ++ show $__LINE__ ++ "!")
will print
Hello from tmp.hs, line 5!
or
Hello from /tmp/tmp.hs, line 5!
depending on whether you provided a relative or absolute filename to ghc.

Related

How to replace paths to executables in source code with Nix that are not in PATH

I wish to write some Haskell that calls an executable as part of its work; and install this on a nixOS host. I don't want the executable to be in my PATH (and to rely on that would disrupt the beautiful dependency model of nix).
If this were, say, a Perl script, I would have a simple builder that looked for strings of a certain format, and replaced them with the executable names, based upon dependencies declared in the .nix file. But that seems somewhat harder with the cabal-based building common to haskell.
Is there a standard idiom for encoding the paths to executables at build time (including during development, as well as at install time) within Haskell code on nix?
For the sake of a concrete example, here is a trivial "script":
import System.Process ( readProcess )
main = do
stdout <- readProcess "hostname" [] ""
putStrLn $ "Hostname: " ++ stdout
I would like to be able to compile run this (in principle) without relying on hostname being in the PATH, but rather replacing hostname with the full /nix/store/-inetutils-/bin/hostname path, and thus also gaining the benefits of dependency management under nix.
This could possibly be managed by using a shell (or similar) script, built using a replacement scheme as defined above, that sets up an environment that the haskell executable expects; but still that would need some bootstrapping via the cabal.mkDerivation, and since I'm a lover of OptParse-Applicative's bash completion, I'm loathe to slow that down with another script to fire up every time I hit the tab key. But if that's what's needed, fair enough.
I did look through cabal.mkDerivation for some sort of pre-build step, but if it's there I'm not seeing it.
Thanks,
Assuming you're building the Haskell app in Nix, you can patch a configuration file via your Nix expression. For an example of how to do this, have a look at this small project.
The crux is that you can define a postConfigure hook like this:
pkgs.haskell.lib.overrideCabal yourProject (old: {
postConfigure = ''
substituteInPlace src/Configuration.hs --replace 'helloPrefix = Nothing' 'helloPrefix = Just "${pkgs.hello}"'
'';
})
What I do with my xmonad build in nix1 is refer to executable paths as things like ##compton##/bin/compton. Then I use a script like this to generate my default.nix file:
#!/usr/bin/env bash
set -eu
packages=($(grep '##[^#]*##' src/Main.hs | sed -e 's/.*##\(.*\)##.*/\1/' | sort -u))
extra_args=()
for p in "${packages[#]}"; do
extra_args+=(--extra-arguments "$p")
done
cabal2nix . "${extra_args[#]}" \
| head -n-1
echo " patchPhase = ''";
echo " substituteInPlace src/Main.hs \\"
for p in "${packages[#]}"; do
echo " --replace '##$p##' '\${$p}' \\"
done
echo " '';"
echo "}"
What it does is grep through src/Main.hs (could easily be changed to find all haskell files, or to some specific configuration module) and pick out all the tags surrounded by## like ##some-package-name##. It then does 2 things with them:
passes them to cabal2nix as extra arguments for the nix expression it generates
post-processes nix expression output from cabal2nix to add a patch phase, which replaces the ##some-package-name## tag in the Haskell source file with the actual path to the derivation.2
This generates a nix-expression like this:
{ mkDerivation, base, compton, networkmanagerapplet, notify-osd
, powerline, setxkbmap, stdenv, synapse, system-config-printer
, taffybar, udiskie, unix, X11, xmonad, xmonad-contrib
}:
mkDerivation {
pname = "xmonad-custom";
version = "0.0.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [
base taffybar unix X11 xmonad xmonad-contrib
];
description = "My XMonad build";
license = stdenv.lib.licenses.bsd3;
patchPhase = ''
substituteInPlace src/Main.hs \
--replace '##compton##' '${compton}' \
--replace '##networkmanagerapplet##' '${networkmanagerapplet}' \
--replace '##notify-osd##' '${notify-osd}' \
--replace '##powerline##' '${powerline}' \
--replace '##setxkbmap##' '${setxkbmap}' \
--replace '##synapse##' '${synapse}' \
--replace '##system-config-printer##' '${system-config-printer}' \
--replace '##udiskie##' '${udiskie}' \
'';
}
The net result is I can just write Haskell code and a cabal package file; I don't have to worry much about maintaining the nix package file as well, only re-running my generate-nix script if my dependencies change.
In my Haskell code I just write paths to executables as if ##the-nix-package-name## was an absolute path to a folder where that package is installed, and everything magically works.
The installed xmonad binary ends up containing hardcoded references to the absolute paths to the executables I call, which is how nix likes to work (this means it automatically knows about the dependency during garbage collection, for example). And I don't have to worry about keeping the things I called in my interactive environment's PATH, or maintaining a wrapper that sets up PATH just for this executable.
1 I have it set up as a cabal project that gets built and installed into the nix store, rather than having it dynamically recompile itself from ~/.xmonad/xmonad.hs
2 Step 2 is a little meta, since I'm using a bash script to generate nix code with an embedded bash script in it
This is not indented to be the answer but if I post this in comment section it would turn out to be ugly formatted.
Also I am not sure if this hack is the right way to do the job.
I notice that if I use nix-shell I can get full path to nix store
Assume hash is always the same, AFAIK I believe it is, you can use it to hard-coded in build recipe.
$ which bash
/run/current-system/sw/bin/bash
[wizzup# ~]
$ nix-shell -p bash
[nix-shell:~]$ which bash
/nix/store/wb34dgkpmnssjkq7yj4qbjqxpnapq0lw-bash-4.4-p12/bin/bash
Lastly, I doubt if you have to to any of this if you use buildInput, it should be the same path.

How do I proc out with tilde expansion AND $PATH searching in Haskell?

I'm trying to run the elm-reactor project, which is written in Haskell. It fails because it's trying to proc out to the elm command like this:
createProcess (proc "elm" $ args fileName)
My elm executable is sitting in ~/.cabal/bin, which is in my PATH.
The System.Process.proc command searches the $PATH for its command argument, but it doesn't do tilde (~) expansion, so it doesn't find elm.
System.Process.shell has the opposite problem. It does tilde expansion, but it doesn't search the $PATH, apparently.
From the source of the System.Process command, it looks like most everything rests on a foreign ccall to "runInteractiveProcess", which I assume is doing whatever $PATH searching is being done. I don't know where the source for runInteractiveProcess would be, and my C is about 15 years worth of rusty.
I can work around this issue by
a) adding the fully-expanded cabal/bin path to my PATH or
b) symlinking an elm from the working directory to its location in cabal/bin.
However, I'd like to offer a suggested fix to the elm project, to save future adopters the trouble I've gone through. Is there a System.Process call that they should be making here that I haven't tried? Or is there a different method they should be using? I suppose at worst they could getEnv for the PATH and HOME, and implement their own file search using that before calling proc - but that breaks cross-platform compatibility. Any other suggestions?
Try using shell instead of proc, i.e.:
createProcess (shell "elm")
This should invoke elm via a shell, which hopefully will interpret tildes in $PATH as desired.
Update: Here is the experiment I performed to test what shell does...
Compile the following program (I called it run-foofoo):
import System.Process
main = do
(,,_,h) <- createProcess $ shell "foofoo"
ec <- waitForProcess h
print ec
Create a new directory ~/new-bin and place the following perl script there as the file foofoo:
#!/usr/bin/perl
print "Got here and PATH is $ENV{PATH}\n";
Run: chmod a+rx ~/new-bin/foofoo
Test with:
PATH="/bin:/usr/bin:/sbin" ./run-foofoo # should fail
PATH="$HOME/new-bin:/bin:/usr/bin:/sbin" ./run-foofoo # should succeed
PATH="~/new-bin:/bin:/usr/bin:/sbin" ./run-foofoo # ???
On my OSX system, the third test reports:
Got here and PATH is ~/new-bin:/bin:/usr/bin:/sbin
ExitSuccess

Haskell compilation with an input file, error openFile: does not exist (No such file or directory)

I apologize if this is a simple question.
I have specified an input file which is in the same directory of the code source file.
isprime :: Int -> [Int] -> Bool
isprime ...
main = do
handle <- openFile "primes-to-100k.txt" ReadMode
contents <- hGetContents handle
i <- getLine
print $ isprime (read i::Int) $ map (\x->read x::Int) $ lines contents
hClose handle
The code runs well when I use "runhaskell Q111.hs" in shell,
but when I compile it with "ghc --make Q111.hs and run, I got an error message
Q111: primes-to-100k.txt: openFile: does not exist (No such file or directory)
logout
The point is that the code runs well with ghci and runhaskell, but the executable can't find the input txt.
Do I have to provide the input txt to compiler using someway?
How are you running the executable? The text file would have to be in the directory you run the program from, which may or may not be where the program is. If you are running ./Q111 from the command line, or if you are double-clicking Q111.exe in Windows, then the text file must be in the same folder as the executable. If you are in a different directory on the command line, then the text file would have to be wherever your current directory is, and not the directory where the executable is.
EDIT: Just saw from your other comment that you are on OS X, and (I assume) are double-clicking the program. From what you said I guess that OS X sets the current directory of such-executed programs to be your home. If you want to get the directory of your program, see the answers to this question. If you use the FindBin package they mention:
main = do
exedir <- getProgPath
handle <- openFile (exedir ++ "/primes-to-100k.txt") ReadMode
...
Try the curdDir <- getCurrentDirectory and paths <- getDirectoryContents and check the paths. If it contains the filename, the path is right(and try using contents <-readFile "file" for simpler programs, but this isn't neccessary).
Edit:
...and use isexist <- doesFileExist "f" for checking the existence.

Multi-input, multi-output compilers with Shake

I'm experimenting with using Shake to build Java code, and am a bit stuck because of the unusual nature of the javac compiler. In general for each module of a large project, the compiler is invoked with all of the source files for that module as input, and produces all of the output files in one pass. Subsequently we typically take the .class files produced by the compiler and assemble them into a JAR (basically just a ZIP).
For example, a typical Java module project is arranged as follows:
a src directory that contains multiple .java files, some of them nested many levels deep in a tree.
a bin directory that contains the output from the compiler. Typically this output follows the same directory structure and filenames, with .class substituted for each .java file, but the mapping is not necessarily one-to-one: a single .java file can produce zero to many .class files!
The rules I would like to define in Shake are therefore as follows:
1) If any file under src is newer than any file under bin then erase all contents of bin and recreate with:
javac -d bin <recursive list of .java files under src>
I know this rule seems excessive, but without invoking the compiler we cannot know the extent of changes in output resulting from even a small change in a single input file.
2) if any file under bin is newer than module.jar then recreate module.jar with:
jar cf module.jar -C bin .
Many thanks!
PS Responses in the vein "just use Ant/Maven/Gradle/" will not be appreciated! I know those tools offer Java compilation out-of-the-box, but they are much harder to compose and aggregate. This is why I want to experiment with a Haskell/Shake-based tool.
Writing rules which produce multiple outputs whose names cannot be statically determined can be a bit tricky. The usual approach is to find an output whose name is statically known and always need that, or if none exists, create a fake file to use as the static output (as per ghc-make, the .result file). In your case you have module.jar as the ultimate output, so I would write:
"module.jar" *> \out -> do
javas <- getDirectoryFiles "" ["src//*.java"]
need javas
liftIO $ removeFiles "" ["bin//*"]
liftIO $ createDirectory "bin"
() <- cmd "javac -d bin" javas
classes <- getDirectoryFiles "" ["bin//*.class"]
need classes
cmd "jar cf" [out] "-C bin ."
There is no advantage to splitting it up into two rules, since you never depend on the .class files (and can't really, since they are unpredictable in name), and if any source file changes then you will always rebuild module.jar anyway. This rule has all the dependencies you mention, plus if you add/rename/delete any .java or .class file then it will automatically recompile, as the getDirectoryFiles call is tracked.

Simple library to do UTF-8 in Haskell (since Streams no longer compile)

I just want to read (and maybe write) UTF-8 data. haskell.org still advertises System.Streams which does not compile with recent ghc:
% runhaskell Setup.lhs configure
Configuring Streams-0.2.1...
runhaskell Setup.lhs build
Preprocessing library Streams-0.2.1...
Building Streams-0.2.1...
[10 of 45] Compiling System.FD ( System/FD.hs, dist/build/System/FD.o )
System/FD.hs:138:22:
Couldn't match expected type `GHC.IOBase.FD'
against inferred type `FD'
In the first argument of `fdType', namely `fd'
In a 'do' expression: fd_type <- fdType fd
In the expression:
let
oflags1 = case mode of
ReadMode -> ...
WriteMode -> ...
ReadWriteMode -> ...
AppendMode -> ...
binary_flags | binary = o_BINARY
| otherwise = 0
oflags = oflags1 .|. binary_flags
in
do fd <- fdOpen filepath oflags 438
fd_type <- fdType fd
when (mode == WriteMode && fd_type == RegularFile)
$ do fdSetFileSize fd 0
....
Similar problem with Streams 0.1. I cannot get more recent versions since the official site is down:
% wget http://files.pupeno.com/software/streams/Streams-0.1.7.tar.bz2
--2009-07-30 15:36:14-- http://files.pupeno.com/software/streams/Streams-0.1.7.tar.bz2
Resolving files.pupeno.com... failed: Name or service not known.
wget: unable to resolve host address `files.pupeno.com'
A better solution? darcs source code?
Use the utf8-string or the more recent text package.
View the list of packages on hackage.
Edit:
L. Kolmodin is right: utf8-string or text is the right answer. I'll leave my original answer below for reference. Google seems to have steered me wrong in choosing IConv. (The equivalent of my IConv wrapper function is already in utf8-string as Codec.Binary.UTF8.String.encodeString.)
Here is what I've been using--I may not remember the complete solution, so let me know if you still run into problems:
From Hackage, install IConv. Unfortunately, Codec.Text.IConv.convert operates on bytestrings, not strings. I guess you could read files directly as bytestrings, but I wrote a converter since HaXml uses normal strings:
import qualified Data.ByteString.Lazy.Char8 as B
utf8FromLatin1 = B.unpack . convert "LATIN1" "UTF-8" . B.pack
Now, on Mac OS, you have to compile with
$ ghc -O2 --make -L/usr/lib -L/opt/local/lib Whatever.hs
Because there was some library conflict, I think with MacPorts, I have to point explicitly to the built-in iconv libraries. There is probably a way to always pass those -L flags to ghc, but I haven't looked it up yet.
utf-8 strings are just byte character sequences, so it should be possible to just read and write the strings as is. All of the first 127 characters, including whitespace, should be ascii. Of course, you would need your own functions for manipulating strings since they are now multi byte sequences.

Resources