alagris
alagris

Reputation: 2216

How to export C function in Emscripten when using CMake

In this tutorial it shows the following example for exporting C functions

./emcc tests/hello_function.cpp -o function.html -s EXPORTED_FUNCTIONS='["_int_sqrt"]' -s EXPORTED_RUNTIME_METHODS='["ccall", "cwrap"]'

I would like to do the same except that I use CMake like this

cd bin
emcmake cmake ../src
emmake make

What is the canonical way of specifying -s in emmake? Should I add it to CMakeLists.txt like

set(EXPORTED_FUNCTIONS '["_int_sqrt"]')

or do something similar?

Upvotes: 5

Views: 4342

Answers (4)

8Observer8
8Observer8

Reputation: 1172

  1. Add extern "C" as the docs says:

main.cpp

extern "C"
{
    void sayHello()
    {
        std::cout << "hello" << std::endl;
    }
}

If you don't add this, it will be an error: em++: error: undefined exported symbol: "_sayHello"

  1. Add this code to the CMakeLists.txt file:
target_link_options(${EXECUTABLE_NAME} PRIVATE
        -sEXPORTED_RUNTIME_METHODS=["cwrap"])

target_link_options(${EXECUTABLE_NAME} PRIVATE
        -sEXPORTED_FUNCTIONS=["_sayHello"])
  1. Call your function in JavaScript:
Module["onRuntimeInitialized"] = function() {

    const sayHello = Module.cwrap("sayHello", null, []);
    sayHello();

};

Upvotes: 0

Vinci
Vinci

Reputation: 1520

I just ran into exactly the same issue and even started one on the Emscripten github page (see here).

What worked for me was to put all the Emscripten specific flags into one long string which I then added with target_compile_options and target_link_options.

  set(EMS
      "SHELL:-s EXPORTED_FUNCTIONS=['_main','_malloc','_int_sqrt'] -s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']"
  )
  target_compile_options(EmscriptenExample PRIVATE ${EMS})
  target_link_options(EmscriptenExample PRIVATE ${EMS})

It's important to drop both, double quotes and whitespaces from the list of functions otherwise it won't work. At least with CMake 3.17.3 escaping the double quotes did not work for me.

/edit For the sake of completeness: Emscripten now allows to remove the whitespace between the -s prefix and the actual flag. This makes it possible to actually use CMake's own target_*_options functions, e.g.:

target_link_options(target PRIVATE -sEXPORTED_FUNCTIONS=['_main','_foo','_bar'])

Upvotes: 0

curiousdannii
curiousdannii

Reputation: 2020

This is the simplest/cleanest way now:

target_link_options(target PRIVATE
        -sEXPORTED_FUNCTIONS=['_main','_foo','_bar'])

And if you have more -s settings (and you probably will) you can add them within this function call, or you can call target_link_options multiple times, both work. It seems quite accommodating, I haven't needed to escape anything.

Upvotes: 4

alagris
alagris

Reputation: 2216

What I figured out so far is that it can be achieved CMake with the following settings

# Here you can add -s flag during compiling object files
add_definitions("-s EXPORTED_RUNTIME_METHODS='[\"ccall\",\"cwrap\"]'")
add_definitions("-s EXTRA_EXPORTED_RUNTIME_METHODS='[\"ccall\",\"cwrap\"]'")
add_definitions("-s EXPORTED_FUNCTIONS='[\"_testInt\"]'")
# Here you can add -s flag during linking
set_target_properties(web_mealy_compiler PROPERTIES LINK_FLAGS "-s EXTRA_EXPORTED_RUNTIME_METHODS=['ccall','cwrap']")
# Set this if you want to to generate sample html file
set(CMAKE_EXECUTABLE_SUFFIX ".html")

Then you should be able to call C functions from javascript as follows:

<script type="text/javascript">
     
    Module['onRuntimeInitialized'] = function() {
     
        console.log("wasm loaded ");
        
        console.log(Module.ccall); // make sure it's not undefined
        console.log(Module._testInt); // make sure it's not undefined
        console.log(Module._testInt()); // this should work
        console.log( Module.ccall('testInt', // name of C function
            'number', // return type
             [], // argument types
             []) // argument values
        );
    }
</script>

And this is my definition of C function:

#include <emscripten.h>
EMSCRIPTEN_KEEPALIVE
int testInt(){
    return 69420;
}

Upvotes: 4

Related Questions