686 lines
20 KiB
C
686 lines
20 KiB
C
// x-run: tcc -Wextra -Wall -run cbt.c
|
|
#ifndef _CBT_H_
|
|
#define _CBT_H_
|
|
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
|
|
enum cbt_loglevel {
|
|
CBT_LOG_FATAL,
|
|
CBT_LOG_ERROR,
|
|
CBT_LOG_WARNING,
|
|
CBT_LOG_INFO,
|
|
CBT_LOG_DEBUG,
|
|
CBT_LOG_ALL
|
|
};
|
|
|
|
struct cbt_strarr {
|
|
char **items;
|
|
size_t size, cap;
|
|
};
|
|
|
|
extern long cbt_start_time;
|
|
extern enum cbt_loglevel cbt_verbosity;
|
|
extern char *cbt_cc, *cbt_cache_dir;
|
|
|
|
void cbt__init(int argc, char **argv, const char *source_file);
|
|
void cbt_cleanup(void);
|
|
#define CBT_INIT(ARGC, ARGV) cbt__init(ARGC, ARGV, __FILE__);
|
|
|
|
extern const char *cbt_log__colors[CBT_LOG_ALL + 1];
|
|
extern const char *cbt_log__typecolors[8];
|
|
|
|
void cbt_log(enum cbt_loglevel lvl, const char *fmt, ...);
|
|
|
|
long cbt_get_time(void);
|
|
|
|
unsigned long cbt_get_modtime(const char *filename);
|
|
bool cbt_needs_recompilation(const char *input, const char *output);
|
|
|
|
const char *cbt_escape_shell(char *arg);
|
|
const char *cbt_escape_args(char **args);
|
|
const char *cbt_escape_argsd(struct cbt_strarr arr);
|
|
|
|
#ifndef CBT_FMT_NBUFS
|
|
#define CBT_FMT_NBUFS 64
|
|
#endif
|
|
|
|
#ifndef CBT_FMT_BUFSIZE
|
|
#define CBT_FMT_BUFSIZE 1024
|
|
#endif
|
|
|
|
const char *cbt_fmt(const char *fmt, ...);
|
|
|
|
struct cbt_proc {
|
|
FILE *fp_stdin, *fp_stdout, *fp_stderr;
|
|
int pid;
|
|
};
|
|
|
|
struct cbt_procgroup {
|
|
struct cbt_proc *items;
|
|
size_t size, cap;
|
|
bool fixed_size;
|
|
};
|
|
|
|
enum cbt_proc_mode {
|
|
CBT_PROC_AUTO = 0,
|
|
CBT_PROC_R = 1,
|
|
CBT_PROC_W = 2,
|
|
CBT_PROC_RW = 3,
|
|
};
|
|
|
|
#define cbt_proc_new(...) _cbt_proc_new(__VA_ARGS__, NULL)
|
|
#define cbt_shell(...) cbt_proc_wait(_cbt_proc_new(__VA_ARGS__, NULL))
|
|
struct cbt_proc _cbt_proc_new(enum cbt_proc_mode mode, ...);
|
|
struct cbt_proc cbt_proc_newv(enum cbt_proc_mode mode, va_list args);
|
|
struct cbt_proc cbt_proc_newd(enum cbt_proc_mode mode, struct cbt_strarr args);
|
|
int cbt_proc_wait(struct cbt_proc proc);
|
|
|
|
enum cbt_libtype { CBT_LIB_STATIC, CBT_LIB_SHARED };
|
|
|
|
struct cbt_lib {
|
|
enum cbt_libtype type;
|
|
const char *libname;
|
|
struct cbt_strarr sources, cflags, ldflags;
|
|
};
|
|
|
|
#define cbt_lib_src(LIB, ...) _cbt_lib_src(LIB, __VA_ARGS__, NULL)
|
|
#define cbt_lib_cflags(LIB, ...) _cbt_lib_cflags(LIB, __VA_ARGS__, NULL)
|
|
#define cbt_lib_ldflags(LIB, ...) _cbt_lib_ldflags(LIB, __VA_ARGS__, NULL)
|
|
struct cbt_lib cbt_lib(const char *libname);
|
|
void _cbt_lib_src(struct cbt_lib *lib, ...);
|
|
void _cbt_lib_cflags(struct cbt_lib *lib, ...);
|
|
void _cbt_lib_ldflags(struct cbt_lib *lib, ...);
|
|
bool cbt_lib_build(struct cbt_lib lib, const char *cc);
|
|
void cbt_lib_free(struct cbt_lib lib);
|
|
|
|
struct cbt_binary {
|
|
const char *out_path;
|
|
struct cbt_strarr sources, cflags, ldflags;
|
|
};
|
|
|
|
#define cbt_binary_add_src(B, ...) _cbt_binary_add_src(B, __VA_ARGS__, NULL)
|
|
#define cbt_binary_build(B, CC, ...) _cbt_binary_build(B, CC, __VA_ARGS__, NULL)
|
|
struct cbt_binary cbt_binary(const char *out_path);
|
|
void _cbt_binary_add_src(struct cbt_binary bin, ...);
|
|
bool _cbt_binary_build(struct cbt_binary bin, const char *compiler, ...);
|
|
void cbt_binary_free(struct cbt_binary bin);
|
|
|
|
#define CBT_ARRLEN(ARR) (sizeof(ARR) / sizeof(ARR[0]))
|
|
|
|
#ifndef CBT_REALLOC
|
|
#define CBT_REALLOC realloc
|
|
#endif
|
|
|
|
#define CBT_DA_INIT_SIZE 256
|
|
|
|
#define cbt_da_add(ARR, ITEM) \
|
|
{ \
|
|
if (ARR.size >= ARR.cap) { \
|
|
size_t size = ARR.size == 0 ? CBT_DA_INIT_SIZE : ARR.size * 2; \
|
|
ARR.items = CBT_REALLOC(ARR.items, size * sizeof(ARR.items[0])); \
|
|
} \
|
|
ARR.items[ARR.size] = (ITEM); \
|
|
ARR.size++; \
|
|
}
|
|
|
|
#define cbt_da_remove(ARR, I) \
|
|
if (I >= 0 && I < ARR.size) { \
|
|
memmove(&ARR.items[I], &ARR.items[I + 1], \
|
|
(ARR.size - 1) * sizeof((ARR).items[0])); \
|
|
ARR.size--; \
|
|
}
|
|
|
|
#define cbt_da_merge(OUT, IN) \
|
|
{ \
|
|
OUT.cap = OUT.size + IN.size; \
|
|
OUT.items = realloc(OUT.items, sizeof(OUT.items[0]) * OUT.cap); \
|
|
size_t offset = OUT.size; \
|
|
OUT.size = OUT.size + IN.size; \
|
|
memmove(&OUT.items[offset], IN.items, sizeof(OUT.items[0]) * IN.size); \
|
|
}
|
|
|
|
#define cbt_da_copy(OUT, IN, T) \
|
|
{ \
|
|
OUT.size = IN.size; \
|
|
OUT.cap = IN.size; \
|
|
OUT.items = malloc(sizeof(IN.items[0]) * IN.size); \
|
|
memcpy(OUT.items, IN.items, sizeof(IN.items[0]) * IN.size); \
|
|
}
|
|
|
|
#ifdef CBT_IMPLEMENTATION
|
|
//-*- begin cbt_impl.c 5
|
|
#define __USE_GNU
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <glob.h>
|
|
#include <libgen.h>
|
|
#ifndef __APPLE__
|
|
#include <linux/limits.h>
|
|
#endif
|
|
#include <poll.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define CBT_FAIL(COND) \
|
|
if (COND) { \
|
|
cbt_log(CBT_LOG_FATAL, "condition %s failed, %s", #COND, strerror(errno)); \
|
|
abort(); \
|
|
};
|
|
|
|
#define CBT_TODO(NAME) \
|
|
{ \
|
|
cbt_log(CBT_LOG_FATAL, "%s: Not implemented! %s:%d", NAME, __FILE__, \
|
|
__LINE__); \
|
|
abort(); \
|
|
}
|
|
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX 1024
|
|
#endif
|
|
|
|
enum cbt_loglevel cbt_verbosity = CBT_LOG_INFO;
|
|
char *cbt_cc = NULL;
|
|
char cbt_location[PATH_MAX] = {0};
|
|
char *cbt_cache_dir = NULL;
|
|
long cbt_start_time = 0;
|
|
bool cbt_running = false;
|
|
|
|
struct _cbt__autoproc_fdset {
|
|
struct pollfd *items;
|
|
size_t size, cap;
|
|
} cbt__autoproc_fdset = {0};
|
|
|
|
struct _cbt__autoproc_set {
|
|
struct _cbt__autoproc_set__item {
|
|
FILE *fp;
|
|
bool is_err;
|
|
} *items;
|
|
size_t size, cap;
|
|
} cbt__autoproc_set = {0};
|
|
|
|
struct cbt_procgroup cbt__proc_pool = {0, .cap = 4, .fixed_size = 1};
|
|
|
|
pthread_t cbt__autoproc_thread;
|
|
pthread_mutex_t _cbt__autoproc_mut = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
const char *cbt_log__colors[CBT_LOG_ALL + 1] = {
|
|
"\x1b[1;31m", "\x1b[31m", "\x1b[33m", "\x1b[32m", "\x1b[34m", "\x1b[35m"};
|
|
|
|
const char *cbt_log__text[CBT_LOG_ALL + 1] = {"FATAL", "ERROR", "WARN ",
|
|
"INFO ", "DEBUG", "TRACE"};
|
|
void *cbt__line_processor(void *);
|
|
|
|
void cbt__init(int argc, char **argv, const char *source_file) {
|
|
cbt_start_time = cbt_get_time();
|
|
cbt_running = true;
|
|
(void)argc;
|
|
|
|
cbt_log(CBT_LOG_INFO, "Running CBT build %s %s", __DATE__, __TIME__);
|
|
|
|
if (!cbt_cc)
|
|
cbt_cc = getenv("CC");
|
|
if (!cbt_cc)
|
|
cbt_cc = "cc";
|
|
|
|
cbt_log(CBT_LOG_INFO, "Found C Compiler: %s", cbt_cc);
|
|
|
|
CBT_FAIL(readlink("/proc/self/exe", cbt_location, PATH_MAX) == -1);
|
|
cbt_log(CBT_LOG_INFO, "Location: %s", cbt_location);
|
|
|
|
char *cbt_cache_dir = strdup(cbt_location);
|
|
cbt_cache_dir = strdup(cbt_fmt("%s/%s", dirname(cbt_cache_dir), "cache"));
|
|
|
|
cbt_log(CBT_LOG_DEBUG, "Args: %s", cbt_escape_args(argv));
|
|
cbt_log(CBT_LOG_DEBUG, "%s", cbt_fmt("format test: %s", "ok"));
|
|
|
|
cbt_log(CBT_LOG_INFO, "Cache dir: %s", cbt_cache_dir);
|
|
|
|
cbt__proc_pool.items = calloc(cbt__proc_pool.cap, sizeof(struct cbt_proc));
|
|
|
|
{
|
|
cbt_log(CBT_LOG_DEBUG, "Starting line processor thread");
|
|
int err;
|
|
if ((err = pthread_create(&cbt__autoproc_thread, NULL, cbt__line_processor,
|
|
NULL)) != 0) {
|
|
cbt_log(CBT_LOG_ERROR, "pthread_create() failed for line processor: %d",
|
|
err);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (cbt_needs_recompilation(source_file, argv[0])) {
|
|
cbt_log(CBT_LOG_INFO, "Recompiling...");
|
|
struct cbt_proc cc = cbt_proc_new(CBT_PROC_AUTO, cbt_cc, source_file, "-o",
|
|
argv[0], "-Wall", "-Wextra", NULL);
|
|
int status = cbt_proc_wait(cc);
|
|
cbt_log(CBT_LOG_INFO, "CC returned %d", status);
|
|
if (status == 0) {
|
|
cbt_cleanup();
|
|
cbt_log(CBT_LOG_INFO, "Restarting CBT", cbt_escape_args(argv));
|
|
execv(argv[0], argv);
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cbt_cleanup(void) {
|
|
usleep(50000);
|
|
pthread_mutex_lock(&_cbt__autoproc_mut);
|
|
cbt_running = false;
|
|
cbt_log(CBT_LOG_INFO, "cbt shutting down");
|
|
pthread_join(cbt__autoproc_thread, NULL);
|
|
pthread_mutex_unlock(&_cbt__autoproc_mut);
|
|
cbt_log(CBT_LOG_INFO, "shutdown complete");
|
|
}
|
|
|
|
const char *cbt_fmt(const char *fmt, ...) {
|
|
static char cbt_fmt_buffers[CBT_FMT_NBUFS][CBT_FMT_BUFSIZE];
|
|
static int cbt_fmt_buffer = 0;
|
|
char *buffer = cbt_fmt_buffers[cbt_fmt_buffer++ % CBT_FMT_NBUFS];
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(buffer, CBT_FMT_BUFSIZE - 1, fmt, args);
|
|
va_end(args);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
long cbt_get_time(void) {
|
|
struct timespec now;
|
|
clock_gettime(CLOCK_REALTIME, &now);
|
|
return (long)(now.tv_sec * 1000000L) + (long)(now.tv_nsec / 1000L);
|
|
}
|
|
|
|
void cbt_log(enum cbt_loglevel lvl, const char *fmt, ...) {
|
|
if (lvl > cbt_verbosity)
|
|
return;
|
|
|
|
double time_delta = (double)(cbt_get_time() - cbt_start_time) / 1000.0L;
|
|
printf("[%10.3lf] [%s%s\x1b[0m] ", time_delta, cbt_log__colors[lvl],
|
|
cbt_log__text[lvl]);
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
// TODO: find a better option
|
|
const char *cbt_escape_shell(char *arg) {
|
|
int n = strlen(arg);
|
|
|
|
char *esc = (char *)cbt_fmt(""); // just take a buffer
|
|
|
|
esc[0] = '\'';
|
|
int j = 1, i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
if (arg[i] == '\'') {
|
|
if (j + 4 >= CBT_FMT_BUFSIZE) {
|
|
return NULL;
|
|
}
|
|
esc[j] = '\'';
|
|
esc[j + 1] = '\\';
|
|
esc[j + 2] = '\'';
|
|
esc[j + 3] = '\'';
|
|
j += 4;
|
|
} else {
|
|
if (j >= CBT_FMT_BUFSIZE) {
|
|
return NULL;
|
|
}
|
|
esc[j++] = arg[i];
|
|
}
|
|
}
|
|
|
|
if (j >= CBT_FMT_BUFSIZE) {
|
|
return NULL;
|
|
}
|
|
esc[j] = '\'';
|
|
|
|
return esc;
|
|
}
|
|
|
|
const char *cbt_escape_args(char **args) {
|
|
if (args == NULL || args[0] == NULL)
|
|
return cbt_fmt("");
|
|
char *out = (char *)cbt_escape_shell(args[0]);
|
|
for (int i = 1; args[i] != NULL; i++) {
|
|
out = (char *)cbt_fmt("%s %s", out, cbt_escape_shell(args[i]));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
const char *cbt_escape_argsd(struct cbt_strarr arr) {
|
|
if (arr.size == 0 || arr.items[0] == NULL)
|
|
return cbt_fmt("");
|
|
char *out = (char *)cbt_escape_shell(arr.items[0]);
|
|
for (size_t i = 1; i < arr.size; i++) {
|
|
if (arr.items[i]) {
|
|
out = (char *)cbt_fmt("%s %s", out, cbt_escape_shell(arr.items[i]));
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
unsigned long cbt_get_modtime(const char *filename) {
|
|
struct stat st;
|
|
int ret = lstat(filename, &st);
|
|
if (ret == -1)
|
|
return -1;
|
|
|
|
cbt_log(CBT_LOG_DEBUG, "stat(%s) -> %ld", filename, st.st_mtime);
|
|
return st.st_mtime;
|
|
}
|
|
|
|
bool cbt_needs_recompilation(const char *input, const char *output) {
|
|
unsigned long mod_in = cbt_get_modtime(input),
|
|
mod_out = cbt_get_modtime(output);
|
|
if (mod_in == (unsigned long)-1) {
|
|
cbt_log(CBT_LOG_WARNING, "cbt_needs_recompilation(): %s does not exist",
|
|
input);
|
|
return false;
|
|
}
|
|
|
|
return mod_out == (unsigned long)-1 || mod_in > mod_out;
|
|
}
|
|
|
|
void cbt__add_fd(int fd, bool is_err) {
|
|
cbt_log(CBT_LOG_DEBUG, "cbt__add_fd() adding %d", fd);
|
|
pthread_mutex_lock(&_cbt__autoproc_mut);
|
|
cbt_log(CBT_LOG_DEBUG, "cbt__add_fd() locked mutex for fd=%d", fd);
|
|
|
|
struct pollfd pfd;
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN | POLLOUT;
|
|
|
|
struct _cbt__autoproc_set__item info = {.is_err = is_err};
|
|
|
|
cbt_da_add(cbt__autoproc_fdset, pfd);
|
|
cbt_da_add(cbt__autoproc_set, info);
|
|
pthread_mutex_unlock(&_cbt__autoproc_mut);
|
|
}
|
|
|
|
struct cbt_proc _cbt_proc_new(enum cbt_proc_mode mode, ...) {
|
|
va_list args;
|
|
va_start(args, mode);
|
|
struct cbt_proc process = cbt_proc_newv(mode, args);
|
|
va_end(args);
|
|
return process;
|
|
}
|
|
|
|
struct cbt_proc cbt_proc_newv(enum cbt_proc_mode mode, va_list args) {
|
|
|
|
struct cbt_strarr args_da = {0};
|
|
while (true) {
|
|
const char *arg = va_arg(args, const char *);
|
|
if (arg == NULL)
|
|
break;
|
|
cbt_da_add(args_da, strdup(arg));
|
|
}
|
|
cbt_da_add(args_da, NULL);
|
|
|
|
return cbt_proc_newd(mode, args_da);
|
|
}
|
|
|
|
struct cbt_proc cbt_proc_newd(enum cbt_proc_mode mode, struct cbt_strarr args) {
|
|
struct cbt_proc procinfo = {0};
|
|
cbt_log(CBT_LOG_DEBUG, "Spawning process with mode %d and args %s", mode,
|
|
cbt_escape_args(args.items));
|
|
|
|
int pipe_fds_sout[2] = {0}, pipe_fds_serr[2] = {0}, pipe_fds_sin[2] = {0};
|
|
if (mode & CBT_PROC_W)
|
|
CBT_FAIL(pipe(pipe_fds_sin) == -1);
|
|
CBT_FAIL(pipe(pipe_fds_sout) == -1);
|
|
CBT_FAIL(pipe(pipe_fds_serr) == -1);
|
|
|
|
procinfo.pid = fork();
|
|
if (procinfo.pid < 0) {
|
|
cbt_log(CBT_LOG_ERROR, "fork() failed: %s", strerror(errno));
|
|
goto cbt_proc_new_cleanup;
|
|
}
|
|
|
|
if (procinfo.pid == 0) { // child
|
|
|
|
if (mode & CBT_PROC_W)
|
|
CBT_FAIL(dup2(pipe_fds_sin[1], STDIN_FILENO) == -1);
|
|
CBT_FAIL(dup2(pipe_fds_sout[1], STDOUT_FILENO) == -1);
|
|
CBT_FAIL(dup2(pipe_fds_serr[1], STDERR_FILENO) == -1);
|
|
|
|
execvp(args.items[0], (char *const *)args.items);
|
|
__builtin_unreachable();
|
|
} else {
|
|
if (mode & CBT_PROC_W)
|
|
procinfo.fp_stdin = fdopen(pipe_fds_sin[0], "w");
|
|
if (mode & CBT_PROC_R)
|
|
procinfo.fp_stdout = fdopen(pipe_fds_sout[0], "r");
|
|
if (mode & CBT_PROC_R)
|
|
procinfo.fp_stderr = fdopen(pipe_fds_serr[0], "r");
|
|
|
|
cbt_log(CBT_LOG_DEBUG, "stdin: %p (fd=%d)", procinfo.fp_stdin,
|
|
pipe_fds_sin[0]);
|
|
cbt_log(CBT_LOG_DEBUG, "stdout: %p (fd=%d)", procinfo.fp_stdout,
|
|
pipe_fds_sout[0]);
|
|
cbt_log(CBT_LOG_DEBUG, "stderr: %p (fd=%d)", procinfo.fp_stderr,
|
|
pipe_fds_serr[0]);
|
|
|
|
if (!(mode & CBT_PROC_R)) {
|
|
cbt__add_fd(pipe_fds_sout[0], false);
|
|
cbt__add_fd(pipe_fds_serr[0], true);
|
|
}
|
|
|
|
cbt_proc_new_cleanup:
|
|
cbt_log(CBT_LOG_DEBUG, "freeing up args");
|
|
for (size_t i = 0; i < args.size; i++) {
|
|
free(args.items[i]);
|
|
}
|
|
free(args.items);
|
|
|
|
return procinfo;
|
|
}
|
|
}
|
|
|
|
int cbt_proc_wait(struct cbt_proc proc) {
|
|
int wstatus;
|
|
|
|
do {
|
|
pid_t w = waitpid(proc.pid, &wstatus, WUNTRACED | WCONTINUED);
|
|
cbt_log(CBT_LOG_DEBUG, "waitpid(%d) -> %d, status = %d", proc.pid, w,
|
|
wstatus);
|
|
if (w == -1) {
|
|
cbt_log(CBT_LOG_WARNING, "waitpid(%d) -> %d (%s)", proc.pid, w,
|
|
strerror(errno));
|
|
return -2000;
|
|
}
|
|
|
|
if (WIFEXITED(wstatus)) {
|
|
return WEXITSTATUS(wstatus);
|
|
} else if (WIFSIGNALED(wstatus)) {
|
|
return 1000 + WTERMSIG(wstatus);
|
|
}
|
|
} while (!WIFEXITED(wstatus) && !WIFSIGNALED(wstatus));
|
|
return -1000;
|
|
}
|
|
|
|
static void cbt__line_processor_sig_handler(int signal) { (void)signal; }
|
|
|
|
void *cbt__line_processor(void *arg) {
|
|
(void)arg;
|
|
struct timespec timeout = {.tv_sec = 0, .tv_nsec = 100};
|
|
|
|
sigset_t sigmask;
|
|
sigemptyset(&sigmask);
|
|
sigaddset(&sigmask, SIGUSR1);
|
|
|
|
struct sigaction sa;
|
|
sa.sa_handler = cbt__line_processor_sig_handler;
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = SA_RESTART;
|
|
|
|
CBT_FAIL(sigaction(SIGUSR1, &sa, NULL) == -1);
|
|
|
|
while (cbt_running) {
|
|
pthread_mutex_lock(&_cbt__autoproc_mut);
|
|
|
|
#if 1 && !defined(__APPLE__)
|
|
int res = ppoll(cbt__autoproc_fdset.items, cbt__autoproc_fdset.size,
|
|
&timeout, &sigmask);
|
|
if (res < 0) {
|
|
cbt_log(CBT_LOG_ERROR, "ppoll() -> %d (%s)", res, strerror(errno));
|
|
break;
|
|
}
|
|
#else
|
|
for (int i = 0; i < cbt__autoproc_set.size; i++) {
|
|
cbt_log(CBT_LOG_DEBUG, "fdset[%d] = { %d, %d }", i,
|
|
cbt__autoproc_set.items[i].fd, cbt__autoproc_set.items[i].events);
|
|
}
|
|
cbt_log(CBT_LOG_DEBUG, "poll(_, %d, _) ...", cbt__autoproc_set.size);
|
|
int res = poll(cbt__autoproc_set.items, cbt__autoproc_set.size, 1000);
|
|
#endif
|
|
|
|
char buf[1024];
|
|
for (size_t i = 0; i < cbt__autoproc_fdset.size; i++) {
|
|
struct pollfd pfd = cbt__autoproc_fdset.items[i];
|
|
struct _cbt__autoproc_set__item item = cbt__autoproc_set.items[i];
|
|
if (pfd.revents != 0) {
|
|
if (pfd.revents & POLLIN) {
|
|
ssize_t s = read(pfd.fd, buf, sizeof(buf));
|
|
cbt_log(item.is_err ? CBT_LOG_ERROR : CBT_LOG_INFO, "fd(%d): %.*s",
|
|
pfd.fd, (int)s - 1, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&_cbt__autoproc_mut);
|
|
usleep(1000);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct cbt_lib cbt_lib(const char *libname) {
|
|
return (struct cbt_lib){.type = CBT_LIB_STATIC,
|
|
.libname = libname,
|
|
.cflags = {0},
|
|
.ldflags = {0},
|
|
.sources = {0}};
|
|
}
|
|
|
|
int _cbt_globerr(const char *epath, int eerrno) {
|
|
cbt_log(CBT_LOG_WARNING, "glob() failed at %s: %s", epath, strerror(eerrno));
|
|
return 0;
|
|
}
|
|
|
|
void _cbt_lib_src(struct cbt_lib *lib, ...) {
|
|
va_list args;
|
|
va_start(args, lib);
|
|
|
|
glob_t globbuf;
|
|
|
|
for (char *arg = va_arg(args, char *); arg; arg = va_arg(args, char *)) {
|
|
glob(arg, GLOB_APPEND | GLOB_BRACE, _cbt_globerr, &globbuf);
|
|
}
|
|
|
|
for (size_t i = 0; i < globbuf.gl_pathc; i++) {
|
|
char *path = strdup(globbuf.gl_pathv[i]);
|
|
cbt_da_add(lib->sources, path);
|
|
}
|
|
|
|
globfree(&globbuf);
|
|
va_end(args);
|
|
}
|
|
|
|
void _cbt_lib_cflags(struct cbt_lib *lib, ...) {
|
|
va_list args;
|
|
va_start(args, lib);
|
|
for (char *arg = va_arg(args, char *); arg; arg = va_arg(args, char *)) {
|
|
cbt_da_add(lib->cflags, arg);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
void _cbt_lib_ldflags(struct cbt_lib *lib, ...) {
|
|
va_list args;
|
|
va_start(args, lib);
|
|
for (char *arg = va_arg(args, char *); arg; arg = va_arg(args, char *)) {
|
|
cbt_da_add(lib->ldflags, arg);
|
|
}
|
|
va_end(args);
|
|
}
|
|
|
|
bool cbt_lib_build(struct cbt_lib lib, const char *compiler) {
|
|
cbt_log(CBT_LOG_INFO, "library: %s", lib.libname);
|
|
cbt_log(CBT_LOG_INFO, " cflags: %s", cbt_escape_argsd(lib.cflags));
|
|
cbt_log(CBT_LOG_INFO, " ldflags: %s", cbt_escape_argsd(lib.ldflags));
|
|
cbt_log(CBT_LOG_INFO, " sources: %s", cbt_escape_argsd(lib.sources));
|
|
cbt_log(CBT_LOG_INFO, " cc: %s", compiler ? compiler : cbt_cc);
|
|
|
|
if (lib.sources.size == 0) {
|
|
cbt_log(CBT_LOG_WARNING, "cbt_lib_build(): no sources for %s", lib.libname);
|
|
return false;
|
|
}
|
|
|
|
struct cbt_strarr args = {0};
|
|
cbt_da_add(args, (char *)(compiler ? compiler : cbt_cc));
|
|
|
|
cbt_da_merge(args, lib.cflags);
|
|
|
|
cbt_log(CBT_LOG_INFO, "args.size: %d", args.size);
|
|
cbt_log(CBT_LOG_INFO, "CMD: %s", cbt_escape_argsd(args));
|
|
|
|
cbt_da_add(args, NULL);
|
|
cbt_da_add(args, "-o");
|
|
cbt_da_add(args, NULL);
|
|
|
|
for (size_t i = 0; i < lib.sources.size; i++) {
|
|
args.items[args.size - 3] = lib.sources.items[i];
|
|
args.items[args.size - 1] = ".o"; // TODO
|
|
struct cbt_proc proc = cbt_proc_newd(CBT_PROC_AUTO, args);
|
|
if (cbt_proc_wait(proc) != 0) {
|
|
cbt_log(CBT_LOG_ERROR, "build failed: %s", cbt_escape_argsd(args));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
char **argp = &args.items[args.size - 1];
|
|
cbt_da_merge(args, lib.ldflags);
|
|
|
|
*argp = (char *)cbt_fmt("lib%s.so", lib.libname);
|
|
|
|
// todo: link
|
|
|
|
return false;
|
|
}
|
|
|
|
void cbt_lib_free(struct cbt_lib lib) {
|
|
free(lib.cflags.items);
|
|
free(lib.ldflags.items);
|
|
for (size_t i = 0; i < lib.sources.size; i++) {
|
|
free(lib.sources.items[i]);
|
|
}
|
|
free(lib.sources.items);
|
|
}
|
|
|
|
struct cbt_binary cbt_binary(const char *out_path);
|
|
void _cbt_binary_add_src(struct cbt_binary bin, ...);
|
|
bool _cbt_binary_build(struct cbt_binary bin, const char *compiler, ...);
|
|
void cbt_binary_free(struct cbt_binary bin);
|
|
|
|
#undef CBT_TODO
|
|
#undef CBT_FAIL
|
|
//-*- end
|
|
#endif
|
|
|
|
#endif
|