CMake project for Emscripten - c++

I want to make CMake and Emscripten friends. Didn't find more or less informative documentation on the Emscripten project website, but they provide CMake toolchain file so I think it should be possible.
So far very basic compilation without advanced parameters works fine, but I have issues while using embind and preloading files.
Linking process seems to miss Emscripten "binaries" and produces warnings for all embind related functions like this one: warning: unresolved symbol: _embind_register_class which results in respective errors while loading compiled JS file in rowser.
No .data file is generated during the compilation.
I've created a minimalistic example which includes two targets: one "normal" (client) and one manual (manual-client) which simply runs emcc as I expect it to be run: https://github.com/xwaffelx/minimal-cmake-emscripten-project/blob/master/README.md
Although manual way works, I don't think it is a proper way to do it...
--- Update ---
As requested, here is even more short example:
CMakeLists.txt file
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0015 NEW)
project(emtest)
set(CMAKE_VERBOSE_MAKEFILE on)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/build.emscripten)
include_directories("lib/assimp/include")
link_directories("lib/assimp/lib-js")
link_libraries("assimp")
file(GLOB_RECURSE CORE_HDR src/.h)
file(GLOB_RECURSE CORE_SRC src/.cpp)
add_definitions("-s DEMANGLE_SUPPORT=1 --preload-file ${CMAKE_SOURCE_DIR}/assets --bind")
add_executable(client ${CORE_SRC} ${CORE_HDR})
Result should be equivalent to running the following command manualy:
emcc
-Ilib/assimp/include
-s DEMANGLE_SUPPORT=1
--preload-file assets
--bind
Application.cpp
lib/assimp/lib-js/libassimp.so
-o client.js
Here is the Application.cpp:
#include "Application.h"
#include <iostream>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
void Application::Initialize() {
std::cout << "Initializing application." << std::endl;
Assimp::Importer importer; // For test purpose
}
void Application::SayHello() {
std::cout << "Hello!" << std::endl;
}
and Application.h:
#ifndef APPLICATION_H
#define APPLICATION_H
#include <emscripten/html5.h>
#include <emscripten/bind.h>
namespace e = emscripten;
class Application {
public:
void Initialize();
void SayHello();
};
EMSCRIPTEN_BINDINGS(EMTest) {
e::class_<Application>("Application")
.constructor()
.function("Initialize", &Application::Initialize)
.function("SayHello", &Application::SayHello);
}
#endif
THen I run cmake as follows:
cmake -DCMAKE_TOOLCHAIN_PATH=path/to/Emscripten.cmake .. && make
However, warnings like warning: unresolved symbol: _embind_register_class are produced during linking and running the code and no preloaded data in client.data file is created while compiling CMake project. At the same time no warnings and everything runs OK while compiling manually.

The solution was to specify all the flags during the linking by providing following CMake instruction:
set_target_properties(client PROPERTIES LINK_FLAGS "-s DEMANGLE_SUPPORT=1 --preload-file assets --bind")
Thanks to developers of emscripten who helped in the github issues tracker.

Related

CMake + ninja - exe file shows no output if header file is included in a source file

I am learning to use CMake and I'm using ninja as a build system. I have created a very simple program, but I have one issue. I have only two files main.cpp and constants.h. Whenever I include constants.h in main.cpp, after building a .exe file, I see no output from it after running it through bash. When i remove the include, it works just fine. Can anybody please guide.
main.cpp
#include <stdio.h>
#include "constants.h" // does not even show output when i add this
int main() {
printf("hello world");
return 0;
}
constants.h
#pragma once
const int WIDTH = 1000;
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
set(CMAKE_CXX_COMIPLER "g++")
set(CMAKE_C_COMPILER "gcc")
project(BoidSimulation)
set(CMAKE_CXX_STANDARD 17)
add_executable(BoidSimulation main.cpp)
Can someone please guide what I'm doing wrong. I have tried to add constants.h in add_exectuable and also used target_include_directories by keeping the header file in some directory.
I build and run through the following commands:
cmake -G "Ninja" -S . -B ./out/build/
cd out/build
ninja
./BoidSimulation
Running this with header file shows no output and no errors on bash, it just moves on to next line. It runs fine through powershell and shows the output with or without header files.
You never print a newline. In some shells this will cause the prompt to be printed over it. This has nothing to do with your constants.h header.

How to include 'jni.h' file using CMake [duplicate]

This question already has answers here:
How to use FIND_JNI on cmake
(2 answers)
Closed 9 months ago.
I am trying to compile my C++ project using CMake on my Mac M1 Pro 12.0.1.
This is my simple directory structure:
In my foo.cpp, I am including jni.h header file:
#include <iostream>
#include "foo.h"
#include <vector>
#include <jni.h>
void foo()
{
std::cout << "Hello World!\n";
}
This is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
project("myapplication")
add_library(my_app SHARED main.cpp foo.cpp)
target_include_directories(my_app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
if(APPLE)
set(JAVA_INCLUDE_PATH "$ENV{JAVA_HOME}/include")
set(JAVA_INCLUDE_PATH2 "$ENV{JAVA_HOME}/include/darwin")
set(JAVA_AWT_INCLUDE_PATH "$ENV{JAVA_HOME}/include")
message("JAVA_INCLUDE_PATH = ${JAVA_INCLUDE_PATH}")
find_package(JNI REQUIRED)
if (JNI_FOUND)
message (STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}")
message (STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}")
endif()
target_include_directories(my_app PUBLIC ${JNI_INCLUDE_DIRS})
endif()
I am getting CMake output as:
-- Found JNI: /System/Library/Frameworks/JavaVM.framework
-- JNI_INCLUDE_DIRS=/Users/vmangal/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/include;/Users/vmangal/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/include/darwin;/Users/vmangal/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home/include
-- JNI_LIBRARIES=/System/Library/Frameworks/JavaVM.framework;/System/Library/Frameworks/JavaVM.framework
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/vmangal/my/practice/cmake/build
My JAVA_HOME environment variable is set to /Users/vmangal/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk
But during make command, I am getting compilation error:
/Users/vmangal/my/practice/cmake/foo.cpp:4:10: fatal error: 'jni.h' file not found
#include <jni.h>
Am I missing something in CMakeLists.txt ?
I had a similar problem and found that when setting variables this way
set(JAVA_INCLUDE_PATH "$ENV{JAVA_HOME}/include")
i was getting wrong imports for C_INCLUDES and CXX_INCLUDES. Like this (see in CMakeFiles/my_app.dir/flags.make):
C_INCLUDES = -I/include -I/include/darwin
CXX_INCLUDES = -I/include -I/include/darwin
Which basically which meant that $ENV{JAVA_HOME} was an empty string.
After I set the full paths in CMakeLists
set(JAVA_INCLUDE_PATH /usr/local/Cellar/openjdk#17/17.0.3/libexec/openjdk.jdk/Contents/Home/include)
everything started working correctly.
So the conclusion is that if you want to run cmake and use $ENV{JAVA_HOME} – use syntax JAVA_HOME=/path/to/java/home cmake ...

Emscripten: Module not found: Can't resolve 'env'

I am trying to compile a small project which references a static library (.a) in /usr/local/lib and it's header (.h) files in /usr/local/include/test into a standalone wasm file. The following code runs fine with no compilation errors.
main.cpp
#include <iostream>
#include "libtest/libtest.h"
int main(int argc, char *argv[]) {
LibTest Test;
std::cout << Test.version() << std::endl;
return 0;
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.15)
project(test_project)
set(CMAKE_CXX_STANDARD 14)
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} test)
However, when I compile into wasm using the following command:
emcc main.cpp -I/usr/local/include -Os -s WASM=1 -s SIDE_MODULE=1 -o main.wasm
I get the error: Module not found: Can't resolve 'env' when importing the wasm file into the client application.
Here's how I'm importing it:
const Component = dynamic({
loader: async () => {
const module = await import("../../main.wasm");
},
ssr: false,
});
NOTE: This will work fine if remove the imported library and do just about anything else (e.g. the Hello World or add(x, y) function examples.)
Anyone ran into this problem?
I'm assuming that the emcc command I am running is failing to compile the library correctly.
You are compiling it into pure Wasm but the C++ code contains functions without a Wasm equivalent; they have to be replaced with JavaScript equivalents and "imported" into the Wasm.
This is normally done by Emscripten but you are telling it not to do this when you specify that you want a standalone Wasm file.

Clear way to setup LSP (vim-lsc) with vim + CMake?

I am trying to set up LSP with vim + cmake. I am using vim-lsc plugin as client and clangd as the server.
Folder structure:
Test/
test.cpp
test.h
CMakeLists.txt
test.h:
#ifndef TEST_H
#define TEST_H
class Apple {
public:
int size;
};
#endif
test.cpp:
#include <iostream>
#include "test.h"
int main() {
Apple apple;
return 0;
}
CMakeLists.txt:
cmake_minimum_required(VERSION 3.10)
project(my_test)
add_executable(test test.cpp)
target_include_directories(test PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
~/.vimrc:
let g:lsc_server_commands = {'c': 'clangd', 'cpp': 'clangd'}
I build with
mkdir build
cd build
cmake ..
make
This compiles fine with no errors and warnings.
However, while editing test.cpp in vim, vim-lsc shows errors not detecting user defined header files and types defined within them. For example, in the above project, the following two errors are shown:
test.cpp|3 col 10 error| 'test.h' file not found [pp_file_not_found]
test.cpp|6 col 5 error| Unknown type name 'Apple' [unknown_typename]
How do I set this up so that headers in the project are identified properly?
EDIT: The problem has changed somewhat. I added a compile_commands.json file to the top level directory to no avail. I suspect that it has to do with the way paths are represented in Git Bash on windows.
The compile_commands.json has the inlcude paths like so:
-IE:\\C:\\Test
If the class is defined in the same file, the lsp protocol seems to be working. Can clang not work with such paths? Could someone shed some light on this?

cmake: add directory to be searched for header files

I am writing c++ code to run tests on an arduino project I've written. In order to run it, I need it to include mockups of a couple libraries (the original libraries don't work in c++). The mockups are in my src directory, along with the tests in main.cpp. The files that rely on the mockups are in the parent directory of src, and I cannot modify them to refer to src without breaking their ability to run when uploaded to an arduino. I also cannot add the mockups or main.cpp to the parent directory, as this would interfere with the operation of the arduino code.
So, I need to add the child directory with the mockups to the directories that are searched when compiling the files in the parent directory.
Directory Outline:
parent
forTest.h
forTest.cpp
src
parson.h
parson.c
String.h
String.cpp
main.cpp
CMakeLists.txt
build
In this case, main.cpp has "#include ../forTest.cpp", and forTest.cpp has "#include parson.h" and "#include String.h"
I am currently running the following code in CMakeLists.txt:
cmake_minimum_required (VERSION 2.6)
project(makeTest)
include_directories(../ )
set (makeTest_VERSION_MAJOR 1)
set (makeTest_VERSION_MINOR 0)
add_executable(makeTest main.cpp ../forTest.cpp String.cpp parson.c)
I then build it in the build directory from the command line with
cmake -G "Unix Makefiles" ../src
make
The cmake command resolves successfully, but the make command hits "fatal error: parson.h not found" when building forTest.cpp
How can I resolve this?
edit: my apologies if this has an obvious answer, I'm very new to Cmake
edit the second: using the changes suggested by Gergely Nyiri and changing #include to #include "/usr/include/string.h" resolves several errors, but introduces a new error : "implicit instantiation of undefined template" during the make step.
It refers to the following code in by string.h mockup file:
#ifndef STRING_H
#define STRING_H
#include "/usr/include/string.h"
using namespace std;
class String {
private:
std::string thisString;
char chars[];
...
#endif
which returns the error:
In file included from /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/cstring:61:
/Users/douglas/Desktop/parts/makeHolder/testMake/src/string.h:9:14: error:
implicit instantiation of undefined template 'std::__1::basic_string<char,
std::__1::char_traits<char>, std::__1::allocator<char> >'
std::string thisString;
This is followed by:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/iosfwd:193:32: note:
template is declared here
class _LIBCPP_TEMPLATE_VIS basic_string;
First of all, I would propose to put CMakeLists.txt in your source root. Then:
cmake_minimum_required (VERSION 2.6)
project(makeTest)
include_directories(src)
set (makeTest_VERSION_MAJOR 1)
set (makeTest_VERSION_MINOR 0)
add_executable(makeTest src/main.cpp forTest.cpp src/String.cpp src/parson.c)
Configure & build:
cd build
cmake ../
make
Even better if you use target_include_directories instead of include_directories:
..
add_executable(makeTest src/main.cpp forTest.cpp src/String.cpp src/parson.c)
target_include_directories(makeTest PRIVATE src)