#!/usr/bin/env python3 from __future__ import print_function import sys import os import platform import errno import re import shutil import hashlib from distutils.core import setup from distutils.extension import Extension from awlsim.common.version import VERSION_STRING try: import py2exe except ImportError as e: py2exe = None try: if py2exe and "py2exe" in sys.argv: raise ImportError from cx_Freeze import setup, Executable cx_Freeze = True except ImportError as e: cx_Freeze = False def makedirs(path, mode=0o755): try: os.makedirs(path, mode) except OSError as e: if e.errno == errno.EEXIST: return raise def hashFile(path): if sys.version_info[0] < 3: ExpectedException = IOError else: ExpectedException = FileNotFoundError try: return hashlib.sha1(open(path, "rb").read()).hexdigest() except ExpectedException as e: return None def __fileopIfChanged(fromFile, toFile, fileop): toFileHash = hashFile(toFile) if toFileHash is not None: fromFileHash = hashFile(fromFile) if toFileHash == fromFileHash: return False makedirs(os.path.dirname(toFile)) fileop(fromFile, toFile) return True def copyIfChanged(fromFile, toFile): return __fileopIfChanged(fromFile, toFile, shutil.copy2) def moveIfChanged(fromFile, toFile): return __fileopIfChanged(fromFile, toFile, os.rename) def makeDummyFile(path): if os.path.isfile(path): return print("creating dummy file '%s'" % path) makedirs(os.path.dirname(path)) fd = open(path, "w") fd.write("\n") fd.close() def pyCythonPatch(fromFile, toFile, basicOnly=False): print("cython-patch: patching file '%s' to '%s'" %\ (fromFile, toFile)) tmpFile = toFile + ".TMP" makedirs(os.path.dirname(tmpFile)) infd = open(fromFile, "r") outfd = open(tmpFile, "w") for line in infd.readlines(): stripLine = line.strip() if stripLine.endswith("# Is Cython installed?") buildCython = False if buildCython: try: cythonParallelBuild = int(os.getenv("CYTHONPARALLEL", "0")) except ValueError: cythonParallelBuild = 0 cythonParallelBuild = bool(cythonParallelBuild == 1 or\ cythonParallelBuild == sys.version_info[0]) if sys.version_info[0] < 3: # Cython2 build libraries need method pickling # for parallel build. def unpickle_method(fname, obj, cls): # Ignore MRO. We don't seem to inherit methods. return cls.__dict__[fname].__get__(obj, cls) def pickle_method(m): return unpickle_method, (m.im_func.__name__, m.im_self, m.im_class) import copy_reg, types copy_reg.pickle(types.MethodType, pickle_method, unpickle_method) def cyBuildWrapper(arg): # This function does the same thing as the for-loop-body # inside of Cython's build_ext.build_extensions() method. # It is called via multiprocessing to build extensions # in parallel. # Note that this might break, if Cython's build_extensions() # is changed and stuff is added to its for loop. Meh. self, ext = arg ext.sources = self.cython_sources(ext.sources, ext) self.build_extension(ext) # Override Cython's build_ext class. class MyCythonBuildExt(Cython_build_ext): def build_extension(self, ext): assert(not ext.name.endswith("__init__")) Cython_build_ext.build_extension(self, ext) def build_extensions(self): # First patch the files, the run the build patchCythonModules(self.build_lib) if cythonParallelBuild: # Run the parallel build, yay. self.check_extensions_list(self.extensions) from multiprocessing.pool import Pool Pool().map(cyBuildWrapper, ((self, ext) for ext in self.extensions)) else: # Run the normal non-parallel build. Cython_build_ext.build_extensions(self) cmdclass["build_ext"] = MyCythonBuildExt registerCythonModules() # Workaround for mbcs codec bug in distutils # http://bugs.python.org/issue10945 import codecs try: codecs.lookup("mbcs") except LookupError: codecs.register(lambda name: codecs.lookup("ascii") if name == "mbcs" else None) freezeExecutables = [ ("awlsim-gui", None), ("awlsim-client", None), ("awlsim-server", None), ("awlsim-symtab", None), ("awlsim-test", None), ("awlsim/coreserver/server.py", "awlsim-server-module"), ] if py2exe: extraKeywords["console"] = [ s for s, e in freezeExecutables ] if cx_Freeze: executables = [] for script, exe in freezeExecutables: if exe: if os.name.lower() in ("nt", "ce"): exe += ".exe" executables.append(Executable(script = script, targetName = exe)) else: executables.append(Executable(script = script)) extraKeywords["executables"] = executables extraKeywords["options"] = { "build_exe" : { "packages" : [ "awlsimhw_debug", "awlsimhw_dummy", "awlsim.library.iec", ], } } setup( name = "awlsim", version = VERSION_STRING, description = "S7 AWL/STL Soft-PLC", license = "GNU General Public License v2 or later", author = "Michael Buesch", author_email = "m@bues.ch", url = "https://bues.ch/h/awlsim", packages = [ "awlsim", "awlsim/common", "awlsim/core", "awlsim/core/instructions", "awlsim/core/systemblocks", "awlsim/coreclient", "awlsim/coreserver", "awlsim/gui", "awlsim/gui/icons", "awlsim/library", "awlsim/library/iec", "awlsimhw_debug", "awlsimhw_dummy", "awlsimhw_linuxcnc", "awlsimhw_pyprofibus", "awlsimhw_rpigpio", "libpilc", ], package_dir = { "libpilc" : "pilc/libpilc", }, scripts = [ "awlsim-gui", "awlsim-client", "awlsim-server", "awlsim-symtab", "awlsim-test", "awlsim-linuxcnc-hal", "awlsim-win.bat", "pilc/pilc-hat-conf", ], cmdclass = cmdclass, ext_modules = ext_modules, keywords = [ "AWL", "STL", "SPS", "PLC", "Step 7", "Siemens", "emulator", "simulator", "PROFIBUS", "LinuxCNC", ], classifiers = [ "Development Status :: 4 - Beta", "Environment :: Console", "Environment :: Win32 (MS Windows)", "Environment :: X11 Applications", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Information Technology", "Intended Audience :: Manufacturing", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Programming Language :: Cython", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: Jython", "Programming Language :: Python :: Implementation :: IronPython", "Topic :: Education", "Topic :: Home Automation", "Topic :: Scientific/Engineering", "Topic :: Software Development", "Topic :: Software Development :: Interpreters", "Topic :: Software Development :: Embedded Systems", "Topic :: Software Development :: Testing", "Topic :: System :: Emulators", ], long_description = open("README.txt").read(), **extraKeywords )