Zachary Bechhoefer
Zachary Bechhoefer

Reputation: 110

ocamlopt: Error during linking

Well, I made this toy:

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;;

Makefile

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

And connect(...) works, but I get the error:

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

On make. Any pointers appreciated. It says the problem is with linking, but when the only method was connect(...) it worked fine. I'm not a seasoned C programmer (if you couldn't tell), so maybe I'm missing something obvious. Any pointers appreciated.

Thanks for your time!

Also, should a post like this also be tagged with mysql?

Upvotes: 2

Views: 1225

Answers (1)

melpomene
melpomene

Reputation: 85767

First off, you're not getting an error for connect because there's already a connect function in the standard library. I strongly suggest renaming your function because mysql_real_connect likely uses the standard connect internally and this would cause infinite recursion if you override it like this.


Quoting from the OCaml manual:

If you are using the native-code compiler ocamlopt, the -custom flag is not needed, as the final linking phase of ocamlopt always builds a standalone executable. To build a mixed OCaml/C executable, execute the ocamlopt command with:

  • the names of the desired OCaml native object files (.cmx and .cmxa files);
  • the names of the C object files and libraries (.o, .a, .so or .dll files) that implement the required primitives.

And also:

The ocamlopt command has a command-line interface very close to that of ocamlc. It accepts the same types of arguments, and processes them sequentially, after all options have been processed:

[...]

  • Arguments ending in .cmx are taken to be compiled object code. These files are linked together, along with the object files obtained by compiling .ml arguments (if any), and the OCaml standard library, to produce a native-code executable program. [...]

  • Arguments ending in .c are passed to the C compiler, which generates a .o/.obj object file. This object file is linked with the program.

And later:

-cclib -llibname
Pass the -llibname option to the linker . This causes the given C library to be linked with the program.


DISCLAIMER: I haven't actually tested the following steps. This is just what I think would make sense from reading the documentation.


From this I assume a correct compilation command would look like

ocamlopt sql_conn.c main.ml -cclib -lmysqlclient

Or in a Makefile:

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

(If you want to add a main target that also executes the file, I'd do something like

.PHONY: main
main: a.out
        ./a.out

)

If you want to separate the steps, it would look like this:

  1. Compile sql_conn.c into sql_conn.o:

     gcc -c sql_conn.c
    
  2. Compile main.ml into main.cmx (among other things):

     ocamlopt -c main.ml
    
  3. Link them together into an executable:

     ocamlopt sql_conn.o main.cmx -cclib -lmysqlclient
    

Or in Makefile form:

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

Problems with your commands:

g++ -c sql_conn.c -lmysqlclient

g++ selects the C++ compiler and prepares to link with the C++ standard libraries. -lmysqlclient adds the mysqlclient library to the set.

But -c tells it not to perform the linking step (so the C++ standard library and mysqlclient are ignored), and the filename (ending with .c) makes it switch to the C compiler.

The whole thing is equivalent to

gcc -c sql_conn.c

This produces sql_conn.o (an object file containing compiled C code).

ocamlopt main.ml

This tells ocamlopt to compile main.ml, link it with the standard OCaml libraries, and produce an executable called a.out. There's nothing that tells it to look in sql_conn.o, so that file is simply ignored.

It's here that you're getting a linker error because print_query wasn't found (it was defined in sql_conn.o). As mentioned above, you're not getting an error for connect because a symbol of that name (albeit with an incompatible signature, but the linker doesn't know that) already exists in the system library.

Upvotes: 3

Related Questions