2015-07-02 1 views
13

Sto provando a compilare del codice Rust con un codice Haskell. Ho un sistema di test configurato con un file, Fibonacci.hs con una funzione che calcola i numeri di Fibonacci in Haskell ed esporta la funzione come fibonacci_hs tramite l'FFI di Haskell (come qui: https://github.com/nh2/haskell-from-python, anche se copierò e incollerò in basso), e in wrapper.c hanno definito le funzioni da esportare da chiamare per l'inizializzazione e l'uscita da RTS di Haskell.Chiamare il codice Haskell collegato dinamicamente da Rust

Il codice simile a questo:

{- Fibonacci.hs -} 
{-# LANGUAGE ForeignFunctionInterface #-} 

module Fibonacci where 

import Foreign.C.Types 

fibonacci :: Int -> Int 
fibonacci n = fibs !! n 
    where fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

fibonacci_hs :: CInt -> CInt 
fibonacci_hs = fromIntegral . fibonacci . fromIntegral 

foreign export ccall fibonacci_hs :: CInt -> CInt 

// wrapper.c 

#include <stdlib.h> 
#include "HsFFI.h" 

void 
example_init (void) 
{ 
    hs_init (NULL, NULL); 
} 

void 
example_exit (void) 
{ 
    hs_exit(); 
} 

posso compilare questi tramite:

ghc -c -dynamic -fPIC Fibonacci.hs

ghc -c -dynamic -fPIC wrapper.c

ed io collegare gli oggetti in una libreria condivisa/dinamico (più su questo in un secondo) tramite:

ghc -o libfibonacci.so -shared -dynamic -fPIC Fibonacci.o wrapper.o -lHSrts

Sulla esecuzione del codice di esempio Python dal repository collegato, funziona bene sul mio Mac, ma non posso ottenere il collegamento con Rust.

A Rust mio codice sembra qualcosa di simile:

//main.rs 
#[link(name = "fibonacci")] 
extern { 
    fn fibonacci_hs (n : i32); // c_int = i32 
    fn fib_init(); // start hs rts 
    fn fib_exit(); // kill hs rts 
} 

fn main() { 
    unsafe { 
     fib_init(); 
     for i in 0..100 { 
      println!("{:?}th fibonacci : {:?}", i, fibonacci_hs(i)); 
     } 
     fib_exit(); 
    } 
} 

E posso compilare con rustc main.rs -L . (dal file della libreria condivisa è locale).

L'errore genero su Mac, quando viene compilato con una libreria dinamica (ghc -o libfibonacci.so -shared -static haskell/Fibonacci.o haskell/wrapper.o -lHSrts poi 'rustc main.rs -L.) È in fase di esecuzione:

dyld: Symbol not found: _ffi_call 
    Referenced from: ./libfibonacci.so 
    Expected in: flat namespace 
in ./libfibonacci.so 
Trace/BPT trap: 5 

Grazie per qualsiasi aiuto in anticipo.

+0

Mi dispiace. @Shepmaster --typo da parte mia, dovrebbe essere .c. – chalkandpaste

+0

@ReidBarton Ho tagliato tutte le cose estranee. Concentrandosi sulla compilazione di una libreria dinamica sul mio Mac usando Rust e Haskell. – chalkandpaste

risposta

5

Quando si compila la libreria condivisa, sembra che è necessario collegare contro libffi così:

ghc -o libfibonacci.dylib -shared -dynamic -fPIC \ 
    Fibonacci.hs wrapper.c -lHSrts -lffi 

ho dedotto questo andando nella mia directory di libreria GHC (/usr/local/lib/ghc-7.10.1/rts) e poi grep per il simbolo ffi_call :

$ grep -lRa ffi_call . 
./include/ffi.h 
./rts/libHSrts-ghc7.10.1.dylib 
... 

ho quindi utilizzato nm per trovare quale biblioteca esatto aveva:

for i in *dylib; do 
    if nm $i | grep -q 'T.*ffi_call'; then 
     echo "== $i"; 
    fi; 
done 

sono stato poi in grado di funzionare con:

DYLD_LIBRARY_PATH='.' ./main 

Purtroppo, sembra il codice non è giusto, come ho appena Prendi un gruppo di tuple vuote. Hai dimenticato di avere un tipo di ritorno sulla funzione, e poi ti imbatti in un problema che il 46 o più Fibbonacci è troppo grande per un u32.

Inoltre, è necessario utilizzare i tipi dal pacchetto libc e potrebbe essere più sicuro utilizzare uno u64 qui.

Ho installato GHC 7.10.1 utilizzando Homebrew, ma si spera che lo stesso modello funzionerebbe altrove.

+0

Grazie mille. Mi sono reso conto l'ultima notte che ho sbagliato il tipo di ritorno: avevo una build che in qualche modo funzionava (non sono stato in grado di riprodurlo oggi) e ho ottenuto tipi di unità/ritorni a tuple vuoti, ma non sono riuscito a capire cosa gli permetteva costruire (nulla nella mia storia del terminale mi ha permesso di riprodurlo). Ancora grazie mille @Shepmaster! – chalkandpaste

5

Lei parla di due diversi comandi ultimo anello,

ghc -o libfibonacci.so -shared -dynamic -fPIC Fibonacci.o wrapper.o -lHSrts 

e

ghc -o libfibonacci.so -shared -static haskell/Fibonacci.o haskell/wrapper.o -lHSrts 

Potrebbe valere la pena in modo esplicito che descrive quello che alcuni di questi flag significa.

  • -shared dice GHC per produrre un oggetto condiviso (piuttosto che un file eseguibile).

  • -dynamic dice GHC di collegare l'uscita contro le versioni di libreria dinamiche delle sue dipendenze Haskell (base, GHC-prim, ecc)

  • -static è l'opposto di -dynamic, racconta GHC a collegare contro il versioni di librerie statiche di dipendenze Haskell.

  • -lHSrts significa collegarsi alla libreria (statica o condivisa) libHSrts. Ma in GHC solo, solo la libreria statica ha il nome di base libHSrts (in modo che il nome del file della libreria sia libHSrts.a). La versione della libreria condivisa ha il nome del file libHSrts-ghc7.8.4.so (aggiusta per la tua versione GHC). Quindi, -lHSrts significa davvero collegarsi alla versione della libreria statica di RTS.

Quindi il secondo comando si collega alle versioni statiche di tutte le dipendenze Haskell, incluso l'RTS. Questo può funzionare su OS X dove tutto il codice deve essere generato come PIC, ma non funzionerà su una normale distribuzione binaria Linux di GHC perché una libreria condivisa deve essere codice PIC, ma le librerie Haskell statiche fornite con GHC sono costruite come non -PIC (sono destinati ad essere collegati in eseguibili non rilocabili). Non capisco completamente perché GHC non sia abbastanza intelligente da aggiungere qui lo -lffi, forse non si aspetta davvero questa combinazione di opzioni poiché non funzionerà su una normale configurazione di Linux.

Il primo comando è dispari, perché si sta collegando staticamente contro l'RTS, ma dinamicamente contro tutte le altre dipendenze Haskell. Se si modifica il nome della libreria nell'opzione -l su -lHSrts-ghc7.8.4, le cose funzioneranno semplicemente su Linux e probabilmente su qualsiasi altra parte (diversa da Windows).