aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTrumeet <yuuta@yuuta.moe>2022-07-21 21:47:47 -0700
committerTrumeet <yuuta@yuuta.moe>2022-07-21 21:47:47 -0700
commit609c725cedbeca31ae3aba3a9c39c5030ea1d20f (patch)
tree3c48656480b312ab0ea231a235fc77a47055d5ca
parent1635741d1def2228a0b29db987612168d283f28d (diff)
downloadacron-609c725cedbeca31ae3aba3a9c39c5030ea1d20f.tar
acron-609c725cedbeca31ae3aba3a9c39c5030ea1d20f.tar.gz
acron-609c725cedbeca31ae3aba3a9c39c5030ea1d20f.tar.bz2
acron-609c725cedbeca31ae3aba3a9c39c5030ea1d20f.zip
fix(libacron/acronc): complete cross-platform build
This patch made several changes to correctly build libac on Windows: 1. Deprecated PkgConfig on Unix. On both platforms, libac requires the JSON-C CMake module to locate libraries (whether is dynamic or static) and headers. On Windows, the common practice is to statically link the library, so libac now provides setup.bat to automatically clone JSON-C and build it. In CMakeLists.txt, libac will automatically pick up the in-tree JSON-C build if it presents. Otherwise, it will use the system default's. Users can override the CMAKE_PREFIX_PATH to supply a custom JSON-C library if it is not found in-tree. 2. Switched from #include <json-c/xxx.h> to <xxx.h>, as the former (current) approach is non-standard and depends on the system header locations. The later (new) approach relies on CMake to supply header search paths, which works on Windows as well. 3. Deprecated --version-script on Unix linkers. Instead, libac now uses -fvisibility (https://gcc.gnu.org/wiki/Visibility) on GCC or LLVM, as well as __declspec(dllexport) on MSVC to export selected symbols. A new macro, LIBAC_EXPORT, is defined in incl.h (new), and all functions needed to export are marked with that macro. 4. Changed several codes (mostly socket-related) to work on Windows. 5. Massive rewrite of CMake in preparation of further installation and static library support. Signed-off-by: Trumeet <yuuta@yuuta.moe>
-rw-r--r--client/libacron/.gitignore1
-rw-r--r--client/libacron/CMakeLists.txt68
-rw-r--r--client/libacron/README.md15
-rw-r--r--client/libacron/acronc/main.c22
-rw-r--r--client/libacron/include/ids.h4
-rw-r--r--client/libacron/include/incl.h14
-rw-r--r--client/libacron/include/libac.h7
-rw-r--r--client/libacron/include/net.h7
-rw-r--r--client/libacron/include/requests.h3
-rw-r--r--client/libacron/library.c1
-rw-r--r--client/libacron/net.c46
-rw-r--r--client/libacron/private/config.h3
-rw-r--r--client/libacron/private/connection.h7
-rw-r--r--client/libacron/private/helpers.c1
-rw-r--r--client/libacron/private/win32.h15
-rw-r--r--client/libacron/setup.bat20
16 files changed, 198 insertions, 36 deletions
diff --git a/client/libacron/.gitignore b/client/libacron/.gitignore
index 1bab38a..6741ba2 100644
--- a/client/libacron/.gitignore
+++ b/client/libacron/.gitignore
@@ -1,5 +1,6 @@
cmake-build-debug/
cmake-build-release/
+json-c/
# https://raw.githubusercontent.com/github/gitignore/main/Global/JetBrains.gitignore
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
diff --git a/client/libacron/CMakeLists.txt b/client/libacron/CMakeLists.txt
index ca9960e..db37671 100644
--- a/client/libacron/CMakeLists.txt
+++ b/client/libacron/CMakeLists.txt
@@ -3,41 +3,83 @@ project(libac C)
set(CMAKE_C_STANDARD 11)
-set(CMAKE_C_FLAGS_DEBUG
- "${CMAKE_C_FLAGS_DEBUG} -g3 -O0 -fsanitize=address -DDEBUG")
-set(CMAKE_EXE_LINKER_FLAGS_DEBUG
- "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address")
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE Debug)
+endif()
+
add_definitions(-D_POSIX_C_SOURCE=200809L)
+IF(CMAKE_BUILD_TYPE MATCHES Debug)
+ add_definitions(-DDEBUG)
+ENDIF(CMAKE_BUILD_TYPE MATCHES Debug)
-set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-Wl,--version-script='${CMAKE_SOURCE_DIR}/export.map'")
+if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ set(CMAKE_C_FLAGS_DEBUG
+ "${CMAKE_C_FLAGS_DEBUG} -g3 -O0 -fsanitize=address")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG
+ "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address")
+ set(CMAKE_C_FLAGS
+ "${CMAKE_C_FLAGS} -fvisibility=hidden")
+elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC")
+ # TODO: MSVC ASAN
+ set(CMAKE_C_FLAGS_DEBUG
+ "${CMAKE_C_FLAGS_DEBUG} /DEBUG /Z7 /Od")
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG
+ "${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
+endif()
-find_package(PkgConfig)
-pkg_check_modules(json-c REQUIRED json-c)
-add_library(ac SHARED
+set(LIBAC_PUBLIC_HEADERS
+ include/incl.h
include/libac.h
include/events.h
include/requests.h
include/common.h
include/net.h
include/ids.h
+ )
+
+set(LIBAC_PRIVATE
+ private/win32.h
private/config.h
- private/serializer.c
- private/helpers.h
+ private/serializer.h
private/helpers.c
private/serializer.h
- private/log.c
private/log.h
private/connection.h
wic/include/http_parser.h
wic/include/wic.h
+ private/serializer.c
+ private/helpers.c
+ private/log.c
wic/src/http_parser.c
wic/src/wic.c
net.c
ids.c
library.c
)
-target_include_directories(ac PUBLIC "${PROJECT_BINARY_DIR}" include/ private/ wic/include/)
-target_link_libraries(ac json-c)
+
+add_library(ac SHARED
+ ${LIBAC_PUBLIC_HEADERS}
+ ${LIBAC_PRIVATE}
+ )
+
+set(LIBAC_INCLUDES "${PROJECT_BINARY_DIR}" include/ private/ wic/include/)
+
+if (EXISTS ${CMAKE_SOURCE_DIR}/json-c/build/install)
+ message("Using in-tree JSON-C")
+ list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/json-c/build/install/lib/cmake/")
+else()
+ message("Using system-wide JSON-C")
+endif()
+
+if(WIN32)
+ set(LIBAC_DEPS ws2_32)
+endif()
+
+find_package(json-c CONFIG)
+set(LIBAC_DEPS ${LIBAC_DEPS} json-c::json-c)
+
+target_include_directories(ac PUBLIC ${LIBAC_INCLUDES})
+target_link_libraries(ac ${LIBAC_DEPS})
add_executable(acronc
acronc/main.c
diff --git a/client/libacron/README.md b/client/libacron/README.md
index 11d7663..8305a5d 100644
--- a/client/libacron/README.md
+++ b/client/libacron/README.md
@@ -23,7 +23,7 @@ cd client/libacron/
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
-make
+cmake --build .
```
The shared library will be at `libac.so`.
@@ -32,6 +32,19 @@ The distributable headers are at `client/libacron/include/`.
To make debug builds, use the Debug build type. Debug builds will have ASAN enabled.
+To build on Windows:
+
+```batch
+git clone https://git.yuuta.moe/Minecraft/acron.git
+CD acron
+git submodule update --init
+CD client/libacron/
+setup.bat
+mkdir build
+cmake -DCMAKE_BUILD_TYPE=Release ..
+cmake --build .
+```
+
## Usage
All functions begin with `ac_`. Include `libac.h`.
diff --git a/client/libacron/acronc/main.c b/client/libacron/acronc/main.c
index ef3bb38..21958fd 100644
--- a/client/libacron/acronc/main.c
+++ b/client/libacron/acronc/main.c
@@ -5,7 +5,10 @@
#include "libac.h"
#include <stdio.h>
-#include <unistd.h>
+#ifdef WIN32
+#include <winsock2.h>
+#include <windef.h>
+#endif
static const char *world_name(const enum ac_world world) {
switch (world) {
@@ -120,6 +123,20 @@ static void handle_response(const ac_response_t *response) {
int main(int argc, char **argv) {
int r;
+#ifdef WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ wVersionRequested = MAKEWORD(2, 2);
+ if ((r = WSAStartup(wVersionRequested, &wsaData))) {
+ fprintf(stderr, "WSAStartup failed with error: %d\n", r);
+ return r;
+ }
+ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
+ fprintf(stderr, "Could not find a usable version of Winsock.dll\n");
+ WSACleanup();
+ return 1;
+ }
+#endif
libac_config_t config = {
#ifdef DEBUG
.out = stderr,
@@ -165,5 +182,8 @@ int main(int argc, char **argv) {
}
ac_disconnect(connection);
ac_free();
+#ifdef WIN32
+ WSACleanup();
+#endif
return r;
} \ No newline at end of file
diff --git a/client/libacron/include/ids.h b/client/libacron/include/ids.h
index 80e7448..34d312e 100644
--- a/client/libacron/include/ids.h
+++ b/client/libacron/include/ids.h
@@ -5,6 +5,8 @@
#ifndef LIBAC_IDS_H
#define LIBAC_IDS_H
+#include "incl.h"
+
#define AC_TYPE_EVENT 0 /* 0b00000000 */
#define AC_TYPE_REQUEST 64 /* 0b01000000 */
#define AC_TYPE_RESPONSE 128 /* 0b10000000 */
@@ -26,6 +28,6 @@ typedef struct ac_obj {
* @param obj Object to free. Must be a valid object.
* @return AC_E_OK or an error code. When succeeds, obj is freed and no longer valid.
*/
-int ac_object_free(ac_obj_t *obj);
+LIBAC_EXPORT int ac_object_free(ac_obj_t *obj);
#endif /* LIBAC_IDS_H */
diff --git a/client/libacron/include/incl.h b/client/libacron/include/incl.h
new file mode 100644
index 0000000..4ac00ab
--- /dev/null
+++ b/client/libacron/include/incl.h
@@ -0,0 +1,14 @@
+/*
+ * Created by yuuta on 7/21/22.
+ */
+
+#ifndef LIBAC_INCL_H
+#define LIBAC_INCL_H
+
+#ifdef _MSC_VER
+#define LIBAC_EXPORT __declspec(dllexport)
+#else
+#define LIBAC_EXPORT __attribute__ ((visibility ("default"))) extern
+#endif
+
+#endif /* LIBAC_INCL_H */
diff --git a/client/libacron/include/libac.h b/client/libacron/include/libac.h
index 542adcb..adf0310 100644
--- a/client/libacron/include/libac.h
+++ b/client/libacron/include/libac.h
@@ -5,13 +5,14 @@
#ifndef LIBAC_LIBAC_H
#define LIBAC_LIBAC_H
+#include "incl.h"
#include "common.h"
#include "events.h"
#include "net.h"
#include "requests.h"
#include "ids.h"
-#include <json-c/json_tokener.h>
+#include <json_tokener.h>
#include <stdio.h>
typedef struct libac_config {
@@ -23,12 +24,12 @@ typedef struct libac_config {
* Initialize libac for the calling thread.
* @return AC_E_OK on success.
*/
-int ac_init(const libac_config_t *config);
+LIBAC_EXPORT int ac_init(const libac_config_t *config);
/**
* Destroy libac configuration for the calling thread.
* @return AC_E_OK on success.
*/
-int ac_free(void);
+LIBAC_EXPORT int ac_free(void);
#endif /* LIBAC_LIBAC_H */
diff --git a/client/libacron/include/net.h b/client/libacron/include/net.h
index 8fb33f8..6d8d9e7 100644
--- a/client/libacron/include/net.h
+++ b/client/libacron/include/net.h
@@ -5,6 +5,7 @@
#ifndef LIBAC_NET_H
#define LIBAC_NET_H
+#include "incl.h"
#include "common.h"
#include "events.h"
#include "requests.h"
@@ -27,7 +28,7 @@ typedef struct ac_connection_parameters {
* @return AC_E_OK or an error code. When failed, *out will be untouched.
* MT-Safe
*/
-int ac_connect(ac_connection_parameters_t parameters, void **out);
+LIBAC_EXPORT int ac_connect(ac_connection_parameters_t parameters, void **out);
/**
* Disconnect the connection.
@@ -35,7 +36,7 @@ int ac_connect(ac_connection_parameters_t parameters, void **out);
* @return AC_E_OK or an error code. When failed, connection is undefined. When succeeds, connection is freed and invalid.
* MT-Unsafe
*/
-int ac_disconnect(void *connection);
+LIBAC_EXPORT int ac_disconnect(void *connection);
/**
* Blocks the current thread until a new response or event arrives.
@@ -43,6 +44,6 @@ int ac_disconnect(void *connection);
* @param response Output response of either an event or a response. May be NULL even if it succeeds.
* @return AC_E_OK or an error code. When failed, *response is NULL.
*/
-int ac_receive(void *connection, ac_obj_t **response);
+LIBAC_EXPORT int ac_receive(void *connection, ac_obj_t **response);
#endif /* LIBAC_NET_H */
diff --git a/client/libacron/include/requests.h b/client/libacron/include/requests.h
index 590bb2e..54c414b 100644
--- a/client/libacron/include/requests.h
+++ b/client/libacron/include/requests.h
@@ -5,6 +5,7 @@
#ifndef LIBAC_REQUESTS_H
#define LIBAC_REQUESTS_H
+#include "incl.h"
#include "common.h"
#include "ids.h"
#include <stdbool.h>
@@ -74,6 +75,6 @@ typedef struct ac_request_set_config {
* @return AC_E_OK or an error code.
* MT-Unsafe: Must be called on the same thread calling ac_receive().
*/
-int ac_request(void *connection, const ac_request_t *request);
+LIBAC_EXPORT int ac_request(void *connection, const ac_request_t *request);
#endif /* LIBAC_REQUESTS_H */
diff --git a/client/libacron/library.c b/client/libacron/library.c
index d401fed..c014ed9 100644
--- a/client/libacron/library.c
+++ b/client/libacron/library.c
@@ -1,3 +1,4 @@
+#include "win32.h"
#include "libac.h"
#include "config.h"
#include "helpers.h"
diff --git a/client/libacron/net.c b/client/libacron/net.c
index cab4625..a0e8659 100644
--- a/client/libacron/net.c
+++ b/client/libacron/net.c
@@ -2,6 +2,7 @@
* Created by yuuta on 7/13/22.
*/
+#include "win32.h"
#include "config.h"
#include "net.h"
#include "connection.h"
@@ -15,11 +16,17 @@
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netdb.h>
#include <unistd.h>
+#endif
static void on_open_handler(struct wic_inst *inst) {
struct ac_connection *conn = wic_get_app(inst);
@@ -32,12 +39,13 @@ static void on_send_handler(struct wic_inst *inst,
enum wic_buffer type) {
struct ac_connection *conn = wic_get_app(inst);
size_t pos;
- ssize_t retval;
+ const uint8_t *ptr = data;
+ int retval;
for (pos = 0U; pos < size; pos += retval) {
size_t n = size - pos;
LOGDV("write(%d, %p[%u, %u])", conn->fd, data, pos, n);
- retval = write(conn->fd, &data[pos], (int) n);
+ retval = (int) send(conn->fd, &ptr[pos], (int) n, 0);
if (retval <= 0) {
/* There's no way to abort the process. */
int e = errno;
@@ -154,13 +162,18 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) {
}
if ((r = getaddrinfo(host, service, NULL, &res))) {
+ /* gai_strerror() seems to cause an linkage error. */
+#ifdef WIN32
+ LOGEV("Resolve host: %d.", r);
+#else
LOGEV("Resolve host: %s.", gai_strerror(r));
+#endif
r = AC_E_NET;
free(conn);
return r;
}
- int fd;
+ SOCKET fd;
if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) {
const int e = errno;
LOGEV("Cannot create socket: %s (%d).",
@@ -171,11 +184,17 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) {
return AC_E_NET;
}
if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+#ifdef WIN32
+ const int e = WSAGetLastError();
+ LOGEV("Cannot connect to socket: %d.",
+ e);
+#else
const int e = errno;
LOGEV("Cannot connect to socket: %s (%d).",
strerror2(e),
e);
- close(fd);
+#endif
+ closesocket(fd);
freeaddrinfo(res);
free(conn);
return AC_E_NET;
@@ -185,7 +204,7 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) {
conn->fd = fd;
if (wic_start(inst) != WIC_STATUS_SUCCESS) {
LOGE("Cannot start the WIC client.");
- close(fd);
+ closesocket(fd);
free(conn);
return AC_E_NET;
}
@@ -193,7 +212,7 @@ int ac_connect(ac_connection_parameters_t parameters, void **out) {
/* Wait until established (ready to send). */
while (!conn->established) {
ac_obj_t *obj = NULL;
- r = ac_receive(conn, &obj);
+ r = ac_receive(conn, &obj, 0);
if (obj) {
LOGW("Received an object before connection is established. Dropping.");
ac_object_free(obj);
@@ -216,7 +235,7 @@ int ac_disconnect(void *connection) {
}
LOGD("Disconnecting...");
wic_close(&conn->inst);
- close(conn->fd);
+ closesocket(conn->fd);
free(conn);
return AC_E_OK;
}
@@ -227,13 +246,18 @@ int ac_receive(void *connection, ac_obj_t **response) {
struct wic_inst *inst = &conn->inst;
static uint8_t buffer[1000U];
- ssize_t bytes;
+ int bytes;
size_t retval, pos;
- if ((bytes = recv(conn->fd, buffer, sizeof(buffer), 0)) <= 0) {
+ if ((bytes = (int) recv(conn->fd, buffer, sizeof(buffer), 0)) <= 0) {
LOGDV("recv(%d) = %d", conn->fd, bytes);
if (bytes < 0) {
const int e = errno;
+ if (e == EAGAIN) {
+ /* Timeout */
+ *response = NULL;
+ return AC_E_OK;
+ }
LOGEV("Failed to receive from socket : %s (%d).",
strerror2(e),
e);
diff --git a/client/libacron/private/config.h b/client/libacron/private/config.h
index 3b670df..df907a0 100644
--- a/client/libacron/private/config.h
+++ b/client/libacron/private/config.h
@@ -5,9 +5,10 @@
#ifndef LIBAC_CONFIG_H
#define LIBAC_CONFIG_H
+#include "win32.h"
#include "libac.h"
-#include <json-c/json_tokener.h>
+#include <json_tokener.h>
#include <stdio.h>
extern _Thread_local struct libac_config *config;
diff --git a/client/libacron/private/connection.h b/client/libacron/private/connection.h
index ad65b11..04d7639 100644
--- a/client/libacron/private/connection.h
+++ b/client/libacron/private/connection.h
@@ -5,10 +5,15 @@
#ifndef LIBAC_CONNECTION_H
#define LIBAC_CONNECTION_H
+#include "win32.h"
#include "wic.h"
#include "libac.h"
#include <stdbool.h>
+#ifdef WIN32
+#include <winsock2.h>
+#endif
+
/**
* Used to transfer deserialization result from receive handler to ac_receive().
*/
@@ -24,7 +29,7 @@ struct ac_result {
struct ac_connection {
ac_connection_parameters_t parameters;
struct wic_inst inst;
- int fd;
+ SOCKET fd;
bool established;
/* Result from message handler.
* Message handling will happen at the same thread as ac_receive (lib wic). */
diff --git a/client/libacron/private/helpers.c b/client/libacron/private/helpers.c
index 4d4f3c8..86d65b6 100644
--- a/client/libacron/private/helpers.c
+++ b/client/libacron/private/helpers.c
@@ -2,6 +2,7 @@
* Created by yuuta on 7/19/22.
*/
+#include "win32.h"
#include "helpers.h"
#include "log.h"
#include "common.h"
diff --git a/client/libacron/private/win32.h b/client/libacron/private/win32.h
new file mode 100644
index 0000000..07cdca8
--- /dev/null
+++ b/client/libacron/private/win32.h
@@ -0,0 +1,15 @@
+/*
+ * Created by yuuta on 7/21/22.
+ */
+
+#ifndef LIBAC_WIN32_H
+#define LIBAC_WIN32_H
+
+#ifdef WIN32
+#define _Thread_local __declspec( thread )
+#else
+#define SOCKET int
+#define closesocket(socket) close(socket)
+#endif
+
+#endif /* LIBAC_WIN32_H */
diff --git a/client/libacron/setup.bat b/client/libacron/setup.bat
new file mode 100644
index 0000000..52f33d1
--- /dev/null
+++ b/client/libacron/setup.bat
@@ -0,0 +1,20 @@
+@ECHO OFF
+ECHO Setting up Windows builds ...
+
+git clone --depth 1 --branch json-c-0.16 https://github.com/json-c/json-c.git json-c
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+MKDIR json-c\build\
+if %errorlevel% neq 0 exit /b %errorlevel%
+MKDIR json-c\build\install\
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+CD json-c\build\
+
+cmake -DCMAKE_INSTALL_PREFIX=install -DCMAKE_BUILD_TYPE=release -DBUILD_SHARED_LIBS=OFF ..
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+cmake --build . --target install
+if %errorlevel% neq 0 exit /b %errorlevel%
+
+CD ..\..\ \ No newline at end of file