如何使用Stack / Cabal构建早期版本的程序输出作为同一构建后期版本的源代码? [英] How do I use the output of a program from an earlier part of a Stack/Cabal build as source in a later part of the same build?

查看:56
本文介绍了如何使用Stack / Cabal构建早期版本的程序输出作为同一构建后期版本的源代码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a very peculiar dependency situation that I would like to package up in a single Stack/Cabal package: I need to build and run my program to get the input to a code-generator which produces output that needs to be linked in to... my program.

好的,因此,更具体的说,这是手动操作的步骤:

OK so in more concrete terms, here are the steps manually:


  1. 堆栈构建以安装所有依赖项,并构建所有不使用竞争者的可执行文件。

  2. stack exec phase1 运行第一阶段,该阶段将生成一个Verilog文件和一个Clash .manifest 文件。

  3. 我有一个自定义源生成器,它使用了第2步中的 .manifest 文件,并生成C ++代码和 Makefile 可用于驱动Verilator。

  4. 运行在第3步中生成的 Makefile

  1. stack build to install all dependencies, and build all non-Verilator-using executables.
  2. stack exec phase1 to run the first phase which generates, among other things, a Verilog file and a Clash .manifest file.
  3. I have a custom source generator, which consumes the .manifest file from step 2, and produces C++ code and a Makefile that can be used to drive Verilator.
  4. Run the Makefile generated in step 3:

  1. 它在第2步的Verilog源代码上运行Verilator,这会产生更多的C ++源代码和新的 Makefile

  2. 然后运行新生成的第二个 Makefile 二进制库

  1. It runs Verilator on the Verilog sources from step 2, which produces more C++ source code and a new Makefile
  2. Then it runs the newly generated second Makefile, which produces a binary library


  • stack build --flag phase2 生成第二个可执行文件。该可执行文件包括 .hsc 文件,这些文件处理步骤2中生成的标头,并且链接到步骤4/2中生成的C ++库。

  • stack build --flag phase2 builds the second executable. This executable includes .hsc files that process headers produced in step 2, and it links to the C++ libraries produced in step 4/2.
  • 我想使它自动化,以便我可以运行 stack build ,所有这些操作都会在后台进行。我什至从哪里开始?!

    I would like to automate this so that I can just run stack build and all this would happen behind the scenes. Where do I even start?!

    为了说明整个过程,这里有一个自包含的模型:

    To illustrate the whole process, here is a self-contained model:

    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



    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



    src/phase2.hs

    import Interface
    import Foreign.Marshal.Utils
    
    main :: IO ()
    main = with (FOO 42) bar
    



    脚本手动运行整个程序



    Script to run the whole thing manually

    stack build
    stack run phase1
    make -C _build
    stack build --flag clashilator-model:phase2
    stack exec phase2
    


    推荐答案

    The牛是裸露的:我设法通过自定义 Setup.hs 来解决。


    The yak is fully bare: I managed to solve it with a custom Setup.hs.


    1. buildHook 中,我基本上会执行 phase1 所做的任何操作应该这样做(而不是将其保留在 phase1 可执行文件中),将所有生成的文件放在 buildDir 以下的位置 LocalBuildInfo 参数。这些生成的文件是C ++源文件和 .hsc 文件。

    1. In buildHook, I basically do whatever phase1 was supposed to do (instead of leaving it in a phase1 executable), putting all generated files in places below the buildDir of the LocalBuildInfo argument. These generated files are C++ source files and an .hsc file.

    然后在正确的目录中运行 make ,生成一些 libFoo.a

    I then run make in the right directory, producing some libFoo.a.

    仍然保留在 buildHook 中,现在有趣的部分开始了:编辑 Executable PackageDescription 中。

    Still in buildHook, now the fun part starts: editing the Executables in the PackageDescription.

    我将 hsc 文件的位置添加到 hsSourceDirs ,而模块本身为 otherModules 。由于 hsc2hs 需要访问生成的C ++标头,因此我还将正确的目录添加到 includeDirs 。对于库本身,我添加到 extraLibDirs 并编辑选项以静态链接到 libFoo。 a ,将标志直接传递到链接器。

    I add the hsc file's location to hsSourceDirs, and the module itself to otherModules. Since hsc2hs requires access to the generated C++ headers, I also add the right directory to includeDirs. For the library itself, I add to extraLibDirs and edit options to link statically to libFoo.a, by passing flags directly to the linker.

    所有这些的结果是对可执行文件,然后将其传递给 PackageDescription ,然后将其传递给默认的 buildHook 。然后运行 hsc2hs ghc 来编译和链接 phase2 可执行文件。

    The result of all this is a modified set of Executables, which I put back into the PackageDescription before passing it to the default buildHook. That one then runs hsc2hs and ghc to compile and link the phase2 executables.

    我已经将 Github上的完整示例项目。查看 Setup.hs clashilator / src / Clash / Clashilator / Setup.hs 来看看它的作用;特别是在 PackageDescription 中的 Executable 的编辑:

    I have put a full example project on Github. Look at Setup.hs and clashilator/src/Clash/Clashilator/Setup.hs to see this in action; in particular, here is the editing of the Executables in the 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
    

    这篇关于如何使用Stack / Cabal构建早期版本的程序输出作为同一构建后期版本的源代码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