How to use jbuild and ppx_driver with ppx_deriving - ocaml

I am trying to use jbuilder together with ppx_deriving (ppx_deriving_yojson specifically) but got stuck for well over an hour now. My current approach is a jbuild file, containing the following:
(jbuild_version 1)
(executables
((names (my-binary))
(libraries
(ppx_deriving
ppx_deriving_yojson
cohttp
yojson))
(preprocess (pps (ppx_deriving_yojson ppx_driver.runner)))))
But that results in
Command [5] exited with code 1:
$ (cd _build/default && ../.ppx/default/ppx_deriving_yojson+ppx_driver.runner/ppx.exe --dump-ast -o src/my_file.pp.ml --impl src/my_file.ml)
File "src/my_file.ml", line 16, characters 5-13:
Error: Attribute `deriving' was not used
Running the generated ppx_driver in _build/.ppx/default/ppx_deriving_yojson+ppx_driver.runner/ppx.exe manually with -print-transformations gives empty output, so I am obviously missing something.
The code builds fine with topkg by just including ppx_deriving and ppx_deriving_yojson as dependencies.

As of more recent versions of ppx_deriving_yojson this should be possible.
Code:
type t = {x: int; y: int} [##deriving to_yojson]
let () = print_endline (Yojson.Safe.to_string (to_yojson {x= 1; y= 2}))
And a sample jbuild file:
(jbuild_version 1)
(executables
((names (main))
(preprocess (pps (ppx_deriving_yojson)))
(libraries (ppx_deriving_yojson.runtime))))
(install
((section bin)
(files ((main.exe as main)))))

Related

Unbound value "string_of_sexp"

It works quite well in utop with #require "ppx_jane" but
I added (preprocess (pps ppx_jane)) in my dune file which looks like this:
(library
(preprocess (pps ppx_jane))
(name raftml)
(modules raft rpc types)
(libraries
core
core_unix
proto
grpc
grpc-lwt
ocaml-protoc
lwt
lwt.unix
h2
h2-lwt-unix
domainslib
yojson
ppx_jane
ppx_sexp_conv
ppx_deriving_yojson
ppx_deriving
ppx_deriving_yojson.runtime))
And my types are like this:
type log = {
mutable command: string;
mutable term: int32;
mutable index: int32
} [##deriving sexp]
I call sexp_of_log in my code like this:
let persist () = Out_channel.write_all "file_name" ~data:(Sexp.to_string (sexp_of_log { command = "hello"; term = (10l); index = (24l); }))
And there's an error when I run dune build: Unbound value "string_of_sexp"
I solved this by adding:
open Sexplib.Std
At the top of the file that has the record types deriving sexp.

OCaml compilation with corebuild

I currently have a project (Go to Python compiler) with the following files
ast.ml
parser.mly
lex.mll
weeder.ml
prettyPrint.ml
main.ml
Here are the dependencies:
parser: ast
lexer: parser, Core, Lexing
weeder: ast
prettyPrint: ast
main: ast, lex, parser, weeder, prettyPrint
I try to compile doing the following which should work according to the documentation I read:
$ menhir parser.mly
> Warning: you are using the standard library and/or the %inline keyword. We
recommend switching on --infer in order to avoid obscure type error messages.
$ ocamllex lex.mll
> 209 states, 11422 transitions, table size 46942 bytes
$ ocamlbuild -no-hygiene main.native
> File "parser.mli", line 77, characters 56-59:
Error: Unbound type constructor ast
Command exited with code 2.
Compilation unsuccessful after building 6 targets (2 cached) in 00:00:00.
ast.ml contains a list of type declarations in which I have a
type ast = ...
I spent a few hours now reading doc for ocamlfind, corebuild and ocamlopt and nothing. At some point it compiled by what seemed like a mere coincidence and never worked again. I'm open to using any tool.
Here is what is in parser.mly
%{
open Ast
exception ParserError of string
let rec deOptionTypeInList tupleList =
match tupleList with
| [] -> []
| (a, Some t)::tl -> (a, t)::(deOptionTypeInList tl)
| _ -> raise (ParserError "no type given in type declaration")
%}
[ ... long list of tokens ... ]
%type <ast> prog (* that seems to be the problem *)
%type <string> packDec
%type <dec> dec
%type <dec> subDec
[...]
%start prog
[ ... rules ... ]
And here is the line, the very last, that is refereed to in the error message.
val prog: (Lexing.lexbuf -> token) -> Lexing.lexbuf -> (ast)
The open Ast construct will not be exported to the .mli file where the type of symbols are mentioned. Try using
%type <Ast.ast>
Edit: also, your build commands are weird. You should not call ocamllex and menhir manually, and consequently not need -no-hygiene. Remove all generated files and just do
ocamlbuild -use-menhir main.byte

Installing RLdev tools on Linux

When installing Haeleth's RLdev I got the error:
$ ./configure && omake
........
File "pa_matches.ml", line 24, characters 2-6:
Parse error: Deprecated syntax, the grammar module is expected
Error while running external preprocessor
Command line: camlp4 pa_o.cmo pa_op.cmo pa_extend.cmo q_MLast.cmo pr_dump.cmo 'pa_matches.ml' > /home/.../tmp/ocamlpp4e8738
*** omake error:
File rlc/OMakefile: lines 26:4-27:88
command terminated with code 2: ocamlc -c -pp 'camlp4 pa_o.cmo pa_op.cmo pa_extend.cmo q_MLast.cmo pr_dump.cmo' -I /usr/lib/ocaml/camlp4 gramlib.cma pa_matches.ml -o pa_matches.cmo
File pa_matches.ml contains only following code:
Line 22: open Pcaml
Line 23: EXTEND
Line 24: expr: LEVEL "expr1"
Line 25: [[ v = expr; "matches"; p = patt -> <:expr< match $v$ with [ $p$ -> True | _ -> False ] >> ]];
Line 26: END
In the INSTALL file specified OCaml version 3.09 but 4.01 installed.
OCaml 3.09 is incompatible to OCaml 4.01. This especially applies to camlp4. With OCaml 3.10 a new, backward incompatible version of camlp4 was introduced.
That's probably the reason, why pa_matches.ml doesn't compile. It uses the old camlp4.

Module case name confusion

I made the mistake of updating software and now I can't run any OUnit tests.
I think I've managed to boil the problem down to a simple REPL session.
$ ocaml -I /opt/local/lib/ocaml/site-lib/oUnit
OCaml version 4.01.0
# Ounit.assert_equal;;
Error: Wrong file naming: /opt/local/lib/ocaml/site-lib/oUnit/ounit.cmi
contains the compiled interface for
Ounit when OUnit was expected
# OUnit.assert_equal;;
Error: Reference to undefined global `OUnit'
Any ideas what I'm doing wrong?
I'm running this on a Mac laptop which has the default case-insensitive/case-preserving file-system, but dinking with the case of the include path doesn't help.
My larger problem manifests thus:
ocamlbuild \
-libs \
nums,str,unix,oUnit,graph \
-cflags \
-g,-w,+a-4,-warn-error,+a-4,-I,/opt/local/lib/ocaml/site-lib/oUnit,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
-lflags \
-g,-I,/opt/local/lib/ocaml/site-lib/oUnit,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
./AllTests.native
Error: No implementations provided for the following modules:
OUnitCore referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitLoggerStd referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitUtils referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitConf referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitAssert referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitBracket referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitTest referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitRunner referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitChooser referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitLogger referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2),
/opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit)
OUnitTestData referenced from /opt/local/lib/ocaml/site-lib/oUnit/oUnit.cmxa(OUnit2)
Command exited with code 2.
Compilation unsuccessful after building 646 targets (645 cached) in 00:00:02.
Your first error is probably because your OS does not distinguish cases in filenames but OCaml does:
$ ocamlobjinfo `ocamlfind query oUnit`/oUnit.cmi
File /mnt/home/jun/.opam/system/lib/oUnit/oUnit.cmi
Unit name: OUnit
Interfaces imported:
cb146e345f0b34fc8ad4b5afe69d1f20 OUnit
...
You can see that the module is called "OUnit" not "Ounit".
The second error is clearly because the library is not loaded into the REPL yet. The REPL's knows the existence of the function, but has no code loaded. It is accessible if you load the library and those on which it depends:
ocaml -I `ocamlfind query oUnit` unix.cma oUnitAdvanced.cma oUnit.cma
OCaml version 4.01.0
# OUnit.assert_equal;;
- : ?cmp:('a -> 'a -> bool) ->
?printer:('a -> string) ->
?pp_diff:(Format.formatter -> 'a * 'a -> unit) ->
?msg:string -> 'a -> 'a -> unit
= <fun>
I solved the problem by changing my ocamlbuild invocation to use -use-ocamlfind which seems to work around all the path confusion.
I also uninstalled all OCaml modules and instead installed OPAM and then installed OUnit and others via OPAM, and re-logged in so that my environment is pristine.
The build command that works for me now is
ocamlbuild \
-use-ocamlfind \
-libs graph \
-pkgs str,unix,ounit \
-cflags -g,-w,+a-4,-warn-error,+a-4,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
-lflags -g,-I,/opt/local/lib/ocaml/site-lib/ocamlgraph \
./AllTests.native
which is generated by my updated build script
(* Usage: ocaml make.ml [<ModuleToBuild>.{byte,native}] *)
#load "unix.cma";;
let args = match Array.to_list Sys.argv with
| [] -> failwith "no program on argv"
| _::[] -> ["AllTests.byte"]
| _::argv -> argv
let library_paths = [
(* TODO: Figure out why `opam install ocamlgraph` fails *)
"-I"; "/opt/local/lib/ocaml/site-lib/ocamlgraph";
]
(* If the -p flag is first in the argument list, then replace it
with a host of flags that enable profiling via ocamlprof. *)
let args, c_opt_flags, l_opt_flags =
match args with
| "-p"::rest ->
(* Treat the targets as debug and profile targets. *)
"-tags"::"debug,profile"
(* Use profiling versions of the compilers which instrument
various flow control constructs with entry counters. *)
::"-ocamlc"::"ocamlcp"
::"-ocamlopt"::"ocamloptp"
::rest,
(* Instrument all available points. *)
["-P"; "a"],
(* Link with a wrapper that dumps profiling data if program
exits noramlly. *)
["-p"]
| _ -> args, [], []
let standard_flags = [
"-use-ocamlfind";
(* TODO: Figure out why `opam install ocamlgraph` fails *)
"-libs"; "graph";
"-pkgs"; "str,unix,ounit"; (* TODO: graph *)
"-cflags"; (String.concat "," (
[
"-g";
"-w"; "+a-4";
"-warn-error"; "+a-4";
]
# c_opt_flags
# library_paths));
"-lflags"; (String.concat "," (
[
"-g";
]
# l_opt_flags
# library_paths));
]
let build_command = "ocamlbuild"
let argv = build_command::(standard_flags # args)
let _ = begin
Printf.printf "%s\n" (String.concat " \\\n\t" argv);
flush stdout;
Unix.execvp build_command (Array.of_list argv);
end;;

Python Embedded in C++

So I have a GUI program that has a great deal of "stuff" going on. I am adding a python scripting interface so someone can interact problematically with this environment. I am using boost python. So first thing I have is a new module I want to create. For simplicity right now my module just is hello world...
#include <boost/python.hpp>
char const* greet() {
return "hello, world" ;
}
BOOST_PYTHON_MODULE(cerrnimapi) {
boost::python::def( "greet", greet ) ;
}
In my system I have a class that looks like this...
Controller::Controller( ) {
Py_Initialize( ) ;
main_module = boost::python::import( "__main__" ) ;
main_namespace = main_module.attr( "__dict__" ) ;
}
void Controller::execute_script( std::string filename ) {
try {
boost::python::api::object ignored =
boost::python::exec_file( filename.c_str(), main_namespace ) ;
} catch( boost::python::error_already_set const & ) {
if (PyErr_ExceptionMatches(PyExc_ZeroDivisionError)) {
} else {
PyErr_Print();
}
}
}
Now when I go to execute the script in the GUI I get an error...
Traceback (most recent call last):
File "/home/mokon/repository/trunk/python.py", line 1, in <module>
import cerrnimapi
ImportError: No module named cerrnimapi
So of course I am building something wrong. My build system uses autotools so here are a few pieces of that build system that relate to this...
In configure.ac:
AM_PATH_PYTHON
AC_ARG_VAR([PYTHON_INCLUDE], [Include flags for python, bypassing python-config])
AC_ARG_VAR([PYTHON_CONFIG], [Path to python-config])
AS_IF([test -z "$PYTHON_INCLUDE"], [
AS_IF([test -z "$PYTHON_CONFIG"], [
AC_PATH_PROGS([PYTHON_CONFIG],
[python$PYTHON_VERSION-config python-config],
[no],
[`dirname $PYTHON`])
AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
])
AC_MSG_CHECKING([python include flags])
PYTHON_INCLUDE=`$PYTHON_CONFIG --includes`
AC_MSG_RESULT([$PYTHON_INCLUDE])
])
AC_ARG_VAR([PYTHON_LD], [Linker flags for python, bypassing python-config])
AS_IF([test -z "$PYTHON_LD"], [
AS_IF([test -z "$PYTHON_CONFIG"], [
AC_PATH_PROGS([PYTHON_CONFIG],
[python$PYTHON_VERSION-config python-config],
[no],
[`dirname $PYTHON`])
AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
])
AC_MSG_CHECKING([python linker flags])
PYTHON_LD=`$PYTHON_CONFIG --ldflags`
AC_MSG_RESULT([$PYTHON_LD])
])
In my obj/ dir Makefile.am...
pyexec_LTLIBRARIES = cerrnimapi.la
cerrnimapi_la_SOURCES = ${SRC_DIR}/lib/PythonAPI.cpp
cerrnimapi_la_LDFLAGS = -avoid-version -module $(PYTHON_LD)
cerrnimapi_la_CXXFLAGS = $(PYTHON_INCLUDE)
My makefile builds the shared lib and its in the obj folder along with my main program. This doesn't help. I have also done a make install to install the cerrnimapi lib in the python folders. This doesn't help.
I have also tried adding the PythonAPI.cpp to my main programs SOURCES but to no avail.
Any ideas? let me know what additional information would be helpful.
Some things to check:
Run nm over your .so file (which might be in .libs) to make sure your module init func is exported.
Make your program print out the value of sys.path (use PyRun_SimpleString) to see where it's expecting your module to turn up. If you're defining modules for your interpreter only, you probably don't want to install them in $pyexecdir.
Read the Extending Embedded Python article. You don't really need to build dynamic libraries at all, unless you're trying for a plugin architecture.
A point on style: You should try and find $PYTHON_CONFIG outside of your tests for $PYTHON_INCLUDE and $PYTHON_LD so you're not doing the AC_PATH_PROGS twice:
AM_PATH_PYTHON
AC_ARG_VAR([PYTHON_CONFIG], [Path to python-config])
AS_IF([test -z "$PYTHON_CONFIG"], [
AC_PATH_PROGS([PYTHON_CONFIG],
[python$PYTHON_VERSION-config python-config],
[no],
[`dirname $PYTHON`])
])
AC_ARG_VAR([PYTHON_INCLUDE], [Include flags for python, bypassing python-config])
AS_IF([test -z "$PYTHON_INCLUDE"], [
AC_MSG_CHECKING([python include flags])
AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
PYTHON_INCLUDE=`$PYTHON_CONFIG --includes`
AC_MSG_RESULT([$PYTHON_INCLUDE])
])
AC_ARG_VAR([PYTHON_LD], [Linker flags for python, bypassing python-config])
AS_IF([test -z "$PYTHON_LD"], [
AC_MSG_CHECKING([python linker flags])
AS_IF([test "$PYTHON_CONFIG" = no], [AC_MSG_ERROR([cannot find python-config for $PYTHON.])])
PYTHON_LD=`$PYTHON_CONFIG --ldflags`
AC_MSG_RESULT([$PYTHON_LD])
])