GHC 6.12.1 dynamic executables fun

•26/01/2010 • 1 Comment

Recently I started to play with the new GHC 6.12.1. One thing I wanted to do is to link executables with -dynamic flag. It works pretty well when invoked like this:

$ ghc --make -dynamic program.hs

The resulting program will have plenty of dependencies (50+ is pretty common). It will also be quite small: for example 26K instead of 681K.

However, when building cabalized program/library one would like to have everything done automatically. However cabal install doesn’t make -dynamic executables. There is a ticket filled about this on Cabal Trac. One possible workaround is to call cabal install with –ghc-options=-dynamic flag. The other is to include this flag in ghc-options field in .cabal file. This is better since we can add it selectively. Consider this example binlib:

name: dynbin
version: 0.0
license: BSD3
license-file: LICENSE
copyright: (c) Christopher Skrzetnicki
author: Christopher Skrzetnicki
maintainer: Christopher Skrzetnicki <gtener@gmail.com>
stability: experimental
synopsis:
description:
category: Other
cabal-version: >= 1.2
build-type: Simple

library
  hs-source-dirs: src
  exposed-modules: Dynbin.Main
  build-depends: base == 4.*

executable dynbin-dyn
  executable: dynbin-dyn
  hs-source-dirs: src
  ghc-options: -dynamic
  main-is: dynamic_main.hs

executable dynbin-stat
  executable: dynbin-stat
  hs-source-dirs: src
  main-is: static_main.hs

Files used are pretty straightforward.

dynamic_main.hs:

import qualified Dynbin.Main

main :: IO ()
main = putStrLn "dynamic_main.hs" >> Dynbin.Main.main

static_main.hs:

import qualified Dynbin.Main

main :: IO ()
main = putStrLn "static_main.hs" >> Dynbin.Main.main

Dynbin.Main:

module Dynbin.Main ( main ) where

main :: IO ()
main = putStrLn "Hello from Dynbin.Main.main !!!"

After installing it works like charm:

$ dynbin-stat
static_main.hs
Hello from Dynbin.Main.main !!!

$ dynbin-dyn
dynamic_main.hs
Hello from Dynbin.Main.main !!!

Calling ldd confirms that first executable is static, and the second is dynamic.

$ ldd `which dynbin-stat`
    linux-gate.so.1 =>  (0xb78dd000)
    librt.so.1 => /lib/librt.so.1 (0xb78ab000)
    libutil.so.1 => /lib/libutil.so.1 (0xb78a7000)
    libdl.so.2 => /lib/libdl.so.2 (0xb78a3000)
    libgmp.so.3 => /usr/lib/libgmp.so.3 (0xb7856000)
    libm.so.6 => /lib/libm.so.6 (0xb782f000)
    libc.so.6 => /lib/libc.so.6 (0xb76e8000)
    libpthread.so.0 => /lib/libpthread.so.0 (0xb76cf000)
    /lib/ld-linux.so.2 (0xb78de000)

$ ldd `which dynbin-dyn`
    linux-gate.so.1 =>  (0xb77f6000)
    libHSunix-2.4.0.0-ghc6.12.1.so => /usr/lib/ghc-6.12.1/unix-2.4.0.0/libHSunix-2.4.0.0-ghc6.12.1.so (0xb7765000)
    librt.so.1 => /lib/librt.so.1 (0xb7734000)
    libutil.so.1 => /lib/libutil.so.1 (0xb7730000)
    libdl.so.2 => /lib/libdl.so.2 (0xb772b000)
    libHSbase-4.2.0.0-ghc6.12.1.so => /usr/lib/ghc-6.12.1/base-4.2.0.0/libHSbase-4.2.0.0-ghc6.12.1.so (0xb72ae000)
    libHSinteger-gmp-0.2.0.0-ghc6.12.1.so => /usr/lib/ghc-6.12.1/integer-gmp-0.2.0.0/libHSinteger-gmp-0.2.0.0-ghc6.12.1.so (0xb729e000)
    libgmp.so.3 => /usr/lib/libgmp.so.3 (0xb7251000)
    libHSghc-prim-0.2.0.0-ghc6.12.1.so => /usr/lib/ghc-6.12.1/ghc-prim-0.2.0.0/libHSghc-prim-0.2.0.0-ghc6.12.1.so (0xb71de000)
    libHSrts-ghc6.12.1.so => /usr/lib/ghc-6.12.1/libHSrts-ghc6.12.1.so (0xb7198000)
    libm.so.6 => /lib/libm.so.6 (0xb7172000)
    libHSffi-ghc6.12.1.so => /usr/lib/ghc-6.12.1/libHSffi-ghc6.12.1.so (0xb7169000)
    libc.so.6 => /lib/libc.so.6 (0xb7022000)
    libpthread.so.0 => /lib/libpthread.so.0 (0xb7009000)
    /lib/ld-linux.so.2 (0xb77f7000)

