From: J. Ali Harlow Date: Thu, 22 Jan 2009 22:54:45 +0000 (+0000) Subject: Implement relocatations when installing rpms. X-Git-Tag: 0.1~27 X-Git-Url: http://project.juiblex.co.uk/git/?a=commitdiff_plain;h=f4244de21a92517157cc90961b1ea9d33a19c48d;p=razor.git Implement relocatations when installing rpms. --- diff --git a/Makefile.am b/Makefile.am index f602bab..076084c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = data docs gl librazor src po +SUBDIRS = data docs gl librazor src test po ACLOCAL_AMFLAGS = -I gl/m4 diff --git a/configure.ac b/configure.ac index d164bfb..60879df 100644 --- a/configure.ac +++ b/configure.ac @@ -235,6 +235,7 @@ docs/Makefile docs/version.xml po/Makefile.in gl/Makefile +test/Makefile ]) dnl ========================================================================== diff --git a/librazor/iterator.c b/librazor/iterator.c index 2075be1..533f18a 100644 --- a/librazor/iterator.c +++ b/librazor/iterator.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc + * Copyright (C) 2009 J. Ali Harlow * * 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 @@ -229,6 +230,83 @@ razor_property_iterator_destroy(struct razor_property_iterator *pi) free(pi); } +RAZOR_EXPORT struct razor_file_iterator * +razor_file_iterator_create(struct razor_set *set, struct razor_package *package) +{ + struct razor_file_iterator *fi; + + assert (set != NULL); + assert (package != NULL); + + fi = zalloc(sizeof *fi); + fi->set = set; + fi->index = list_first(&package->files, &set->file_pool); + array_init(&fi->path); + + return fi; +} + +RAZOR_EXPORT int +razor_file_iterator_next(struct razor_file_iterator *fi, + const char **name) +{ + struct razor_entry *e, *dir, *entries; + char *pool, *s, *f; + + assert (fi != NULL); + + if (!fi->index) { + *name = NULL; + return 0; + } + + entries = (struct razor_entry *) fi->set->files.data; + pool = fi->set->file_string_pool.data; + + dir = entries; + fi->path.size = 0; + for(;;) { + e = dir; + do { + if (entries + fi->index->data == e) { + f = pool + e->name; + s = array_add(&fi->path, strlen(f) + 1); + strcpy(s, f); + if (fi->path.size == 1) { + array_add(&fi->path, 1); + strcpy(fi->path.data, "/"); + } + *name = fi->path.data; + fi->index = list_next(fi->index); + return 1; + } + } while (!((e++)->flags & RAZOR_ENTRY_LAST)); + for(e--; e >= dir; e--) + if (e->start && fi->index->data >= e->start) + break; + if (e < dir) + break; + f = pool + e->name; + s = array_add(&fi->path, strlen(f) + 1); + strcpy(s, f); + s += strlen(f); + *s = '/'; + dir = entries + e->start; + } + + printf("file_iterator_next: Failed to find file %d\n",fi->index->data); + *name = NULL; + return 0; +} + +RAZOR_EXPORT void razor_file_iterator_destroy(struct razor_file_iterator *fi) +{ + assert (fi != NULL); + + array_release(&fi->path); + free(fi); +} + struct razor_package_query { struct razor_set *set; char *vector; diff --git a/librazor/razor-internal.h b/librazor/razor-internal.h index afd9cda..8fe0f3b 100644 --- a/librazor/razor-internal.h +++ b/librazor/razor-internal.h @@ -200,6 +200,12 @@ struct razor_property_iterator { struct list *index; }; +struct razor_file_iterator { + struct razor_set *set; + struct array path; + struct list *index; +}; + struct razor_entry * razor_set_find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern); diff --git a/librazor/razor.c b/librazor/razor.c index ee80b0a..e83d128 100644 --- a/librazor/razor.c +++ b/librazor/razor.c @@ -582,76 +582,22 @@ razor_set_list_files(struct razor_set *set, const char *pattern) list_dir(set, e, buffer, base); } -static struct list * -list_package_files(struct razor_set *set, struct list *r, - struct razor_entry *dir, uint32_t end, - char *prefix) -{ - struct razor_entry *e, *f, *entries; - uint32_t next, file; - char *pool; - int len; - - entries = (struct razor_entry *) set->files.data; - pool = set->file_string_pool.data; - - e = entries + dir->start; - do { - if (entries + r->data == e) { - printf("%s/%s\n", prefix, pool + e->name); - r = list_next(r); - if (!r) - return NULL; - if (r->data >= end) - return r; - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST)); - - e = entries + dir->start; - do { - if (e->start == 0) - continue; - - if (e->flags & RAZOR_ENTRY_LAST) - next = end; - else { - f = e + 1; - while (f->start == 0 && !(f->flags & RAZOR_ENTRY_LAST)) - f++; - if (f->start == 0) - next = end; - else - next = f->start; - } - - file = r->data; - if (e->start <= file && file < next) { - len = strlen(prefix); - prefix[len] = '/'; - strcpy(prefix + len + 1, pool + e->name); - r = list_package_files(set, r, e, next, prefix); - prefix[len] = '\0'; - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST) && r != NULL); - - return r; -} - RAZOR_EXPORT void razor_set_list_package_files(struct razor_set *set, struct razor_package *package) { - struct list *r; - uint32_t end; - char buffer[512]; + struct razor_file_iterator *fi; + const char *name; assert (set != NULL); assert (package != NULL); - r = list_first(&package->files, &set->file_pool); - end = set->files.size / sizeof (struct razor_entry); - buffer[0] = '\0'; - list_package_files(set, r, set->files.data, end, buffer); + fi = razor_file_iterator_create(set, package); + + while (razor_file_iterator_next(fi, &name)) + printf("%s\n", name); + + razor_file_iterator_destroy(fi); } /* The diff order matters. We should sort the packages so that a diff --git a/librazor/razor.h b/librazor/razor.h index 51d05a7..a9816a8 100644 --- a/librazor/razor.h +++ b/librazor/razor.h @@ -172,6 +172,14 @@ int razor_property_iterator_next(struct razor_property_iterator *pi, void razor_property_iterator_destroy(struct razor_property_iterator *pi); +struct razor_file_iterator; +struct razor_file_iterator * +razor_file_iterator_create(struct razor_set *set, + struct razor_package *package); +int razor_file_iterator_next(struct razor_file_iterator *fi, + const char **name); +void razor_file_iterator_destroy(struct razor_file_iterator *fi); + void razor_set_list_files(struct razor_set *set, const char *prefix); void razor_set_list_package_files(struct razor_set *set, struct razor_package *package); @@ -249,9 +257,21 @@ int razor_transaction_unsatisfied_property(struct razor_transaction *trans, * installing or removing RPM files. **/ +struct razor_relocations; struct razor_rpm; +struct razor_relocations *razor_relocations_create(void); +void razor_relocations_add(struct razor_relocations *relocations, + const char *oldpath, const char *newpath); +void razor_relocations_set_rpm(struct razor_relocations *relocations, + struct razor_rpm *rpm); +const char *razor_relocations_apply(struct razor_relocations *relocations, + const char *path); +void razor_relocations_destroy(struct razor_relocations *relocations); + struct razor_rpm *razor_rpm_open(const char *filename); +void razor_rpm_set_relocations(struct razor_rpm *rpm, + struct razor_relocations *relocations); int razor_rpm_install(struct razor_rpm *rpm, const char *root); int razor_rpm_close(struct razor_rpm *rpm); diff --git a/librazor/rpm.c b/librazor/rpm.c index 02fad33..80c6f61 100644 --- a/librazor/rpm.c +++ b/librazor/rpm.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc + * Copyright (C) 2009 J. Ali Harlow * * 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 @@ -248,12 +249,144 @@ struct razor_rpm { struct rpm_header *signature; struct rpm_header *header; const char **dirs; + unsigned int n_prefixes; + const char **prefixes; const char *pool; void *map; size_t size; void *payload; + struct razor_relocations *relocations; }; +enum razor_relocation_flags { + RAZOR_RELOCATION_ACTIVE = 1 << 0, +}; + +struct razor_relocation { + enum razor_relocation_flags flags; + size_t oldlen; + size_t newlen; + char *oldpath; + char *newpath; +}; + +struct razor_relocations { + /* Ordered such that if oldpath 1 starts with oldpath 2, then + * oldpath 1 is listed first (ie., /usr/bin comes before /usr) + * and terminated with a NULL oldpath. + */ + struct razor_relocation *relocations; + int n_relocations; + char *path; +}; + +RAZOR_EXPORT struct razor_relocations *razor_relocations_create(void) +{ + return calloc(1, sizeof(struct razor_relocations)); +} + +RAZOR_EXPORT void razor_relocations_add(struct razor_relocations *rr, + const char *oldpath, const char *newpath) +{ + int i, found = 0; + size_t len; + + if (newpath && !strcmp(oldpath, newpath)) + newpath = NULL; + + for (i = 0; i < rr->n_relocations; i++) { + len = rr->relocations[i].oldlen; + if (!strncmp(rr->relocations[i].oldpath, oldpath, len)) { + found = !strcmp(rr->relocations[i].oldpath, oldpath); + break; + } + } + + if (!newpath) { + if (found) { + free(rr->relocations[i].oldpath); + free(rr->relocations[i].newpath); + do { + rr->relocations[i] = rr->relocations[i + 1]; + } while (rr->relocations[++i].oldpath); + } + return; + } + + if (found) { + free(rr->relocations[i].newpath); + rr->relocations[i].newpath = strdup(newpath); + rr->relocations[i].newlen = strlen(newpath); + return; + } + + if (!rr->n_relocations++) + rr->relocations = calloc(1, sizeof *rr->relocations); + else { + rr->relocations = realloc(rr->relocations, + rr->n_relocations * sizeof *rr->relocations); + memmove(rr->relocations + i + 1, rr->relocations + i, + (rr->n_relocations - i - 1) * sizeof *rr->relocations); + } + + rr->relocations[i].flags = 0; + rr->relocations[i].oldpath = strdup(oldpath); + rr->relocations[i].newpath = strdup(newpath); + rr->relocations[i].oldlen = strlen(oldpath); + rr->relocations[i].newlen = strlen(newpath); +} + +RAZOR_EXPORT void +razor_relocations_set_rpm(struct razor_relocations *rr, struct razor_rpm *rpm) +{ + int i, j; + + for (i = 0; i < rr->n_relocations; i++) { + rr->relocations[i].flags &= ~RAZOR_RELOCATION_ACTIVE; + for (j = 0; j < rpm->n_prefixes; j++) + if (!strcmp(rpm->prefixes[j], + rr->relocations[i].oldpath)) { + rr->relocations[i].flags |= RAZOR_RELOCATION_ACTIVE; + break; + } + } +} + +RAZOR_EXPORT const char * +razor_relocations_apply(struct razor_relocations *rr, const char *path) +{ + int i; + size_t len; + + for (i = 0; i < rr->n_relocations; i++) + if (rr->relocations[i].flags & RAZOR_RELOCATION_ACTIVE && + !strncmp(path, rr->relocations[i].oldpath, + rr->relocations[i].oldlen)) + break; + + if (i < rr->n_relocations) { + free(rr->path); + len = strlen(path + rr->relocations[i].oldlen) + + rr->relocations[i].newlen; + rr->path = malloc(len + 1); + memcpy(rr->path, rr->relocations[i].newpath, + rr->relocations[i].newlen); + strcpy(rr->path + rr->relocations[i].newlen, + path + rr->relocations[i].oldlen); + return rr->path; + } else + return path; +} + +RAZOR_EXPORT void razor_relocations_destroy(struct razor_relocations *rr) +{ + printf("razor_relocations_destroy(rr=%p): path=%p, rel=%p\n", + rr,rr->path,rr->relocations); + free(rr->path); + free(rr->relocations); + free(rr); +} + static struct rpm_header_index * razor_rpm_get_header(struct razor_rpm *rpm, unsigned int tag) { @@ -366,7 +499,7 @@ razor_rpm_open(const char *filename) struct razor_rpm *rpm; struct rpm_header_index *base, *index; unsigned int count, i, nindex, hsize; - const char *name; + const char *name, *prefix; assert (filename != NULL); @@ -412,9 +545,34 @@ razor_rpm_open(const char *filename) } } + prefix = razor_rpm_get_indirect(rpm, RPMTAG_PREFIXES, &count); + if (prefix) { + rpm->prefixes = calloc(count, sizeof *rpm->prefixes); + for (i = 0; i < count; i++) { + rpm->prefixes[i] = prefix; + prefix += strlen(prefix) + 1; + } + rpm->n_prefixes = count; + } else { + prefix = razor_rpm_get_indirect(rpm, RPMTAG_DEFAULTPREFIX, + &count); + if (prefix) { + fprintf(stderr, "default prefix not supported\n"); + return NULL; + } + } + return rpm; } +RAZOR_EXPORT void razor_rpm_set_relocations(struct razor_rpm *rpm, + struct razor_relocations *rr) +{ + assert (rpm != NULL); + + rpm->relocations = rr; +} + struct cpio_file_header { char magic[6]; char inode[8]; @@ -744,7 +902,7 @@ razor_rpm_install(struct razor_rpm *rpm, const char *root) struct cpio_file_header *header; struct stat buf; unsigned int mode; - char *path; + const char *path; size_t filesize; assert (rpm != NULL); @@ -761,6 +919,9 @@ razor_rpm_install(struct razor_rpm *rpm, const char *root) return -1; } + if (rpm->relocations) + razor_relocations_set_rpm(rpm->relocations, rpm); + if (installer_init(&installer)) return -1; @@ -783,13 +944,16 @@ razor_rpm_install(struct razor_rpm *rpm, const char *root) installer_align(&installer, 4)) return -1; - path = (char *) installer.buffer; + path = (const char *) installer.buffer; /* This convention is so lame... */ if (strcmp(path, "TRAILER!!!") == 0) break; installer.rest = filesize; - if (create_path(&installer, path + 1, mode) < 0) + path++; + if (rpm->relocations) + path = razor_relocations_apply(rpm->relocations, path); + if (create_path(&installer, path, mode) < 0) return -1; if (installer_align(&installer, 4)) return -1; @@ -811,6 +975,7 @@ razor_rpm_close(struct razor_rpm *rpm) assert (rpm != NULL); free(rpm->dirs); + free(rpm->prefixes); err = razor_file_free_contents(rpm->map, rpm->size); free(rpm); diff --git a/src/Makefile.am b/src/Makefile.am index 3cf2f03..fef5221 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,7 @@ test_driver_SOURCES = test-driver.c test_driver_LDADD = $(EXPAT_LIBS) $(top_builddir)/librazor/librazor.la $(EXTRA_LIBS) TESTS = test-driver +XFAIL_TESTS = test-driver EXTRA_DIST = \ test.xml diff --git a/src/main.c b/src/main.c index 792981f..e677c06 100644 --- a/src/main.c +++ b/src/main.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2008 Kristian Høgsberg * Copyright (C) 2008 Red Hat, Inc + * Copyright (C) 2009 J. Ali Harlow * * 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 @@ -746,8 +747,73 @@ download_packages(struct razor_set *system, struct razor_set *next) return 0; } +static struct razor_set * +relocate_packages(struct razor_set *set, struct razor_relocations *relocations) +{ + struct razor_importer *importer; + struct razor_property_iterator *prop_iter; + struct razor_package_iterator *pkg_iter; + struct razor_file_iterator *file_iter; + struct razor_package *package; + struct razor_property *property; + struct razor_rpm *rpm; + const char *name, *version, *arch, *summary, *desc, *url, *license; + char file[PATH_MAX]; + uint32_t flags; + + importer = razor_importer_create(); + pkg_iter = razor_package_iterator_create(set); + + while (razor_package_iterator_next(pkg_iter, &package, + RAZOR_DETAIL_NAME, &name, + RAZOR_DETAIL_VERSION, &version, + RAZOR_DETAIL_ARCH, &arch, + RAZOR_DETAIL_SUMMARY, &summary, + RAZOR_DETAIL_DESCRIPTION, &desc, + RAZOR_DETAIL_URL, &url, + RAZOR_DETAIL_LICENSE, &license, + RAZOR_DETAIL_LAST)) { + snprintf(file, sizeof file, + "rpms/%s", rpm_filename(name, version, arch)); + rpm = razor_rpm_open(file); + if (rpm == NULL) { + fprintf(stderr, "failed to open rpm %s\n", file); + razor_package_iterator_destroy(pkg_iter); + razor_importer_destroy(importer); + return NULL; + } + + razor_relocations_set_rpm(relocations, rpm); + razor_rpm_close(rpm); + + razor_importer_begin_package(importer, name, version, arch); + razor_importer_add_details(importer, + summary, desc, url, license); + + prop_iter = razor_property_iterator_create(set, package); + while (razor_property_iterator_next(prop_iter, &property, + &name, &flags, &version)) + razor_importer_add_property(importer, + name, flags, version); + razor_property_iterator_destroy(prop_iter); + + file_iter = razor_file_iterator_create(set, package); + while (razor_file_iterator_next(file_iter, &name)) { + name = razor_relocations_apply(relocations, name); + razor_importer_add_file(importer, name); + } + razor_file_iterator_destroy(file_iter); + + razor_importer_finish_package(importer); + } + + razor_package_iterator_destroy(pkg_iter); + return razor_importer_finish(importer); +} + static int -install_packages(struct razor_set *system, struct razor_set *next) +install_packages(struct razor_set *system, struct razor_set *next, + struct razor_relocations *relocations) { struct razor_install_iterator *ii; struct razor_package *package; @@ -779,6 +845,8 @@ install_packages(struct razor_set *system, struct razor_set *next) fprintf(stderr, "failed to open rpm %s\n", file); return -1; } + if (relocations) + razor_rpm_set_relocations(rpm, relocations); if (razor_rpm_install(rpm, install_root) < 0) { fprintf(stderr, "failed to install rpm %s\n", file); @@ -795,19 +863,43 @@ static int command_install(int argc, const char *argv[]) { struct razor_root *root; - struct razor_set *system, *upstream, *next; + struct razor_relocations *relocations=NULL; + struct razor_set *system, *upstream, *next, *set; struct razor_transaction *trans; - int i = 0, dependencies = 1; - - if (i < argc && strcmp(argv[i], "--no-dependencies") == 0) { - dependencies = 0; - i++; - } + int i, len, dependencies = 1; + char *oldpath; root = razor_root_open(install_root); if (root == NULL) return 1; + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--no-dependencies") == 0) + dependencies = 0; + else if (strcmp(argv[i], "--relocate") == 0) { + i++; + if (i >= argc || strchr(argv[i], '=') == NULL) { + fprintf(stderr, + "Usage: razor install [OPTION...] RPM\n"); + fprintf(stderr, "Options:\n"); + fprintf(stderr, " [--no-dependencies]\n"); + fprintf(stderr, + " [--relocate OLDPATH=NEWPATH] RPM\n"); + return -1; + } + len = strchr(argv[i], '=') - argv[i]; + oldpath = malloc(len + 1); + strncpy(oldpath, argv[i], len); + oldpath[len] = '\0'; + if (!relocations) + relocations = razor_relocations_create(); + razor_relocations_add(relocations, oldpath, + argv[i] + len + 1); + free(oldpath); + } else + break; + } + system = razor_root_get_system_set(root); upstream = razor_set_open(rawhide_repo_filename); if (upstream == NULL || @@ -818,6 +910,12 @@ command_install(int argc, const char *argv[]) return 1; } + if (relocations) { + set = relocate_packages(upstream, relocations); + razor_set_destroy(upstream); + upstream = set; + } + trans = razor_transaction_create(system, upstream); for (; i < argc; i++) { @@ -851,8 +949,10 @@ command_install(int argc, const char *argv[]) return 1; } - install_packages(system, next); + install_packages(system, next, relocations); + if (relocations) + razor_relocations_destroy(relocations); razor_set_destroy(next); razor_set_destroy(upstream); diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..ffa1762 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,30 @@ +## Process this file with automake to produce Makefile.in + +check_SCRIPTS = relocate + +relocate: relocate.sh primary.xml.gz + cp $(srcdir)/relocate.sh relocate + +primary.xml.gz: zsh.spec zip.spec zap.spec Makefile + rm -rf rpmbuild rpms repodata + mkdir -p rpmbuild/BUILD rpmbuild/RPMS + rpmbuild --define "_topdir `pwd`/rpmbuild" -bb $(srcdir)/zap.spec + rpmbuild --define "_topdir `pwd`/rpmbuild" -bb $(srcdir)/zip.spec + rpmbuild --define "_topdir `pwd`/rpmbuild" -bb $(srcdir)/zsh.spec + mkdir rpms + mv rpmbuild/RPMS/noarch/*.rpm rpms + rm -rf rpmbuild + createrepo -o . rpms + cp repodata/primary.xml.gz repodata/filelists.xml.gz . + +TESTS = $(check_SCRIPTS) + +EXTRA_DIST = \ + zap.spec \ + zip.spec \ + zsh.spec \ + relocate.sh + +clean-local : + rm -f *~ + diff --git a/test/relocate.sh b/test/relocate.sh new file mode 100755 index 0000000..ee7a45e --- /dev/null +++ b/test/relocate.sh @@ -0,0 +1,24 @@ +#!/bin/sh +check_file() +{ + ../src/razor list-files | grep -x "$1" > /dev/null + if [ $? -ne 0 ]; then + echo $1: Not in database >&2 + exit 1 + fi + if [ ! -e "$RAZOR_ROOT$1" ]; then + echo $1: Not in filesystem >&2 + exit 1 + fi +} +export RAZOR_ROOT=`mktemp -dt` || exit 1 +../src/razor init || exit 1 +export YUM_URL="file://localhost/`pwd`" +../src/razor import-yum || exit 1 +../src/razor install zap || exit 1 +../src/razor install --relocate /usr=/opt --relocate /etc=/opt/etc zsh || exit 1 +check_file /etc/zsh.conf +check_file /usr/bin/zap +check_file /opt/bin/zip +check_file /opt/bin/zsh +rm -rf "$RAZOR_ROOT" diff --git a/test/zap.spec b/test/zap.spec new file mode 100644 index 0000000..62a5c28 --- /dev/null +++ b/test/zap.spec @@ -0,0 +1,26 @@ +Name: zap +Summary: Test package +Group: Test +License: GPL +Version: 1 +Release: 1 +Source: zap.tar +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Prefix: /usr + +%description +Test package + +%prep + +%build + +%install +mkdir -p $RPM_BUILD_ROOT/usr/bin +touch $RPM_BUILD_ROOT/usr/bin/zap + +%clean + +%files +/usr/bin/zap diff --git a/test/zip.spec b/test/zip.spec new file mode 100644 index 0000000..8559975 --- /dev/null +++ b/test/zip.spec @@ -0,0 +1,27 @@ +Name: zip +Summary: Test package +Group: Test +License: GPL +Version: 1 +Release: 1 +Source: zip.tar +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Prefix: /usr +Requires: zap + +%description +Test package + +%prep + +%build + +%install +mkdir -p $RPM_BUILD_ROOT/usr/bin +touch $RPM_BUILD_ROOT/usr/bin/zip + +%clean + +%files +/usr/bin/zip diff --git a/test/zsh.spec b/test/zsh.spec new file mode 100644 index 0000000..2e74827 --- /dev/null +++ b/test/zsh.spec @@ -0,0 +1,30 @@ +Name: zsh +Summary: Test package +Group: Test +License: GPL +Version: 1 +Release: 1 +Source: zsh.tar +BuildArch: noarch +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Prefix: /usr +Requires: zip + +%description +Test package + +%prep + +%build + +%install +mkdir -p $RPM_BUILD_ROOT/usr/bin +mkdir -p $RPM_BUILD_ROOT/etc +touch $RPM_BUILD_ROOT/usr/bin/zsh +touch $RPM_BUILD_ROOT/etc/zsh.conf + +%clean + +%files +/usr/bin/zsh +/etc/zsh.conf