Build distribution package as wheel (.whl) file for Python
This commit is contained in:
parent
5f1ade68be
commit
e44b8cd30d
|
@ -10,3 +10,4 @@ Changelog.patool*
|
||||||
/testresults.txt
|
/testresults.txt
|
||||||
/tests/__pycache__
|
/tests/__pycache__
|
||||||
/_patool_configdata.py
|
/_patool_configdata.py
|
||||||
|
/patool.egg-info/
|
||||||
|
|
35
Makefile
35
Makefile
|
@ -5,14 +5,11 @@ MAINTAINER:=$(shell $(PYTHON) setup.py --maintainer)
|
||||||
AUTHOR:=$(shell $(PYTHON) setup.py --author)
|
AUTHOR:=$(shell $(PYTHON) setup.py --author)
|
||||||
APPNAME:=$(shell $(PYTHON) setup.py --name)
|
APPNAME:=$(shell $(PYTHON) setup.py --name)
|
||||||
ARCHIVE_SOURCE:=$(APPNAME)-$(VERSION).tar.gz
|
ARCHIVE_SOURCE:=$(APPNAME)-$(VERSION).tar.gz
|
||||||
ARCHIVE_WIN32:=$(APPNAME)-$(VERSION).exe
|
ARCHIVE_WHEEL:=$(APPNAME)-$(VERSION)-py2.py3-none-any.whl
|
||||||
GITUSER:=wummel
|
GITUSER:=wummel
|
||||||
GITREPO:=$(APPNAME)
|
GITREPO:=$(APPNAME)
|
||||||
HOMEPAGE:=$(HOME)/public_html/patool-webpage.git
|
HOMEPAGE:=$(HOME)/public_html/patool-webpage.git
|
||||||
WEBMETA:=doc/web/app.yaml
|
WEBMETA:=doc/web/app.yaml
|
||||||
DEBUILDDIR:=$(HOME)/projects/debian/unofficial
|
|
||||||
DEBORIGFILE:=$(DEBUILDDIR)/$(APPNAME)_$(VERSION).orig.tar.gz
|
|
||||||
DEBPACKAGEDIR:=$(DEBUILDDIR)/$(APPNAME)-$(VERSION)
|
|
||||||
# Pytest options:
|
# Pytest options:
|
||||||
# --resultlog: write test results in file
|
# --resultlog: write test results in file
|
||||||
# -s: do not capture stdout/stderr (some tests fail otherwise)
|
# -s: do not capture stdout/stderr (some tests fail otherwise)
|
||||||
|
@ -27,21 +24,20 @@ all:
|
||||||
|
|
||||||
dist:
|
dist:
|
||||||
[ -d dist ] || mkdir dist
|
[ -d dist ] || mkdir dist
|
||||||
$(PYTHON) setup.py sdist --formats=tar
|
$(PYTHON) setup.py sdist --formats=tar bdist_wheel
|
||||||
gzip --best dist/$(APPNAME)-$(VERSION).tar
|
gzip --best dist/$(APPNAME)-$(VERSION).tar
|
||||||
# [ ! -f ../$(ARCHIVE_WIN32) ] || cp ../$(ARCHIVE_WIN32) dist
|
|
||||||
|
|
||||||
sign:
|
sign:
|
||||||
[ -f dist/$(ARCHIVE_SOURCE).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_SOURCE)
|
[ -f dist/$(ARCHIVE_SOURCE).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_SOURCE)
|
||||||
# [ -f dist/$(ARCHIVE_WIN32).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_WIN32)
|
[ -f dist/$(ARCHIVE_WHEEL).asc ] || gpg --detach-sign --armor dist/$(ARCHIVE_WHEEL)
|
||||||
|
|
||||||
upload: upload_source #upload_binary
|
upload: upload_source upload_binary
|
||||||
|
|
||||||
upload_source:
|
upload_source:
|
||||||
twine upload dist/$(ARCHIVE_SOURCE) dist/$(ARCHIVE_SOURCE).asc
|
twine upload dist/$(ARCHIVE_SOURCE) dist/$(ARCHIVE_SOURCE).asc
|
||||||
|
|
||||||
upload_binary:
|
upload_binary:
|
||||||
cp dist/$(ARCHIVE_WIN32) dist/$(ARCHIVE_WIN32).asc \
|
cp dist/$(ARCHIVE_WHEEL) dist/$(ARCHIVE_WHEEL).asc \
|
||||||
$(HOMEPAGE)/dist
|
$(HOMEPAGE)/dist
|
||||||
|
|
||||||
update_webmeta:
|
update_webmeta:
|
||||||
|
@ -81,10 +77,6 @@ releasecheck: test check
|
||||||
echo "Version in doc/changelog.txt:"; head -n1 doc/changelog.txt; \
|
echo "Version in doc/changelog.txt:"; head -n1 doc/changelog.txt; \
|
||||||
echo "Version in setup.py: $(VERSION)"; false; \
|
echo "Version in setup.py: $(VERSION)"; false; \
|
||||||
fi
|
fi
|
||||||
# @if [ ! -f ../$(ARCHIVE_WIN32) ]; then \
|
|
||||||
# echo "Missing WIN32 distribution archive at ../$(ARCHIVE_WIN32)"; \
|
|
||||||
# false; \
|
|
||||||
# fi
|
|
||||||
$(PYTHON) setup.py check --restructuredtext
|
$(PYTHON) setup.py check --restructuredtext
|
||||||
|
|
||||||
check:
|
check:
|
||||||
|
@ -128,22 +120,9 @@ test: localbuild
|
||||||
$(PYTHON) -m pytest $(PYTESTOPTS) $(TESTOPTS) $(TESTS)
|
$(PYTHON) -m pytest $(PYTESTOPTS) $(TESTOPTS) $(TESTS)
|
||||||
|
|
||||||
doc/$(APPNAME).txt: doc/$(APPNAME).1
|
doc/$(APPNAME).txt: doc/$(APPNAME).1
|
||||||
# make text file from man page for Windows builds
|
# make text file from man page for wheel builds
|
||||||
cols=`stty size | cut -d" " -f2`; stty cols 72; man -l $< | sed -e 's/.\cH//g' > $@; stty cols $$cols
|
cols=`stty size | cut -d" " -f2`; stty cols 72; man -l $< | sed -e 's/.\cH//g' > $@; stty cols $$cols
|
||||||
|
|
||||||
deb:
|
|
||||||
# build a debian package
|
|
||||||
[ -f $(DEBORIGFILE) ] || cp dist/$(ARCHIVE_SOURCE) $(DEBORIGFILE)
|
|
||||||
sed -i -e 's/VERSION_$(APPNAME):=.*/VERSION_$(APPNAME):=$(VERSION)/' $(DEBUILDDIR)/$(APPNAME).mak
|
|
||||||
[ -d $(DEBPACKAGEDIR) ] || (cd $(DEBUILDDIR); \
|
|
||||||
patool extract $(DEBORIGFILE); \
|
|
||||||
cd $(CURDIR); \
|
|
||||||
git checkout debian; \
|
|
||||||
cp -r debian $(DEBPACKAGEDIR); \
|
|
||||||
rm -f $(DEBPACKAGEDIR)/debian/.gitignore; \
|
|
||||||
git checkout master)
|
|
||||||
$(MAKE) -C $(DEBUILDDIR) $(APPNAME)_clean $(APPNAME)
|
|
||||||
|
|
||||||
update-copyright:
|
update-copyright:
|
||||||
# update-copyright is a local tool which updates the copyright year for each
|
# update-copyright is a local tool which updates the copyright year for each
|
||||||
# modified file.
|
# modified file.
|
||||||
|
@ -154,6 +133,6 @@ changelog:
|
||||||
# closes issues mentioned in the changelog entries.
|
# closes issues mentioned in the changelog entries.
|
||||||
github-changelog $(DRYRUN) $(GITUSER) $(GITREPO) doc/changelog.txt
|
github-changelog $(DRYRUN) $(GITUSER) $(GITREPO) doc/changelog.txt
|
||||||
|
|
||||||
.PHONY: changelog update-copyright deb test clean count pyflakes check
|
.PHONY: changelog update-copyright test clean count pyflakes check
|
||||||
.PHONY: releasecheck release upload sign dist all tag register homepage
|
.PHONY: releasecheck release upload sign dist all tag register homepage
|
||||||
.PHONY: localbuild doccheck
|
.PHONY: localbuild doccheck
|
||||||
|
|
11
setup.cfg
11
setup.cfg
|
@ -1,11 +1,4 @@
|
||||||
[global]
|
[global]
|
||||||
;command_packages = distcmds
|
|
||||||
|
|
||||||
[bdist_rpm]
|
[bdist_wheel]
|
||||||
release = 1
|
universal = 1
|
||||||
packager = Bastian Kleineidam <bastian.kleineidam@web.de>
|
|
||||||
doc_files = doc/*.txt
|
|
||||||
provides = patool
|
|
||||||
group = Applications/Archiving
|
|
||||||
install_script = install-rpm.sh
|
|
||||||
python = python
|
|
||||||
|
|
229
setup.py
229
setup.py
|
@ -26,24 +26,8 @@ import re
|
||||||
import shutil
|
import shutil
|
||||||
import glob
|
import glob
|
||||||
import subprocess
|
import subprocess
|
||||||
try:
|
from setuptools import setup
|
||||||
from cx_Freeze import setup, Executable
|
|
||||||
except ImportError:
|
|
||||||
from distutils.core import setup
|
|
||||||
try:
|
|
||||||
# py2exe monkey-patches the distutils.core.Distribution class
|
|
||||||
# So we need to import it before importing the Distribution class
|
|
||||||
import py2exe
|
|
||||||
has_py2exe = True
|
|
||||||
except ImportError:
|
|
||||||
# py2exe is not installed
|
|
||||||
has_py2exe = False
|
|
||||||
try:
|
|
||||||
from cx_Freeze.dist import Distribution
|
|
||||||
executables = [Executable("patool")]
|
|
||||||
except ImportError:
|
|
||||||
from distutils.core import Distribution
|
from distutils.core import Distribution
|
||||||
executables = None
|
|
||||||
from distutils.command.install_lib import install_lib
|
from distutils.command.install_lib import install_lib
|
||||||
from distutils import util
|
from distutils import util
|
||||||
from distutils.file_util import write_file
|
from distutils.file_util import write_file
|
||||||
|
@ -53,28 +37,6 @@ AppVersion = "1.9"
|
||||||
MyName = "Bastian Kleineidam"
|
MyName = "Bastian Kleineidam"
|
||||||
MyEmail = "bastian.kleineidam@web.de"
|
MyEmail = "bastian.kleineidam@web.de"
|
||||||
|
|
||||||
py_excludes = ['doctest', 'unittest', 'Tkinter', '_ssl', 'pdb',
|
|
||||||
'email', 'calendar', 'ftplib', 'httplib', 'pickle', 'optparse','rfc822'
|
|
||||||
]
|
|
||||||
# py2exe options for Windows packaging
|
|
||||||
py2exe_options = dict(
|
|
||||||
packages=["encodings", 'patoolib.programs'],
|
|
||||||
excludes=py_excludes,
|
|
||||||
# silence py2exe error about not finding msvcp90.dll
|
|
||||||
dll_excludes=['MSVCP90.dll'],
|
|
||||||
compressed=1,
|
|
||||||
optimize=2,
|
|
||||||
)
|
|
||||||
# cx_Freeze for Linux RPM packaging
|
|
||||||
cxfreeze_options = dict(
|
|
||||||
packages=["encodings", 'patoolib.programs'],
|
|
||||||
excludes=py_excludes,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Microsoft Visual C++ runtime version (tested with Python 2.7.2)
|
|
||||||
MSVCP90Version = '9.0.21022.8'
|
|
||||||
MSVCP90Token = '1fc8b3b9a1e18e3b'
|
|
||||||
|
|
||||||
|
|
||||||
def normpath (path):
|
def normpath (path):
|
||||||
"""Norm a path name to platform specific notation."""
|
"""Norm a path name to platform specific notation."""
|
||||||
|
@ -128,31 +90,6 @@ def get_nt_platform_vars ():
|
||||||
return os.path.expandvars(progvar), architecture
|
return os.path.expandvars(progvar), architecture
|
||||||
|
|
||||||
|
|
||||||
def add_msvc_files (files):
|
|
||||||
"""Add needed MSVC++ runtime files. Only Version 9.0.21022.8 is tested
|
|
||||||
and can be downloaded here:
|
|
||||||
http://www.microsoft.com/en-us/download/details.aspx?id=29
|
|
||||||
"""
|
|
||||||
prog_dir, architecture = get_nt_platform_vars()
|
|
||||||
dirname = "Microsoft.VC90.CRT"
|
|
||||||
version = "%s_%s_x-ww_d08d0375" % (MSVCP90Token, MSVCP90Version)
|
|
||||||
args = (architecture, dirname, version)
|
|
||||||
path = r'C:\Windows\WinSxS\%s_%s_%s\*.*' % args
|
|
||||||
files.append((dirname, glob.glob(path)))
|
|
||||||
# Copy the manifest file into the build directory and rename it
|
|
||||||
# because it must have the same name as the directory.
|
|
||||||
path = r'C:\Windows\WinSxS\Manifests\%s_%s_%s.manifest' % args
|
|
||||||
target = os.path.join(os.getcwd(), 'build', '%s.manifest' % dirname)
|
|
||||||
shutil.copy(path, target)
|
|
||||||
files.append((dirname, [target]))
|
|
||||||
|
|
||||||
|
|
||||||
if 'py2exe' in sys.argv[1:]:
|
|
||||||
if not has_py2exe:
|
|
||||||
raise SystemExit("py2exe module could not be imported")
|
|
||||||
add_msvc_files(data_files)
|
|
||||||
|
|
||||||
|
|
||||||
class MyInstallLib (install_lib, object):
|
class MyInstallLib (install_lib, object):
|
||||||
"""Custom library installation."""
|
"""Custom library installation."""
|
||||||
|
|
||||||
|
@ -185,7 +122,7 @@ class MyInstallLib (install_lib, object):
|
||||||
else:
|
else:
|
||||||
val = getattr(cmd_obj, attr)
|
val = getattr(cmd_obj, attr)
|
||||||
if attr == 'install_data':
|
if attr == 'install_data':
|
||||||
cdir = os.path.join(val, "share", "dosage")
|
cdir = os.path.join(val, "share", AppName)
|
||||||
data.append('config_dir = %r' % cnormpath(cdir))
|
data.append('config_dir = %r' % cnormpath(cdir))
|
||||||
elif attr == 'install_lib':
|
elif attr == 'install_lib':
|
||||||
if cmd_obj.root:
|
if cmd_obj.root:
|
||||||
|
@ -259,161 +196,6 @@ class MyDistribution (Distribution, object):
|
||||||
"creating %s" % filename, self.verbose >= 1, self.dry_run)
|
"creating %s" % filename, self.verbose >= 1, self.dry_run)
|
||||||
|
|
||||||
|
|
||||||
class InnoScript:
|
|
||||||
"""Class to generate INNO script."""
|
|
||||||
|
|
||||||
def __init__(self, lib_dir, dist_dir, windows_exe_files=[],
|
|
||||||
console_exe_files=[], service_exe_files=[],
|
|
||||||
comserver_files=[], lib_files=[]):
|
|
||||||
"""Store INNO script infos."""
|
|
||||||
self.lib_dir = lib_dir
|
|
||||||
self.dist_dir = dist_dir
|
|
||||||
if not self.dist_dir[-1] in "\\/":
|
|
||||||
self.dist_dir += "\\"
|
|
||||||
self.name = AppName
|
|
||||||
self.version = AppVersion
|
|
||||||
self.windows_exe_files = [self.chop(p) for p in windows_exe_files]
|
|
||||||
self.console_exe_files = [self.chop(p) for p in console_exe_files]
|
|
||||||
self.service_exe_files = [self.chop(p) for p in service_exe_files]
|
|
||||||
self.comserver_files = [self.chop(p) for p in comserver_files]
|
|
||||||
self.lib_files = [self.chop(p) for p in lib_files]
|
|
||||||
self.icon = os.path.abspath(r'doc\icon\favicon.ico')
|
|
||||||
|
|
||||||
def chop(self, pathname):
|
|
||||||
"""Remove distribution directory from path name."""
|
|
||||||
assert pathname.startswith(self.dist_dir)
|
|
||||||
return pathname[len(self.dist_dir):]
|
|
||||||
|
|
||||||
def create(self, pathname=r"dist\omt.iss"):
|
|
||||||
"""Create Inno script."""
|
|
||||||
self.pathname = pathname
|
|
||||||
self.distfilebase = "%s-%s" % (self.name, self.version)
|
|
||||||
self.distfile = self.distfilebase + ".exe"
|
|
||||||
with open(self.pathname, "w") as fd:
|
|
||||||
self.write_inno_script(fd)
|
|
||||||
|
|
||||||
def write_inno_script (self, fd):
|
|
||||||
"""Write Inno script contents."""
|
|
||||||
print("; WARNING: This script has been created by py2exe. Changes to this script", file=fd)
|
|
||||||
print("; will be overwritten the next time py2exe is run!", file=fd)
|
|
||||||
print("[Setup]", file=fd)
|
|
||||||
print("AppName=%s" % self.name, file=fd)
|
|
||||||
print("AppVerName=%s %s" % (self.name, self.version), file=fd)
|
|
||||||
print("ChangesEnvironment=true", file=fd)
|
|
||||||
print(r"DefaultDirName={pf}\%s" % self.name, file=fd)
|
|
||||||
print("DefaultGroupName=%s" % self.name, file=fd)
|
|
||||||
print("OutputBaseFilename=%s" % self.distfilebase, file=fd)
|
|
||||||
print("OutputDir=..", file=fd)
|
|
||||||
print("SetupIconFile=%s" % self.icon, file=fd)
|
|
||||||
print(file=fd)
|
|
||||||
print("[Tasks]", file=fd)
|
|
||||||
print("Name: modifypath; Description: Add application directory to %PATH%;", file=fd)
|
|
||||||
print(file=fd)
|
|
||||||
# List of source files
|
|
||||||
files = self.windows_exe_files + \
|
|
||||||
self.console_exe_files + \
|
|
||||||
self.service_exe_files + \
|
|
||||||
self.comserver_files + \
|
|
||||||
self.lib_files
|
|
||||||
print('[Files]', file=fd)
|
|
||||||
for path in files:
|
|
||||||
print(r'Source: "%s"; DestDir: "{app}\%s"; Flags: ignoreversion' % (path, os.path.dirname(path)), file=fd)
|
|
||||||
# Set icon filename
|
|
||||||
print('[Icons]', file=fd)
|
|
||||||
for path in self.windows_exe_files:
|
|
||||||
print(r'Name: "{group}\%s"; Filename: "{app}\%s"' %
|
|
||||||
(self.name, path), file=fd)
|
|
||||||
for path in self.console_exe_files:
|
|
||||||
name = os.path.basename(path).capitalize()
|
|
||||||
print(r'Name: "{group}\%s help"; Filename: "cmd.exe"; Parameters: "/K %s --help";' % (name, path), file=fd)
|
|
||||||
print(r'Name: "{group}\Uninstall %s"; Filename: "{uninstallexe}"' % self.name, file=fd)
|
|
||||||
print(file=fd)
|
|
||||||
# Uninstall optional log files
|
|
||||||
print('[UninstallDelete]', file=fd)
|
|
||||||
for path in (self.windows_exe_files + self.console_exe_files):
|
|
||||||
exename = os.path.basename(path)
|
|
||||||
print(r'Type: files; Name: "{pf}\%s\%s.log"' % (self.name, exename), file=fd)
|
|
||||||
print(file=fd)
|
|
||||||
# Add app dir to PATH
|
|
||||||
print("[Code]", file=fd)
|
|
||||||
print("""\
|
|
||||||
const
|
|
||||||
ModPathName = 'modifypath';
|
|
||||||
ModPathType = 'user';
|
|
||||||
|
|
||||||
function ModPathDir(): TArrayOfString;
|
|
||||||
begin
|
|
||||||
setArrayLength(Result, 1)
|
|
||||||
Result[0] := ExpandConstant('{app}');
|
|
||||||
end;
|
|
||||||
#include "modpath.iss"
|
|
||||||
""", file=fd)
|
|
||||||
shutil.copy(r"scripts\modpath.iss", "dist")
|
|
||||||
|
|
||||||
|
|
||||||
def compile (self):
|
|
||||||
"""Compile Inno script with iscc.exe."""
|
|
||||||
progpath = get_nt_platform_vars()[0]
|
|
||||||
cmd = r'%s\Inno Setup 5\iscc.exe' % progpath
|
|
||||||
subprocess.check_call([cmd, self.pathname])
|
|
||||||
|
|
||||||
def sign (self):
|
|
||||||
"""Sign InnoSetup installer with local self-signed certificate."""
|
|
||||||
print("*** signing the inno setup installer ***")
|
|
||||||
pfxfile = r'scripts\%s.pfx' % self.name
|
|
||||||
if os.path.isfile(pfxfile):
|
|
||||||
path = get_windows_sdk_path()
|
|
||||||
signtool = os.path.join(path, "bin", "signtool.exe")
|
|
||||||
if os.path.isfile(signtool):
|
|
||||||
cmd = [signtool, 'sign', '/f', pfxfile, self.distfile]
|
|
||||||
subprocess.check_call(cmd)
|
|
||||||
else:
|
|
||||||
print("No signed installer: signtool.exe not found.")
|
|
||||||
else:
|
|
||||||
print("No signed installer: certificate %s not found." % pfxfile)
|
|
||||||
|
|
||||||
def get_windows_sdk_path():
|
|
||||||
"""Return path of Microsoft Windows SDK installation, or None if
|
|
||||||
not found."""
|
|
||||||
try:
|
|
||||||
import _winreg as winreg
|
|
||||||
except ImportError:
|
|
||||||
import winreg
|
|
||||||
sub_key = r"Software\Microsoft\Microsoft SDKs\Windows"
|
|
||||||
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, sub_key) as key:
|
|
||||||
name = "CurrentInstallFolder"
|
|
||||||
return winreg.QueryValueEx(key, name)[0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
from py2exe.build_exe import py2exe as py2exe_build
|
|
||||||
|
|
||||||
class MyPy2exe (py2exe_build):
|
|
||||||
"""First builds the exe file(s), then creates a Windows installer.
|
|
||||||
Needs InnoSetup to be installed."""
|
|
||||||
|
|
||||||
def run (self):
|
|
||||||
"""Generate py2exe installer."""
|
|
||||||
# First, let py2exe do it's work.
|
|
||||||
py2exe_build.run(self)
|
|
||||||
print("*** preparing the inno setup script ***")
|
|
||||||
lib_dir = self.lib_dir
|
|
||||||
dist_dir = self.dist_dir
|
|
||||||
# create the Installer, using the files py2exe has created.
|
|
||||||
script = InnoScript(lib_dir, dist_dir, self.windows_exe_files,
|
|
||||||
self.console_exe_files, self.service_exe_files,
|
|
||||||
self.comserver_files, self.lib_files)
|
|
||||||
print("*** creating the inno setup script ***")
|
|
||||||
script.create()
|
|
||||||
print("*** compiling the inno setup script ***")
|
|
||||||
script.compile()
|
|
||||||
script.sign()
|
|
||||||
except ImportError:
|
|
||||||
class MyPy2exe:
|
|
||||||
"""Dummy py2exe class."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
args = dict(
|
args = dict(
|
||||||
name = AppName,
|
name = AppName,
|
||||||
version = AppVersion,
|
version = AppVersion,
|
||||||
|
@ -462,13 +244,6 @@ installed.
|
||||||
distclass = MyDistribution,
|
distclass = MyDistribution,
|
||||||
cmdclass = {
|
cmdclass = {
|
||||||
'install_lib': MyInstallLib,
|
'install_lib': MyInstallLib,
|
||||||
'py2exe': MyPy2exe,
|
|
||||||
},
|
|
||||||
options = {
|
|
||||||
"py2exe": py2exe_options,
|
|
||||||
"build_exe": cxfreeze_options,
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if executables:
|
|
||||||
args["executables"] = executables
|
|
||||||
setup(**args)
|
setup(**args)
|
||||||
|
|
Loading…
Reference in New Issue