mirror of
https://github.com/pygos/pkg-utils.git
synced 2024-12-22 18:00:49 +01:00
Add buildstrategy command
Basically an improved port of the makeshift depgraph from the pygos build system. Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
This commit is contained in:
parent
6dd9ae94bd
commit
af7c6f09df
3 changed files with 367 additions and 0 deletions
|
@ -22,6 +22,10 @@ pkg_SOURCES += main/cmd/install/collect.c main/cmd/install/install.c
|
|||
pkg_SOURCES += main/cmd/install/install.h main/cmd/install/pkglist.c
|
||||
pkg_SOURCES += main/cmd/install/tsort.c
|
||||
|
||||
# buildstrategy command
|
||||
pkg_SOURCES += main/cmd/buildstrategy/buildstrategy.h
|
||||
pkg_SOURCES += main/cmd/buildstrategy/buildstrategy.c
|
||||
|
||||
# unpack command
|
||||
pkg_SOURCES += main/cmd/unpack.c
|
||||
|
||||
|
|
336
main/cmd/buildstrategy/buildstrategy.c
Normal file
336
main/cmd/buildstrategy/buildstrategy.c
Normal file
|
@ -0,0 +1,336 @@
|
|||
#include "buildstrategy.h"
|
||||
|
||||
typedef int (*line_handler_t)(const char *lhs, const char *rhs);
|
||||
|
||||
static int foreach_line(const char *filename, line_handler_t cb)
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
|
||||
for (;;) {
|
||||
char *line = NULL, *rhs, *lhs, *ptr;
|
||||
size_t n = 0;
|
||||
ssize_t ret;
|
||||
|
||||
errno = 0;
|
||||
ret = getline(&line, &n, f);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == 0) {
|
||||
free(line);
|
||||
break;
|
||||
}
|
||||
perror(filename);
|
||||
free(line);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rhs = strchr(line, ',');
|
||||
if (rhs == NULL) {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
*(rhs++) = '\0';
|
||||
while (isspace(*rhs))
|
||||
++rhs;
|
||||
|
||||
ptr = rhs;
|
||||
while (*ptr != '\0' && !isspace(*ptr))
|
||||
++ptr;
|
||||
*ptr = '\0';
|
||||
|
||||
lhs = line;
|
||||
while (isspace(*lhs))
|
||||
++lhs;
|
||||
|
||||
if (*lhs == '\0' || *rhs == '\0') {
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cb(lhs, rhs)) {
|
||||
free(line);
|
||||
fclose(f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static hash_table_t tbl_provides;
|
||||
static hash_table_t tbl_sourcepkgs;
|
||||
|
||||
static int handle_depends(const char *sourcepkg, const char *binpkg)
|
||||
{
|
||||
source_pkg_t *src, *dep;
|
||||
size_t count;
|
||||
void *new;
|
||||
|
||||
src = hash_table_lookup(&tbl_sourcepkgs, sourcepkg);
|
||||
if (src == NULL)
|
||||
return 0;
|
||||
|
||||
dep = hash_table_lookup(&tbl_provides, binpkg);
|
||||
if (dep == NULL) {
|
||||
fprintf(stderr, "nothing provides '%s' required by '%s'\n",
|
||||
binpkg, sourcepkg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((src->num_depends % 10) == 0) {
|
||||
count = src->num_depends + 10;
|
||||
|
||||
new = realloc(src->depends, sizeof(src->depends[0]) * count);
|
||||
if (new == NULL) {
|
||||
fputs("out of memory\n", stderr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
src->depends = new;
|
||||
}
|
||||
|
||||
src->depends[src->num_depends++] = dep;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_provides(const char *sourcepkg, const char *binpkg)
|
||||
{
|
||||
source_pkg_t *src = NULL;
|
||||
|
||||
src = hash_table_lookup(&tbl_provides, binpkg);
|
||||
if (src != NULL) {
|
||||
fprintf(stderr, "%s: package already provided by %s\n",
|
||||
binpkg, src->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
src = hash_table_lookup(&tbl_sourcepkgs, sourcepkg);
|
||||
|
||||
if (src == NULL) {
|
||||
src = calloc(1, sizeof(*src));
|
||||
if (src == NULL)
|
||||
goto fail_oom;
|
||||
|
||||
src->name = strdup(sourcepkg);
|
||||
if (src->name == NULL)
|
||||
goto fail_oom;
|
||||
|
||||
if (hash_table_set(&tbl_sourcepkgs, sourcepkg, src))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return hash_table_set(&tbl_provides, binpkg, src);
|
||||
fail_oom:
|
||||
fputs("out of memory\n", stderr);
|
||||
fail:
|
||||
if (src != NULL)
|
||||
free(src->name);
|
||||
free(src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static const struct option long_opts[] = {
|
||||
{ "provides", no_argument, NULL, 'p' },
|
||||
{ "depends", no_argument, NULL, 'd' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *short_opts = "p:d:";
|
||||
|
||||
static int process_args(int argc, char **argv)
|
||||
{
|
||||
const char *provides = NULL, *depends = NULL;
|
||||
int i;
|
||||
|
||||
for (;;) {
|
||||
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||||
if (i == -1)
|
||||
break;
|
||||
|
||||
switch (i) {
|
||||
case 'p':
|
||||
provides = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
depends = optarg;
|
||||
break;
|
||||
default:
|
||||
goto fail_arg;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
fputs("no packages specified\n", stderr);
|
||||
goto fail_arg;
|
||||
}
|
||||
|
||||
if (provides == NULL) {
|
||||
fputs("no source package provides specified\n", stderr);
|
||||
goto fail_arg;
|
||||
}
|
||||
|
||||
if (hash_table_init(&tbl_provides, 1024))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (hash_table_init(&tbl_sourcepkgs, 1024))
|
||||
goto fail_provides;
|
||||
|
||||
if (foreach_line(provides, handle_provides))
|
||||
goto fail_srcpkg;
|
||||
|
||||
if (depends != NULL && foreach_line(depends, handle_depends) != 0)
|
||||
goto fail_srcpkg;
|
||||
|
||||
return 0;
|
||||
fail_srcpkg:
|
||||
hash_table_cleanup(&tbl_sourcepkgs);
|
||||
fail_provides:
|
||||
hash_table_cleanup(&tbl_provides);
|
||||
return -1;
|
||||
fail_arg:
|
||||
tell_read_help(argv[0]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pkg_mark_deps(source_pkg_t *pkg)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (pkg->flags & FLAG_BUILD_PKG)
|
||||
return;
|
||||
|
||||
pkg->flags |= FLAG_BUILD_PKG;
|
||||
|
||||
for (i = 0; i < pkg->num_depends; ++i) {
|
||||
if ((pkg->depends[i]->flags & FLAG_BUILD_PKG) == 0)
|
||||
pkg_mark_deps(pkg->depends[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int remove_untagged(void *usr, const char *name, void *p)
|
||||
{
|
||||
int mask = *((int *)usr);
|
||||
source_pkg_t *pkg = p;
|
||||
(void)name;
|
||||
|
||||
if ((pkg->flags & mask) == 0) {
|
||||
free(pkg->name);
|
||||
free(pkg->depends);
|
||||
free(pkg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int remove_unmarked_deps(void *usr, const char *name, void *p)
|
||||
{
|
||||
source_pkg_t *pkg = p;
|
||||
size_t i = 0;
|
||||
(void)usr; (void)name;
|
||||
|
||||
while (i < pkg->num_depends) {
|
||||
if ((pkg->depends[i]->flags & FLAG_BUILD_PKG) == 0) {
|
||||
pkg->depends[i] = pkg->depends[pkg->num_depends - 1];
|
||||
pkg->num_depends -= 1;
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int find_no_dep(void *usr, const char *name, void *p)
|
||||
{
|
||||
source_pkg_t *pkg = p;
|
||||
int *found = usr;
|
||||
|
||||
if (pkg->num_depends == 0) {
|
||||
printf("%s\n", name);
|
||||
pkg->flags &= ~FLAG_BUILD_PKG;
|
||||
*found = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_buildstrategy(int argc, char **argv)
|
||||
{
|
||||
int i, ret = EXIT_FAILURE;
|
||||
source_pkg_t *pkg;
|
||||
|
||||
if (process_args(argc, argv))
|
||||
return EXIT_FAILURE;
|
||||
|
||||
for (i = optind; i < argc; ++i) {
|
||||
pkg = hash_table_lookup(&tbl_provides, argv[i]);
|
||||
|
||||
if (pkg == NULL) {
|
||||
fprintf(stderr, "nothing provides '%s'\n", argv[i]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pkg_mark_deps(pkg);
|
||||
}
|
||||
|
||||
i = FLAG_BUILD_PKG;
|
||||
hash_table_foreach(&tbl_sourcepkgs, &i, remove_untagged);
|
||||
|
||||
while (tbl_sourcepkgs.count > 0) {
|
||||
i = 0;
|
||||
hash_table_foreach(&tbl_sourcepkgs, &i, find_no_dep);
|
||||
if (!i) {
|
||||
fputs("cycle detected in package dependencies!\n",
|
||||
stderr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
hash_table_foreach(&tbl_sourcepkgs, NULL, remove_unmarked_deps);
|
||||
|
||||
i = FLAG_BUILD_PKG;
|
||||
hash_table_foreach(&tbl_sourcepkgs, &i, remove_untagged);
|
||||
}
|
||||
|
||||
ret = EXIT_SUCCESS;
|
||||
out:
|
||||
i = 0;
|
||||
hash_table_foreach(&tbl_sourcepkgs, &i, remove_untagged);
|
||||
hash_table_cleanup(&tbl_sourcepkgs);
|
||||
hash_table_cleanup(&tbl_provides);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static command_t buildstrategy = {
|
||||
.cmd = "buildstrategy",
|
||||
.usage = "[OPTIONS...] <packages>",
|
||||
.s_desc = "work out what source packages to build in what order",
|
||||
.l_desc =
|
||||
"The buildstrategy command takes as input a description of what binary\n"
|
||||
"packages a source package needs in order to build and what binary packages\n"
|
||||
"in turn produces in the process. The command then takes from the command\n"
|
||||
"line arguments a list of desired binary packages and works out a minimal\n"
|
||||
"set of source packages to build and in what order they should be built.\n"
|
||||
"\n"
|
||||
"Possible options:\n"
|
||||
" --provides, -p <file> A two column CSV file. Each line contains the name\n"
|
||||
" of a source package and a binary package that it\n"
|
||||
" produces when built. If a source packages provides\n"
|
||||
" multiple binary packages, use one line for each\n"
|
||||
" binary package.\n"
|
||||
" --depends, -d <file> A two column CSV file. Each line contains the name\n"
|
||||
" of a source package and a binary package that it\n"
|
||||
" requires to build.\n",
|
||||
.run_cmd = cmd_buildstrategy,
|
||||
};
|
||||
|
||||
REGISTER_COMMAND(buildstrategy)
|
27
main/cmd/buildstrategy/buildstrategy.h
Normal file
27
main/cmd/buildstrategy/buildstrategy.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef BUILDSTRATEGY_H
|
||||
#define BUILDSTRATEGY_H
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "util/hashtable.h"
|
||||
|
||||
enum {
|
||||
FLAG_BUILD_PKG = 0x01,
|
||||
};
|
||||
|
||||
typedef struct source_pkg_t {
|
||||
char *name;
|
||||
|
||||
struct source_pkg_t **depends;
|
||||
size_t num_depends;
|
||||
|
||||
int flags;
|
||||
} source_pkg_t;
|
||||
|
||||
#endif /* BUILDSTRATEGY_H */
|
Loading…
Reference in a new issue