haskell - 如何使用 Stack/Cabal 构建早期部分的程序输出作为同一构建后续部分的源?

标签 haskell build cabal ffi haskell-stack

我有一个非常特殊的依赖情况,我想打包在一个 Stack/Cabal 包中:我需要构建并运行我的程序以获取代码生成器的输入,该代码生成器生成需要链接到的输出。 ..我的程序。

好的,更具体地说,以下是手动步骤:

  • stack build安装所有依赖项,并构建所有非 Verilator 使用的可执行文件。
  • stack exec phase1运行第一阶段,除其他外,生成一个 Verilog 文件和一个 Clash .manifest文件。
  • 我有一个自定义源生成器,它使用 .manifest步骤 2 中的文件,并生成 C++ 代码和 Makefile可用于驱动 Verilator。
  • 运行 Makefile在步骤 3 中生成:
  • 它在步骤 2 中的 Verilog 源代码上运行 Verilator,生成更多 C++ 源代码和新的 Makefile
  • 然后运行新生成的第二个 Makefile ,生成二进制库
  • stack build --flag phase2构建第二个可执行文件。该可执行文件包括 .hsc处理步骤 2 中生成的 header 的文件,并链接到步骤 4/2 中生成的 C++ 库。

  • 我想自动化这个,这样我就可以运行 stack build而这一切都将在幕后发生。我什至从哪里开始?!

    为了说明整个过程,这里有一个自包含的模型:
    package.yaml
    name: clashilator-model
    version: 0
    category: acme
    
    dependencies:
      - base
      - directory
    
    source-dirs:
      - src
    
    flags:
      phase2:
        manual: True
        default: False
    
    executables:
      phase1:
        main: phase1.hs
    
      phase2:
        main: phase2.hs
        when:
        - condition: flag(phase2)
          then:
            source-dirs:
              - src
              - _build/generated
            extra-libraries: stdc++ 
            extra-lib-dirs: _build/compiled
            ghc-options:
              -O3 -fPIC -pgml g++
              -optl-Wl,--allow-multiple-definition
              -optl-Wl,--whole-archive -optl-Wl,-Bstatic
              -optl-Wl,-L_build/compiled -optl-Wl,-lImpl
              -optl-Wl,-Bdynamic -optl-Wl,--no-whole-archive
    
            build-tools: hsc2hs
            include-dirs: _build/generated
          else:
            buildable: false    
    
    src/phase1.hs
    import System.Directory
    
    main :: IO ()
    main = do
        createDirectoryIfMissing True "_build/generated"
        writeFile "_build/generated/Interface.hsc" hsc
        writeFile "_build/generated/Impl.h" h
        writeFile "_build/generated/Impl.c" c
        writeFile "_build/Makefile" makeFile
    
    makeFile = unlines
        [ "compiled/libImpl.a: compiled/Impl.o"
        , "\trm -f $@"
        , "\tmkdir -p compiled"
        , "\tar rcsT $@ $^"
        , ""
        , "compiled/Impl.o: generated/Impl.c generated/Impl.h"
        , "\tmkdir -p compiled"
        , "\t$(COMPILE.c) $(OUTPUT_OPTION) $<"
        ]
    
    hsc = unlines
        [ "module Interface where"
        , "import Foreign.Storable"
        , "import Foreign.Ptr"
        , ""
        , "data FOO = FOO Int deriving Show"
        , ""
        , "#include \"Impl.h\""
        , ""
        , "foreign import ccall unsafe \"bar\" bar :: Ptr FOO -> IO ()"
        , "instance Storable FOO where"
        , "  alignment _ = #alignment FOO"
        , "  sizeOf _ = #size FOO"
        , "  peek ptr = FOO <$> (#peek FOO, fd1) ptr"
        , "  poke ptr (FOO x) = (#poke FOO, fd1) ptr x"
        ]
    
    h = unlines
       [ "#pragma once"
       , ""
       , "typedef struct{ int fd1; } FOO;"
       ]
    
    c = unlines
       [ "#include \"Impl.h\""
       , "#include <stdio.h>"
       , ""
       , "void bar(FOO* arg)"
       , "{ printf(\"bar: %d\\n\", arg->fd1); }"
       ]
    
    src/phase2.hs
    import Interface
    import Foreign.Marshal.Utils
    
    main :: IO ()
    main = with (FOO 42) bar
    

    脚本手动运行整个事情
    stack build
    stack run phase1
    make -C _build
    stack build --flag clashilator-model:phase2
    stack exec phase2
    

    最佳答案

    牦牛全裸 : 我设法用自定义 Setup.hs 解决了它.

  • buildHook ,我基本上什么都做phase1应该这样做(而不是将其留在 phase1 可执行文件中),将所有生成的文件放在 buildDir 下面的位置的LocalBuildInfo争论。这些生成的文件是 C++ 源文件和 .hsc文件。
  • 然后我运行 make在正确的目录中,产生一些 libFoo.a .
  • 还在 buildHook ,现在有趣的部分开始了:编辑 Executable s 在 PackageDescription .
    我添加 hsc文件的位置到 hsSourceDirs , 和模块本身到 otherModules .由于hsc2hs需要访问生成的 C++ 头文件,我还将正确的目录添加到 includeDirs .对于图书馆本身,我添加到 extraLibDirs并编辑 options静态链接到 libFoo.a ,通过将标志直接传递给链接器。
  • 所有这一切的结果是一组修改后的 Executable s,我把它放回 PackageDescription在将其传递给默认值 buildHook 之前.然后运行 ​​hsc2hsghc编译和链接phase2可执行文件。

  • 我放了一个 full example project on Github .看Setup.hsclashilator/src/Clash/Clashilator/Setup.hs看到这个在行动;特别是,这里是 Executable 的编辑s 在 PackageDescription :
    -- TODO: Should we also edit `Library` components?
    buildVerilator :: LocalBuildInfo -> BuildFlags -> [FilePath] -> String -> IO (Executable -> Executable)
    buildVerilator localInfo buildFlags srcDir mod = do
        let outDir = buildDir localInfo
        (verilogDir, manifest) <- clashToVerilog localInfo buildFlags srcDir mod
    
        let verilatorDir = "_verilator"
        Clashilator.generateFiles (".." </> verilogDir) (outDir </> verilatorDir) manifest
    
        -- TODO: bake in `pkg-config --cflags verilator`
        () <- cmd (Cwd (outDir </> verilatorDir)) "make"
    
        let incDir = outDir </> verilatorDir </> "src"
            libDir = outDir </> verilatorDir </> "obj"
            lib = "VerilatorFFI"
    
        let fixupOptions f (PerCompilerFlavor x y) = PerCompilerFlavor (f x) (f y)
    
            linkFlags =
                [ "-fPIC"
                , "-pgml", "g++"
                , "-optl-Wl,--whole-archive"
                , "-optl-Wl,-Bstatic"
                , "-optl-Wl,-l" <> lib
                , "-optl-Wl,-Bdynamic"
                , "-optl-Wl,--no-whole-archive"
                ]
    
            fixupExe = foldr (.) id $
                [ includeDirs %~ (incDir:)
                , extraLibDirs %~ (libDir:)
                , options %~ fixupOptions (linkFlags++)
    
                , hsSourceDirs %~ (incDir:)
                , otherModules %~ (fromString lib:)
                ]
    
        return fixupExe
    

    关于haskell - 如何使用 Stack/Cabal 构建早期部分的程序输出作为同一构建后续部分的源?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61572353/

    相关文章:

    haskell - cabal 安装 wx 缺少 C 库

    java - 如何在没有互联网访问权限的情况下使用 gradle?

    c# - 有没有办法告诉 Visual Studio 在发布的结果中包含子文件夹的配置文件?

    haskell - 是否可以为强制写一个别名?

    haskell - 使用 - 在 haskell 中加入两个 IO

    xcode - 如何使用适用于 OSX 的 Xcode 3.2.2 构建通用二进制文件 (ppc/i386)?

    haskell - 在沙箱中安装了 parsec,但在 ghci 中尝试加载文件时找不到库

    Haskell 模块导入自身

    haskell - Haskell 中的惰性加泰罗尼亚数字

    haskell - 在 Haskell 中简单输入 lambda 演算失败