This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,34 @@
----------------------------------------------------------------------------------
* (foreign_library ...) is unavailable before Dune 2.0.
$ echo "(lang dune 1.0)" > dune-project
$ mkdir -p lib
$ cat >lib/dune <<EOF
> (foreign_library
> (language c)
> (names add))
> EOF
$ dune build
File "lib/dune", lines 1-3, characters 0-44:
1 | (foreign_library
2 | (language c)
3 | (names add))
Error: 'foreign_library' is only available since version 2.0 of the dune
language. Please update your dune-project file to have (lang dune 2.0).
[1]
----------------------------------------------------------------------------------
* (foreign_library ...) is available in Dune 2.0.
* "archive_name" is a required field.
$ echo "(lang dune 2.0)" > dune-project
$ dune build
File "lib/dune", lines 1-3, characters 0-44:
1 | (foreign_library
2 | (language c)
3 | (names add))
Error: Field "archive_name" is missing
[1]

View file

@ -0,0 +1,41 @@
Test the `byte_complete` executable mode
$ cat >dune-project <<EOF
> (lang dune 2.2)
> EOF
$ cat >dune <<EOF
> (library
> (name foo)
> (modules foo)
> (foreign_stubs
> (language c)
> (names stub)))
> (executable
> (name prog)
> (modules prog)
> (libraries foo)
> (modes byte byte_complete))
> EOF
$ cat >stub.c <<EOF
> #include <caml/mlvalues.h>
> #include <caml/alloc.h>
> CAMLprim value foo() {
> return caml_copy_string("OCaml, plus fort que le segfault!");
> }
> EOF
$ echo 'external foo : unit -> string = "foo"' > foo.ml
$ echo 'print_endline (Foo.foo ())' > prog.ml
$ dune build
The stubs are not present in the .bc:
$ grep -q 'OCaml, plus fort que le segfault!' _build/default/prog.bc
[1]
But they are in the .bc.exe:
$ grep -q 'OCaml, plus fort que le segfault!' _build/default/prog.bc.exe

View file

@ -0,0 +1,51 @@
This test won't work with MSVC as it's using GCC and clang CLI
parameters.
The flag should be present in the command, as we want it in the
:standard set.
We don't actually test that the compiler outputs colors, just that
Dune correctly passes the flag when required, and doesn't when it's
not.
test that we pass the flag
==========================
$ cat >dune <<EOF
> (library
> (name test)
> (foreign_stubs (language c) (names stub)))
> EOF
$ dune rules -m stub.o | grep -ce "-fdiagnostics-color=always"
1
test color flag disabled
========================
$ cat >dune <<EOF
> (library
> (name test)
> (foreign_stubs
> (flags :standard \ -fdiagnostics-color=always)
> (language c) (names stub)))
> EOF
$ dune rules -m stub.o | grep -ce "-fdiagnostics-color=always"
0
[1]
test that we correctly filter out the color codes
=================================================
$ cat >dune <<EOF
> (library
> (name test)
> (foreign_stubs
> (language c) (names stub)))
> EOF
We check there is no ESC [ sequence.
$ dune build 2>&1 | grep `printf '\033\\['`
[1]

View file

@ -0,0 +1,6 @@
(library
(name test)
(foreign_stubs (language c) (names foo))
(foreign_stubs (language c) (names bar) (flags)))
(vendored_dirs vendor)

View file

@ -0,0 +1,110 @@
Prior to dune 2.8 ocamlc_cflags and ocamlc_cppflags where
always prepended to the C compiler command line
In the following tests, foo.c is built with the :standard set of flags while
bar.c is built with an "empty" set of flags.
$ O_CC=$(ocamlc -config-var c_compiler)
$ O_CFLAGS=$(ocamlc -config-var ocamlc_cflags)
$ O_CPPFLAGS=$(ocamlc -config-var ocamlc_cppflags)
$ O_CC=$(echo $O_CC | sed -e 's/^[[:blank:]]*//')
$ O_CFLAGS=$(echo $O_CFLAGS | sed -e 's/^[[:blank:]]*//')
$ O_CPPFLAGS=$(echo $O_CPPFLAGS | sed -e 's/^[[:blank:]]*//')
use_standard_c_and_cxx_flags = default (false)
==================================
$ cat >dune-project <<EOF
> (lang dune 2.8)
> EOF
$ dune rules -m foo.o | tr -s '\t\n\\' ' ' > out_foo
File "dune", line 4, characters 36-39:
4 | (foreign_stubs (language c) (names bar) (flags)))
^^^
Warning: The flag set for these foreign sources overrides the `:standard` set
of flags. However the flags in this standard set are still added to the
compiler arguments by Dune. This might cause unexpected issues. You can
disable this warning by defining the option `(use_standard_c_and_cxx_flags
<bool>)` in your `dune-project` file. Setting this option to `true` will
effectively prevent Dune from silently adding c-flags to the compiler
arguments which is the new recommended behaviour.
$ dune rules -m bar.o | tr -s '\t\n\\' ' ' > out_bar
File "dune", line 4, characters 36-39:
4 | (foreign_stubs (language c) (names bar) (flags)))
^^^
Warning: The flag set for these foreign sources overrides the `:standard` set
of flags. However the flags in this standard set are still added to the
compiler arguments by Dune. This might cause unexpected issues. You can
disable this warning by defining the option `(use_standard_c_and_cxx_flags
<bool>)` in your `dune-project` file. Setting this option to `true` will
effectively prevent Dune from silently adding c-flags to the compiler
arguments which is the new recommended behaviour.
No warning in vendored subfolder
$ dune build vendor/barv.o
Ocamlc_cflags are duplicated if the :standard set is kept:
$ cat out_foo | grep -ce "${O_CFLAGS} ${O_CPPFLAGS} ${O_CFLAGS}"
1
Whether or not the :standard flags is overridden, both ocamlc_cflags and
ocamlc_cpp flags appear in the compiler command line:
$ cat out_foo | grep -ce "${O_CFLAGS} ${O_CPPFLAGS}"
1
$ cat out_bar | grep -ce "${O_CFLAGS} ${O_CPPFLAGS}"
1
use_standard_c_and_cxx_flags = true
=================================
$ cat >dune-project <<EOF
> (lang dune 2.8)
> (use_standard_c_and_cxx_flags true)
> EOF
$ dune rules -m foo.o | tr -s '\t\n\\' ' ' > out_foo
$ dune rules -m bar.o | tr -s '\t\n\\' ' ' > out_bar
Ocamlc_cflags are not duplicated anymore:
$ cat out_foo | grep -ce "${O_CFLAGS} ${O_CPPFLAGS} ${O_CFLAGS}"
0
[1]
When the :standard flags is overridden, ocamlc_cflags and
ocamlc_cpp are effectively removed from the compiler command line
$ cat out_foo | grep -ce "${O_CFLAGS} ${O_CPPFLAGS}"
1
$ cat out_bar | grep -ce "${O_CFLAGS}"
0
[1]
$ cat out_bar | grep -ce "${O_CPPFLAGS}"
0
[1]
use_standard_c_and_cxx_flags = true but dune < 2.8
================================================
$ cat >dune-project <<EOF
> (lang dune 2.7)
> (use_standard_c_and_cxx_flags true)
> EOF
$ dune rules
File "dune-project", line 2, characters 0-35:
2 | (use_standard_c_and_cxx_flags true)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'use_standard_c_and_cxx_flags' is only available since version 2.8 of
the dune language. Please update your dune-project file to have (lang dune
2.8).
[1]

View file

@ -0,0 +1,6 @@
(library
(name testv)
(foreign_stubs
(language c)
(names barv)
(flags)))

View file

@ -0,0 +1,3 @@
(library
(name q)
(c_names q_stub))

View file

@ -0,0 +1 @@
#define ANSWER 42

View file

@ -0,0 +1,2 @@
external q : unit -> int = "ocaml_question"

View file

@ -0,0 +1,2 @@
val q : unit -> int

View file

@ -0,0 +1,9 @@
#include "q.h"
#include <caml/mlvalues.h>
#include <caml/memory.h>
CAMLprim value ocaml_question (value unit)
{
CAMLparam1 (unit);
CAMLreturn (Val_int(ANSWER));
}

View file

@ -0,0 +1,3 @@
(executable
(name run)
(libraries q))

View file

@ -0,0 +1,5 @@
Test that an OCaml executables with C stubs works.
$ dune exec ./qnativerun/run.exe
42
# $ dune exec ./qbyterun/run.bc

View file

@ -0,0 +1,14 @@
#include <iostream>
#include <cstring>
#include <caml/misc.h>
#include <caml/mlvalues.h>
// strdup is not part of the C standard library but is part of POSIX.
// By default GCC and Clang both use the GNU variant of the C standard,
// so we are using strdup here to ensure users have access to it by default.
// If -std=c++11 is used instead of -std=gnu++11 this would fail to compile
// on non-POSIX platforms such as Cygwin.
extern "C" CAMLprim value cpp11(value _unit) {
std::cout << strdup("Hi from C++11") << std::endl;
return Val_unit;
}

View file

@ -0,0 +1,3 @@
external cpp11 : unit -> unit = "cpp11"
let () = cpp11 ()

View file

@ -0,0 +1,19 @@
-std=c++11 is given on OCaml 5.0
================================
$ cat >dune-project <<EOF
> (lang dune 3.14)
> EOF
$ cat >dune <<EOF
> ;; will fail if :standard doesn't have -std=c++11 on OCaml >= 5.0
> (executable
> (name cpp11)
> (foreign_stubs
> (language cxx)
> (names cpp11)
> (flags -std=c++98 :standard)))
> EOF
$ dune exec ./cpp11.exe
Hi from C++11

View file

