Saya memiliki situasi ketergantungan yang sangat aneh yang ingin saya kemas dalam satu paket Stack/Cabal: Saya perlu membangun dan menjalankan program saya untuk mendapatkan input ke generator kode yang menghasilkan output yang perlu ditautkan. .. program saya.

Oke jadi secara lebih konkrit, berikut langkah-langkahnya secara manual:

  1. stack build untuk menginstal semua dependensi, dan membangun semua executable yang tidak menggunakan Verilator.
  2. stack exec phase1 untuk menjalankan fase pertama yang menghasilkan, antara lain, file Verilog dan file Clash .manifest.
  3. Saya memiliki generator sumber khusus, yang menggunakan file .manifest dari langkah 2, dan menghasilkan kode C++ dan Makefile yang dapat digunakan untuk menggerakkan Verilator.
  4. Jalankan Makefile yang dihasilkan pada langkah 3:
    1. Ini menjalankan Verilator pada sumber Verilog dari langkah 2, yang menghasilkan lebih banyak kode sumber C++ dan Makefile
    2. baru
    3. Kemudian menjalankan Makefile kedua yang baru dibuat, yang menghasilkan perpustakaan biner
  5. stack build --flag phase2 membangun executable kedua. Eksekusi ini mencakup file .hsc yang memproses header yang dihasilkan pada langkah 2, dan menautkan ke pustaka C++ yang dihasilkan pada langkah 4/2.

Saya ingin mengotomatiskan ini sehingga saya dapat menjalankan stack build dan semua ini akan terjadi di belakang layar. Di mana saya bahkan mulai?!

Untuk mengilustrasikan keseluruhan proses, berikut adalah model mandiri:

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

Script untuk menjalankan semuanya secara manual

stack build
stack run phase1
make -C _build
stack build --flag clashilator-model:phase2
stack exec phase2
9
Cactus 3 Mei 2020, 12:36

1 menjawab

Jawaban Terbaik

Yak benar-benar kosong: Saya berhasil menyelesaikannya dengan Setup.hs khusus.

  1. Di buildHook, pada dasarnya saya melakukan apa pun yang seharusnya dilakukan phase1 (alih-alih membiarkannya dalam phase1 yang dapat dieksekusi), meletakkan semua file yang dihasilkan di tempat di bawah buildDir LocalBuildInfo argumen. File yang dihasilkan ini adalah file sumber C++ dan file .hsc.

  2. Saya kemudian menjalankan make di direktori yang benar, menghasilkan beberapa libFoo.a.

  3. Masih di buildHook, sekarang bagian yang menyenangkan dimulai: mengedit Executable di PackageDescription.

    Saya menambahkan lokasi file hsc ke hsSourceDirs, dan modul itu sendiri ke otherModules. Karena hsc2hs memerlukan akses ke header C++ yang dihasilkan, saya juga menambahkan direktori yang tepat ke includeDirs. Untuk perpustakaan itu sendiri, saya menambahkan ke extraLibDirs dan mengedit options untuk menautkan secara statis ke libFoo.a, dengan mengirimkan tanda langsung ke penaut.

  4. Hasil dari semua ini adalah kumpulan Executable yang dimodifikasi, yang saya masukkan kembali ke PackageDescription sebelum meneruskannya ke buildHook default. Yang itu kemudian menjalankan hsc2hs dan ghc untuk mengkompilasi dan menautkan phase2 yang dapat dieksekusi.

Saya telah menempatkan contoh proyek lengkap di Github. Lihat Setup.hs dan clashilator/src/Clash/Clashilator/Setup.hs untuk melihat ini beraksi; khususnya, berikut adalah pengeditan Executable di 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
0
Community 20 Juni 2020, 09:12