From bb33df56cbbb02650328dd201e2d9686363d15f5 Mon Sep 17 00:00:00 2001 From: sinanmohd Date: Sun, 23 Jun 2024 18:49:29 +0530 Subject: evanix: init arg parsing --- include/evanix.h | 15 ++++++++ include/jobs.h | 2 +- include/util.h | 3 ++ src/build.c | 19 ++++++++- src/evanix.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/htab.c | 3 +- src/jobs.c | 21 +++++----- src/util.c | 65 ++++++++++++++++++++++++++++--- 8 files changed, 217 insertions(+), 26 deletions(-) create mode 100644 include/evanix.h diff --git a/include/evanix.h b/include/evanix.h new file mode 100644 index 0000000..aa53b9f --- /dev/null +++ b/include/evanix.h @@ -0,0 +1,15 @@ +#include + +#ifndef EVANIX_H + +struct evanix_opts_t { + bool isflake; + bool isdryrun; + bool ispipelined; + bool close_stderr_exec; +}; + +extern struct evanix_opts_t evanix_opts; + +#define EVANIX_H +#endif diff --git a/include/jobs.h b/include/jobs.h index d4916f7..2357680 100644 --- a/include/jobs.h +++ b/include/jobs.h @@ -33,7 +33,7 @@ typedef enum { } job_read_state_t; int job_read(FILE *stream, struct job **jobs); -int jobs_init(FILE **stream); +int jobs_init(FILE **stream, char *expr); void job_free(struct job *j); int job_parents_list_insert(struct job *job, struct job *parent); void job_deps_list_rm(struct job *job, struct job *dep); diff --git a/include/util.h b/include/util.h index 130bbdd..7c15354 100644 --- a/include/util.h +++ b/include/util.h @@ -1,3 +1,4 @@ +#include #include #include @@ -14,3 +15,5 @@ int json_streaming_read(FILE *stream, cJSON **json); int vpopen(FILE **stream, const char *file, char *const argv[]); +int atob(const char *s); +int run(const char *file, char *argv[]); diff --git a/src/build.c b/src/build.c index fa76c13..39750b5 100644 --- a/src/build.c +++ b/src/build.c @@ -5,6 +5,7 @@ #include #include "build.h" +#include "evanix.h" #include "jobs.h" #include "queue.h" #include "util.h" @@ -42,13 +43,27 @@ out: static int build(struct queue *queue) { struct job *job; - int ret = 0; + char *args[3]; + size_t argindex; + int ret; ret = queue_pop(queue, &job, queue->htab); if (ret < 0) return ret; - printf("nix build %s^*\n", job->drv_path); + argindex = 0; + args[argindex++] = "nix-build"; + args[argindex++] = job->drv_path; + args[argindex++] = NULL; + + if (evanix_opts.isdryrun) { + for (size_t i = 0; i < argindex - 1; i++) + printf("%s%c", args[i], + (i + 2 == argindex) ? '\n' : ' '); + } else { + run("nix-build", args); + } + job_free(job); return 0; diff --git a/src/evanix.c b/src/evanix.c index e83d0ea..64bd763 100644 --- a/src/evanix.c +++ b/src/evanix.c @@ -1,18 +1,40 @@ +#include #include #include #include "build.h" +#include "evanix.h" #include "queue.h" #include "util.h" -int main(void) +static const char usage[] = + "Usage: evanix [options] expr\n" + "\n" + " -h, --help Show help message and quit.\n" + " -f, --flake Build a flake.\n" + " -d, --dry-run Show what derivations would be " + "built.\n" + " -p, --pipelined Use evanix build pipeline.\n" + " -c, --close-stderr-exec Close stderr on exec.\n" + "\n"; + +struct evanix_opts_t evanix_opts = { + .close_stderr_exec = true, + .isflake = false, + .ispipelined = true, + .isdryrun = false, +}; + +static int evanix(char *expr); + +static int evanix(char *expr) { struct queue_thread *queue_thread = NULL; struct build_thread *build_thread = NULL; FILE *stream = NULL; int ret = 0; - ret = jobs_init(&stream); + ret = jobs_init(&stream, expr); if (ret < 0) goto out_free; @@ -35,14 +57,21 @@ int main(void) goto out_free; } - ret = pthread_create(&build_thread->tid, NULL, build_thread_entry, - build_thread); + if (evanix_opts.ispipelined) + ret = pthread_create(&build_thread->tid, NULL, + build_thread_entry, build_thread); + else + ret = pthread_join(queue_thread->tid, NULL); if (ret < 0) { print_err("%s", strerror(ret)); goto out_free; } - ret = pthread_join(queue_thread->tid, NULL); + if (evanix_opts.ispipelined) + ret = pthread_join(queue_thread->tid, NULL); + else + ret = pthread_create(&build_thread->tid, NULL, + build_thread_entry, build_thread); if (ret < 0) { print_err("%s", strerror(ret)); goto out_free; @@ -57,5 +86,79 @@ int main(void) out_free: queue_thread_free(queue_thread); free(build_thread); - exit(ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + return ret; +} + +int main(int argc, char *argv[]) +{ + extern int optind, opterr, optopt; + extern char *optarg; + int ret, longindex; + + static struct option longopts[] = { + {"help", no_argument, NULL, 'h'}, + {"flake", no_argument, NULL, 'f'}, + {"dry-run", no_argument, NULL, 'd'}, + {"pipelined", required_argument, NULL, 'p'}, + {"close-stderr-exec", required_argument, NULL, 'c'}, + {NULL, 0, NULL, 0}, + }; + + while ((ret = getopt_long(argc, argv, "", longopts, &longindex)) != + -1) { + switch (ret) { + case 'h': + printf("%s", usage); + exit(EXIT_SUCCESS); + break; + case 'f': + evanix_opts.isflake = true; + break; + case 'd': + evanix_opts.isdryrun = true; + break; + case 'p': + ret = atob(optarg); + if (ret < 0) { + fprintf(stderr, + "option --%s requires a bool argument\n" + "Try 'evanix --help' for more " + "information.\n", + longopts[longindex].name); + exit(EXIT_FAILURE); + } + + evanix_opts.ispipelined = ret; + break; + case 'c': + ret = atob(optarg); + if (ret < 0) { + fprintf(stderr, + "option --%s requires a bool argument\n" + "Try 'evanix --help' for more " + "information.\n", + longopts[longindex].name); + exit(EXIT_FAILURE); + } + + evanix_opts.close_stderr_exec = ret; + break; + default: + fprintf(stderr, + "Try 'evanix --help' for more information.\n"); + exit(EXIT_FAILURE); + break; + } + } + if (optind != argc - 1) { + fprintf(stderr, "evanix: invalid expr operand\n" + "Try 'evanix --help' for more information.\n"); + exit(EXIT_FAILURE); + } + + ret = evanix(argv[optind]); + if (ret < 0) + exit(EXIT_FAILURE); + + exit(EXIT_SUCCESS); } diff --git a/src/htab.c b/src/htab.c index 066d761..11d5a98 100644 --- a/src/htab.c +++ b/src/htab.c @@ -71,13 +71,12 @@ int htab_enter(struct htab *htab, const char *key, void *data) return -errno; } - e.data = data; ret = hsearch_r(e, ENTER, &ep, htab->table); if (ret == 0) { print_err("%s", strerror(errno)); return -errno; } - ep->data = NULL; + ep->data = data; if (ep->key != e.key) { free(e.key); diff --git a/src/jobs.c b/src/jobs.c index 6f06ef9..22d2b1c 100644 --- a/src/jobs.c +++ b/src/jobs.c @@ -5,6 +5,7 @@ #include +#include "evanix.h" #include "jobs.h" #include "util.h" @@ -219,7 +220,8 @@ int job_read(FILE *stream, struct job **job) temp = cJSON_GetObjectItemCaseSensitive(root, "error"); if (cJSON_IsString(temp)) { - puts(temp->valuestring); + if (evanix_opts.close_stderr_exec) + puts(temp->valuestring); ret = JOB_READ_EVAL_ERR; goto out_free; } @@ -361,17 +363,18 @@ out_free_job: return ret; } -int jobs_init(FILE **stream) +int jobs_init(FILE **stream, char *expr) { + size_t argindex; + char *args[4]; int ret; - /* TODO: proproperly handle args */ - char *const args[] = { - "nix-eval-jobs", - "--flake", - "github:sinanmohd/evanix#packages.x86_64-linux", - NULL, - }; + argindex = 0; + args[argindex++] = "nix-eval-jobs"; + if (evanix_opts.isflake) + args[argindex++] = "--flake"; + args[argindex++] = expr; + args[argindex++] = NULL; /* the package is wrapProgram-ed with nix-eval-jobs */ ret = vpopen(stream, "nix-eval-jobs", args); diff --git a/src/util.c b/src/util.c index a2ae788..5001e01 100644 --- a/src/util.c +++ b/src/util.c @@ -1,11 +1,15 @@ #include +#include #include #include #include +#include +#include #include #include +#include "evanix.h" #include "util.h" int json_streaming_read(FILE *stream, cJSON **json) @@ -40,8 +44,8 @@ out_free_line: int vpopen(FILE **stream, const char *file, char *const argv[]) { - int fd[2]; - int ret; + int fd[2], ret; + int nullfd = -1; ret = pipe(fd); if (ret < 0) { @@ -69,13 +73,62 @@ int vpopen(FILE **stream, const char *file, char *const argv[]) close(fd[0]); ret = dup2(fd[1], STDOUT_FILENO); - if (ret < 0) - goto out_err; + if (ret < 0) { + print_err("%s", strerror(errno)); + goto out_close_fd_1; + } + + if (evanix_opts.close_stderr_exec) { + nullfd = open("/dev/null", O_WRONLY); + if (nullfd < 0) { + print_err("%s", strerror(errno)); + goto out_close_fd_1; + } + ret = dup2(nullfd, STDERR_FILENO); + if (ret < 0) { + print_err("%s", strerror(errno)); + goto out_close_nullfd; + } + } execvp(file, argv); + print_err("%s", strerror(errno)); -out_err: +out_close_nullfd: + if (nullfd >= 0) + close(nullfd); +out_close_fd_1: close(fd[1]); - print_err("%s", strerror(errno)); exit(EXIT_FAILURE); } + +int atob(const char *s) +{ + if (!strcmp(s, "true") || !strcmp(s, "yes") || !strcmp(s, "y")) + return true; + else if (!strcmp(s, "false") || !strcmp(s, "no") || !strcmp(s, "n")) + return false; + + return -1; +} + +int run(const char *file, char *argv[]) +{ + int ret, wstatus; + + ret = fork(); + switch (ret) { + case -1: + print_err("%s", strerror(errno)); + return -errno; + case 0: + execvp(file, argv); + print_err("%s", strerror(errno)); + exit(EXIT_FAILURE); + default: + ret = waitpid(ret, &wstatus, 0); + if (!WIFEXITED(wstatus)) + return -EPERM; + return WEXITSTATUS(wstatus) == 0 ? 0 : -EPERM; + } +} -- cgit v1.2.3