#include #include #include #include #include #include "jobs.h" #include "util.h" static void output_free(struct output *output); static int job_new(struct job **j, char *name, char *drv_path, struct job *parent); static int job_output_insert(struct job *j, char *name, char *store_path); static int job_read_inputdrvs(struct job *job, cJSON *input_drvs); static int job_read_outputs(struct job *job, cJSON *outputs); static int job_deps_list_insert(struct job *job, struct job *dep); static int job_output_list_insert(struct job *job, struct output *output); static void output_free(struct output *output) { if (output == NULL) return; free(output->name); free(output->store_path); free(output); } static int job_output_list_insert(struct job *job, struct output *output) { size_t newsize; void *ret; if (job->outputs_filled < job->outputs_size) { job->outputs[job->outputs_filled++] = output; return 0; } newsize = job->outputs_size == 0 ? 1 : job->outputs_size * 2; ret = realloc(job->outputs, newsize * sizeof(*job->outputs)); if (ret == NULL) { print_err("%s", strerror(errno)); return -errno; } job->outputs = ret; job->outputs_size = newsize; job->outputs[job->outputs_filled++] = output; return 0; } void job_deps_list_rm(struct job *job, struct job *dep) { for (size_t i = 0; i < job->deps_filled; i++) { if (job->deps[i] != dep) continue; job->deps[i] = job->deps[job->deps_filled - 1]; job->deps_filled -= 1; return; } } static int job_deps_list_insert(struct job *job, struct job *dep) { size_t newsize; void *ret; if (job->deps_filled < job->deps_size) { job->deps[job->deps_filled++] = dep; return 0; } newsize = job->deps_size == 0 ? 1 : job->deps_size * 2; ret = realloc(job->deps, newsize * sizeof(*job->deps)); if (ret == NULL) { print_err("%s", strerror(errno)); return -errno; } job->deps = ret; job->deps_size = newsize; job->deps[job->deps_filled++] = dep; return 0; } int job_parents_list_insert(struct job *job, struct job *parent) { size_t newsize; void *ret; if (job->parents_filled < job->parents_size) { job->parents[job->parents_filled++] = parent; return 0; } newsize = job->parents_size == 0 ? 1 : job->parents_size * 2; ret = realloc(job->parents, newsize * sizeof(*job->parents)); if (ret == NULL) { print_err("%s", strerror(errno)); return -errno; } job->parents = ret; job->parents_size = newsize; job->parents[job->parents_filled++] = parent; return 0; } static int job_output_insert(struct job *j, char *name, char *store_path) { struct output *o; int ret = 0; o = malloc(sizeof(*o)); if (o == NULL) { print_err("%s", strerror(errno)); return -errno; } o->name = strdup(name); if (o->name == NULL) { print_err("%s", strerror(errno)); ret = -errno; goto out_free_o; } if (store_path != NULL) { o->store_path = strdup(store_path); if (o->store_path == NULL) { print_err("%s", strerror(errno)); ret = -errno; goto out_free_name; } } else { o->store_path = NULL; } ret = job_output_list_insert(j, o); if (ret < 0) goto out_free_store_path; out_free_store_path: if (ret < 0) free(o->store_path); out_free_name: if (ret < 0) free(o->name); out_free_o: if (ret < 0) free(o); return 0; } static int job_read_inputdrvs(struct job *job, cJSON *input_drvs) { cJSON *output; struct job *dep_job = NULL; int ret = 0; for (cJSON *array = input_drvs; array != NULL; array = array->next) { ret = job_new(&dep_job, NULL, array->string, job); if (ret < 0) goto out_free_dep_job; cJSON_ArrayForEach (output, array) { ret = job_output_insert(dep_job, output->valuestring, NULL); if (ret < 0) job_free(dep_job); } ret = job_deps_list_insert(job, dep_job); if (ret < 0) job_free(dep_job); dep_job = NULL; } out_free_dep_job: if (ret < 0) job_free(dep_job); return ret; } static int job_read_outputs(struct job *job, cJSON *outputs) { int ret; for (cJSON *i = outputs; i != NULL; i = i->next) { ret = job_output_insert(job, i->string, i->valuestring); if (ret < 0) return ret; } return 0; } int job_read(FILE *stream, struct job **job) { cJSON *temp; char *drv_path = NULL; struct job *j = NULL; cJSON *root = NULL; char *name = NULL; int ret = 0; ret = json_streaming_read(stream, &root); if (ret < 0 || ret == -EOF) return JOB_READ_EOF; temp = cJSON_GetObjectItemCaseSensitive(root, "error"); if (cJSON_IsString(temp)) { puts(temp->valuestring); ret = JOB_READ_EVAL_ERR; goto out_free; } temp = cJSON_GetObjectItemCaseSensitive(root, "name"); if (!cJSON_IsString(temp)) { ret = JOB_READ_JSON_INVAL; goto out_free; } name = temp->valuestring; temp = cJSON_GetObjectItemCaseSensitive(root, "drvPath"); if (!cJSON_IsString(temp)) { free(name); ret = JOB_READ_JSON_INVAL; goto out_free; } drv_path = temp->valuestring; ret = job_new(&j, name, drv_path, NULL); if (ret < 0) goto out_free; temp = cJSON_GetObjectItemCaseSensitive(root, "inputDrvs"); if (!cJSON_IsObject(temp)) { ret = JOB_READ_JSON_INVAL; goto out_free; } ret = job_read_inputdrvs(j, temp->child); if (ret < 0) goto out_free; temp = cJSON_GetObjectItemCaseSensitive(root, "outputs"); if (!cJSON_IsObject(temp)) { ret = JOB_READ_JSON_INVAL; goto out_free; } ret = job_read_outputs(j, temp->child); if (ret < 0) goto out_free; out_free: cJSON_Delete(root); if (ret != JOB_READ_SUCCESS) job_free(j); else *job = j; if (ret == JOB_READ_JSON_INVAL) print_err("%s", "Invalid JSON"); return ret; } void job_free(struct job *job) { if (job == NULL) return; /* deps_filled will be decremented by recusrive call to the job_free * see job_deps_list_rm() in the next for loop */ while (job->deps_filled) job_free(*job->deps); free(job->deps); for (size_t i = 0; i < job->parents_filled; i++) job_deps_list_rm(job->parents[i], job); free(job->parents); for (size_t i = 0; i < job->outputs_filled; i++) output_free(job->outputs[i]); free(job->outputs); free(job->drv_path); free(job->name); free(job); } static int job_new(struct job **j, char *name, char *drv_path, struct job *parent) { struct job *job; int ret = 0; job = malloc(sizeof(*job)); if (job == NULL) { print_err("%s", strerror(errno)); return -errno; } job->transitive = true; job->outputs_size = 0; job->outputs_filled = 0; job->outputs = NULL; job->deps_size = 0; job->deps_filled = 0; job->deps = NULL; job->parents_size = 0; job->parents_filled = 0; job->parents = NULL; if (name != NULL) { job->name = strdup(name); if (job->name == NULL) { print_err("%s", strerror(errno)); ret = -errno; goto out_free_job; } } else { job->name = NULL; } job->drv_path = strdup(drv_path); if (job->drv_path == NULL) { print_err("%s", strerror(errno)); ret = -errno; goto out_free_name; } if (parent != NULL) { ret = job_parents_list_insert(job, parent); if (ret < 0) goto out_free_drv_path; } out_free_drv_path: if (ret < 0) free(job->drv_path); out_free_name: if (ret < 0) free(job->name); out_free_job: if (ret < 0) free(job); else *j = job; return ret; } int jobs_init(FILE **stream) { int ret; /* TODO: proproperly handle args */ char *const args[] = { "nix-eval-jobs", "--flake", "github:NixOS/nixpkgs#legacyPackages.x86_64-linux.python310Packages", NULL, }; /* the package is wrapProgram-ed with nix-eval-jobs */ ret = vpopen(stream, "nix-eval-jobs", args); return ret; }