DkSDK CMake Tutorial

What you need to know

To complete this tutorial, you should:

  • Be able to use git to clone a project

  • Be familiar enough with one of the following C IDEs to open, configure and build a C project:

    • Visual Studio Code

    • CLion

    • Visual Studio

    • Xcode

  • Have access to at least one computer from the following list:

    • Windows 64-bit

    • macOS

    • Linux x86_64

Step One - Accessing DkSDK

Follow the steps laid out in the DkSDK Subscriber Access Guide.

Step Two - Creating a Project

For your new project, decide on

NAME

A project name like AcmeWidgets, where Acme is a short abbreviation for your company, brand or team and Widgets describes your project.

The project naming rules are as follows:

  • The project name must be at least 3 characters long

  • must only have alphanumeric characters

  • must have its first character be an uppercase letter

  • must have its second character be a lowercase letter

  • must contain, after the second character, another sequence of an upper case letter followed by a lowercase letter

DIR

Where you would like to place your new project directory

Then:

  1. Make a git clone of DkHelloWorld.

  2. Open PowerShell on Windows, Terminal on macOS, or a shell on Linux. Then change directory to where you cloned DkHelloWorld:

    cd .../DkHelloWorld
    
  3. In the same PowerShell/Terminal/shell, run the following with your chosen NAME and DIR:

    ./dk dksdk.project.new NAME AcmeWidgets DIR ../acme-widgets
    
  4. Open your new project in your C IDE.

    You can build and run the main-cli target in the src/MainCLI directory to start an echo server. You won't get any output unless you add the -v option to main-cli.

  5. There is a README.md in your new project with other instructions. For now though, just continue with Step Three - Embedding OCaml in C below.

Step Three - Embedding OCaml in C

You won't need to read these references now, but the two references below explain in detail how to embed OCaml into your C code:

  1. Real World OCaml - The Compiler Backend

  2. OCaml 4.14 Language Manual - Interfacing C with OCaml

What follows is the simple example from Real World OCaml - The Compiler Backend:

  1. Create a directory src/EmbedOut in your project tree.

  2. Create the file src/EmbedOut/embed_out.c. The highlighted line is the interesting line: it will call the OCaml code we'll write in the next step. Here is the C code:

/* filename: src/EmbedOut/embed_out.c */

/************************************************************************* * * * Copyright 2023 Diskuv, Inc. * * * * Licensed under the DkSDK SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT * * <https://diskuv.com/legal/> or under the * * Open Software License version 3.0 * * <https://opensource.org/license/osl-3-0-php/&gt;, at your option. * * This file may not be copied, modified, or distributed except * * according to those terms. * * * *************************************************************************/ #include <stdio.h> #include <caml/alloc.h> #include <caml/mlvalues.h> #include <caml/memory.h> #include <caml/callback.h>

#ifdef _WIN32 # define portable_main wmain # define portable_char wchar_t #else # define portable_main main # define portable_char char #endif

int (portable_main)(int argc, portable_char **argv) { printf("Before calling OCaml\n"); fflush(stdout); caml_startup (argv); caml_shutdown (); printf("After calling OCaml\n"); return 0; }

Note

Windows wmain function

If you are familiar with Unix C programming, the wmain function and wchar_t typedef may be unusual to you. In Windows the w prefix means wide-characters, which is a fancy way of saying Unicode support.

  1. Create the files src/EmbedOut/embed_ml.ml:

    (* filename: src/EmbedOut/embed_ml.ml *)
    let () =
      print_endline "hello embedded world 1";
      Embed_me2.run ()
    

    and src/EmbedOut/embed_me2.ml:

    (* filename: src/EmbedOut/embed_me2.ml *)
    let run () = print_endline "hello embedded world 2"
    

    Note

    Differences from Real World OCaml

    With DkSDK you provide one entry point to a library, and from that entry point do all the calls to other modules you need. That entry point (embed_ml.ml in our case) must have the same name as the library which we'll define in the next step.

  2. Create src/EmbedOut/CMakeLists.txt:

    # src/EmbedOut/CMakeLists.txt
    

    DkSDKProject_AddPackage(AUTO_OPAM_PACKAGE)

    add_library(embed_ml STATIC embed_ml.ml embed_me2.ml)

    add_executable(embed-out embed_out.c) target_link_libraries(embed-out PRIVATE DkSDK::OCaml::Compile DkSDK::OCaml::ForStaticBinary embed_ml)

  3. Add the highlighted line to bottom of src/CMakeLists.txt:

    # src/CMakeLists.txt
    

    add_subdirectory(HelloLib) add_subdirectory(MainCLI) add_subdirectory(EmbedOut)

Then configure the project. You can now build and run the embed-out target. You should see:

Before calling OCaml
hello embedded world 1
hello embedded world 2
After calling OCaml

Step Four - What's Next?

You have just:

  • Created a new project

  • Created a new target that embeds C and OCaml code

There is a README.md inside your new project that you should skim.

Enjoy!!