diff --git a/README.md b/README.md index 77cc090..448215c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,41 @@ -There's a setup.py in pEpPythonAdapter/ +# pEpPythonAdapter -In Python we've setup tools, so it's: +## Build Insttructions -python3 setup.py build -python3 setup.py install +These build instructions should work on: + * Linux + * MacOS + * Windows + +### Build +To build against system wide pEp installation (libs/includes) +```bash +python3 setup.py build_ext +``` + +To build against a pEp installation in your home dir (libs/includes): +```bash +python3 setup.py build_ext --local +``` -That is a Python concept. You can find more about it here: +To build against a pEp installation in a custom installation root (libs/includes) +```bash +python3 setup.py build_ext --prefix= +``` + +### Install + +To install the extension module system wide, as root, run: +```bash +python3 setup.py install +``` -https://docs.python.org/3/extending/building.html#building-c-and-c-extensions-with-distutils +To install the extension module into you home dir +```bash +python3 setup.py install --user +``` +To install the extension module into a custom destination +```bash +python3 setup.py install --prefix= +``` diff --git a/setup.py b/setup.py index e9ccb1d..a970502 100644 --- a/setup.py +++ b/setup.py @@ -1,284 +1,236 @@ # -*- coding: utf-8 -*- - # This file is under GNU Affero General Public License 3.0 # see LICENSE.txt +# +# For more debugging, export DISTUTILS_DEBUG=True from __future__ import print_function import sys -from sys import argv import os from os import environ -from os.path import dirname, exists, join +from os.path import join +import platform from setuptools import setup, Extension from glob import glob -import sysconfig - -if sys.platform == 'winnt': - if sys.version_info[0] >= 3: - import winreg - else: - import _winreg as winreg - from setuptools.command.build_ext import build_ext +verboseLevel = 0 -if sys.version_info[0] < 3: - FileNotFoundError = EnvironmentError - -def find(fname, pathlist): - for path in pathlist: - cand_fname = join(path, fname) - if exists(cand_fname): - return path - raise FileNotFoundError(fname) - -def append_once(lst, element): - if element not in lst: - lst.append(element) - -def extend_once(lst, elements): - for element in elements: - if element not in lst: - lst.append(element) - -def getPythonLibver(): - g = sysconfig.get_config_vars() - sent = [] - for template in ( - '{cmd}{py_version_nodot}{abiflags}', - '{cmd}{py_version_nodot}', - '{cmd}{py_version_major}', - '{cmd}{py_version_major}{abiflags}', - '{cmd}' - ): - val = template.format( - cmd = g['PYTHON'], - py_version_nodot = g['py_version_nodot'], - py_version_major = sys.version_info[0], - abiflags = g.get('ABIFLAGS', ''), - ) - if val not in sent: - yield val - sent.append(val) - +def pEpLog(*msg): + if verboseLevel > 0: + import inspect + msgstr = '' + separator = ' ' + for m in msg: + msgstr += str(m) + msgstr += separator + func = inspect.currentframe().f_back.f_code + print(func.co_filename + " : " + func.co_name + " : " + msgstr) class BuildExtCommand(build_ext): - # default_pyver = next( getPythonLibver() ) user_options = build_ext.user_options + [ - # ('boost-python=', None, 'Boost Python version to use (e.g. \'python34\')'), - ('local', None, 'Use local pEp install in HOME/USERPROFILE'), - ('with-pEp-engine=', None, 'Path to pEp Engine source'), - ('with-pEp-libadapter=', None, 'Path to pEp C++ Adapter Library source'), - ('with-boost=', None, 'Path to Boost install prefix'), - ('with-asn1c-share=', None, 'Path to installed ASN1C share directory'), + ('local', None, 'Use local pEp install in HOME/USERPROFILE for libs/includes'), + ('prefix=', None, 'Use local pEp install in prefix for libs/includes'), ] - def windowsGetInstallLocation(): + def initialize_options(self): + pEpLog("called") + build_ext.initialize_options(self) + self.local = None != environ.get('PER_USER_DIRECTORY') + self.prefix = getattr(self, "prefix=", None) + + def windowsGetInstallLocation(self): + pEpLog("called") # Note: should be installed to 'C:\Program Files (x86)' while a 32-bit distro # TODO: Try desktop adapter location first, then COM server # FIXME: This is wrong, we should chase the COM server, not the Outlook Plugin (even if they're in the same place) - REG_PATH = "Software\\Microsoft\\Office\\Outlook\\Addins\\pEp" + reg_path = "Software\\Microsoft\\Office\\Outlook\\Addins\\pEp" + KeyName = 'FileName' regKey = None + pEpLog("Registry Lookup:", reg_path, KeyName) try: - regKey = winreg.OpenKey( - winreg.HKEY_LOCAL_MACHINE, REG_PATH, 0, winreg.KEY_READ) + regKey = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path, 0, winreg.KEY_READ) # Keys: Description, FileName, FriendlyName, LoadBehavior - com_server, regtype = winreg.QueryValueEx(regKey, 'FileName') + com_server, regtype = winreg.QueryValueEx(regKey, KeyName) winreg.CloseKey(regKey) except WindowsError: + pEpLog("Unknown Error") com_server = None finally: if winreg: winreg.CloseKey(regKey) # \\bin\\COM_Server.exe dirname = os.path.dirname - return dirname( dirname( com_server ) ) - - def initialize_options(self): - build_ext.initialize_options(self) - # self.boost_python = BuildExtCommand.default_pyver - self.local = None != environ.get('PER_USER_DIRECTORY') - self.with_pEp_engine = None - self.with_pEp_libadapter = None - self.with_boost = None - self.with_asn1c_share = None + ret = dirname(dirname(com_server)) + pEpLog("Value:", ret) + return ret + + def get_build_info_winnt(self): + home = environ.get('PER_USER_DIRECTORY') or environ.get('USERPROFILE') + sys_root = environ.get('SystemRoot') + profile_root = environ.get('AppData') + local_root = environ.get('LocalAppData') + inst_prefix = self.windowsGetInstallLocation() + sys_includes = [ + join(inst_prefix, 'include'), + join(profile_root, 'pEp', 'include'), + join(local_root, 'pEp', 'include'), + join(sys_root, 'pEp', 'include'), + ] + sys_libdirs = [ + join(inst_prefix, 'bin'), + join(profile_root, 'pEp', 'bin'), + join(local_root, 'pEp', 'bin'), + join(sys_root, 'pEp', 'bin'), + ] + libs = [ + 'pEpEngine', + 'boost_python37-mt', + 'boost_locale-mt' + ] + return (home, sys_includes, sys_libdirs, libs) + + def get_build_info_darwin(self): + home = environ.get('PER_USER_DIRECTORY') or environ.get('HOME') + sys_includes = [ + '/opt/local/include', + '/usr/local/include', + '/Library/Frameworks/PrettyEasyPrivacy.framework/Versions/A/include', + '/usr/include', + ] + sys_libdirs = [ + '/opt/local/lib', + '/usr/local/lib', + '/Library/Frameworks/PrettyEasyPrivacy.framework/Versions/A/lib', + '/usr/lib', + ] + libs = [ + 'pEpEngine', + 'boost_python37-mt', + 'boost_locale-mt' + ] + return (home, sys_includes, sys_libdirs, libs) + + def get_build_info_linux(self): + home = environ.get('PER_USER_DIRECTORY') or environ.get('HOME') + sys_includes = [ + '/usr/local/include', + '/usr/include', + ] + sys_libdirs = [ + '/usr/local/lib', + '/usr/lib', + '/usr/lib/{}-linux-gnu'.format(platform.machine()) + ] + libs = [ + 'pEpEngine', + 'boost_python3', + 'boost_locale' + ] + return (home, sys_includes, sys_libdirs, libs) def finalize_options(self): + pEpLog("called") build_ext.finalize_options(self) - # normalize paths given by user (expand tilde etc.) - for with_option in ('pEp_engine', 'pEp_libadapter', 'boost', 'asn1c_share'): - w_opt = 'with_' + with_option - w_val = getattr(self, w_opt) - if w_val != None: - setattr(self, w_opt, os.path.normpath(w_val)) - - if sys.platform == 'darwin': - HOME = environ.get('PER_USER_DIRECTORY') or environ.get('HOME') - PEPLIBNAME = 'libpEpEngine.dylib' - LIBPEPA = 'libpEpAdapter.a' - BOOSTLIBNAME = 'libboost_{boost_python:s}-mt.dylib' - SYS_INCLUDES = [ - '/opt/local/include', # we prefer MacPorts over Homebrew (but support both) - '/usr/local/include', - '/Library/Frameworks/PrettyEasyPrivacy.framework/Versions/A/include', - join(environ["HOME"], 'include'), - '/usr/include', - ] - SYS_SHARES = [ - '/opt/local/share', - '/usr/local/share', - '/Library/Frameworks/PrettyEasyPrivacy.framework/Versions/A/share', - join(environ["HOME"], 'share'), - '/usr/share', - ] - SYS_LIB_PREFIXES = [ - '/opt/local/lib', - '/usr/local/lib', - '/Library/Frameworks/PrettyEasyPrivacy.framework/Versions/A/lib', - join(environ["HOME"], 'lib'), - '/usr/lib', - ] - elif sys.platform == 'winnt': - HOME = environ.get('PER_USER_DIRECTORY') or environ.get('USERPROFILE') - SYS_ROOT = environ.get('SystemRoot') - PROFILE_ROOT = environ.get('AppData') - LOCAL_ROOT = environ.get('LocalAppData') - INST_PREFIX = windowsGetInstallLocation() - PEPLIBNAME = 'pEpEngine.dll' - LIBPEPA = 'libpEpAdapter.a' - BOOSTLIBNAME = 'boost_{boost_python:s}-mt.dll' - SYS_INCLUDES = [ - join(INST_PREFIX, 'include'), - join(PROFILE_ROOT, 'pEp', 'include'), - join(LOCAL_ROOT, 'pEp', 'include'), - join(SYS_ROOT, 'pEp', 'include'), - ] - SYS_SHARES = [ - join(INST_PREFIX, 'share'), - join(PROFILE_ROOT, 'pEp', 'share'), - join(LOCAL_ROOT, 'pEp', 'share'), - join(SYS_ROOT, 'pEp', 'share'), - ] - SYS_LIB_PREFIXES = [ - join(INST_PREFIX, 'bin'), - join(PROFILE_ROOT, 'pEp', 'bin'), - join(LOCAL_ROOT, 'pEp', 'bin'), - join(SYS_ROOT, 'pEp', 'bin'), - ] + pEpLog("verbose: ", self.verbose) + pEpLog("local: ", self.local) + pEpLog("prefix: ", self.prefix) + pEpLog("sys.platform: ", sys.platform) + + global verboseLevel + verboseLevel = self.verbose + + # get build information for platform + if sys.platform == 'winnt': + build_info = self.get_build_info_winnt() + elif sys.platform == 'darwin': + build_info = self.get_build_info_darwin() + elif sys.platform == 'linux': + build_info = self.get_build_info_linux() else: - HOME = environ.get('PER_USER_DIRECTORY') or environ.get('HOME') - PEPLIBNAME = 'pEpEngine.so' - LIBPEPA = 'libpEpAdapter.a' - BOOSTLIBNAME = 'boost_{boost_python:s}-mt.so' - SYS_INCLUDES = ['/usr/local/pEp/include', '/usr/local/include', '/usr/include'] - SYS_SHARES = ['/usr/local/pEp/share', '/usr/local/share', '/usr/share'] - SYS_LIB_PREFIXES = ['/usr/local/pEp/bin', '/usr/local/bin', '/usr/bin'] - - use_local_incl = (self.local or os.path.isfile( - join(HOME, 'include', 'pEp', 'pEpEngine.h')) ) - use_local_lib = (self.local or os.path.isfile( - join(HOME, 'lib', PEPLIBNAME)) ) - - INCLUDES = [ join(HOME, 'include') ] if use_local_incl else [] - INCLUDES.extend(SYS_INCLUDES) - SHARES = [ join(HOME, 'share') ] if use_local_incl else [] - SHARES.extend(SYS_SHARES) - LIBS = [ join(HOME, 'lib') ] if use_local_lib else [] - LIBS.extend(SYS_LIB_PREFIXES) - - if not self.with_pEp_engine: - ENGINE_INC = find( join('pEp', 'pEpEngine.h'), INCLUDES ) - ENGINE_LIB = find( PEPLIBNAME, LIBS ) - else: - if exists( join(self.with_pEp_engine, 'include', 'pEp') ): - ENGINE_INC = find( join('pEp', 'pEpEngine.h'), (join(self.with_pEp_engine, 'include'),) ) - ENGINE_LIB = find( PEPLIBNAME, (join(self.with_pEp_engine, 'lib'),) ) - else: - ENGINE_INC = find( 'pEpEngine.h', (join(self.with_pEp_engine, 'src'),) ) - ENGINE_LIB = find( PEPLIBNAME, (join(self.with_pEp_engine, 'src'),) ) - - if not self.with_pEp_libadapter: - LIBPEPA_INC = find( join('pEp', 'Adapter.hh'), INCLUDES ) - LIBPEPA_LIB = find( LIBPEPA, LIBS ) - else: - if exists( join(self.with_pEp_libadapter, 'include', 'pEp') ): - LIBPEPA_INC = find( join('pEp', 'Adapter.hh'), (join(self.with_pEp_libadapter, 'include'),) ) - LIBPEPA_LIB = find( LIBPEPA, (join(self.with_pEp_libadapter, 'lib'),) ) - else: - LIBPEPA_INC = find( 'Adapter.hh', (join(self.with_pEp_libadapter, 'src'),) ) - LIBPEPA_LIB = find( LIBPEPA, (join(self.with_pEp_libadapter, 'src'),) ) - - if not self.with_boost: - BOOST_INC = find( join('boost', 'system', 'config.hpp'), INCLUDES ) - BOOST_LIB = None - e_ = None - for py in getPythonLibver(): - f = BOOSTLIBNAME.format(boost_python=py) - try: - BOOST_LIB = find( f, LIBS ) - break - except Exception as e: - if self.verbose: - print("Not found: " + f) - e_ = e if e_ is None else e_ - if BOOST_LIB is None: - raise e_ - else: - raise NotImplementedError("Building from Boost source not implemented yet") # FIXME - # BOOST_INC = find( join('boost', 'system', 'config.hpp'), (self.with_boost,) ) - # BOOST_LIB = find( BOOSTLIBNAME, (self.with_boost,) ) - - if not self.with_asn1c_share: - ASN1C_INC = find( join('asn1c', 'asn_system.h'), SHARES ) - else: - ASN1C_INC = find( join('asn1c', 'asn_system.h'), (self.with_asn1c_share,) ) - - if self.verbose: - print('ENGINE_INC=%s' % ENGINE_INC) - print('ENGINE_LIB=%s' % ENGINE_LIB) - print('LIBPEPA_INC=%s' % LIBPEPA_INC) - print('LIBPEPA_LIB=%s' % LIBPEPA_LIB) - print('BOOST_INC=%s' % BOOST_INC) - print('BOOST_LIB=%s' % BOOST_LIB) - print('ASN1C_INC=%s' % ASN1C_INC) + pEpLog("Platform not supported:" + sys.platform) + exit() + + (home, sys_includes, sys_libdirs, libs) = build_info + + # Build the Includes -I and Library paths -L + # Start empty + includes = [] + libdirs = [] + + # Append home-dir + if self.local: + pEpLog("local mode") + home_include=[ join(home, 'include') ] + home_libdirs=[ join(home, 'lib') ] + includes += home_include + libdirs += home_libdirs + + # Append prefix-dir + if self.prefix: + pEpLog("using prefix=",self.prefix) + prefix_include=[ join(self.prefix, 'include') ] + prefix_libdirs=[ join(self.prefix, 'lib') ] + includes += prefix_include + libdirs += prefix_libdirs + + # Append default system dirs + includes += sys_includes + libdirs += sys_libdirs + + # Compile flags + compile_flags = ['-std=c++14'] + if self.debug: + pEpLog("debug mode") + compile_flags += ['-O0', '-g', '-UNDEBUG'] + # Apply the build information global module_pEp - extend_once( module_pEp.include_dirs, [ENGINE_INC, LIBPEPA_INC, BOOST_INC, ASN1C_INC] ) - extend_once( module_pEp.library_dirs, [ENGINE_LIB, LIBPEPA_LIB, BOOST_LIB] ) - extend_once( module_pEp.libraries, ['pEpEngine', 'boost_python37-mt', 'boost_locale-mt'] ) + module_pEp.include_dirs = includes + module_pEp.library_dirs = libdirs + module_pEp.libraries = libs + module_pEp.extra_compile_args = compile_flags - if self.debug: - module_pEp.extra_compile_args = ['-O0', '-g', '-UNDEBUG', '-std=c++14'] - else: - module_pEp.extra_compile_args = ['-std=c++14'] # FIXME + pEpLog("Include Dirs:", module_pEp.include_dirs) + pEpLog("Libs Dirs:", module_pEp.library_dirs) + pEpLog("Libraries:", module_pEp.libraries) + pEpLog("Compile flags:", module_pEp.extra_compile_args) def run(self): build_ext.run(self) -# module_pEp global is referenced in BuildExtCommand -module_pEp = Extension('pEp', - sources = glob('src/*.cc'), - libraries = ['pEpEngine'], - # extra_compile_args = compile_args, - # include_dirs = [ENGINE_INC, BOOST_INC, ASN1C_INC], - # library_dirs = [ENGINE_LIB, BOOST_LIB], - ) +if sys.platform == 'winnt': + if sys.version_info[0] >= 3: + import winreg + else: + import _winreg as winreg +if sys.version_info[0] < 3: + FileNotFoundError = EnvironmentError + + +module_pEp = Extension( + 'pEp', + sources = glob('src/*.cc'), +) +# "MAIN" Function setup( name='pEp', version='2.0', description='p≡p for Python', author="Volker Birk", author_email="vb@pep-project.org", - ext_modules=[module_pEp,], + maintainer="Heck", + maintainer_email="heck@pep.foundation", + ext_modules=[module_pEp], cmdclass={ 'build_ext': BuildExtCommand, },