public inbox for gentoo-portage-dev@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Michał Górny" <mgorny@gentoo.org>
To: gentoo-portage-dev@lists.gentoo.org
Cc: "Michał Górny" <mgorny@gentoo.org>
Subject: [gentoo-portage-dev] [PATCH v3] Install Portage using setup.py
Date: Fri,  5 Sep 2014 08:58:42 +0200	[thread overview]
Message-ID: <1409900322-1962-1-git-send-email-mgorny@gentoo.org> (raw)
In-Reply-To: <1408825856-12510-1-git-send-email-mgorny@gentoo.org>

Changes in v2:
- 'sdist' support

Changes in v3:
- version substituted properly in docs & mans
- cleaner handling of install_data
---
 .gitignore           |   1 +
 MANIFEST.in          |  18 ++
 Makefile             | 215 -----------------
 doc/Makefile         |  11 -
 doc/fragment/date    |   0
 doc/fragment/version |   1 -
 mkrelease.sh         | 141 -----------
 pym/portage/const.py |   4 +-
 setup.py             | 657 +++++++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 678 insertions(+), 370 deletions(-)
 create mode 100644 MANIFEST.in
 delete mode 100644 Makefile
 delete mode 100644 doc/Makefile
 delete mode 100644 doc/fragment/date
 delete mode 100644 doc/fragment/version
 delete mode 100755 mkrelease.sh
 create mode 100755 setup.py

diff --git a/.gitignore b/.gitignore
index 074bb86..c2dd534 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
 *.py[co]
 __pycache__/
 *.class
