Undefined module when using Oasis to build an OCaml project - ocaml

It's me, again.. I am working on an OCaml project and I would like to use Oasis to build the whole code base. Here is how my codebase is organized.
src/
core/
init.ml
type.ml
utils.ml
plugin/
main.ml
I firstly only build the library with the following _oasis file:
Library "engine"
Path: src/core
Modules:
Init,
Type,
Utils
BuildDepends: deriving, deriving.syntax, core, batteries
XMETADescription: core engine
And it works fine. There is no error and I can find a library engine.a in the _build/src/core folder.
However, when I try to include the library in the main.ml with the following way:
Module T = Engine.Type
...
And compile with the following _oasis file:
Library "engine"
Path: src/core
Modules:
Init,
Type,
Utils
BuildDepends: deriving, deriving.syntax, core, batteries
XMETADescription: core engine
Executable "main"
Path: src/plugin
MainIs: main.ml
CompiledObject: best
Install: false
BuildDepends: core, batteries, engine
I got an error:
Unbound module Engine
Am I doing anything wrong here? Could anyone give me some help? Thank you!

Your _oasis module defines a library with the name engine but it does not define an Engine module. So your Init, Type and Utils modules are exposed and should be accessible directly without any prefix.
If you want to pack those modules into a parent you can:
Manually pack each module into one big engine.ml file
Use Pack: true in the Library section of your _oasis file which would pack the included modules into a module called Engine
Use module aliases (see the OCaml manual for more information)

Related

How can I wrap a static library (C++) into an XCFramework for use in Swift?

I have been given a static library libExample.a together with a bunch of C++ headers, which I need to use in an iOS app. The binary is fat, containing objects for iphoneos-arm64/e and iphonesimulator-x64.
I have done some research on the subject and came to the conclusion, that using a XCFramework would be the best thing to do. Still, I feel completely out of my depth, since this is my first time trying anything of the sort.
What I have done so far
1. Creating the XCFramework from library files
lipo -extract architectures from fat binary
xcodebuild -create-xcframework -library LIB-arm64.a -headers HEADERS -library ...
Importing the XCFramework into my Swift project didn't yield any usable modules. Also the folder was missing a lot of the files, I've seen in examples. It seemed like the wrong way to go about it, so I tried...
2. Creating a Framework and then a XCFramework from it
Files
Create new Objective-C Framework project ExampleFramework
Pull all my headers and the fat binary into the project
Add all my headers to the ExampleFramework.h umbrella header
Create the following ExampleFramework.modulemap:
framework module ExampleFramework {
umbrella header "ExampleFramework.h”
link "Example"
export *
module * { export * }
}
Settings
General:
1.1. Add libExample.a to Frameworks & Libraries
Build Phases:
2.1. Make all headers public
2.2. Add libExample.a to Link Binary with Libraries
2.3. Add libExample.a to Copy Bundle Resources
Build Settings:
3.1. Skip Install : No
3.2. Build Libraries for distribution : Yes
3.3. Module map file: ExampleFramework/ExampleFramework.modulemap
3.4. Defines modules: Yes
3.5. Compile Sources As: Objective-C++
I then archived the framework, for iphoneos and iphonesimulator respectively:
xcodebuild archive \
-scheme "ExampleFramework" ONLY_ACTIVE_ARCH=NO \
-archivePath "path/to/ExampleFramework_${SDK}.xcarchive" \
-sdk ${SDK} \
SKIP_INSTALL=NO \
BUILD_LIBRARIES_FOR_DISTRIBUTION=YES
...and generated a XCFramework from the outputs:
xcodebuild -create-xcframework \
-framework "path/to/ExampleFramework_iphoneos.xcarchive/Products/Library/Frameworks/ExampleFramework.framework"
-framework ...
-output "path/to/ExampleFramework.xcframework"
Build Errors
The folder structure I got from this looked promising, so I tried adding the XCFramework to my iOS project. The module was being found now, but XCode stopped compiling at the first #include, saying it wasn't able to find stdexcept, from which I concluded it was missing the C++ standard library headers.
I tried setting Header Search Paths in Build Settings. Looking for the right path I found a couple, that looked relevant to me:
/Applications/Xcode_13.2.1.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1
/Applications/Xcode_13.2.1.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
/Applications/Xcode_13.2.1.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/usr/include
Depending on which one I choose and whether I clean before building, I'm getting a bunch of different errors (it seems random). Mainly:
Unknown type name 'namespace'
and lots and lots of others, that lead to me believe XCode doesn't recognize the headers as C++.
Or:
Cyclic dependency on module 'Darwin' : Darwin -> std -> Darwin
I really have no idea at this point, so any solutions or suggestions pointing me into the right direction, would me much appreciated. I'd also be open to some completely different approach, I just want to use the library somehow.
Thanks!
For reference from module.modulemap
framework module GoogleAppMeasurement {
umbrella header "GoogleAppMeasurement-umbrella.h"
export *
module * { export * }
link framework "Security"
link framework "SystemConfiguration"
link "c++"
link "sqlite3"
link "z"
}

