aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsinanmohd <sinan@sinanmohd.com>2024-06-23 18:49:29 +0530
committersinanmohd <sinan@sinanmohd.com>2024-06-24 11:15:39 +0530
commitbb33df56cbbb02650328dd201e2d9686363d15f5 (patch)
tree048b2066bff4b7b4e5c2d85212de087c2b1a73a5
parenta3c16056c2ea8083db39054271f1118aed37ccc3 (diff)
evanix: init arg parsing
-rw-r--r--include/evanix.h15
-rw-r--r--include/jobs.h2
-rw-r--r--include/util.h3
-rw-r--r--src/build.c19
-rw-r--r--src/evanix.c115
-rw-r--r--src/htab.c3
-rw-r--r--src/jobs.c21
-rw-r--r--src/util.c65
8 files changed, 217 insertions, 26 deletions
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 <stdbool.h>
+
+#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 <stdbool.h>
#include <stdio.h>
#include <cjson/cJSON.h>
@@ -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 <string.h>
#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 <getopt.h>
#include <stdlib.h>
#include <string.h>
#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 <bool> Use evanix build pipeline.\n"
+ " -c, --close-stderr-exec <bool> 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 <cjson/cJSON.h>
+#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 <errno.h>
+#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <cjson/cJSON.h>
+#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;
+ }
+}