some infra
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
.env
|
||||
build/
|
||||
.DS_Store
|
||||
config.json
|
||||
+69
-11
@@ -388,6 +388,43 @@ else()
|
||||
# which is before Concord has been built.
|
||||
file(MAKE_DIRECTORY "${CONCORD_INCLUDE_DIR}")
|
||||
|
||||
# Upstream's `make install` is three shell globs relative to the working
|
||||
# directory, fed to install(1). When one matches nothing the shell passes it
|
||||
# through literally and install reports `cannot stat 'include/*.h'`, which
|
||||
# says nothing about which glob mattered or why. Do the copy ourselves with
|
||||
# absolute paths and name the two failure modes that actually occur.
|
||||
#
|
||||
# The globs run at install time rather than configure time because
|
||||
# gencodecs/discord_codecs.h does not exist until the build has generated it.
|
||||
set(CONCORD_INSTALL_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/concord_install.cmake")
|
||||
file(CONFIGURE
|
||||
OUTPUT "${CONCORD_INSTALL_SCRIPT}"
|
||||
@ONLY
|
||||
CONTENT [[
|
||||
file(MAKE_DIRECTORY "@CONCORD_INCLUDE_DIR@/concord" "@CONCORD_PREFIX@/lib")
|
||||
|
||||
# Upstream flattens all three header directories into one include/concord. Check
|
||||
# them one at a time: a single empty directory is the interesting failure, and a
|
||||
# combined glob would hide it behind whichever siblings still matched.
|
||||
set(_hdrs "")
|
||||
foreach(_dir include core gencodecs)
|
||||
file(GLOB _found "@concord_SOURCE_DIR@/${_dir}/*.h")
|
||||
if(NOT _found)
|
||||
message(FATAL_ERROR
|
||||
"Concord: no headers in @concord_SOURCE_DIR@/${_dir} -- the source "
|
||||
"tree is incomplete; remove build/_deps and reconfigure to re-clone")
|
||||
endif()
|
||||
list(APPEND _hdrs ${_found})
|
||||
endforeach()
|
||||
file(COPY ${_hdrs} DESTINATION "@CONCORD_INCLUDE_DIR@/concord")
|
||||
|
||||
file(GLOB _libs "@concord_SOURCE_DIR@/lib/libdiscord.*")
|
||||
if(NOT _libs)
|
||||
message(FATAL_ERROR "Concord: build produced no library in @concord_SOURCE_DIR@/lib")
|
||||
endif()
|
||||
file(COPY ${_libs} DESTINATION "@CONCORD_PREFIX@/lib")
|
||||
]])
|
||||
|
||||
# Concord gets our instrumentation but not our warning set: ASan only sees a
|
||||
# bug if the translation unit that owns the memory was compiled with it, and
|
||||
# Concord allocates plenty that our code then touches. CFLAGS goes through
|
||||
@@ -396,30 +433,43 @@ else()
|
||||
# override those and break the build.
|
||||
set(CONCORD_CFLAGS "-O2 -g $<JOIN:${HAMMY_INSTRUMENT_COMPILE}, >")
|
||||
|
||||
# gencodecs/Makefile hardcodes CC/HOSTCC/CPP to "cc" for its host-side code
|
||||
# generator, with plain '=' assignments that the environment cannot override.
|
||||
# On a system where cc is GCC and we build with Clang, the generator is then
|
||||
# handed our Clang-only flags (-fno-sanitize=function) and dies. Command-line
|
||||
# assignments do beat makefile assignments, so force the compiler there --
|
||||
# while leaving CFLAGS in the environment, per the note above.
|
||||
#
|
||||
# gencodecs-pp is a build-time text filter, run as `cpp ... | ./gencodecs-pp`.
|
||||
# ASan's exit-time leak check would turn a leak in that throwaway tool into a
|
||||
# build failure reported as a broken pipeline, so switch it off for the
|
||||
# duration of Concord's build only; our own binary is unaffected.
|
||||
ExternalProject_Add(concord_external
|
||||
SOURCE_DIR "${concord_SOURCE_DIR}"
|
||||
DOWNLOAD_COMMAND "" # FetchContent already cloned it
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_IN_SOURCE TRUE # upstream's Makefile has no out-of-tree mode
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_C_COMPILER}" "CFLAGS=${CONCORD_CFLAGS}"
|
||||
# Upstream tracks nothing we can express as a byproduct, so the stamp
|
||||
# would happily report "built" after a `make clean` emptied lib/.
|
||||
BUILD_ALWAYS TRUE
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E env
|
||||
"CC=${CMAKE_C_COMPILER}"
|
||||
"CFLAGS=${CONCORD_CFLAGS}"
|
||||
"ASAN_OPTIONS=detect_leaks=0"
|
||||
${HAMMY_MAKE_EXECUTABLE}
|
||||
INSTALL_COMMAND ${HAMMY_MAKE_EXECUTABLE} install "PREFIX=${CONCORD_PREFIX}"
|
||||
"CC=${CMAKE_C_COMPILER}"
|
||||
"HOSTCC=${CMAKE_C_COMPILER}"
|
||||
"CPP=${CMAKE_C_COMPILER} -E"
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} -P "${CONCORD_INSTALL_SCRIPT}"
|
||||
BUILD_BYPRODUCTS "${CONCORD_LIBRARY}"
|
||||
USES_TERMINAL_BUILD TRUE
|
||||
# USES_TERMINAL_BUILD would silently disable LOG_BUILD; with
|
||||
# LOG_OUTPUT_ON_FAILURE we get the full log exactly when it matters.
|
||||
LOG_BUILD TRUE
|
||||
LOG_INSTALL TRUE
|
||||
LOG_OUTPUT_ON_FAILURE TRUE
|
||||
)
|
||||
|
||||
ExternalProject_Add_Step(concord_external mirror_headers
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
"${CONCORD_INCLUDE_DIR}/concord"
|
||||
"${PROJECT_SOURCE_DIR}/include/concord"
|
||||
DEPENDEES install
|
||||
COMMENT "Mirroring Concord headers into include/concord"
|
||||
)
|
||||
|
||||
add_library(concord::concord STATIC IMPORTED GLOBAL)
|
||||
set_target_properties(concord::concord PROPERTIES
|
||||
IMPORTED_LOCATION "${CONCORD_LIBRARY}"
|
||||
@@ -456,6 +506,14 @@ target_include_directories(hammy PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
)
|
||||
|
||||
target_include_directories(hammy SYSTEM PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/third_party
|
||||
)
|
||||
|
||||
# Concord's headers arrive through concord::concord. CMake passes an imported
|
||||
# target's interface includes as -isystem, so upstream's headers never trip our
|
||||
# warning set and no second copy under third_party/ is needed.
|
||||
|
||||
target_link_libraries(hammy PRIVATE concord::concord)
|
||||
if(HAMMY_CONCORD_EXTERNAL)
|
||||
add_dependencies(hammy ${HAMMY_CONCORD_EXTERNAL})
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef HAMMY_BOT_H
|
||||
#define HAMMY_BOT_H
|
||||
|
||||
#include <concord/discord.h>
|
||||
#include <dlibc/vector.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <hammy/command.h>
|
||||
|
||||
typedef struct {
|
||||
struct discord* client; // Owning reference to the client - handoff from main.c
|
||||
bool commands_registered; // A flag if commands have been registered. Avoid re-registering every reconnect.
|
||||
vector_t* commands; // vector_t of commands. Owning.
|
||||
} hammy_bot_t;
|
||||
|
||||
// Alloc on heap and create
|
||||
hammy_bot_t* hammy_bot_create();
|
||||
|
||||
// Sets the on_ready() call function pointer
|
||||
bool hammy_bot_set_on_ready(hammy_bot_t* bot, void (*func)(struct discord* client, const struct discord_ready* event));
|
||||
|
||||
// Runs the bot. Return true on succeed, false on failure.
|
||||
bool hammy_bot_run(hammy_bot_t* bot);
|
||||
|
||||
// Adds a command to the vector as a COPY, get rid of the old struct.
|
||||
bool hammy_bot_add_command(hammy_bot_t* bot, hammy_command_t* command);
|
||||
|
||||
// Registers all commands. Return true if succeeded or already registered; false if failure.
|
||||
bool hammy_bot_register_commands(hammy_bot_t* bot);
|
||||
|
||||
// Destroy bot, NULLs the passing reference
|
||||
bool hammy_bot_destroy(hammy_bot_t** bot);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef HAMMY_COMMAND_H
|
||||
#define HAMMY_COMMAND_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct {
|
||||
// TODO: add shit here
|
||||
char empty;
|
||||
} hammy_command_t;
|
||||
|
||||
// Creates the command on the heap, returns NULL if failed.
|
||||
hammy_command_t* hammy_command_create();
|
||||
|
||||
// Destroys a command.
|
||||
void hammy_command_destroy(void* cmd);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#include <hammy/bot.h>
|
||||
|
||||
hammy_bot_t* hammy_bot_create() {
|
||||
hammy_bot_t* bot = (hammy_bot_t*)malloc(sizeof(hammy_bot_t));
|
||||
if (!bot) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
bot->client = NULL;
|
||||
bot->commands_registered = false;
|
||||
bot->commands = vector_create(sizeof(hammy_command_t));
|
||||
|
||||
// Check if vector allocation errored
|
||||
if (!bot->commands) {
|
||||
free(bot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (vector_set_destructor(bot->commands, &hammy_command_destroy) < 0) {
|
||||
vector_destroy(&bot->commands);
|
||||
free(bot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Init the discord client - even though this is an owning reference, the main() and others may call functions on it (if we don't stay singlethread anymore then FIX THIS)
|
||||
bot->client = discord_from_json("config.json");
|
||||
if (!bot->client) {
|
||||
vector_destroy(&bot->commands);
|
||||
free(bot);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return bot;
|
||||
}
|
||||
|
||||
bool hammy_bot_set_on_ready(hammy_bot_t* bot, void (*func)(struct discord* client, const struct discord_ready* event)) {
|
||||
if (!bot || !func || !bot->client) { return false; }
|
||||
|
||||
discord_set_on_ready(bot->client, func);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hammy_bot_run(hammy_bot_t* bot) {
|
||||
if (!bot || !bot->client) { return false; }
|
||||
|
||||
discord_run(bot->client);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hammy_bot_add_command(hammy_bot_t* bot, hammy_command_t* command) {
|
||||
if (!bot || !command) { return false; }
|
||||
|
||||
// Push into commands vector
|
||||
if (vector_push_back(bot->commands, command) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool hammy_bot_register_commands(hammy_bot_t* bot) {
|
||||
if (!bot) { return false; }
|
||||
|
||||
// TODO: Figure out registration logic
|
||||
}
|
||||
|
||||
bool hammy_bot_destroy(hammy_bot_t** bot) {
|
||||
if (!bot || !(*bot)) { return false; }
|
||||
|
||||
if ((*bot)->client) {
|
||||
discord_cleanup((*bot)->client);
|
||||
}
|
||||
|
||||
// TODO: Potential other stuff here
|
||||
vector_destroy(&(*bot)->commands); // commands is NULL form hereon, calls destructors on commands automatically
|
||||
free(*bot);
|
||||
*bot = NULL;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <hammy/command.h>
|
||||
|
||||
hammy_command_t* hammy_command_create() {
|
||||
hammy_command_t* cmd = (hammy_command_t*)malloc(sizeof(hammy_command_t));
|
||||
if (!cmd) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO: set defaults
|
||||
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void hammy_command_destroy(void* cmd) {
|
||||
if (!cmd) {
|
||||
return;
|
||||
}
|
||||
|
||||
cmd = (hammy_command_t*)cmd;
|
||||
|
||||
// TODO: Other things here eventually
|
||||
|
||||
free(cmd);
|
||||
}
|
||||
+28
-6
@@ -1,21 +1,43 @@
|
||||
#include <string.h>
|
||||
#include <concord/discord.h>
|
||||
#include <signal.h>
|
||||
#include <concord/log.h>
|
||||
|
||||
#include <hammy/bot.h>
|
||||
|
||||
static void on_signal(int sig) {
|
||||
(void)sig;
|
||||
discord_shutdown_all();
|
||||
}
|
||||
|
||||
void on_ready(struct discord* client, const struct discord_ready* event) {
|
||||
(void)client;
|
||||
log_info("Logged in as %s", event->user->username);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct sigaction sa = { 0 };
|
||||
sa.sa_handler = &on_signal;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0; /* deliberately no SA_RESTART */
|
||||
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
|
||||
ccord_global_init();
|
||||
|
||||
struct discord* client = discord_config_init("config.json");
|
||||
hammy_bot_t* bot = hammy_bot_create();
|
||||
if (!bot) {
|
||||
log_error("Hammy Bot creation returned NULL! Bailing!");
|
||||
ccord_global_cleanup();
|
||||
|
||||
discord_set_on_ready(client, &on_ready);
|
||||
return 1;
|
||||
}
|
||||
|
||||
hammy_bot_set_on_ready(bot, &on_ready);
|
||||
hammy_bot_run(bot); // This will block and exit afterwards
|
||||
|
||||
hammy_bot_destroy(&bot);
|
||||
|
||||
discord_run(client);
|
||||
|
||||
discord_cleanup(client);
|
||||
ccord_global_cleanup();
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user