Reputation: 110
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
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 ofocamlopt
always builds a standalone executable. To build a mixed OCaml/C executable, execute theocamlopt
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.
The
ocamlopt
command has a command-line interface very close to that ofocamlc
. 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:
Compile sql_conn.c
into sql_conn.o
:
gcc -c sql_conn.c
Compile main.ml
into main.cmx
(among other things):
ocamlopt -c main.ml
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