2013-08-31 2 views
13

나는 C++ 바인딩을 생성하는 것을 목표로하는 Haskell 프로젝트를 가지고있다. 필자는 C 래퍼를 작성하고 정적으로 링크 된 독립 실행 형 라이브러리로 컴파일했습니다.C 라이브러리를 Haskell 라이브러리에 정적으로 연결하기

하스켈 바인딩을 C 래퍼에 정적으로 연결하여 별도로 C 래퍼를 배포 할 필요가 없도록 작성하고 싶습니다.하지만 제대로 작동하지 않는 것처럼 보이고 도움이 필요합니다.

추가 라이브러리로 C 라이브러리를 지정했지만 내 cabal build 단계가 컴파일 명령에 추가하지 않는 것 같습니다.

이것을 설명하기 위해 작은 프로젝트를 만들었습니다 (http://github.com/deech/CPlusPlusBindings).

그것은 작은 C++ 클래스 (https://github.com/deech/CPlusPlusBindings/tree/master/cpp-src)를 포함는 C 래퍼 (https://github.com/deech/CPlusPlusBindings/tree/master/c-src)는 작업 C 테스트 루틴 (https://github.com/deech/CPlusPlusBindings/tree/master/c-test)와 하스켈 (https://github.com/deech/CPlusPlusBindings/blob/master/src/BindingTest.chs를) 파일.

C 라이브러리는 Cabal 파일에없는 Setup.hs에 추가됩니다. 그게 바로 빌드 스텝 f 전에 Cabal을 통해 "make"를 사용하여 C 라이브러리를 빌드하는 실제 프로젝트를 가지고 있기 때문입니다. 빌드 단계에서 의 extraLibs 부분에 라이브러리 이름이 포함되고 extraLibDirs에는 올바른 디렉토리가 포함되어 있음을 확인했습니다.

cabal build의 출력은 다음과 같습니다

creating dist/setup 
./dist/setup/setup build --verbose=2 
creating dist/build 
creating dist/build/autogen 
Building CPlusPlusBinding-0.1.0.0... 
Preprocessing library CPlusPlusBinding-0.1.0.0... 
Building library... 
creating dist/build 
/usr/local/bin/ghc --make -fbuilding-cabal-package -O -odir dist/build -hidir dist/build -stubdir dist/build -i -idist/build -isrc -idist/build/autogen -Idist/build/autogen -Idist/build -I/home/deech/Old/Haskell/CPlusPlusBinding/c-src -I/home/deech/Old/Haskell/CPlusPlusBinding/cpp-includes -optP-include -optPdist/build/autogen/cabal_macros.h -package-name CPlusPlusBinding-0.1.0.0 -hide-all-packages -package-db dist/package.conf.inplace -package-id base-4.6.0.1-8aa5d403c45ea59dcd2c39f123e27d57 -XHaskell98 -XForeignFunctionInterface BindingTest 
Linking... 
/usr/bin/ar -r dist/build/libHSCPlusPlusBinding-0.1.0.0.a dist/build/BindingTest.o 
/usr/bin/ar: creating dist/build/libHSCPlusPlusBinding-0.1.0.0.a 
/usr/bin/ld -x --hash-size=31 --reduce-memory-overheads -r -o dist/build/HSCPlusPlusBinding-0.1.0.0.o dist/build/BindingTest.o 
In-place registering CPlusPlusBinding-0.1.0.0... 
/usr/local/bin/ghc-pkg update - --global --user --package-db=dist/package.conf.inplace 

불행하게도 컴파일이나 링크 단계도는 C 라이브러리를 사용합니다. 다른 경고 또는 오류는 없습니다.

답변

10

내가가 가지고이 문제를 해결하려면 만들어 내 Cabal 파일의 ghc-options 태그를 사용하는 C 바인딩 '오브젝트 파일과

  1. 다시 링크 하스켈 라이브러리와
  2. 있는지 그들은에 연결 올바른 순서.

모든 변경 사항은 테스트 프로젝트 (http://github.com/deech/CPlusPlusBindings)에 있습니다.

C 및 Haskell 개체를 모두 포함하는 새 보관 파일을 만드는 프로세스가 아래에 자세히 설명되어 있으며 간단하지 않습니다. 복잡성은 빌드 프로세스의 링커 부분에 연결하는 방법이 없기 때문에 발생합니다 (Cabal 1.16.0.2 현재).

Cabal 파일에 플래그를 설정하는 것은 쉽지 않으므로 여기서 설명하지 않습니다. 추가하여 하스켈 라이브러리 custom에 빌드 유형을 설정

  1. 을 다시 링크

    : 도당 파일

    build-type: custom 
    

    .와 Setup.hsmain 방법을 대체하여

  2. 삽입 사용자 정의 빌드 로직은 :

    main = defaultMainWithHooks simpleUserHooks { 
           buildHook = myBuildHook, 
           ... 
         } 
    

    이 대신 simpleUserHooks에 정의 된 기본 빌드 프로세스로가는이있는 myBuildHook 함수를 사용해야합니다 빌드 프로세스를 알려줍니다 아래에 정의되어 있습니다. 마찬가지로 정리 프로세스는 사용자 정의 함수 myCleanHook으로 대체됩니다.

  3. 빌드 후크를 정의하십시오. 이 빌드 후크는 명령 행에서 make을 실행하여 C++ 및 C 부분을 빌드 한 다음 Haskell 바인딩을 연결할 때 C 오브젝트 파일을 사용합니다.

    우리는 myBuildHook 시작 :

    myBuildHook pkg_descr local_bld_info user_hooks bld_flags = do 
    

    첫번째 인수없이 make을 실행하여 :

    rawSystemExit normal "make" [] 
    

    는 그 다음 PackageDescription 기록에 헤더 파일의 위치와 라이브러리 디렉토리 및 라이브러리 자체를 추가 새 패키지 설명으로 LocalBuildInfo을 업데이트하십시오.

    let new_pkg_descr = (addLib . addLibDirs . addIncludeDirs $ pkg_descr) 
        new_local_bld_info = local_bld_info {localPkgDescr = new_pkg_descr} 
    

    buildHook가 실행되기 전에 configureHookLocalBuildInfo 레코드의 compBuildOrder (구성 요소 빌드 순서) 키에 컴파일 순서를 저장했습니다. 우리는 라이브러리 구축과 빌드 프로세스의 실행 가능한 구축 부분을 분리하기 위해 라이브러리 구축을 격리해야합니다.

    빌드 순서

    그냥 목록이며, 우리는 그냥 일반 CLibName 타입 생성자의 경우 빌드 구성 요소가 그래서 우리는 목록에서 이러한 요소를 분리 만 그들과 함께 LocalBuildInfo 기록 갱신 라이브러리입니다 알고

    let (libs, nonlibs) = partition 
             (\c -> case c of 
               CLibName -> True 
               _ -> False) 
             (compBuildOrder new_local_bld_info) 
        lib_lbi = new_local_bld_info {compBuildOrder = libs} 
    
  4. 을 이 생성 된 아카이브를 구축 할 일단

    buildHook simpleUserHooks new_pkg_descr lib_lbi user_hooks bld_flags 
    
  5. 그러나 우리는 포함하도록 다시 작성해야합니다 :

  6. 이제 우리는 업데이트 된 레코드의 기본 빌드 훅을 실행 1 단계에서 make 명령에 의해 생성 된 C 객체는 그래서 우리는 몇 가지 설정과 C 오브젝트 파일 경로의 목록을 잡아 :

    let verbosity = fromFlag (buildVerbosity bld_flags) 
    info verbosity "Relinking archive ..." 
    let pref = buildDir local_bld_info 
        verbosity = fromFlag (buildVerbosity bld_flags) 
    cobjs <- getLibDirContents >>= return . map (\f -> combine clibdir f) 
                 . filter (\f -> takeExtension f == ".o") 
    

    을 그리고 빌드의 각 구성 요소에 작용 withComponentsLBI에 넘겨. 이 경우 라이브러리 부분 만 다루기 때문에 하나의 구성 요소 만 있습니다.

    withComponentsLBI pkg_descr local_bld_info $ \comp clbi -> 
        case comp of 
        (CLib lib) -> do 
           hobjs <- getHaskellObjects lib local_bld_info pref objExtension True 
           let staticObjectFiles = hobjs ++ cobjs 
           (arProg, _) <- requireProgram verbosity arProgram (withPrograms local_bld_info) 
           let pkgid = packageId pkg_descr 
            vanillaLibFilePath = pref </> mkLibName pkgid 
           Ar.createArLibArchive verbosity arProg vanillaLibFilePath staticObjectFiles 
        _ -> return() 
    
  7. 4 단계에서 실행 된 기본 buildHook 임시 패키지 데이터베이스 파일을 만든 : Cabal 그래서 우리는 링커를 다시 실행할 수있는 아카이브를 만들기위한 하스켈 오브젝트 파일의 목록과 createArLibArchive을 얻기 위해 getHaskellObjects을 제공합니다 기본 시스템 패키지 파일에 라이브러리를 설치하지 않고 실행 파일을 링크 할 수 있도록 빌드 된 라이브러리의 설명을 포함하는 "package.conf.inplace"라는 이름을 사용합니다.불행하게도 모든 buildHook 실행 공백 그것을 밖으로 그래서 우리는 임시 복사본에 유지해야합니다

    let distPref = fromFlag (buildDistPref bld_flags) 
         dbFile = distPref </> "package.conf.inplace" 
    (tempFilePath, tempFileHandle) <- openTempFile distPref "package.conf" 
    hClose tempFileHandle 
    copyFile dbFile tempFilePath 
    
  8. 이제 우리는 필터링 된 빌드 프로세스의 실행 부분과 함께 LocalBuildInfo 구조에 해당 복사본에 대한 경로를 저장 3 단계

    let exe_lbi = new_local_bld_info { 
            withPackageDB = withPackageDB 
                new_local_bld_info ++ 
                [SpecificPackageDB tempFilePath], 
            compBuildOrder = nonlibs 
           } 
    

    PackageDescriptionextraTmpFiles 부분에 다시 경로를 저장하기에 부족하므로이 기본 정리 후크 제거 할 수 있습니다.

    exe_pkg_descr = new_pkg_descr {extraTmpFiles = extraTmpFiles new_pkg_descr ++ [tempFilePath]} 
    
  9. 이제 우리는 마침내 바로 실행 구성 요소에 대한 (지금 새 아카이브에 대해 알고) 업데이트 된 레코드를 다시 기본 buildHook 실행

    buildHook simpleUserHooks exe_pkg_descr exe_lbi user_hooks bld_flags 
    
관련 문제