@ -0,0 +1,7 @@
#include <stdio.h>
#include "foo.cxx"
void foo () {
int n = cppfoo();
printf("n = %d\n", n);
}

View file

@ -0,0 +1,4 @@
int cppfoo()
{
return 42;
}

View file

@ -0,0 +1 @@
external foo : unit -> unit = "foo"

View file

@ -0,0 +1,134 @@
$ cat >dune <<EOF
> (library
> (name foo)
> (cxx_names foo)
> (c_names bar)
> (modules foo))
>
> (executable
> (name bar)
> (libraries foo)
> (modules bar))
>
> (alias
> (name default)
> (action (run ./bar.exe)))
> EOF
$ cat >dune-project <<EOF
> (lang dune 1.8)
> EOF
* .cxx extension is allowed
$ dune build
n = 42
$ echo "(lang dune 1.11)" > dune-project
* Compilation succeeds when unused baz.cpp and baz.cxx exist
$ cat >baz.cpp <<EOF
> #include <stdio.h>
> extern "C" void foo () {
> printf("m = %d\n", 42);
> }
> EOF
$ cat >baz.cxx <<EOF
> #include <stdio.h>
> extern "C" void foo () {
> printf("k = %d\n", 42);
> }
> EOF
$ dune clean
$ dune build
n = 42
* Compilation fails when baz.cpp and baz.cxx conflict
* Note that this behaviour differs from earlier Dune version
that simply pick one of the sources and compile it to baz.o
$ cat >dune <<EOF
> (library
> (name foo)
> (cxx_names baz)
> (modules foo))
> (executable
> (name bar)
> (libraries foo)
> (modules bar))
> (alias
> (name default)
> (action (run ./bar.exe)))
> EOF
$ dune clean
$ dune build
File "dune", line 3, characters 11-14:
3 | (cxx_names baz)
^^^
Error: Multiple sources map to the same object name "baz":
- baz.cpp
- baz.cxx
This is not allowed; please rename them or remove "baz" from object names.
Hint: You can also avoid the name clash by placing the objects into different
foreign archives and building them in different directories. Foreign archives
can be defined using the (foreign_library ...) stanza.
[1]
* Compilation succeeds when using :standard in pre-2.0 Dune language.
This works because the translation layer from pre-2.0 to 2.0 replaces
:standard with the empty set.
$ cat >dune <<EOF
> (library
> (name foo)
> (cxx_names :standard foo)
> (c_names bar)
> (modules foo))
> (executable
> (name bar)
> (libraries foo)
> (modules bar))
> (alias
> (name default)
> (action (run ./bar.exe)))
> EOF
$ dune clean
$ dune build
n = 42
* Compilation fails when using :standard in Dune 2.0
$ echo "(lang dune 2.0)" > dune-project
$ cat >dune <<EOF
> (library
> (name foo)
> (foreign_stubs (language cxx) (names :standard foo))
> (foreign_stubs (language c) (names bar))
> (modules foo))
> (executable
> (name bar)
> (libraries foo)
> (modules bar))
> (rule
> (alias default)
> (action (run ./bar.exe)))
> EOF
$ dune clean
$ dune build
File "dune", line 3, characters 0-52:
3 | (foreign_stubs (language cxx) (names :standard foo))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Multiple sources map to the same object name "baz":
- baz.cpp
- baz.cxx
This is not allowed; please rename them or remove "baz" from object names.
Hint: You can also avoid the name clash by placing the objects into different
foreign archives and building them in different directories. Foreign archives
can be defined using the (foreign_library ...) stanza.
[1]

View file

@ -0,0 +1,12 @@
#include <caml/mlvalues.h>
#include <caml/io.h>
#include <iostream>
extern "C" value baz(value unit) { return Val_int(2046); }
extern "C" void hello_world_baz ();
void hello_world_baz ()
{
std::cout << "Hello World Baz!\n";
}

View file

@ -0,0 +1,11 @@
#include <caml/mlvalues.h>
#include <iostream>
extern "C" value bazexe(value unit) { return Val_int(4096); }
extern "C" void hello_world_bazexe ();
void hello_world_bazexe ()
{
std::cout << "Hello World Bazexe!";
}

View file

@ -0,0 +1,33 @@
(library
(name quad)
(modules quad)
(foreign_stubs (language cxx) (names baz)))
(executable
(name main)
(libraries quad)
(foreign_stubs (language cxx) (names bazexe))
(modules main))
(env
(_
(ocamlopt_flags
:standard
(:include extra_flags.sexp))))
(rule
(enabled_if
(or
(<> %{system} macosx)
(<> %{architecture} arm64)))
(action
(write-file extra_flags.sexp "()")))
; with XCode 15+, the linker complains about duplicate -lc++ libraries
(rule
(enabled_if
(and
(= %{system} macosx)
(= %{architecture} arm64)))
(action
(write-file extra_flags.sexp "(-ccopt -Wl,-no_warn_duplicate_libraries)")))

View file

@ -0,0 +1,9 @@
external bazexe : unit -> int = "bazexe"
external hello_world_bazexe : unit -> unit = "hello_world_bazexe"
let () = Quad.hello (); hello_world_bazexe ()
let () = Printf.printf "%d\n%d\n"
(Quad.quad ())
(bazexe ())

View file

@ -0,0 +1,6 @@
external baz : unit -> int = "baz"
external hello_world_baz : unit -> unit = "hello_world_baz"
let quad x = baz x
let hello () = hello_world_baz ()

View file

@ -0,0 +1,145 @@
Default: use_standard_c_and_cxx_flags = false
=============================================
$ cat >dune-project <<EOF
> (lang dune 2.8)
> EOF
> The flags that Dune should use for compilation
$ GCC_CF="-x c++"
$ Clang_CF="-x c++"
$ Msvc_CF="/TP"
> And linking
$ GCC_LF_OPT=" -ldopt -lstdc++ -ldopt -shared-libgcc"
$ Clang_LF_OPT=" -ldopt -lc++"
$ Msvc_LF_OPT=""
$ GCC_LF_LIB=" -cclib -lstdc++ -cclib -shared-libgcc"
$ Clang_LF_LIB=" -cclib -lc++"
$ Msvc_LF_LIB=""
> Check that compiler detection is done
$ dune build .dune/cc_vendor/cc_vendor
$ cat _build/default/.dune/cc_vendor/cc_vendor |
> grep -ce "clang\|gcc\|msvc"
1
> No specific flags added for compilation...
$ dune rules baz.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF|$Msvc_CF"
0
[1]
$ dune rules bazexe.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF\|$Msvc_CF"
0
[1]
> ...nor linking
$ dune rules libquad_stubs.a | tr -s '\n' ' ' |
> grep -ce "quad_stubs baz.o)"
1
$ dune rules main.exe | tr -s '\n' ' ' |
> grep -ce "Main.cmx)"
1
With use_standard_c_and_cxx_flags = false
=========================================
$ cat >dune-project <<EOF
> (lang dune 2.8)
> (use_standard_c_and_cxx_flags false)
> EOF
> No specific flags added for compilation...
$ dune rules baz.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF|$Msvc_CF"
0
[1]
$ dune rules bazexe.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF\|$Msvc_CF"
0
[1]
> ...nor linking
$ dune rules libquad_stubs.a | tr -s '\n' ' ' |
> grep -ce "quad_stubs baz.o)"
1
$ dune rules main.exe | tr -s '\n' ' ' |
> grep -ce "Main.cmx)"
1
With use_standard_c_and_cxx_flags = true
========================================
$ cat >dune-project <<EOF
> (lang dune 2.8)
> (use_standard_c_and_cxx_flags true)
> EOF
> Check that compiler detection is done
$ dune build .dune/cc_vendor/cc_vendor
$ cat _build/default/.dune/cc_vendor/cc_vendor |
> grep -ce "clang\|gcc\|msvc"
1
> Specific flags added for compilation...
$ dune rules baz.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF\|$Msvc_CF"
1
$ dune rules bazexe.o | tr -s '\n' ' ' |
> grep -ce "$GCC_CF\|$Clang_CF\|$Msvc_CF"
1
> ..and link
$ dune rules libquad_stubs.a | tr -s '\n' ' ' |
> grep -ce "quad_stubs baz.o$GCC_LF_OPT)\|quad_stubs baz.o$Clang_LF_OPT)\|quad_stubs baz.o$Msvc_LF_OPT)"
1
$ dune rules main.exe | tr -s '\n' ' ' |
> grep -ce "Main.cmx$GCC_LF_LIB)\|Main.cmx$Clang_LF_LIB)\|Main.cmx$Msvc_LF_LIB)"
1
$ dune clean
$ dune exec ./main.exe
2046
4096
Hello World Baz!
Hello World Bazexe!
$ [ -f _build/default/.dune/cc_vendor/cc_vendor ]
(this also works with sandbox=symlink, #6415)
$ dune exec --sandbox symlink ./main.exe
2046
4096
Hello World Baz!
Hello World Bazexe!
cc_vendor is not computed if not required
=====================================
$ dune clean
$ dune exec ./sub/main_no_stubs.exe
OK
$ [ -f _build/default/.dune/cc_vendor/cc_vendor ]
[1]
one can extend link flags in env
================================
$ OTHER=" --other-flag --yet-some-other-flag)"
$ dune rules sub/main.exe --profile some-profile | tr -s '\n' ' ' |
> grep -ce "Main.cmx$GCC_LF_LIB$OTHER\|Main.cmx$Clang_LF_LIB$OTHER\|Main.cmx$Msvc_LF_LIB$OTHER"
1

View file

@ -0,0 +1,11 @@
#include <caml/mlvalues.h>
#include <iostream>
extern "C" value bazexe(value unit) { return Val_int(4096); }
extern "C" void hello_world_bazexe ();
void hello_world_bazexe ()
{
std::cout << "Hello World Bazexe!";
}

View file

@ -0,0 +1,12 @@
(executable
(name main)
(link_flags (:standard --yet-some-other-flag \ --remove-this-one-later))
(foreign_stubs (language cxx) (names bazexe))
(modules main))
(executable
(name main_no_stubs)
(modules main_no_stubs))
(env (some-profile (link_flags (:standard --other-flag --remove-this-one-later))))

View file

@ -0,0 +1 @@
let () = print_endline "OK"

View file

@ -0,0 +1,40 @@
We demonstrate a strange property of the foreign stubs.
Although we introduce a dependency on all header files found in all source
directories, we do not add include directories, so they aren't accessible.
$ cat >dune-project <<EOF
> (lang dune 3.8)
> EOF
$ cat >dune <<EOF
> (include_subdirs unqualified)
> (executable
> (name foo)
> (foreign_stubs
> (language c)
> (names bar)))
> EOF
$ touch foo.ml
$ cat >bar.c <<EOF
> void foo { }
> EOF
$ mkdir subdir
$ cat >subdir/dune <<EOF
> (rule (with-stdout-to foo.h (system "exit 1")))
> EOF
$ dune build foo.exe
File "subdir/dune", line 1, characters 0-47:
1 | (rule (with-stdout-to foo.h (system "exit 1")))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Command exited with code 1.
[1]
The rules include the dependency on foo.h, but the include directory has to be
added manually.
$ dune rules _build/default/bar.o | grep subdir
(File (In_build_dir _build/default/subdir/foo.h))))

View file

@ -0,0 +1,14 @@
(cram
(applies_to github3766)
(enabled_if
(>= %{ocaml_version} 4.12.0))
(deps ./github3766.t/test.exe))
(subdir
github3766.t
(executable
(libraries stdune spawn unix)
(name test)))
(cram
(deps sandboxed.sh))

View file

@ -0,0 +1,9 @@
(library
(name foo)
(modules ())
(c_names foo))
(library
(name bar)
(modules ())
(cxx_names foo))

View file

@ -0,0 +1,11 @@
This test showcases that although libraries can technically have non overlapping
stubs names, things are still broken if their .o files overlap:
$ dune build @all 2>&1 | dune_cmd sanitize
File "dune", line 4, characters 10-13:
4 | (c_names foo))
^^^
Error: Multiple definitions for the same object file "foo". See another
definition at dune:9.
Hint: You can avoid the name clash by renaming one of the objects, or by
placing it into a different directory.

View file

@ -0,0 +1,40 @@
This test showcases that although libraries can technically have non overlapping
stubs names, things are still broken if their .o files overlap:
$ cat >dune <<EOF
> (library
> (name foo)
> (c_names foo sub/foo))
> EOF
Another form of this bug is if the same source is present in different
directories. In this case, the rules are fine, but this is probably not what the
user intended.
$ dune build @all
File "dune", line 3, characters 14-21:
3 | (c_names foo sub/foo))
^^^^^^^
Error: Relative part of stub is not necessary and should be removed. To
include sources in subdirectories, use the (include_subdirs ...) stanza.
[1]
$ cat >dune <<EOF
> (include_subdirs unqualified)
> (library
> (name foo)
> (c_names foo))
> EOF
$ dune build @all
File "dune", line 4, characters 10-13:
4 | (c_names foo))
^^^
Error: Multiple sources map to the same object name "foo":
- foo.c
- sub/foo.c
This is not allowed; please rename them or remove "foo" from object names.
Hint: You can also avoid the name clash by placing the objects into different
foreign archives and building them in different directories. Foreign archives
can be defined using the (foreign_library ...) stanza.
[1]

View file

@ -0,0 +1,9 @@
(library
(name foo)
(modules ())
(c_names foo))
(library
(name bar)
(modules ())
(c_names foo))

View file

@ -0,0 +1,10 @@
c_names with overlapping names in different stanzas
$ dune build @all 2>&1 | dune_cmd sanitize
File "dune", line 4, characters 10-13:
4 | (c_names foo))
^^^
Error: Multiple definitions for the same object file "foo". See another
definition at dune:9.
Hint: You can avoid the name clash by renaming one of the objects, or by
placing it into a different directory.

View file

@ -0,0 +1,4 @@
(library
(name foo)
(c_names foo)
(cxx_names foo))

View file

@ -0,0 +1,14 @@
c_names and cxx_names with overlapping names in the same stanza
$ dune build @all
File "dune", line 4, characters 12-15:
4 | (cxx_names foo))
^^^
Error: Multiple sources map to the same object name "foo":
- foo.c
- foo.cpp
This is not allowed; please rename them or remove "foo" from object names.
Hint: You can also avoid the name clash by placing the objects into different
foreign archives and building them in different directories. Foreign archives
can be defined using the (foreign_library ...) stanza.
[1]

View file

@ -0,0 +1,4 @@
external foo : string -> unit = "foo"
let () =
foo "A"

View file

@ -0,0 +1,4 @@
external foo : string -> unit = "foo"
let () =
foo "B"

View file

@ -0,0 +1,4 @@
(executables
(names aa bb)
(foreign_stubs (language c) (names foo))
(modes exe))

View file

@ -0,0 +1,7 @@
#include <stdio.h>
#include <caml/mlvalues.h>
void foo(value s)
{
puts(String_val(s));
}

View file

@ -0,0 +1,25 @@
$ dune exec ./aa.exe
A
$ dune exec ./bb.exe
B
$ mkdir err
$ echo "(lang dune 2.1)" > err/dune-project
$ touch err/foo.ml err/stubs.c
$ cat > err/dune << EOF
> (executable
> (name foo)
> (modes exe byte)
> (foreign_stubs (language c) (names stubs)))
> EOF
$ dune build --root err @all
Entering directory 'err'
File "dune", lines 1-4, characters 0-86:
1 | (executable
2 | (name foo)
3 | (modes exe byte)
4 | (foreign_stubs (language c) (names stubs)))
Error: Pure bytecode executables cannot contain foreign stubs.
Hint: If you only need to build a native executable use "(modes exe)".
Leaving directory 'err'
[1]

View file

@ -0,0 +1,35 @@
----------------------------------------------------------------------------------
Build an executable which depends on foreign object files compiled with a rule.
$ echo "(lang dune 3.5)" > dune-project
$ cat >dune <<EOF
> (executable
> (name calc)
> (extra_objects add mul))
> (rule
> (targets add.o mul.o)
> (deps add.c mul.c)
> (action (run %{cc} -c -I %{ocaml_where} %{deps})))
> EOF
$ cat >calc.ml <<EOF
> external add : int -> int -> int = "add"
> external mul : int -> int -> int = "mul"
> let calc x y z = mul (add x y) z
> let () = print_int (calc 3 4 6)
> let () = print_string "\n"
> EOF
$ cat >add.c <<EOF
> #include <caml/mlvalues.h>
> value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); }
> EOF
$ cat >mul.c <<EOF
> #include <caml/mlvalues.h>
> value mul(value x, value y) { return Val_int(Int_val(x) * Int_val(y)); }
> EOF
$ dune exec ./calc.exe
42

View file

@ -0,0 +1,18 @@
----------------------------------------------------------------------------------
Test that duplicate foreign objects results in an error
$ echo "(lang dune 3.5)" > dune-project
$ cat >dune <<EOF
> (executable
> (name foo)
> (extra_objects foo foo))
> EOF
$ dune build
File "dune", line 3, characters 16-19:
3 | (extra_objects foo foo))
^^^
Error: Duplicate object name: foo. Already appears at:
- dune:3
[1]

View file

@ -0,0 +1,73 @@
----------------------------------------------------------------------------------
Build an executable which depends on foreign object files.
----------------------------------------------------------------------------------
* (extra_objects ...) is unavailable before Dune 3.5.
$ echo "(lang dune 3.4)" > dune-project
$ mkdir -p bin
$ cat >bin/dune <<EOF
> (executable
> (name calc)
> (extra_objects add mul))
> EOF
$ dune build
File "bin/dune", line 3, characters 1-24:
3 | (extra_objects add mul))
^^^^^^^^^^^^^^^^^^^^^^^
Error: 'extra_objects' is only available since version 3.5 of the dune
language. Please update your dune-project file to have (lang dune 3.5).
[1]
----------------------------------------------------------------------------------
* Error for missing object file
$ echo "(lang dune 3.5)" > dune-project
$ cat >bin/calc.ml <<EOF
> external add : int -> int -> int = "add"
> external mul : int -> int -> int = "mul"
> let calc x y z = mul (add x y) z
> let () = print_int (calc 3 4 6)
> let () = print_string "\n"
> EOF
$ dune build
File "bin/dune", line 2, characters 7-11:
2 | (name calc)
^^^^
Error: No rule found for bin/add.o
File "bin/dune", line 2, characters 7-11:
2 | (name calc)
^^^^
Error: No rule found for bin/mul.o
[1]
----------------------------------------------------------------------------------
* Successful build when all object files are available
$ cat >bin/add.c <<EOF
> #include <caml/mlvalues.h>
> value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); }
> EOF
$ cat >bin/mul.c <<EOF
> #include <caml/mlvalues.h>
> value mul(value x, value y) { return Val_int(Int_val(x) * Int_val(y)); }
> EOF
$ cat >>bin/dune <<EOF
> (rule
> (target mul.o)
> (deps mul.c)
> (action (run %{bin:ocamlc} mul.c)))
> (rule
> (target add.o)
> (deps add.c)
> (action (run %{bin:ocamlc} add.c)))
> EOF
$ dune exec ./bin/calc.exe
42

View file

@ -0,0 +1,40 @@
----------------------------------------------------------------------------------
Build an library which indirectly depends on foreign object files.
----------------------------------------------------------------------------------
* Building library with indirect dependency on object file
$ echo "(lang dune 3.5)" > dune-project
$ mkdir -p lib
$ cat >lib/dune <<EOF
> (library
> (name calc)
> (extra_objects add)
> (foreign_stubs (language c) (names dep)))
> EOF
$ cat >lib/calc.ml <<EOF
> external add : int -> int -> int = "add"
> EOF
$ cat >lib/dep.c <<EOF
> #include <caml/mlvalues.h>
> extern value add(value x, value y);
> value dummy() { return add(Val_int(1), Val_int(2)); }
> EOF
$ cat >lib/add.c <<EOF
> #include <caml/mlvalues.h>
> value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); }
> EOF
$ cat >>lib/dune <<EOF
> (rule
> (target add.o)
> (deps add.c)
> (action (run %{bin:ocamlc} add.c)))
> EOF
$ dune build

View file

@ -0,0 +1,95 @@
----------------------------------------------------------------------------------
Build a library which depends on foreign object files.
----------------------------------------------------------------------------------
* (extra_objects ...) is unavailable before Dune 3.5.
$ echo "(lang dune 3.4)" > dune-project
$ mkdir -p lib
$ cat >lib/dune <<EOF
> (library
> (name calc)
> (extra_objects add mul))
> EOF
$ dune build
File "lib/dune", line 3, characters 1-24:
3 | (extra_objects add mul))
^^^^^^^^^^^^^^^^^^^^^^^
Error: 'extra_objects' is only available since version 3.5 of the dune
language. Please update your dune-project file to have (lang dune 3.5).
[1]
----------------------------------------------------------------------------------
* Error for missing object file
$ echo "(lang dune 3.5)" > dune-project
$ cat >lib/calc.ml <<EOF
> external add : int -> int -> int = "add"
> external mul : int -> int -> int = "mul"
> let calc x y z = mul (add x y) z
> EOF
$ cat >lib/calc.mli <<EOF
> val calc : int -> int -> int -> int
> EOF
$ dune build
File "lib/dune", lines 1-3, characters 0-47:
1 | (library
2 | (name calc)
3 | (extra_objects add mul))
Error: No rule found for lib/add.o
File "lib/dune", lines 1-3, characters 0-47:
1 | (library
2 | (name calc)
3 | (extra_objects add mul))
Error: No rule found for lib/mul.o
[1]
----------------------------------------------------------------------------------
* Successful build when all object files are available
$ cat >lib/add.c <<EOF
> #include <caml/mlvalues.h>
> value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); }
> EOF
$ cat >lib/mul.c <<EOF
> #include <caml/mlvalues.h>
> value mul(value x, value y) { return Val_int(Int_val(x) * Int_val(y)); }
> EOF
$ cat >>lib/dune <<EOF
> (rule
> (target mul.o)
> (deps mul.c)
> (action (run %{bin:ocamlc} mul.c)))
> (rule
> (target add.o)
> (deps add.c)
> (action (run %{bin:ocamlc} add.c)))
> EOF
$ dune build
----------------------------------------------------------------------------------
* Add an executable to test that we can link against the foreign object files
$ mkdir -p bin
$ cat >bin/dune <<EOF
> (executable
> (name main)
> (libraries calc))
> EOF
$ cat >bin/main.ml <<EOF
> let () = print_int (Calc.calc 3 4 6)
> let () = print_string "\n"
> EOF
$ dune exec ./bin/main.exe
42

View file

@ -0,0 +1,69 @@
Interaction of the foreign_library stanza and the enabled_if field.
$ cat > dune-project <<EOF
> (lang dune 3.18)
> EOF
$ cat > a.c <<EOF
> int foo() {
> return 1;
> }
> EOF
We should allow multiple foreign libraries to define the same archive if only
one of them is enabled:
$ cat > dune <<EOF
> (foreign_library
> (enabled_if true)
> (language c)
> (archive_name a)
> (names a))
>
> (foreign_library
> (enabled_if false)
> (language c)
> (archive_name a)
> (names a))
>
> (foreign_library
> (enabled_if false)
> (language c)
> (archive_name a)
> (names a))
> EOF
$ dune build
Repeat the test, but now two of the libraries are indeed enabled which is
illegal:
$ cat > dune <<EOF
> (foreign_library
> (enabled_if true)
> (language c)
> (archive_name a)
> (names a))
>
> (foreign_library
> (enabled_if true)
> (language c)
> (archive_name a)
> (names a))
>
> (foreign_library
> (enabled_if false)
> (language c)
> (archive_name a)
> (names a))
> EOF
$ dune build
File "dune", line 5, characters 8-9:
5 | (names a))
^
Error: Multiple definitions for the same object file "a". See another
definition at dune:11.
Hint: You can avoid the name clash by renaming one of the objects, or by
placing it into a different directory.
[1]

View file

@ -0,0 +1,106 @@
Testing extra_objects in foreign libraries
$ cat > dune-project <<EOF
> (lang dune 3.19)
> EOF
First we create a foreign library that uses an extra object file containing our message.
$ cat > dune <<EOF
> (foreign_library
> (archive_name foo)
> (names foo)
> (language c)
> (extra_objects message))
> EOF
$ cat > foo.c <<EOF
> #include <stdio.h>
> extern char *message;
> int f() { printf("%s\n", message); return 0; }
> EOF
Our message is compiled separately from the foreign library and the object file is
included in the (extra_objects) field.
$ cat > message.c <<EOF
> char *message = "Hello from C!";
> EOF
$ ocamlopt -c message.c -o message.o
We create a test executable that links against the foreign library to check everything is
working as intended.
$ cat > test.c <<EOF
> #include <stdio.h>
> extern int f();
> int main() { return f(); }
> EOF
$ dune build _build/default/libfoo.a
$ ocamlopt test.c _build/default/libfoo.a -o test.exe
$ chmod +x test.exe
Our test executable works as intended and prints the message from the object file.
$ ./test.exe
Hello from C!
$ rm test.exe
$ rm test.o
$ rm message.o
Testing that (:include) works
$ cat > dune <<EOF
> (foreign_library
> (archive_name foo)
> (names foo)
> (language c)
> (extra_objects
> (:include message.in)))
> EOF
$ cat > message.in <<EOF
> message
> EOF
$ cat > message.c <<EOF
> char *message = "Hello from C with an (:include)!";
> EOF
$ ocamlopt -c message.c -o message.o
$ dune build _build/default/libfoo.a
$ ocamlopt test.c _build/default/libfoo.a -o test.exe
$ chmod +x test.exe
$ ./test.exe
Hello from C with an (:include)!
Testing how (:include) interacts with a non-existant file.
$ rm message.in test.exe test.o
$ dune build _build/default/libfoo.a
File "dune", lines 1-6, characters 0-105:
1 | (foreign_library
2 | (archive_name foo)
3 | (names foo)
4 | (language c)
5 | (extra_objects
6 | (:include message.in)))
Error: No rule found for message.in
[1]
Testing how (:include) interacts when returning a non-existent object file.
$ cat > message.in <<EOF
> foobar
> EOF
$ dune build _build/default/libfoo.a
File "dune", lines 1-6, characters 0-105:
1 | (foreign_library
2 | (archive_name foo)
3 | (names foo)
4 | (language c)
5 | (extra_objects
6 | (:include message.in)))
Error: No rule found for foobar.o
[1]

View file