Dune: build library and access it in another project and hide or make inaccessible private or implementation modules

I am using Dune for building OCaml projects and I'd like to build a standalone library for reusing it in other projects.
The root folder of the library is mylib and the library was initialized with dune init lib mylib src (after cd mylib).
The directory tree of mylib, the root of the project, is the following:
mylib
+---dune-project
│
+---src
dune
priv.ml
mymodule.ml
mymodule.mli
Here follows the content of the files.
mylib/dune-project:
(lang dune 2.7)
mylib/src/dune:
(library
(name mylib))
mylib/src/priv.ml:
let rec loop a accu i =
let n = Array.length a in
if i = n then
accu
else
loop a (accu + Array.unsafe_get a i) (succ i)
mylib/src/mymodule.mli:
val sum : int array -> int
mylib/src/mymodule.ml:
let sum a =
Priv.loop a 0 0
After giving context and showing the content of each file of this toy example, the question follows:
How can I build the library mylib and use it in another, separate project?
(For example, in another project, I'd consume the library with the following:
Open Mylib
let () =
print_int (Mymodule.sum [1;2;3])
OR
let () =
print_int (Mylib.Mymodule.sum [1;2;3])
)
With Dune, given the executable main, you would write the following dune file to use library mylib if it was published on Opam.
dune:
(executable
(name main)
(libraries mylib))
After successfully building the library and linking it in another project, how can I hide or not expose some modules? For example, given the toy library of before, mylib, I would like to not expose and make inaccessible the module Priv (such that Priv would only be usable inside modules of mylib - like a protected/internal class in Java/C#).
​
I tried searching on Real World OCaml, Cornell CS3110 textbook, Dune documentation, OCaml Learn but, unless deeply nested, I found nothing.
Thank you very much for your help, if I didn't explain something in a clear way, please ask and I will try to explain better.
In the root directory, these should be a file called mylib.opam with content similar to this one. Then, the (public_name mylib) s-expression should be added to library in mylib/src/dune. Finally, making sure to be in the root of the project, the library shall be built with dune build and installed with opam install ..
For actually using the library in another project, even in a different dune workspace, all it's needed is to add (libraries mylib) to the dune file of project that will use the library.
The (private_modules priv) s-expression should be added to library in mylib/src/dune. Then, mylib.ml needs to be created with the following content: module Mymodule = Mymodule: this will make sure that only Mymodule will be exposed under the package Mylib.

OCaml: using Oasis with multiple-level src folders

I am trying to use oasis to compile my project, and my project is organized in this way:
_oasis
src/
main.ml
core_a.ml
core_b.ml
type.ml
plugins/
plugin_a.ml
plugin_b.ml
Note that in the plugin_a.ml, it refers to module type.ml (i.e., open Type).
When I use oasis to compile the project, it reports:
Unbound module Type
Here is the simplified version of my _oasis file:
....
BuildTools: ocamlbuild
BuildDepends: deriving, deriving.syntax, core, batteries
Executable "main"
Path: src
MainIs: main.ml
CompiledObject: best
Install: false
BuildDepends: deriving, deriving.syntax, core, batteries
Am I doing anything wrong here? Or what I am doing is not the best practice to organize a project like this?
I think your project structure is a bit strange. You have a sub directory for plugins, but you should note that there's no namespace or package hierarchy so this is not really useful in practice.
As for whether this is possible, the answer seems to be mixed:
It's not possible to do it with just oasis because it doesn't let you specify multiple values for the Path option.
It should be easily done with ocamlbuild however by tagging everything you need with include (but I don't recommend this).
As your oasis project grows, you should look into defining library sections in oasis and using those to organize inter project dependencies. E.g., in this case you could create a "plugins" library where you include plugin_a and plugin_b. But without some planning ahead here, you will quickly run into circular dependencies.

OCamlfind local library unbound module

I am trying to use ocamlfind to install a library. I am using OCamlMakeFile. Here is my Makefile:
OCAMLMAKEFILE = OCamlMakeFile
RESULT = owebl
SOURCES = src/utils.ml src/verb.ml src/request.ml src/template.ml src/response.ml src/rule.ml src/handler.ml src/server.ml
PACKS = unix str
all: native-code-library byte-code-library
install: libinstall
uninstall: libuninstall
include $(OCAMLMAKEFILE)
So basically the library is a collection of modules that I want to distribute. A basic usage of the library is (main.ml):
open Response
open Rule
open Verb
open Server
let handler =
Handler.create
(StaticRouteRule.create "/" [GET])
(FileResponse.create
~static_file:(FileResponse.StaticFile "/index.html")
())
let server = Server.create "0.0.0.0" 8080 [handler];;
server#serve
I compile the library just by running "make". It generates three files: "owebl.a, owebl.cma, owebl.cmxa". Then I install the library using ocamlfind:
ocamlfind install owebl META owebl.a owebl.cma owebl.cmxa
Oh, the META file is:
version = "0.1"
name = "OWebl"
description = "A Web Framework for OCaml"
archive(byte) = "owebl.cma"
archive(native) = "owebl.cmxa"
requires = "unix,str"
Everything works until I try to compile the above example that uses the library. I run:
ocamlbuild -use-ocamlfind -pkgs owebl main.native
And I get:
ocamlbuild -use-ocamlfind -pkgs owebl main.native
+ ocamlfind ocamlc -c -package owebl -o main.cmo main.ml
File "main.ml", line 1, characters 5-10:
Error: Unbound module Owebl
Command exited with code 2.
Hint: Recursive traversal of subdirectories was not enabled for this build,
as the working directory does not look like an ocamlbuild project (no
'_tags' or 'myocamlbuild.ml' file). If you have modules in subdirectories,
you should add the option "-r" or create an empty '_tags' file.
To enable recursive traversal for some subdirectories only, you can use the
following '_tags' file:
true: -traverse
<dir1> or <dir2>: traverse
Compilation unsuccessful after building 2 targets (1 cached) in 00:00:00.
make: *** [fileserver] Error 10
I tried adding open Owebl to the beginning of main.ml but I just got "unbound module Owebl" similarly. I'm pretty lost as to why these modules are all unbound. What am I missing?
You should use LIB_PACK_NAME variable if you want to pack your modules under umbrella namespace, like OWebl, otherwise they will be available just as it is, e.g. Response, Rule, etc. Also, you should specify the mli files as well as ml files in SOURCES variable. And finally, you shouldn't forget to install cmi files, as well as .a files. Installing mli files is also considered a good tradition, although is not strictly required. Also, consider using OCamlMakefile installation facilities instead of custom installation script.
I am no longer using OCamlMakefile therefore am not sure, but assuming OCamlMakefile compiles things with -for-pack OWebl option and properly builds a packaged module owebl.cmo, you also need to install owebl.cmi.
OCaml compiler seeks cmi files to check the existence of modules and to get their type information. If not installed, the compiler cannot find it. cma and cmxa files are collections of objects and do not provide type information.

Using "ocamlfind" to make the OCaml compiler and toplevel find (project specific) libraries

I'm trying to use ocamlfind with both the OCaml compiler and toplevel. From what I understood, I need to place the required libraries in the _tags file at the root of my project, so that the ocamlfind tool will take care of loading them - allowing me to open them in my modules like so :
open Sdl
open Sdlvideo
open Str
Currently, my _tags file looks like this :
<*>: pkg_sdl,pkg_str
I can apparently launch the ocamlfind command with the ocamlc or ocamlopt argument, provided I wan't to compile my project, but I did not see an option to launch the toplevel in the same manner. Is there any way to do this (something like "ocamlfind ocaml")?
I also don't know how to place my project specific modules in the _tags file : imagine I have a module name Land. I am currently using the #use "land.ml" directive to open the file and load the module, but it has been suggested that this is not good practice. What syntax should I use in _tags to specify it should be loaded by ocamlfind (considering land.ml is not in the ocamlfind search path) ?
Thank you,
Charlie P.
Edit : According to the first answer of this post, the _tags file is not to be used with ocamlfind. The questions above still stand, there is just a new one to the list : what is the correct way to specify the libraries to ocamlfind ?
try this:
$ cat >> .ocamlinit
#use "topfind";;
#require "sdl";;
#require "sdlvideo";;
open Sdl
open Sdlvideo;;
open Str;;
.ocamlinit is sourced from the current directory, falling back to /home/user/.ocamlinit. you can explicitly override it via ocaml -init <filename>
One should distinguish ocamlfind packages, module names and file names. Source code references only module names. Modules are provided by .cma .cmo .cmx (and .cmi) files. Ocamlfind packages are named collections of such files (binaries built from some ocaml library sources). Ocamlfind is not strictly necessary to build a project - just specify the paths to all used libraries via -I. But if the project is distributed in source form - another people will have troubles building it cause used libraries are placed in different places. Then one seeks the way to specify the names of used "third party code pieces" and some external tool to resolve those names to actual paths. This tool is ocamlfind.
First find the ocamlfind package that provides the modules you need (in this case Sdl and Sdlvideo - just run ocamlfind list, most probably the package is named sdl.
Compile with ocamlfind ocamlc -package <package name> -linkpkg source.ml -o program
Alternatively use ocamlfind to extract the path to the .cma file (and others) provided by the package (ocamlfind query <package name>) and use this path with your build tool (plain Makefile, ocamlbuild, etc).
Moreover ocamlfind (is intended to) take away the burden of remembering how the actual .cma files are named and what matching compiler options are required and in what order the packages are depending on each other. One just needs to specify the package name.
Keep in mind that there is no one-to-one strict formal relationship between library name (purely human-targeted name e.g. ocaml-extlib), module names (ExtLib, Enum, IO, etc), file names (extLib.cma) and ocamlfind package name (extlib), though for convenience package maintainers and library authors usually choose guessable names :)
The _tags file is not for ocamlfind, but for ocamlbuild.
Exemple :
ocamlbuild land.native