在 Windows 上的 utop 中尝试`#require "ctypes.foreign";;` 时出错(Cygwin)

Error trying to `#require "ctypes.foreign";;` in utop on Windows (Cygwin)

我成功安装了 运行 OCaml + OPAM + utop在 Windows(在 Cygwin 中)感谢 awesome guide by Jonathan Protzenko。在utop中,我特别希望能够使用ctypes + ctypes.foreign 以快速试验和原型访问各种 WinAPI 调用。理论上,我设法成功安装了 ctypes 包(opam install ... 成功)。不幸的是,当我尝试在 utop 中实际加载它们 时,它们惨遭失败 并出现令人惊讶的错误消息:

utop # #require "ctypes.foreign";;
Error: Reference to undefined global `Ctypes_closure_properties'

尝试在 rlwrap ocaml 中执行相同的操作会给出更长的错误消息:

# #require "ctypes.foreign";;
C:\OCaml\lib\unix.cma: loaded
C:\OCaml\lib\bigarray.cma: loaded
C:\OCaml\lib\str.cma: loaded
C:\cygwin64\home\Mateusz\.opam\system\lib\bytes: added to search path
C:\cygwin64\home\Mateusz\.opam\system\lib\ctypes: added to search path
C:\cygwin64\home\Mateusz\.opam\system\lib\ctypes\ctypes.cma: loaded
C:\cygwin64\home\Mateusz\.opam\system\lib\ctypes\ctypes-foreign-base.cma: loaded
Cannot load required shared library dllctypes-foreign-base_stubs.
Reason: dllctypes-foreign-base_stubs.dll: Cannot resolve ffi_type_pointer.
C:\cygwin64\home\Mateusz\.opam\system\lib\ctypes\ctypes-foreign-unthreaded.cma: loaded
Characters -1--1:
  #require "ctypes.foreign";;

Error: Reference to undefined global `Ctypes_closure_properties'

注意:full transcript of the sessions is here — 它包含 cmd.exe 终端中 utop 会话的输出,以及 rlwrap ocaml 终端中的附加 rlwrap ocaml 会话的输出Cygwin 终端。

我完全不知道为什么会这样,或者我如何进一步尝试 debug/pinpoint/diagnose 这种情况,以便我可以尝试找到一些解决方法。 (顺便说一句——我是 OCaml 和 OPAM 的新手,虽然我在 C/C++ 和 Linux 方面相当有经验。)下面是我的一些问题,我imagine 可能有希望帮助解决问题,足以推动我克服障碍:

mingw64-x86_64-libffi 附带静态和动态库,但 gnu 链接器默认使用动态版本。

给 ctypes 的构建打补丁会很麻烦(ctypes 的构建系统相当复杂,是脚本和 makefile 的混合体)。所以只需尝试以下操作:删除动态库(/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libffi.dll.a/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libffi-6.dll),重建 ctypes,然后恢复删除的文件。

opam-repository-mingw

顺便说一下:https://fdopen.github.io/opam-repository-mingw/installation/ contains a patched version of flexdll (https://github.com/alainfrisch/flexdll/pull/3 已应用 - 问题并非特定于 libffi),它支持 libffi 的动态和静态版本。如果 /usr/x86_64-w64-mingw32/sys-root/mingw/bin 在您的 PATH 中,opam install ctypes-foreign ctypes utop 应该开箱即用。下面是完整的分步指南:

  1. 下载一个 64 位 graphical installer 和 运行 它。
  2. 安装完成后,启动"Cygwin64 Terminal"(gets installed into Cygwin group in the Start菜单).
  3. 键入 cygwin-install gui 启动 Cygwin Setup/Installer 并使用它安装一些文本编辑器 (joenanovim 或任何你喜欢的)。
  4. 使用您安装的文本编辑器编辑您的 ~/.bashrc 文件,添加以下行:

    export PATH="/usr/x86_64-w64-mingw32/sys-root/mingw/bin:$PATH"
    

    然后在您的 Cygwin 终端 中执行它(只需在终端中键入它,或者 运行 以下命令代替):

    $ source ~/.bashrc
    
  5. 安装 depext 它将自动下载 OPAM 包所需的任何 OS 级别(本机)依赖项,使用它来安装 ctypesutop:

    $ opam install depext depext-cygwinports
    $ opam depext -i ctypes-foreign ctypes utop
    
  6. Cygwin Terminal切换到cmd.exe, 例如,在 Cygwin 终端 中键入以下命令:

    $ cmd /c start cmd
    
  7. cmd.exe终端启动utop,并用它调用MessageBox WinAPI function 作为一切正常的测试:

    c:> utop
    
    utop # #require "ctypes.foreign";;
    utop # let dll = Dl.dlopen ~filename:"user32.dll" ~flags:[];;
    val dll : Dl.library = <abstr>
    utop # open Ctypes;;
    utop # let mb =
      Foreign.foreign ~from:dll "MessageBoxA"
      (ptr void @-> string @-> string @-> uint @-> returning int) ;;
    val mb : unit Ctypes_static.ptr -> bytes -> bytes -> Unsigned.uint -> int =
      <fun>
    utop # mb null "hello" "world" Unsigned.UInt.zero;;
    - : int = 1
    utop # #quit;;
    
    c:>
    

    [注意:并非所有上述 utop 命令都可能需要,但这对我有用。]