Write custom installation configurations for the hasekll Module
Write the custom installation configuration table of contents for the hasekll Module
- 1. Cause
- 1.1 brief introduction to the installation process of Cabal's Haskell Module
- 2. A simple example of using the FFI Module
- 2.1 suppose there is such a module, how can we make it build automatically? :
- 2.2 informal means (hackage testing is not possible, but simple)
- 2.3 formal means (cost is complicated setup settings)
- 3. Tips for compiling the ghci of the FFI Haskell module:
1
Cause
I have written several C ++ template functions for numerical ing. The data files to be processed may be large and the concurrent stream processing is better.
To make full use of Haskell
Lazy Io and concurrency inertia evaluation policy,
So the final decision was written as Haskell.
The module uses FFI
Call.
The problem afterwards is that if you want to upload your little stuff to hackage
,
Then the module must meet certain specifications and pass hackage
Test
.
This note is just a little result of a day after the Google and Cabal documents go viral.
This article mainly references Comrade Jeremy o'donoghue (wxhaskell1
Project member)
In stackoverflow2
And wordexpress3
Blog highlights.
1.1
Brief Introduction of Cabal Haskell module Installation Process
First, Cabal
Read the. Cabal file of the module (Format
). The most important purpose of the. Cabal file is to provide module dependency information,
For Haskell
Dependencies between modules, generally without human interference, Cabal
Recursively solves the dependencies between modules,
Automatically download from the Internet (This point is Perl
CPAN
Some fight, but cabal
From the customization of installation and Output Control,
Is much better than CPAN), but if C/C ++ or other external dynamic libraries and compilation tools are required, the default simple installation mode will not work.
-- Setup. hsimport distribution. simplemain = defaultmain in simple mode
2
A simple use of ffi
Sample module 2.1
Suppose there is such a module, how can we make it build automatically? :
# Mymodule. cabal file name: mymouleversion: 0.0.1stability: experimentallicense: LGPL-3License-file: licensecabal-version:> = 1.8 .................. # This is the installation policy used by cabal. If you need to customize it, you must write it as follows. # Otherwise, setup will not be parsed. the content in HS, and the default Simple mode build-type: Custom # is the type of the module. It is very important to determine the final module data structure to be analyzed executable myexec main-is: Main. HS other-modules: mymodule1, mymodule2, mymodule3, mymodule4, mymodule5 # This is another module required by this module. this cabal can automatically process build-depends: base >=4 & <5, bytestring, filepath, containers, parallel> 3 # Haskell extension to be used, as long as the target platform is not a very biased Haskell compiler, extensions: foreignfunctioninterface, flexiblecontexts, typesynonyminstances, patternguards, doandifthenelse # This is the part that needs to be processed. This part may require special compilation options (Ref: Extra) # For dynamic libraries other than the standard library, C-Sources: mycfuncs1.c #2 C/C ++ files that require special care, mycpptemplatefun2.cpp # include-dirs :. CC-options:-wall-fopenmp-O3 extra-libraries: stdc ++, gomp extra-Lib-dirs :. # ghc compilation option ghc-options:-wall-O3-rtsopts-threaded
2.2
Informal means (hackage testing is not possible, but simple)
Extra-Lib-dirs :. #2 C/C ++ files are compiled and placed in the current directory LD-options:-lstdc ++,-lgomp, mycfuncs1.o (Ref: notgood) # When ghc generates the final binary file, to be passed to, mycpptemplatefun2.o # linker Option
- In the same way, select the custom installation mode. In setup. Hs, you can simply change it as follows: notgoodhaskell
Import distribution. simpleimport distribution. simple. setupimport distribution. simple. utils (rawsystemexit) -- in the latest module, -- rawsysemit is already a -- deprecated interface -- you can replace it with The New Interface main: Io () main = defamainmainwithhooks simpleuserhooks {prebuild =/a B-> makelib A> prebuild simpleuserhooks a B} makelib: ARGs-> buildflags-> io () -- Ref: notgoodhaskell -- makelib _ flags = rawsystemexit (fromflag $ buildverbosity flags) "env" ["make", "-- directory =. /cCode "] -- cCode is the location of the C/C ++ code -- you can complete the configuration by calling make resolution-makefile.
Disadvantages
-
Runhaskell or runghc installation may be successful.
But calling cabal is a cabal that fails in any way.
The symptoms are missing databases.
- Runhaskell setup configure <br/> runhaskell setup build <br/> sudo runhaskell setup install
- Sudo cabal install (Ref: Cabal) <br/> ...... <br/> # output similar to the following <br/> error: <br/> * missing Foreign C libray: stdc ++, gomp <br/>
-
Principle
-
The Cabal prompt is misleading. The result of the failure is that it cannot enter the compilation stage. We set buildhooker. If it is not passed in the dependency test before, it will go down directly. This can be proved by the following means:
Cabal install-V3 # Set the most detailed output mode and report every step of Detection
-
Result
-
In this way, we can know that Cabal detects external libraries by first finding the temporary folder of the local system and then creating a mock C file under it, and try to use the provided external library and linker option to compile it. Once it fails, the report will not detect the external library.
-
Countermeasure
-
Knowing how to make a mistake, the following describes how to fool this detection method, and then let cabal go into the compilation stage. As long as we enter the compilation stage and execute our makefile, everything goes smoothly.
- After obtaining the module, You can manually run make and then enter cabal install (the Haskell module required for automatic installation ).
-
Disadvantages
-
You cannot automatically download and install files from the Internet, or even hackage won't allow you to upload files. You cannot test the files.
- When the module is provided directly, run make first, and then package the module, that is, the C code compilation is provided in advance. o file, which can be cheated by the detection. After all, the detection only compiles the mock file and overwrites it when executing make.
-
Disadvantages
-
It is not very "elegant", especially if it is to be released to different platforms and corresponding to different ghc compiling backend, it may cause errors and will be similar to the previous error prompt.
2.3
Formal means (cost is complicated setup settings)
At this time, we need to improve the custom installation mode so that Cabal can read the buildhooker4 defined in setup. HS of the module.
To
To determine the behavior of the runtime (such as reading the configuration of the Local Machine, running make, calling other compilers, etc ). First, add a custom hook to main.
-- Customize setup for installation. HS -- * load the required module import data. maybe import distribution. simple import distribution. simple. setup import distribution. simple. localbuildinfo import distribution. simple. program import distribution. simple. program. DB import distribution. packagedescription import distribution. verbosity import system. filepath -- * global variable to control the output report details debugverbose: verbositydebugverbose = silentcompilerverbose: verbositycompilerverbose = verbose -- * main function main: Io () of the Build Process () main = defamainmainwithhooks simpleuserhooks {buildhook = mybuildhook --, confhook = myconfhook -- hooker of dependency test} -- it can be customized if necessary
The following is the definition of the hook. Pay special attention to the type of the module. The information part of the Cabal file to be extracted varies depending on the type.
Mybuildhook: packagedescription-> localbuildinfo-> userhooks-> buildflags-> io () mybuildhook pkg_descr export user_hooks records = Let exes = executables pkg_descr EXE = head exes exe_bi = buildinfo $ EXE gcclibs = extralibs exe_bi -- default last link used library -- (if there is a better Compiler use extension Library) c_srcs = csources exe_bi -- C/C ++ source files [filepath] gcc_opts = ccoptions exe_bi -- [String] inc_dirs = encoded DED IRS exe_bi -- include dir [filepath] -- GCC series kit. If the host machine does not have an appropriate compiler, use the default GCC = gccprogram Gar = arprogram GPP = simpleprogram "G ++" -- intelc ++ suite, for example, I want to use concurrent new features such as cilk plus and cean added to Intel compiler V12. ICPC = simpleprogram "ICPC" -- Intel C ++ Compiler (Linux) IAR = simpleprogram "xiar" -- Intel ar-binding (Linux) PDB = addknownprograms [GCC, GPP, ICPC, IAR, Gar] emptyprogramdb -- external program to be set in
The following example shows how to check whether the host has a specific compiler and its version before compilation. For example, the intel compiler is provided only after V12.
Cilk plus and array notion. If the host has this feature, replace the default compiler and default library and use a stronger library for compilation. If not, call the default GCC,
The corresponding version of ghc compiler Windows provides GCC integration (mingw), so it only detects GCC and can be set similarly if you are familiar with Microsoft compiler.
Do putstrln "invoking C ++ compile phase" -- Compilation prompt -- the easy part here is that ICPC and G ++ have the same interface, you only need to input ICPC-dumpversion -- to print 12.0.2 to stdout. Therefore, the profiling function simply calls the ID. For other tools, you need to obtain the version and customize the profiling function. VG <-findprogramversion "-dumpversion" id debugverbose "G ++" VI <-findprogramversion "-dumpversion" id debugverbose "ICPC" Let vgnum = head $ versionbranch $ frommaybe (version [0, 0] [""]) VG gxx = If vgnum> 0 then GPP else GCC -- extracts 12 from 12.0.2. If no ICPC exists, this value is set to 0. vinum = head $ versionbranch $ frommaybe (version [0, 0] [""]) VI cxx = If vinum> = 12 then ICPC else gxx cc_opts = If vinum> = 12 then icc_opts else gcc_opts ar '= If vinum> = 12 then IAR else Gar libs = If vinum> = 12 then icpclibs else gcclibs -- call cabal, activate the pdb' for the registered program query <-configureallknownprograms debugverbose PDB
The second half of the hook function is as follows:
Let cc = fromjust (lookupprogram cxx PDB ') <br/> AR = fromjust (lookupprogram ar 'pdb ') <br/> -- this step is the compiled function. <br/> objs <-mapm (compilecxx CC cc_opts inc_dirs ". ") c_srcs <br/> -- after compilation is complete, all the compiled OBJ files are statically linked to a libmyfunc file in the current directory of the source file. A <br/> runprogram compilerverbose AR (aropts ++ objs) </P> <p> let exe_bi '= exe_bi {csources = [] -- remove the used source file <br/>, extralibs = mylib: libs} -- add the newly compiled static library to the final link library <br/> ex E '= EXE {buildinfo = exe_bi'} <br/> pkg_descr '= pkg_descr {executables = [EXE']} -- encapsulate other parameters in an unchanged manner <br/> -- the following line invokes the Standard build behaviour <br/> putstrln "INVOKE default build hook" <br/> -- throws the remaining parameters to the default hooker for execution. <Br/> BH <-buildhook simpleuserhooks pkg_descr 'local_bld_info user_hooks bld_flags <br/> return BH <br/>
The above is the definition of the hook program, followed by some specific activation functions and module-specific definitions. Including the parameter definitions for Calling C ++ and Ar programs.
Compilecxx :: configuredprogram -- ^ C/C ++ compiler-> [String] -- ^ compile options from Cabal-> [String] -- ^ include paths from Cabal-> filepath -- ^ base output directory-> filepath -- ^ path to source file-> Io filepathcompilecxx cxx opts incls out_path cxx_src = Let Your defiles = map ("-I" ++) incls cxx_src '= normalise cxx_src out_file = normalise $ out_path </> dropfilename cxx_src </> replaceextension (takefilename cxx_src )". O "out = ["-c ", cxx_src ',"-o ", out_file] opts' = opts in do runprogram compilerverbose cxx (includefiles ++ opts '++ out) return out_file -- AR parameter aropts: [String] aropts = ["-RC", "libseqtrans. A "] mylib: stringmylib =" seqtrans "-- Intel-specific compiler option icc_opts: [String] icc_opts = ["-wall ","-c ", "-O3", "-xhost", "-FP-model", "precise", "-parallel"] -- library icpclibs used by Intel Compiler :: [String] icpclibs = ["cilkrts", "mkl_rt", "irc", "iomp5"]
3
Compile FFI
Ghci of the Haskell Module
TIPS:
Ghc's strength lies in its FFI
, Ghc itself can link the OBJ file generated by C with the compiled HS File
(This is mainly because the back-end of the compiler is perfect.) as the interpreter of ghc, ghci calls the OBJ of Normal C if the Haskell Module
Static library files can be directly delivered to ghci together with HS files on the command line. However, if the compiled C code needs to call the external dynamic library file
This is not so easy. It is very important and common to debug the Haskell FFI code. In fact, ghci has a well-developed runtime Load
The C dynamic library is capable, but the documentation is not detailed. After my experiment, you can easily call it in the following way:
$LD_PRELOAD=/usr/lib/libstdc++.so.6 ghciGHCi, version 7.0.1: http://www.haskell.org/ghc/ for helpLoading package ghc-prim ... linking ... done.Loading package integer-gmp ... linking ... done.Loading package base ... linking ... done.Loading package ffi-1.0 ... linking ... done.Prelude>
To call multiple databases, you can:
$ Ld_preload =/path/to/dylib1:/path/to/dylib2 ghci cfunswf eddylib. O cfuncorrespondinghaskellmodule. Hs
PS Postscript:
If you want to write something on csdn, TMD is not a pain point. The HTML format highlighted by your own typographical code will be garbled in any way, the default highlight settings here only support several mainstream languages...
Footnotes:
1
Wxhaskell: A portable and native GUI Library
2
C compiler Selection in cabal package
3
Building a shared library in cabal
4
Distribution. Simple
Author: boyun Tang
Date: 2011-02-27 19:23:59 CST
HTML generated by org-mode 7.4 In Emacs 23