So this is OK for now. But what happens if we choose to have some TH code? Let’s use some. The following code uses packages hslogger and hslogger-template to add some logging to our code.

{-# LANGUAGE TemplateHaskell, ForeignFunctionInterface, CPP #-}

module Dynbin.Main ( main ) where

import System.Log.Logger.TH (deriveLoggers)
import qualified System.Log.Logger as HSL
import System.Log.Logger ( updateGlobalLogger, rootLoggerName, setLevel )

$(deriveLoggers "HSL" [HSL.WARNING, HSL.NOTICE, HSL.INFO])

-- dynamic linking
#ifdef MAIN_LIB
main_lib = MAIN_LIB
foreign export ccall MAIN_LIB main :: IO ()
#endif

main :: IO ()
main = do updateGlobalLogger rootLoggerName (setLevel HSL.INFO)
          infoM "Oh hai!"

dynbin.cabal:

name: dynbin
version: 0.0
license: BSD3
license-file: LICENSE
copyright: (c) Christopher Skrzetnicki
author: Christopher Skrzetnicki
maintainer: Christopher Skrzetnicki <gtener@gmail.com>
stability: experimental
synopsis:
description:
category: Other
cabal-version: >= 1.2
build-type: Simple

library
  hs-source-dirs: src
  exposed-modules: Dynbin.Main
  cpp-options: -DMAIN_LIB="main_lib"
  build-depends: base == 4.*, hslogger, hslogger-template

executable dynbin-dyn
  executable: dynbin-dyn
  hs-source-dirs: src
  ghc-options: -dynamic
  cpp-options: -DMAIN_LIB="main_lib"
  main-is: dynamic_dlopen.hs
  other-modules: Paths_dynbin
  build-depends: unix, filepath, directory

executable dynbin-stat
  executable: dynbin-stat
  hs-source-dirs: src
  main-is: static_main.hs

The static executable builds and works well:

$ dynbin-stat
static_main.hs
Dynbin.Main: Oh hai!

However, dynbin-dyn doesn’t. Here is what happens:

Resolving dependencies...
Configuring dynbin-0.0...
Preprocessing library dynbin-0.0...
Preprocessing executables for dynbin-0.0...
Building dynbin-0.0...
Registering dynbin-0.0...
[1 of 2] Compiling Dynbin.Main      ( src/Dynbin/Main.hs, dist/build/dynbin-dyn/dynbin-dyn-tmp/Dynbin/Main.o )
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package array-0.3.0.0 ... linking ... done.
Loading package filepath-1.1.0.3 ... linking ... done.
Loading package old-locale-1.0.0.2 ... linking ... done.
Loading package old-time-1.0.0.3 ... linking ... done.
Loading package unix-2.4.0.0 ... linking ... done.
Loading package directory-1.0.1.0 ... linking ... done.
Loading package process-1.0.1.2 ... linking ... done.
Loading package time-1.1.4 ... linking ... done.
Loading package random-1.0.0.2 ... linking ... done.
Loading package haskell98 ... linking ... done.
Loading package syb-0.1.0.2 ... linking ... done.
Loading package base-3.0.3.2 ... linking ... done.
Loading package containers-0.3.0.0 ... linking ... done.
Loading package mtl-1.1.0.2 ... linking ... done.
Loading package parsec-2.1.0.1 ... linking ... done.
Loading package network-2.2.1.7 ... linking ... done.
Loading package hslogger-1.0.7 ... linking ... done.
Loading package pretty-1.0.1.1 ... linking ... done.
Loading package template-haskell ... linking ... done.
Loading package hslogger-template-1.0.0 ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
src/Dynbin/Main.hs:1:0:
    Dynamic linking required, but this is a non-standard build (eg. prof).
    You need to build the program twice: once the normal way, and then
    in the desired way using -osuf to set the object file suffix.
cabal: Error: some packages failed to install:
dynbin-0.0 failed during the building phase. The exception was:
ExitFailure 1

What does it mean? Well, we can’t simply mix dynamic builds with TH. I’m not sure why is it so, but the description how to solve it when using ghc –make is here. But here we are in a cabal install world. So how we are going to solve it?

There are two solutions. The first, mentioned in discussion for ticket #600, is this: move all executables to a different package and make it depend on the first one. This solution works, but is boring ;-) Here is a .cabal file for this:

name: dynbin-prog
version: 0.0
license: BSD3
license-file: LICENSE
copyright: (c) Christopher Skrzetnicki
author: Christopher Skrzetnicki
maintainer: Christopher Skrzetnicki <gtener@gmail.com>
stability: experimental
synopsis:
description:
category: Other
cabal-version: >= 1.2
build-type: Simple

executable dynbin-dyn
  executable: dynbin-dyn
  hs-source-dirs: src
  ghc-options: -dynamic
  main-is: dynamic_main.hs
  build-depends: dynbin, base == 4.*

executable dynbin-stat
  executable: dynbin-stat
  hs-source-dirs: src
  main-is: static_main.hs
  build-depends: dynbin, base == 4.*

There is another solution which is much more fun.

HERE BE DRAGONS

The solution is this: build dynamic library and then use dlopen to open it, then dlsym to select and run specific symbol from the library itself. Pretty simple, huh? So let’s get to work.

First lest define a common symbol for calling. We might choose to take something from output from this command:

$ nm -D dist/build/libHSdynbin-0.0-ghc6.12.1.so
         w _Jv_RegisterClasses
00002dd8 A __bss_start
         w __cxa_finalize
         w __gmon_start__
         U __stginit_base_Prelude_dyn
000019f8 T __stginit_dynbinzm0zi0_DynbinziMain
000019a4 T __stginit_dynbinzm0zi0_DynbinziMain_dyn
         U __stginit_hsloggerzm1zi0zi7_SystemziLogziLogger_dyn
         U __stginit_hsloggerzmtemplatezm1zi0zi0_SystemziLogziLoggerziTH_dyn
00002dd8 A _edata
00002de0 A _end
00001ad8 T _fini
000014f0 T _init
         U base_GHCziBase_unpackCStringzh_info
         U base_GHCziTopHandler_runIO_closure
00002dc0 D dynbinzm0zi0_DynbinziMain_main_closure
00001984 T dynbinzm0zi0_DynbinziMain_main_info
00002dbc D dynbinzm0zi0_DynbinziMain_main_srt
00002db4 D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs1_closure
00001934 T dynbinzm0zi0_DynbinziMain_zdfmainzuaOs1_info
00002da0 D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs1_srt
00002d80 D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs2_closure
00001674 T dynbinzm0zi0_DynbinziMain_zdfmainzuaOs2_info
00002d90 D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs3_closure
000016f8 T dynbinzm0zi0_DynbinziMain_zdfmainzuaOs3_info
00002dcc D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs_closure
0000199c T dynbinzm0zi0_DynbinziMain_zdfmainzuaOs_info
00002dc8 D dynbinzm0zi0_DynbinziMain_zdfmainzuaOs_srt
         U getStablePtr
         U ghczmprim_GHCziTypes_ZMZN_closure
         U hsloggerzm1zi0zi7_SystemziLog_INFO_closure
         U hsloggerzm1zi0zi7_SystemziLogziLogger_Logger_con_info
         U hsloggerzm1zi0zi7_SystemziLogziLogger_alertM2_closure
         U hsloggerzm1zi0zi7_SystemziLogziLogger_alertM2_info
         U hsloggerzm1zi0zi7_SystemziLogziLogger_saveGlobalLogger1_closure
         U hsloggerzm1zi0zi7_SystemziLogziLogger_saveGlobalLogger1_info
         U hsloggerzm1zi0zi7_SystemziLogziLogger_zdwa_closure
         U hsloggerzm1zi0zi7_SystemziLogziLogger_zdwa_info
         U newCAF
         U rts_apply
         U rts_checkSchedStatus
         U rts_evalIO
         U rts_lock
         U rts_unlock
         U stg_CAF_BLACKHOLE_info
         U stg_IND_STATIC_info
         U stg_gc_ut
         U stg_upd_frame_info

But this would make the solution even more fun than it is already. Instead let’s take different approach.

We shall export Dynbin.Main.main with foreign export using symbol defined with CPP macro MAIN_LIB. Then we shall use the same macro in dynbin-dyn to call the exported symbol. Both simple and fun! Here comes the code:

dynamic_dlopen.hs:

{-# LANGUAGE ForeignFunctionInterface, CPP #-}

{-

Required CPP Macros:
- MAIN_LIB: entry point for dynamic library

-}

module Main(main) where

import System.Posix.DynamicLinker
import System.Directory
import System.FilePath
import Control.Applicative
import Foreign
import Paths_dynbin ( getLibDir )

main_lib = MAIN_LIB

main = do
  libdir <- getLibDir
  print libdir

  shared <- filter (\fn -> takeExtension fn == ".so") <$> getDirectoryContents libdir
  case shared of
    [] -> error "No shared libraries!"
    [x] -> putStrLn ("One shared lib: " ++ x) >> onShared (libdir </> x)
    _ -> error "Multiple shared libraries!"

foreign import ccall "dynamic" call_dyn_fun :: FunPtr (IO ()) -> IO ()

dlopen' a1 a2 = print (a1,a2) >> dlopen a1 a2

dlcall :: DL -> String -> IO ()
dlcall dynlib fun = do
  print (dynlib,fun)
  fptr <- dlsym dynlib fun
  case nullFunPtr == fptr of
    False -> call_dyn_fun fptr
    True -> putStrLn =<< dlerror

onShared :: FilePath -> IO ()
onShared libPath = do
  main <- dlopen' libPath [RTLD_LAZY, RTLD_LOCAL]
  dlcall main main_lib
  putStrLn "main_lib exited"
  return ()

static_main.hs , unchanged:

import qualified Dynbin.Main

main :: IO ()
main = putStrLn "static_main.hs" >> Dynbin.Main.main

Dynbin.Main:

{-# LANGUAGE TemplateHaskell #-}

module Dynbin.Main ( main ) where

import System.Log.Logger.TH (deriveLoggers)
import qualified System.Log.Logger as HSL
import System.Log.Logger ( updateGlobalLogger, rootLoggerName, setLevel )

$(deriveLoggers "HSL" [HSL.WARNING, HSL.NOTICE, HSL.INFO])

main :: IO ()
main = do updateGlobalLogger rootLoggerName (setLevel HSL.INFO)
          infoM "Oh hai!"

And this solution works as expected:

$ dynbin-dyn
"/home/tener//lib/dynbin-0.0/ghc-6.12.1"
One shared lib: libHSdynbin-0.0-ghc6.12.1.so
("/home/tener//lib/dynbin-0.0/ghc-6.12.1/libHSdynbin-0.0-ghc6.12.1.so",[RTLD_LAZY,RTLD_LOCAL])
(DLHandle 0x09216080,"main_lib")
Dynbin.Main: Oh hai!
main_lib exited
$ dynbin-stat
static_main.hs
Dynbin.Main: Oh hai!

The whole setup is pretty fragile. If you build run-dyn without -dynamic flag you will get runtime errors. Note that this behaviour is likely to be linked with this ticket.

Without -dynamic:

$ dynbin-dyn
"/home/tener//lib/dynbin-0.0/ghc-6.12.1"
One shared lib: libHSdynbin-0.0-ghc6.12.1.so
("/home/tener//lib/dynbin-0.0/ghc-6.12.1/libHSdynbin-0.0-ghc6.12.1.so",[RTLD_LAZY,RTLD_LOCAL])
dynbin-dyn: user error (dlopen:
        /usr/lib/ghc-6.12.1/ghc-prim-0.2.0.0/libHSghc-prim-0.2.0.0-ghc6.12.1.so:
        undefined symbol: stg_newByteArrayzh)

So, what do you think about the whole idea?


Configuration used:

$ uname -a
Linux laptener 2.6.32-ARCH #1 SMP PREEMPT Tue Jan 19 06:08:04
       UTC 2010 i686 Intel(R) Core(TM)2 Duo
       CPU T7500 @ 2.20GHz GenuineIntel GNU/Linux
$ cabal -V
cabal-install version 0.8.0
using version 1.8.0.2 of the Cabal library
$ ghc -V
The Glorious Glasgow Haskell Compilation System, version 6.12.1

I also have two non-default flags in my ~/.cabal/config file:

   library-profiling: True
   shared: True

Sharing wireless connection with wired computer

•07/01/2010 • Leave a Comment

Yesterday I wanted to install Arch Linux on a brand new mini computer (with Quad-Core Atom!). I did that on wired connection in one place and then wanted to install additional packages at home.

Problem is: I only have wireless connection at home. So I decided I’ll share wireless connection using my laptop connected to a switch. Here is a diagram of my setup:
Network setup

The hardest part was finding manual how to do this that is on the right level of advancement. There are plenty of descriptions how to create a corporate-level network and configure that with loads of features. And they’re all far too long and quite an overkill for my purposes.

The solution I came up with comes from many sources, most notably Arch Linux wiki pages about basic routing and dnsmasq. The rest is manual pages and various other sources.

So, how to achieve what I wanted?

  1. Enable kernel routing in laptop. Hardly any manual mention that simple step. The temporary solution is to execute command “echo 1 > /proc/sys/net/ipv4/ip_forward”. Alternatively in /etc/sysctl.conf set “net.ipv4.ip_forward=1″.
  2. Install iptables and dnsmasq. Both are needed for this setup. First one will handle forwarding packets from “Laptop sara” and “Laptop router” subnets. The second is a relatively tiny DNS and DHCP server.
  3. Configure dnsmasq. In router’s subnet ip’s are from 192.168.1.1-255 range. dnsmasq will use eth0 interface and assign ip’s from 192.168.2.2-255 range. Here is /etc/dnsmasq.conf that will do that:
    interface=eth0
    expand-hosts
    domain=localdomain
    dhcp-range=192.168.2.2,192.168.2.10,1h
    dhcp-host=sara,192.168.2.3 # static assignment
    #log-dhcp # usefull for debugging
    log-queries # less spam than above, can be left enabled

    Of course we need to enable dnsmasq: “/etc/rc.d/dnsmasq start”.
  4. Configure iptables. Simple and fun:
    iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE # note the use of 'wlan0' here
    /etc/rc.d/iptables save
    /etc/rc.d/iptables start

  5. 192.168.2.1 will be used as eth0’s address. This one is simple: “ifconfig eth0 up && ifconfig eth0 192.168.2.1″. I also had to turn of wicd connection manager daemon, since it used to disconnect me from wired connection from time to time for no real reason: “/etc/rc.d/wicd stop”.
  6. All that is left is making sara ask for assignment: “sudo dhcpcd” and done. We are up and running!

Shared Haskell .so library with GHC 6.10.4 and Cabal

•03/01/2010 • 2 Comments

I wanted to extend an app written in ANSI C with a functionality written in Haskell. This can be somewhat complicated process. If you use GHC 6.12.1 or newer it can supposedly be easily done with -shared flag. Well, I’m not sure how it works there, but installing GHC 6.12.1 is a painful process. Instead I wanted to do the same with GHC 6.10.4. Turns out it can be done with a simple Makefile like this:

shared:
      ghc -O --make \
           -no-hs-main -optl '-shared' -optc '-DMODULE=HaskellExts' \
           -o HaskellExts.so src/HaskellExts.hs src/module_init.c

module_init.c is also pretty simple and generic:
#define CAT(a,b) XCAT(a,b)
#define XCAT(a,b) a ## b
#define STR(a) XSTR(a)
#define XSTR(a) #a

#include

extern void CAT (__stginit_, MODULE) (void);

static void library_init (void) __attribute__ ((constructor));
static void
library_init (void)
{
      /* This seems to be a no-op, but it makes the GHCRTS envvar work. */
      static char *argv[] = { STR (MODULE) ".so", 0 }, **argv_ = argv;
      static int argc = 1;
     
      hs_init (&argc, &argv_);
      hs_add_root (CAT (__stginit_, MODULE));
}

static void library_exit (void) __attribute__ ((destructor));
static void
library_exit (void)
{
hs_exit ();
}

I took both from http://wiki.python.org/moin/PythonVsHaskell.

Now, this method works in general. However, I wanted to make a .cabal package description, because cabal-install handles some common tasks (like pulling dependencies and cleaning) automatically. This turned out to be little tricky. I had to cheat: the shared library is actually build as executable. Here is the core part of .cabal:

executable HaskellExts.so
      build-depends: base == 4.*, directory == 1.0.* , filepath == 1.1.*, edit-distance == 0.1.2.*
      hs-source-dirs: src
      ghc-options: -optl-shared -optc-DMODULE=HaskellExts -no-hs-main
      main-is: HaskellExts.hs
      c-sources: src/module_init.c
      include-dirs: src
      install-includes: HaskellExts.h
      cc-options: -DMODULE=HaskellExts -shared
      ld-options: -shared

I’m not sure it is the minimal configuration that works. But it works for me so I’m happy with it. The whole code is available here: http://dl.dropbox.com/u/1479187/blog/killer-haskell-exts.7z

Another day, another release.

•05/12/2009 • Leave a Comment

It’s been a rich time in terms of number of packages I’ve released recently.

I’ve had several half-baked packages on my hard drive I’ve never actually made polished. I made a decision it can no longer be this way – I should contribute to the Haskell community I owe so much.

Today brings the release of a small wrapping package: readline-statevar. It brings StateVar API for readline package, which wraps libreadline.

Handful of links:
Announcement post
Git repo
Issue tracker
Hackage page

Kinda sorty

•01/12/2009 • Leave a Comment

I needed a tool to perform relatively simple sorting. An output from ‘du -h’ looks like this:

~/ du -a -h cups-pdf
216K cups-pdf/job_41-_stdin_.pdf
2,6M cups-pdf/job_46-202aw__Polska_do_Droga_bez_nazwy_-_Mapy_Google.pdf
424K cups-pdf/job_45-274e_-_Lifestyle_-_studente.pdf
68K cups-pdf/job_44-Google_Maps_API_-_API_Key_Signup.pdf
40K cups-pdf/job_50-Wersja_do_druku___MICKIEWICZA_-_rondo.pdf
136K cups-pdf/job_58-Studia_magisterskie_-_program_po_zmianach_w_roku_2005.pdf
536K cups-pdf/job_40-_stdin_.pdf
1,6M cups-pdf/job_39-_stdin_.pdf
40K cups-pdf/job_52-263j.pdf
104K cups-pdf/job_43-cracking_wpa__Aircrack-ng_.pdf
34M cups-pdf/job_60-DjVu_PostScript_document.pdf
80K cups-pdf/job_56-Bluetooth_GPRS_Howto_-_ArchWiki.pdf
40K cups-pdf/job_51-Wersja_do_druku___Al_Podwale-Palisadowa.pdf
1,1M cups-pdf/job_42-_stdin_.pdf
41M cups-pdf

I needed it sorted per filesize, which is present in first column. Unfortunately I couldn’t find a way to do this with “sort” utility. So I wrote this tool. It works as it should:

█▓▒░tener@laptener░▒▓██▓▒░ śro gru 02 01:16:12
~/ du -a -h cups-pdf | sorty

40K cups-pdf/job_50-Wersja_do_druku___MICKIEWICZA_-_rondo.pdf
40K cups-pdf/job_52-263j.pdf
40K cups-pdf/job_51-Wersja_do_druku___Al_Podwale-Palisadowa.pdf
68K cups-pdf/job_44-Google_Maps_API_-_API_Key_Signup.pdf
80K cups-pdf/job_56-Bluetooth_GPRS_Howto_-_ArchWiki.pdf
104K cups-pdf/job_43-cracking_wpa__Aircrack-ng_.pdf
136K cups-pdf/job_58-Studia_magisterskie_-_program_po_zmianach_w_roku_2005.pdf
216K cups-pdf/job_41-_stdin_.pdf
424K cups-pdf/job_45-274e_-_Lifestyle_-_studente.pdf
536K cups-pdf/job_40-_stdin_.pdf
1,6M cups-pdf/job_39-_stdin_.pdf
1,1M cups-pdf/job_42-_stdin_.pdf
2,6M cups-pdf/job_46-202aw__Polska_do_Droga_bez_nazwy_-_Mapy_Google.pdf
34M cups-pdf/job_60-DjVu_PostScript_document.pdf
41M cups-pdf

I’ve put it on Hackage here, and the repo is on GitHub here.

Released package-vt-0.1.3.3

•27/11/2009 • Leave a Comment

Just a quick update now. I’ve just released a brand new tool called package-vt. It should be used for detecting changes in packages that should cause a version bump.

You can read more on Haskell Cafe.
Page on Hackage.
Git repo.

Stay tuned for more info!

Rsync over ssh with custom port

•14/11/2009 • Leave a Comment

It’s just a quick tip. How can you force rsync to use a specific port when connecting to remote machine via ssh? Turns out it’s pretty easy, if you dig into man page deep enough. Simply specify needed port in ‘-e’ option like this:

rsync -av -e "ssh -p 123" example.com:/path/on/remote/server ~/local/path

As a bonus, with the same option you can add a lot more useful options, like compression and compression ratio:

... -e "ssh -p 123 -C -oCompressionLevel=9" ...

I’m not dead

•10/11/2009 • Leave a Comment

… yet. It’s actually quite the opposite: I’m alive but quite busy. It’s hard for me to find any time to write anything for the blog. I’ve been writing quite a lot recently, most of which you can read in this article.

To be more specific: I wrote an introductory article about basics of Haskell for a polish computer science portal. I put quite some effort in it (~25h), but I hope there are people that will benefit from it greatly.

Haskell jobs

•26/10/2009 • Leave a Comment

Recently I’ve been looking for a job involving Haskell or functional programming in general. No luck. I know that there are is page in Haskell wiki about that, but it’s almost empty, if I filter out those which doesn’t match my preferences.

First thing is: most companies are from US. Next came UK finance companies (like Barclays or Credit Suisse), which are way too far for me. There are also a couple of links to now-dead startups that meant to use Haskell as a silver bullet. No luck I suppose. The one interesting company listed there is Starling Software. Too bad they are from Japan. Another notable exception is Erlang Training and Consulting Ltd. Sadly, while they do have an office in Kraków, they don’t seem to willing to expand their staff there.

I also tried several job search engines for a FP job in Poland, without luck.

My research seems to agree with a previous work you can read about here.

It is sad that most companies that use FP seem to be either too far away or long dead.

I would really love to see one of those near Wrocław.

Python web server for documentation

•25/09/2009 • Leave a Comment

In this blog post we can read about handy way to run a web server to serve files in current directory using Python. I wanted to use it in order to read a documentation for Haskell modules stored locally. Typing

python -m SimpleHTTPServer

was easy, but I wasn’t happy with the result. On main page there was index of all modules. That’s fine. However after choosing one I would see a directory with two entries:

  1. html (directory)
  2. LICENSE (file)

I wanted to skip right into ‘html’ directory. Here is code to do just that:

#!/usr/bin/python3

from http.server import *
import http.server
from os import system, chdir
import sys

def run(server_class=HTTPServer, handler_class=http.server.SimpleHTTPRequestHandler):
    server_address = ('', 4000)
    httpd = server_class(server_address, handler_class)
    httpd.serve_forever()

class MyRequestHandler( http.server.SimpleHTTPRequestHandler ):
    def do_GET(self,*args,**kwds):
        tmp = self.path.split('/')
        # redirect?
        if (len(tmp) == 3) and (tmp[0] == tmp[2]) and tmp[0] == '':
            self.send_response(301)
            self.send_header('Location', self.path + 'html/')
        else:
            http.server.SimpleHTTPRequestHandler.do_GET(self,*args,**kwds)

if __name__ == '__main__':
    chdir('/home/tener/share/doc')
    run(handler_class=MyRequestHandler)

One remark: I decided write code compatible with new Python 3, since that was easy enough and likely to work with new versions for a long time.