@ -0,0 +1,887 @@
----------------------------------------------------------------------------------
Testsuite for the (foreign_library ...) stanza.
$ export DUNE_SANDBOX=symlink
$ echo "(lang dune 3.0)" > dune-project
----------------------------------------------------------------------------------
* Multiple (foreign_library ...) declarations.
* Mixing C and C++ foreign library archives.
* Passing flags via (flags ...) field.
* Interaction with (foreign_archives ...) stanza.
$ mkdir lib
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ cat >lib/add.c <<EOF
> #include <caml/mlvalues.h>
> value add(value x, value y) { return Val_int(Int_val(x) + Int_val(y)); }
> EOF
$ cat >lib/mul.c <<EOF
> #include <caml/mlvalues.h>
> value mul(value x, value y) { return Val_int(Int_val(x) * Int_val(y)); }
> EOF
$ cat >lib/config.cpp <<EOF
> #include <caml/mlvalues.h>
> extern "C" value config(value unit) { return Val_int(CONFIG_VALUE); }
> EOF
$ cat >lib/calc.ml <<EOF
> external add : int -> int -> int = "add"
> external mul : int -> int -> int = "mul"
> external config : unit -> int = "config"
> let calc x y z = add (mul (add x y) z) (config ())
> EOF
$ cat >lib/calc.mli <<EOF
> val calc : int -> int -> int -> int
> EOF
$ cat >dune <<EOF
> (executable
> (name main)
> (modes exe byte)
> (libraries calc)
> (modules main))
> EOF
$ cat >main.ml <<EOF
> let () = Printf.printf "%d" (Calc.calc 1 2 3)
> EOF
$ rm -rf _build
$ dune build
$ dune exec ./main.exe
2009
$ (cd _build/default && ocamlrun -I lib main.bc)
2009
----------------------------------------------------------------------------------
* Include directories via the (include_dirs ...) field.
* Extra dependencies via the (extra_deps ...) field.
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ cat >lib/config.cpp <<EOF
> #include <caml/mlvalues.h>
> #include "ten.h"
> extern "C" value config(value unit) { return Val_int(CONFIG_VALUE + TEN); }
> EOF
$ mkdir -p lib/headers
$ cat >lib/headers/ten.h <<EOF
> #include "../eight.h"
> #include "some/deep/path/one.h"
> #define TEN (1 + EIGHT + ONE)
> EOF
$ mkdir -p lib/headers/some/deep/path
$ cat >lib/headers/some/deep/path/one.h <<EOF
> #define ONE 1
> EOF
$ cat >lib/eight.h <<EOF
> #define EIGHT 8
> EOF
$ rm -rf _build
$ dune build
$ dune exec ./main.exe
2019
$ (cd _build/default && ocamlrun -I lib main.bc)
2019
----------------------------------------------------------------------------------
* Error message when a given (include_dir ...) is not found.
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers another/dir)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ dune build
File "lib/dune", line 12, characters 23-34:
12 | (include_dirs headers another/dir)
^^^^^^^^^^^
Error: Include directory "another/dir" does not exist.
[1]
----------------------------------------------------------------------------------
* Error when specifying a non-existing external directory in (include_dirs... )
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers /some/path)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ dune build
File "lib/dune", line 12, characters 23-33:
12 | (include_dirs headers /some/path)
^^^^^^^^^^
Error: Unable to read the include directory.
Reason: stat(/some/path): No such file or directory.
[1]
----------------------------------------------------------------------------------
* Error when specifying an external file in (include_dirs... )
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers /usr/bin/env)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ dune build
File "lib/dune", line 12, characters 23-35:
12 | (include_dirs headers /usr/bin/env)
^^^^^^^^^^^^
Error: Unable to read the include directory.
Reason: "/usr/bin/env" is not a directory.
[1]
----------------------------------------------------------------------------------
* Error message for multiple declarations with the same "archive_name".
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add))
> (foreign_library
> (archive_name addmul)
> (language c)
> (names mul))
> (library
> (name calc)
> (modules calc)
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ dune build
File "lib/dune", line 6, characters 1-22:
6 | (archive_name addmul)
^^^^^^^^^^^^^^^^^^^^^
Error: Multiple foreign libraries with the same archive name "addmul"; the
name has already been taken in lib/dune:2.
[1]
----------------------------------------------------------------------------------
* Interaction of (foreign_stubs ...) and (foreign_archives ...).
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_stubs (language c) (names month))
> (foreign_archives addmul config))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ cat >lib/month.c <<EOF
> #include <caml/mlvalues.h>
> #include <caml/alloc.h>
> value month(value unit) { return caml_copy_string("October"); }
> EOF
$ cat >lib/calc.ml <<EOF
> external add : int -> int -> int = "add"
> external mul : int -> int -> int = "mul"
> external config : unit -> int = "config"
> external month : unit -> string = "month"
> let calc x y z = add (mul (add x y) z) (config ())
> EOF
$ cat >lib/calc.mli <<EOF
> val calc : int -> int -> int -> int
> val month : unit -> string
> EOF
$ cat >main.ml <<EOF
> let () = Printf.printf "%s %d" (Calc.month ()) (Calc.calc 1 2 3)
> EOF
$ rm -rf _build
$ dune build
$ dune exec ./main.exe
October 2019
$ (cd _build/default && ocamlrun -I lib main.bc)
October 2019
----------------------------------------------------------------------------------
* Error when using (foreign_archives ...) and a pure bytecode (executable ...).
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_stubs (language c) (names month))
> (foreign_archives addmul config))
> (foreign_library
> (archive_name day)
> (language c)
> (names day))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ cat >dune <<EOF
> (executable
> (name main)
> (modes exe byte)
> (libraries calc)
> (foreign_archives lib/day)
> (modules main))
> EOF
$ cat >lib/day.c <<EOF
> #include <caml/mlvalues.h>
> value day(value unit) { return Val_int(8); }
> EOF
$ cat >main.ml <<EOF
> external day : unit -> int = "day"
> let () = Printf.printf "%d %s %d" (day ()) (Calc.month ()) (Calc.calc 1 2 3)
> EOF
$ dune build
File "dune", lines 1-6, characters 0-105:
1 | (executable
2 | (name main)
3 | (modes exe byte)
4 | (libraries calc)
5 | (foreign_archives lib/day)
6 | (modules main))
Error: Pure bytecode executables cannot contain foreign archives.
Hint: If you only need to build a native executable use "(modes exe)".
[1]
----------------------------------------------------------------------------------
* Interaction of (foreign_archives ...) and (executables ...).
* Foreign archives in subdirectories.
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_stubs (language c) (names month))
> (foreign_archives addmul config))
> (foreign_library
> (archive_name day)
> (language c)
> (names day))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (flags :standard -DCONFIG_VALUE=2000)
> (names config))
> EOF
$ cat >dune <<EOF
> (executable
> (modes exe)
> (name main)
> (libraries calc)
> (foreign_archives lib/day)
> (modules main))
> EOF
$ cat >lib/day.c <<EOF
> #include <caml/mlvalues.h>
> value day(value unit) { return Val_int(8); }
> EOF
$ cat >main.ml <<EOF
> external day : unit -> int = "day"
> let () = Printf.printf "%d %s %d" (day ()) (Calc.month ()) (Calc.calc 1 2 3)
> EOF
$ rm -rf _build
$ dune build
$ dune exec ./main.exe
8 October 2019
----------------------------------------------------------------------------------
* Use (env ...) to pass C++ flags.
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_stubs (language c) (names month))
> (foreign_archives addmul config))
> (foreign_library
> (archive_name day)
> (language c)
> (names day))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (extra_deps eight.h)
> (names config))
> EOF
$ cat >dune <<EOF
> (env (_ (cxx_flags :standard -DCONFIG_VALUE=2000)))
> (executable
> (modes exe)
> (name main)
> (libraries calc)
> (foreign_archives lib/day)
> (modules main))
> EOF
$ dune exec ./main.exe
8 October 2019
----------------------------------------------------------------------------------
* Generated header.
$ cat >lib/dune <<EOF
> (foreign_library
> (archive_name addmul)
> (language c)
> (names add mul))
> (library
> (name calc)
> (modules calc)
> (foreign_stubs (language c) (names month))
> (foreign_archives addmul config))
> (foreign_library
> (archive_name day)
> (language c)
> (names day))
> (foreign_library
> (archive_name config)
> (language cxx)
> (include_dirs headers)
> (flags :standard -DCONFIG_VALUE=2000)
> (extra_deps eight.h)
> (names config))
> EOF
$ mkdir -p lib2/headers
$ cat >lib2/dune <<EOF
> (foreign_library
> (archive_name today)
> (language c)
> (include_dirs headers)
> (names today))
> EOF
$ cat >lib2/headers/dune <<EOF
> (rule
> (action (write-file today.h "#define TODAY \"Today\"")))
> EOF
$ cat >lib2/today.c <<EOF
> #include <caml/mlvalues.h>
> #include <caml/alloc.h>
> #include "today.h"
> value today(value unit) { return caml_copy_string(TODAY); }
> EOF
$ cat >dune <<EOF
> (executable
> (name main)
> (modes exe)
> (libraries calc)
> (foreign_archives lib/day lib2/today)
> (modules main))
> EOF
$ cat >main.ml <<EOF
> external day : unit -> int = "day"
> external today : unit -> string = "today"
> let () = Printf.printf "%s: %d %s %d" (today ()) (day ()) (Calc.month ()) (Calc.calc 1 2 3)
> EOF
$ rm -rf _build
$ dune exec ./main.exe
Today: 8 October 2019
----------------------------------------------------------------------------------
* Object files with the same name in different archives.
* Generated C source file.
$ mkdir -p lib3
$ cat >lib3/dune <<EOF
> (foreign_library
> (archive_name new_day)
> (language c)
> (names day))
> (rule
> (action (write-file day.c "#include <caml/mlvalues.h>\nvalue new_day(value unit) { return Val_int(14); }\n")))
> EOF
$ cat >dune <<EOF
> (executable
> (name main)
> (modes exe)
> (libraries calc)
> (foreign_archives lib/day lib2/today lib3/new_day)
> (modules main))
> EOF
$ cat >main.ml <<EOF
> external day : unit -> int = "day"
> external today : unit -> string = "today"
> external new_day : unit -> int = "new_day"
> let () = Printf.printf "%s: %02d %s %d\n" (today ()) ( day ()) (Calc.month ()) (Calc.calc 1 2 3);
> Printf.printf "%s: %02d %s %d\n" (today ()) (new_day ()) (Calc.month ()) (Calc.calc 1 2 3);
> EOF
$ rm -rf _build
$ dune exec ./main.exe
Today: 08 October 2019
Today: 14 October 2019
----------------------------------------------------------------------------------
* Library directories in (include_dir ...)
* Build fails due to the missing "header.h"
$ mkdir -p some/dir/answer
$ cat >some/dir/dune <<EOF
> (foreign_library
> (archive_name clib)
> (language c)
> (names src))
> (executable
> (modes exe)
> (name main)
> (foreign_archives clib)
> (modules main))
> EOF
$ cat >some/dir/answer/dune <<EOF
> (library
> (name answer))
> EOF
$ cat >some/dir/answer/header.h <<EOF
> #define ANSWER 42
> EOF
$ cat >some/dir/src.c <<EOF
> #include <caml/mlvalues.h>
> #include "header.h"
> value answer(value unit) { return Val_int(ANSWER); }
> EOF
$ cat >some/dir/main.ml <<EOF
> external answer : unit -> int = "answer"
> let () = Printf.printf "Answer = %d\n" (answer ());
> EOF
$ rm -rf _build
$ dune exec some/dir/main.exe 2> /dev/null
[1]
----------------------------------------------------------------------------------
* Library directories in (include_dir ...)
* Build succeeds
$ cat >some/dir/dune <<EOF
> (foreign_library
> (archive_name clib)
> (language c)
> (include_dirs (lib answer))
> (names src))
> (executable
> (modes exe)
> (name main)
> (foreign_archives clib)
> (modules main))
> EOF
$ rm -rf _build
$ dune exec some/dir/main.exe
Answer = 42
----------------------------------------------------------------------------------
* External library directories in (include_dir ...)
* Using an external directory in (include_dir ...)
$ mkdir -p external
$ cat >external/dune-project <<EOF
> (lang dune 2.1)
> (name external_library)
> EOF
$ cat >external/dune <<EOF
> (library
> (name extlib)
> (public_name external_library)
> (install_c_headers correction))
> EOF
$ cat >external/correction.h <<EOF
> #define CORRECTION (-21)
> EOF
$ rm -rf _build
$ touch external/external_library.opam
$ ( cd external && dune build @install \
> && dune install --prefix install --display=short 2>&1 | dune_cmd sanitize )
Installing install/lib/external_library/META
Installing install/lib/external_library/correction.h
Installing install/lib/external_library/dune-package
Installing install/lib/external_library/extlib$ext_lib
Installing install/lib/external_library/extlib.cma
Installing install/lib/external_library/extlib.cmi
Installing install/lib/external_library/extlib.cmt
Installing install/lib/external_library/extlib.cmx
Installing install/lib/external_library/extlib.cmxa
Installing install/lib/external_library/extlib.ml
Installing install/lib/external_library/opam
Installing install/lib/external_library/extlib.cmxs
$ echo "(lang dune 2.1)" > some/dir/dune-project
$ cat >some/dir/dune <<EOF
> (foreign_library
> (archive_name clib)
> (language c)
> (include_dirs (lib answer) (lib external_library))
> (names src))
> (executable
> (modes exe)
> (name main)
> (foreign_archives clib)
> (modules main))
> EOF
$ cat >some/dir/src.c <<EOF
> #include <caml/mlvalues.h>
> #include "header.h"
> #include "correction.h"
> value answer(value unit) { return Val_int((ANSWER + CORRECTION) * 2); }
> EOF
$ export OCAMLPATH=$PWD/external/install/lib; dune exec ./main.exe --root=some/dir
Entering directory 'some/dir'
Leaving directory 'some/dir'
Answer = 42
----------------------------------------------------------------------------------
* External library directories in (include_dir ...)
* Build fails when using an unknown library
$ cat >some/dir/dune <<EOF
> (foreign_library
> (archive_name clib)
> (language c)
> (include_dirs (lib answer) (lib unknown_lib))
> (names src))
> (executable
> (modes exe)
> (name main)
> (foreign_archives clib)
> (modules main))
> EOF
$ rm -rf _build
$ dune exec some/dir/main.exe
File "some/dir/dune", line 4, characters 33-44:
4 | (include_dirs (lib answer) (lib unknown_lib))
^^^^^^^^^^^
Error: Library "unknown_lib" not found.
-> required by _build/default/some/dir/src.o
-> required by _build/default/some/dir/libclib.a
-> required by _build/default/some/dir/main.exe
[1]
----------------------------------------------------------------------------------
* Error when using path separators in an archive name
$ mkdir -p github2914/dir
$ echo "(lang dune 2.1)" > github2914/dir/dune-project
$ cat >github2914/dir/dune <<EOF
> (foreign_library
> (archive_name some/path/id)
> (language c)
> (names src))
> EOF
$ dune build --root=github2914/dir
Entering directory 'github2914/dir'
File "dune", line 2, characters 15-27:
2 | (archive_name some/path/id)
^^^^^^^^^^^^
Error: Path separators are not allowed in archive names.
Leaving directory 'github2914/dir'
[1]
----------------------------------------------------------------------------------
* Using (foreign_archives dir/id) in a (library ...), see #2914
$ mkdir -p github2914/dir
$ cat >github2914/dir/dune <<EOF
> (foreign_library
> (archive_name id)
> (language c)
> (names src))
> EOF
$ cat >github2914/dir/src.c <<EOF
> #include <caml/mlvalues.h>
> value id(value unit) { return Val_int(2914); }
> EOF
$ cat >github2914/dune <<EOF
> (library
> (name bug)
> (foreign_archives dir/id)
> (modules bug))
> (executable
> (name main)
> (modes byte exe)
> (libraries bug)
> (modules main))
> EOF
$ cat >github2914/bug.ml <<EOF
> external id : unit -> int = "id"
> let fix = Printf.sprintf "Bug #%d has been fixed" (id ())
> EOF
$ cat >github2914/ticket.mli <<EOF
> val fix : string
> EOF
$ cat >github2914/main.ml <<EOF
> let () = Printf.printf "%s\n" Bug.fix;
> EOF
$ dune exec github2914/main.exe
Bug #2914 has been fixed
$ dune build @github2914/all
$ (cd _build/default/github2914 && ocamlrun -I dir main.bc)
Bug #2914 has been fixed
----------------------------------------------------------------------------------
* Make sure the [Byte_with_stubs_statically_linked_in] mode works too
$ cat >dune-workspace <<EOF
> (lang dune 2.0)
> (context
> (default (disable_dynamically_linked_foreign_archives true)))
> EOF
$ cat >github2914/dune <<EOF
> (library
> (name bug)
> (foreign_archives dir/id)
> (modules bug))
> (executable
> (name main)
> (modes byte)
> (libraries bug)
> (modules main))
> EOF
$ dune clean
$ dune exec github2914/main.exe
Bug #2914 has been fixed
--------------------------------------------------------------------------------
* Make sure that foreign stubs referencing code in foreign libs works too
$ mkdir -p stubs_in_libs
$ cat >stubs_in_libs/dune-project <<EOF
> (lang dune 2.5)
> EOF
$ cat >stubs_in_libs/lib.h <<EOF
> extern int foo(void);
> EOF
$ cat >stubs_in_libs/lib.c <<EOF
> int foo(void) { return 12; }
> EOF
$ cat >stubs_in_libs/stubs.c <<EOF
> #include <caml/mlvalues.h>
> #include "lib.h"
> value bar(value unit) { return Val_int(foo()); }
> EOF
$ cat >stubs_in_libs/main.ml <<EOF
> external bar: unit -> int = "bar" [@@noalloc]
> let () = print_endline (string_of_int (bar ()))
> EOF
$ cat >stubs_in_libs/dune <<EOF
> (foreign_library
> (archive_name lib)
> (language c)
> (names lib))
>
> (executable
> (name main)
> (foreign_archives lib)
> (foreign_stubs (language c) (names stubs)))
> EOF
$ dune build --root stubs_in_libs
Entering directory 'stubs_in_libs'
Leaving directory 'stubs_in_libs'
$ stubs_in_libs/_build/default/main.exe
12
--------------------------------------------------------------------------------
* Test (enabled_if)
$ mkdir -p enabled_if
$ cat >enabled_if/dune-project <<EOF
> (lang dune 3.13)
> EOF
$ cat >enabled_if/lib.c <<EOF
> int foo(void) { return 0; }
> EOF
$ cat >enabled_if/dune <<EOF
> (foreign_library
> (archive_name lib)
> (language c)
> (enabled_if (<> %{env:ENABLE=} "0"))
> (names lib))
> EOF
$ dune build --root enabled_if
Entering directory 'enabled_if'
File "dune", line 4, characters 1-37:
4 | (enabled_if (<> %{env:ENABLE=} "0"))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'enabled_if' is only available since version 3.14 of the dune
language. Please update your dune-project file to have (lang dune 3.14).
Leaving directory 'enabled_if'
[1]
$ cat >enabled_if/dune-project <<EOF
> (lang dune 3.14)
> EOF
$ cat >>enabled_if/dune <<EOF
> (library
> (name lib)
> (foreign_archives lib))
> EOF
$ ENABLE=1 dune build --root enabled_if
Entering directory 'enabled_if'
Leaving directory 'enabled_if'
$ ENABLE=0 dune build --root enabled_if
Entering directory 'enabled_if'
File "dune", lines 6-8, characters 0-45:
6 | (library
7 | (name lib)
8 | (foreign_archives lib))
Error: No rule found for liblib.a
Leaving directory 'enabled_if'
[1]

