aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2024-10-24 16:21:36 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2024-10-24 16:21:36 -0400
commit4231ce4470fed52aca9afbe38c6b79a3d31b4136 (patch)
treef8f7f445a92fcd475d045a79d91e67e8d6ae20ee /src
parent7729d546d2dbda85ca1d86a913e97b51487355ba (diff)
downloadstasis-4231ce4470fed52aca9afbe38c6b79a3d31b4136.tar.gz
Generalize *_index_provides interface
* Replaces conda_* and pip_* with pkg_index_provides * Because this function can fail in so many ways I've added pkg_index_provides_strerror() and a detection macro PKG_INDEX_PROVIDES_FAILED() to make things easier
Diffstat (limited to 'src')
-rw-r--r--src/lib/core/conda.c91
-rw-r--r--src/lib/core/delivery.c16
2 files changed, 61 insertions, 46 deletions
diff --git a/src/lib/core/conda.c b/src/lib/core/conda.c
index 25069f8..8fb45cf 100644
--- a/src/lib/core/conda.c
+++ b/src/lib/core/conda.c
@@ -78,13 +78,27 @@ int pip_exec(const char *args) {
return system(command);
}
-int pip_index_provides(const char *index_url, const char *spec) {
+static const char *PKG_ERROR_STR[] = {
+ "success",
+ "[internal] unhandled package manager mode",
+ "[internal] unable to create temporary log for process output",
+ "package manager encountered an error at runtime",
+ "package manager received a signal",
+ "package manager failed to execute",
+};
+
+const char *pkg_index_provides_strerror(int code) {
+ code = -code - (-PKG_INDEX_PROVIDES_ERROR_MESSAGE_OFFSET);
+ return PKG_ERROR_STR[code];
+}
+
+int pkg_index_provides(int mode, const char *index, const char *spec) {
char cmd[PATH_MAX] = {0};
char spec_local[255] = {0};
if (isempty((char *) spec)) {
// NULL or zero-length; no package spec means there's nothing to do.
- return -1;
+ return PKG_NOT_FOUND;
}
// Normalize the local spec string
@@ -98,22 +112,36 @@ int pip_index_provides(const char *index_url, const char *spec) {
if (logfd < 0) {
perror(logfile);
remove(logfile); // fail harmlessly if not present
- return -1;
+ return PKG_INDEX_PROVIDES_E_INTERNAL_LOG_HANDLE;
}
-
int status = 0;
struct Process proc;
memset(&proc, 0, sizeof(proc));
proc.redirect_stderr = 1;
strcpy(proc.f_stdout, logfile);
- // Do an installation in dry-run mode to see if the package exists in the given index.
- snprintf(cmd, sizeof(cmd) - 1, "python -m pip install --dry-run --no-cache --no-deps --index-url=%s '%s'", index_url, spec_local);
- status = shell(&proc, cmd);
+ if (mode == PKG_USE_PIP) {
+ // Do an installation in dry-run mode to see if the package exists in the given index.
+ strncpy(cmd, "python -m pip install --dry-run --no-cache --no-deps ", sizeof(cmd) - 1);
+ if (index) {
+ snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "--index-url='%s' ", index);
+ }
+ snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "'%s' ", spec_local);
+ } else if (mode == PKG_USE_CONDA) {
+ strncpy(cmd, "mamba search ", sizeof(cmd) - 1);
+ if (index) {
+ snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "--channel '%s' ", index);
+ }
+ snprintf(cmd + strlen(cmd), (sizeof(cmd) - 1) - strlen(cmd), "'%s' ", spec_local);
+ } else {
+ return PKG_INDEX_PROVIDES_E_INTERNAL_MODE_UNKNOWN;
+ }
// Print errors only when shell() itself throws one
- // If some day we want to see the errors thrown by pip too, use this condition instead: (status != 0)
+ // If some day we want to see the errors thrown by pip too, use this
+ // condition instead: (status != 0)
+ status = shell(&proc, cmd);
if (status < 0) {
FILE *fp = fdopen(logfd, "r");
if (!fp) {
@@ -131,7 +159,25 @@ int pip_index_provides(const char *index_url, const char *spec) {
}
}
remove(logfile);
- return proc.returncode == 0;
+
+ if (WTERMSIG(proc.returncode)) {
+ // This gets its own return value because if the external program
+ // received a signal, even its status is zero, it's not reliable
+ return PKG_INDEX_PROVIDES_E_MANAGER_SIGNALED;
+ }
+
+ if (status < 0) {
+ return PKG_INDEX_PROVIDES_E_MANAGER_EXEC;
+ } else if (WEXITSTATUS(proc.returncode) > 1) {
+ // Pip and conda both return 2 on argument parsing errors
+ return PKG_INDEX_PROVIDES_E_MANAGER_RUNTIME;
+ } else if (WEXITSTATUS(proc.returncode) == 1) {
+ // Pip and conda both return 1 when a package is not found.
+ // Unfortunately this applies to botched version specs, too.
+ return PKG_NOT_FOUND;
+ } else {
+ return PKG_FOUND;
+ }
}
int conda_exec(const char *args) {
@@ -440,33 +486,6 @@ char *conda_get_active_environment() {
return result;
}
-int conda_provides(const char *spec) {
- struct Process proc;
- memset(&proc, 0, sizeof(proc));
-
- // Short circuit:
- // Running "mamba search" without an argument will print every package in
- // all channels, then return "found". Prevent this.
- // No data implies "not found".
- if (isempty((char *) spec)) {
- return 0;
- }
-
- strcpy(proc.f_stdout, "/dev/null");
- strcpy(proc.f_stderr, "/dev/null");
-
- // It's worth noting the departure from using conda_exec() here:
- // conda_exec() expects the program output to be visible to the user.
- // For this operation we only need the exit value.
- char cmd[PATH_MAX] = {0};
- snprintf(cmd, sizeof(cmd) - 1, "mamba search %s", spec);
- if (shell(&proc, cmd) < 0) {
- fprintf(stderr, "shell: %s", strerror(errno));
- return -1;
- }
- return proc.returncode == 0; // 0=not_found, 1=found
-}
-
int conda_index(const char *path) {
char command[PATH_MAX];
sprintf(command, "index %s", path);
diff --git a/src/lib/core/delivery.c b/src/lib/core/delivery.c
index 5645dcc..ada708d 100644
--- a/src/lib/core/delivery.c
+++ b/src/lib/core/delivery.c
@@ -254,20 +254,16 @@ void delivery_defer_packages(struct Delivery *ctx, int type) {
int upstream_exists = 0;
if (DEFER_PIP == type) {
- upstream_exists = pip_index_provides(PYPI_INDEX_DEFAULT, name);
+ upstream_exists = pkg_index_provides(PKG_USE_PIP, PYPI_INDEX_DEFAULT, name);
} else if (DEFER_CONDA == type) {
- upstream_exists = conda_provides(name);
- } else {
- fprintf(stderr, "\nUnknown package type: %d\n", type);
- exit(1);
+ upstream_exists = pkg_index_provides(PKG_USE_CONDA, NULL, name);
}
- if (upstream_exists < 0) {
- fprintf(stderr, "%s's existence command failed for '%s'\n"
- "(This may be due to a network/firewall issue!)\n", mode, name);
+ if (PKG_INDEX_PROVIDES_FAILED(upstream_exists)) {
+ fprintf(stderr, "%s's existence command failed for '%s': %s\n",
+ mode, name, pkg_index_provides_strerror(upstream_exists));
exit(1);
- }
- if (!upstream_exists) {
+ } else if (upstream_exists == PKG_NOT_FOUND) {
build_for_host = 1;
} else {
build_for_host = 0;