Stefan Fiedler: Add support for automated run-time dependency checks (partial). Implementation in rocket.sh is missing. diff -dur mine-0.25/create.c mine-0.25-p/create.c --- mine-0.25/create.c 2005-03-23 09:51:06.000000000 +0100 +++ mine-0.25-p/create.c 2006-12-29 19:12:05.000000000 +0100 @@ -94,6 +94,7 @@ exit(1); } +/* All subtrees are optional. */ char * varadm_subtrees[] = { "packages", @@ -102,6 +103,9 @@ "cksums", "dependencies", "descs", + "provides", + "requires", + "conflicts", NULL }; @@ -128,7 +132,8 @@ for (i=0; varadm_subtrees[i]; i++) { snprintf(filename, 1024, "%s/%s/%s", varadm, varadm_subtrees[i], package); - gem_create_addfile(&c, varadm_subtrees[i], filename); + if (!access(filename, R_OK)) + gem_create_addfile(&c, varadm_subtrees[i], filename); } snprintf(filename, 1024, "%s/descs/%s", varadm, package); diff -dur mine-0.25/gasgui.h mine-0.25-p/gasgui.h --- mine-0.25/gasgui.h 2005-03-23 09:51:06.000000000 +0100 +++ mine-0.25-p/gasgui.h 2006-12-29 19:12:05.000000000 +0100 @@ -25,6 +25,9 @@ struct dependency { char *name; + char *version; + /* The enumeration denotes no op, <, <=, >, >=, =, or @ (string compare). */ + enum { null, lt, le, gt, ge, eq, at } cmp_op; struct package *pkg; struct dependency *next; }; @@ -36,7 +39,7 @@ int action; /* -1=remove, 0=nothing, 2=install */ int *size; /* filesystem_count + 1 elements */ struct package *next; - struct dependency *deps; + struct dependency *provides, *requires, *conflicts; }; struct directory_entry { diff -dur mine-0.25/mkpdb.c mine-0.25-p/mkpdb.c --- mine-0.25/mkpdb.c 2005-03-23 09:51:06.000000000 +0100 +++ mine-0.25-p/mkpdb.c 2006-12-29 19:12:05.000000000 +0100 @@ -72,6 +72,15 @@ if ( dump_field(&c, "descs") == -1 ) goto error; if ( dump_field(&c, "dependencies") == -1 ) goto error; if ( dump_field(&c, "cksums") == -1 ) goto error; + if ( dump_field(&c, "provides") == -1) { + putchar('\027'); putchar('\n'); + } + if ( dump_field(&c, "requires") == -1) { + putchar('\027'); putchar('\n'); + } + if ( dump_field(&c, "conflicts") == -1) { + putchar('\027'); putchar('\n'); + } putchar('\004'); putchar('\n'); diff -dur mine-0.25/readdb.c mine-0.25-p/readdb.c --- mine-0.25/readdb.c 2005-10-04 18:35:32.000000000 +0200 +++ mine-0.25-p/readdb.c 2006-12-29 20:27:16.000000000 +0100 @@ -40,7 +40,6 @@ int filesystem_count; struct memdb_t files_on_disks; -struct memdb_t pkg_addr_hash; struct directory * get_create_directory(struct directory * base, char *name) @@ -137,9 +136,6 @@ if ( last_p ) last_p->next = p; (last_p=p)->next = NULL; - snprintf(line, 1024, "%p", p); - memdb_put(&pkg_addr_hash, packagename, line); - d = get_create_directory(rootdir, "all"); e = calloc(1, sizeof(struct directory_entry)); e->next = d->list; d->list = e; e->content.pkg = p; @@ -165,10 +165,13 @@ p->version = malloc(line_length-3); sscanf(line+4, "%s", p->version); #else - p->version = strdup(line+4); - for (i=0; p->version[i]; i++) - if (p->version[i] == ' ') - p->version[i] = '-'; + char *ver, *extraver; + ver = strtok(line+4, " "); + extraver = strtok(NULL, " "); + if (ver == NULL) ver = "0000"; + if (extraver == NULL) extraver = "0"; + sprintf(line,"%s-%s", ver, extraver); + p->version = strdup(line); #endif continue; } @@ -191,30 +187,6 @@ } if ( !dbf ) fclose(f); - /* read package dependencies */ - if ( !dbf ) { - snprintf(filename, PATH_MAX, "%s/%s/info/dependencies/%s", - sourcedir, config, packagename); - f = fopen(filename, "r"); - if ( f == NULL ) { - fprintf(stderr, "Can't open %s: %s\n", - filename, strerror(errno)); - return -1; - } - } - - while ( fgets(line, 1024, f) != NULL && strcmp(line,"\027\n") ) { - if ( (t = strchr(line, '\n')) != NULL ) *t=0; - if ( (t = strchr(line, ' ')) == NULL ) continue; - t++; - - if ( !strcmp(t, packagename) ) continue; - dep = calloc(1, sizeof(struct dependency)); - dep->name = malloc(strlen(t)+1); strcpy(dep->name, t); - dep->next = p->deps; p->deps = dep; - } - if ( !dbf ) fclose(f); - snprintf(line, 1024, "%s/pkgs/%s-%s.gem", config, p->name, p->version); if ( memdb_get(&files_on_disks, line) ) { p->disk_number = atoi(memdb_search_result); @@ -253,6 +225,11 @@ fclose(f); } + /* Skip var/adm/dependencies section. */ + if ( dbf ) { + while ( fgets(line, 1024, dbf) != NULL && strcmp(line,"\027\n") ); + } + if ( !dbf ) { snprintf(filename, PATH_MAX, "%s/%s/info/cksums/%s", sourcedir, config, packagename); @@ -288,6 +265,92 @@ if ( !dbf ) fclose(f); } + /* Read run-time dependencies sections. */ + if ( !dbf ) { + snprintf(filename, PATH_MAX, "%s/%s/info/provides/%s", + sourcedir, config, packagename); + f = fopen(filename, "r"); + } else { + f = dbf; + } + if ( f != NULL ) + while ( fgets(line, 1024, f) != NULL && strcmp(line,"\027\n") ) { + t=strtok(line, " \n"); if ( t == NULL ) continue; + + dep = calloc(1, sizeof(struct dependency)); + dep->name = malloc(strlen(t)+1); strcpy(dep->name, t); + t=strtok(NULL, " \n"); if ( t != NULL ) { + dep->version = malloc(strlen(t)+1); + strcpy(dep->version, t); + }; + dep->next = p->provides; p->provides = dep; + } + if ( !dbf ) fclose(f); + + if ( !dbf ) { + snprintf(filename, PATH_MAX, "%s/%s/info/requires/%s", + sourcedir, config, packagename); + f = fopen(filename, "r"); + } else { + f = dbf; + } + if ( f != NULL ) + while ( fgets(line, 1024, f) != NULL && strcmp(line,"\027\n") ) { + t=strtok(line, " \n"); if ( t == NULL ) continue; + + dep = calloc(1, sizeof(struct dependency)); + dep->name = malloc(strlen(t)+1); strcpy(dep->name, t); + t=strtok(NULL, " \n"); if ( t != NULL ) { + if (strlen(t) == 1) + switch (t[0]) { + case '<': dep->cmp_op=lt; break; + case '>': dep->cmp_op=gt; break; + case '=': dep->cmp_op=eq; break; + case '@': dep->cmp_op=at; break; + } + else if (!strcmp(t, "<=")) dep->cmp_op=le; + else if (!strcmp(t, ">=")) dep->cmp_op=ge; + }; + t=strtok(NULL, " \n"); if ( t != NULL ) { + dep->version = malloc(strlen(t)+1); + strcpy(dep->version, t); + }; + dep->next = p->requires; p->requires = dep; + } + if ( !dbf ) fclose(f); + + if ( !dbf ) { + snprintf(filename, PATH_MAX, "%s/%s/info/conflicts/%s", + sourcedir, config, packagename); + f = fopen(filename, "r"); + } else { + f = dbf; + } + if ( f != NULL ) + while ( fgets(line, 1024, f) != NULL && strcmp(line,"\027\n") ) { + t=strtok(line, " \n"); if ( t == NULL ) continue; + + dep = calloc(1, sizeof(struct dependency)); + dep->name = malloc(strlen(t)+1); strcpy(dep->name, t); + t=strtok(NULL, " \n"); if ( t != NULL ) { + if (strlen(t) == 1) + switch (t[0]) { + case '<': dep->cmp_op=lt; break; + case '>': dep->cmp_op=gt; break; + case '=': dep->cmp_op=eq; break; + case '@': dep->cmp_op=at; break; + } + else if (!strcmp(t, "<=")) dep->cmp_op=le; + else if (!strcmp(t, ">=")) dep->cmp_op=ge; + }; + t=strtok(NULL, " \n"); if ( t != NULL ) { + dep->version = malloc(strlen(t)+1); + strcpy(dep->version, t); + }; + dep->next = p->conflicts; p->conflicts = dep; + } + if ( !dbf ) fclose(f); + /* seek until EOF mark for this package */ if ( dbf ) { while ( fgets(line, 1024, f) != NULL && strcmp(line,"\004\n") ) continue; @@ -396,6 +459,54 @@ fclose(f); } +/* + Match provided and required dependencies. + Returns true if a provided dependency prov matches a required + dependency req. + Matching dependencies always have the same name. If the comparation + operator cmp_op is null, equal names are sufficient for a match. + For other cmp_op values, the return value depends on a comparison + between the versions of prov and req: + @ (at) compares if versions are equal strings; + other values compare the order of versions in major.minor + format (e.g. 0.7.9 < 0.7.10). +*/ +int match_dependency(struct dependency *req, struct dependency *prov) +{ + int result=0; + char * part1, *part2; + + if (strcmp(req->name, prov->name)) return 0; + if (req->cmp_op == null) return 1; + if (req->cmp_op == at) return (prov->version != NULL) && !strcmp(req->version, prov->version); + + char *saveptr1, *saveptr2; + part1=strtok_r(prov->version, ".", &saveptr1); + part2=strtok_r(req->version, ".", &saveptr2); + + while (part1 != NULL || part2 != NULL) + { + int i1 = (part1 == NULL) ? 0 : atoi(part1); + int i2 = (part2 == NULL) ? 0 : atoi(part2); + if (i1 < i2) { result=-1 ; break ; } + else if (i1 > i2) { result=1 ; break ; } + if (part1 != NULL) part1=strtok_r(NULL, ".", &saveptr1); + if (part2 != NULL) part2=strtok_r(NULL, ".", &saveptr2); + + } + switch (req->cmp_op) { + case lt: return result==-1; + case gt: return result==1; + case eq: return result==0; + case le: return result==0 || result==-1; + case ge: return result==0 || result==1; + case null: + case at: ; + } + + return 0; +} + int read_packages() { struct dirent **namelist; @@ -407,7 +518,6 @@ int i; memdb_init(&files_on_disks); - memdb_init(&pkg_addr_hash); get_filesystem_info(); printf("Reading package database. Please wait...\n"); @@ -460,7 +570,7 @@ return -1; } else { /* read packages in reversed order so the linked list is - * ordered well (new entries are insterted at the begin) */ + * ordered well (new entries are inserted at the begin) */ while (i--) { if ( namelist[i]->d_name[0] != '.' && strcmp(namelist[i]->d_name, "TRANS.TBL") ) @@ -479,25 +589,33 @@ for (d = directories; d != NULL; d = d->next) sort_directory(d); for (p = packages; p != NULL; p = p->next) { - /* fill the dependenies with references to the individual packges */ - for (next_dep = &(p->deps); *next_dep != NULL;) { - char* pkg_ptr_str = memdb_get(&pkg_addr_hash, (*next_dep)->name); - if (pkg_ptr_str) - sscanf(pkg_ptr_str, "%p", &(*next_dep)->pkg); - if ((*next_dep)->pkg) { - next_dep = &((*next_dep)->next); - } else { - /* drop dependency since it is not in db - this can happend - if multiple builds are run with modified packages */ - struct dependency *tbd_dep = *next_dep; - *next_dep = tbd_dep->next; - free (tbd_dep->name); - free (tbd_dep); + /* Fill the dependencies with references to the individual packages. */ + for (next_dep = &(p->requires); *next_dep != NULL;) { + struct package *prov; + for (prov = packages; prov != NULL; prov = prov->next) { + struct dependency *next_prov; + for (next_prov = prov->provides; next_prov != NULL; next_prov = next_prov->next) { + if (match_dependency(*next_dep, next_prov)) + { + (*next_dep)->pkg=prov; + next_dep=&((*next_dep)->next); + goto dep_matched; + } + } } - } + + /* drop dependency since it is not in db - this can happen + if multiple builds are run with modified packages */ + struct dependency *tbd_dep = *next_dep; + *next_dep = tbd_dep->next; + free (tbd_dep->name); + free (tbd_dep); + + dep_matched: ; + } } + memdb_free(&files_on_disks); - memdb_init(&pkg_addr_hash); return 0; } diff -dur mine-0.25/rocket.sh mine-0.25-p/rocket.sh --- mine-0.25/rocket.sh 2006-07-15 01:42:48.000000000 +0200 +++ mine-0.25-p/rocket.sh 2006-12-29 19:12:05.000000000 +0100 @@ -32,7 +32,6 @@ update .............. update the package cache upgrade ............. update all packages to newest version install ....... install/update package - inst-nodeps ... install without dependencies remove ........ remove package search ...... search package descriptions for this regex @@ -118,6 +117,9 @@ close("/var/adm/rocket/descs/" pkg); close("/var/adm/rocket/dependencies/" pkg); close("/var/adm/rocket/cksums/" pkg); + close("/var/adm/rocket/provides/" pkg); + close("/var/adm/rocket/requires/" pkg); + close("/var/adm/rocket/conflicts/" pkg); print pkg " '"$1"'/" pkg "-" ver ".gem" \ >> "/var/adm/rocket/locations.tmp"; } @@ -133,7 +135,9 @@ if (match($2, "^'$4'$") == 0) print > "/var/adm/rocket/dependencies/" pkg; } -chunk == 3 && !ignore { print > "/var/adm/rocket/cksums/" pkg; } +chunk == 4 && !ignore { print > "/var/adm/rocket/provides/" pkg; } +chunk == 5 && !ignore { print > "/var/adm/rocket/requires/" pkg; } +chunk == 6 && !ignore { print > "/var/adm/rocket/conflicts/" pkg; } chunk == 1 && $1 == "[V]" { ver = $2 "-" $3; } @@ -220,7 +224,9 @@ update) rm -rf /var/adm/rocket/{descs,dependencies,cksums} + rm -rf /var/adm/rocket/{provides,requires,conflicts} mkdir -p /var/adm/rocket/{descs,dependencies,cksums,dbs} + mkdir -p /var/adm/rocket/{provides,requires,conflicts} touch /var/adm/rocket/locations.tmp gen_a_list() { grep '^[ ]*'$1'[ ]\+' /etc/rocket.conf | \ @@ -360,8 +366,10 @@ install) shift - if [ $nodeps = 0 ] + if [ $nodeps = 1 ] then + deps="" + else deps=$( echo $( echo "$*" | tr ' ' '\n' | gawk ' @@ -405,8 +413,6 @@ } '; ); ) - else - deps="" fi if [ -n "$deps" ]; then diff -dur mine-0.25/selector.c mine-0.25-p/selector.c --- mine-0.25/selector.c 2005-12-27 20:21:48.000000000 +0100 +++ mine-0.25-p/selector.c 2006-12-29 19:12:05.000000000 +0100 @@ -110,7 +110,6 @@ assert_ptr(listitems, "dialog_menu"); for (i = 0; i < item_no; ++i) { - listitems[i].name = ItemName(i); listitems[i].text = ItemText(i); listitems[i].help = (dialog_vars.item_help) ? ItemHelp(i) : ""; @@ -130,7 +129,7 @@ { struct dependency *dep; - for (dep = p->deps; dep != NULL; dep = dep->next) + for (dep = p->requires; dep != NULL; dep = dep->next) if ( (dep->pkg->is_installed && dep->pkg->action < 0) || (!dep->pkg->is_installed && !dep->pkg->action) ) { if ( dep->pkg->is_dependency ) { @@ -402,8 +401,8 @@ "You can navigate through the menu by using either the arrow keys\n" "or +/-/btab/tab (for up, down, left, right).\n" "\n" -"Furthermore you can entry directories (de/select packages respect-\n" -"ively) by pressing the return-key while the button focus is on the\n" +"Furthermore you can enter directories (de/select packages respect-\n" +"ively) by pressing the return key while the button focus is on the\n" "`Select' button.\n" "\n" "There are also a few extra commands for additional information and\n" @@ -574,7 +573,7 @@ int rc, pkgcount; char *main_item; - /* Create enought space for the items[] and entries[] arrays */ + /* Create enough space for the items[] and entries[] arrays */ items = malloc( (directory_entry_count+20) * sizeof(char*) * 2 ); entries = malloc( (directory_entry_count+20) * sizeof(void*) ); main_item = malloc(10); --- mine-0.25/rocket.sh 2007-05-17 23:14:19.000000000 +0200 +++ mine-0.25/rocket 2007-05-17 23:13:02.000000000 +0200 @@ -1,5 +1,7 @@ #!/bin/bash +export LC_ALL=C + help() { cat << EOT @@ -60,6 +62,8 @@ esac done +var_adm_dir="$root/var/adm/" + upd_archive() { filename="$( echo "$2" | tr "/:" __; )" refetch=0 @@ -153,6 +157,71 @@ fi } +# resolve_dep +# +# return all packages in that provide meeting requirement. +# is one of <, >, <=, >=, =, or @. +# +resolve_dep () +{ + local library="$1" cmp_op="$2" version="$3" var_adm_dir="$4" pkg_set="$5" + local pkg lib ver + + if [ -z "$cmp_op" -o "$cmp_op" == "@" ] ; then + cd $var_adm_dir/provides + grep -m1 -e "^$library${version:+ }$version\( \|$\)" $pkg_set 2>/dev/null | + sed -e"s,\(.*\):\(.*\),\1," | tr "\n" " " + else + while read pkg lib ver ; do + version_compare "$ver" "$version" "$cmp_op" || continue + echo -n "$pkg " + done < <( cd $var_adm_dir/provides ; grep "^$library " $pkg_set 2>/dev/null | sed -e"s,\(.*\):\(.*\),\1 \2," ) + fi +} + +# get_requires ... +# +# get shared library requirements of the given packages +# +get_requires () +{ + ( cd $var_adm_dir/rocket/requires ; cat $* 2>/dev/null | sort -u ) +} + +version_compare() { + local ver1="$1" ver2="$2" cmp_op="$3" result=0 + local part1 part2 delim1 delim2 + + if [ "$cmp_op" == "@" ] ; then + [ "$ver1" == "$ver2" ] && return 0 || return 1 + fi + + while [ "$ver1" -o "$ver2" ] ; do + part1="${ver1%%[^0-9]*}" ; ver1="${ver1#$part1}" + part2="${ver2%%[^0-9]*}" ; ver2="${ver2#$part2}" + delim1="${ver1%%[0-9]*}" ; ver1="${ver1#$delim1}" + delim2="${ver2%%[0-9]*}" ; ver2="${ver2#$delim2}" + + # 1.2.0 == 1.2 + [ ! "$part1" ] && part1=0 + [ ! "$part2" ] && part2=0 + + if [ "$part1" -lt "$part2" ] ; then + result=2 ; break + elif [ "$part1" -gt "$part2" ] ; then + result=1 ; break + fi + done + + case $cmp_op in + "=") [ "$result" == 0 ] ;; + ">") [ "$result" == 1 ] ;; + "<") [ "$result" == 2 ] ;; + ">=") [ "$result" == 1 -o $result == 0 ] ;; + "<=") [ "$result" == 2 -o $result == 0 ] ;; + esac ; return $? +} + case "$1" in updsrc) @@ -370,49 +439,52 @@ then deps="" else - deps=$( echo $( echo "$*" | tr ' ' '\n' | -gawk ' + # for each required library of each selected pkg: + # 1) check if the library is in the installed or selected pkg set: + # 1a) if yes, the dependency is resolved. + # 2) If no, check if the library is in the installable set: + # 2a) if there is one pkg providing this library, add that pkg to the selected set. + # 2b) if there is more than one pkg providing this library, ask the user which ones to install. + # 3) If the library isn't in any set, issue a warning. The dependency is not resolved. -function get_deps(p, - depsfn) { - if (A[p]) return; - A[p] = 1; + INSTALLED_PKGS="$( cd $var_adm_dir/descs ; ls )" + SELECTED_PKGS="$*" + INSTALLABLE_PKGS="$( cd $var_adm_dir/rocket/descs ; ls )" + ADDED_PKGS="" - depsfn = "/var/adm/rocket/dependencies/" p; + # Initialize the shared library dependency queue. + # Each library in this queue is checked for open dependencies. + dep_queue="$( mktemp -t dep_queue.XXXXXX )" + dep_checked="$( mktemp -t dep_checked.XXXXXX )" + get_requires $SELECTED_PKGS > $dep_queue + echo > $dep_checked - while ((getline < depsfn) > 0) { - if (!D[$2]) { - D[$2] = 1; - get_deps($2); - } - } + while read lib cmp ver ; do + # Skip if we already checked this library. + [[ "`grep -m 1 -c "^$lib${cmp:+ }$cmp${ver:+ }$ver$" $dep_checked`" -gt 0 ]] && continue + echo "$lib${cmp:+ }$cmp${ver:+ }$ver" >> $dep_checked - close(depsfn); -} + deps="`resolve_dep "$lib" "$cmp" "$ver" "$var_adm_dir" "$INSTALLED_PKGS"`" + if [ -z "$deps" ] ; then + deps="`resolve_dep "$lib" "$cmp" "$ver" "$var_adm_dir/rocket" "$SELECTED_PKGS $ADDED_PKGS"`" + fi + if [ -z "$deps" ] ; then + deps="`resolve_dep "$lib" "$cmp" "$ver" "$var_adm_dir/rocket" "$INSTALLABLE_PKGS"`" -{ - P[$1] = 1; - get_deps($1); -} + [ -z "$deps" ] && continue -END { - for (d in D) { - if (!P[d]) { - pkgfn = "/var/adm/packages/" d; - if ((getline < pkgfn) > 0) - close(pkgfn); - else { - descfn = "/var/adm/rocket/descs/" d; - if ((getline < descfn) > 0) { - close(descfn); - print d; - } - } - } - } -} + # adding pkg to selected set + # user input required here; for now the first pkg is selected + ADDED_PKGS="$ADDED_PKGS ${deps%% *}" -'; ); ) + # adding requirements of pkg to dependency queue + get_requires $deps >> $dep_queue + fi +# echo "$lib (\"$cmp\" \"$ver\")... ${deps:-NO PROVIDER FOUND}" + done < $dep_queue + rm -f $dep_queue $dep_checked + + deps="$ADDED_PKGS" fi if [ -n "$deps" ]; then