+/build
 /tags
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..d65c874
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,18 @@
+# docs
+include DEVELOPING
+include LICENSE
+include TEST-NOTES
+
+# docbook sources
+include doc/custom.xsl
+recursive-include doc *.docbook
+
+# extra conf files used in ebuild
+include cnf/make.conf.example.*
+
+# extra files for tests
+include .portage_not_installed
+include cnf/metadata.dtd
+
+# extra scripts
+include misc/*
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 9eb6e66..0000000
--- a/Makefile
+++ /dev/null
@@ -1,215 +0,0 @@
-SHELL = /bin/sh
-PN ?= portage
-PF ?= portage
-HOMEPAGE ?= http://www.gentoo.org/proj/en/portage/index.xml
-PWD ?= $(shell pwd)
-S ?= $(PWD)
-WORKDIR ?= $(PWD)
-DESTDIR = $(PWD)/image/
-srcdir = $(S)
-prefix = /usr
-sysconfdir = /etc
-exec_prefix = $(prefix)
-bindir = $(exec_prefix)/bin
-sbindir = $(exec_prefix)/sbin
-libdir = $(exec_prefix)/lib
-datarootdir = $(prefix)/share
-datadir = $(datarootdir)
-mandir = $(datarootdir)/man
-docdir = $(datarootdir)/doc/$(PF)
-htmldir = $(docdir)/html
-portage_datadir = $(datarootdir)/$(PN)
-portage_confdir = $(portage_datadir)/config
-portage_setsdir = $(portage_confdir)/sets
-portage_base = $(libdir)/$(PN)
-EPYDOC_OPTS = -qqqqq --no-frames --show-imports
-INSMODE = 0644
-EXEMODE = 0755
-DIRMODE = 0755
-SYSCONFDIR_FILES = etc-update.conf dispatch-conf.conf
-PORTAGE_CONFDIR_FILES = make.conf.example make.globals repos.conf
-LOGROTATE_FILES = elog-save-summary
-BINDIR_FILES = ebuild egencache emerge emerge-webrsync \
-	emirrordist portageq quickpkg repoman
-SBINDIR_FILES = archive-conf dispatch-conf emaint \
-	env-update etc-update fixpackages regenworld
-DOCS = ChangeLog NEWS RELEASE-NOTES
-LINGUAS ?= $(shell cd "$(srcdir)/man" && find -mindepth 1 -type d)
-
-ifdef PYTHONPATH
-	PYTHONPATH := $(srcdir)/pym:$(PYTHONPATH)
-else
-	PYTHONPATH := $(srcdir)/pym
-endif
-
-all: docbook epydoc
-
-docbook:
-	set -e; \
-	touch "$(srcdir)/doc/fragment/date"; \
-	$(MAKE) -C "$(srcdir)/doc" xhtml xhtml-nochunks
-
-epydoc:
-	set -e; \
-	env PYTHONPATH="$(PYTHONPATH)" epydoc \
-		-o "$(WORKDIR)/epydoc" \
-		--name $(PN) \
-		--url "$(HOMEPAGE)" \
-		$(EPYDOC_OPTS) \
-		$$(cd "$(srcdir)" && find pym -name '*.py' | sed \
-		-e s:/__init__.py$$:: \
-		-e s:\.py$$:: \
-		-e s:^pym/:: \
-		-e s:/:.:g \
-		| sort); \
-	rm -f "$(WORKDIR)/epydoc/api-objects.txt"; \
-
-test:
-	set -e; \
-	"$(srcdir)/pym/portage/tests/runTests.py"; \
-
-install:
-	set -e; \
-	cd "$(srcdir)/cnf"; \
-	install -d -m$(DIRMODE) "$(DESTDIR)$(sysconfdir)"; \
-	install -m$(INSMODE) $(SYSCONFDIR_FILES) "$(DESTDIR)$(sysconfdir)"; \
-	\
-	install -d -m$(DIRMODE) "$(DESTDIR)$(portage_confdir)"; \
-	cd "$(srcdir)/cnf"; \
-	install -m$(INSMODE) $(PORTAGE_CONFDIR_FILES) \
-		"$(DESTDIR)$(portage_confdir)"; \
-	install -d -m$(DIRMODE) "$(DESTDIR)$(portage_setsdir)"; \
-	cd "$(S)/cnf/sets"; \
-	install -m$(INSMODE) *.conf "$(DESTDIR)$(portage_setsdir)"; \
-	\
-	install -d -m$(DIRMODE) "$(DESTDIR)$(sysconfdir)/logrotate.d"; \
-	cd "$(srcdir)/cnf/logrotate.d"; \
-	install -m$(INSMODE) $(LOGROTATE_FILES) \
-		"$(DESTDIR)$(sysconfdir)/logrotate.d"; \
-	\
-	for x in $$(cd "$(srcdir)" && find bin -type d) ; do \
-		cd "$(srcdir)/$$x"; \
-		install -d -m$(DIRMODE) "$(DESTDIR)$(portage_base)/$$x"; \
-		files=$$(find . -mindepth 1 -maxdepth 1 -type f ! -type l); \
-		if [ -n "$$files" ] ; then \
-			install -m$(EXEMODE) $$files \
-				"$(DESTDIR)$(portage_base)/$$x"; \
-		fi; \
-		symlinks=$$(find . -mindepth 1 -maxdepth 1 -type l); \
-		if [ -n "$$symlinks" ] ; then \
-			cp -P $$symlinks "$(DESTDIR)$(portage_base)/$$x"; \
-		fi; \
-	done; \
-	\
-	for x in $$(cd "$(srcdir)" && find pym/* -type d \
-		! -path "pym/portage/tests*") ; do \
-		cd "$(srcdir)/$$x"; \
-		files=$$(echo *.py); \
-		if [ -z "$$files" ] || [ "$$files" = "*.py" ]; then \
-			# __pycache__ directories contain no py files \
-			continue; \
-		fi; \
-		install -d -m$(DIRMODE) "$(DESTDIR)$(portage_base)/$$x"; \
-		install -m$(INSMODE) $$files "$(DESTDIR)$(portage_base)/$$x"; \
-	done; \
-	\
-	install -d -m$(DIRMODE) "$(DESTDIR)$(bindir)"; \
-	relative_path=".."; \
-	x=$(bindir) ; \
-	y="$(portage_base)"; \
-	if [ "$${x#$(prefix)}" != "$$x" ] && \
-		[ "$${y#$(prefix)}" != "$$y" ]; then \
-		x=$${x#$(prefix)}; \
-		y=$${y#$(prefix)}; \
-	fi; \
-	x=$${x%/*}; \
-	while [ -n "$$x" ] ; do \
-		relative_path=$${relative_path}/..; \
-		x=$${x%/*}; \
-	done; \
-	relative_path=$$relative_path$$y; \
-	for x in $(BINDIR_FILES) ; do \
-		ln -sf "$$relative_path/bin/$$x" \
-			"$(DESTDIR)$(bindir)/$$x"; \
-	done; \
-	\
-	install -d -m$(DIRMODE) "$(DESTDIR)$(sbindir)"; \
-	relative_path=".."; \
-	x=$(sbindir) ; \
-	y="$(portage_base)"; \
-	if [ "$${x#$(prefix)}" != "$$x" ] && \
-		[ "$${y#$(prefix)}" != "$$y" ]; then \
-		x=$${x#$(prefix)}; \
-		y=$${y#$(prefix)}; \
-	fi; \
-	x=$${x%/*}; \
-	while [ -n "$$x" ] ; do \
-		relative_path=$${relative_path}/..; \
-		x=$${x%/*}; \
-	done; \
-	relative_path=$$relative_path$$y; \
-	for x in $(SBINDIR_FILES) ; do \
-		ln -sf "$$relative_path/bin/$$x" \
-			"$(DESTDIR)$(sbindir)/$$x"; \
-	done; \
-	\
-	ln -sf "$$relative_path/bin/env-update" \
-		"$(DESTDIR)$(sbindir)/update-env"; \
-	ln -sf "$$relative_path/bin/etc-update" \
-		"$(DESTDIR)$(sbindir)/update-etc"; \
-	\
-	# We install some minimal tests for use as a preinst sanity check. \
-	# These tests must be able to run without a full source tree and \
-	# without relying on a previous portage instance being installed. \
-	install -d -m$(DIRMODE) \
-		"$(DESTDIR)$(portage_base)/pym/portage/tests"; \
-	install -m$(EXEMODE) "$(srcdir)/pym/portage/tests/runTests" \
-		"$(DESTDIR)$(portage_base)/pym/portage/tests"; \
-	cd "$(srcdir)/pym/portage/tests"; \
-	install -m$(INSMODE) *.py \
-		"$(DESTDIR)$(portage_base)/pym/portage/tests"; \
-	install -d -m$(DIRMODE) \
-		"$(DESTDIR)$(portage_base)/pym/portage/tests/lint"; \
-	cd "$(srcdir)/pym/portage/tests/lint"; \
-	install -m$(INSMODE) *.py __test__ \
-		"$(DESTDIR)$(portage_base)/pym/portage/tests/lint"; \
-	\
-	install -d -m$(DIRMODE) "$(DESTDIR)$(docdir)"; \
-	cd "$(srcdir)"; \
-	install -m $(INSMODE) $(DOCS) "$(DESTDIR)$(docdir)"; \
-	\
-	for x in "" $(LINGUAS); do \
-		for y in 1 5 ; do \
-			if [ -d "$(srcdir)/man/$$x" ]; then \
-				cd "$(srcdir)/man/$$x"; \
-				files=$$(echo *.$$y); \
-				if [ -z "$$files" ] || [ "$$files" = "*.$$y" ]; then \
-					continue; \
-				fi; \
-				install -d -m$(DIRMODE) "$(DESTDIR)$(mandir)/$$x/man$$y"; \
-				install -m$(INSMODE) *.$$y "$(DESTDIR)$(mandir)/$$x/man$$y"; \
-			fi; \
-		done; \
-	done; \
-	\
-	if [ -f "$(srcdir)/doc/portage.html" ] ; then \
-		install -d -m$(DIRMODE) "$(DESTDIR)$(htmldir)"; \
-		cd "$(srcdir)/doc"; \
-		install -m$(INSMODE) *.html "$(DESTDIR)$(htmldir)"; \
-	fi; \
-	\
-	if [ -d "$(WORKDIR)/epydoc" ] ; then \
-		install -d -m$(DIRMODE) "$(DESTDIR)$(htmldir)"; \
-		cp -pPR "$(WORKDIR)/epydoc" \
-			"$(DESTDIR)$(htmldir)/api"; \
-		cd "$(DESTDIR)$(htmldir)/api"; \
-		find . -type d | xargs chmod $(DIRMODE); \
-		find . -type f | xargs chmod $(INSMODE); \
-	fi; \
-
-clean:
-	set -e; \
-	$(MAKE) -C "$(srcdir)/doc" clean; \
-	rm -rf "$(WORKDIR)/epydoc"; \
-
-.PHONY: all clean docbook epydoc install test
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644
index 261a0b4..0000000
--- a/doc/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all: xhtml xhtml-nochunks
-
-XMLTO_FLAGS = -m custom.xsl
-man pdf txt xhtml xhtml-nochunks:
-	xmlto $@ $(XMLTO_FLAGS) portage.docbook
-
-clean distclean:
-	rm -f *.1 *.html portage.txt
-
-.PHONY: all clean distclean \
-        man pdf txt xhtml xhtml-nochunks
diff --git a/doc/fragment/date b/doc/fragment/date
deleted file mode 100644
index e69de29..0000000
diff --git a/doc/fragment/version b/doc/fragment/version
deleted file mode 100644
index f85674c..0000000
--- a/doc/fragment/version
+++ /dev/null
@@ -1 +0,0 @@
-<releaseinfo>VERSION</releaseinfo>
diff --git a/mkrelease.sh b/mkrelease.sh
deleted file mode 100755
index f9f7564..0000000
--- a/mkrelease.sh
+++ /dev/null
@@ -1,141 +0,0 @@
-#!/bin/bash
-# Copyright 2008-2014 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-
-RELEASE_BUILDDIR=${RELEASE_BUILDDIR:-/var/tmp/portage-release}
-SOURCE_DIR=${RELEASE_BUILDDIR}/checkout
-BRANCH=${BRANCH:-master}
-USE_TAG=false
-CHANGELOG_REVISION=
-UPLOAD_LOCATION=
-RUNTESTS=false
-USER=
-
-usage() {
-	echo "Usage: ${0##*/} [--changelog-rev <tree-ish>] [-t|--tag] [-u|--upload <location>]  [--user <username>] [--runtests] <version>"
-	exit ${1:-0}
-}
-
-die() {
-	printf 'error: %s\n' "$*"
-	usage 1
-}
-
-ARGS=$(getopt -o htu: --long help,changelog-rev:,runtests,tag,upload:,user: \
-	-n "${0##*/}" -- "$@")
-[ $? != 0 ] && die "initialization error"
-
-eval set -- "${ARGS}"
-
-while true; do
-	case $1 in
-		--changelog-rev)
-			CHANGELOG_REVISION=$2
-			shift 2
-			;;
-		-t|--tag)
-			USE_TAG=true
-			shift
-			;;
-		-u|--upload)
-			UPLOAD_LOCATION=$2
-			shift 2
-			;;
-		--user)
-			USER=$2"@"
-			shift 2
-			;;
-		-h|--help)
-			usage
-			;;
-		--runtests)
-			RUNTESTS=true
-			shift
-			;;
-		--)
-			shift
-			break
-			;;
-		*)
-			die "unknown option: $1"
-			;;
-	esac
-done
-
-[ $# != 1 ] && die "Need version argument"
-[[ -n ${1/[0-9]*} ]] && die "Invalid version argument"
-
-VERSION=$1
-RELEASE=portage-${VERSION}
-RELEASE_DIR=${RELEASE_BUILDDIR}/${RELEASE}
-RELEASE_TARBALL="${RELEASE_BUILDDIR}/${RELEASE}.tar.bz2"
-TREE_ISH=${BRANCH}
-if [[ ${USE_TAG} == "true" ]] ; then
-	TREE_ISH="v${VERSION}"
-fi
-
-echo ">>> Cleaning working directories ${RELEASE_DIR} ${SOURCE_DIR}"
-rm -rf "${RELEASE_DIR}" "${SOURCE_DIR}" || die "directory cleanup failed"
-mkdir -p "${RELEASE_DIR}" || die "directory creation failed"
-mkdir -p "${SOURCE_DIR}" || die "mkdir failed"
-
-echo ">>> Starting GIT archive"
-git archive --format=tar ${TREE_ISH} | \
-	tar -xf - -C "${SOURCE_DIR}" || die "git archive failed"
-
-echo ">>> Building release tree"
-cp -a "${SOURCE_DIR}/"{bin,cnf,doc,man,misc,pym} "${RELEASE_DIR}/" || die "directory copy failed"
-cp "${SOURCE_DIR}/"{.portage_not_installed,DEVELOPING,LICENSE,Makefile,NEWS,README,RELEASE-NOTES,TEST-NOTES} \
-	"${RELEASE_DIR}/" || die "file copy failed"
-
-if [[ ${RUNTESTS} == "true" ]] ; then
-	pushd "${SOURCE_DIR}" >/dev/null
-	./runtests.sh --python-versions=supported || die "tests failed"
-	popd >/dev/null
-fi
-
-rm -rf "${SOURCE_DIR}" || die "directory cleanup failed"
-
-echo ">>> Setting portage.VERSION"
-sed -e "s/^VERSION = .*/VERSION = \"${VERSION}\"/" \
-	-i "${RELEASE_DIR}/pym/portage/__init__.py" || \
-	die "Failed to patch portage.VERSION"
-
-echo ">>> Creating Changelog"
-git_log_opts=""
-if [[ -n ${CHANGELOG_REVISION} ]] ; then
-	git_log_opts+=" ${CHANGELOG_REVISION}^..${TREE_ISH}"
-else
-	git_log_opts+=" ${TREE_ISH}"
-fi
-skip_next=false
-git log ${git_log_opts} | fmt -w 80 -p "    " | while read -r ; do
-	if [[ ${skip_next} == "true" ]] ; then
-		skip_next=false
-	elif [[ ${REPLY} == "    svn path="* ]] ; then
-		skip_next=true
-	else
-		echo "${REPLY}"
-	fi
-done > "${RELEASE_DIR}/ChangeLog" || die "ChangeLog creation failed"
-
-cd "${RELEASE_BUILDDIR}"
-
-echo ">>> Creating release tarball ${RELEASE_TARBALL}"
-tar --owner portage --group portage -cjf "${RELEASE_TARBALL}" "${RELEASE}" || \
-	die "tarball creation failed"
-
-DISTDIR=$(portageq distdir)
-if [[ -n ${DISTDIR} && -d ${DISTDIR} && -w ${DISTDIR} ]] ; then
-	echo ">>> Copying release tarball into ${DISTDIR}"
-	cp "${RELEASE_TARBALL}" "${DISTDIR}"/ || echo "!!! tarball copy failed"
-fi
-
-if [[ -n ${UPLOAD_LOCATION} ]] ; then
-	echo ">>> Uploading ${RELEASE_TARBALL} to ${USER}dev.gentoo.org:${UPLOAD_LOCATION}"
-	scp "${RELEASE_TARBALL}" "${USER}dev.gentoo.org:${UPLOAD_LOCATION}" || die "upload failed"
-else
-	du -h "${RELEASE_TARBALL}"
-fi
-
-exit 0
diff --git a/pym/portage/const.py b/pym/portage/const.py
index f518b47..acb90f9 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -60,8 +60,8 @@ GLOBAL_CONFIG_PATH       = "/usr/share/portage/config"
 # these variables are not used with target_root or config_root
 # NOTE: Use realpath(__file__) so that python module symlinks in site-packages
 # are followed back to the real location of the whole portage installation.
-PORTAGE_BASE_PATH        = os.path.join(os.sep, os.sep.join(os.path.realpath(
-                               __file__.rstrip("co")).split(os.sep)[:-3]))
+# NOTE: Please keep PORTAGE_BASE_PATH in one line to help substitutions.
+PORTAGE_BASE_PATH        = os.path.join(os.sep, os.sep.join(os.path.realpath(__file__.rstrip("co")).split(os.sep)[:-3]))
 PORTAGE_BIN_PATH         = PORTAGE_BASE_PATH + "/bin"
 PORTAGE_PYM_PATH         = os.path.realpath(os.path.join(__file__, '../..'))
 LOCALE_DATA_PATH         = PORTAGE_BASE_PATH + "/locale"  # FIXME: not used
diff --git a/setup.py b/setup.py
new file mode 100755
index 0000000..e5c3631
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,657 @@
+#!/usr/bin/env python
+# Copyright 1998-2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from distutils.core import setup, Command
+from distutils.command.build import build
+from distutils.command.build_scripts import build_scripts
+from distutils.command.clean import clean
+from distutils.command.install import install
+from distutils.command.install_data import install_data
+from distutils.command.install_lib import install_lib
+from distutils.command.install_scripts import install_scripts
+from distutils.command.sdist import sdist
+from distutils.dep_util import newer
+from distutils.dir_util import mkpath, remove_tree
+from distutils.util import change_root, subst_vars
+
+import codecs, collections, errno, glob, os, os.path, re, subprocess, sys
+
+# TODO:
+# - smarter rebuilds of docs w/ 'install_docbook' and 'install_epydoc'.
+
+x_scripts = {
+	'bin': [
+		'bin/ebuild', 'bin/egencache', 'bin/emerge', 'bin/emerge-webrsync',
+		'bin/emirrordist', 'bin/portageq', 'bin/quickpkg', 'bin/repoman'
+	],
+	'sbin': [
+		'bin/archive-conf', 'bin/dispatch-conf', 'bin/emaint', 'bin/env-update',
+		'bin/etc-update', 'bin/fixpackages', 'bin/regenworld'
+	],
+}
+
+
+class x_build(build):
+	""" Build command with extra build_man call. """
+
+	def run(self):
+		build.run(self)
+		self.run_command('build_man')
+
+
+class build_man(Command):
+	""" Perform substitutions in manpages. """
+
+	user_options = [
+	]
+
+	def initialize_options(self):
+		self.build_base = None
+
+	def finalize_options(self):
+		self.set_undefined_options('build',
+			('build_base', 'build_base'))
+
+	def run(self):
+		for d, files in self.distribution.data_files:
+			if not d.startswith('$mandir/'):
+				continue
+
+			for source in files:
+				target = os.path.join(self.build_base, source)
+				mkpath(os.path.dirname(target))
+
+				if not newer(source, target) and not newer(__file__, target):
+					continue
+
+				print('copying and updating %s -> %s' % (
+					source, target))
+
+				with codecs.open(source, 'r', 'utf8') as f:
+					data = f.readlines()
+				data[0] = data[0].replace('VERSION',
+						self.distribution.get_version())
+				with codecs.open(target, 'w', 'utf8') as f:
+					f.writelines(data)
+
+
+class docbook(Command):
+	""" Build docs using docbook. """
+
+	user_options = [
+		('doc-formats=', None, 'Documentation formats to build (all xmlto formats for docbook are allowed, comma-separated'),
+	]
+
+	def initialize_options(self):
+		self.doc_formats = 'xhtml,xhtml-nochunks'
+
+	def finalize_options(self):
+		self.doc_formats = self.doc_formats.replace(',', ' ').split()
+
+	def run(self):
+		if not os.path.isdir('doc/fragment'):
+			mkpath('doc/fragment')
+
+		with open('doc/fragment/date', 'w'):
+			pass
+		with open('doc/fragment/version', 'w') as f:
+			f.write('<releaseinfo>%s</releaseinfo>' % self.distribution.get_version())
+
+		for f in self.doc_formats:
+			print('Building docs in %s format...' % f)
+			subprocess.check_call(['xmlto', '-o', 'doc',
+				'-m', 'doc/custom.xsl', f, 'doc/portage.docbook'])
+
+
+class epydoc(Command):
+	""" Build API docs using epydoc. """
+
+	user_options = [
+	]
+
+	def initialize_options(self):
+		self.build_lib = None
+
+	def finalize_options(self):
+		self.set_undefined_options('build_py', ('build_lib', 'build_lib'))
+
+	def run(self):
+		self.run_command('build_py')
+
+		print('Building API documentation...')
+
+		process_env = os.environ.copy()
+		pythonpath = self.build_lib
+		try:
+			pythonpath += ':' + process_env['PYTHONPATH']
+		except KeyError:
+			pass
+		process_env['PYTHONPATH'] = pythonpath
+
+		subprocess.check_call(['epydoc', '-o', 'epydoc',
+			'--name', self.distribution.get_name(),
+			'--url', self.distribution.get_url(),
+			'-qq', '--no-frames', '--show-imports',
+			'--exclude', 'portage.tests',
+			'_emerge', 'portage', 'repoman'],
+			env = process_env)
+		os.remove('epydoc/api-objects.txt')
+
+
+class install_docbook(install_data):
+	""" install_data for docbook docs """
+
+	user_options = install_data.user_options
+
+	def initialize_options(self):
+		install_data.initialize_options(self)
+		self.htmldir = None
+
+	def finalize_options(self):
+		self.set_undefined_options('install', ('htmldir', 'htmldir'))
+		install_data.finalize_options(self)
+
+	def run(self):
+		if not os.path.exists('doc/portage.html'):
+			self.run_command('docbook')
+		self.data_files = [
+			(self.htmldir, glob.glob('doc/*.html')),
+		]
+		install_data.run(self)
+
+
+class install_epydoc(install_data):
+	""" install_data for epydoc docs """
+
+	user_options = install_data.user_options
+
+	def initialize_options(self):
+		install_data.initialize_options(self)
+		self.htmldir = None
+
+	def finalize_options(self):
+		self.set_undefined_options('install', ('htmldir', 'htmldir'))
+		install_data.finalize_options(self)
+
+	def run(self):
+		if not os.path.exists('epydoc/index.html'):
+			self.run_command('epydoc')
+		self.data_files = [
+			(os.path.join(self.htmldir, 'api'), glob.glob('epydoc/*')),
+		]
+		install_data.run(self)
+
+
+class x_build_scripts_custom(build_scripts):
+	def finalize_options(self):
+		build_scripts.finalize_options(self)
+		if 'dir_name' in dir(self):
+			self.build_dir = os.path.join(self.build_dir, self.dir_name)
+			if self.dir_name in x_scripts:
+				self.scripts = x_scripts[self.dir_name]
+			else:
+				self.scripts = set(self.scripts)
+				for other_files in x_scripts.values():
+					self.scripts.difference_update(other_files)
+
+	def run(self):
+		# group scripts by subdirectory
+		split_scripts = collections.defaultdict(list)
+		for f in self.scripts:
+			dir_name = os.path.dirname(f[len('bin/'):])
+			split_scripts[dir_name].append(f)
+
+		base_dir = self.build_dir
+		base_scripts = self.scripts
+		for d, files in split_scripts.items():
+			self.build_dir = os.path.join(base_dir, d)
+			self.scripts = files
+			self.copy_scripts()
+
+		# restore previous values
+		self.build_dir = base_dir
+		self.scripts = base_scripts
+
+
+class x_build_scripts_bin(x_build_scripts_custom):
+	dir_name = 'bin'
+
+
+class x_build_scripts_sbin(x_build_scripts_custom):
+	dir_name = 'sbin'
+
+
+class x_build_scripts_portagebin(x_build_scripts_custom):
+	dir_name = 'portage'
+
+
+class x_build_scripts(build_scripts):
+	def initialize_option(self):
+		build_scripts.initialize_options(self)
+
+	def finalize_options(self):
+		build_scripts.finalize_options(self)
+
+	def run(self):
+		self.run_command('build_scripts_bin')
+		self.run_command('build_scripts_portagebin')
+		self.run_command('build_scripts_sbin')
+
+
+class x_clean(clean):
+	""" clean extended for doc & post-test cleaning """
+
+	def clean_docs(self):
+		def get_doc_outfiles():
+			for dirpath, dirnames, filenames in os.walk('doc'):
+				for f in filenames:
+					if f.endswith('.docbook') or f == 'custom.xsl':
+						pass
+					else:
+						yield os.path.join(dirpath, f)
+
+				# do not recurse
+				break
+
+
+		for f in get_doc_outfiles():
+			print('removing %s' % repr(f))
+			os.remove(f)
+
+		if os.path.isdir('doc/fragment'):
+			remove_tree('doc/fragment')
+
+		if os.path.isdir('epydoc'):
+			remove_tree('epydoc')
+
+	def clean_tests(self):
+		# do not remove incorrect dirs accidentally
+		top_dir = os.path.normpath(os.path.join(self.build_lib, '..'))
+		cprefix = os.path.commonprefix((self.build_base, top_dir))
+		if cprefix != self.build_base:
+			return
+
+		bin_dir = os.path.join(top_dir, 'bin')
+		if os.path.exists(bin_dir):
+			remove_tree(bin_dir)
+
+		conf_dir = os.path.join(top_dir, 'cnf')
+		if os.path.islink(conf_dir):
+			print('removing %s symlink' % repr(conf_dir))
+			os.unlink(conf_dir)
+
+		pni_file = os.path.join(top_dir, '.portage_not_installed')
+		if os.path.exists(pni_file):
+			print('removing %s' % repr(pni_file))
+			os.unlink(pni_file)
+
+	def clean_man(self):
+		man_dir = os.path.join(self.build_base, 'man')
+		if os.path.exists(man_dir):
+			remove_tree(man_dir)
+
+	def run(self):
+		if self.all:
+			self.clean_tests()
+			self.clean_docs()
+			self.clean_man()
+
+		clean.run(self)
+
+
+class x_install(install):
+	""" install command with extra Portage paths """
+
+	user_options = install.user_options + [
+		# note: $prefix and $exec_prefix are reserved for Python install
+		('system-prefix=', None, "Prefix for architecture-independent data"),
+		('system-exec-prefix=', None, "Prefix for architecture-specific data"),
+
+		('bindir=', None, "Install directory for main executables"),
+		('datarootdir=', None, "Data install root directory"),
+		('docdir=', None, "Documentation install directory"),
+		('htmldir=', None, "HTML documentation install directory"),
+		('mandir=', None, "Manpage root install directory"),
+		('portage-base=', 'b', "Portage install base"),
+		('portage-bindir=', None, "Install directory for Portage internal-use executables"),
+		('portage-datadir=', None, 'Install directory for data files'),
+		('sbindir=', None, "Install directory for superuser-intended executables"),
+		('sysconfdir=', None, 'System configuration path'),
+	]
+
+	# note: the order is important for proper substitution
+	paths = [
+		('system_prefix', '/usr'),
+		('system_exec_prefix', '$system_prefix'),
+
+		('bindir', '$system_exec_prefix/bin'),
+		('sbindir', '$system_exec_prefix/sbin'),
+		('sysconfdir', '/etc'),
+
+		('datarootdir', '$system_prefix/share'),
+		('docdir', '$datarootdir/doc/$package-$version'),
+		('htmldir', '$docdir/html'),
+		('mandir', '$datarootdir/man'),
+
+		('portage_base', '$system_exec_prefix/lib/portage'),
+		('portage_bindir', '$portage_base/bin'),
+		('portage_datadir', '$datarootdir/portage'),
+
+		# not customized at the moment
+		('logrotatedir', '$sysconfdir/logrotate'),
+		('portage_confdir', '$portage_datadir/config'),
+		('portage_setsdir', '$portage_confdir/sets'),
+	]
+
+	def initialize_options(self):
+		install.initialize_options(self)
+
+		for key, default in self.paths:
+			setattr(self, key, default)
+		self.subst_paths = {}
+
+	def finalize_options(self):
+		install.finalize_options(self)
+
+		# substitute variables
+		new_paths = {
+			'package': self.distribution.get_name(),
+			'version': self.distribution.get_version(),
+		}
+		for key, default in self.paths:
+			new_paths[key] = subst_vars(getattr(self, key), new_paths)
+			setattr(self, key, new_paths[key])
+		self.subst_paths = new_paths
+
+
+class x_install_data(install_data):
+	""" install_data with customized path support """
+
+	user_options = install_data.user_options
+
+	def initialize_options(self):
+		install_data.initialize_options(self)
+		self.build_base = None
+		self.paths = None
+
+	def finalize_options(self):
+		install_data.finalize_options(self)
+		self.set_undefined_options('build',
+			('build_base', 'build_base'))
+		self.set_undefined_options('install',
+			('subst_paths', 'paths'))
+
+	def run(self):
+		self.run_command('build_man')
+
+		def process_data_files(df):
+			for d, files in df:
+				# substitute man sources
+				if d.startswith('$mandir/'):
+					files = [os.path.join(self.build_base, v) for v in files]
+
+				# substitute variables in path
+				d = subst_vars(d, self.paths)
+				yield (d, files)
+
+		old_data_files = self.data_files
+		self.data_files = process_data_files(self.data_files)
+
+		install_data.run(self)
+		self.data_files = old_data_files
+
+
+class x_install_lib(install_lib):
+	""" install_lib command with Portage path substitution """
+
+	user_options = install_lib.user_options
+
+	def initialize_options(self):
+		install_lib.initialize_options(self)
+		self.portage_base = None
+		self.portage_bindir = None
+		self.portage_confdir = None
+
+	def finalize_options(self):
+		install_lib.finalize_options(self)
+		self.set_undefined_options('install',
+			('portage_base', 'portage_base'),
+			('portage_bindir', 'portage_bindir'),
+			('portage_confdir', 'portage_confdir'))
+
+	def install(self):
+		ret = install_lib.install(self)
+
+		def rewrite_file(path, val_dict):
+			path = os.path.join(self.install_dir, path)
+			print('Rewriting %s' % path)
+			with codecs.open(path, 'r', 'utf-8') as f:
+				data = f.read()
+
+			for varname, val in val_dict.items():
+				regexp = r'(?m)^(%s\s*=).*$' % varname
+				repl = r'\1 %s' % repr(val)
+
+				data = re.sub(regexp, repl, data)
+
+			with codecs.open(path, 'w', 'utf-8') as f:
+				f.write(data)
+
+		rewrite_file('portage/__init__.py', {
+			'VERSION': self.distribution.get_version(),
+		})
+		rewrite_file('portage/const.py', {
+			'PORTAGE_BASE_PATH': self.portage_base,
+			'PORTAGE_BIN_PATH': self.portage_bindir,
+			'PORTAGE_CONFIG_PATH': self.portage_confdir,
+		})
+
+		return ret
+
+
+class x_install_scripts_custom(install_scripts):
+	def initialize_options(self):
+		install_scripts.initialize_options(self)
+		self.root = None
+
+	def finalize_options(self):
+		self.set_undefined_options('install',
+			('root', 'root'),
+			(self.var_name, 'install_dir'))
+		install_scripts.finalize_options(self)
+		self.build_dir = os.path.join(self.build_dir, self.dir_name)
+
+		# prepend root
+		if self.root is not None:
+			self.install_dir = change_root(self.root, self.install_dir)
+
+
+class x_install_scripts_bin(x_install_scripts_custom):
+	dir_name = 'bin'
+	var_name = 'bindir'
+
+
+class x_install_scripts_sbin(x_install_scripts_custom):
+	dir_name = 'sbin'
+	var_name = 'sbindir'
+
+
+class x_install_scripts_portagebin(x_install_scripts_custom):
+	dir_name = 'portage'
+	var_name = 'portage_bindir'
+
+
+class x_install_scripts(install_scripts):
+	def initialize_option(self):
+		pass
+
+	def finalize_options(self):
+		pass
+
+	def run(self):
+		self.run_command('install_scripts_bin')
+		self.run_command('install_scripts_portagebin')
+		self.run_command('install_scripts_sbin')
+
+
+class x_sdist(sdist):
+	""" sdist defaulting to .tar.bz2 format """
+
+	def finalize_options(self):
+		if self.formats is None:
+			self.formats = ['bztar']
+
+		sdist.finalize_options(self)
+
+
+class build_tests(x_build_scripts_custom):
+	""" Prepare build dir for running tests. """
+
+	def initialize_options(self):
+		x_build_scripts_custom.initialize_options(self)
+		self.build_base = None
+		self.build_lib = None
+
+	def finalize_options(self):
+		x_build_scripts_custom.finalize_options(self)
+		self.set_undefined_options('build',
+			('build_base', 'build_base'),
+			('build_lib', 'build_lib'))
+
+		# since we will be writing to $build_lib/.., it is important
+		# that we do not leave $build_base
+		self.top_dir = os.path.normpath(os.path.join(self.build_lib, '..'))
+		cprefix = os.path.commonprefix((self.build_base, self.top_dir))
+		if cprefix != self.build_base:
+			raise SystemError('build_lib must be a subdirectory of build_base')
+
+		self.build_dir = os.path.join(self.top_dir, 'bin')
+
+	def run(self):
+		self.run_command('build_py')
+
+		# install all scripts $build_lib/../bin
+		# (we can't do a symlink since we want shebangs corrected)
+		x_build_scripts_custom.run(self)
+
+		# symlink 'cnf' directory
+		conf_dir = os.path.join(self.top_dir, 'cnf')
+		if os.path.exists(conf_dir):
+			if not os.path.islink(conf_dir):
+				raise SystemError('%s exists and is not a symlink (collision)'
+					% repr(conf_dir))
+			os.unlink(conf_dir)
+		conf_src = os.path.relpath('cnf', self.top_dir)
+		print('Symlinking %s -> %s' % (conf_dir, conf_src))
+		os.symlink(conf_src, conf_dir)
+
+		# create $build_lib/../.portage_not_installed
+		# to enable proper paths in tests
+		with open(os.path.join(self.top_dir, '.portage_not_installed'), 'w') as f:
+			pass
+
+
+class test(Command):
+	""" run tests """
+
+	user_options = []
+
+	def initialize_options(self):
+		self.build_lib = None
+
+	def finalize_options(self):
+		self.set_undefined_options('build',
+			('build_lib', 'build_lib'))
+
+	def run(self):
+		self.run_command('build_tests')
+		subprocess.check_call([
+			sys.executable, '-bWd',
+			os.path.join(self.build_lib, 'portage/tests/runTests.py')
+		])
+
+
+def find_packages():
+	for dirpath, dirnames, filenames in os.walk('pym'):
+		if '__init__.py' in filenames:
+			yield os.path.relpath(dirpath, 'pym')
+
+
+def find_scripts():
+	for dirpath, dirnames, filenames in os.walk('bin'):
+		for f in filenames:
+			yield os.path.join(dirpath, f)
+
+
+def get_manpages():
+	linguas = os.environ.get('LINGUAS')
+	if linguas is not None:
+		linguas = linguas.split()
+
+	for dirpath, dirnames, filenames in os.walk('man'):
+		groups = collections.defaultdict(list)
+		for f in filenames:
+			fn, suffix = f.rsplit('.', 1)
+			groups[suffix].append(os.path.join(dirpath, f))
+
+		topdir = dirpath[len('man/'):]
+		if not topdir or linguas is None or topdir in linguas:
+			for g, mans in groups.items():
+				yield [os.path.join('$mandir', topdir, 'man%s' % g), mans]
+
+setup(
+	name = 'portage',
+	version = '2.2.12',
+	url = 'https://wiki.gentoo.org/wiki/Project:Portage',
+	author = 'Gentoo Portage Development Team',
+	author_email = 'dev-portage@gentoo.org',
+
+	package_dir = {'': 'pym'},
+	packages = list(find_packages()),
+	# something to cheat build & install commands
+	scripts = list(find_scripts()),
+
+	data_files = list(get_manpages()) + [
+		['$sysconfdir', ['cnf/etc-update.conf', 'cnf/dispatch-conf.conf']],
+		['$logrotatedir', ['cnf/logrotate.d/elog-save-summary']],
+		['$portage_confdir', [
+			'cnf/make.conf.example', 'cnf/make.globals', 'cnf/repos.conf']],
+		['$portage_setsdir', ['cnf/sets/portage.conf']],
+		['$docdir', ['NEWS', 'RELEASE-NOTES']],
+	],
+
+	cmdclass = {
+		'build': x_build,
+		'build_man': build_man,
+		'build_scripts': x_build_scripts,
+		'build_scripts_bin': x_build_scripts_bin,
+		'build_scripts_portagebin': x_build_scripts_portagebin,
+		'build_scripts_sbin': x_build_scripts_sbin,
+		'build_tests': build_tests,
+		'clean': x_clean,
+		'docbook': docbook,
+		'epydoc': epydoc,
+		'install': x_install,
+		'install_data': x_install_data,
+		'install_docbook': install_docbook,
+		'install_epydoc': install_epydoc,
+		'install_lib': x_install_lib,
+		'install_scripts': x_install_scripts,
+		'install_scripts_bin': x_install_scripts_bin,
+		'install_scripts_portagebin': x_install_scripts_portagebin,
+		'install_scripts_sbin': x_install_scripts_sbin,
+		'sdist': x_sdist,
+		'test': test,
+	},
+
+	classifiers = [
+		'Development Status :: 5 - Production/Stable',
+		'Environment :: Console',
+		'Intended Audience :: System Administrators',
+		'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
+		'Operating System :: POSIX',
+		'Programming Language :: Python',
+		'Topic :: System :: Installation/Setup'
+	]
+)
-- 
2.1.0



  parent reply	other threads:[~2014-09-05  6:58 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-08-21 20:19 [gentoo-portage-dev] [PATCHES] setup.py install for Portage Michał Górny
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 1/6] self-update: Copy only relevant packages from PORTAGE_PYM_PATH Michał Górny
2014-08-22 21:47   ` Brian Dolbec
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 2/6] portage.const: Make PORTAGE_PYM_PATH point to the real package tree Michał Górny
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 3/6] tests: Append .py to files that need to be installed Michał Górny
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 4/6] tests: Fix running on installed copy of Portage Michał Górny
2014-08-22 22:19   ` Brian Dolbec
2014-08-22 22:52     ` Michał Górny
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 5/6] Install Python modules using setup.py Michał Górny
2014-08-22 22:30   ` Brian Dolbec
2014-08-22 22:55     ` Michał Górny
2014-08-23 20:30     ` [gentoo-portage-dev] [PATCH v2] Install Portage " Michał Górny
2014-08-23 21:56       ` Brian Dolbec
2014-08-29 17:08       ` Brian Dolbec
2014-08-29 23:30         ` Brian Dolbec
2014-09-05  6:58       ` Michał Górny [this message]
2014-08-21 20:19 ` [gentoo-portage-dev] [PATCH 6/6] travis: Use setup.py for running tests Michał Górny
2014-08-21 20:22 ` [gentoo-portage-dev] [PATCHES] setup.py install for Portage Michał Górny
2014-08-21 20:25   ` Michał Górny
2014-08-22 22:32 ` Brian Dolbec
2014-08-24 19:42 ` Brian Dolbec
2014-08-24 21:19   ` Michał Górny
2014-09-12  7:17 ` Michał Górny

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1409900322-1962-1-git-send-email-mgorny@gentoo.org \
    --to=mgorny@gentoo.org \
    --cc=gentoo-portage-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox