如何让第三方可执行文件访问Cabal构建的未安装共享库?
让第三方可执行文件找到Cabal构建的foreign-library共享库
foreign-library确实是适配你场景的方案,下面是几个无需提前执行cabal install、仅通过cabal run就能让第三方程序加载到共享库的实用思路:
1. 用自定义Setup脚本捕获库路径,传递给自研可执行文件
Cabal不会自动暴露foreign-library的输出路径,但可以通过自定义Setup.hs脚本在构建阶段捕获这个路径,再传递给你的自研可执行文件:
- 编写
Setup.hs,重写postBuild钩子,获取foreign-library的构建输出目录,把路径写入临时文件(比如./lib-path.txt):import Distribution.Simple import Distribution.Simple.LocalBuildInfo import System.FilePath import System.IO main = defaultMainWithHooks simpleUserHooks { postBuild = \_ _ _ lbi -> do -- 获取foreign-library的输出目录(适配不同系统和GHC版本) let libDir = libdir lbi </> compilerIdString (compiler lbi) libPath = libDir </> "libdasyuridia.so" -- 替换为你的库文件名 -- 把路径写入文件,供自研可执行文件读取 writeFile "./lib-path.txt" libPath } - 自研可执行文件启动时读取该文件,拿到共享库路径后传递给第三方程序。
2. 在cabal run时动态设置环境变量
通过Cabal的executable配置段结合自定义Setup脚本,给自研可执行文件注入包含共享库路径的环境变量:
- 在你的
.cabal文件中,给自研可执行文件添加环境变量占位符:executable my-haskell-exe main-is: Main.hs build-depends: base >=4.14 && <5, dasyuridia default-language: Haskell2010 environment: LD_LIBRARY_PATH: $SHARED_LIB_PATH - 在
Setup.hs中,重写confHook动态替换环境变量的值:import Distribution.Simple import Distribution.Simple.Configure import Distribution.Simple.LocalBuildInfo import Distribution.PackageDescription import System.FilePath main = defaultMainWithHooks simpleUserHooks { confHook = \args flags -> do lbi <- confHook simpleUserHooks args flags let pkgDesc = localPkgDescr lbi -- 获取共享库输出目录 libDir = libdir lbi </> compilerIdString (compiler lbi) -- 更新可执行文件的环境变量配置 newExes = map updateEnv (executables pkgDesc) newPkgDesc = pkgDesc { executables = newExes } updateEnv exe = exe { buildInfo = (buildInfo exe) { envBuildInfo = [ ("SHARED_LIB_PATH", libDir) ] } } return lbi { localPkgDescr = newPkgDesc } } - 自研可执行文件启动第三方程序时,将
LD_LIBRARY_PATH(Linux)或DYLD_LIBRARY_PATH(macOS)环境变量传递过去,或者直接把路径作为参数传给第三方程序。
3. 构建时自动复制共享库到项目固定目录
如果不想处理动态路径,可以在构建完成后把共享库复制到项目内的固定位置(比如./lib),自研可执行文件直接用相对路径访问:
- 在
Setup.hs的postBuild钩子中添加复制逻辑:import Distribution.Simple import Distribution.Simple.LocalBuildInfo import System.FilePath import System.Directory main = defaultMainWithHooks simpleUserHooks { postBuild = \_ _ _ lbi -> do let libDir = libdir lbi </> compilerIdString (compiler lbi) srcLib = libDir </> "libdasyuridia.so" destDir = "./lib" createDirectoryIfMissing True destDir copyFile srcLib (destDir </> takeFileName srcLib) } - 之后自研可执行文件可以直接指定
./lib作为库路径,设置第三方程序的环境变量时使用这个固定路径即可。
内容的提问来源于stack exchange,提问作者Daniel Wagner
相关产品推荐
相关产品推荐

