diff --git a/.travis.yml b/.travis.yml index 691fa5f30..318cfff21 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,11 +44,31 @@ matrix: - PYTHON_BUILD=true - BREW_BUNDLE=true - - name: "klayout python3.6.5_1 osx10.13" + - name: "klayout python3.6.6 osx10.13" os: osx osx_image: xcode9.4 # macOS 10.13 env: - - MATRIX_EVAL="brew update; brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/f2a764ef944b1080be64bd88dca9a1d80130c558/Formula/python.rb; brew switch python 3.6.5_1; shopt -s expand_aliases; alias python='python3'; alias pip='pip3';" + - MATRIX_EVAL="brew update; brew install sashkab/python/python36; brew link --force --overwrite python36; shopt -s expand_aliases; alias python='/usr/local/opt/python36/bin/python3.6'; alias pip='/usr/local/opt/python36/bin/pip3.6';" + - ARCHFLAGS="-std=c++11" + - PIP_UPDATE="1" + - PYTHON_BUILD=true + - BREW_BUNDLE=false + + - name: "klayout python3.5.6 osx10.13" + os: osx + osx_image: xcode9.4 # macOS 10.13 + env: + - MATRIX_EVAL="brew update; brew install sashkab/python/python35; brew link --force --overwrite python35; shopt -s expand_aliases; alias python='/usr/local/opt/python35/bin/python3.5'; alias pip='/usr/local/opt/python35/bin/pip3.5';" + - ARCHFLAGS="-std=c++11" + - PIP_UPDATE="1" + - PYTHON_BUILD=true + - BREW_BUNDLE=false + + - name: "klayout python3.4.9 osx10.13" + os: osx + osx_image: xcode9.4 # macOS 10.13 + env: + - MATRIX_EVAL="brew update; brew install sashkab/python/python34; brew link --force --overwrite python34; shopt -s expand_aliases; alias python='/usr/local/opt/python34/bin/python3.4'; alias pip='/usr/local/opt/python34/bin/pip3.4';" - ARCHFLAGS="-std=c++11" - PIP_UPDATE="1" - PYTHON_BUILD=true @@ -68,12 +88,26 @@ matrix: os: osx osx_image: xcode8 # macOS 10.11 env: - - MATRIX_EVAL="brew update; brew config; brew upgrade python;" + - MATRIX_EVAL="brew update; brew config; brew upgrade python; brew postinstall python; ls -l /usr/local/opt/python/libexec/bin/; shopt -s expand_aliases; alias python='/usr/local/opt/python/libexec/bin/python'; alias pip='/usr/local/opt/python/libexec/bin/pip';" - ARCHFLAGS="-std=c++11" - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - name: "klayout python3.7 package" + os: linux + dist: trusty # Ubuntu 14.04 + sudo: false + language: python + python: '3.7-dev' + env: + - MATRIX_EVAL="" + - PIP_UPDATE="1" + - PYTHON_BUILD=true + - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ + - name: "klayout python3.6 package" os: linux dist: trusty # Ubuntu 14.04 @@ -85,6 +119,8 @@ matrix: - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ - name: "klayout python2.7 package" os: linux @@ -97,6 +133,8 @@ matrix: - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ - name: "klayout python2.6 package" os: linux @@ -109,6 +147,8 @@ matrix: - PIP_UPDATE="0" # setuptools installed from last pip has syntax error on py 2.6 - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ - name: "klayout python3.3 package" os: linux @@ -121,6 +161,8 @@ matrix: - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ - name: "klayout python3.4 package" os: linux @@ -133,6 +175,8 @@ matrix: - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ - name: "klayout python3.5 package" os: linux @@ -145,6 +189,8 @@ matrix: - PIP_UPDATE="1" - PYTHON_BUILD=true - BREW_BUNDLE=false + - CC=clang + - CXX=clang++ # KLayout builds for mac # Python 3 @@ -211,7 +257,6 @@ matrix: before_install: - env - - rvm install ruby --latest - gem install dropbox-deployment - eval "${MATRIX_EVAL}" - if [ "$BREW_BUNDLE" = true ]; then @@ -232,6 +277,7 @@ script: python setup.py build; python setup.py bdist_wheel; python setup.py install; + python -m unittest testdata/pymod/import_db.py testdata/pymod/import_rdb.py testdata/pymod/import_tl.py; mkdir -p deploy/dist-pymod; cp -a dist/* deploy/dist-pymod/; python -c 'import klayout.db as db; print(dir(db))'; diff --git a/Changelog b/Changelog index ef0c3ce59..138367007 100644 --- a/Changelog +++ b/Changelog @@ -17,6 +17,9 @@ Performance issue with many layers with width >1 * Bugfix: https://github.com/klayoutmatthias/klayout/issues/176 Painting issue with texts +* Bugfix: https://github.com/klayoutmatthias/klayout/issues/185 + Hash values available as __hash__ standard method now + for Python * Bugfix: some potential memory corruption issues fixed During the efforts for making the code base compatible with MSVC, some potential candidates for memory corruption diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 000000000..8088e14c0 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,11 @@ +recursive-include src/tl/tl *.cc *.h +recursive-include src/db/db *.cc *.h +recursive-include src/gsi/gsi *.cc *.h +recursive-include src/rdb/rdb *.cc *.h +recursive-include src/pya/pya *.cc *.h +recursive-include src/pymod *.cc *.h +include src/plugins/*/db_plugin/*.cc +include src/plugins/*/*/db_plugin/*.cc +include src/plugins/*/db_plugin/*.h +include src/plugins/*/*/db_plugin/*.h +recursive-include src/plugins/common *.h diff --git a/setup.py b/setup.py index f7b4d2c44..78cc876c2 100644 --- a/setup.py +++ b/setup.py @@ -60,10 +60,10 @@ import os import platform import distutils.sysconfig as sysconfig from distutils.errors import CompileError +import distutils.command.build_ext import multiprocessing N_cores = multiprocessing.cpu_count() - # monkey-patch for parallel compilation # from https://stackoverflow.com/questions/11013851/speeding-up-build-process-with-distutils def parallelCCompile(self, sources, output_dir=None, macros=None, include_dirs=None, debug=0, extra_preargs=None, extra_postargs=None, depends=None): @@ -103,6 +103,24 @@ if sys.version_info[0] * 10 + sys.version_info[1] > 26: import distutils.ccompiler distutils.ccompiler.CCompiler.compile = parallelCCompile + +from distutils.command.build_ext import build_ext +_old_get_ext_filename = build_ext.get_ext_filename + + +def patched_get_ext_filename(self, ext_name): + r"""Convert the name of an extension (eg. "foo.bar") into the name + of the file from which it will be loaded (eg. "foo/bar.so", or + "foo\bar.pyd"). + """ + filename = _old_get_ext_filename(self, ext_name) + # Making sure this matches qmake's default extension .dylib, instead of .so + if platform.system() == "Darwin" and '_dbpi' in ext_name: + filename = filename.replace('.so', '.dylib') + return filename + + +distutils.command.build_ext.build_ext.get_ext_filename = patched_get_ext_filename # ---------------------------------------------------------------------------------------- @@ -121,6 +139,7 @@ class Config(object): self.build_platlib = build_cmd.build_platlib self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX") + if self.ext_suffix is None: self.ext_suffix = ".so" @@ -131,7 +150,10 @@ class Config(object): Returns the library name for a given module The library name is usually decorated (i.e. "tl" -> "tl.cpython-35m-x86_64-linux-gnu.so"). """ - return mod + self.ext_suffix + ext_suffix = self.ext_suffix + if platform.system() == "Darwin" and '_dbpi' in mod: + ext_suffix = ext_suffix.replace('.so', '.dylib') + return mod + ext_suffix def path_of(self, mod): """ @@ -149,7 +171,7 @@ class Config(object): return [] else: return ["-Wno-strict-aliasing", # Avoids many "type-punned pointer" warnings - "-std=c++0x", # because we use unordered_map/unordered_set + "-std=c++11", # because we use unordered_map/unordered_set ] def link_args(self, mod): @@ -191,7 +213,7 @@ class Config(object): """ Gets the version string """ - return "0.26.0.dev1" + return "0.26.0.dev4" config = Config() @@ -201,13 +223,15 @@ config = Config() _tl_path = os.path.join("src", "tl", "tl") -_tl_sources = glob.glob(os.path.join(_tl_path, "*.cc")) +_tl_sources = set(glob.glob(os.path.join(_tl_path, "*.cc"))) # Exclude sources which are compatible with Qt only -_tl_sources.remove(os.path.join(_tl_path, "tlHttpStreamQt.cc")) -_tl_sources.remove(os.path.join(_tl_path, "tlHttpStreamNoQt.cc")) -_tl_sources.remove(os.path.join(_tl_path, "tlFileSystemWatcher.cc")) -_tl_sources.remove(os.path.join(_tl_path, "tlDeferredExecutionQt.cc")) +# Caveat, in source distribution tarballs from pypi, these files will +# not exist. So we need an error-free discard method instead of list's remove. +_tl_sources.discard(os.path.join(_tl_path, "tlHttpStreamQt.cc")) +_tl_sources.discard(os.path.join(_tl_path, "tlHttpStreamNoQt.cc")) +_tl_sources.discard(os.path.join(_tl_path, "tlFileSystemWatcher.cc")) +_tl_sources.discard(os.path.join(_tl_path, "tlDeferredExecutionQt.cc")) _tl = Extension(config.root + '._tl', define_macros=config.macros() + [('MAKE_TL_LIBRARY', 1)], @@ -215,7 +239,7 @@ _tl = Extension(config.root + '._tl', libraries=['curl', 'expat'], extra_link_args=config.link_args('_tl'), extra_compile_args=config.compile_args('_tl'), - sources=_tl_sources) + sources=list(_tl_sources)) # ------------------------------------------------------------------ # _gsi dependency library @@ -249,10 +273,12 @@ _pya = Extension(config.root + '._pya', # _db dependency library _db_path = os.path.join("src", "db", "db") -_db_sources = glob.glob(os.path.join(_db_path, "*.cc")) +_db_sources = set(glob.glob(os.path.join(_db_path, "*.cc"))) # Not a real source: -_db_sources.remove(os.path.join(_db_path, "fonts.cc")) +# Caveat, in source distribution tarballs from pypi, these files will +# not exist. So we need an error-free discard method instead of list's remove. +_db_sources.discard(os.path.join(_db_path, "fonts.cc")) _db = Extension(config.root + '._db', define_macros=config.macros() + [('MAKE_DB_LIBRARY', 1)], @@ -261,7 +287,7 @@ _db = Extension(config.root + '._db', language='c++', extra_link_args=config.link_args('_db'), extra_compile_args=config.compile_args('_db'), - sources=_db_sources) + sources=list(_db_sources)) # ------------------------------------------------------------------ # _rdb dependency library diff --git a/src/pya/pya/pyaModule.cc b/src/pya/pya/pyaModule.cc index 9670a0736..ecfb35c3c 100644 --- a/src/pya/pya/pyaModule.cc +++ b/src/pya/pya/pyaModule.cc @@ -2687,6 +2687,12 @@ PythonModule::make_classes (const char *mod_name) add_python_doc (*c, mt, int (mid), tl::to_string (tr ("This method is also available as 'str(object)'"))); } + } else if (name == "hash" && m_first->compatible_with_num_args (0)) { + + // The hash method is also routed via the tp_hash implementation + alt_names.push_back ("__hash__"); + add_python_doc (*c, mt, int (mid), tl::to_string (tr ("This method is also available as 'hash(object)'"))); + } else if (name == "inspect" && m_first->compatible_with_num_args (0)) { // The str method is also routed via the tp_str implementation diff --git a/src/tl/unit_tests/unit_tests.pro b/src/tl/unit_tests/unit_tests.pro index 59776da0c..89d63ec6c 100644 --- a/src/tl/unit_tests/unit_tests.pro +++ b/src/tl/unit_tests/unit_tests.pro @@ -30,6 +30,7 @@ SOURCES = \ tlVariant.cc \ tlXMLParser.cc \ tlUri.cc \ + tlStreamTests.cc \ tlWebDAV.cc \ tlHttpStream.cc \ tlInt128Support.cc \ diff --git a/testdata/python/dbTransTest.py b/testdata/python/dbTransTest.py index 8f7643f45..b3423b01e 100644 --- a/testdata/python/dbTransTest.py +++ b/testdata/python/dbTransTest.py @@ -530,6 +530,14 @@ class DBTransTests(unittest.TestCase): self.assertEqual(t1.hash() == t3.hash(), False) self.assertEqual(t1.hash() == t4a.hash(), False) self.assertEqual(t1.hash() == t4b.hash(), False) + self.assertEqual(hash(t1) == hash(t2), True) + self.assertEqual(hash(t1) == hash(t3), False) + self.assertEqual(hash(t1) == hash(t4a), False) + self.assertEqual(hash(t1) == hash(t4b), False) + self.assertEqual(t1.__hash__() == t2.__hash__(), True) + self.assertEqual(t1.__hash__() == t3.__hash__(), False) + self.assertEqual(t1.__hash__() == t4a.__hash__(), False) + self.assertEqual(t1.__hash__() == t4b.__hash__(), False) # Transformations can't be used as hash keys currently if False: