From: Michael Tremer Date: Sat, 14 Oct 2023 17:50:42 +0000 (+0000) Subject: cli: Completely try again... X-Git-Tag: 0.9.30~1523 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ee8a3a64ffc138435a2d44c17eeac15d5b392809;p=people%2Fms%2Fpakfire.git cli: Completely try again... Signed-off-by: Michael Tremer --- diff --git a/Makefile.am b/Makefile.am index e7522a58c..6a1f0136b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -449,6 +449,8 @@ libcli_la_SOURCES = \ src/cli/lib/info.h \ src/cli/lib/install.h \ src/cli/lib/install.c \ + src/cli/lib/pakfire.c \ + src/cli/lib/pakfire.h \ src/cli/lib/progressbar.c \ src/cli/lib/progressbar.h \ src/cli/lib/provides.h \ diff --git a/src/cli/lib/build.c b/src/cli/lib/build.c index f1e98f087..c63096d8d 100644 --- a/src/cli/lib/build.c +++ b/src/cli/lib/build.c @@ -18,101 +18,87 @@ # # #############################################################################*/ +#include #include -#include #include #include #include "build.h" #include "color.h" #include "command.h" +#include "pakfire.h" #include #include +#define MAX_MAKEFILES 32 + struct config { const char* id; const char* target; int flags; + + // Makefiles + char* makefiles[MAX_MAKEFILES]; + unsigned int num_makefiles; }; -static void help(void) { - printf( - "%s [OPTIONS...] build [OPTIONS...] MAKEFILE\n\n" - "Options:\n" - " --disable-ccache Disables using ccache\n" - " --disable-snapshot Do not use the snapshot\n" - " --disable-tests Do not run tests\n" - " --non-interactive Run the build non-interactively\n" - " --target Output all packages to this directory\n" - " -h --help Show help\n", - program_invocation_short_name - ); - - exit(0); -} +enum { + OPT_DISABLE_CCACHE = 1, + OPT_DISABLE_SNAPSHOT = 2, + OPT_DISABLE_TESTS = 3, + OPT_ID = 4, + OPT_NON_INTERACTIVE = 5, + OPT_TARGET = 6, +}; -static int parse_argv(struct config* config, int argc, char* argv[]) { - enum { - ARG_DISABLE_CCACHE, - ARG_DISABLE_SNAPSHOT, - ARG_DISABLE_TESTS, - ARG_ID, - ARG_NON_INTERACTIVE, - ARG_TARGET, - }; +static struct argp_option options[] = { + { "disable-ccache", OPT_DISABLE_CCACHE, NULL, 0, "Disable the ccache", 0 }, + { "disable-snapshot", OPT_DISABLE_SNAPSHOT, NULL, 0, "Do not use the snapshot", 0 }, + { "disable-tests", OPT_DISABLE_TESTS, NULL, 0, "Do not run tests", 0 }, + { "id", OPT_ID, "ID", 0, "Use this build ID", 1 }, + { "non-interactive", OPT_NON_INTERACTIVE, NULL, 0, "Run the build non-interactively", 0 }, + { "target", OPT_TARGET, "TARGET", 0, "Output all packages into this directory", 0 }, + { NULL }, +}; - static const struct option options[] = { - { "disable-ccache", no_argument, NULL, ARG_DISABLE_CCACHE }, - { "disable-snapshot", no_argument, NULL, ARG_DISABLE_SNAPSHOT }, - { "disable-tests", no_argument, NULL, ARG_DISABLE_TESTS }, - { "id", required_argument, NULL, ARG_ID }, - { "non-interactive", no_argument, NULL, ARG_NON_INTERACTIVE }, - { "target", required_argument, NULL, ARG_TARGET }, - { "help", no_argument, NULL, 'h' }, - { NULL }, - }; - int c; +static error_t parse(int key, char* arg, void* data) { + struct config* config = data; - for (;;) { - c = getopt_long(argc, argv, "h", options, NULL); - if (c < 0) + switch (key) { + case OPT_DISABLE_CCACHE: + config->flags |= PAKFIRE_BUILD_DISABLE_CCACHE; break; - switch (c) { - case 'h': - help(); - - case ARG_DISABLE_CCACHE: - config->flags |= PAKFIRE_BUILD_DISABLE_CCACHE; - break; + case OPT_DISABLE_SNAPSHOT: + config->flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT; + break; - case ARG_DISABLE_SNAPSHOT: - config->flags |= PAKFIRE_BUILD_DISABLE_SNAPSHOT; - break; + case OPT_DISABLE_TESTS: + config->flags |= PAKFIRE_BUILD_DISABLE_TESTS; + break; - case ARG_DISABLE_TESTS: - config->flags |= PAKFIRE_BUILD_DISABLE_TESTS; - break; + case OPT_ID: + config->id = arg; + break; - case ARG_ID: - config->id = optarg; - break; + case OPT_NON_INTERACTIVE: + config->flags &= ~PAKFIRE_BUILD_INTERACTIVE; + break; - case ARG_NON_INTERACTIVE: - config->flags &= ~PAKFIRE_BUILD_INTERACTIVE; - break; + case OPT_TARGET: + config->target = arg; + break; - case ARG_TARGET: - config->target = optarg; - break; + case ARGP_KEY_ARG: + if (config->num_makefiles >= MAX_MAKEFILES) + return -ENOBUFS; - case '?': - break; + config->makefiles[config->num_makefiles++] = arg; + break; - default: - break; - } + default: + return ARGP_ERR_UNKNOWN; } return 0; @@ -152,7 +138,8 @@ static int log_callback(struct pakfire_build* build, void* data, return 0; } -int cli_build(struct pakfire* pakfire, int argc, char* argv[]) { +int cli_build(void* data, int argc, char* argv[]) { + struct pakfire* pakfire = NULL; struct pakfire_build* build = NULL; struct config config = { .id = NULL, @@ -161,10 +148,17 @@ int cli_build(struct pakfire* pakfire, int argc, char* argv[]) { }; int r; - // Parse commandline - r = parse_argv(&config, argc, argv); + struct cli_config* cli_config = data; + + // Parse the command line + r = cli_parse(options, NULL, NULL, parse, argc, argv, &config); if (r) - return r; + goto ERROR; + + // Setup pakfire + r = cli_setup_pakfire(&pakfire, cli_config); + if (r) + goto ERROR; // Setup the build environment r = pakfire_build_create(&build, pakfire, config.id, config.flags); @@ -186,11 +180,11 @@ int cli_build(struct pakfire* pakfire, int argc, char* argv[]) { } // Process all packages - for (int i = 0; i < argc; i++) { + for (unsigned int i = 0; i < config.num_makefiles; i++) { // Run the build - r = pakfire_build_exec(build, argv[i]); + r = pakfire_build_exec(build, config.makefiles[i]); if (r) { - fprintf(stderr, "Could not build %s\n", argv[i]); + fprintf(stderr, "Could not build %s\n", config.makefiles[i]); goto ERROR; } } @@ -198,6 +192,8 @@ int cli_build(struct pakfire* pakfire, int argc, char* argv[]) { ERROR: if (build) pakfire_build_unref(build); + if (pakfire) + pakfire_unref(pakfire); return r; } diff --git a/src/cli/lib/build.h b/src/cli/lib/build.h index 8e1220895..cf8f236c0 100644 --- a/src/cli/lib/build.h +++ b/src/cli/lib/build.h @@ -21,8 +21,6 @@ #ifndef PAKFIRE_CLI_BUILD_H #define PAKFIRE_CLI_BUILD_H -#include - -int cli_build(struct pakfire* pakfire, int argc, char* argv[]); +int cli_build(void* data, int argc, char* argv[]); #endif /* PAKFIRE_CLI_BUILD_H */ diff --git a/src/cli/lib/command.c b/src/cli/lib/command.c index 81b46a461..7ac7b920d 100644 --- a/src/cli/lib/command.c +++ b/src/cli/lib/command.c @@ -18,12 +18,12 @@ # # #############################################################################*/ +#include #include #include +#include #include -#include - #include "command.h" static const struct command* command_find(const struct command* commands, const char* verb) { @@ -35,22 +35,143 @@ static const struct command* command_find(const struct command* commands, const return NULL; } -int command_dispatch(struct pakfire* pakfire, const struct command* commands, - int arg_index, int argc, char* argv[]) { - const struct command* command = NULL; +static unsigned int count_arguments(int argc, char* argv[]) { + unsigned int arguments = 0; + + for (int i = 1; i < argc; i++) { + if (*argv[i] == '-') + continue; - const char* verb = argv[arg_index - 1]; + arguments++; + } + + return arguments; +} - // Shift - argc -= arg_index; - argv += arg_index; +static int update_program_name(int argc, char* argv[], const char* verb) { + char* program_name = NULL; + int r; - // Find a matching command - command = command_find(commands, verb); - if (!command) { - fprintf(stderr, "Unknown command '%s'\n", verb); + // XXX maybe program_name should move into the ctx? + + // Program name + r = asprintf(&program_name, "%s %s", program_invocation_short_name, verb); + if (r < 0) + return r; + + program_invocation_short_name = argv[0] = program_name; + + return 0; +} + +static int command_run(const struct command* command, int argc, char* argv[], void* data) { + int r; + + if (!command) return -EINVAL; + + // Update the program name + r = update_program_name(argc, argv, command->verb); + if (r) + return r; + + // Run the command + return command->callback(data, argc, argv); +} + +struct command_ctx { + const struct argp_option* options; + const struct command* commands; + command_parse parse; + void* data; + + // The selected command + const struct command* command; + int argc; + char** argv; +}; + +static error_t __command_parse(int key, char* arg, struct argp_state* state) { + struct command_ctx* ctx = state->input; + int r; + + // Just call the parse function if we don't have any commands + if (!ctx->commands) + return ctx->parse(key, arg, ctx->data); + + switch (key) { + // Show help if no arguments have been passed + case ARGP_KEY_NO_ARGS: + argp_failure(state, EXIT_FAILURE, 0, "Missing command"); + break; + + // Try to find a command + case ARGP_KEY_ARG: + const struct command* command = ctx->command = command_find(ctx->commands, arg); + + // Fail if the command wasn't found + if (!command) { + argp_failure(state, EXIT_FAILURE, 0, "Unknown command '%s'", arg); + break; + } + + // Return UNKNOWN so that we get called for ARGP_KEY_ARGS + return ARGP_ERR_UNKNOWN; + + // Store all remaining options & arguments + case ARGP_KEY_ARGS: + ctx->argc = state->argc - state->next; + ctx->argv = &state->argv[state->next]; + break; + + // Do not pass any other things to the callback + case ARGP_KEY_END: + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + case ARGP_KEY_INIT: + case ARGP_KEY_FINI: + break; + + // Otherwise call the callback + default: + return ctx->parse(key, arg, ctx->data); + } + + return 0; +} + +int cli_parse(const struct argp_option* options, const struct command* commands, + const char* doc, command_parse parse, int argc, char** argv, void* data) { + int r; + + // Setup context + struct command_ctx ctx = { + .options = options, + .commands = commands, + .parse = parse, + .data = data, + }; + + // Setup the parser + struct argp parser = { + .options = options, + .parser = __command_parse, + .args_doc = doc, + }; + int arg_index = 0; + + // Parse command line options + r = argp_parse(&parser, argc, argv, ARGP_IN_ORDER, &arg_index, &ctx); + if (r) + return r; + + // Dispatch the selected command + if (commands) { + // Run the command + r = command_run(ctx.command, ctx.argc, ctx.argv, ctx.data); + if (r) + return r; } - return command->callback(pakfire, argc, argv); + return 0; } diff --git a/src/cli/lib/command.h b/src/cli/lib/command.h index 5caa7d0dd..d205ec891 100644 --- a/src/cli/lib/command.h +++ b/src/cli/lib/command.h @@ -21,15 +21,20 @@ #ifndef PAKFIRE_CLI_COMMAND_H #define PAKFIRE_CLI_COMMAND_H -#include +#include + +typedef error_t (*command_parse)(int key, char* arg, void* data); struct command { const char* verb; - int flags; - int (*callback)(struct pakfire* pakfire, int argc, char* argv[]); + enum { + CLI_REQUIRE_ONE_ARGUMENT = (1 << 0), + CLI_REQUIRE_ONE_OR_MORE_ARGUMENTS = (1 << 1), + } flags; + int (*callback)(void* config, int argc, char* argv[]); }; -int command_dispatch(struct pakfire* pakfire, const struct command* commands, - int arg_index, int argc, char* argv[]); +int cli_parse(const struct argp_option* options, const struct command* commands, + const char* doc, command_parse parse, int argc, char** argv, void* data); #endif /* PAKFIRE_CLI_COMMAND_H */ diff --git a/src/cli/lib/image.c b/src/cli/lib/image.c index 79d050c9b..1d60e20f3 100644 --- a/src/cli/lib/image.c +++ b/src/cli/lib/image.c @@ -26,9 +26,15 @@ int cli_image(struct pakfire* pakfire, int argc, char* argv[]) { static const struct command commands[] = { +#if 0 { "create", 0, cli_image_create }, +#endif { NULL }, }; + return 0; + +#if 0 return command_dispatch(pakfire, commands, 1, argc, argv); +#endif } diff --git a/src/cli/lib/pakfire.c b/src/cli/lib/pakfire.c new file mode 100644 index 000000000..146ca4013 --- /dev/null +++ b/src/cli/lib/pakfire.c @@ -0,0 +1,109 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2023 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#include +#include + +#include +#include + +#include "pakfire.h" +#include "progressbar.h" + +static FILE* open_distro_config(const char* distro) { + char path[PATH_MAX]; + FILE* f = NULL; + int r; + + // XXX hard-coded path + + // Make the path + r = snprintf(path, sizeof(path), "/etc/pakfire/distros/%s.conf", distro); + if (r < 0) + return NULL; + + // Open the configuration file + f = fopen(path, "r"); + if (!f) { + switch (errno) { + case ENOENT: + fprintf(stderr, "Could not find distro '%s': %m\n", distro); + break; + + default: + break; + } + } + + return f; +} + +static void cli_set_repo_enabled(struct pakfire* pakfire, const char* name, int enabled) { + struct pakfire_repo* repo = NULL; + + // Find the repository + repo = pakfire_get_repo(pakfire, name); + + // Ignore if the repository could not be found + if (!repo) + return; + + // Set status + pakfire_repo_set_enabled(repo, enabled); + pakfire_repo_unref(repo); +} + +int cli_setup_pakfire(struct pakfire** pakfire, struct cli_config* config) { + struct pakfire* p = NULL; + FILE* f = NULL; + int r; + + // Open the distro configuration + f = open_distro_config(config->distro); + if (!f) { + r = 1; + goto ERROR; + } + + // Initialize Pakfire + r = pakfire_create(&p, NULL, config->arch, f, config->flags, NULL, NULL); + if (r) + goto ERROR; + + // Setup progress callback + pakfire_set_setup_progress_callback(p, cli_setup_progressbar, NULL); + + // Enable repositories + for (unsigned int i = 0; i < config->num_enable_repos; i++) + cli_set_repo_enabled(p, config->enable_repos[i], 1); + + // Disable repositories + for (unsigned int i = 0; i < config->num_disable_repos; i++) + cli_set_repo_enabled(p, config->disable_repos[i], 0); + + // Return pointer + *pakfire = p; + +ERROR: + if (f) + fclose(f); + + return r; +} diff --git a/src/cli/lib/pakfire.h b/src/cli/lib/pakfire.h new file mode 100644 index 000000000..ee39ff529 --- /dev/null +++ b/src/cli/lib/pakfire.h @@ -0,0 +1,42 @@ +/*############################################################################# +# # +# Pakfire - The IPFire package management system # +# Copyright (C) 2023 Pakfire development team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +#############################################################################*/ + +#ifndef PAKFIRE_CLI_PAKFIRE_H +#define PAKFIRE_CLI_PAKFIRE_H + +#include + +#define MAX_REPOS 16 + +struct cli_config { + const char* distro; + char* arch; + int flags; + + // Repos + const char* enable_repos[MAX_REPOS]; + unsigned int num_enable_repos; + const char* disable_repos[MAX_REPOS]; + unsigned int num_disable_repos; +}; + +int cli_setup_pakfire(struct pakfire** pakfire, struct cli_config* config); + +#endif /* PAKFIRE_CLI_PAKFIRE_H */ diff --git a/src/cli/lib/repo.c b/src/cli/lib/repo.c index 6caff547f..6d4fa4a9f 100644 --- a/src/cli/lib/repo.c +++ b/src/cli/lib/repo.c @@ -26,9 +26,15 @@ int cli_repo(struct pakfire* pakfire, int argc, char* argv[]) { static const struct command commands[] = { +#if 0 { "compose", 0, cli_repo_compose }, +#endif { NULL }, }; + return 0; + +#if 0 return command_dispatch(pakfire, commands, 1, argc, argv); +#endif } diff --git a/src/cli/pakfire-builder.c b/src/cli/pakfire-builder.c index 5e3f8f55c..60c7265c4 100644 --- a/src/cli/pakfire-builder.c +++ b/src/cli/pakfire-builder.c @@ -34,6 +34,7 @@ #include "lib/dist.h" #include "lib/image.h" #include "lib/info.h" +#include "lib/pakfire.h" #include "lib/progressbar.h" #include "lib/provides.h" #include "lib/repo.h" @@ -44,20 +45,6 @@ #include "lib/terminal.h" #include "lib/version.h" -#define MAX_REPOS 16 - -struct config { - const char* distro; - char* arch; - int flags; - - // Repos - const char* enable_repos[MAX_REPOS]; - unsigned int num_enable_repos; - const char* disable_repos[MAX_REPOS]; - unsigned int num_disable_repos; -}; - const char* argp_program_version = PACKAGE_VERSION; enum { @@ -77,7 +64,8 @@ static struct argp_option options[] = { }; static const struct command commands[] = { - { "build", 0, cli_build }, + { "build", CLI_REQUIRE_ONE_OR_MORE_ARGUMENTS, cli_build }, +#if 0 { "clean", 0, cli_clean }, { "dist", 0, cli_dist }, { "image", 0, cli_image }, @@ -88,6 +76,7 @@ static const struct command commands[] = { { "requires", 0, cli_requires }, { "shell", 0, cli_shell }, { "search", 0, cli_search }, +#endif { NULL }, }; @@ -103,8 +92,8 @@ const char* doc = "shell [OPTIONS...]\n" "search [OPTIONS...] PATTERN"; -static error_t parse(int key, char* arg, struct argp_state* state) { - struct config* config = state->input; +static error_t parse(int key, char* arg, void* data) { + struct cli_config* config = data; switch (key) { case OPT_ARCH: @@ -136,14 +125,6 @@ static error_t parse(int key, char* arg, struct argp_state* state) { config->disable_repos[config->num_disable_repos++] = arg; break; - // Show help if no arguments have been passed - case ARGP_KEY_NO_ARGS: - argp_usage(state); - - // Stop parsing when we find the first argument - case ARGP_KEY_ARG: - break; - default: return ARGP_ERR_UNKNOWN; } @@ -151,120 +132,14 @@ static error_t parse(int key, char* arg, struct argp_state* state) { return 0; } -static void cli_set_repo_enabled(struct pakfire* pakfire, const char* name, int enabled) { - struct pakfire_repo* repo = NULL; - - // Find the repository - repo = pakfire_get_repo(pakfire, name); - - // Ignore if the repository could not be found - if (!repo) - return; - - // Set status - pakfire_repo_set_enabled(repo, enabled); - pakfire_repo_unref(repo); -} - -static FILE* open_distro_config(const char* distro) { - char path[PATH_MAX]; - FILE* f = NULL; - int r; - - // XXX hard-coded path - - // Make the path - r = snprintf(path, sizeof(path), "/etc/pakfire/distros/%s.conf", distro); - if (r < 0) - return NULL; - - // Open the configuration file - f = fopen(path, "r"); - if (!f) { - switch (errno) { - case ENOENT: - fprintf(stderr, "Could not find distro '%s': %m\n", distro); - break; - - default: - break; - } - } - - return f; -} - -static int setup_pakfire(const struct config* config, struct pakfire** pakfire) { - struct pakfire* p = NULL; - FILE* f = NULL; - int r; - - // Open the distro configuration - f = open_distro_config(config->distro); - if (!f) { - r = 1; - goto ERROR; - } - - // Initialize Pakfire - r = pakfire_create(&p, NULL, config->arch, f, config->flags, NULL, NULL); - if (r) - goto ERROR; - - // Setup progress callback - pakfire_set_setup_progress_callback(p, cli_setup_progressbar, NULL); - - // Enable repositories - for (unsigned int i = 0; i < config->num_enable_repos; i++) - cli_set_repo_enabled(p, config->enable_repos[i], 1); - - // Disable repositories - for (unsigned int i = 0; i < config->num_disable_repos; i++) - cli_set_repo_enabled(p, config->disable_repos[i], 0); - - // Return pointer - *pakfire = p; - -ERROR: - if (f) - fclose(f); - - return r; -} - int main(int argc, char* argv[]) { - struct config config = { + struct cli_config config = { // XXX hard-coded distro .distro = "ipfire3", .arch = NULL, .flags = PAKFIRE_FLAGS_BUILD, }; - struct pakfire* pakfire = NULL; - int r; - - struct argp parser = { - .options = options, - .parser = parse, - .args_doc = doc, - }; - int arg_index = 0; - - // Parse command line options - r = argp_parse(&parser, argc, argv, 0, &arg_index, &config); - if (r) - goto ERROR; - - // Setup Pakfire - r = setup_pakfire(&config, &pakfire); - if (r) - goto ERROR; - - // Run a command - r = command_dispatch(pakfire, commands, arg_index - 1, argc, argv); - -ERROR: - if (pakfire) - pakfire_unref(pakfire); - return r; + // Parse the command line and run any commands + return cli_parse(options, commands, doc, parse, argc, argv, &config); } diff --git a/src/cli/pakfire.c b/src/cli/pakfire.c index 438711a06..5f835f900 100644 --- a/src/cli/pakfire.c +++ b/src/cli/pakfire.c @@ -60,6 +60,7 @@ struct config { static int cli_main(struct pakfire* pakfire, int argc, char* argv[]) { static const struct command commands[] = { +#if 0 { "clean", 0, cli_clean }, { "info", 0, cli_info }, { "install", 0, cli_install }, @@ -70,10 +71,14 @@ static int cli_main(struct pakfire* pakfire, int argc, char* argv[]) { { "search", 0, cli_search }, { "sync", 0, cli_sync }, { "update", 0, cli_update }, +#endif { NULL }, }; +#if 0 return command_dispatch(pakfire, commands, argc, argv); +#endif + return 0; } static void help(void) {