Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,19 @@ jobs:
cmake .. -G "MinGW Makefiles" -DBUILD_TESTING=ON -DBUILD_TESTING_INTEGRATION=ON -DC_WARNINGS_AS_ERRORS=ON -DCPP_WARNINGS_AS_ERRORS=ON
cmake --build .
ctest --verbose -R "allocator|value|example_basic|integration_basic"

build_and_test_linux_wasm:
strategy:
matrix:
platform: [ubuntu-18.04]
runs-on: ${{ matrix.platform }}
steps:
- name: Set-up repository
uses: actions/checkout@v2

Comment thread
jbajic marked this conversation as resolved.
- name: Build with clang
run: |
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DC_WARNINGS_AS_ERRORS=ON -DWASM=ON
make
Comment thread
antaljanosbenjamin marked this conversation as resolved.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ tool/generated/

# Doxygen output files.
html/

# emsdk folder pulled from wasm/install_deps.sh
wasm/emsdk
32 changes: 26 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,23 @@

cmake_minimum_required(VERSION 3.8)

project(mgclient VERSION 1.3.0)
if(WASM)
execute_process(COMMAND ${CMAKE_SOURCE_DIR}/wasm/install_deps.sh)
set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/wasm/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake")
endif()

project(mgclient VERSION 1.2.0)
# Minor version increase can also mean ABI incompatibility with previous
# versions. IMPORTANT: Take care of the SO version manually.
set(mgclient_SOVERSION 2)
set(WASM OFF CACHE BOOL "Compile mgclient for wasm")
add_definitions(-DMGCLIENT_VERSION="${mgclient_VERSION}")

# Deal with the operating system.
if((NOT UNIX) AND EMSCRIPTEN)
message(FATAL_ERROR "WASM build is only supported in Linux")
Comment thread
gitbuda marked this conversation as resolved.
endif()

if(WIN32)
message(STATUS "ON WINDOWS BUILD")
set(MGCLIENT_ON_WINDOWS TRUE)
Expand All @@ -29,14 +39,16 @@ if(WIN32)
# "lib;" breaks gtest lib referencing.
set(MGCLIENT_FIND_LIBRARY_PREFIXES "lib")
elseif(UNIX AND NOT APPLE)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(EMSCRIPTEN)
message(STATUS "ON LINUX WASM BUILD")
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
message(STATUS "ON LINUX BUILD")
set(MGCLIENT_ON_LINUX TRUE)
add_definitions(-DMGCLIENT_ON_LINUX)
set(MGCLIENT_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}")
else()
else()
message(FATAL_ERROR "Unsupported operating system. Please create issue or contribute!")
endif()
set(MGCLIENT_ON_LINUX TRUE)
add_definitions(-DMGCLIENT_ON_LINUX)
set(MGCLIENT_FIND_LIBRARY_PREFIXES "${CMAKE_FIND_LIBRARY_PREFIXES}")
elseif(APPLE)
message(STATUS "ON APPLE BUILD")
set(MGCLIENT_ON_APPLE TRUE)
Expand Down Expand Up @@ -79,6 +91,14 @@ set_project_c_warnings(project_c_warnings)
add_library(project_cpp_warnings INTERFACE)
set_project_cpp_warnings(project_cpp_warnings)

if(EMSCRIPTEN)
# same as project_c_warnings but without -O3. In the future we should
# experiment and switch to O3. because it reduces the js output size
# significantly.
set(CMAKE_C_FLAGS_DEBUG "-g -O0 -Wall -Wextra -Wpedantic -std=gnu11")
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -Wall -Wextra -Wpedantic -std=gnu11")
endif()

