aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Dolbec <dolsen@gentoo.org>2017-03-22 04:29:12 -0700
committerBrian Dolbec <dolsen@gentoo.org>2017-03-22 04:35:11 -0700
commit81c73cae27a5602fbe4134e47de2663af85f1e99 (patch)
treea2fd252c1ede8c49bfc09ffa1260049808205496
parentrevdep_rebuild/assign.py: handle directory symlinks (bug 611808) (diff)
downloadgentoolkit-81c73cae27a5602fbe4134e47de2663af85f1e99.tar.gz
gentoolkit-81c73cae27a5602fbe4134e47de2663af85f1e99.tar.bz2
gentoolkit-81c73cae27a5602fbe4134e47de2663af85f1e99.zip
Initial import of remaining gentoolkit-dev packages
Unable to merge due to confilts in history, removed gnetoolkit paths in gentoolkit-dev... So do a basic new files commit instead. For the previous history of the different packages, refer to the gentoolkit-dev branch.
-rw-r--r--AUTHORS.gentoolkit-dev6
-rw-r--r--ebump/AUTHORS4
-rw-r--r--ebump/ChangeLog8
-rw-r--r--ebump/Makefile20
-rw-r--r--ebump/README18
-rwxr-xr-xebump/ebump389
-rw-r--r--ebump/ebump.1126
-rw-r--r--pym/gentoolkit/ekeyword/.pylintrc36
-rw-r--r--pym/gentoolkit/ekeyword/AUTHORS10
-rw-r--r--pym/gentoolkit/ekeyword/Makefile20
-rw-r--r--pym/gentoolkit/ekeyword/README20
l---------pym/gentoolkit/ekeyword/ekeyword1
-rwxr-xr-xpym/gentoolkit/ekeyword/ekeyword.py538
-rwxr-xr-xpym/gentoolkit/ekeyword/ekeyword_unittest.py424
-rwxr-xr-xpym/gentoolkit/ekeyword/pylint49
-rw-r--r--pym/gentoolkit/ekeyword/pytest.ini3
-rw-r--r--pym/gentoolkit/ekeyword/tests/process-1.ebuild5
l---------pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list1
-rw-r--r--pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list45
-rw-r--r--pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc295
-rw-r--r--pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep0
l---------pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc1
-rw-r--r--pym/gentoolkit/imlate/Makefile18
-rwxr-xr-xpym/gentoolkit/imlate/imlate480
-rw-r--r--pym/gentoolkit/imlate/imlate.148
25 files changed, 2565 insertions, 0 deletions
diff --git a/AUTHORS.gentoolkit-dev b/AUTHORS.gentoolkit-dev
new file mode 100644
index 0000000..ca985ba
--- /dev/null
+++ b/AUTHORS.gentoolkit-dev
@@ -0,0 +1,6 @@
+Christian Ruppert <idl0r@gentoo.org>
+Paul Varner <fuzzyray@gentoo.org>
+Karl Trygve Kalleberg <karltk@gentoo.org>
+
+See the AUTHOR file in the various src/<foo> subdirectories for a full
+log of who's done what with whome and when.
diff --git a/ebump/AUTHORS b/ebump/AUTHORS
new file mode 100644
index 0000000..0cf8ad8
--- /dev/null
+++ b/ebump/AUTHORS
@@ -0,0 +1,4 @@
+Christian Ruppert <idl0r@gentoo.org>
+
+Original author:
+Karl Trygve Kalleberg <karltk@gentoo.org>
diff --git a/ebump/ChangeLog b/ebump/ChangeLog
new file mode 100644
index 0000000..4434b94
--- /dev/null
+++ b/ebump/ChangeLog
@@ -0,0 +1,8 @@
+2004-06-21 Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Fixed handling of deletion.
+
+2004-03-11 Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Fixed incorrect cut'ing of wc -l output when updating ChangeLog
+
+2004-02-08 Karl Trygve Kalleberg <karltk@gentoo.org>
+ * Initial import
diff --git a/ebump/Makefile b/ebump/Makefile
new file mode 100644
index 0000000..61afab3
--- /dev/null
+++ b/ebump/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all
+all:
+
+dist:
+ mkdir -p ../../$(DISTDIR)/src/ebump/
+ cp Makefile AUTHORS README ChangeLog ebump ebump.1 ../../$(DISTDIR)/src/ebump/
+
+install: all
+ install -m 0755 ebump $(BINDIR)/
+ install -d $(DOCDIR)/ebump
+ install -m 0644 AUTHORS README ChangeLog $(DOCDIR)/ebump/
+ install -m 0644 ebump.1 $(MAN1DIR)/
diff --git a/ebump/README b/ebump/README
new file mode 100644
index 0000000..f13592e
--- /dev/null
+++ b/ebump/README
@@ -0,0 +1,18 @@
+
+Package : ebump
+Version : 0.1.1
+Author : See AUTHORS
+
+MOTIVATION
+
+The ebump utility is a Gentoo-specific tool for bumping the revision of
+a given ebuild and auxiliary files in the Portage tree. It is only
+useful for Gentoo developers with CVS commit access.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+N/A
diff --git a/ebump/ebump b/ebump/ebump
new file mode 100755
index 0000000..47ffd86
--- /dev/null
+++ b/ebump/ebump
@@ -0,0 +1,389 @@
+#! /bin/sh
+# Copyright (c) 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 1999-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+__version__="0.1.1"
+__author__="Karl Trygve Kalleberg"
+__email__="<karltk@gentoo.org>"
+__description__="Ebuild version bumping tool"
+
+
+
+die() {
+ echo $1 >&2
+ exit -1
+}
+
+einfo() {
+ if [ ${opt_verbosity:-0} -eq 1 ] ; then
+ echo $*
+ fi
+}
+
+print_version() {
+ echo "${__description__}, v${__version__}"
+ echo "Copyright (c) 2004 ${__author__} ${__email__}"
+ echo "Copyright 1999-2010 Gentoo Foundation"
+ echo "Distributed under the terms of the GNU General Public License v2"
+}
+
+print_usage() {
+ echo "Usage: ebump <options> foo<.ebuild>"
+ echo "Ebuild version bumping tool, v${__version__}"
+ echo " -V|--version show version info"
+ echo " -v|--verbose increase verbosity"
+ echo " -q|--quiet turn off output"
+ echo " -a|--no-auxfiles don't bump auxfiles (files/*)"
+ echo " -c|--no-changelog do not update ChangeLog (via echangelog)"
+ echo " -C|--no-vcs do not add to VCS"
+ echo " -m|--message append message to ChangeLog"
+ echo " -d|--delete-old delete previous revision from VCS (DANGEROUS!)"
+}
+
+#
+# Load options from /etc/gentoolkit/ebump.conf and ${HOME}/.gentoo/ebump.conf
+# Home directory file takes precedence.
+#
+load_options() {
+ # FIXME: Sourcing config files like this is really a bad idea; users may
+ # easily override any function in this program inside his config files.
+ if [ -f "/etc/gentoolkit/ebump.conf" ] ; then
+ . /etc/gentoolkit/ebump.conf
+ fi
+ if [ -f "${HOME}/.gentoo/gentool-env" ] ; then
+ . ${HOME}/.gentoo/gentool-env
+ fi
+ if [ -f "${HOME}/.gentoo/ebump.conf" ] ; then
+ . ${HOME}/.gentoo/ebump.conf
+ fi
+
+ # FIXME: remove this warning in 2-3 releases.
+ if [ -n "${opt_add_cvs}" ]; then
+ echo "Warning: opt_add_cvs is deprecated, please use opt_add_vcs from now on!" >&2
+ fi
+}
+
+#
+# Find closes ebuild to ${1}, if any
+#
+find_ebuild() {
+ local f=${1}
+
+ if [ -f "${f}" ] ; then
+ echo ${f}
+ fi
+
+ if [ -f "${f}.ebuild" ] ; then
+ echo ${f}
+ fi
+}
+
+#
+# splitname (version|name|revision) package-name-version-revision
+#
+splitname() {
+ case $1 in
+ version)
+ echo ${2} | sed -r "s/.*-([0-9].*)/\1/"
+ ;;
+ name)
+ name=$(echo ${2} | sed -r "s/(.*)-[0-9].*/\1/")
+ if [ ${name} = ${2} ] ; then
+ if [ $(echo ${2} | grep "^[0-9].*") ] ; then
+ # The filename starts with a version number, thus it has no
+ # name
+ name=""
+ else
+ # The filename doesn't have a recognizeable version number;
+ # everything is a name
+ name=${2}
+ fi
+ fi
+ echo ${name}
+ ;;
+ revision)
+ rev=$(echo ${2} | sed -r "s/.*-r([0-9][0-9]*)/\1/")
+ if [ ${rev} = ${2} ] ; then
+ rev=0
+ fi
+ echo ${rev}
+ ;;
+ vernorev)
+ ver=$(echo ${2} | sed -r "s/.*-([0-9].*)-r[0-9]+/\1/")
+ if [ ${ver} = ${2} ] ; then
+ ver=$(echo ${2} | sed -r "s/.*-([0-9].*)/\1/")
+ fi
+ echo ${ver}
+ ;;
+ *)
+ echo
+ ;;
+ esac
+}
+
+process_ebuild() {
+ local vcs=$1
+ shift
+ local ebuild_arg="${*}"
+ shift $#
+
+ # Files to add to VCS
+ local addfiles=""
+ # Files to remove from VCS
+ local delfiles=""
+
+ if [ -z "${ebuild_arg}" ] ; then
+ print_usage
+ exit
+ fi
+
+ for ebuild in $ebuild_arg; do
+ #
+ # Try to find a matching ebuild
+ #
+ local ebuild_name=$(find_ebuild ${ebuild})
+ if [ -z "${ebuild_name}" ] ; then
+ die "Could not find ${ebuild}"
+ fi
+
+ einfo "Processing ebuild ${ebuild_name}"
+
+ #
+ # Bump revision suffix (or add one)
+ #
+ local PF=$(basename ${ebuild_name} .ebuild)
+ local PN=$(splitname name ${PF})
+ local PV=$(splitname version ${PF})
+ local rev=$(splitname revision ${PF})
+ local PV_norev=$(splitname vernorev ${PF})
+ local newPF=${PN}-${PV_norev}-r$((rev+1))
+
+# echo $PF / $PN / $PV / $rev / $PV_norev / $newPF
+
+ einfo "Bumped ${PF}.ebuild to ${newPF}.ebuild"
+
+ if [ "${vcs}" = "svn" ]; then
+ svn cp ${PF}.ebuild ${newPF}.ebuild
+ else
+ cp ${PF}.ebuild ${newPF}.ebuild
+ fi
+
+ einfo "Reset keywords to ~arch"
+
+ ekeyword '~all' "${newPF}.ebuild"
+
+ addfiles="${addfiles} ${newPF}.ebuild"
+ delfiles="${delfiles} ${PF}.ebuild"
+
+ #
+ # (Optional) Bump relevant files in files/
+ #
+ if [ "${opt_bump_auxfiles}" = "y" ] ; then
+ # Gather list of auxiliary files in files/ that has a versioned
+ # filename, where the version matches our current version.
+ local bumplist=""
+ for x in $(echo files/*) ; do
+ if [ ! -z "$(echo $x | grep "${PV}$")" ] ; then
+ bumplist="${bumplist} ${x}"
+ fi
+ done
+
+ # Bump version of all matches
+ for x in ${bumplist} ; do
+ local bn=$(basename ${x})
+ local dn=$(dirname ${x})
+ local newbn
+
+ PN=$(splitname name ${bn})
+ PV=$(splitname version ${bn})
+ rev=$(splitname revision ${bn})
+ PV_norev=$(splitname vernorev ${bn})
+
+# echo $PN / ${PV_norev} / ${rev}
+
+ # Special case for when we have no name part; filename
+ # is just a version number
+ if [ -z "${PN}" ] ; then
+ newbn=${PV_norev}-r$((rev+1))
+ else
+ newbn=${PN}-${PV_norev}-r$((rev+1))
+ fi
+
+ if [ -d ${dn}/${bn} ] ; then
+ if [ -e ${dn}/${newbn} ] ; then
+ echo "Directory ${dn}/${newbn} exists, not copying" >&2
+ else
+ cp -a ${dn}/${bn} ${dn}/${newbn}
+ # uhm, is that necessary?
+# find ${dn}/${newbn} -name CVS | xargs rm -rf
+ fi
+ else
+ cp ${dn}/${bn} ${dn}/${newbn}
+ fi
+
+ addfiles="${addfiles} ${dn}/${newbn}"
+ delfiles="${delfiles} ${dn}/${bn}"
+
+ einfo "Bumped ${dn}/${bn} to ${dn}/${newbn}"
+ done
+ fi
+ done
+
+# echo "addfiles ${addfiles}"
+# echo "delfiles ${delfiles}"
+
+ #
+ # (Optional) Add VCS entry for all new files
+ #
+ if [ "${opt_add_vcs}" = "y" ] ; then
+# for x in ${addfiles} ; do
+# if [ -d ${x} ] ; then
+# find ${x} -exec ${vcs} add {} ';'
+# else
+# ${vcs} add ${x}
+# fi
+# done
+ $vcs add $addfiles
+ einfo "Added ${addfiles} to VCS"
+ fi
+
+
+ #
+ # (Optional) Delete previous entry
+ #
+ # Could we use 'rm' instead of remove for all vcs?
+ if [ "${opt_delete_old}" = "y" ] ; then
+# for x in ${delfiles} ; do
+# if [ "${vcs}" = "cvs" ]; then
+# ${vcs} remove -f ${x}
+# elif [ "${vcs}" = "git" ]; then
+# ${vcs} rm ${x}
+# else
+# ${vcs} remove ${x}
+# fi
+# done
+ if [ "${vcs}" = "cvs" ]; then
+ $vcs remove -f $delfiles
+ elif [ "${vcs}" = "git" ]; then
+ $vcs rm $delfiles
+ else
+ $vcs remove $delfiles
+ fi
+ einfo "Removed ${delfiles} from VCS"
+ fi
+
+ #
+ # (Optional) Add ChangeLog entry
+ #
+ if [ "${opt_add_changelog}" = "y" ] && [ "${opt_add_vcs}" = "y" ]; then
+ # FIXME: remove this warning in 2-3 releases
+ if [ -n "${AUTHORNAME}" ] || [ -n "${AUTHOREMAIL}" ]; then
+ echo "Warning: AUTHORNAME and AUTHOREMAIL is deprecated!" >&2
+ echo "Please take a look at echangelog(1)." >&2
+ echo "To avoid this warning unset AUTHORNAME and AUTHOREMAIL." >&2
+ fi
+
+ echangelog "${opt_commitmessage}" || set $?
+
+ if [ ${1:-0} -ne 0 ]; then
+ einfo "Modifying ChangeLog failed!"
+ else
+ einfo "Added ChangeLog entry"
+ fi
+ fi
+}
+
+get_vcs() {
+ if [ -d "CVS" ]; then
+ echo "cvs"
+ return 0
+ elif [ -d ".svn" ]; then
+ echo "svn"
+ return 0
+ else
+ if [ -x "$(which git)" ]; then
+ if [ -n "$(git rev-parse --git-dir 2>/dev/null)" ]; then
+ echo "git"
+ return 0
+ fi
+ fi
+
+ echo
+ return 1
+ fi
+}
+
+#
+# Global options
+#
+opt_verbosity=0
+opt_add_changelog=y
+opt_add_vcs=y
+opt_bump_auxfiles=y
+opt_delete_old=n
+opt_commitmessage=""
+
+load_options
+
+while [ ${#} -gt 0 ] ; do
+ arg=${1}
+ shift
+
+ case ${arg} in
+ -h|--help)
+ print_usage
+ exit 0
+ ;;
+ -m|--message)
+ opt_commitmessage="${1}"
+ shift
+ continue
+ ;;
+ -a|--no-auxfiles)
+ opt_bump_auxfiles=n
+ continue
+ ;;
+ -c|--no-changelog)
+ opt_add_changelog=n
+ continue
+ ;;
+ -C|--no-vcs)
+ opt_add_vcs=n
+ continue
+ ;;
+ -V|--version)
+ print_version
+ exit
+ ;;
+ -v|--verbose)
+ opt_verbosity=1
+ continue
+ ;;
+ -q|--quiet)
+ opt_verbosity=0
+ continue
+ ;;
+ -d|--delete-old)
+ opt_delete_old=y
+ continue
+ ;;
+ *)
+ ebuild_arg="${ebuild_arg:+${ebuild_arg} }${arg}"
+ continue
+ ;;
+ esac
+done
+
+vcs=$(get_vcs)
+if [ -z "${vcs}" ]; then
+ echo "Warning: no cvs, git or svn repository found!" >&2
+ echo "Changes can't be added to the VCS" >&2
+ opt_add_vcs=n
+ opt_delete_old=n
+fi
+
+process_ebuild "${vcs}" $ebuild_arg
+
+# TODO:
+# - put cli parser into separate functions
diff --git a/ebump/ebump.1 b/ebump/ebump.1
new file mode 100644
index 0000000..b1e473d
--- /dev/null
+++ b/ebump/ebump.1
@@ -0,0 +1,126 @@
+.TH "ebump" "1" "0.1.1" "Gentoolkit" "Gentoo Administration"
+.SH "NAME"
+.LP
+ebump \- Gentoo: Ebuild revision bumper
+.SH "SYNTAX"
+.LP
+ebump [\fIoption\fP] <\fIpackage-name[-version]\fP>
+
+.SH "DESCRIPTION"
+
+.LP
+\fIebump\fR bumps the revision of a particular ebuild, and all auxiliary
+files in the files/ directory that have a matching version suffix.
+
+.LP
+By default, the all new revision files will be added to the VCS.
+
+.LP
+You must stand in the directory of the ebuild to be bumped.
+
+.SH "OPTIONS"
+.LP
+\fB\-C\fR
+.br
+\fB--no-vcs\fB
+.IP
+Do not add new files to VCS.
+
+.LP
+\fB\-V\fR
+.br
+\fB--version\fB
+.IP
+Display version information and exit.
+
+.LP
+\fB\-v\fR
+.br
+\fB--verbose\fB
+.IP
+Increase verbosity level. May be used more than once.
+
+.LP
+\fB\-q\fR
+.br
+\fB--quiet\fB
+.IP
+Do not output any non-essential information.
+
+.LP
+\fB\-a\fR
+.br
+\fB--no-auxfiles\fB
+.IP
+don't bump auxfiles (files/*)
+
+.LP
+\fB\-c\fR
+.br
+\fB--no-changelog\fB
+.IP
+do not update ChangeLog (via echangelog)
+
+.LP
+\fB\-m\fR <\fIChangeLog text\fR>
+.br
+\fB\--message\fR <\fIChangeLog text\fR>
+.IP
+Specifies the message to add to the ChangeLog, instead of the standard
+placeholder.
+
+.LP
+\fB\-d\fR
+.br
+\fB\--delete-old\fR
+.IP
+Delete old revision and old auxiliary files from VCS. This is
+\fIdangerous\fR and should only be used if you know exactly what you are
+doing, because
+.br
+1) the old revision may be stable on a different architecture than the one you
+are working on.
+.br
+2) the auxiliary files may be required by other versions of the ebuild.
+.br
+3) the new revision should usually undergo a period of testing before being marked stable.
+
+.SH "CONFIGURATION"
+
+.LP
+\fB/etc/gentoolkit/ebump.conf\fR
+.br
+\fB~/.gentoo/ebump.conf\fR
+.IP
+From these files, \fIebump\fR will load the settings
+.br
+\fBopt_verbosity\fR (default \fI1\fR) - verbosity level 0-10
+.br
+\fBopt_add_changelog\fR (default \fIy\fR) - add entry in ChangeLog
+.br
+\fBopt_add_vcs\fR (default \fIy\fR) - add new files to VCS
+.br
+\fBopt_bump_auxfiles\fR (default \fIy\fR) - bump auxiliary files in files/
+.br
+\fBopt_delete_old\fR (default \fIn\fR) - delete old revision (DANGEROUS!)
+.br
+\fBopt_commitmessage\fR (default \fI""\fR) - default ChangeLog message
+
+.LP
+\fB(DEPRECATED)\fR
+.br
+\fB~/.gentoo/gentool-env\fR
+.IR
+From this file, \fIebump\fR will load the env vars \fBAUTHORNAME\fR and
+\fBAUTHOREMAIL\fR, which are used to generate proper ChangeLog entries.
+
+.SH "SEE ALSO"
+.LP
+The rest of the utilities in \fIapp-portage/gentoolkit-dev\fR, such as
+\fIechangelog(1)\fR and \fIekeyword(1)\fR.
+
+.SH "AUTHORS"
+.LP
+Karl Trygve Kalleberg <karltk@gentoo.org>
+.br
+Christian Ruppert <idl0r@gentoo.org>
diff --git a/pym/gentoolkit/ekeyword/.pylintrc b/pym/gentoolkit/ekeyword/.pylintrc
new file mode 100644
index 0000000..cd5b31e
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/.pylintrc
@@ -0,0 +1,36 @@
+[MESSAGES CONTROL]
+# Disable the message, report, category or checker with the given id(s). You
+# can either give multiple identifier separated by comma (,) or put this option
+# multiple times (only on the command line, not in the configuration file where
+# it should appear only once).
+disable=
+ missing-docstring,
+ too-many-lines,
+ too-many-branches,
+ too-many-statements,
+ too-few-public-methods,
+ too-many-instance-attributes,
+ too-many-public-methods,
+ too-many-locals,
+ too-many-arguments,
+ locally-enabled,
+ locally-disabled,
+ fixme,
+ bad-continuation,
+ invalid-name,
+
+[REPORTS]
+reports=no
+
+[FORMAT]
+max-line-length=80
+indent-string='\t'
+
+[SIMILARITIES]
+min-similarity-lines=20
+
+[VARIABLES]
+dummy-variables-rgx=_
+
+[DESIGN]
+max-parents=10
diff --git a/pym/gentoolkit/ekeyword/AUTHORS b/pym/gentoolkit/ekeyword/AUTHORS
new file mode 100644
index 0000000..353a45e
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/AUTHORS
@@ -0,0 +1,10 @@
+Current python version:
+ Mike Frysinger <vapier@gentoo.org>
+
+Previous perl version:
+ Christian Ruppert <idl0r@gentoo.org>
+ Paul Varner <fuzzyray@gentoo.org>
+ Mike Frysinger <vapier@gentoo.org>
+
+Original author:
+ Aron Griffis <agriffis@gentoo.org>
diff --git a/pym/gentoolkit/ekeyword/Makefile b/pym/gentoolkit/ekeyword/Makefile
new file mode 100644
index 0000000..cfa1a06
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2004 Karl Trygve Kalleberg <karltk@gentoo.org>
+# Copyright 2004 Gentoo Technologies, Inc.
+# Distributed under the terms of the GNU General Public License v2
+#
+# $Header$
+
+include ../../makedefs.mak
+
+.PHONY: all clean dist
+all:
+
+dist:
+ mkdir -p ../../$(DISTDIR)/src/ekeyword
+ cp Makefile AUTHORS README ekeyword.py ekeyword_unittest.py \
+ ../../$(DISTDIR)/src/ekeyword/
+
+install: all
+ install -m 0755 ekeyword.py $(BINDIR)/ekeyword
+ install -d $(DOCDIR)/ekeyword
+ install -m 0644 AUTHORS README $(DOCDIR)/ekeyword/
diff --git a/pym/gentoolkit/ekeyword/README b/pym/gentoolkit/ekeyword/README
new file mode 100644
index 0000000..b147e4a
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/README
@@ -0,0 +1,20 @@
+Package : ekeyword
+Version : 1.0
+Author : See AUTHORS
+
+MOTIVATION
+
+Update the KEYWORDS in an ebuild.
+
+MECHANICS
+
+N/A
+
+IMPROVEMENTS
+
+- Should we allow users to pass in */-*/~*?
+- Should we collapse multiple globs into one.
+- Should we support multiline KEYWORDS values? No...
+- Support autodetection of ~user homedir expansions.
+ e.g. If "arm" is a user, then "~arm" will be passed in as "/home/arm".
+ We should catch that and normalize it back to "~arm".
diff --git a/pym/gentoolkit/ekeyword/ekeyword b/pym/gentoolkit/ekeyword/ekeyword
new file mode 120000
index 0000000..8374306
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword
@@ -0,0 +1 @@
+ekeyword.py \ No newline at end of file
diff --git a/pym/gentoolkit/ekeyword/ekeyword.py b/pym/gentoolkit/ekeyword/ekeyword.py
new file mode 100755
index 0000000..31225b0
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword.py
@@ -0,0 +1,538 @@
+#!/usr/bin/python
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Mike Frysinger <vapier@gentoo.org>
+
+"""Manage KEYWORDS in ebuilds easily.
+
+This tool provides a simple way to add or update KEYWORDS in a set of ebuilds.
+Each command-line argument is processed in order, so that keywords are added to
+the current list as they appear, and ebuilds are processed as they appear.
+
+Instead of specifying a specific arch, it's possible to use the word "all".
+This causes the change to apply to all keywords presently specified in the
+ebuild.
+
+The ^ leader instructs ekeyword to remove the specified arch.
+
+Examples:
+
+ # Mark all existing arches in the ebuild as stable.
+ $ %(prog)s all foo-1.ebuild
+
+ # Mark arm as stable and x86 as unstable.
+ $ %(prog)s arm ~x86 foo-1.ebuild
+
+ # Mark hppa as unsupported (explicitly adds -hppa).
+ $ %(prog)s -hppa foo-1.ebuild
+
+ # Delete alpha keywords from all ebuilds.
+ $ %(prog)s ^alpha *.ebuild
+
+ # Mark sparc as stable for foo-1 and m68k as unstable for foo-2.
+ $ %(prog)s sparc foo-1.ebuild ~m68k foo-2.ebuild
+
+ # Mark s390 as the same state as amd64.
+ $ %(prog)s s390=amd64 foo-1.ebuild
+"""
+
+from __future__ import print_function
+
+import argparse
+import collections
+import difflib
+import io
+import os
+import re
+import subprocess
+import sys
+
+import portage
+from portage.output import colorize, nocolor
+
+
+VERSION = '1.0 awesome'
+
+# Operation object that describes how to perform a change.
+# Args:
+# op: The operation to perform when |ref_arch| is not set:
+# None: Mark |arch| stable
+# '-': Mark |arch| as not applicable (e.g. -foo)
+# '~': Mark |arch| as unstable (e.g. ~foo)
+# '^': Delete |arch| so it isn't listed at all
+# arch: The required arch to update
+# ref_arch: Set |arch| status to this arch (ignoring |op|)
+Op = collections.namedtuple('Op', ('op', 'arch', 'ref_arch'))
+
+
+def warning(msg):
+ """Write |msg| as a warning to stderr"""
+ print('warning: %s' % msg, file=sys.stderr)
+
+
+def keyword_to_arch(keyword):
+ """Given a keyword, strip it down to its arch value
+
+ When an ARCH shows up in KEYWORDS, it may have prefixes like ~ or -.
+ Strip all that cruft off to get back to the ARCH.
+ """
+ return keyword.lstrip('-~')
+
+
+def sort_keywords(arches):
+ """Sort |arches| list in the order developers expect
+
+ This is vaguely defined because it is kind of vaguely defined once you get
+ past the basic (Linux-only) keywords.
+
+ Args:
+ arches: An iterable of ARCH values.
+
+ Returns:
+ A sorted list of |arches|
+ """
+ keywords = []
+
+ # Globs always come first.
+ for g in ('-*', '*', '~*'):
+ if g in arches:
+ arches.remove(g)
+ keywords.append(g)
+
+ def arch_key(keyword):
+ """Callback for python sorting functions
+
+ Used to turn a Gentoo keyword into a sortable form.
+ """
+ # Sort independent of leading marker (~ or -).
+ arch = keyword_to_arch(keyword)
+
+ # A keyword may have a "-" in it. We split on that and sort
+ # by the two resulting items. The part after the hyphen is
+ # the primary key.
+ if '-' in arch:
+ arch, plat = arch.split('-', 1)
+ else:
+ arch, plat = arch, ''
+
+ return (plat, arch)
+
+ keywords += sorted(arches, key=arch_key)
+
+ return keywords
+
+
+def diff_keywords(old_keywords, new_keywords, style='color-inline'):
+ """Show pretty diff between list of keywords
+
+ Args:
+ old_keywords: The old set of KEYWORDS
+ new_keywords: The new set of KEYWORDS
+ style: The diff style
+
+ Returns:
+ A string containing the diff output ready to shown to the user
+ """
+ def show_diff(s):
+ output = ''
+
+ for tag, i0, i1, j0, j1 in s.get_opcodes():
+
+ if tag == 'equal':
+ output += s.a[i0:i1]
+
+ if tag in ('delete', 'replace'):
+ o = s.a[i0:i1]
+ if style == 'color-inline':
+ o = colorize('bg_darkred', o)
+ else:
+ o = '-{%s}' % o
+ output += o
+
+ if tag in ('insert', 'replace'):
+ o = s.b[j0:j1]
+ if style == 'color-inline':
+ o = colorize('bg_darkgreen', o)
+ else:
+ o = '+{%s}' % o
+ output += o
+
+ return output
+
+ sold = str(' '.join(old_keywords))
+ snew = str(' '.join(new_keywords))
+ s = difflib.SequenceMatcher(str.isspace, sold, snew, autojunk=False)
+ return show_diff(s)
+
+
+def process_keywords(keywords, ops, arch_status=None):
+ """Process |ops| for |keywords|"""
+ new_keywords = set(keywords).copy()
+
+ # Process each op one at a time.
+ for op, oarch, refarch in ops:
+ # Figure out which keywords we need to modify.
+ if oarch == 'all':
+ if arch_status is None:
+ raise ValueError('unable to process "all" w/out profiles.desc')
+ old_arches = set([keyword_to_arch(a) for a in new_keywords])
+ if op is None:
+ # Process just stable keywords.
+ arches = [k for k, v in arch_status.items()
+ if v == 'stable' and k in old_arches]
+ else:
+ # Process all possible keywords. We use the arch_status as a
+ # master list. If it lacks some keywords, then we might miss
+ # somethings here, but not much we can do.
+ arches = list(old_arches)
+
+ # We ignore the glob arch as we never want to tweak it.
+ if '*' in arches:
+ arches.remove('*')
+
+ # For keywords that are explicitly disabled, do not update. When
+ # people use `ekeyword ~all ...` or `ekeyword all ...`, they rarely
+ # (if ever) want to change a '-sparc' to 'sparc' or '-sparc' to
+ # '~sparc'. We force people to explicitly do `ekeyword sparc ...`
+ # in these cases.
+ arches = [x for x in arches if '-' + x not in new_keywords]
+ else:
+ arches = [oarch]
+
+ if refarch:
+ # Figure out the state for this arch based on the reference arch.
+ # TODO: Add support for "all" keywords.
+ # XXX: Should this ignore the '-' state ? Does it make sense to
+ # sync e.g. "s390" to "-ppc" ?
+ refkeyword = [x for x in new_keywords if refarch == keyword_to_arch(x)]
+ if not refkeyword:
+ op = '^'
+ elif refkeyword[0].startswith('~'):
+ op = '~'
+ elif refkeyword[0].startswith('-'):
+ op = '-'
+ else:
+ op = None
+
+ # Finally do the actual update of the keywords list.
+ for arch in arches:
+ new_keywords -= set(['%s%s' % (x, arch) for x in ('', '~', '-')])
+
+ if op is None:
+ new_keywords.add(arch)
+ elif op in ('~', '-'):
+ new_keywords.add('%s%s' % (op, arch))
+ elif op == '^':
+ # Already deleted. Whee.
+ pass
+ else:
+ raise ValueError('unknown operation %s' % op)
+
+ return new_keywords
+
+
+def process_content(ebuild, data, ops, arch_status=None, verbose=0,
+ quiet=0, style='color-inline'):
+ """Process |ops| for |data|"""
+ # Set up the user display style based on verbose/quiet settings.
+ if verbose > 1:
+ disp_name = ebuild
+ def logit(msg):
+ print('%s: %s' % (disp_name, msg))
+ elif quiet > 1:
+ def logit(_msg):
+ pass
+ else:
+ # Chop the full path and the .ebuild suffix.
+ disp_name = os.path.basename(ebuild)[:-7]
+ def logit(msg):
+ print('%s: %s' % (disp_name, msg))
+
+ # Match any KEYWORDS= entry that isn't commented out.
+ keywords_re = re.compile(r'^([^#]*\bKEYWORDS=)([\'"])(.*)(\2)(.*)')
+ updated = False
+ content = []
+
+ # Walk each line of the ebuild looking for KEYWORDS to process.
+ for line in data:
+ m = keywords_re.match(line)
+ if not m:
+ content.append(line)
+ continue
+
+ # Ok, we've got it, now let's process things.
+ old_keywords = set(m.group(3).split())
+ new_keywords = process_keywords(
+ old_keywords, ops, arch_status=arch_status)
+
+ # Finally let's present the results to the user.
+ if (new_keywords != old_keywords) or verbose:
+ # Only do the diff work if something actually changed.
+ updated = True
+ old_keywords = sort_keywords(old_keywords)
+ new_keywords = sort_keywords(new_keywords)
+ line = '%s"%s"%s\n' % (m.group(1), ' '.join(new_keywords),
+ m.group(5))
+ if style in ('color-inline', 'inline'):
+ logit(diff_keywords(old_keywords, new_keywords, style=style))
+ else:
+ if style == 'long-multi':
+ logit(' '.join(['%*s' % (len(keyword_to_arch(x)) + 1, x)
+ for x in old_keywords]))
+ logit(' '.join(['%*s' % (len(keyword_to_arch(x)) + 1, x)
+ for x in new_keywords]))
+ else:
+ deleted_keywords = [x for x in old_keywords
+ if x not in new_keywords]
+ logit('--- %s' % ' '.join(deleted_keywords))
+ added_keywords = [x for x in new_keywords
+ if x not in old_keywords]
+ logit('+++ %s' % ' '.join(added_keywords))
+
+ content.append(line)
+
+ if not updated:
+ logit('no updates')
+
+ return updated, content
+
+
+def process_ebuild(ebuild, ops, arch_status=None, verbose=0, quiet=0,
+ dry_run=False, style='color-inline', manifest=False):
+ """Process |ops| for |ebuild|
+
+ Args:
+ ebuild: The ebuild file to operate on & update in place
+ ops: An iterable of operations (Op objects) to perform on |ebuild|
+ arch_status: A dict mapping default arches to their stability; see the
+ load_profile_data function for more details
+ verbose: Be verbose; show various status messages
+ quiet: Be quiet; only show errors
+ dry_run: Do not make any changes to |ebuild|; show what would be done
+ style: The diff style
+
+ Returns:
+ Whether any updates were processed
+ """
+ with io.open(ebuild, encoding='utf8') as f:
+ updated, content = process_content(
+ ebuild, f, ops, arch_status=arch_status,
+ verbose=verbose, quiet=quiet, style=style)
+ if updated and not dry_run:
+ with io.open(ebuild, 'w', encoding='utf8') as f:
+ f.writelines(content)
+ if manifest:
+ subprocess.check_call(['ebuild', ebuild, 'manifest'])
+ return updated
+
+
+def portage_settings():
+ """Return the portage settings we care about."""
+ # Portage creates the db member on the fly which confuses the linter.
+ # pylint: disable=no-member
+ return portage.db['/']['vartree'].settings
+
+
+def load_profile_data(portdir=None, repo='gentoo'):
+ """Load the list of known arches from the tree
+
+ Args:
+ portdir: The repository to load all data from (and ignore |repo|)
+ repo: Look up this repository by name to locate profile data
+
+ Returns:
+ A dict mapping the keyword to its preferred state:
+ {'x86': 'stable', 'mips': 'dev', ...}
+ """
+ if portdir is None:
+ portdir = portage_settings().repositories[repo].location
+
+ arch_status = {}
+
+ try:
+ arch_list = os.path.join(portdir, 'profiles', 'arch.list')
+ with open(arch_list) as f:
+ for line in f:
+ line = line.split('#', 1)[0].strip()
+ if line:
+ arch_status[line] = None
+ except IOError:
+ pass
+
+ try:
+ profile_status = {
+ 'stable': 0,
+ 'dev': 1,
+ 'exp': 2,
+ None: 3,
+ }
+ profiles_list = os.path.join(portdir, 'profiles', 'profiles.desc')
+ with open(profiles_list) as f:
+ for line in f:
+ line = line.split('#', 1)[0].split()
+ if line:
+ arch, _profile, status = line
+ arch_status.setdefault(arch, status)
+ curr_status = profile_status[arch_status[arch]]
+ new_status = profile_status[status]
+ if new_status < curr_status:
+ arch_status[arch] = status
+ except IOError:
+ pass
+
+ if arch_status:
+ arch_status['all'] = None
+ else:
+ warning('could not read profile files: %s' % arch_list)
+ warning('will not be able to verify args are correct')
+
+ return arch_status
+
+
+def arg_to_op(arg):
+ """Convert a command line |arg| to an Op"""
+ arch_prefixes = ('-', '~', '^')
+
+ op = None
+ arch = arg
+ refarch = None
+
+ if arg and arg[0] in arch_prefixes:
+ op, arch = arg[0], arg[1:]
+
+ if '=' in arch:
+ if not op is None:
+ raise ValueError('Cannot use an op and a refarch')
+ arch, refarch = arch.split('=', 1)
+
+ return Op(op, arch, refarch)
+
+
+def ignorable_arg(arg, quiet=0):
+ """Whether it's ok to ignore this argument"""
+ if os.path.isdir(arg):
+ if not quiet:
+ warning('ignoring directory %s' % arg)
+ return True
+
+ WHITELIST = (
+ 'Manifest',
+ 'metadata.xml',
+ )
+ base = os.path.basename(arg)
+ if (base.startswith('ChangeLog') or
+ base in WHITELIST or
+ base.startswith('.') or
+ base.endswith('~')):
+ if not quiet:
+ warning('ignoring file: %s' % arg)
+ return True
+
+ return False
+
+
+def args_to_work(args, arch_status=None, _repo='gentoo', quiet=0):
+ """Process |args| into a list of work itmes (ebuild/arches to update)"""
+ work = []
+ todo_arches = []
+ last_todo_arches = None
+
+ for arg in args:
+ if arg.endswith('.ebuild'):
+ if not todo_arches:
+ todo_arches = last_todo_arches
+ if not todo_arches:
+ raise ValueError('missing arches to process for %s' % arg)
+ work.append([arg, todo_arches])
+ last_todo_arches = todo_arches
+ todo_arches = []
+ else:
+ op = arg_to_op(arg)
+ if not arch_status or op.arch in arch_status:
+ todo_arches.append(op)
+ elif not ignorable_arg(arg, quiet=quiet):
+ raise ValueError('unknown arch/argument: %s' % arg)
+
+ if todo_arches:
+ raise ValueError('missing ebuilds to process!')
+
+ return work
+
+
+def get_parser():
+ """Return an argument parser for ekeyword"""
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument('-m', '--manifest', default=False, action='store_true',
+ help='Run `ebuild manifest` on the ebuild after modifying it')
+ parser.add_argument('-n', '--dry-run', default=False, action='store_true',
+ help='Show what would be changed, but do not commit')
+ parser.add_argument('-v', '--verbose', action='count', default=0,
+ help='Be verbose while processing things')
+ parser.add_argument('-q', '--quiet', action='count', default=0,
+ help='Be quiet while processing things (only show errors)')
+ parser.add_argument('--format', default='auto', dest='style',
+ choices=('auto', 'color-inline', 'inline', 'short-multi', 'long-multi'),
+ help='Selet output format for showing differences')
+ parser.add_argument('-V', '--version', action='version', version=VERSION,
+ help='Show version information')
+ return parser
+
+
+def main(argv):
+ if argv is None:
+ argv = sys.argv[1:]
+
+ # Extract the args ourselves. This is to allow things like -hppa
+ # without tripping over the -h/--help flags. We can't use the
+ # parse_known_args function either.
+ # This sucks and really wish we didn't need to do this ...
+ parse_args = []
+ work_args = []
+ while argv:
+ arg = argv.pop(0)
+ if arg.startswith('--'):
+ if arg == '--':
+ work_args += argv
+ break
+ else:
+ parse_args.append(arg)
+ # Handle flags that take arguments.
+ if arg in ('--format',):
+ if argv:
+ parse_args.append(argv.pop(0))
+ elif len(arg) == 2 and arg[0] == '-':
+ parse_args.append(arg)
+ else:
+ work_args.append(arg)
+
+ parser = get_parser()
+ opts = parser.parse_args(parse_args)
+ if not work_args:
+ parser.error('need arches/ebuilds to process')
+
+ if opts.style == 'auto':
+ if not portage_settings().get('NOCOLOR', 'false').lower() in ('no', 'false'):
+ nocolor()
+ opts.style = 'short'
+ else:
+ opts.style = 'color-inline'
+
+ arch_status = load_profile_data()
+ try:
+ work = args_to_work(work_args, arch_status=arch_status, quiet=opts.quiet)
+ except ValueError as e:
+ parser.error(e)
+
+ for ebuild, ops in work:
+ process_ebuild(ebuild, ops, arch_status=arch_status,
+ verbose=opts.verbose, quiet=opts.quiet,
+ dry_run=opts.dry_run, style=opts.style,
+ manifest=opts.manifest)
+
+ return os.EX_OK
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/pym/gentoolkit/ekeyword/ekeyword_unittest.py b/pym/gentoolkit/ekeyword/ekeyword_unittest.py
new file mode 100755
index 0000000..de40e7a
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/ekeyword_unittest.py
@@ -0,0 +1,424 @@
+#!/usr/bin/python
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# Written by Mike Frysinger <vapier@gentoo.org>
+
+# pylint: disable=no-self-use
+
+"""Unittests for ekeyword"""
+
+from __future__ import print_function
+
+import os
+import subprocess
+import tempfile
+import unittest
+
+import mock
+
+import ekeyword
+
+
+TESTDIR = os.path.join(os.path.dirname(__file__), 'tests')
+
+
+class TestSortKeywords(unittest.TestCase):
+ """Tests for sort_keywords"""
+
+ def _test(self, input_data, exp_data):
+ """Sort |input_data| and make sure it matches |exp_data|"""
+ output_data = ekeyword.sort_keywords(input_data.split())
+ self.assertEqual(exp_data.split(), output_data)
+
+ def testNull(self):
+ """Verify whitespace is collapsed"""
+ self._test('', '')
+ self._test(' ', '')
+
+ def testGlob(self):
+ """Verify globs get sorted before all others"""
+ self._test('* arm', '* arm')
+ self._test('arm -* x86', '-* arm x86')
+ self._test('hppa ~* amd64', '~* amd64 hppa')
+
+ def testMixedPlatform(self):
+ """Verify core arches get sorted before all w/suffix"""
+ self._test('arm-linux alpha amd64-fbsd hppa',
+ 'alpha hppa amd64-fbsd arm-linux')
+
+ def testPrefixes(self):
+ """Verify -/~ and such get ignored for sorting"""
+ self._test('-hppa arm ~alpha -* ~arm-linux',
+ '-* ~alpha arm -hppa ~arm-linux')
+
+ def testPlatform(self):
+ """Verify we sort based on platform first"""
+ self._test('x86-linux ppc-macos x86-fbsd amd64-linux amd64-fbsd',
+ 'amd64-fbsd x86-fbsd amd64-linux x86-linux ppc-macos')
+
+
+class TestDiffKeywords(unittest.TestCase):
+ """Tests for diff_keywords"""
+
+ def testEmpty(self):
+ """Test when there is no content to diff"""
+ ret = ekeyword.diff_keywords([], [])
+ self.assertEqual(ret, '')
+
+ def testSame(self):
+ """Test when there is no difference"""
+ ret = ekeyword.diff_keywords(['a b c'], ['a b c'])
+ self.assertEqual(ret, 'a b c')
+
+ def testInsert(self):
+ """Test when content is simply added"""
+ ret = ekeyword.diff_keywords(['a'], ['~a'])
+ self.assertNotEqual(ret, '')
+
+ def testDelete(self):
+ """Test when content is simply deleted"""
+ ret = ekeyword.diff_keywords(['~a'], ['a'])
+ self.assertNotEqual(ret, '')
+
+ def testReplace(self):
+ """Test when some content replaces another"""
+ ret = ekeyword.diff_keywords(['~a'], ['-a'])
+ self.assertNotEqual(ret, '')
+
+ def _testSmokeStyle(self, style):
+ return ekeyword.diff_keywords(
+ ['~a', 'b', '-abcde'],
+ ['a', '-b', '-abxde'], style=style)
+
+ def testSmokeStyleColor(self):
+ """Run a full smoke test for color-inline style"""
+ ret = self._testSmokeStyle('color-inline')
+ self.assertNotEqual(ret, '')
+
+ def testSmokeStyleNoColor(self):
+ """Run a full smoke test for non-color-inline style"""
+ self._testSmokeStyle('nocolor')
+
+
+class TestProcessKeywords(unittest.TestCase):
+ """Tests for process_keywords"""
+
+ def _test(self, keywords, ops, exp, arch_status=None):
+ # This func doesn't return sorted results (which is fine),
+ # so do so ourselves to get stable tests.
+ ret = ekeyword.process_keywords(
+ keywords.split(), ops, arch_status=arch_status)
+ self.assertEqual(sorted(ret), sorted(exp.split()))
+
+ def testAdd(self):
+ ops = (
+ ekeyword.Op(None, 'arm', None),
+ ekeyword.Op('~', 's390', None),
+ ekeyword.Op('-', 'sh', None),
+ )
+ self._test('moo', ops, 'arm ~s390 -sh moo')
+
+ def testModify(self):
+ ops = (
+ ekeyword.Op(None, 'arm', None),
+ ekeyword.Op('~', 's390', None),
+ ekeyword.Op('-', 'sh', None),
+ )
+ self._test('~arm s390 ~sh moo', ops, 'arm ~s390 -sh moo')
+
+ def testDelete(self):
+ ops = (
+ ekeyword.Op('^', 'arm', None),
+ ekeyword.Op('^', 's390', None),
+ ekeyword.Op('^', 'x86', None),
+ )
+ self._test('arm -s390 ~x86 bar', ops, 'bar')
+
+ def testSync(self):
+ ops = (
+ ekeyword.Op('=', 'arm64', 'arm'),
+ ekeyword.Op('=', 'ppc64', 'ppc'),
+ ekeyword.Op('=', 'amd64', 'x86'),
+ ekeyword.Op('=', 'm68k', 'mips'),
+ ekeyword.Op('=', 'ia64', 'alpha'),
+ ekeyword.Op('=', 'sh', 'sparc'),
+ ekeyword.Op('=', 's390', 's390x'),
+ ekeyword.Op('=', 'boo', 'moo'),
+ )
+ self._test(
+ 'arm64 arm '
+ '~ppc64 ~ppc '
+ '~amd64 x86 '
+ 'm68k ~mips '
+ '-ia64 alpha '
+ 'sh -sparc '
+ 's390 '
+ 'moo ',
+ ops,
+ 'arm64 arm ~ppc64 ~ppc amd64 x86 ~m68k ~mips ia64 alpha '
+ '-sh -sparc boo moo')
+
+ def testAllNoStatus(self):
+ ops = (
+ ekeyword.Op(None, 'all', None),
+ )
+ self.assertRaises(ValueError, self._test, '', ops, '')
+
+ def testAllStable(self):
+ ops = (
+ ekeyword.Op(None, 'all', None),
+ )
+ arch_status = {
+ 'alpha': None,
+ 'arm': 'stable',
+ 'arm64': 'exp',
+ 'm68k': 'dev',
+ }
+ self._test('* ~alpha ~arm ~arm64 ~m68k ~mips ~arm-linux', ops,
+ '* ~alpha arm ~arm64 ~m68k ~mips ~arm-linux', arch_status)
+
+ def testAllUnstable(self):
+ ops = (
+ ekeyword.Op('~', 'all', None),
+ )
+ arch_status = {
+ 'alpha': None,
+ 'arm': 'stable',
+ 'arm64': 'exp',
+ 'm68k': 'dev',
+ 's390': 'dev',
+ 'sh': 'dev',
+ }
+ self._test('-* ~* * alpha arm arm64 m68k arm-linux', ops,
+ '-* ~* * ~alpha ~arm ~arm64 ~m68k ~arm-linux', arch_status)
+
+ def testAllMultiUnstableStable(self):
+ ops = (
+ ekeyword.Op('~', 'all', None),
+ ekeyword.Op(None, 'all', None),
+ )
+ arch_status = {
+ 'alpha': None,
+ 'arm': 'stable',
+ 'arm64': 'exp',
+ 'm68k': 'dev',
+ }
+ self._test('-* ~* * alpha arm arm64 m68k', ops,
+ '-* ~* * ~alpha arm ~arm64 ~m68k', arch_status)
+
+ def testAllDisabled(self):
+ """Make sure ~all does not change -arch to ~arch"""
+ ops = (
+ ekeyword.Op('~', 'all', None),
+ )
+ self._test('alpha -sparc ~x86', ops,
+ '~alpha -sparc ~x86', {})
+
+
+class TestProcessContent(unittest.TestCase):
+ """Tests for process_content"""
+
+ def _testKeywords(self, line):
+ ops = (
+ ekeyword.Op(None, 'arm', None),
+ ekeyword.Op('~', 'sparc', None),
+ )
+ return ekeyword.process_content(
+ 'file', ['%s\n' % line], ops, quiet=True)
+
+ def testKeywords(self):
+ """Basic KEYWORDS mod"""
+ updated, ret = self._testKeywords('KEYWORDS=""')
+ self.assertTrue(updated)
+ self.assertEqual(ret, ['KEYWORDS="arm ~sparc"\n'])
+
+ def testKeywordsIndented(self):
+ """Test KEYWORDS indented by space"""
+ updated, ret = self._testKeywords(' KEYWORDS=""')
+ self.assertTrue(updated)
+ self.assertEqual(ret, [' KEYWORDS="arm ~sparc"\n'])
+
+ def testKeywordsSingleQuote(self):
+ """Test single quoted KEYWORDS"""
+ updated, ret = self._testKeywords("KEYWORDS=' '")
+ self.assertTrue(updated)
+ self.assertEqual(ret, ['KEYWORDS="arm ~sparc"\n'])
+
+ def testKeywordsComment(self):
+ """Test commented out KEYWORDS"""
+ updated, ret = self._testKeywords('# KEYWORDS=""')
+ self.assertFalse(updated)
+ self.assertEqual(ret, ['# KEYWORDS=""\n'])
+
+ def testKeywordsCode(self):
+ """Test code leading KEYWORDS"""
+ updated, ret = self._testKeywords('[[ ${PV} ]] && KEYWORDS=""')
+ self.assertTrue(updated)
+ self.assertEqual(ret, ['[[ ${PV} ]] && KEYWORDS="arm ~sparc"\n'])
+
+ def testKeywordsEmpty(self):
+ """Test KEYWORDS not set at all"""
+ updated, ret = self._testKeywords(' KEYWORDS=')
+ self.assertFalse(updated)
+ self.assertEqual(ret, [' KEYWORDS=\n'])
+
+ def _testSmoke(self, style='color-inline', verbose=0, quiet=0):
+ ops = (
+ ekeyword.Op(None, 'arm', None),
+ ekeyword.Op('~', 'sparc', None),
+ )
+ ekeyword.process_content(
+ 'asdf', ['KEYWORDS="arm"'], ops, verbose=verbose,
+ quiet=quiet, style=style)
+
+ def testSmokeQuiet(self):
+ """Smoke test for quiet mode"""
+ self._testSmoke(quiet=10)
+
+ def testSmokeVerbose(self):
+ """Smoke test for verbose mode"""
+ self._testSmoke(verbose=10)
+
+ def testSmokeStyleColor(self):
+ """Smoke test for color-inline style"""
+ self._testSmoke('color-inline')
+
+ def testSmokeStyleInline(self):
+ """Smoke test for inline style"""
+ self._testSmoke('inline')
+
+ def testSmokeStyleShortMulti(self):
+ """Smoke test for short-multi style"""
+ self._testSmoke('short-multi')
+
+ def testSmokeStyleLongMulti(self):
+ """Smoke test for long-multi style"""
+ self._testSmoke('long-multi')
+
+
+class TestProcessEbuild(unittest.TestCase):
+ """Tests for process_ebuild
+
+ This is fairly light as most code is in process_content.
+ """
+
+ def _process_ebuild(self, *args, **kwargs):
+ """Set up a writable copy of an ebuild for process_ebuild()"""
+ with tempfile.NamedTemporaryFile() as tmp:
+ with open(tmp.name, 'wb') as fw:
+ with open(os.path.join(TESTDIR, 'process-1.ebuild'), 'rb') as f:
+ orig_content = f.read()
+ fw.write(orig_content)
+ ekeyword.process_ebuild(tmp.name, *args, **kwargs)
+ with open(tmp.name, 'rb') as f:
+ return (orig_content, f.read())
+
+ def _testSmoke(self, dry_run):
+ ops = (
+ ekeyword.Op(None, 'arm', None),
+ ekeyword.Op('~', 'sparc', None),
+ )
+ orig_content, new_content = self._process_ebuild(ops, dry_run=dry_run)
+ if dry_run:
+ self.assertEqual(orig_content, new_content)
+ else:
+ self.assertNotEqual(orig_content, new_content)
+
+ def testSmokeNotDry(self):
+ self._testSmoke(False)
+
+ def testSmokeDry(self):
+ self._testSmoke(True)
+
+ def testManifestUpdated(self):
+ """Verify `ebuild ... manifest` runs on updated files"""
+ with mock.patch.object(subprocess, 'check_call') as m:
+ self._process_ebuild((ekeyword.Op('~', 'arm', None),),
+ manifest=True)
+ m.assert_called_once_with(['ebuild', mock.ANY, 'manifest'])
+
+ def testManifestNotUpdated(self):
+ """Verify we don't run `ebuild ... manifest` on unmodified files"""
+ with mock.patch.object(subprocess, 'check_call') as m:
+ self._process_ebuild((ekeyword.Op(None, 'arm', None),),
+ manifest=True)
+ self.assertEqual(m.call_count, 0)
+
+
+class TestLoadProfileData(unittest.TestCase):
+ """Tests for load_profile_data"""
+
+ def _test(self, subdir):
+ portdir = os.path.join(TESTDIR, 'profiles', subdir)
+ return ekeyword.load_profile_data(portdir=portdir)
+
+ def testLoadBoth(self):
+ """Test loading both arch.list and profiles.desc"""
+ ret = self._test('both')
+ self.assertIn('arm', ret)
+ self.assertEqual(ret['arm'], 'stable')
+ self.assertIn('arm64', ret)
+ self.assertEqual(ret['arm64'], 'exp')
+
+ def testLoadArchOnly(self):
+ """Test loading only arch.list"""
+ ret = self._test('arch-only')
+ self.assertIn('arm', ret)
+ self.assertEqual(ret['arm'], None)
+ self.assertIn('x86-solaris', ret)
+
+ def testLoadProfilesOnly(self):
+ """Test loading only profiles.desc"""
+ ret = self._test('profiles-only')
+ self.assertIn('arm', ret)
+ self.assertEqual(ret['arm'], 'stable')
+ self.assertIn('arm64', ret)
+ self.assertEqual(ret['arm64'], 'exp')
+
+ def testLoadNone(self):
+ """Test running when neither files exists"""
+ ret = self._test('none')
+ self.assertEqual(ret, {})
+
+
+class TestArgToOps(unittest.TestCase):
+ """Tests for arg_to_op()"""
+
+ def _test(self, arg, op):
+ self.assertEqual(ekeyword.arg_to_op(arg), ekeyword.Op(*op))
+
+ def testStable(self):
+ self._test('arm', (None, 'arm', None))
+
+ def testUnstable(self):
+ self._test('~ppc64', ('~', 'ppc64', None))
+
+ def testDisabled(self):
+ self._test('-sparc', ('-', 'sparc', None))
+
+ def testDeleted(self):
+ self._test('^x86-fbsd', ('^', 'x86-fbsd', None))
+
+ def testSync(self):
+ self._test('s390=x86', (None, 's390', 'x86'))
+
+
+class TestMain(unittest.TestCase):
+ """Tests for the main entry point"""
+
+ def testSmoke(self):
+ ekeyword.main(['arm', '--dry-run', os.path.join(TESTDIR, 'process-1.ebuild')])
+
+ def testVersion(self):
+ with self.assertRaises(SystemExit) as e:
+ ekeyword.main(['--version', '--dry-run'])
+ self.assertEqual(e.exception.code, os.EX_OK)
+
+ def testEmptyString(self):
+ with self.assertRaises(SystemExit) as e:
+ ekeyword.main(['', os.path.join(TESTDIR, 'process-1.ebuild')])
+ self.assertNotEqual(e.exception.code, os.EX_OK)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pym/gentoolkit/ekeyword/pylint b/pym/gentoolkit/ekeyword/pylint
new file mode 100755
index 0000000..3a9a368
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/pylint
@@ -0,0 +1,49 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+# Copyright 1999-2017 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+"""Run pylint with the right settings."""
+
+from __future__ import print_function
+
+import os
+import sys
+
+
+def find_all_modules(source_root):
+ """Locate all python modules in the tree for scanning"""
+ ret = []
+
+ for root, _dirs, files in os.walk(source_root, topdown=False):
+ # Add all of the .py modules in the tree.
+ ret += [os.path.join(root, x) for x in files if x.endswith('.py')]
+
+ # Add the main scripts that don't end in .py.
+ ret += [os.path.join(source_root, x) for x in ('pylint',)]
+
+ return ret
+
+
+def main(argv):
+ """The main entry point"""
+ source_root = os.path.dirname(os.path.realpath(__file__))
+
+ if not argv:
+ argv = find_all_modules(source_root)
+
+ pympath = source_root
+ pythonpath = os.environ.get('PYTHONPATH')
+ if pythonpath is None:
+ pythonpath = pympath
+ else:
+ pythonpath = pympath + ':' + pythonpath
+ os.environ['PYTHONPATH'] = pythonpath
+
+ pylintrc = os.path.join(source_root, '.pylintrc')
+ cmd = ['pylint', '--rcfile', pylintrc]
+ os.execvp(cmd[0], cmd + argv)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[1:]))
diff --git a/pym/gentoolkit/ekeyword/pytest.ini b/pym/gentoolkit/ekeyword/pytest.ini
new file mode 100644
index 0000000..622c9d8
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+addopts = --cov
+python_files = *_unittest.py
diff --git a/pym/gentoolkit/ekeyword/tests/process-1.ebuild b/pym/gentoolkit/ekeyword/tests/process-1.ebuild
new file mode 100644
index 0000000..75168c6
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/process-1.ebuild
@@ -0,0 +1,5 @@
+# asdf
+
+KEYWORDS="arm ~hppa x86"
+
+# blah
diff --git a/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list b/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list
new file mode 120000
index 0000000..361ad76
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/arch-only/profiles/arch.list
@@ -0,0 +1 @@
+../../both/profiles/arch.list \ No newline at end of file
diff --git a/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list
new file mode 100644
index 0000000..e4787c0
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/arch.list
@@ -0,0 +1,45 @@
+alpha
+amd64
+amd64-fbsd
+arm
+arm64
+hppa
+ia64
+m68k
+mips
+ppc
+ppc64
+s390
+sh
+sparc
+sparc-fbsd
+x86
+x86-fbsd
+
+# Prefix keywords
+ppc-aix
+x86-freebsd
+x64-freebsd
+sparc64-freebsd
+hppa-hpux
+ia64-hpux
+x86-interix
+amd64-linux
+arm-linux
+ia64-linux
+ppc64-linux
+x86-linux
+ppc-macos
+x86-macos
+x64-macos
+m68k-mint
+x86-netbsd
+ppc-openbsd
+x86-openbsd
+x64-openbsd
+sparc-solaris
+sparc64-solaris
+x64-solaris
+x86-solaris
+x86-winnt
+x86-cygwin
diff --git a/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc
new file mode 100644
index 0000000..be751a8
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/both/profiles/profiles.desc
@@ -0,0 +1,295 @@
+#############################################
+# This is a list of valid profiles for each architecture. This file is used by
+# repoman when doing a repoman scan or repoman full.
+# DO NOT ADD PROFILES WITH A "die" or "exit" IN THEM OR IT KILLS REPOMAN
+#
+#layout:
+#arch profile_directory status
+
+# Alpha Profiles
+alpha default/linux/alpha/13.0 stable
+alpha default/linux/alpha/13.0/desktop stable
+alpha default/linux/alpha/13.0/desktop/gnome stable
+alpha default/linux/alpha/13.0/desktop/gnome/systemd stable
+alpha default/linux/alpha/13.0/desktop/kde stable
+alpha default/linux/alpha/13.0/desktop/kde/systemd stable
+alpha default/linux/alpha/13.0/developer stable
+
+# AMD64 Profiles
+amd64 default/linux/amd64/13.0 stable
+amd64 default/linux/amd64/13.0/selinux dev
+amd64 default/linux/amd64/13.0/desktop stable
+amd64 default/linux/amd64/13.0/desktop/gnome stable
+amd64 default/linux/amd64/13.0/desktop/gnome/systemd stable
+amd64 default/linux/amd64/13.0/desktop/kde stable
+amd64 default/linux/amd64/13.0/desktop/kde/systemd stable
+amd64 default/linux/amd64/13.0/developer stable
+amd64 default/linux/amd64/13.0/no-multilib dev
+amd64 default/linux/amd64/13.0/x32 dev
+
+# ARM Profiles
+arm default/linux/arm/13.0 stable
+arm default/linux/arm/13.0/desktop dev
+arm default/linux/arm/13.0/desktop/gnome dev
+arm default/linux/arm/13.0/desktop/gnome/systemd dev
+arm default/linux/arm/13.0/desktop/kde dev
+arm default/linux/arm/13.0/desktop/kde/systemd dev
+arm default/linux/arm/13.0/developer dev
+arm default/linux/arm/13.0/armv4 dev
+arm default/linux/arm/13.0/armv4/desktop dev
+arm default/linux/arm/13.0/armv4/desktop/gnome dev
+arm default/linux/arm/13.0/armv4/desktop/kde dev
+arm default/linux/arm/13.0/armv4/developer dev
+arm default/linux/arm/13.0/armv4t dev
+arm default/linux/arm/13.0/armv4t/desktop dev
+arm default/linux/arm/13.0/armv4t/desktop/gnome dev
+arm default/linux/arm/13.0/armv4t/desktop/kde dev
+arm default/linux/arm/13.0/armv4t/developer dev
+arm default/linux/arm/13.0/armv5te dev
+arm default/linux/arm/13.0/armv5te/desktop dev
+arm default/linux/arm/13.0/armv5te/desktop/gnome dev
+arm default/linux/arm/13.0/armv5te/desktop/kde dev
+arm default/linux/arm/13.0/armv5te/developer dev
+arm default/linux/arm/13.0/armv6j dev
+arm default/linux/arm/13.0/armv6j/desktop dev
+arm default/linux/arm/13.0/armv6j/desktop/gnome dev
+arm default/linux/arm/13.0/armv6j/desktop/kde dev
+arm default/linux/arm/13.0/armv6j/developer dev
+arm default/linux/arm/13.0/armv7a dev
+arm default/linux/arm/13.0/armv7a/desktop dev
+arm default/linux/arm/13.0/armv7a/desktop/gnome dev
+arm default/linux/arm/13.0/armv7a/desktop/kde dev
+arm default/linux/arm/13.0/armv7a/developer dev
+
+# ARM64 Profiles
+arm64 default/linux/arm64/13.0 exp
+arm64 default/linux/arm64/13.0/desktop exp
+arm64 default/linux/arm64/13.0/developer exp
+
+# HPPA Profiles
+hppa default/linux/hppa/13.0 stable
+hppa default/linux/hppa/13.0/desktop dev
+hppa default/linux/hppa/13.0/developer dev
+
+# IA64 Profiles
+ia64 default/linux/ia64/13.0 stable
+ia64 default/linux/ia64/13.0/desktop stable
+ia64 default/linux/ia64/13.0/desktop/gnome stable
+ia64 default/linux/ia64/13.0/desktop/gnome/systemd stable
+ia64 default/linux/ia64/13.0/desktop/kde stable
+ia64 default/linux/ia64/13.0/desktop/kde/systemd stable
+ia64 default/linux/ia64/13.0/developer stable
+
+# M68K Profiles
+m68k default/linux/m68k/13.0 dev
+m68k default/linux/m68k/13.0/desktop dev
+m68k default/linux/m68k/13.0/desktop/gnome dev
+m68k default/linux/m68k/13.0/desktop/kde dev
+m68k default/linux/m68k/13.0/developer dev
+
+# MIPS Profiles
+mips default/linux/mips/13.0 dev
+mips default/linux/mips/13.0/n32 dev
+mips default/linux/mips/13.0/n64 exp
+mips default/linux/mips/13.0/multilib dev
+mips default/linux/mips/13.0/multilib/n32 dev
+mips default/linux/mips/13.0/multilib/n64 exp
+mips default/linux/mips/13.0/mipsel dev
+mips default/linux/mips/13.0/mipsel/n32 dev
+mips default/linux/mips/13.0/mipsel/n64 exp
+mips default/linux/mips/13.0/mipsel/multilib dev
+mips default/linux/mips/13.0/mipsel/multilib/n32 dev
+mips default/linux/mips/13.0/mipsel/multilib/n64 exp
+
+# PPC32 Profiles
+ppc default/linux/powerpc/ppc32/13.0 stable
+ppc default/linux/powerpc/ppc32/13.0/desktop stable
+ppc default/linux/powerpc/ppc32/13.0/desktop/gnome stable
+ppc default/linux/powerpc/ppc32/13.0/desktop/gnome/systemd stable
+ppc default/linux/powerpc/ppc32/13.0/desktop/kde stable
+ppc default/linux/powerpc/ppc32/13.0/desktop/kde/systemd stable
+ppc default/linux/powerpc/ppc32/13.0/developer stable
+
+# PPC64 Profiles
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/desktop stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/gnome stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/gnome/systemd stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/kde stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/desktop/kde/systemd stable
+ppc default/linux/powerpc/ppc64/13.0/32bit-userland/developer stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/desktop stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/gnome stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/gnome/systemd stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/kde stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/desktop/kde/systemd stable
+ppc64 default/linux/powerpc/ppc64/13.0/64bit-userland/developer stable
+
+# S390 Profiles
+s390 default/linux/s390/13.0 dev
+s390 default/linux/s390/13.0/s390x dev
+
+# SH Profiles
+sh default/linux/sh/13.0 dev
+sh default/linux/sh/13.0/desktop dev
+sh default/linux/sh/13.0/desktop/gnome dev
+sh default/linux/sh/13.0/desktop/kde dev
+sh default/linux/sh/13.0/developer dev
+
+# SPARC Profiles
+sparc default/linux/sparc/13.0 stable
+sparc default/linux/sparc/13.0/desktop stable
+sparc default/linux/sparc/13.0/desktop/gnome stable
+sparc default/linux/sparc/13.0/desktop/kde stable
+sparc default/linux/sparc/13.0/developer stable
+
+# x86 Profiles
+x86 default/linux/x86/13.0 stable
+x86 default/linux/x86/13.0/selinux dev
+x86 default/linux/x86/13.0/desktop stable
+x86 default/linux/x86/13.0/desktop/gnome stable
+x86 default/linux/x86/13.0/desktop/gnome/systemd stable
+x86 default/linux/x86/13.0/desktop/kde stable
+x86 default/linux/x86/13.0/desktop/kde/systemd stable
+x86 default/linux/x86/13.0/developer stable
+
+# Gentoo/FreeBSD Profiles
+amd64-fbsd default/bsd/fbsd/amd64/9.1 stable
+amd64-fbsd default/bsd/fbsd/amd64/9.2 dev
+amd64-fbsd default/bsd/fbsd/amd64/9.1/clang exp
+amd64-fbsd default/bsd/fbsd/amd64/9.2/clang exp
+sparc-fbsd default/bsd/fbsd/sparc/8.2 exp
+x86-fbsd default/bsd/fbsd/x86/9.1 dev
+x86-fbsd default/bsd/fbsd/x86/9.2 dev
+
+# Hardened Profiles
+amd64 hardened/linux/amd64 stable
+amd64 hardened/linux/amd64/selinux stable
+amd64 hardened/linux/amd64/no-multilib stable
+amd64 hardened/linux/amd64/no-multilib/selinux stable
+amd64 hardened/linux/amd64/x32 dev
+amd64 hardened/linux/uclibc/amd64 dev
+arm hardened/linux/arm/armv7a dev
+arm hardened/linux/arm/armv6j dev
+arm hardened/linux/uclibc/arm/armv7a dev
+ia64 hardened/linux/ia64 dev
+mips hardened/linux/uclibc/mips exp
+mips hardened/linux/uclibc/mips/mipsel exp
+ppc hardened/linux/powerpc/ppc32 dev
+ppc hardened/linux/powerpc/ppc64/32bit-userland dev
+ppc64 hardened/linux/powerpc/ppc64/64bit-userland dev
+x86 hardened/linux/x86 stable
+x86 hardened/linux/x86/selinux stable
+x86 hardened/linux/uclibc/x86 dev
+
+# uclibc/embedded multiarch profiles
+#amd64 uclibc/amd64 dev
+#arm uclibc/arm dev
+#arm uclibc/arm/2.4 dev
+#mips uclibc/mips dev
+#mips uclibc/mips/hardened dev
+#ppc uclibc/ppc dev
+#ppc uclibc/ppc/2.4 dev
+#ppc uclibc/ppc/hardened dev
+#ppc uclibc/ppc/hardened/2.4 dev
+#sh uclibc/sh dev
+#sh uclibc/sh/2.4 dev
+#x86 uclibc/x86 dev
+#x86 uclibc/x86/2.4 dev
+#x86 uclibc/x86/2005.1 dev
+#x86 uclibc/x86/2005.1/2.4 dev
+#x86 uclibc/x86/hardened dev
+#x86 uclibc/x86/hardened/2.4 dev
+
+
+# These are Gentoo Prefix profiles, maintained by the Prefix team
+
+# Linux Profiles
+amd64-linux prefix/linux/amd64 exp
+arm-linux prefix/linux/arm exp
+ia64-linux prefix/linux/ia64 exp
+ppc64-linux prefix/linux/ppc64 exp
+x86-linux prefix/linux/x86 exp
+
+# Mac OS X Profiles
+ppc-macos prefix/darwin/macos/10.4/ppc exp
+x86-macos prefix/darwin/macos/10.4/x86 exp
+ppc-macos prefix/darwin/macos/10.5/ppc exp
+x86-macos prefix/darwin/macos/10.5/x86 exp
+x64-macos prefix/darwin/macos/10.5/x64 exp
+x86-macos prefix/darwin/macos/10.6/x86 exp
+x64-macos prefix/darwin/macos/10.6/x64 exp
+x86-macos prefix/darwin/macos/10.7/x86 exp
+x64-macos prefix/darwin/macos/10.7/x64 exp
+x86-macos prefix/darwin/macos/10.8/x86 exp
+x64-macos prefix/darwin/macos/10.8/x64 exp
+x86-macos prefix/darwin/macos/10.9/x86 exp
+x64-macos prefix/darwin/macos/10.9/x64 exp
+
+# Solaris Profiles
+sparc-solaris prefix/sunos/solaris/5.9/sparc exp
+sparc-solaris prefix/sunos/solaris/5.10/sparc exp
+sparc64-solaris prefix/sunos/solaris/5.10/sparc64 exp
+x86-solaris prefix/sunos/solaris/5.10/x86 exp
+x64-solaris prefix/sunos/solaris/5.10/x64 exp
+sparc-solaris prefix/sunos/solaris/5.11/sparc exp
+sparc64-solaris prefix/sunos/solaris/5.11/sparc64 exp
+x86-solaris prefix/sunos/solaris/5.11/x86 exp
+x64-solaris prefix/sunos/solaris/5.11/x64 exp
+
+# AIX Profiles
+ppc-aix prefix/aix/5.2.0.0/ppc exp
+ppc-aix prefix/aix/5.3.0.0/ppc exp
+ppc-aix prefix/aix/6.1.0.0/ppc exp
+
+# Interix Profiles
+x86-interix prefix/windows/interix/3.5/x86 exp
+x86-interix prefix/windows/interix/5.2/x86 exp
+x86-interix prefix/windows/interix/6.0/x86 exp
+x86-interix prefix/windows/interix/6.1/x86 exp
+
+# Windows Profiles
+x86-winnt prefix/windows/winnt/3.5/x86 exp
+x86-winnt prefix/windows/winnt/5.2/x86 exp
+x86-winnt prefix/windows/winnt/6.0/x86 exp
+x86-winnt prefix/windows/winnt/6.1/x86 exp
+
+# Cygwin Profiles
+x86-cygwin prefix/windows/cygwin/1.7/x86 exp
+
+# HP-UX Profiles
+ia64-hpux prefix/hpux/B.11.23/ia64 exp
+hppa-hpux prefix/hpux/B.11.31/hppa2.0 exp
+ia64-hpux prefix/hpux/B.11.31/ia64 exp
+
+# FreeBSD Profiles
+x86-freebsd prefix/bsd/freebsd/7.1/x86 exp
+x64-freebsd prefix/bsd/freebsd/7.1/x64 exp
+x86-freebsd prefix/bsd/freebsd/7.2/x86 exp
+x64-freebsd prefix/bsd/freebsd/7.2/x64 exp
+x86-freebsd prefix/bsd/freebsd/8.0/x86 exp
+x64-freebsd prefix/bsd/freebsd/8.0/x64 exp
+x86-freebsd prefix/bsd/freebsd/8.1/x86 exp
+x64-freebsd prefix/bsd/freebsd/8.1/x64 exp
+sparc64-freebsd prefix/bsd/freebsd/8.1/sparc64 exp
+x86-freebsd prefix/bsd/freebsd/8.2/x86 exp
+x64-freebsd prefix/bsd/freebsd/8.2/x64 exp
+x86-freebsd prefix/bsd/freebsd/9.0/x86 exp
+x64-freebsd prefix/bsd/freebsd/9.0/x64 exp
+x86-freebsd prefix/bsd/freebsd/9.1/x86 exp
+x64-freebsd prefix/bsd/freebsd/9.1/x64 exp
+
+
+# OpenBSD Profiles
+ppc-openbsd prefix/bsd/openbsd/4.2/ppc exp
+x86-openbsd prefix/bsd/openbsd/4.2/x86 exp
+x64-openbsd prefix/bsd/openbsd/4.2/x64 exp
+
+# NetBSD Profiles
+x86-netbsd prefix/bsd/netbsd/4.0/x86 exp
+
+# FreeMiNT
+m68k-mint prefix/mint/m68k exp
+
+# vim: set ts=8:
diff --git a/pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep b/pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/none/profiles/.keep
diff --git a/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc b/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc
new file mode 120000
index 0000000..04f8005
--- /dev/null
+++ b/pym/gentoolkit/ekeyword/tests/profiles/profiles-only/profiles/profiles.desc
@@ -0,0 +1 @@
+../../both/profiles/profiles.desc \ No newline at end of file
diff --git a/pym/gentoolkit/imlate/Makefile b/pym/gentoolkit/imlate/Makefile
new file mode 100644
index 0000000..6735696
--- /dev/null
+++ b/pym/gentoolkit/imlate/Makefile
@@ -0,0 +1,18 @@
+# Copyright 1999-2009 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+include ../../makedefs.mak
+
+.PHONY: all
+
+all:
+
+dist:
+ mkdir -p ../../$(DISTDIR)/src/imlate/
+ cp Makefile imlate imlate.1 ../../$(DISTDIR)/src/imlate/
+
+install: all
+ install -m 0755 imlate $(BINDIR)/
+ install -m 0644 imlate.1 $(MAN1DIR)/
+
diff --git a/pym/gentoolkit/imlate/imlate b/pym/gentoolkit/imlate/imlate
new file mode 100755
index 0000000..0be72e4
--- /dev/null
+++ b/pym/gentoolkit/imlate/imlate
@@ -0,0 +1,480 @@
+#!/usr/bin/python
+# Copyright 1999-2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Id$
+# Author: Christian Ruppert <idl0r@gentoo.org>
+
+# Python 2.6 compatibility
+from __future__ import print_function
+
+VERSION = "1.0.1"
+
+# works just with stable keywords!
+MAIN_ARCH = "auto" # can be overridden by -m ARCH
+TARGET_ARCH = "auto" # can be overridden by -t ARCH
+# auto means e.g.:
+# MAIN_ARCH = amd64
+# TARGET_ARCH = ~amd64
+# That will show you general stable candidates for amd64.
+# The arch will be taken from your portage settings (e.g. make.conf).
+
+################################
+# do not change anything below #
+################################
+
+from os.path import join, basename
+from sys import stderr, stdout
+from os import stat
+from time import time
+from xml.dom import minidom, NotFoundErr
+from xml.parsers.expat import ExpatError
+# TODO: just import needed stuff to safe memory/time and maybe use "as foo"
+import portage
+import portage.versions
+
+if __name__ == "__main__":
+ from optparse import OptionParser
+ from time import gmtime, strftime
+
+# override/change portage module settings
+def _portage_settings( var, value, settings = None ):
+ if not settings:
+ settings = portage.settings
+
+ settings.unlock()
+ settings[var] = value
+ # backup_changes is very important since it can cause trouble,
+ # if we do not backup our changes!
+ settings.backup_changes( var )
+ settings.lock()
+
+# add stuff to our imlate dict
+def _add_ent( imlate, cat, pkg, ver, our_ver ):
+ if not cat in list(imlate.keys()):
+ imlate[cat] = {}
+ if not pkg in list(imlate[cat].keys()):
+ imlate[cat][pkg] = []
+
+ imlate[cat][pkg].append( ver )
+ imlate[cat][pkg].append( our_ver )
+
+ return imlate
+
+def _fill( width, line, fill = " " ):
+ while len( line ) < width:
+ line = "%s%s" % ( str( line ), str( fill ) )
+ return line
+
+# create a hopefully pretty result
+def show_result( conf, pkgs ):
+ # X - len(colX) = space to fill
+ col1 = 40
+ col2 = 20
+
+ _header = "%s candidates for 'gentoo' on '%s'"
+ _helper = "category/package[:SLOT] our version best version"
+ _cand = ""
+ header = ""
+
+ if conf["FILE"] == "stdout":
+ out = stdout
+ elif conf["FILE"] == "stderr":
+ out = stderr
+ else:
+ out = open( conf["FILE"], "w" )
+
+ if conf["STABLE"] and conf["KEYWORD"]:
+ _cand = "%i Stable and %i Keyword(~)" % ( conf["STABLE_SUM"],
+ conf["KEYWORD_SUM"] )
+ elif conf["STABLE"]:
+ _cand = "%i Stable" % conf["STABLE_SUM"]
+ elif conf["KEYWORD"]:
+ _cand = "%i Keyword(~)" % conf["KEYWORD_SUM"]
+
+ header = _header % ( _cand, conf["MAIN_ARCH"] )
+
+ print("Generated on: %s" % conf["TIME"], file=out)
+ print(_fill( len( header ), "", "=" ), file=out)
+ print(header, file=out)
+ print(_fill( len( header ), "", "=" ), file=out)
+ print(file=out)
+
+ print(_helper, file=out)
+ print(_fill( len( _helper ), "", "-" ), file=out)
+
+ for cat in sorted( pkgs.keys() ):
+ print("%s/" % cat, file=out)
+ for pkg in sorted( pkgs[cat].keys() ):
+ print("%s%s%s" % ( _fill( col1, ( " %s" % pkg ) ),
+ _fill( col2, pkgs[cat][pkg][1] ),
+ pkgs[cat][pkg][0] ), file=out)
+
+ if conf["FILE"] != "stdout":
+ out.close()
+
+def _get_metadata(metadata, element, tag):
+ values = []
+
+ try:
+ metadatadom = minidom.parse(metadata)
+ except ExpatError as e:
+ raise ExpatError("%s: %s" % (metadata, e,))
+
+ try:
+ elements = metadatadom.getElementsByTagName(element)
+ if not elements:
+ return values
+ except NotFoundErr:
+ return values
+
+ try:
+ for _element in elements:
+ node = _element.getElementsByTagName(tag)
+
+ if tag == "herd" and (not node or not node[0].childNodes):
+# print >> stderr, "'%s' is missing a <herd> tag or it is empty," % metadata
+# print >> stderr, "please file a bug at https://bugs.gentoo.org and refer to http://www.gentoo.org/proj/en/devrel/handbook/handbook.xml?part=2&chap=4"
+ values.append("no-herd")
+ continue
+
+ try:
+ values.append(node[0].childNodes[0].data)
+ except IndexError:
+ pass
+ except NotFoundErr:
+ raise NotFoundErr("%s: Malformed input: missing 'flag' tag(s)" % (metadata))
+
+ metadatadom.unlink()
+ return values
+
+def is_maintainer(maintainer, metadata):
+ data = []
+
+ if maintainer == None:
+ return True
+
+ mtainer = maintainer.split(",")
+
+ data = _get_metadata(metadata, "maintainer", "email")
+
+ if not data and len(maintainer) == 0:
+ return True
+ elif not data and len(maintainer) > 0:
+ return False
+ else:
+ for addy in data:
+ for contact in mtainer:
+ if addy == contact:
+ return True
+ if addy.startswith(contact):
+ return True
+ return False
+
+def is_herd(herd, metadata):
+ data = []
+
+ if herd == None:
+ return True
+
+ hrd = herd.split(",")
+ data = _get_metadata(metadata, "pkgmetadata", "herd")
+
+ if not data and len(herd) == 0:
+ return True
+ elif not data and len(herd) > 0:
+ return False
+ else:
+ for hd in data:
+ for hd2 in hrd:
+ if hd == hd2:
+ return True
+ if hd.startswith(hd2):
+ return True
+
+ return False
+
+
+# fetch a list of arch (just stable) packages
+# -* is important to be sure that just arch is used
+def get_packages( conf ):
+ _pkgs = {}
+
+ _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( conf["TARGET_ARCH"] ) ),
+ conf["portdb"].settings )
+
+ for cp in conf["portdb"].dbapi.cp_all():
+ cpvrs = []
+ slots = {}
+
+ if conf["USER_PKGS"]:
+ if not cp in conf["USER_PKGS"] and not basename(cp) in conf["USER_PKGS"]:
+ continue
+
+ # None is important to match also on empty string
+ if conf["MAINTAINER"] != None:
+ if not is_maintainer(conf["MAINTAINER"], join(conf["PORTDIR"], cp, "metadata.xml")):
+ continue
+ if conf["HERD"] != None:
+ if not is_herd(conf["HERD"], join(conf["PORTDIR"], cp, "metadata.xml")):
+ continue
+
+ cpvrs = conf["portdb"].dbapi.match( cp )
+
+ for cpvr in cpvrs:
+ slot = conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+ if not slot in slots:
+ slots[slot] = []
+ slots[slot].append(cpvr)
+
+ for slot in sorted(slots):
+ cpvr = portage.versions.best( slots[slot] )
+
+ if cpvr:
+ ( cat, pkg, ver, rev ) = portage.versions.catpkgsplit( cpvr )
+
+ if not cat in list(_pkgs.keys()):
+ _pkgs[cat] = {}
+ if not pkg in list(_pkgs[cat].keys()):
+ _pkgs[cat][pkg] = []
+
+ if rev != "r0":
+ ver = "%s-%s" % ( ver, rev )
+
+ _pkgs[cat][pkg].append( ver )
+
+ return _pkgs
+
+# compare get_packages() against MAIN_ARCH
+def get_imlate( conf, pkgs ):
+ _portage_settings( "ACCEPT_KEYWORDS", ( "-* %s" % str( conf["MAIN_ARCH"] ) ),
+ conf["portdb"].settings )
+
+ stable = str( conf["MAIN_ARCH"].lstrip("~") )
+ testing = "~%s" % stable
+ exclude = "-%s" % stable
+ exclude_all = "-*"
+
+ imlate = {}
+
+ for cat in sorted( pkgs.keys() ):
+ for pkg in sorted( pkgs[cat].keys() ):
+ for vr in pkgs[cat][pkg]:
+ cpvr = ""
+ abs_pkg = ""
+ kwds = ""
+ our = ""
+ our_ver = ""
+ mtime = 0
+ slot = 0
+
+ # 0 = none(default), 1 = testing(~arch), 2 = stable(arch),
+ # 3 = exclude(-arch), 4 = exclude_all(-*)
+ # -* would be overridden by ~arch or arch
+ kwd_type = 0
+
+ cpvr = "%s/%s-%s" % ( cat, pkg, vr )
+
+ # absolute ebuild path for mtime check
+ abs_pkg = join( conf["PORTDIR"], cat, pkg, basename( cpvr ) )
+ abs_pkg = "%s.ebuild" % str( abs_pkg )
+
+ kwds = conf["portdb"].dbapi.aux_get( cpvr, ["KEYWORDS"] )[0]
+
+ # FIXME: %s is bad.. maybe even cast it, else there are issues because its unicode
+ slot = ":%s" % conf["portdb"].dbapi.aux_get( cpvr, ["SLOT"] )[0]
+ if slot == ":0":
+ slot = ""
+
+ # sorted() to keep the right order
+ # e.g. -* first, -arch second, arch third and ~arch fourth
+ # -* -foo ~arch
+ # example: -* would be overridden by ~arch
+ for kwd in sorted( kwds.split() ):
+ if kwd == stable:
+ kwd_type = 2
+ break
+ elif kwd == exclude:
+ kwd_type = 3
+ break
+ elif kwd == exclude_all:
+ kwd_type = 4
+ elif kwd == testing:
+ kwd_type = 1
+ break
+
+ # ignore -arch and already stabilized packages
+ if kwd_type == 3 or kwd_type == 2:
+ continue
+ # drop packages with -* and without ~arch or arch
+ # even if there is another version which includes arch or ~arch
+ if kwd_type == 4:
+ continue
+ # drop "stable candidates" with mtime < 30 days
+ # Shall we use gmtime/UTC here?
+ if kwd_type == 1:
+ mtime = int( ( time() - stat( abs_pkg ).st_mtime ) / 60 / 60 / 24 )
+ if mtime < conf["MTIME"]:
+ continue
+
+ # look for an existing stable version
+ our = portage.versions.best( conf["portdb"].dbapi.match( "%s/%s%s" % ( cat, pkg, slot ) ) )
+ if our:
+ _foo = portage.versions.pkgsplit( our )
+ our_ver = _foo[1]
+ if _foo[2] != "r0":
+ our_ver = "%s-%s" % ( our_ver, _foo[2] )
+ else:
+ our_ver = ""
+
+ # we just need the version if > our_ver
+ if our_ver:
+ if portage.versions.vercmp( our_ver, vr ) >= 0:
+ continue
+
+ if kwd_type == 1 and conf["STABLE"]:
+ imlate = _add_ent( imlate, cat, ("%s%s" % (pkg, slot)), vr, our_ver )
+ conf["STABLE_SUM"] += 1
+ elif kwd_type == 0 and conf["KEYWORD"]:
+ conf["KEYWORD_SUM"] += 1
+ imlate = _add_ent( imlate, cat, ( "~%s%s" % (pkg, slot) ),
+ vr, our_ver )
+
+ return imlate
+
+# fetch portage related settings
+def get_settings( conf = None ):
+ if not isinstance( conf, dict ) and conf:
+ raise TypeError("conf must be dict() or None")
+ if not conf:
+ conf = {}
+
+ # TODO: maybe we should improve it a bit ;)
+ mysettings = portage.config( config_incrementals = portage.const.INCREMENTALS, local_config = False )
+
+ if conf["MAIN_ARCH"] == "auto":
+ conf["MAIN_ARCH"] = "%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+ if conf["TARGET_ARCH"] == "auto":
+ conf["TARGET_ARCH"] = "~%s" % mysettings["ACCEPT_KEYWORDS"].split(" ")[0].lstrip("~")
+
+ # TODO: exclude overlay categories from check
+ if conf["CATEGORIES"]:
+ _mycats = []
+ for _cat in conf["CATEGORIES"].split(","):
+ _cat = _cat.strip()
+ _mycats.append(_cat )
+ if _cat not in mysettings.categories:
+ raise ValueError("invalid category for -C switch '%s'" % _cat)
+ mysettings.categories = _mycats
+
+ # maybe thats not necessary because we override porttrees below..
+ _portage_settings( "PORTDIR_OVERLAY", "", mysettings )
+ trees = portage.create_trees()
+ trees["/"]["porttree"].settings = mysettings
+ portdb = trees["/"]["porttree"]
+ portdb.dbapi.settings = mysettings
+ portdb.dbapi.porttrees = [portage.portdb.porttree_root]
+ # does it make sense to remove _all_ useless stuff or just leave it as it is?
+ #portdb.dbapi._aux_cache_keys.clear()
+ #portdb.dbapi._aux_cache_keys.update(["EAPI", "KEYWORDS", "SLOT"])
+
+ conf["PORTDIR"] = portage.settings["PORTDIR"]
+ conf["portdb"] = portdb
+
+ return conf
+
+
+# just for standalone
+def main():
+ conf = {}
+ pkgs = {}
+
+ parser = OptionParser( version = "%prog " + VERSION )
+ parser.usage = "%prog [options] [category/package] ..."
+ parser.disable_interspersed_args()
+
+ parser.add_option( "-f", "--file", dest = "filename", action = "store", type = "string",
+ help = "write result into FILE [default: %default]", metavar = "FILE", default = "stdout" )
+ parser.add_option( "-m", "--main", dest = "main_arch", action = "store", type = "string",
+ help = "set main ARCH (e.g. your arch) [default: %default]", metavar = "ARCH", default = MAIN_ARCH )
+ parser.add_option( "-t", "--target", dest = "target_arch", action = "store", type = "string",
+ help = "set target ARCH (e.g. x86) [default: %default]", metavar = "ARCH", default = TARGET_ARCH )
+ parser.add_option( "--mtime", dest = "mtime", action = "store", type = "int",
+ help = "set minimum MTIME in days [default: %default]", metavar = "MTIME", default = 30 )
+
+ # TODO: leave a good comment here (about True/False) :)
+ parser.add_option( "-s", "--stable", dest = "stable", action = "store_true", default = False,
+ help = "just show stable candidates (e.g. -s and -k is the default result) [default: True]" )
+ parser.add_option( "-k", "--keyword", dest = "keyword", action = "store_true", default = False,
+ help = "just show keyword candidates (e.g. -s and -k is the default result) [default: True]" )
+
+ parser.add_option( "-M", "--maintainer", dest = "maintainer", action = "store", type = "string",
+ help = "Show only packages from the specified maintainer", metavar = "MAINTAINER", default = None)
+
+ parser.add_option( "-H", "--herd", dest = "herd", action = "store", type = "string",
+ help = "Show only packages from the specified herd", metavar = "HERD", default = None)
+
+# # EXPERIMENTAL
+# parser.add_option( "-e", "--experimental", dest = "experimental", action = "store_true", default = False,
+# help = "enables experimental functions/features (have a look for # EXPERIMENTAL comments in the source) [default: %default]" )
+
+ parser.add_option( "-C", "--category", "--categories", dest = "categories", action = "store", default = None,
+ metavar = "CATEGORIES",
+ help = "just check in the specified category/categories (comma separated) [default: %default]")
+
+ ( options, args ) = parser.parse_args()
+
+ if len( args ) > 0:
+ conf["USER_PKGS"] = args
+ else:
+ conf["USER_PKGS"] = []
+
+ # cleanup optparse
+ try:
+ parser.destroy()
+ except AttributeError:
+ # to be at least python 2.4 compatible
+ del parser._short_opt
+ del parser._long_opt
+ del parser.defaults
+
+ # generated timestamp (UTC)
+ conf["TIME"] = strftime( "%a %b %d %H:%M:%S %Z %Y", gmtime() )
+
+ # package counter
+ conf["KEYWORD_SUM"] = 0
+ conf["STABLE_SUM"] = 0
+
+ if not options.main_arch in portage.archlist and options.main_arch != "auto":
+ raise ValueError("invalid MAIN ARCH defined!")
+ if not options.target_arch in portage.archlist and options.target_arch != "auto":
+ raise ValueError("invalid TARGET ARCH defined!")
+
+ conf["MAIN_ARCH"] = options.main_arch
+ conf["TARGET_ARCH"] = options.target_arch
+
+ conf["FILE"] = options.filename
+ conf["MTIME"] = options.mtime
+
+ if not options.stable and not options.keyword:
+ conf["STABLE"] = True
+ conf["KEYWORD"] = True
+ else:
+ conf["STABLE"] = options.stable
+ conf["KEYWORD"] = options.keyword
+
+# conf["EXPERIMENTAL"] = options.experimental
+ conf["CATEGORIES"] = options.categories
+
+ conf["MAINTAINER"] = options.maintainer
+ conf["HERD"] = options.herd
+
+ # append to our existing
+ conf = get_settings( conf )
+ pkgs = get_packages( conf )
+ pkgs = get_imlate( conf, pkgs )
+
+ show_result( conf, pkgs )
+
+if __name__ == "__main__":
+ main()
+
diff --git a/pym/gentoolkit/imlate/imlate.1 b/pym/gentoolkit/imlate/imlate.1
new file mode 100644
index 0000000..b9163a4
--- /dev/null
+++ b/pym/gentoolkit/imlate/imlate.1
@@ -0,0 +1,48 @@
+.TH "imlate" "1" "1.0.0" "Christian Ruppert" "gentoolkit-dev"
+.SH "NAME"
+.LP
+imlate \- Displays candidates for keywords for an architecture based upon a target architecture.
+.SH "SYNTAX"
+.LP
+imlate [options]
+
+
+.SH "OPTIONS"
+.TP
+.B \-\-version
+show program's version number and exit
+.TP
+.B \-h, \-\-help
+show this help message and exit
+.TP
+.B \-f FILE, \-\-file=FILE
+write result into FILE [default: stdout]
+.TP
+.B \-m ARCH, \-\-main=ARCH
+set main ARCH (e.g. your arch) [default: amd64]
+.TP
+.B \-t ARCH, \-\-target=ARCH
+set target ARCH (e.g. x86) [default: x86]
+.TP
+.B \-\-mtime=MTIME
+set minimum MTIME in days [default: 30]
+.TP
+.B \-s, \-\-stable
+just show stable candidates (e.g. \-s and \-k is the default result) [default: True]
+.TP
+.B \-k, \-\-keyword
+just show keyword candidates (e.g. \-s and \-k is the default result) [default: True]
+.TP
+.B \-M MAINTAINER, \-\-maintainer=MAINTAINER
+Show only packages from the specified maintainer
+.TP
+.B \-H HERD, \-\-herd=HERD
+Show only packages from the specified herd
+.TP
+.B \-C CATEGORIES, \-\-category=CATEGORIES, \-\-categories=CATEGORIES
+just check in the specified category/categories (comma separated) [default: none]
+.SH "AUTHORS"
+.LP
+Christian Ruppert <idl0r@gentoo.org>
+.SH "BUGS"
+Please report any bugs to http://bugs.gentoo.org