View file

@ -0,0 +1,27 @@
Error when specifying an (archive_name ...) in (foreign_stubs ...) stanza.
$ echo "(lang dune 2.0)" > dune-project
$ ./sandboxed.sh
$ cat >dune <<EOF
> (library
> (name foo)
> (foreign_stubs (archive_name baz) (language c) (names foo))
> (foreign_archives bar))
> (rule
> (targets bar%{ext_obj})
> (deps bar.c)
> (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libbar_stubs.a)
> (deps bar%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> EOF
$ dune build
File "dune", line 3, characters 16-34:
3 | (foreign_stubs (archive_name baz) (language c) (names foo))
^^^^^^^^^^^^^^^^^^
Error: The field "archive_name" is not allowed in the (foreign_stubs ...)
stanza. For named foreign archives use the (foreign_library ...) stanza.
[1]

View file

@ -0,0 +1,129 @@
----------------------------------------------------------------------------------
Testing the building of bytecode executables with foreign archives.
$ echo "(lang dune 2.0)" > dune-project
$ ./sandboxed.sh
----------------------------------------------------------------------------------
* Fails to build a pure bytecode executable with a foreign archive
$ cat >dune <<EOF
> (executable
> (modes byte)
> (name main)
> (modules main)
> (foreign_archives time))
> (foreign_library
> (archive_name time)
> (language c)
> (names time))
> EOF
$ cat >time.c <<EOF
> #include <caml/mlvalues.h>
> value current_time(value unit) { return Val_int(1345); }
> EOF
$ cat >main.ml <<EOF
> external current_time : unit -> int = "current_time"
> let () = Printf.printf "clock = %d" (current_time ())
> EOF
$ dune clean
$ dune exec ./main.bc
File "dune", lines 1-5, characters 0-80:
1 | (executable
2 | (modes byte)
3 | (name main)
4 | (modules main)
5 | (foreign_archives time))
Error: Pure bytecode executables cannot contain foreign archives.
Hint: If you only need to build a native executable use "(modes exe)".
[1]
----------------------------------------------------------------------------------
* Build a bytecode executable by statically linking in a foreign archive when the
setting [disable_dynamically_linked_foreign_archives] is [true] in the workspace
$ cat >dune-workspace <<EOF
> (lang dune 2.0)
> (context
> (default (disable_dynamically_linked_foreign_archives true)))
> EOF
$ dune clean
$ dune exec ./main.bc
clock = 1345
----------------------------------------------------------------------------------
* Make sure no rules are generated for foreign dynamically linked archives
$ dune build _build/default/dlltime.so
Error: Don't know how to build _build/default/dlltime.so
[1]
'
----------------------------------------------------------------------------------
* Fails to install a library with foreign stubs when a [dll*.so] rule is missing
$ cat >dune-project <<EOF
> (lang dune 1.11)
> (package
> (name foo))
> EOF
$ cat >dune-workspace <<EOF
> (lang dune 2.0)
> (context
> (default (disable_dynamically_linked_foreign_archives false)))
> EOF
$ cat >dune <<EOF
> (library
> (public_name foo.clock)
> (name clock)
> (modules clock)
> (self_build_stubs_archive (time)))
> (rule
> (targets time%{ext_obj})
> (deps time.c)
> (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libtime_stubs.a)
> (deps time%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> EOF
$ cat >clock.ml <<EOF
> external current_time : unit -> int = "current_time"
> let clock = current_time ()
> EOF
$ cat >clock.mli <<EOF
> val clock : int
> EOF
$ dune clean
$ dune build @install
File "dune", lines 1-5, characters 0-100:
1 | (library
2 | (public_name foo.clock)
3 | (name clock)
4 | (modules clock)
5 | (self_build_stubs_archive (time)))
Error: No rule found for dlltime_stubs.so
[1]
----------------------------------------------------------------------------------
* Succeeds to install a library with foreign stubs when a [dll*.so] rule is missing
but the setting [disable_dynamically_linked_foreign_archives] is [true] in the workspace
$ cat >dune-workspace <<EOF
> (lang dune 2.0)
> (context
> (default (disable_dynamically_linked_foreign_archives true)))
> EOF
$ dune clean
$ dune build @install

View file

@ -0,0 +1,89 @@
----------------------------------------------------------------------------------
Compatability testsuite for the (foreign_stubs ...) field.
$ ./sandboxed.sh
----------------------------------------------------------------------------------
* Error when using both (self_build_stubs_archive ...) and (c_names ...) before 2.0.
$ echo "(lang dune 1.0)" > dune-project
$ cat >dune <<EOF
> (library
> (name foo)
> (c_names foo)
> (self_build_stubs_archive (bar)))
> EOF
$ dune build
File "dune", line 4, characters 1-33:
4 | (self_build_stubs_archive (bar)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: A library cannot use (self_build_stubs_archive ...) and (c_names ...)
simultaneously. This is supported starting from Dune 2.0.
[1]
----------------------------------------------------------------------------------
* Error when using (c_names ...) in (library ...) in Dune 2.0.
$ echo "(lang dune 2.0)" > dune-project
$ dune build
File "dune", line 3, characters 1-14:
3 | (c_names foo)
^^^^^^^^^^^^^
Error: 'c_names' was deleted in version 2.0 of the dune language. Use the
(foreign_stubs ...) field instead.
[1]
----------------------------------------------------------------------------------
* Error when using (c_names ...) in (executable ...) in Dune 2.0.
$ cat >dune <<EOF
> (executable
> (name foo)
> (c_names bar))
> EOF
$ dune build
File "dune", line 3, characters 2-9:
3 | (c_names bar))
^^^^^^^
Error: Unknown field "c_names"
[1]
----------------------------------------------------------------------------------
* Error when using (self_build_stubs_archive ...) in (library ...) in Dune 2.0.
$ cat >dune <<EOF
> (library
> (name foo)
> (foreign_stubs (language c) (names foo))
> (self_build_stubs_archive (bar)))
> EOF
$ dune build
File "dune", line 4, characters 1-33:
4 | (self_build_stubs_archive (bar)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: 'self_build_stubs_archive' was deleted in version 2.0 of the dune
language. Use the (foreign_archives ...) field instead.
[1]
----------------------------------------------------------------------------------
* Error when using (self_build_stubs_archive ...) in (executable ...) in Dune 2.0.
$ cat >dune <<EOF
> (executable
> (name foo)
> (foreign_stubs (language c) (names bar))
> (self_build_stubs_archive (baz)))
> EOF
$ dune build
File "dune", line 4, characters 2-26:
4 | (self_build_stubs_archive (baz)))
^^^^^^^^^^^^^^^^^^^^^^^^
Error: Unknown field "self_build_stubs_archive"
[1]

View file

@ -0,0 +1,61 @@
----------------------------------------------------------------------------------
* Error when a C source file is missing.
$ ./sandboxed.sh
$ echo "(lang dune 2.0)" > dune-project
$ cat >dune <<EOF
> (library
> (name foo)
> (foreign_stubs (language c) (names foo))
> (foreign_archives bar))
> EOF
$ dune build
File "dune", line 3, characters 36-39:
3 | (foreign_stubs (language c) (names foo))
^^^
Error: Object "foo" has no source; "foo.c" must be present.
[1]
----------------------------------------------------------------------------------
* Error when a self-built archive is missing.
$ cat >foo.c <<EOF
> #include <caml/mlvalues.h>
> value foo(value unit) { return Val_int(9); }
> EOF
$ dune build 2>&1 | dune_cmd sanitize
File "dune", lines 1-4, characters 0-87:
1 | (library
2 | (name foo)
3 | (foreign_stubs (language c) (names foo))
4 | (foreign_archives bar))
Error: No rule found for libbar$ext_lib
----------------------------------------------------------------------------------
* Build succeeds when a self-built archive exists.
$ cat >bar.c <<EOF
> #include <caml/mlvalues.h>
> value bar(value unit) { return Val_int(10); }
> EOF
$ cat >dune <<EOF
> (library
> (name foo)
> (foreign_stubs (language c) (names foo))
> (foreign_archives bar))
> (rule
> (targets bar%{ext_obj})
> (deps bar.c)
> (action (run %{ocaml-config:c_compiler} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libbar.a)
> (deps bar%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> EOF
$ dune build

View file

@ -0,0 +1,265 @@
Testsuite for the (foreign_stubs ...) field.
$ echo "(lang dune 3.0)" > dune-project
$ ./sandboxed.sh
----------------------------------------------------------------------------------
* Foreign stubs in C and C++ language.
* Multiple foreign stub archives.
$ cat >baz.cpp <<EOF
> #include <caml/mlvalues.h>
> extern "C" value baz(value unit) { return Val_int(0); }
> EOF
$ cat >qux.cpp <<EOF
> #include <caml/mlvalues.h>
> extern "C" value qux(value unit) { return Val_int(2000); }
> EOF
$ cat >foo.c <<EOF
> #include <caml/mlvalues.h>
> value foo(value unit) { return Val_int(9); }
> EOF
$ cat >bar.c <<EOF
> #include <caml/mlvalues.h>
> value bar(value unit) { return Val_int(10); }
> EOF
$ cat >quad.ml <<EOF
> external foo : unit -> int = "foo"
> external bar : unit -> int = "bar"
> external baz : unit -> int = "baz"
> external qux : unit -> int = "qux"
> let quad x = foo x + bar x + baz x + qux x
> EOF
$ cat >quad.mli <<EOF
> val quad : unit -> int
> EOF
$ cat >main.ml <<EOF
> let () = Printf.printf "%d" (Quad.quad ())
> EOF
$ cat >dune <<EOF
> (library
> (name quad)
> (modules quad)
> (foreign_stubs (language c) (names foo))
> (foreign_archives bar qux)
> (foreign_stubs (language cxx) (names baz)))
> (rule
> (targets bar%{ext_obj})
> (deps bar.c)
> (action (run %{cc} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libbar.a)
> (deps bar%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllbar%{ext_dll})
> (deps bar%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (rule
> (targets qux%{ext_obj})
> (deps qux.cpp)
> (action (run %{cxx} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libqux.a)
> (deps qux%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllqux%{ext_dll})
> (deps qux%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (executable
> (name main)
> (modes exe byte)
> (libraries quad)
> (modules main))
> EOF
$ dune build
$ dune exec ./main.exe
2019
$ (cd _build/default && ocamlrun -I . ./main.bc)
2019
----------------------------------------------------------------------------------
* Unused foreign sources are ignored.
$ touch foo.cpp
$ touch foo.cxx
$ dune build
----------------------------------------------------------------------------------
* Fails when (extra_deps ...) is missing
$ cat >foo.c <<EOF
> #include <caml/mlvalues.h>
> #include "eight.h"
> value foo(value unit) { return Val_int(1 + EIGHT); }
> EOF
$ dune build 2> /dev/null
[1]
----------------------------------------------------------------------------------
* Succeeds when (extra_deps ...) is present
$ cat >eight.h <<EOF
> #define EIGHT 8
> EOF
$ cat >dune <<EOF
> (library
> (name quad)
> (modules quad)
> (foreign_stubs (language c) (names foo) (extra_deps eight.h))
> (foreign_archives bar qux)
> (foreign_stubs (language cxx) (names baz)))
> (rule
> (targets bar%{ext_obj})
> (deps bar.c)
> (action (run %{cc} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libbar.a)
> (deps bar%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllbar%{ext_dll})
> (deps bar%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (rule
> (targets qux%{ext_obj})
> (deps qux.cpp)
> (action (run %{cxx} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libqux.a)
> (deps qux%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllqux%{ext_dll})
> (deps qux%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (executable
> (name main)
> (modes exe byte)
> (libraries quad)
> (modules main))
> EOF
$ dune exec ./main.exe
2019
$ (cd _build/default && ocamlrun -I . ./main.bc)
2019
----------------------------------------------------------------------------------
* Fails when (include_dirs ...) is missing
$ cat >foo.c <<EOF
> #include <caml/mlvalues.h>
> #include "eight.h"
> #include "another/dir/one.h"
> value foo(value unit) { return Val_int(ONE + EIGHT); }
> EOF
$ dune build 2> /dev/null
[1]
----------------------------------------------------------------------------------
* Succeeds when (include_dirs ...) is present
$ mkdir -p another/dir
$ cat >another/dir/one.h <<EOF
> #define ONE 1
> EOF
$ cat >dune <<EOF
> (library
> (name quad)
> (modules quad)
> (foreign_stubs (language c) (names foo) (extra_deps eight.h) (include_dirs another/dir))
> (foreign_archives bar qux)
> (foreign_stubs (language cxx) (names baz)))
> (rule
> (targets bar%{ext_obj})
> (deps bar.c)
> (action (run %{cc} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libbar.a)
> (deps bar%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllbar%{ext_dll})
> (deps bar%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (rule
> (targets qux%{ext_obj})
> (deps qux.cpp)
> (action (run %{cxx} -c -I %{ocaml-config:standard_library} -o %{targets} %{deps})))
> (rule
> (targets libqux.a)
> (deps qux%{ext_obj})
> (action (run ar rcs %{targets} %{deps})))
> (rule
> (targets dllqux%{ext_dll})
> (deps qux%{ext_obj})
> (action (run %{cc} -shared -o %{targets} %{deps})))
> (executable
> (name main)
> (modes exe byte)
> (libraries quad)
> (modules main))
> EOF
$ dune exec ./main.exe
2019
$ (cd _build/default && ocamlrun -I . ./main.bc)
2019
----------------------------------------------------------------------------------
* Fails when using standard names in foreign stubs due to multiple C++ sources
$ cat >dune <<EOF
> (library
> (name quad)
> (modules quad)
> (foreign_stubs (language cxx)))
> EOF
$ dune build
File "dune", line 4, characters 1-31:
4 | (foreign_stubs (language cxx)))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Multiple sources map to the same object name "foo":
- foo.cpp
- foo.cxx
This is not allowed; please rename them or remove "foo" from object names.
Hint: You can also avoid the name clash by placing the objects into different
foreign archives and building them in different directories. Foreign archives
can be defined using the (foreign_library ...) stanza.
[1]
----------------------------------------------------------------------------------
* Succeeds when using a subset of standard names
$ cat >dune <<EOF
> (library
> (name quad)
> (modules quad)
> (foreign_stubs
> (language cxx)
> (names :standard \ foo)))
> EOF
$ dune clean
$ dune build

View file

@ -0,0 +1,4 @@
(executable
(name main)
(modes byte)
(libraries hello_world))

View file

@ -0,0 +1,5 @@
(library
(name hello_world)
(foreign_stubs
(language cxx)
(names foreign_hello_world)))

View file

@ -0,0 +1,8 @@
#include <iostream>
extern "C" void hello_world ();
void hello_world ()
{
std::cout << "Hello World!";
}

View file

@ -0,0 +1 @@
external hello_world : unit -> unit = "hello_world"

View file

@ -0,0 +1 @@
Hello_world.hello_world ();;

View file

@ -0,0 +1,7 @@
Building a cxx library should run ocamlmklib with the correct link flags for
gcc (-lstdc++) and clang (-lc++). In case of failure this test will print a
series of "undefined symbols" errors for various elements of the c++ std.
$ dune build
On windows this test should always pass because no specific flag is needed for
msvc to link with the standard library.

View file

@ -0,0 +1,10 @@
#include "caml/mlvalues.h"
value caml_b_or_n(value unit)
{
#ifdef NATIVE_CODE
return Val_int(1);
#else
return Val_int(0);
#endif
}

View file

@ -0,0 +1,6 @@
#include "caml/mlvalues.h"
value caml_b_and_n(value unit)
{
return Val_int(0);
}

View file

@ -0,0 +1,27 @@
(executable
(modes native byte_complete)
(modules stubs_exe)
(name stubs_exe)
(foreign_stubs
(language c)
(mode byte)
(names c_stubs))
(foreign_stubs
(language c)
(mode native)
(flags :standard -DNATIVE_CODE)
(names c_stubs)))
(executable
(modes native byte_complete)
(modules stubs_lib)
(name stubs_lib)
(libraries mode_dep_stubs))
(executable
(modes native byte_complete)
(modules stubs_same_exe)
(name stubs_same_exe)
(foreign_stubs
(language c)
(names c_stubs_same)))

View file

@ -0,0 +1,10 @@
#include "caml/mlvalues.h"
value caml_b_or_n(value unit)
{
#ifdef NATIVE_CODE
return Val_int(1);
#else
return Val_int(0);
#endif
}

View file

@ -0,0 +1,11 @@
(library
(name mode_dep_stubs)
(foreign_stubs
(language c)
(mode byte)
(names c_stubs_lib))
(foreign_stubs
(language c)
(mode native)
(flags :standard -DNATIVE_CODE)
(names c_stubs_lib)))

View file

@ -0,0 +1 @@
external stub_byte_or_native : unit -> int = "caml_b_or_n"

View file

@ -0,0 +1,104 @@
##############
Project Toggle
##############
Without the toggle, we get an error message for using the new mode subfield
$ cat >dune-project <<EOF
> (lang dune 3.5)
> EOF
$ dune build 2>&1 | head -n 6
File "dune", line 11, characters 3-16:
11 | (mode native)
^^^^^^^^^^^^^
Error: 'mode' is available only when mode_specific_stubs is enabled in the
dune-project file. You must enable it using (using mode_specific_stubs 0.1)
in your dune-project file.
But the toggle only exists in Dune 3.5
$ cat >dune-project <<EOF
> (lang dune 3.1)
> (using mode_specific_stubs 0.1)
> EOF
$ dune build
File "dune-project", line 2, characters 27-30:
2 | (using mode_specific_stubs 0.1)
^^^
Error: Version 0.1 of syntax extension for mode-specific foreign stubs is not
supported until version 3.5 of the dune language.
There are no supported versions of this extension in version 3.1 of the dune
language.
[1]
With Dune 3.5 no error is displayed
$ cat >dune-project <<EOF
> (lang dune 3.5)
> (using mode_specific_stubs 0.1)
> EOF
$ dune build
$ dune clean
###########
Executables
###########
Native executables should output 1, others 0 if they have mode-dependent stubs
$ dune exec ./stubs_exe.exe
Byte (0) or native (1) ? 1
$ dune exec ./stubs_exe.bc.exe
Byte (0) or native (1) ? 0
But stubs_same_exe which does not have mode dependent stubs and should
always output 0
$ dune exec ./stubs_same_exe.exe
Byte (0) and native (0) ? 0
$ dune exec ./stubs_same_exe.bc.exe
Byte (0) and native (0) ? 0
There should be two different object files for the mode-dependent stub `c_stub`
but only one for the non-mode-dependent stub `c_stub_same`
$ ls _build/default/*.o
_build/default/c_stubs_byte.o
_build/default/c_stubs_native.o
_build/default/c_stubs_same.o
$ dune clean
#########
Libraries
#########
$ dune exec ./stubs_lib.exe
Byte (0) or native (1) ? 1
$ dune exec ./stubs_lib.bc.exe
Byte (0) or native (1) ? 0
$ dune clean
Now we try will being in the sandbox
$ cat >sdune <<'EOF'
> #!/usr/bin/env bash
> DUNE_SANDBOX=symlink dune "$@"
> EOF
$ chmod +x sdune
$ ./sdune exec ./stubs_exe.exe
Byte (0) or native (1) ? 1
$ ./sdune exec ./stubs_exe.bc.exe
Byte (0) or native (1) ? 0
$ ./sdune exec ./stubs_lib.exe
Byte (0) or native (1) ? 1
$ ./sdune exec ./stubs_lib.bc.exe
Byte (0) or native (1) ? 0

View file

@ -0,0 +1,3 @@
external stub_byte_or_native : unit -> int = "caml_b_or_n"
let () = Printf.printf "Byte (0) or native (1) ? %i\n" (stub_byte_or_native ())

View file

@ -0,0 +1 @@
let () = Printf.printf "Byte (0) or native (1) ? %i\n" (Mode_dep_stubs.stub_byte_or_native ())

View file

@ -0,0 +1,3 @@
external stub_byte_and_native : unit -> int = "caml_b_and_n"
let () = Printf.printf "Byte (0) and native (0) ? %i\n" (stub_byte_and_native ())

View file

@ -0,0 +1,4 @@
(executable
(name stubs_exe)
(modes native byte_complete)
(libraries stubs_lib stubs_liboth))

View file

@ -0,0 +1,2 @@
(lang dune 3.5)
(using mode_specific_stubs 0.1)

View file

@ -0,0 +1,10 @@
#include "caml/mlvalues.h"
value caml_b_or_n(value unit)
{
#ifdef NATIVE_CODE
return Val_int(1);
#else
return Val_int(0);
#endif
}

Some files were not shown because too many files have changed in this diff Show more