Gentoo Archives: gentoo-portage-dev

From: "Michał Górny" <mgorny@g.o>
To: gentoo-portage-dev@l.g.o
Cc: "Michał Górny" <mgorny@g.o>
Subject: [gentoo-portage-dev] [PATCH v3] Install Portage using setup.py
Date: Fri, 05 Sep 2014 06:58:58
Message-Id: 1409900322-1962-1-git-send-email-mgorny@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH v2] Install Portage using setup.py by "Michał Górny"
1 Changes in v2:
2 - 'sdist' support
3
4 Changes in v3:
5 - version substituted properly in docs & mans
6 - cleaner handling of install_data
7 ---
8 .gitignore | 1 +
9 MANIFEST.in | 18 ++
10 Makefile | 215 -----------------
11 doc/Makefile | 11 -
12 doc/fragment/date | 0
13 doc/fragment/version | 1 -
14 mkrelease.sh | 141 -----------
15 pym/portage/const.py | 4 +-
16 setup.py | 657 +++++++++++++++++++++++++++++++++++++++++++++++++++
17 9 files changed, 678 insertions(+), 370 deletions(-)
18 create mode 100644 MANIFEST.in
19 delete mode 100644 Makefile
20 delete mode 100644 doc/Makefile
21 delete mode 100644 doc/fragment/date
22 delete mode 100644 doc/fragment/version
23 delete mode 100755 mkrelease.sh
24 create mode 100755 setup.py
25
26 diff --git a/.gitignore b/.gitignore
27 index 074bb86..c2dd534 100644
28 --- a/.gitignore
29 +++ b/.gitignore
30 @@ -1,4 +1,5 @@
31 *.py[co]
32 __pycache__/
33 *.class
34 +/build
35 /tags
36 diff --git a/MANIFEST.in b/MANIFEST.in
37 new file mode 100644
38 index 0000000..d65c874
39 --- /dev/null
40 +++ b/MANIFEST.in
41 @@ -0,0 +1,18 @@
42 +# docs
43 +include DEVELOPING
44 +include LICENSE
45 +include TEST-NOTES
46 +
47 +# docbook sources
48 +include doc/custom.xsl
49 +recursive-include doc *.docbook
50 +
51 +# extra conf files used in ebuild
52 +include cnf/make.conf.example.*
53 +
54 +# extra files for tests
55 +include .portage_not_installed
56 +include cnf/metadata.dtd
57 +
58 +# extra scripts
59 +include misc/*
60 diff --git a/Makefile b/Makefile
61 deleted file mode 100644
62 index 9eb6e66..0000000
63 --- a/Makefile
64 +++ /dev/null
65 @@ -1,215 +0,0 @@
66 -SHELL = /bin/sh
67 -PN ?= portage
68 -PF ?= portage
69 -HOMEPAGE ?= http://www.gentoo.org/proj/en/portage/index.xml
70 -PWD ?= $(shell pwd)
71 -S ?= $(PWD)
72 -WORKDIR ?= $(PWD)
73 -DESTDIR = $(PWD)/image/
74 -srcdir = $(S)
75 -prefix = /usr
76 -sysconfdir = /etc
77 -exec_prefix = $(prefix)
78 -bindir = $(exec_prefix)/bin
79 -sbindir = $(exec_prefix)/sbin
80 -libdir = $(exec_prefix)/lib
81 -datarootdir = $(prefix)/share
82 -datadir = $(datarootdir)
83 -mandir = $(datarootdir)/man
84 -docdir = $(datarootdir)/doc/$(PF)
85 -htmldir = $(docdir)/html
86 -portage_datadir = $(datarootdir)/$(PN)
87 -portage_confdir = $(portage_datadir)/config
88 -portage_setsdir = $(portage_confdir)/sets
89 -portage_base = $(libdir)/$(PN)
90 -EPYDOC_OPTS = -qqqqq --no-frames --show-imports
91 -INSMODE = 0644
92 -EXEMODE = 0755
93 -DIRMODE = 0755
94 -SYSCONFDIR_FILES = etc-update.conf dispatch-conf.conf
95 -PORTAGE_CONFDIR_FILES = make.conf.example make.globals repos.conf
96 -LOGROTATE_FILES = elog-save-summary
97 -BINDIR_FILES = ebuild egencache emerge emerge-webrsync \
98 - emirrordist portageq quickpkg repoman
99 -SBINDIR_FILES = archive-conf dispatch-conf emaint \
100 - env-update etc-update fixpackages regenworld
101 -DOCS = ChangeLog NEWS RELEASE-NOTES
102 -LINGUAS ?= $(shell cd "$(srcdir)/man" && find -mindepth 1 -type d)
103 -
104 -ifdef PYTHONPATH
105 - PYTHONPATH := $(srcdir)/pym:$(PYTHONPATH)
106 -else
107 - PYTHONPATH := $(srcdir)/pym
108 -endif
109 -
110 -all: docbook epydoc
111 -
112 -docbook:
113 - set -e; \
114 - touch "$(srcdir)/doc/fragment/date"; \
115 - $(MAKE) -C "$(srcdir)/doc" xhtml xhtml-nochunks
116 -
117 -epydoc:
118 - set -e; \
119 - env PYTHONPATH="$(PYTHONPATH)" epydoc \
120 - -o "$(WORKDIR)/epydoc" \
121 - --name $(PN) \
122 - --url "$(HOMEPAGE)" \
123 - $(EPYDOC_OPTS) \
124 - $$(cd "$(srcdir)" && find pym -name '*.py' | sed \
125 - -e s:/__init__.py$$:: \
126 - -e s:\.py$$:: \
127 - -e s:^pym/:: \
128 - -e s:/:.:g \
129 - | sort); \
130 - rm -f "$(WORKDIR)/epydoc/api-objects.txt"; \
131 -
132 -test:
133 - set -e; \
134 - "$(srcdir)/pym/portage/tests/runTests.py"; \
135 -
136 -install:
137 - set -e; \
138 - cd "$(srcdir)/cnf"; \
139 - install -d -m$(DIRMODE) "$(DESTDIR)$(sysconfdir)"; \
140 - install -m$(INSMODE) $(SYSCONFDIR_FILES) "$(DESTDIR)$(sysconfdir)"; \
141 - \
142 - install -d -m$(DIRMODE) "$(DESTDIR)$(portage_confdir)"; \
143 - cd "$(srcdir)/cnf"; \
144 - install -m$(INSMODE) $(PORTAGE_CONFDIR_FILES) \
145 - "$(DESTDIR)$(portage_confdir)"; \
146 - install -d -m$(DIRMODE) "$(DESTDIR)$(portage_setsdir)"; \
147 - cd "$(S)/cnf/sets"; \
148 - install -m$(INSMODE) *.conf "$(DESTDIR)$(portage_setsdir)"; \
149 - \
150 - install -d -m$(DIRMODE) "$(DESTDIR)$(sysconfdir)/logrotate.d"; \
151 - cd "$(srcdir)/cnf/logrotate.d"; \
152 - install -m$(INSMODE) $(LOGROTATE_FILES) \
153 - "$(DESTDIR)$(sysconfdir)/logrotate.d"; \
154 - \
155 - for x in $$(cd "$(srcdir)" && find bin -type d) ; do \
156 - cd "$(srcdir)/$$x"; \
157 - install -d -m$(DIRMODE) "$(DESTDIR)$(portage_base)/$$x"; \
158 - files=$$(find . -mindepth 1 -maxdepth 1 -type f ! -type l); \
159 - if [ -n "$$files" ] ; then \
160 - install -m$(EXEMODE) $$files \
161 - "$(DESTDIR)$(portage_base)/$$x"; \
162 - fi; \
163 - symlinks=$$(find . -mindepth 1 -maxdepth 1 -type l); \
164 - if [ -n "$$symlinks" ] ; then \
165 - cp -P $$symlinks "$(DESTDIR)$(portage_base)/$$x"; \
166 - fi; \
167 - done; \
168 - \
169 - for x in $$(cd "$(srcdir)" && find pym/* -type d \
170 - ! -path "pym/portage/tests*") ; do \
171 - cd "$(srcdir)/$$x"; \
172 - files=$$(echo *.py); \
173 - if [ -z "$$files" ] || [ "$$files" = "*.py" ]; then \
174 - # __pycache__ directories contain no py files \
175 - continue; \
176 - fi; \
177 - install -d -m$(DIRMODE) "$(DESTDIR)$(portage_base)/$$x"; \
178 - install -m$(INSMODE) $$files "$(DESTDIR)$(portage_base)/$$x"; \
179 - done; \
180 - \
181 - install -d -m$(DIRMODE) "$(DESTDIR)$(bindir)"; \
182 - relative_path=".."; \
183 - x=$(bindir) ; \
184 - y="$(portage_base)"; \
185 - if [ "$${x#$(prefix)}" != "$$x" ] && \
186 - [ "$${y#$(prefix)}" != "$$y" ]; then \
187 - x=$${x#$(prefix)}; \
188 - y=$${y#$(prefix)}; \
189 - fi; \
190 - x=$${x%/*}; \
191 - while [ -n "$$x" ] ; do \
192 - relative_path=$${relative_path}/..; \
193 - x=$${x%/*}; \
194 - done; \
195 - relative_path=$$relative_path$$y; \
196 - for x in $(BINDIR_FILES) ; do \
197 - ln -sf "$$relative_path/bin/$$x" \
198 - "$(DESTDIR)$(bindir)/$$x"; \
199 - done; \
200 - \
201 - install -d -m$(DIRMODE) "$(DESTDIR)$(sbindir)"; \
202 - relative_path=".."; \
203 - x=$(sbindir) ; \
204 - y="$(portage_base)"; \
205 - if [ "$${x#$(prefix)}" != "$$x" ] && \
206 - [ "$${y#$(prefix)}" != "$$y" ]; then \
207 - x=$${x#$(prefix)}; \
208 - y=$${y#$(prefix)}; \
209 - fi; \
210 - x=$${x%/*}; \
211 - while [ -n "$$x" ] ; do \
212 - relative_path=$${relative_path}/..; \
213 - x=$${x%/*}; \
214 - done; \
215 - relative_path=$$relative_path$$y; \
216 - for x in $(SBINDIR_FILES) ; do \
217 - ln -sf "$$relative_path/bin/$$x" \
218 - "$(DESTDIR)$(sbindir)/$$x"; \
219 - done; \
220 - \
221 - ln -sf "$$relative_path/bin/env-update" \
222 - "$(DESTDIR)$(sbindir)/update-env"; \
223 - ln -sf "$$relative_path/bin/etc-update" \
224 - "$(DESTDIR)$(sbindir)/update-etc"; \
225 - \
226 - # We install some minimal tests for use as a preinst sanity check. \
227 - # These tests must be able to run without a full source tree and \
228 - # without relying on a previous portage instance being installed. \
229 - install -d -m$(DIRMODE) \
230 - "$(DESTDIR)$(portage_base)/pym/portage/tests"; \
231 - install -m$(EXEMODE) "$(srcdir)/pym/portage/tests/runTests" \
232 - "$(DESTDIR)$(portage_base)/pym/portage/tests"; \
233 - cd "$(srcdir)/pym/portage/tests"; \
234 - install -m$(INSMODE) *.py \
235 - "$(DESTDIR)$(portage_base)/pym/portage/tests"; \
236 - install -d -m$(DIRMODE) \
237 - "$(DESTDIR)$(portage_base)/pym/portage/tests/lint"; \
238 - cd "$(srcdir)/pym/portage/tests/lint"; \
239 - install -m$(INSMODE) *.py __test__ \
240 - "$(DESTDIR)$(portage_base)/pym/portage/tests/lint"; \
241 - \
242 - install -d -m$(DIRMODE) "$(DESTDIR)$(docdir)"; \
243 - cd "$(srcdir)"; \
244 - install -m $(INSMODE) $(DOCS) "$(DESTDIR)$(docdir)"; \
245 - \
246 - for x in "" $(LINGUAS); do \
247 - for y in 1 5 ; do \
248 - if [ -d "$(srcdir)/man/$$x" ]; then \
249 - cd "$(srcdir)/man/$$x"; \
250 - files=$$(echo *.$$y); \
251 - if [ -z "$$files" ] || [ "$$files" = "*.$$y" ]; then \
252 - continue; \
253 - fi; \
254 - install -d -m$(DIRMODE) "$(DESTDIR)$(mandir)/$$x/man$$y"; \
255 - install -m$(INSMODE) *.$$y "$(DESTDIR)$(mandir)/$$x/man$$y"; \
256 - fi; \
257 - done; \
258 - done; \
259 - \
260 - if [ -f "$(srcdir)/doc/portage.html" ] ; then \
261 - install -d -m$(DIRMODE) "$(DESTDIR)$(htmldir)"; \
262 - cd "$(srcdir)/doc"; \
263 - install -m$(INSMODE) *.html "$(DESTDIR)$(htmldir)"; \
264 - fi; \
265 - \
266 - if [ -d "$(WORKDIR)/epydoc" ] ; then \
267 - install -d -m$(DIRMODE) "$(DESTDIR)$(htmldir)"; \
268 - cp -pPR "$(WORKDIR)/epydoc" \
269 - "$(DESTDIR)$(htmldir)/api"; \
270 - cd "$(DESTDIR)$(htmldir)/api"; \
271 - find . -type d | xargs chmod $(DIRMODE); \
272 - find . -type f | xargs chmod $(INSMODE); \
273 - fi; \
274 -
275 -clean:
276 - set -e; \
277 - $(MAKE) -C "$(srcdir)/doc" clean; \
278 - rm -rf "$(WORKDIR)/epydoc"; \
279 -
280 -.PHONY: all clean docbook epydoc install test
281 diff --git a/doc/Makefile b/doc/Makefile
282 deleted file mode 100644
283 index 261a0b4..0000000
284 --- a/doc/Makefile
285 +++ /dev/null
286 @@ -1,11 +0,0 @@
287 -all: xhtml xhtml-nochunks
288 -
289 -XMLTO_FLAGS = -m custom.xsl
290 -man pdf txt xhtml xhtml-nochunks:
291 - xmlto $@ $(XMLTO_FLAGS) portage.docbook
292 -
293 -clean distclean:
294 - rm -f *.1 *.html portage.txt
295 -
296 -.PHONY: all clean distclean \
297 - man pdf txt xhtml xhtml-nochunks
298 diff --git a/doc/fragment/date b/doc/fragment/date
299 deleted file mode 100644
300 index e69de29..0000000
301 diff --git a/doc/fragment/version b/doc/fragment/version
302 deleted file mode 100644
303 index f85674c..0000000
304 --- a/doc/fragment/version
305 +++ /dev/null
306 @@ -1 +0,0 @@
307 -<releaseinfo>VERSION</releaseinfo>
308 diff --git a/mkrelease.sh b/mkrelease.sh
309 deleted file mode 100755
310 index f9f7564..0000000
311 --- a/mkrelease.sh
312 +++ /dev/null
313 @@ -1,141 +0,0 @@
314 -#!/bin/bash
315 -# Copyright 2008-2014 Gentoo Foundation
316 -# Distributed under the terms of the GNU General Public License v2
317 -
318 -RELEASE_BUILDDIR=${RELEASE_BUILDDIR:-/var/tmp/portage-release}
319 -SOURCE_DIR=${RELEASE_BUILDDIR}/checkout
320 -BRANCH=${BRANCH:-master}
321 -USE_TAG=false
322 -CHANGELOG_REVISION=
323 -UPLOAD_LOCATION=
324 -RUNTESTS=false
325 -USER=
326 -
327 -usage() {
328 - echo "Usage: ${0##*/} [--changelog-rev <tree-ish>] [-t|--tag] [-u|--upload <location>] [--user <username>] [--runtests] <version>"
329 - exit ${1:-0}
330 -}
331 -
332 -die() {
333 - printf 'error: %s\n' "$*"
334 - usage 1
335 -}
336 -
337 -ARGS=$(getopt -o htu: --long help,changelog-rev:,runtests,tag,upload:,user: \
338 - -n "${0##*/}" -- "$@")
339 -[ $? != 0 ] && die "initialization error"
340 -
341 -eval set -- "${ARGS}"
342 -
343 -while true; do
344 - case $1 in
345 - --changelog-rev)
346 - CHANGELOG_REVISION=$2
347 - shift 2
348 - ;;
349 - -t|--tag)
350 - USE_TAG=true
351 - shift
352 - ;;
353 - -u|--upload)
354 - UPLOAD_LOCATION=$2
355 - shift 2
356 - ;;
357 - --user)
358 - USER=$2"@"
359 - shift 2
360 - ;;
361 - -h|--help)
362 - usage
363 - ;;
364 - --runtests)
365 - RUNTESTS=true
366 - shift
367 - ;;
368 - --)
369 - shift
370 - break
371 - ;;
372 - *)
373 - die "unknown option: $1"
374 - ;;
375 - esac
376 -done
377 -
378 -[ $# != 1 ] && die "Need version argument"
379 -[[ -n ${1/[0-9]*} ]] && die "Invalid version argument"
380 -
381 -VERSION=$1
382 -RELEASE=portage-${VERSION}
383 -RELEASE_DIR=${RELEASE_BUILDDIR}/${RELEASE}
384 -RELEASE_TARBALL="${RELEASE_BUILDDIR}/${RELEASE}.tar.bz2"
385 -TREE_ISH=${BRANCH}
386 -if [[ ${USE_TAG} == "true" ]] ; then
387 - TREE_ISH="v${VERSION}"
388 -fi
389 -
390 -echo ">>> Cleaning working directories ${RELEASE_DIR} ${SOURCE_DIR}"
391 -rm -rf "${RELEASE_DIR}" "${SOURCE_DIR}" || die "directory cleanup failed"
392 -mkdir -p "${RELEASE_DIR}" || die "directory creation failed"
393 -mkdir -p "${SOURCE_DIR}" || die "mkdir failed"
394 -
395 -echo ">>> Starting GIT archive"
396 -git archive --format=tar ${TREE_ISH} | \
397 - tar -xf - -C "${SOURCE_DIR}" || die "git archive failed"
398 -
399 -echo ">>> Building release tree"
400 -cp -a "${SOURCE_DIR}/"{bin,cnf,doc,man,misc,pym} "${RELEASE_DIR}/" || die "directory copy failed"
401 -cp "${SOURCE_DIR}/"{.portage_not_installed,DEVELOPING,LICENSE,Makefile,NEWS,README,RELEASE-NOTES,TEST-NOTES} \
402 - "${RELEASE_DIR}/" || die "file copy failed"
403 -
404 -if [[ ${RUNTESTS} == "true" ]] ; then
405 - pushd "${SOURCE_DIR}" >/dev/null
406 - ./runtests.sh --python-versions=supported || die "tests failed"
407 - popd >/dev/null
408 -fi
409 -
410 -rm -rf "${SOURCE_DIR}" || die "directory cleanup failed"
411 -
412 -echo ">>> Setting portage.VERSION"
413 -sed -e "s/^VERSION = .*/VERSION = \"${VERSION}\"/" \
414 - -i "${RELEASE_DIR}/pym/portage/__init__.py" || \
415 - die "Failed to patch portage.VERSION"
416 -
417 -echo ">>> Creating Changelog"
418 -git_log_opts=""
419 -if [[ -n ${CHANGELOG_REVISION} ]] ; then
420 - git_log_opts+=" ${CHANGELOG_REVISION}^..${TREE_ISH}"
421 -else
422 - git_log_opts+=" ${TREE_ISH}"
423 -fi
424 -skip_next=false
425 -git log ${git_log_opts} | fmt -w 80 -p " " | while read -r ; do
426 - if [[ ${skip_next} == "true" ]] ; then
427 - skip_next=false
428 - elif [[ ${REPLY} == " svn path="* ]] ; then
429 - skip_next=true
430 - else
431 - echo "${REPLY}"
432 - fi
433 -done > "${RELEASE_DIR}/ChangeLog" || die "ChangeLog creation failed"
434 -
435 -cd "${RELEASE_BUILDDIR}"
436 -
437 -echo ">>> Creating release tarball ${RELEASE_TARBALL}"
438 -tar --owner portage --group portage -cjf "${RELEASE_TARBALL}" "${RELEASE}" || \
439 - die "tarball creation failed"
440 -
441 -DISTDIR=$(portageq distdir)
442 -if [[ -n ${DISTDIR} && -d ${DISTDIR} && -w ${DISTDIR} ]] ; then
443 - echo ">>> Copying release tarball into ${DISTDIR}"
444 - cp "${RELEASE_TARBALL}" "${DISTDIR}"/ || echo "!!! tarball copy failed"
445 -fi
446 -
447 -if [[ -n ${UPLOAD_LOCATION} ]] ; then
448 - echo ">>> Uploading ${RELEASE_TARBALL} to ${USER}dev.gentoo.org:${UPLOAD_LOCATION}"
449 - scp "${RELEASE_TARBALL}" "${USER}dev.gentoo.org:${UPLOAD_LOCATION}" || die "upload failed"
450 -else
451 - du -h "${RELEASE_TARBALL}"
452 -fi
453 -
454 -exit 0
455 diff --git a/pym/portage/const.py b/pym/portage/const.py
456 index f518b47..acb90f9 100644
457 --- a/pym/portage/const.py
458 +++ b/pym/portage/const.py
459 @@ -60,8 +60,8 @@ GLOBAL_CONFIG_PATH = "/usr/share/portage/config"
460 # these variables are not used with target_root or config_root
461 # NOTE: Use realpath(__file__) so that python module symlinks in site-packages
462 # are followed back to the real location of the whole portage installation.
463 -PORTAGE_BASE_PATH = os.path.join(os.sep, os.sep.join(os.path.realpath(
464 - __file__.rstrip("co")).split(os.sep)[:-3]))
465 +# NOTE: Please keep PORTAGE_BASE_PATH in one line to help substitutions.
466 +PORTAGE_BASE_PATH = os.path.join(os.sep, os.sep.join(os.path.realpath(__file__.rstrip("co")).split(os.sep)[:-3]))
467 PORTAGE_BIN_PATH = PORTAGE_BASE_PATH + "/bin"
468 PORTAGE_PYM_PATH = os.path.realpath(os.path.join(__file__, '../..'))
469 LOCALE_DATA_PATH = PORTAGE_BASE_PATH + "/locale" # FIXME: not used
470 diff --git a/setup.py b/setup.py
471 new file mode 100755
472 index 0000000..e5c3631
473 --- /dev/null
474 +++ b/setup.py
475 @@ -0,0 +1,657 @@
476 +#!/usr/bin/env python
477 +# Copyright 1998-2014 Gentoo Foundation
478 +# Distributed under the terms of the GNU General Public License v2
479 +
480 +from distutils.core import setup, Command
481 +from distutils.command.build import build
482 +from distutils.command.build_scripts import build_scripts
483 +from distutils.command.clean import clean
484 +from distutils.command.install import install
485 +from distutils.command.install_data import install_data
486 +from distutils.command.install_lib import install_lib
487 +from distutils.command.install_scripts import install_scripts
488 +from distutils.command.sdist import sdist
489 +from distutils.dep_util import newer
490 +from distutils.dir_util import mkpath, remove_tree
491 +from distutils.util import change_root, subst_vars
492 +
493 +import codecs, collections, errno, glob, os, os.path, re, subprocess, sys
494 +
495 +# TODO:
496 +# - smarter rebuilds of docs w/ 'install_docbook' and 'install_epydoc'.
497 +
498 +x_scripts = {
499 + 'bin': [
500 + 'bin/ebuild', 'bin/egencache', 'bin/emerge', 'bin/emerge-webrsync',
501 + 'bin/emirrordist', 'bin/portageq', 'bin/quickpkg', 'bin/repoman'
502 + ],
503 + 'sbin': [
504 + 'bin/archive-conf', 'bin/dispatch-conf', 'bin/emaint', 'bin/env-update',
505 + 'bin/etc-update', 'bin/fixpackages', 'bin/regenworld'
506 + ],
507 +}
508 +
509 +
510 +class x_build(build):
511 + """ Build command with extra build_man call. """
512 +
513 + def run(self):
514 + build.run(self)
515 + self.run_command('build_man')
516 +
517 +
518 +class build_man(Command):
519 + """ Perform substitutions in manpages. """
520 +
521 + user_options = [
522 + ]
523 +
524 + def initialize_options(self):
525 + self.build_base = None
526 +
527 + def finalize_options(self):
528 + self.set_undefined_options('build',
529 + ('build_base', 'build_base'))
530 +
531 + def run(self):
532 + for d, files in self.distribution.data_files:
533 + if not d.startswith('$mandir/'):
534 + continue
535 +
536 + for source in files:
537 + target = os.path.join(self.build_base, source)
538 + mkpath(os.path.dirname(target))
539 +
540 + if not newer(source, target) and not newer(__file__, target):
541 + continue
542 +
543 + print('copying and updating %s -> %s' % (
544 + source, target))
545 +
546 + with codecs.open(source, 'r', 'utf8') as f:
547 + data = f.readlines()
548 + data[0] = data[0].replace('VERSION',
549 + self.distribution.get_version())
550 + with codecs.open(target, 'w', 'utf8') as f:
551 + f.writelines(data)
552 +
553 +
554 +class docbook(Command):
555 + """ Build docs using docbook. """
556 +
557 + user_options = [
558 + ('doc-formats=', None, 'Documentation formats to build (all xmlto formats for docbook are allowed, comma-separated'),
559 + ]
560 +
561 + def initialize_options(self):
562 + self.doc_formats = 'xhtml,xhtml-nochunks'
563 +
564 + def finalize_options(self):
565 + self.doc_formats = self.doc_formats.replace(',', ' ').split()
566 +
567 + def run(self):
568 + if not os.path.isdir('doc/fragment'):
569 + mkpath('doc/fragment')
570 +
571 + with open('doc/fragment/date', 'w'):
572 + pass
573 + with open('doc/fragment/version', 'w') as f:
574 + f.write('<releaseinfo>%s</releaseinfo>' % self.distribution.get_version())
575 +
576 + for f in self.doc_formats:
577 + print('Building docs in %s format...' % f)
578 + subprocess.check_call(['xmlto', '-o', 'doc',
579 + '-m', 'doc/custom.xsl', f, 'doc/portage.docbook'])
580 +
581 +
582 +class epydoc(Command):
583 + """ Build API docs using epydoc. """
584 +
585 + user_options = [
586 + ]
587 +
588 + def initialize_options(self):
589 + self.build_lib = None
590 +
591 + def finalize_options(self):
592 + self.set_undefined_options('build_py', ('build_lib', 'build_lib'))
593 +
594 + def run(self):
595 + self.run_command('build_py')
596 +
597 + print('Building API documentation...')
598 +
599 + process_env = os.environ.copy()
600 + pythonpath = self.build_lib
601 + try:
602 + pythonpath += ':' + process_env['PYTHONPATH']
603 + except KeyError:
604 + pass
605 + process_env['PYTHONPATH'] = pythonpath
606 +
607 + subprocess.check_call(['epydoc', '-o', 'epydoc',
608 + '--name', self.distribution.get_name(),
609 + '--url', self.distribution.get_url(),
610 + '-qq', '--no-frames', '--show-imports',
611 + '--exclude', 'portage.tests',
612 + '_emerge', 'portage', 'repoman'],
613 + env = process_env)
614 + os.remove('epydoc/api-objects.txt')
615 +
616 +
617 +class install_docbook(install_data):
618 + """ install_data for docbook docs """
619 +
620 + user_options = install_data.user_options
621 +
622 + def initialize_options(self):
623 + install_data.initialize_options(self)
624 + self.htmldir = None
625 +
626 + def finalize_options(self):
627 + self.set_undefined_options('install', ('htmldir', 'htmldir'))
628 + install_data.finalize_options(self)
629 +
630 + def run(self):
631 + if not os.path.exists('doc/portage.html'):
632 + self.run_command('docbook')
633 + self.data_files = [
634 + (self.htmldir, glob.glob('doc/*.html')),
635 + ]
636 + install_data.run(self)
637 +
638 +
639 +class install_epydoc(install_data):
640 + """ install_data for epydoc docs """
641 +
642 + user_options = install_data.user_options
643 +
644 + def initialize_options(self):
645 + install_data.initialize_options(self)
646 + self.htmldir = None
647 +
648 + def finalize_options(self):
649 + self.set_undefined_options('install', ('htmldir', 'htmldir'))
650 + install_data.finalize_options(self)
651 +
652 + def run(self):
653 + if not os.path.exists('epydoc/index.html'):
654 + self.run_command('epydoc')
655 + self.data_files = [
656 + (os.path.join(self.htmldir, 'api'), glob.glob('epydoc/*')),
657 + ]
658 + install_data.run(self)
659 +
660 +
661 +class x_build_scripts_custom(build_scripts):
662 + def finalize_options(self):
663 + build_scripts.finalize_options(self)
664 + if 'dir_name' in dir(self):
665 + self.build_dir = os.path.join(self.build_dir, self.dir_name)
666 + if self.dir_name in x_scripts:
667 + self.scripts = x_scripts[self.dir_name]
668 + else:
669 + self.scripts = set(self.scripts)
670 + for other_files in x_scripts.values():
671 + self.scripts.difference_update(other_files)
672 +
673 + def run(self):
674 + # group scripts by subdirectory
675 + split_scripts = collections.defaultdict(list)
676 + for f in self.scripts:
677 + dir_name = os.path.dirname(f[len('bin/'):])
678 + split_scripts[dir_name].append(f)
679 +
680 + base_dir = self.build_dir
681 + base_scripts = self.scripts
682 + for d, files in split_scripts.items():
683 + self.build_dir = os.path.join(base_dir, d)
684 + self.scripts = files
685 + self.copy_scripts()
686 +
687 + # restore previous values
688 + self.build_dir = base_dir
689 + self.scripts = base_scripts
690 +
691 +
692 +class x_build_scripts_bin(x_build_scripts_custom):
693 + dir_name = 'bin'
694 +
695 +
696 +class x_build_scripts_sbin(x_build_scripts_custom):
697 + dir_name = 'sbin'
698 +
699 +
700 +class x_build_scripts_portagebin(x_build_scripts_custom):
701 + dir_name = 'portage'
702 +
703 +
704 +class x_build_scripts(build_scripts):
705 + def initialize_option(self):
706 + build_scripts.initialize_options(self)
707 +
708 + def finalize_options(self):
709 + build_scripts.finalize_options(self)
710 +
711 + def run(self):
712 + self.run_command('build_scripts_bin')
713 + self.run_command('build_scripts_portagebin')
714 + self.run_command('build_scripts_sbin')
715 +
716 +
717 +class x_clean(clean):
718 + """ clean extended for doc & post-test cleaning """
719 +
720 + def clean_docs(self):
721 + def get_doc_outfiles():
722 + for dirpath, dirnames, filenames in os.walk('doc'):
723 + for f in filenames:
724 + if f.endswith('.docbook') or f == 'custom.xsl':
725 + pass
726 + else:
727 + yield os.path.join(dirpath, f)
728 +
729 + # do not recurse
730 + break
731 +
732 +
733 + for f in get_doc_outfiles():
734 + print('removing %s' % repr(f))
735 + os.remove(f)
736 +
737 + if os.path.isdir('doc/fragment'):
738 + remove_tree('doc/fragment')
739 +
740 + if os.path.isdir('epydoc'):
741 + remove_tree('epydoc')
742 +
743 + def clean_tests(self):
744 + # do not remove incorrect dirs accidentally
745 + top_dir = os.path.normpath(os.path.join(self.build_lib, '..'))
746 + cprefix = os.path.commonprefix((self.build_base, top_dir))
747 + if cprefix != self.build_base:
748 + return
749 +
750 + bin_dir = os.path.join(top_dir, 'bin')
751 + if os.path.exists(bin_dir):
752 + remove_tree(bin_dir)
753 +
754 + conf_dir = os.path.join(top_dir, 'cnf')
755 + if os.path.islink(conf_dir):
756 + print('removing %s symlink' % repr(conf_dir))
757 + os.unlink(conf_dir)
758 +
759 + pni_file = os.path.join(top_dir, '.portage_not_installed')
760 + if os.path.exists(pni_file):
761 + print('removing %s' % repr(pni_file))
762 + os.unlink(pni_file)
763 +
764 + def clean_man(self):
765 + man_dir = os.path.join(self.build_base, 'man')
766 + if os.path.exists(man_dir):
767 + remove_tree(man_dir)
768 +
769 + def run(self):
770 + if self.all:
771 + self.clean_tests()
772 + self.clean_docs()
773 + self.clean_man()
774 +
775 + clean.run(self)
776 +
777 +
778 +class x_install(install):
779 + """ install command with extra Portage paths """
780 +
781 + user_options = install.user_options + [
782 + # note: $prefix and $exec_prefix are reserved for Python install
783 + ('system-prefix=', None, "Prefix for architecture-independent data"),
784 + ('system-exec-prefix=', None, "Prefix for architecture-specific data"),
785 +
786 + ('bindir=', None, "Install directory for main executables"),
787 + ('datarootdir=', None, "Data install root directory"),
788 + ('docdir=', None, "Documentation install directory"),
789 + ('htmldir=', None, "HTML documentation install directory"),
790 + ('mandir=', None, "Manpage root install directory"),
791 + ('portage-base=', 'b', "Portage install base"),
792 + ('portage-bindir=', None, "Install directory for Portage internal-use executables"),
793 + ('portage-datadir=', None, 'Install directory for data files'),
794 + ('sbindir=', None, "Install directory for superuser-intended executables"),
795 + ('sysconfdir=', None, 'System configuration path'),
796 + ]
797 +
798 + # note: the order is important for proper substitution
799 + paths = [
800 + ('system_prefix', '/usr'),
801 + ('system_exec_prefix', '$system_prefix'),
802 +
803 + ('bindir', '$system_exec_prefix/bin'),
804 + ('sbindir', '$system_exec_prefix/sbin'),
805 + ('sysconfdir', '/etc'),
806 +
807 + ('datarootdir', '$system_prefix/share'),
808 + ('docdir', '$datarootdir/doc/$package-$version'),
809 + ('htmldir', '$docdir/html'),
810 + ('mandir', '$datarootdir/man'),
811 +
812 + ('portage_base', '$system_exec_prefix/lib/portage'),
813 + ('portage_bindir', '$portage_base/bin'),
814 + ('portage_datadir', '$datarootdir/portage'),
815 +
816 + # not customized at the moment
817 + ('logrotatedir', '$sysconfdir/logrotate'),
818 + ('portage_confdir', '$portage_datadir/config'),
819 + ('portage_setsdir', '$portage_confdir/sets'),
820 + ]
821 +
822 + def initialize_options(self):
823 + install.initialize_options(self)
824 +
825 + for key, default in self.paths:
826 + setattr(self, key, default)
827 + self.subst_paths = {}
828 +
829 + def finalize_options(self):
830 + install.finalize_options(self)
831 +
832 + # substitute variables
833 + new_paths = {
834 + 'package': self.distribution.get_name(),
835 + 'version': self.distribution.get_version(),
836 + }
837 + for key, default in self.paths:
838 + new_paths[key] = subst_vars(getattr(self, key), new_paths)
839 + setattr(self, key, new_paths[key])
840 + self.subst_paths = new_paths
841 +
842 +
843 +class x_install_data(install_data):
844 + """ install_data with customized path support """
845 +
846 + user_options = install_data.user_options
847 +
848 + def initialize_options(self):
849 + install_data.initialize_options(self)
850 + self.build_base = None
851 + self.paths = None
852 +
853 + def finalize_options(self):
854 + install_data.finalize_options(self)
855 + self.set_undefined_options('build',
856 + ('build_base', 'build_base'))
857 + self.set_undefined_options('install',
858 + ('subst_paths', 'paths'))
859 +
860 + def run(self):
861 + self.run_command('build_man')
862 +
863 + def process_data_files(df):
864 + for d, files in df:
865 + # substitute man sources
866 + if d.startswith('$mandir/'):
867 + files = [os.path.join(self.build_base, v) for v in files]
868 +
869 + # substitute variables in path
870 + d = subst_vars(d, self.paths)
871 + yield (d, files)
872 +
873 + old_data_files = self.data_files
874 + self.data_files = process_data_files(self.data_files)
875 +
876 + install_data.run(self)
877 + self.data_files = old_data_files
878 +
879 +
880 +class x_install_lib(install_lib):
881 + """ install_lib command with Portage path substitution """
882 +
883 + user_options = install_lib.user_options
884 +
885 + def initialize_options(self):
886 + install_lib.initialize_options(self)
887 + self.portage_base = None
888 + self.portage_bindir = None
889 + self.portage_confdir = None
890 +
891 + def finalize_options(self):
892 + install_lib.finalize_options(self)
893 + self.set_undefined_options('install',
894 + ('portage_base', 'portage_base'),
895 + ('portage_bindir', 'portage_bindir'),
896 + ('portage_confdir', 'portage_confdir'))
897 +
898 + def install(self):
899 + ret = install_lib.install(self)
900 +
901 + def rewrite_file(path, val_dict):
902 + path = os.path.join(self.install_dir, path)
903 + print('Rewriting %s' % path)
904 + with codecs.open(path, 'r', 'utf-8') as f:
905 + data = f.read()
906 +
907 + for varname, val in val_dict.items():
908 + regexp = r'(?m)^(%s\s*=).*$' % varname
909 + repl = r'\1 %s' % repr(val)
910 +
911 + data = re.sub(regexp, repl, data)
912 +
913 + with codecs.open(path, 'w', 'utf-8') as f:
914 + f.write(data)
915 +
916 + rewrite_file('portage/__init__.py', {
917 + 'VERSION': self.distribution.get_version(),
918 + })
919 + rewrite_file('portage/const.py', {
920 + 'PORTAGE_BASE_PATH': self.portage_base,
921 + 'PORTAGE_BIN_PATH': self.portage_bindir,
922 + 'PORTAGE_CONFIG_PATH': self.portage_confdir,
923 + })
924 +
925 + return ret
926 +
927 +
928 +class x_install_scripts_custom(install_scripts):
929 + def initialize_options(self):
930 + install_scripts.initialize_options(self)
931 + self.root = None
932 +
933 + def finalize_options(self):
934 + self.set_undefined_options('install',
935 + ('root', 'root'),
936 + (self.var_name, 'install_dir'))
937 + install_scripts.finalize_options(self)
938 + self.build_dir = os.path.join(self.build_dir, self.dir_name)
939 +
940 + # prepend root
941 + if self.root is not None:
942 + self.install_dir = change_root(self.root, self.install_dir)
943 +
944 +
945 +class x_install_scripts_bin(x_install_scripts_custom):
946 + dir_name = 'bin'
947 + var_name = 'bindir'
948 +
949 +
950 +class x_install_scripts_sbin(x_install_scripts_custom):
951 + dir_name = 'sbin'
952 + var_name = 'sbindir'
953 +
954 +
955 +class x_install_scripts_portagebin(x_install_scripts_custom):
956 + dir_name = 'portage'
957 + var_name = 'portage_bindir'
958 +
959 +
960 +class x_install_scripts(install_scripts):
961 + def initialize_option(self):
962 + pass
963 +
964 + def finalize_options(self):
965 + pass
966 +
967 + def run(self):
968 + self.run_command('install_scripts_bin')
969 + self.run_command('install_scripts_portagebin')
970 + self.run_command('install_scripts_sbin')
971 +
972 +
973 +class x_sdist(sdist):
974 + """ sdist defaulting to .tar.bz2 format """
975 +
976 + def finalize_options(self):
977 + if self.formats is None:
978 + self.formats = ['bztar']
979 +
980 + sdist.finalize_options(self)
981 +
982 +
983 +class build_tests(x_build_scripts_custom):
984 + """ Prepare build dir for running tests. """
985 +
986 + def initialize_options(self):
987 + x_build_scripts_custom.initialize_options(self)
988 + self.build_base = None
989 + self.build_lib = None
990 +
991 + def finalize_options(self):
992 + x_build_scripts_custom.finalize_options(self)
993 + self.set_undefined_options('build',
994 + ('build_base', 'build_base'),
995 + ('build_lib', 'build_lib'))
996 +
997 + # since we will be writing to $build_lib/.., it is important
998 + # that we do not leave $build_base
999 + self.top_dir = os.path.normpath(os.path.join(self.build_lib, '..'))
1000 + cprefix = os.path.commonprefix((self.build_base, self.top_dir))
1001 + if cprefix != self.build_base:
1002 + raise SystemError('build_lib must be a subdirectory of build_base')
1003 +
1004 + self.build_dir = os.path.join(self.top_dir, 'bin')
1005 +
1006 + def run(self):
1007 + self.run_command('build_py')
1008 +
1009 + # install all scripts $build_lib/../bin
1010 + # (we can't do a symlink since we want shebangs corrected)
1011 + x_build_scripts_custom.run(self)
1012 +
1013 + # symlink 'cnf' directory
1014 + conf_dir = os.path.join(self.top_dir, 'cnf')
1015 + if os.path.exists(conf_dir):
1016 + if not os.path.islink(conf_dir):
1017 + raise SystemError('%s exists and is not a symlink (collision)'
1018 + % repr(conf_dir))
1019 + os.unlink(conf_dir)
1020 + conf_src = os.path.relpath('cnf', self.top_dir)
1021 + print('Symlinking %s -> %s' % (conf_dir, conf_src))
1022 + os.symlink(conf_src, conf_dir)
1023 +
1024 + # create $build_lib/../.portage_not_installed
1025 + # to enable proper paths in tests
1026 + with open(os.path.join(self.top_dir, '.portage_not_installed'), 'w') as f:
1027 + pass
1028 +
1029 +
1030 +class test(Command):
1031 + """ run tests """
1032 +
1033 + user_options = []
1034 +
1035 + def initialize_options(self):
1036 + self.build_lib = None
1037 +
1038 + def finalize_options(self):
1039 + self.set_undefined_options('build',
1040 + ('build_lib', 'build_lib'))
1041 +
1042 + def run(self):
1043 + self.run_command('build_tests')
1044 + subprocess.check_call([
1045 + sys.executable, '-bWd',
1046 + os.path.join(self.build_lib, 'portage/tests/runTests.py')
1047 + ])
1048 +
1049 +
1050 +def find_packages():
1051 + for dirpath, dirnames, filenames in os.walk('pym'):
1052 + if '__init__.py' in filenames:
1053 + yield os.path.relpath(dirpath, 'pym')
1054 +
1055 +
1056 +def find_scripts():
1057 + for dirpath, dirnames, filenames in os.walk('bin'):
1058 + for f in filenames:
1059 + yield os.path.join(dirpath, f)
1060 +
1061 +
1062 +def get_manpages():
1063 + linguas = os.environ.get('LINGUAS')
1064 + if linguas is not None:
1065 + linguas = linguas.split()
1066 +
1067 + for dirpath, dirnames, filenames in os.walk('man'):
1068 + groups = collections.defaultdict(list)
1069 + for f in filenames:
1070 + fn, suffix = f.rsplit('.', 1)
1071 + groups[suffix].append(os.path.join(dirpath, f))
1072 +
1073 + topdir = dirpath[len('man/'):]
1074 + if not topdir or linguas is None or topdir in linguas:
1075 + for g, mans in groups.items():
1076 + yield [os.path.join('$mandir', topdir, 'man%s' % g), mans]
1077 +
1078 +setup(
1079 + name = 'portage',
1080 + version = '2.2.12',
1081 + url = 'https://wiki.gentoo.org/wiki/Project:Portage',
1082 + author = 'Gentoo Portage Development Team',
1083 + author_email = 'dev-portage@g.o',
1084 +
1085 + package_dir = {'': 'pym'},
1086 + packages = list(find_packages()),
1087 + # something to cheat build & install commands
1088 + scripts = list(find_scripts()),
1089 +
1090 + data_files = list(get_manpages()) + [
1091 + ['$sysconfdir', ['cnf/etc-update.conf', 'cnf/dispatch-conf.conf']],
1092 + ['$logrotatedir', ['cnf/logrotate.d/elog-save-summary']],
1093 + ['$portage_confdir', [
1094 + 'cnf/make.conf.example', 'cnf/make.globals', 'cnf/repos.conf']],
1095 + ['$portage_setsdir', ['cnf/sets/portage.conf']],
1096 + ['$docdir', ['NEWS', 'RELEASE-NOTES']],
1097 + ],
1098 +
1099 + cmdclass = {
1100 + 'build': x_build,
1101 + 'build_man': build_man,
1102 + 'build_scripts': x_build_scripts,
1103 + 'build_scripts_bin': x_build_scripts_bin,
1104 + 'build_scripts_portagebin': x_build_scripts_portagebin,
1105 + 'build_scripts_sbin': x_build_scripts_sbin,
1106 + 'build_tests': build_tests,
1107 + 'clean': x_clean,
1108 + 'docbook': docbook,
1109 + 'epydoc': epydoc,
1110 + 'install': x_install,
1111 + 'install_data': x_install_data,
1112 + 'install_docbook': install_docbook,
1113 + 'install_epydoc': install_epydoc,
1114 + 'install_lib': x_install_lib,
1115 + 'install_scripts': x_install_scripts,
1116 + 'install_scripts_bin': x_install_scripts_bin,
1117 + 'install_scripts_portagebin': x_install_scripts_portagebin,
1118 + 'install_scripts_sbin': x_install_scripts_sbin,
1119 + 'sdist': x_sdist,
1120 + 'test': test,
1121 + },
1122 +
1123 + classifiers = [
1124 + 'Development Status :: 5 - Production/Stable',
1125 + 'Environment :: Console',
1126 + 'Intended Audience :: System Administrators',
1127 + 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)',
1128 + 'Operating System :: POSIX',
1129 + 'Programming Language :: Python',
1130 + 'Topic :: System :: Installation/Setup'
1131 + ]
1132 +)
1133 --
1134 2.1.0