# Set default build type to 'Release'
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,12 @@ cd build
cmake .. -G "MinGW Makefiles"
cmake --build . --target install
```
## Building WASM (linux only)
Compiling `mgclient` for wasm requires the Emscripten sdk. This is automated in the following steps:
1. mkdir build && cd build
2. cmake .. -DWASM=ON
3. make
Now there should be an `mgclient.js` and an `mgclient.wasm` found in `mgclient/build/`

## Using the library

Expand Down
7 changes: 7 additions & 0 deletions include/mgclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
#ifndef MGCLIENT_MGCLIENT_H
#define MGCLIENT_MGCLIENT_H

#ifdef __EMSCRIPTEN__
#define MGCLIENT_EXPORT EMSCRIPTEN_KEEPALIVE
#include "emscripten.h"
#else
#include "mgclient-export.h"
#endif

#ifdef __cplusplus
extern "C" {
Expand Down Expand Up @@ -1127,7 +1132,9 @@ MGCLIENT_EXPORT void mg_point_3d_destroy(mg_point_3d *point_3d);
/// the server.
enum mg_sslmode {
MG_SSLMODE_DISABLE, ///< Only try a non-SSL connection.
#ifndef __EMSCRIPTEN__
MG_SSLMODE_REQUIRE, ///< Only try a SSL connection.
#endif
};

/// An object encapsulating a Bolt session.
Expand Down
124 changes: 75 additions & 49 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,58 +35,84 @@ else()
message(FATAL_ERROR "Operating system undefined.")
endif()

find_package(OpenSSL REQUIRED)
if(EMSCRIPTEN)
list(APPEND mgclient_src_files mgwasm.c)
add_executable(mgclient ${mgclient_src_files})
set_target_properties(mgclient PROPERTIES LINK_FLAGS "-s ASYNCIFY=1 -s MODULARIZE -s EXPORT_NAME=\"load_mgclient\" --shared-memory --no-entry -s USE_PTHREADS=1 -s WEBSOCKET_SUBPROTOCOL=\"binary\" -s EXPORTED_RUNTIME_METHODS=\"ccall, cwrap, getValue, setValue, UTF8ToString, allocateUTF8\"")

include(GenerateExportHeader)
target_include_directories(mgclient
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}")
else()
find_package(OpenSSL REQUIRED)
include(GenerateExportHeader)

add_library(mgclient-static STATIC ${mgclient_src_files})
set_target_properties(mgclient-static PROPERTIES
OUTPUT_NAME mgclient)
target_compile_definitions(mgclient-static PUBLIC MGCLIENT_STATIC_DEFINE)
target_include_directories(mgclient-static
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${OPENSSL_INCLUDE_DIR}")
target_link_libraries(mgclient-static
PRIVATE
${OPENSSL_LIBRARIES} project_options project_c_warnings)
if(MGCLIENT_ON_WINDOWS)
target_link_libraries(mgclient-static PUBLIC ws2_32)
endif()
add_library(mgclient-static STATIC ${mgclient_src_files})

add_library(mgclient-shared SHARED ${mgclient_src_files})
set_target_properties(mgclient-shared PROPERTIES
OUTPUT_NAME mgclient
SOVERSION ${mgclient_SOVERSION}
C_VISIBILITY_PRESET hidden)
target_include_directories(mgclient-shared
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${OPENSSL_INCLUDE_DIR}")
target_link_libraries(mgclient-shared PRIVATE ${OPENSSL_LIBRARIES})
if(MGCLIENT_ON_WINDOWS)
target_link_libraries(mgclient-shared PUBLIC ws2_32)
endif()
generate_export_header(mgclient-static
BASE_NAME "mgclient"
EXPORT_FILE_NAME "mgclient-export.h")

set_target_properties(mgclient-static PROPERTIES
OUTPUT_NAME mgclient)
target_compile_definitions(mgclient-static PUBLIC MGCLIENT_STATIC_DEFINE)
target_include_directories(mgclient-static
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${OPENSSL_INCLUDE_DIR}")
target_link_libraries(mgclient-static
PRIVATE
${OPENSSL_LIBRARIES} project_options project_c_warnings)

if(MGCLIENT_ON_WINDOWS)
target_link_libraries(mgclient-static PUBLIC ws2_32)
endif()

generate_export_header(mgclient-shared
BASE_NAME "mgclient"
EXPORT_FILE_NAME "mgclient-export.h")
add_library(mgclient-shared SHARED ${mgclient_src_files})

include(GNUInstallDirs)
generate_export_header(mgclient-shared
BASE_NAME "mgclient"
EXPORT_FILE_NAME "mgclient-export.h")

install(TARGETS mgclient-static mgclient-shared
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY
"${PROJECT_SOURCE_DIR}/include/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/mgclient-export.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
set_target_properties(mgclient-shared PROPERTIES
OUTPUT_NAME mgclient
SOVERSION ${mgclient_SOVERSION}
C_VISIBILITY_PRESET hidden)
target_include_directories(mgclient-shared
PRIVATE
"${PROJECT_SOURCE_DIR}/src"
PUBLIC
"${PROJECT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}"
"${OPENSSL_INCLUDE_DIR}")
target_link_libraries(mgclient-shared
PRIVATE
${OPENSSL_LIBRARIES} project_options project_c_warnings)

if(MGCLIENT_ON_WINDOWS)
target_link_libraries(mgclient-shared PUBLIC ws2_32)
endif()

generate_export_header(mgclient-shared
BASE_NAME "mgclient"
EXPORT_FILE_NAME "mgclient-export.h")

include(GNUInstallDirs)

install(TARGETS mgclient-static mgclient-shared
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(DIRECTORY
"${PROJECT_SOURCE_DIR}/include/"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/mgclient-export.h"
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
endif()
49 changes: 40 additions & 9 deletions src/linux/mgsocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.

#include "mgsocket.h"

#include <stdlib.h>
#include <string.h>

#include "mgcommon.h"
#include "mgsocket.h"

#define MG_RETRY_ON_EINTR(expression) \
__extension__({ \
long result; \
do { \
result = (long)(expression); \
} while (result == -1L && errno == EINTR); \
result; \
#ifdef __EMSCRIPTEN__
#include "emscripten.h"
#include "mgwasm.h"
#endif

#define MG_RETRY_ON_EINTR(expression) \
__extension__({ \
long result; \
do { \
result = (long)(expression); \
} while (result == -1L && (errno == EINTR)); \
result; \
})

// Please refer to https://man7.org/linux/man-pages/man2 for more details about
Expand All @@ -47,14 +54,32 @@ int mg_socket_create_handle_error(int sock, mg_session *session) {
return MG_SUCCESS;
}

static int mg_socket_test_status_is_error(long status) {
#ifdef __EMSCRIPTEN__
if (status == -1L && errno != EINPROGRESS) {
#else
if (status == -1L) {
#endif
return 1;
}
return 0;
}

int mg_socket_connect(int sock, const struct sockaddr *addr,
socklen_t addrlen) {
long status = MG_RETRY_ON_EINTR(connect(sock, addr, addrlen));
if (status == -1L) {
if (mg_socket_test_status_is_error(status)) {
return MG_ERROR_SOCKET;
}
#ifdef __EMSCRIPTEN__
if (mg_wasm_suspend_until_ready_to_write(sock) == -1) {
return MG_ERROR_SOCKET;
}
#endif

return MG_SUCCESS;
}

int mg_socket_connect_handle_error(int *sock, int status, mg_session *session) {
if (status != MG_SUCCESS) {
mg_session_set_error(session, "couldn't connect to host: %s",
Expand All @@ -69,6 +94,11 @@ int mg_socket_connect_handle_error(int *sock, int status, mg_session *session) {
}

int mg_socket_options(int sock, mg_session *session) {
#ifdef __EMSCRIPTEN__
(void)sock;
(void)session;
return MG_SUCCESS;
#else
struct {
int level;
int optname;
Expand Down Expand Up @@ -100,6 +130,7 @@ int mg_socket_options(int sock, mg_session *session) {
}
}
return MG_SUCCESS;
#endif
}

ssize_t mg_socket_send(int sock, const void *buf, int len) {
Expand Down
Loading