gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 01:58:13 43 4
gpt4 key购买 nike

我有一个非常特殊的依赖情况,我想打包在一个 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/

    43 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com