ocamlopt:链接时出错

时间:2018-04-30 02:13:03

标签: c ocaml

好吧,我做了这个玩具:

sql_conn.c:

// https://dev.mysql.com/doc/refman/8.0/en/mysql-real-connect.html
// http://zetcode.com/db/mysqlc/
#include <stdio.h>
#include <stdlib.h>
#include <caml/mlvalues.h>
#include <mysql/mysql.h>

CAMLprim value connect(value dbname, value dbuser, value dbpassword){
    MYSQL *con = mysql_init(NULL);
    mysql_real_connect(con,"localhost",String_val(dbuser), String_val(dbpassword),String_val(dbname),0,NULL,0);
    return (value) con;
}

CAMLprim value print_query(value con, value tbl, value field, value constraint){
    char query[100];
    sprintf(query, "select * from %s where %s='%s';",(char *) tbl,(char *) field,(char *) constraint);
    mysql_query((MYSQL*) con, query);

    MYSQL_RES *result = mysql_store_result((MYSQL*) con);
    int num_fields = mysql_num_fields(result);
    MYSQL_ROW row;
    while ((row = mysql_fetch_row(result))) 
    { 
        for(int i = 0; i < num_fields; i++) 
        { 
            printf("%s ", row[i] ? row[i] : "NULL");
        } 
            printf("\n"); 
    }
    mysql_free_result(result);
    return Val_unit;
}

main.ml:

type dbconn

external print_query: dbconn -> string -> string -> string -> unit = "print_query"

external connect: string -> string -> string -> dbconn = "connect"

let database = "dogs";;
let user = "root";;
let pwd = "kafka";;

let tbl = "dogs";;
let field = "Name";;
let arg = "reximus";;
let db = connect database user pwd;;
print_query db tbl field arg;;

生成文件

main:
    g++ -c sql_conn.c -lmysqlclient
    ocamlopt main.ml
    ./a.out

连接(...)有效,但我收到错误:

g++ -c sql_conn.c -lmysqlclient
ocamlopt main.ml
main.o: In function `camlMain__entry':
main.ml:(.text+0x89): undefined reference to `print_query'
main.o: In function `camlMain__6':
main.ml:(.data+0xa8): undefined reference to `print_query'
collect2: error: ld returned 1 exit status
File "caml_startup", line 1:
Error: Error during linking
Makefile:2: recipe for target 'main' failed
make: *** [main] Error 2

在制作上。任何指针赞赏。它说问题在于链接,但是当唯一的方法是连接(...)时,它工作正常。我不是一个经验丰富的C程序员(如果你不能告诉),所以也许我错过了一些明显的东西。任何指针都赞赏。

谢谢你的时间!

另外,像这样的帖子是否也应该用mysql标记?

1 个答案:

答案 0 :(得分:2)

首先,您没有收到connect的错误消息,因为已经有connect function in the standard library。我强烈建议重命名你的函数,因为mysql_real_connect可能在内部使用标准connect,如果你这样覆盖它会导致无限递归。

引自the OCaml manual

  

如果您使用的是本机代码编译器ocamlopt,则不需要-custom标志,因为ocamlopt的最终链接阶段始终构建独立的可执行文件。要构建混合的OCaml / C可执行文件,请执行ocamlopt命令:

     
      
  • 所需OCaml原生对象文件(.cmx.cmxa文件)的名称;
  •   
  • 实现所需基元的C对象文件和库(.o.a.so.dll文件)的名称。
  •   

And also

  

ocamlopt命令的命令行界面非常接近ocamlc的命令行界面。它接受相同类型的参数,并在处理完所有选项后按顺序处理它们:

     

[...]

     
      
  • .cmx结尾的参数被视为编译对象代码。这些文件与通过编译.ml参数(如果有)获得的目标文件以及OCaml标准库链接在一起,以生成本机代码可执行程序。 [...]

  •   
  • .c结尾的参数传递给C编译器,后者生成.o / .obj目标文件。该目标文件与程序链接。

  •   

后来:

  

-cclib -llibname
      将-llibname选项传递给链接器。这会导致给定的C库与程序链接。

免责声明:我还没有真正测试过以下步骤。这正是我认为通过阅读文档会有意义的。

由此我假设正确的编译命令看起来像

ocamlopt sql_conn.c main.ml -cclib -lmysqlclient

或者在Makefile中:

a.out: sql_conn.c main.ml
        ocamlopt sql_conn.c main.ml -cclib -lmysqlclient

(如果您想添加同时执行该文件的main目标,我会做

之类的事情
.PHONY: main
main: a.out
        ./a.out

如果您想分开这些步骤,它将如下所示:

  1. sql_conn.c编译为sql_conn.o

    gcc -c sql_conn.c
    
  2. main.ml编译为main.cmx(以及其他内容):

    ocamlopt -c main.ml
    
  3. 将它们链接到一个可执行文件中:

    ocamlopt sql_conn.o main.cmx -cclib -lmysqlclient
    
  4. 或者以Makefile形式:

    sql_conn.o: sql_conn.c
            gcc -c sql_conn.c
    
    main.cmx: main.ml
            ocamlopt -c main.ml
    
    a.out: sql_conn.o main.cmx
            ocamlopt sql_conn.o main.cmx -cclib -lmysqlclient
    

    您的命令出现问题:

    g++ -c sql_conn.c -lmysqlclient
    

    g++选择C ++编译器并准备与C ++标准库链接。 -lmysqlclientmysqlclient库添加到集合中。

    -c告诉它不要执行链接步骤(因此忽略C ++标准库和mysqlclient),文件名(以.c结尾)使其切换到C编译器。

    整件事情等同于

    gcc -c sql_conn.c
    

    这会生成sql_conn.o(包含已编译C代码的对象文件)。

    ocamlopt main.ml
    

    这告诉ocamlopt编译main.ml,将其与标准OCaml库链接,并生成一个名为a.out的可执行文件。没有什么可以告诉它查看sql_conn.o,因此该文件被忽略。

    您在此处收到链接器错误,因为找不到print_query(它在sql_conn.o中定义)。如上所述,您没有收到connect的错误,因为系统库中已存在该名称的符号(尽管签名不兼容,但链接器不知道)。