pax_global_header00006660000000000000000000000064136547435370014533gustar00rootroot0000000000000052 comment=b46dd85f5169cd3a616ea523b2a2bc0df61f23c7 scikit-build-0.11.1/000077500000000000000000000000001365474353700141765ustar00rootroot00000000000000scikit-build-0.11.1/.circleci/000077500000000000000000000000001365474353700160315ustar00rootroot00000000000000scikit-build-0.11.1/.circleci/config.yml000066400000000000000000000025511365474353700200240ustar00rootroot00000000000000 initialize-venv: &initialize-venv name: Initialize Virtual Environment command: | python -m virtualenv ../venv || python -m venv ../venv . ../venv/bin/activate ci-steps: &ci-steps steps: - checkout - run: <<: *initialize-venv - run: name: Install dependencies command: | . ../venv/bin/activate pip install -U scikit-ci scikit-ci-addons ci_addons --install ../addons ci install - run: name: Flake8 command: | . ../venv/bin/activate ci before_build - run: name: Build command: | . ../venv/bin/activate ci build - run: name: Test command: | . ../venv/bin/activate ci test - run: name: Coverage command: | . ../venv/bin/activate ci after_test version: 2 jobs: python27: docker: - image: circleci/python:2.7.15-stretch <<: *ci-steps python35: docker: - image: circleci/python:3.5.7-stretch <<: *ci-steps python36: docker: - image: circleci/python:3.6.8-stretch <<: *ci-steps python37: docker: - image: circleci/python:3.7.2-stretch <<: *ci-steps workflows: version: 2 test-package-publish: jobs: - python27 - python35 - python36 - python37 scikit-build-0.11.1/.codecov.yml000066400000000000000000000004231365474353700164200ustar00rootroot00000000000000coverage: status: project: default: # Don't allow overall project coverage to be dropped more than 2% threshold: 2 patch: default: # 75% of the changed code must be covered by tests threshold: 25 only_pulls: true scikit-build-0.11.1/.coveragerc000066400000000000000000000001431365474353700163150ustar00rootroot00000000000000[run] branch = True source = skbuild omit = skbuild/_version.py [xml] output = tests/coverage.xml scikit-build-0.11.1/.flake8000066400000000000000000000005521365474353700153530ustar00rootroot00000000000000[flake8] max-line-length: 120 # Whether to display the pep8 instructions on failure (can be quite verbose) show-pep8: False # Whether to show source code for each failure show-source: True # Maximum cyclomatic complexity allowed max-complexity: 14 format: pylint exclude: .git,.idea,.eggs,__pycache__,.tox,docs/conf.py,_skbuild,build,docs/cmake.py,versioneer.py scikit-build-0.11.1/.gitattributes000066400000000000000000000010041365474353700170640ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp *.sln merge=union *.csproj merge=union *.vbproj merge=union *.fsproj merge=union *.dbproj merge=union # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain skbuild/_version.py export-subst scikit-build-0.11.1/.gitignore000066400000000000000000000007761365474353700162000ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.eggs *.egg-info dist build _skbuild eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .cache .pytest_cache/ .coverage .tox coverage.xml htmlcov # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject # Complexity output/*.html output/*/index.html # Sphinx docs/_build # IDE junk .idea/* *.swp .vscode/* # build output (testing) skbuild/_cmake_test_compile/* scikit-build-0.11.1/.pylintrc000066400000000000000000000330001365474353700160370ustar00rootroot00000000000000[MASTER] # Specify a configuration file. #rcfile= # Python code to execute, usually for sys.path manipulation such as # pygtk.require(). #init-hook= # Add files or directories to the blacklist. They should be base names, not # paths. ignore=CVS # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. ignore-patterns= # Pickle collected data for later comparisons. persistent=yes # List of plugins (as comma separated values of python modules names) to load, # usually to register additional checkers. load-plugins= # Use multiple processes to speed up Pylint. jobs=1 # Allow loading of arbitrary C extensions. Extensions are imported into the # active Python interpreter and may run arbitrary code. unsafe-load-any-extension=no # A comma-separated list of package or module names from where C extensions may # be loaded. Extensions are loading into the active Python interpreter and may # run arbitrary code extension-pkg-whitelist= # Allow optimization of some AST trees. This will activate a peephole AST # optimizer, which will apply various small optimizations. For instance, it can # be used to obtain the result of joining multiple strings with the addition # operator. Joining a lot of strings can lead to a maximum recursion error in # Pylint and this flag can prevent that. It has one side effect, the resulting # AST will be different than the one from reality. This option is deprecated # and it will be removed in Pylint 2.0. optimize-ast=no [MESSAGES CONTROL] # Only show warnings with the listed confidence levels. Leave empty to show # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED confidence= # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option # multiple time (only on the command line, not in the configuration file where # it should appear only once). See also the "--disable" option for examples. #enable= # Disable the message, report, category or checker with the given id(s). You # can either give multiple identifiers separated by comma (,) or put this # option multiple times (only on the command line, not in the configuration # file where it should appear only once).You can also use "--disable=all" to # disable everything first and then reenable specific checks. For example, if # you want to run only the similarities checker, you can use "--disable=all # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" disable=bad-continuation,invalid-name,too-few-public-methods,import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,filter-builtin-not-iterating,using-cmp-argument,useless-suppression,range-builtin-not-iterating,suppressed-message,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,hex-method,nonzero-method,map-builtin-not-iterating [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs # (visual studio) and html. You can also give a reporter class, eg # mypackage.mymodule.MyReporterClass. output-format=text # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". This option is deprecated # and it will be removed in Pylint 2.0. files-output=no # Tells whether to display a full report or only the messages reports=yes # Python expression which should return a note less than 10 (10 is the highest # note). You have access to the variables errors warning, statement which # respectively contain the number of errors / warnings messages and the total # number of statements analyzed. This is used by the global evaluation report # (RP0004). evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) # Template used to display messages. This is a python new-style format string # used to format the message information. See doc for all details #msg-template= [MISCELLANEOUS] # List of note tags to take in consideration, separated by a comma. notes=FIXME,XXX,TODO [FORMAT] # Maximum number of characters on a single line. max-line-length=120 # Regexp for a line that is allowed to be longer than the limit. ignore-long-lines=^\s*(# )??$ # Allow the body of an if to be on the same line as the test if there is no # else. single-line-if-stmt=no # List of optional constructs for which whitespace checking is disabled. `dict- # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. # `trailing-comma` allows a space between comma and closing bracket: (a, ). # `empty-line` allows space-only lines. no-space-check=trailing-comma,dict-separator # Maximum number of lines in a module max-module-lines=1000 # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 # tab). indent-string=' ' # Number of spaces of indent required inside a hanging or continued line. indent-after-paren=4 # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. expected-line-ending-format= [SPELLING] # Spelling dictionary name. Available dictionaries: none. To make it working # install python-enchant package. spelling-dict= # List of comma separated words that should not be checked. spelling-ignore-words= # A path to a file that contains private dictionary; one word per line. spelling-private-dict-file= # Tells whether to store unknown words to indicated private dictionary in # --spelling-private-dict-file option instead of raising a message. spelling-store-unknown-words=no [LOGGING] # Logging modules to check that the string format arguments are in logging # function parameter format logging-modules=logging [SIMILARITIES] # Minimum lines number of a similarity. min-similarity-lines=4 # Ignore comments when computing similarities. ignore-comments=yes # Ignore docstrings when computing similarities. ignore-docstrings=yes # Ignore imports when computing similarities. ignore-imports=no [TYPECHECK] # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). ignore-mixin-members=yes # List of module names for which member attributes should not be checked # (useful for modules/projects where namespaces are manipulated during runtime # and thus existing member attributes cannot be deduced by static analysis. It # supports qualified module names, as well as Unix pattern matching. ignored-modules=distutils # List of class names for which member attributes should not be checked (useful # for classes with dynamically set attributes). This supports the use of # qualified names. ignored-classes=optparse.Values,thread._local,_thread._local # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. generated-members= # List of decorators that produce context managers, such as # contextlib.contextmanager. Add to this list to register other decorators that # produce valid context managers. contextmanager-decorators=contextlib.contextmanager [VARIABLES] # Tells whether we should check for unused import in __init__ files. init-import=no # A regular expression matching the name of dummy variables (i.e. expectedly # not used). dummy-variables-rgx=(_+[a-zA-Z0-9]*?$)|dummy # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. additional-builtins= # List of strings which can identify a callback function by name. A callback # name must start or end with one of those strings. callbacks=cb_,_cb # List of qualified module names which can have objects that can redefine # builtins. redefining-builtins-modules=six.moves,future.builtins [BASIC] # Good variable names which should always be accepted, separated by a comma good-names=i,j,k,ex,Run,_ # Bad variable names which should always be refused, separated by a comma bad-names=foo,bar,baz,toto,tutu,tata # Colon-delimited sets of names that determine each other's naming style when # the name regexes allow several styles. name-group= # Include a hint for the correct naming format with invalid-name include-naming-hint=no # List of decorators that produce properties, such as abc.abstractproperty. Add # to this list to register other decorators that produce valid properties. property-classes=abc.abstractproperty # Regular expression matching correct function names function-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for function names function-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct variable names variable-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for variable names variable-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct constant names const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Naming hint for constant names const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ # Regular expression matching correct attribute names attr-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for attribute names attr-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct argument names argument-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for argument names argument-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression matching correct class attribute names class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Naming hint for class attribute names class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ # Regular expression matching correct inline iteration names inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ # Naming hint for inline iteration names inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ # Regular expression matching correct class names class-rgx=[A-Z_][a-zA-Z0-9]+$ # Naming hint for class names class-name-hint=[A-Z_][a-zA-Z0-9]+$ # Regular expression matching correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Naming hint for module names module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ # Regular expression matching correct method names method-rgx=[a-z_][a-z0-9_]{2,30}$ # Naming hint for method names method-name-hint=[a-z_][a-z0-9_]{2,30}$ # Regular expression which should only match function or class names that do # not require a docstring. no-docstring-rgx=^_ # Minimum line length for functions/classes that require docstrings, shorter # ones are exempt. docstring-min-length=-1 [ELIF] # Maximum number of nested blocks for function / method body max-nested-blocks=5 [DESIGN] # Maximum number of arguments for function / method max-args=6 # Argument names that match this expression will be ignored. Default to name # with leading underscore ignored-argument-names=_.* # Maximum number of locals for function / method body max-locals=15 # Maximum number of return / yield for function / method body max-returns=6 # Maximum number of branch for function / method body max-branches=12 # Maximum number of statements in function / method body max-statements=50 # Maximum number of parents for a class (see R0901). max-parents=7 # Maximum number of attributes for a class (see R0902). max-attributes=7 # Minimum number of public methods for a class (see R0903). min-public-methods=2 # Maximum number of public methods for a class (see R0904). max-public-methods=20 # Maximum number of boolean expressions in a if statement max-bool-expr=5 [CLASSES] # List of method names used to declare (i.e. assign) instance attributes. defining-attr-methods=__init__,__new__,setUp # List of valid names for the first argument in a class method. valid-classmethod-first-arg=cls # List of valid names for the first argument in a metaclass class method. valid-metaclass-classmethod-first-arg=mcs # List of member names, which should be excluded from the protected access # warning. exclude-protected=_asdict,_fields,_replace,_source,_make [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules=regsub,TERMIOS,Bastion,rexec # Create a graph of every (i.e. internal and external) dependencies in the # given file (report RP0402 must not be disabled) import-graph= # Create a graph of external dependencies in the given file (report RP0402 must # not be disabled) ext-import-graph= # Create a graph of internal dependencies in the given file (report RP0402 must # not be disabled) int-import-graph= # Force import order to recognize a module as part of the standard # compatibility libraries. known-standard-library= # Force import order to recognize a module as part of a third party library. known-third-party=enchant # Analyse import fallback blocks. This can be used to support both Python 2 and # 3 compatible code, which means that the block might have code that exists # only in one or another interpreter, leading to false positives when analysed. analyse-fallback-blocks=no [EXCEPTIONS] # Exceptions that will emit a warning when being caught. Defaults to # "Exception" overgeneral-exceptions=Exception scikit-build-0.11.1/.travis.yml000066400000000000000000000020271365474353700163100ustar00rootroot00000000000000# Config file for automatic testing at travis-ci.org branches: only: - master language: python matrix: include: - os: osx language: generic env: - PYTHON_VERSION=3.7.0 - os: osx language: generic env: - PYTHON_VERSION=3.6.5 - os: osx language: generic env: - PYTHON_VERSION=3.5.5 - os: osx language: generic env: - PYTHON_VERSION=2.7.15 cache: directories: - $HOME/.pyenv/versions/3.7.0 - $HOME/.pyenv/versions/3.6.5 - $HOME/.pyenv/versions/3.5.5 - $HOME/.pyenv/versions/3.4.8 - $HOME/.pyenv/versions/2.7.15 - $HOME/downloads before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then mkdir $HOME/bin; ln -s $(which pip2) $HOME/bin/pip; ln -s $(which python2) $HOME/bin/python; fi - python -m pip install --disable-pip-version-check --upgrade pip - pip install -U scikit-ci scikit-ci-addons - ci_addons --install ../addons install: - ci install script: - ci test after_success: - ci after_test scikit-build-0.11.1/AUTHORS.rst000066400000000000000000000002011365474353700160460ustar00rootroot00000000000000======= Credits ======= Please see the GitHub project page at https://github.com/scikit-build/scikit-build/graphs/contributors scikit-build-0.11.1/CHANGES.rst000066400000000000000000000711551365474353700160110ustar00rootroot00000000000000============= Release Notes ============= This is the list of changes to scikit-build between each release. For full details, see the commit logs at http://github.com/scikit-build/scikit-build Scikit-build 0.11.1 =================== Bug fixes --------- * Support using scikit-build with conan where ``distro<1.2.0`` is required. Thanks :user:`AntoinePrv` and :user:`Chrismarsh` for reporting issue :issue:`472` and :issue:`488`. Documentation ------------- * Fix link in `Conda: Step-by-step` release guide available in :doc:`/make_a_release` section. Scikit-build 0.11.0 =================== New Features ------------ * Add a hook to process the cmake install manifest building the wheel. The hook function can be specified as an argument to the `setup()` function. This can be used e.g. to prevent installing cmake configuration files, headers, or static libraries with the wheel. Thanks :user:`SylvainCorlay` for the contribution. See :issue:`473`. * Add support for passing :ref:`CMake configure options ` like `-DFOO:STRING:bar` as global `setuptools` or `pip` options. * Add support for building project using PyPy or PyPy3. See https://pypy.org See :issue:`407`. * Add support for OS/400 (now known as IBM i). Thanks :user:`jwoehr` for the contribution. See :issue:`444`. * Display CMake command used to configure the project. Thanks :user:`native-api` for the contribution. See :issue:`443`. * CMake modules: * Improve CMake module :doc:`/cmake-modules/F2PY` adding `add_f2py_target()` CMake function allowing to generate `*-f2pywrappers.f` and `*module.c` files from `*.pyf` files. Thanks :user:`xoviat` for the contribution. * Update CMake module :doc:`/cmake-modules/PythonExtensions` adding `add_python_library()` and `add_python_extension()`. Thanks :user:`xoviat` for the contribution. Bug fixes --------- * Fix python 2.7 installation ensuring setuptools < 45 is required. See :issue:`478`. * Fix unclosed file resource in :meth:`skbuild.cmaker.CMaker.check_for_bad_installs`. Thanks :user:`Nic30` for the suggestion. See :issue:`429`. * Update CMake module :doc:`/cmake-modules/PythonExtensions`: * Ensure correct suffix is used for compiled python module on windows. See :issue:`383`. * Fix warning using `EXT_SUFFIX` config variable instead of deprecated `SO` variable. See :issue:`381`. * Honor the `MACOSX_DEPLOYMENT_TARGET` environment variable if it is defined on macOS. Thanks :user:`certik` for the contribution. See :issue:`441`. * Fix CMake module :doc:`/cmake-modules/F2PY` to ensure the `f2py` executable specific to the python version being used is found. See :issue:`449`. Thanks :user:`bnavigator` for the contribution. * Replace `platform.linux_distribution()` which was removed in Python 3.8 by a call to `distro.id()`. This adds the `distro` package as dependency. See :issue:`458`. Thanks :user:`bnavigator` for the contribution. Documentation ------------- * Add :doc:`/notes` section to the `For maintainers` top-level category that includes a comparison between `sysconfig` and `distutils.sysconfig` modules. * Remove obsolete comment in `cmaker.py`. See :issue:`439`. Thanks :user:`isuruf` Tests ----- * Update :func:`initialize_git_repo_and_commit` to prevent signing message on system with commit signing enabled globally. Scikit-build 0.10.0 =================== New Features ------------ * Improve message displayed when discovering a working environment for building projects. For example, instead of displaying ``-- Trying "Ninja" generator``, it now displays a message like ``-- Trying "Ninja (Visual Studio 15 2017 Win64 v140)" generator``. Bug fixes --------- * Checking generator candidates can now handle handle paths and binaries with spaces, so that ``setup.py --cmake-executable "C:/Program Files (x86)/cmake/cmake.exe"`` works as expected. Contributed by :user:`jokva`. See :issue:`400`. * Fix sdist command to ensure symlinks in original source tree are maintained. Contributed by :user:`anibali`. See :issue:`401`. * Ensure use of `bdist_egg` or `bdist_rpm` commands trigger build using cmake. * Fix default value returned by :func:`skbuild.constants.skbuild_plat_name()` on macOS. See :issue:`417`. Internal API ------------ * Add :meth:`skbuild.platforms.windows.find_visual_studio`. Documentation ------------- * Fix typo in example associated with :doc:`/cmake-modules/PythonExtensions`. Thanks :user:`eirrgang` for the contribution. * Update :doc:`/make_a_release` section to include `Conda: Step-by-step` release guide. Tests ----- * Introduce ``check_sdist_content()`` and fix tests that are checking content of sdist to account for changes introduced in Python 3.8 and backported to python 2.7, 3.6 and 3.7. The changes introduced in `python/cpython#9419 `_ adds directory entries to ZIP files created by distutils. Thanks :user:`anibali` for the contribution. See :issue:`404`. * Fix ``check_wheel_content()`` to consider changes in ``0.33.1 < wheel.__version__ < 0.33.4`` where directory entries are included when building wheel. See _`pypa/wheel#294 `. * Fix reporting of ``AssertionError`` raised in ``check_wheel_content()`` function by relocating the source code into a dedicated module ``tests.pytest_helpers`` and by adding a ``conftest.py`` configuration file registering it for pytest assertion rewriting. See https://docs.pytest.org/en/latest/writing_plugins.html#assertion-rewriting and :issue:`403`. * Fix ``test_generator_selection`` when building with "Visual C++ for Python 2.7" installed for all users. This addresses failure associated with ``win_c_compilervs2008cxx_compilervs2008python2.7`` when running test in `scikit-build-feedstock `_ where "Visual C++ for Python 2.7" is installed using (`vcpython27 `_ chocolatey package. * Continuous Integration * Add support for Azure Pipelines for Python 3.7 32-bit and 64-bit * AppVeyor: Disable test for Python 3.7 32-bit and 64-bit. * CircleCI: Update version of docker images from jessie to stretch. This addresses issue `circleci/circleci-images#370 `_. * TravisCI: Remove obsolete Python 3.4 testing. It reached `end-of-life on March 18 2019 `_. Scikit-build 0.9.0 ================== New Features ------------ * Add support for building distutils based extensions associated with ``ext_modules`` setup keyword along side skbuild based extensions. This means using ``build_ext`` command (and associated ``--inplace`` argument) is supported. Thanks :user:`Erotemic` for the contribution. See :issue:`284`. Bug fixes --------- * Fix build of wheels if path includes spaces. See issue :issue:`375`. Thanks :user:`padraic-padraic` for the contribution. * Ensure wheel platform name is correctly set when providing custom ``CMAKE_OSX_DEPLOYMENT_TARGET`` and ``CMAKE_OSX_ARCHITECTURES`` values are provided. Thanks :user:`nonhermitian` for the contribution. See :issue:`377`. * Fix testing with recent version of pytest by updating the pytest-runner requirements expression in `setup.py`. Thanks :user:`mackelab` for the contribution. Scikit-build 0.8.1 ================== Bug fixes --------- * Fix ``bdist_wheel`` command to support ``wheel >= 0.32.0``. Thanks :user:`fbudin69500` for reporting issue :issue:`360`. Tests ----- * Fix ``test_distribution.py`` updating use of ``Path.files()`` and requiring ``path.py>=11.5.0``. Scikit-build 0.8.0 ================== New Features ------------ * Introduced :const:`skbuild.constants.CMAKE_DEFAULT_EXECUTABLE` to facilitate distribution of scikit-build in package manager like `Nixpkgs `_ where all paths to dependencies are hardcoded. Suggested by :user:`FRidh`. * Setup keywords: * If not already set, ``zip_safe`` option is set to ``False``. Suggested by :user:`blowekamp`. * Add support for ``--skip-generator-test`` when a generator is explicitly selected using ``--generator``. This allows to speed up overall build when the build environment is known. Bug fixes --------- * Fix support for building project with CMake source directory outside of the ``setup.py`` directory. See :issue:`335` fixed by :user:`massich`. * Fix reading of `.cmake` files having any character not available in `CP-1252 `_ (the default code page on windows). See :issue:`334` fixed by :user:`bgermann`. * Fix parsing of macOS specific arguments like ``--plat-name macosx-X.Y-x86_64`` and ``-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=X.Y`` and ensure that the ones specified as command line arguments override the default values or the one hard-coded in the ``cmake_args`` setup keyword. Thanks :user:`yonip` for the help addressing :issue:`342`. * Support case where relative directory set in `package_dir` has an ending slash. For example, specifying ``package_dir={'awesome': 'src/awesome/'},`` is now properly handled. * Fix support for isolated build environment ensuring the CMake project is reconfigured when ``pip install -e .`` is called multiple times. See :issue:`352`. Documentation ------------- * README: Update overall download count. * Add logo and update sphinx configuration. Thanks :user:`SteveJordanKW` for the design work. * Update :ref:`CMake installation ` section. Thanks :user:`thewtex`. * Add :ref:`support_isolated_build` section. * Add :ref:`optimized_incremental_build` section. * Update :ref:`usage documentation ` to specify that ``--universal`` and ``--python-tags`` have no effect. Thanks :user:`bgermann` for the suggestion. See :issue:`353`. * Simplify documentation merging `Extension Build System` section with the `Advanced Usage` section. Thanks :user:`thewtex` for the suggestion. Tests ----- * Add ``check_wheel_content`` utility function. * Skip `test_setup_requires_keyword_include_cmake` if running in conda test environment or if https://pypi.org is not reachable. Suggested by :user:`Luthaf`. * Continuous Integration * TravisCI: * Remove testing of linux now covered by CircleCI, add testing for Python 3.5, 3.6 and 3.7 on macOS. * Ensure system python uses latest version of pip * AppVeyor, CircleCI: Add testing for Python 3.7 * Remove uses of unneeded ``$`` command wrapper. scikit-build should already take care of setting up the expected environment. * Always install up-to-date `scikit-ci`_ and `scikit-ci-addons`_. * Simplify release process managing `versioning` with `python-versioneer `_ and update :ref:`making_a_release` documentation. Scikit-build 0.7.1 ================== Documentation ------------- * Fix description and classifier list in setup.py. * Fix link in README. Scikit-build 0.7.0 ================== New Features ------------ * Faster incremental build by re-configuring the project only if needed. This was achieved by (1) adding support to retrieve the environment mapping associated with the generator set in the ``CMakeCache.txt`` file, (2) introducing a :func:`CMake spec file ` storing the CMake version as well as the the CMake arguments and (3) re-configuring only if either the generator or the CMake specs change. Thanks :user:`xoviat` for the contribution. See :issue:`301`. * CMake modules: * CMake module :doc:`/cmake-modules/PythonExtensions`: Set symbol visibility to export only the module init function. This applies to GNU and MSVC compilers. Thanks :user:`xoviat`. See :issue:`299`. * Add CMake module :doc:`/cmake-modules/F2PY` useful to find the ``f2py`` executable for building Python extensions with Fortran. Thanks to :user:`xoviat` for moving forward with the integration. Concept for the module comes from the work of :user:`scopatz` done in `PyNE `_ project. See :issue:`273`. * Update CMake module :doc:`/cmake-modules/NumPy` setting variables ``NumPy_CONV_TEMPLATE_EXECUTABLE`` and ``NumPy_FROM_TEMPLATE_EXECUTABLE``. Thanks :user:`xoviat` for the contribution. See :issue:`278`. * Setup keywords: * Add support for :ref:`cmake_languages ` setup keyword. * Add support for ``include_package_data`` and ``exclude_package_data`` setup keywords as well as parsing of ``MANIFEST.in``. See :issue:`315`. Thanks :user:`reiver-dev` for reporting the issue. * Add support for ``cmake_minimum_required_version`` setup keyword. See :issue:`312`. Suggested by :user:`henryiii`. * Install cmake if found in ``setup_requires`` list. See :issue:`313`. Suggested by :user:`henryiii`. * Add support for ``--cmake-executable`` scikit-build command line option. Thanks :user:`henryborchers` for the suggestion. See :issue:`317`. * Use ``_skbuild/platform-X.Y`` instead of ``_skbuild`` to build package. This allows to have a different build directory for each python version. Thanks :user:`isuruf` for the suggestion and :user:`xoviat` for contributing the feature. See :issue:`283`. * Run cmake and ``develop`` command when command ``test`` is executed. Bug fixes --------- * Fix support of ``--hide-listing`` when building wheel. * CMake module :doc:`/cmake-modules/Cython`: Fix escaping of spaces associated with ``CYTHON_FLAGS`` when provided as command line arguments to the cython executable through CMake cache entries. See :issue:`265` fixed by :user:`neok-m4700`. * Ensure package data files specified in the ``setup()`` function using ``package_data`` keyword are packaged and installed. * Support specifying a default directory for all packages not already associated with one using syntax like ``package_dir={'':'src'}`` in ``setup.py``. Thanks :user:`benjaminjack` for reporting the issue. See :issue:`274`. * Improve ``--skip-cmake`` command line option support so that it can re-generate a source distribution or a python wheel without having to run cmake executable to re-configure and build. Thanks to :user:`jonwoodring` for reporting the issue on the `mailing list `_. * Set ``skbuild `` as wheel generator. See `PEP-0427 `_ and :issue:`191`. * Ensure ``MANIFEST.in`` is considered when generating source distribution. Thanks :user:`seanlis` for reporting the problem and providing an initial patch, and thanks :user:`henryiii` for implementing the corresponding test. See :issue:`260`. * Support generation of source distribution for git repository having submodules. This works only for version of git >= 2.11 supporting the ``--recurse-submodules`` option with ``ls-files`` command. Internal API ------------ * Add :meth:`skbuild.cmaker.get_cmake_version`. Python Support -------------- * Tests using Python 3.3.x were removed and support for this version of python is not guaranteed anymore. Support was removed following the deprecation warnings reported by version 0.31.0 of wheel package, these were causing the tests ``test_source_distribution`` and ``test_wheel`` to fail. Tests ----- * Speedup execution of tests that do not require any CMake language enabled. This is achieved by (1) introducing the test project ``hello-no-language``, (2) updating test utility functions ``execute_setup_py`` and ``project_setup_py_test`` to accept the optional parameter ``disable_languages_test`` allowing to skip unneeded compiler detection in test project used to verify that the selected CMake generator works as expected, and (3) updating relevant tests to use the new test project and parameters. Overall testing time on all continuous integration services was reduced: * AppVeyor: * from **~16 to ~7** minutes for 64 and 32-bit Python 2.7 tests done using Visual Studio Express 2008 * from more than **2 hours to ~50 minutes** for 64 and 32-bit Python 3.5 tests done using Visual Studio 2015. Improvement specific to Python 3.x were obtained by caching the results of slow calls to ``distutils.msvc9compiler.query_vcvarsall`` (for Python 3.3 and 3.4) and ``distutils._msvccompiler._get_vc_env`` (for Python 3.5 and above). These functions were called multiple times to create the list of :class:`skbuild.platform_specifics.windows.CMakeVisualStudioCommandLineGenerator` used in :class:`skbuild.platform_specifics.windows.WindowsPlatform`. * CircleCI: from **~7 to ~5** minutes. * TravisCI: from **~21 to ~10** minutes. * Update maximum line length specified in flake8 settings from 80 to 120 characters. * Add ``prepend_sys_path`` utility function. * Ensure that the project directory is prepended to ``sys.path`` when executing test building sample project with the help of ``execute_setup_py`` function. * Add codecov config file for better defaults and prevent associated Pull Request checks from reporting failure when coverage only slightly changes. Documentation ------------- * Improve internal API documentation: * :mod:`skbuild.platform_specifics.windows` * :mod:`skbuild.command` * :mod:`skbuild.command.generate_source_manifest` * :mod:`skbuild.utils` * Split usage documentation into a `Basic Usage` and `Advanced Usage` sections. Cleanups -------- * Fix miscellaneous pylint warnings. Scikit-build 0.6.1 ================== Bug fixes --------- * Ensure CMake arguments passed to scikit-build and starting with ``-DCMAKE_*`` are passed to the test project allowing to determine which generator to use. For example, this ensures that arguments like ``-DCMAKE_MAKE_PROGRAM:FILEPATH=/path/to/program`` are passed. See :issue:`256`. Documentation ------------- * Update :doc:`/make_a_release` section including instructions to update ``README.rst`` with up-to-date pypi download statistics based on Google big table. Scikit-build 0.6.0 ================== New features ------------ * Improve ``py_modules`` support: Python modules generated by CMake are now properly included in binary distribution. * Improve developer mode support for ``py_modules`` generated by CMake. Bug fixes --------- * Do not implicitly install python modules when the beginning of their name match a package explicitly listed. For example, if a project has a package ``foo/__init__.py`` and a module ``fooConfig.py``, and only package ``foo`` was listed in ``setup.py``, ``fooConfig.py`` is not installed anymore. * CMake module :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup`: Fix the caching of *dynamic lookup* variables. See :issue:`240` fixed by :user:`blowekamp`. Requirements ------------ * wheel: As suggested by :user:`thewtex`, unpinning version of the package by requiring ``>=0.29.0`` instead of ``==0.29.0`` will avoid uninstalling a newer version of wheel package on up-to-date system. Documentation ------------- * Add a command line :ref:`CMake Options ` section to :doc:`Usage <\usage>`. * Fix :ref:`table ` listing *Visual Studio IDE* version and corresponding with *CPython version* in :doc:`/generators`. * Improve :doc:`/make_a_release` section. Tests ----- * Extend ``test_hello``, ``test_setup``, and ``test_sdist_hide_listing`` to (1) check if python modules are packaged into source and wheel distributions and (2) check if python modules are copied into the source tree when developer mode is enabled. Internal API ------------ * Fix :meth:`skbuild.setuptools_wrap.strip_package` to handle empty package. * Teach :meth:`skbuild.command.build_py.build_py.find_modules` function to look for `py_module` file in ``CMAKE_INSTALL_DIR``. * Teach :class:`skbuild.utils.PythonModuleFinder` to search for `python module` in the CMake install tree. * Update :meth:`skbuild.setuptools_wrap._consolidate` to copy file into the CMake tree only if it exists. * Update :meth:`skbuild.setuptools_wrap._copy_file` to create directory only if there is one associated with the destination file. Scikit-build 0.5.1 ================== Bug fixes --------- * Ensure file copied in "develop" mode have "mode bits" maintained. Scikit-build 0.5.0 ================== New features ------------ * Improve user experience by running CMake only if needed. See :issue:`207` * Add support for :ref:`cmake_with_sdist ` setup keyword argument. * Add support for ``--force-cmake`` and ``--skip-cmake`` global :ref:`setup command-line options `. * scikit-build conda-forge recipe added by :user:`isuruf`. See `conda-forge/staged-recipes#1989 `_ * Add support for `development mode `_. (:issue:`187`). * Improved :doc:`/generators` selection: * If available, uses :ref:`Ninja` build system generator on all platforms. An advantages is that ninja automatically parallelizes the build based on the number of CPUs. * Automatically set the expected `Visual Studio` environment when ``Ninja`` or ``NMake Makefiles`` generators are used. * Support `Microsoft Visual C++ Compiler for Python 2.7 `_. See :issue:`216`. * Prompt for user to install the required compiler if it is not available. See :issue:`27`. * Improve :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup` CMake Module extending the API of ``check_dynamic_lookup`` function: * Update long signature: ```` is now optional * Add support for short signature: ``check_dynamic_lookup()``. See `SimpleITK/SimpleITK#80 `_. Bug fixes --------- * Fix scikit-build source distribution and add test. See :issue:`214` Thanks :user:`isuruf` for reporting the issue. * Support building extension within a virtualenv on windows. See :issue:`119`. Documentation ------------- * add :doc:`/generators` section * add :doc:`/changes` section * allow github issues and users to easily be referenced using ``:issue:`XY``` and ``:user:`username``` markups. This functionality is enabled by the `sphinx-issue `_ sphinx extension * make_a_release: Ensure uploaded distributions are signed * usage: * Add empty cross-compilation / wheels building sections * Add :ref:`Why should I use scikit-build ? ` * Add :ref:`Setup options ` section * hacking: * Add :ref:`internal_api` section generated using `sphinx-apidoc`. * Add :ref:`internal_cmake_modules` to document :doc:`/cmake-modules/targetLinkLibrariesWithDynamicLookup` CMake module. Requirements ------------ * setuptools: As suggested by :user:`mivade` in :issue:`212`, remove the hard requirement for ``==28.8.0`` and require version ``>= 28.0.0``. This allows to "play" nicely with conda where it is problematic to update the version of setuptools. See `pypa/pip#2751 `_ and `ContinuumIO/anaconda-issues#542 `_. Tests ----- * Improve "push_dir" tests to not rely on build directory name. Thanks :user:`isuruf` for reporting the issue. * travis/install_pyenv: Improve MacOSX build time updating `scikit-ci-addons`_ * Add ``get_cmakecache_variables`` utility function. .. _scikit-ci-addons: http://scikit-ci-addons.readthedocs.io Internal API ------------ * :meth:`skbuild.cmaker.CMaker.configure`: Change parameter name from ``generator_id`` to ``generator_name``. This is consistent with how generator are identified in `CMake documentation `_. This change breaks backward compatibility. * :meth:`skbuild.platform_specifics.abstract.CMakePlatform.get_best_generator`: Change parameter name from ``generator`` to ``generator_name``. Note that this function is also directly importable from :mod:`skbuild.platform_specifics`. This change breaks backward compatibility. * :class:`skbuild.platform_specifics.abstract.CMakeGenerator`: This class allows to handle generators as sophisticated object instead of simple string. This is done anticipating the support for `CMAKE_GENERATOR_PLATFORM `_ and `CMAKE_GENERATOR_TOOLSET `_. Note also that the class is directly importable from :mod:`skbuild.platform_specifics` and is now returned by :meth:`skbuild.platform_specifics.get_best_generator`. This change breaks backward compatibility. Cleanups -------- * appveyor.yml: * Remove unused "on_failure: event logging" and "notifications: GitHubPullRequest" * Remove unused SKIP env variable Scikit-build 0.4.0 ================== New features ------------ * Add support for ``--hide-listing`` option * allow to build distributions without displaying files being included * useful when building large project on Continuous Integration service limiting the amount of log produced by the build * CMake module: ``skbuild/resources/cmake/FindPythonExtensions.cmake`` * Function ``python_extension_module``: add support for `module suffix `_ Bug fixes --------- * Do not package python modules under "purelib" dir in non-pure wheel * CMake module: ``skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake``: * Fix the logic checking for cross-compilation (the regression was introduced by :issue:`51` and :issue:`47` * It configure the text project setting `CMAKE_ENABLE_EXPORTS `_ to ON. Doing so ensure the executable compiled in the test exports symbols (if supported by the underlying platform) Docs ---- * Add `short note `_ explaining how to include scikit-build CMake module * Move "Controlling CMake using scikit-build" into a "hacking" section * Add initial version of `"extension_build_system" documentation `_ Tests ----- * tests/samples: Simplify project removing unneeded install rules and file copy * Simplify continuous integration * use `scikit-ci `_ and `scikit-ci-addons`_ * speed up build setting up caching * Makefile: * Fix `coverage` target * Add `docs-only` target allowing to regenerate the Sphinx documentation without opening a new page in the browser. Scikit-build 0.3.0 ================== New features ------------ * Improve support for "pure", "CMake" and "hybrid" python package * a "pure" package is a python package that have all files living in the project source tree * an "hybrid" package is a python package that have some files living in the project source tree and some files installed by CMake * a "CMake" package is a python package that is fully generated and installed by CMake without any of his files existing in the source tree * Add support for source distribution. See :issue:`84` * Add support for setup arguments specific to scikit-build: * ``cmake_args``: additional option passed to CMake * ``cmake_install_dir``: relative directory where the CMake project being built should be installed * ``cmake_source_dir``: location of the CMake project * Add CMake module ``FindNumPy.cmake`` * Automatically set ``package_dir`` to reasonable defaults * Support building project without CMakeLists.txt Bug fixes --------- * Fix dispatch of arguments to setuptools, CMake and build tool. See :issue:`118` * Force binary wheel generation. See :issue:`106` * Fix support for ``py_modules`` (`6716723 `_) * Do not raise error if calling "clean" command twice Documentation ------------- * Improvement of documentation published on http://scikit-build.readthedocs.io/en/latest/ * Add docstrings for most of the modules, classes and functions Tests ----- * Ensure each test run in a dedicated temporary directory * Add tests to raise coverage from 70% to 91% * Refactor CI testing infrastructure introducing CI drivers written in python for AppVeyor, CircleCI and TravisCI * Switch from ``nose`` to ``py.test`` * Relocate sample projects into a dedicated home: https://github.com/scikit-build/scikit-build-sample-projects Cleanups -------- * Refactor commands introducing ``set_build_base_mixin`` and ``new_style`` * Remove unused code scikit-build-0.11.1/CONTRIBUTING.rst000066400000000000000000000063311365474353700166420ustar00rootroot00000000000000============ Contributing ============ Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given. Types of Contributions ---------------------- You can contribute in many ways: Report Bugs ~~~~~~~~~~~ Report bugs at https://github.com/scikit-build/scikit-build/issues. If you are reporting a bug, please include: * Your operating system name and version. * Any details about your local setup that might be helpful in troubleshooting. * Detailed steps to reproduce the bug. Fix Bugs ~~~~~~~~ Look through the GitHub issues for bugs. Anything tagged with "bug" is open to whoever wants to implement it. Implement Features ~~~~~~~~~~~~~~~~~~ Look through the GitHub issues for features. Anything tagged with "feature" is open to whoever wants to implement it. Write Documentation ~~~~~~~~~~~~~~~~~~~ The scikit-build project could always use more documentation. We welcome help with the official scikit-build docs, in docstrings, or even on blog posts and articles for the web. Submit Feedback ~~~~~~~~~~~~~~~ The best way to send feedback is to file an issue at https://github.com/scikit-build/scikit-build/issues. If you are proposing a new feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. * Remember that this is a volunteer-driven project, and that contributions are welcome :) Get Started ----------- Ready to contribute? Here's how to set up `scikit-build` for local development. 1. Fork the `scikit-build` repo on GitHub. 2. Clone your fork locally:: $ git clone git@github.com:your_name_here/scikit-build.git 3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed (`pip install virtualenvwrapper`), this is how you set up your cloned fork for local development:: $ mkvirtualenv scikit-build $ cd scikit-build/ $ python setup.py develop 4. Create a branch for local development:: $ git checkout -b name-of-your-bugfix-or-feature Now you can make your changes locally. 5. When you're done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:: $ flake8 $ python setup.py test $ tox If needed, you can get flake8 and tox by using `pip install` to install them into your virtualenv. 6. Commit your changes and push your branch to GitHub:: $ git add . $ git commit -m "Your detailed description of your changes." $ git push origin name-of-your-bugfix-or-feature 7. Submit a pull request through the GitHub website. Pull Request Guidelines ----------------------- Before you submit a pull request, check that it meets these guidelines: 1. The pull request should include tests. 2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in `README.rst`. 3. The pull request should work for Python 2.7, and 3.3, 3.4, 3.5 and PyPy. Check https://travis-ci.org/scikit-build/scikit-build/pull_requests and make sure that the tests pass for all supported Python versions. Tips ---- To run a subset of tests:: $ pytest tests/test_skbuild.py scikit-build-0.11.1/HISTORY.rst000066400000000000000000000003251365474353700160710ustar00rootroot00000000000000.. :changelog: History ------- PyCMake was created at SciPy 2014 in response to general difficulties building C++ and Fortran based Python extensions across platforms. It was renamed to "scikit-build" in 2016. scikit-build-0.11.1/LICENSE000066400000000000000000000053141365474353700152060ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Mike Sarahan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This project borrows a great deal from the setup tools of the PyNE project. Here is its license: Copyright 2011-2014, the PyNE Development Team. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE PYNE DEVELOPMENT TEAM ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of the stakeholders of the PyNE project or the employers of PyNE developers. scikit-build-0.11.1/MANIFEST.in000066400000000000000000000006041365474353700157340ustar00rootroot00000000000000include AUTHORS.rst include CONTRIBUTING.rst include HISTORY.rst include LICENSE include README.rst include requirements.txt include requirements-dev.txt include skbuild/resources/cmake/*.cmake recursive-include tests * recursive-exclude * __pycache__ recursive-exclude * *.py[co] recursive-include docs *.rst conf.py Makefile make.bat include versioneer.py include skbuild/_version.py scikit-build-0.11.1/Makefile000066400000000000000000000041461365474353700156430ustar00rootroot00000000000000.PHONY: help clean clean-build clean-coverage clean-pyc clean-skbuild clear-test lint test test-all coverage docs-only docs release dist help: @echo "$(MAKE) [target]" @echo @echo " targets:" @echo " clean - remove build and testing artifacts" @echo " lint - check style with flake8" @echo " test - run tests quickly with the default Python" @echo " test-all - run tests on every Python version with tox" @echo " coverage - check code coverage quickly with the default Python" @echo " docs-only - generate Sphinx HTML documentation, including API docs" @echo " docs - same as 'docs-only' and load HTML using default web browser" @echo " release - package and upload a release" @echo " dist - package" @echo clean: clean-build clean-coverage clean-pyc clean-skbuild clear-test clean-build: rm -fr build/ rm -fr dist/ find . -type d -name '*.eggs' -exec rm -rf {} + find . -type d -name '*.egg-info' -exec rm -rf {} + find . -type f -name 'MANIFEST' -exec rm -f {} + find tests/samples/*/dist/ -type d -exec rm -rf {} + > /dev/null 2>&1 || true clean-coverage: rm -fr htmlcov/ find . -name '.coverage' -exec rm -f {} + find . -name 'coverage.xml' -exec rm -f {} + clean-pyc: find . -name '__pycache__' -exec rm -rf {} + find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + find . -name '*~' -exec rm -f {} + clean-skbuild: rm -rf _skbuild find tests/samples/*/_skbuild/ -type d -exec rm -rf {} + > /dev/null 2>&1 || true clear-test: rm -rf .cache rm -rf .pytest_cache/ lint: flake8 test: python setup.py test test-all: tox coverage: test coverage html open htmlcov/index.html || xdg-open htmlcov/index.html docs-only: rm -f docs/skbuild.rst rm -f docs/modules.rst sphinx-apidoc -o docs/ --module-first skbuild $(MAKE) -C docs clean $(MAKE) -C docs html docs: docs-only open docs/_build/html/index.html || xdg-open docs/_build/html/index.html release: clean python setup.py sdist upload python setup.py bdist_wheel upload dist: clean python setup.py sdist python setup.py bdist_wheel ls -l dist scikit-build-0.11.1/README.rst000066400000000000000000000133171365474353700156720ustar00rootroot00000000000000=============================== scikit-build =============================== Improved build system generator for CPython C/C++/Fortran/Cython extensions. Better support is available for additional compilers, build systems, cross compilation, and locating dependencies and determining their build requirements. The **scikit-build** package is fundamentally just glue between the `setuptools` Python module and `CMake `_. To get started, see `this example `_ and `scikit-build-sample-projects `_. Latest Release -------------- .. table:: +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ | Versions | Downloads | +=============================================================================+===============================================================================+ | .. image:: https://img.shields.io/pypi/v/scikit-build.svg | .. image:: https://img.shields.io/badge/downloads-660k%20total-green.svg | | :target: https://pypi.python.org/pypi/scikit-build | :target: https://pypi.python.org/pypi/scikit-build | +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ | .. image:: https://anaconda.org/conda-forge/scikit-build/badges/version.svg | .. image:: https://anaconda.org/conda-forge/scikit-build/badges/downloads.svg | | :target: https://anaconda.org/conda-forge/scikit-build | :target: https://anaconda.org/conda-forge/scikit-build | +-----------------------------------------------------------------------------+-------------------------------------------------------------------------------+ Build Status ------------ .. table:: +---------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+ | | Linux | MacOSX | Windows | +===============+=========================================================================================+=========================================================================================+===========================================================================================================+ | PyPI | .. image:: https://circleci.com/gh/scikit-build/scikit-build.svg?style=shield | .. image:: https://img.shields.io/travis/scikit-build/scikit-build.svg?maxAge=2592000 | .. image:: https://ci.appveyor.com/api/projects/status/77bjtsihsjaywjr0?svg=true | | | :target: https://circleci.com/gh/scikit-build/scikit-build | :target: https://travis-ci.org/scikit-build/scikit-build | :target: https://ci.appveyor.com/project/scikit-build/scikit-build/branch/master | +---------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+ | Conda | .. image:: https://circleci.com/gh/conda-forge/scikit-build-feedstock.svg?style=shield | .. image:: https://travis-ci.org/conda-forge/scikit-build-feedstock.svg?branch=master | .. image:: https://ci.appveyor.com/api/projects/status/github/conda-forge/scikit-build-feedstock?svg=True | | | :target: https://circleci.com/gh/conda-forge/scikit-build-feedstock | :target: https://travis-ci.org/conda-forge/scikit-build-feedstock | :target: https://ci.appveyor.com/project/conda-forge/scikit-build-feedstock/branch/master | +---------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+ Overall Health -------------- .. image:: https://requires.io/github/scikit-build/scikit-build/requirements.svg?branch=master :target: https://requires.io/github/scikit-build/scikit-build/requirements/?branch=master :alt: Requirements Status .. image:: https://codecov.io/gh/scikit-build/scikit-build/branch/master/graph/badge.svg :target: https://codecov.io/gh/scikit-build/scikit-build .. image:: https://landscape.io/github/scikit-build/scikit-build/master/landscape.svg?style=flat :target: https://landscape.io/github/scikit-build/scikit-build :alt: Code Health Miscellaneous ------------- * Free software: MIT license * Documentation: http://scikit-build.readthedocs.org * Source code: https://github.com/scikit-build/scikit-build * Mailing list: https://groups.google.com/forum/#!forum/scikit-build scikit-build-0.11.1/appveyor.yml000066400000000000000000000043161365474353700165720ustar00rootroot00000000000000branches: only: - master version: "0.0.1.{build}" environment: matrix: # Visual Studio (Python 2 & 3, 32 & 64 bit) - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 PYTHON_DIR: "C:\\Python27" PYTHON_VERSION: "2.7.x" PYTHON_ARCH: "32" BLOCK: "0" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 PYTHON_DIR: "C:\\Python27-x64" PYTHON_VERSION: "2.7.x" PYTHON_ARCH: "64" BLOCK: "0" # Used in 'test_platform_windows_find_visual_studio' # See https://www.appveyor.com/docs/windows-images-software/#visual-studio-2008 SKBUILD_TEST_FIND_VS2008_INSTALLATION_EXPECTED: "0" SKBUILD_TEST_FIND_VS2010_INSTALLATION_EXPECTED: "1" SKBUILD_TEST_FIND_VS2012_INSTALLATION_EXPECTED: "1" SKBUILD_TEST_FIND_VS2013_INSTALLATION_EXPECTED: "1" SKBUILD_TEST_FIND_VS2015_INSTALLATION_EXPECTED: "1" SKBUILD_TEST_FIND_VS2017_INSTALLATION_EXPECTED: "0" # Disabled due to appveyor timeout # - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 # PYTHON_DIR: "C:\\Python35" # PYTHON_VERSION: "3.5.x" # PYTHON_ARCH: "32" # BLOCK: "0" - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 PYTHON_DIR: "C:\\Python35-x64" PYTHON_VERSION: "3.5.x" PYTHON_ARCH: "64" BLOCK: "0" # Disabled because done using azure-pipelines # - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 # PYTHON_DIR: "C:\\Python37" # PYTHON_VERSION: "3.7.x" # PYTHON_ARCH: "32" # BLOCK: "0" # # - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017 # PYTHON_DIR: "C:\\Python37-x64" # PYTHON_VERSION: "3.7.x" # PYTHON_ARCH: "64" # BLOCK: "0" cache: - C:\\cmake-3.6.2 init: - python -m pip install -U scikit-ci scikit-ci-addons - python -m ci_addons --install ../addons - ps: ../addons/appveyor/rolling-build.ps1 install: - python -m ci install build_script: - python -m ci build test_script: - python -m ci test after_test: - python -m ci after_test on_finish: - ps: ../addons/appveyor/enable-worker-remote-access.ps1 -check_for_block artifacts: # Archive the generated packages in the ci.appveyor.com build report. - path: dist\* matrix: fast_finish: false shallow_clone: false scikit-build-0.11.1/azure-pipelines.yml000066400000000000000000000064231365474353700200420ustar00rootroot00000000000000trigger: - master jobs: - job: Windows pool: vmImage: 'vs2017-win2016' strategy: matrix: Python37: python.arch: 'x86' python.version: '3.7' Python37-x64: python.arch: 'x64' python.version: '3.7' maxParallel: 2 steps: - powershell: | $environmentVars = get-childitem -path env:* foreach($var in $environmentVars) { $keyname = $var.Key $keyvalue = $var.Value Write-Output "${keyname}: $keyvalue" } displayName: Display env. variables - task: UsePythonVersion@0 inputs: versionSpec: '$(python.version)' addToPath: true architecture: '$(python.arch)' - script: | python -m pip install -U scikit-ci scikit-ci-addons python -m ci_addons --install ../addons displayName: Prerequisites - script: python -m ci install displayName: Install - script: python -m ci build displayName: Build - script: python -m ci test displayName: Test env: # Used in 'test_platform_windows_find_visual_studio' # See https://github.com/Microsoft/azure-pipelines-image-generation/blob/master/images/win/Vs2017-Server2016-Readme.md SKBUILD_TEST_FIND_VS2008_INSTALLATION_EXPECTED: 0 SKBUILD_TEST_FIND_VS2010_INSTALLATION_EXPECTED: 0 SKBUILD_TEST_FIND_VS2012_INSTALLATION_EXPECTED: 0 SKBUILD_TEST_FIND_VS2013_INSTALLATION_EXPECTED: 0 SKBUILD_TEST_FIND_VS2015_INSTALLATION_EXPECTED: 1 SKBUILD_TEST_FIND_VS2017_INSTALLATION_EXPECTED: 1 - script: python -m ci after_test displayName: After Test env: CODECOV_TOKEN: $(codecov_token) # - job: Linux_PyPy3 # pool: # vmIMage: 'ubuntu-16.04' # steps: # - script: printenv # displayName: Display env. variables # - script: | # sudo apt-get -yq update # sudo apt-get -yq install libxml2-dev libxslt-dev gfortran # displayName: 'Install packages needed for source builds' # - script: | # wget -q http://buildbot.pypy.org/nightly/py3.6/pypy-c-jit-latest-linux64.tar.bz2 -O ../pypy.tar.bz2 # mkdir -p ../pypy3 # (cd ../pypy3; tar --strip-components=1 -xf ../pypy.tar.bz2) # ../pypy3/bin/pypy3 -m ensurepip # ../pypy3/bin/pypy3 -m pip install virtualenv # ../pypy3/bin/virtualenv ../pypy-venv # displayName: 'Get PyPy' # - script: | # source ../pypy-venv/bin/activate # python -m pip install -U scikit-ci scikit-ci-addons # python -m ci_addons --install ../addons # displayName: Prerequisites # - script: | # source ../pypy-venv/bin/activate # python -m ci install # displayName: Install # - script: | # source ../pypy-venv/bin/activate # python -m ci build # displayName: Build # - script: | # source ../pypy-venv/bin/activate # python -m ci test # displayName: Test # - script: | # source ../pypy-venv/bin/activate # python -m ci after_test # displayName: After Test # env: # CODECOV_TOKEN: $(codecov_token) scikit-build-0.11.1/docs/000077500000000000000000000000001365474353700151265ustar00rootroot00000000000000scikit-build-0.11.1/docs/Makefile000066400000000000000000000151711365474353700165730ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/complexity.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/complexity.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/complexity" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/complexity" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."scikit-build-0.11.1/docs/authors.rst000066400000000000000000000000331365474353700173410ustar00rootroot00000000000000.. include:: ../AUTHORS.rstscikit-build-0.11.1/docs/changes.rst000066400000000000000000000000331365474353700172640ustar00rootroot00000000000000.. include:: ../CHANGES.rstscikit-build-0.11.1/docs/cmake-modules.rst000066400000000000000000000011431365474353700204050ustar00rootroot00000000000000============= CMake modules ============= To facilitate the writing of ``CMakeLists.txt`` used to build CPython C/C++/Cython extensions, **scikit-build** provides the following CMake modules: .. toctree:: :maxdepth: 1 cmake-modules/Cython cmake-modules/NumPy cmake-modules/PythonExtensions cmake-modules/F2PY They can be included using ``find_package``: .. code-block:: cmake find_package(Cython REQUIRED) find_package(NumPy REQUIRED) find_package(PythonExtensions REQUIRED) find_package(F2PY REQUIRED) For more details, see the respective documentation of each modules. scikit-build-0.11.1/docs/cmake-modules/000077500000000000000000000000001365474353700176545ustar00rootroot00000000000000scikit-build-0.11.1/docs/cmake-modules/Cython.rst000066400000000000000000000002201365474353700216440ustar00rootroot00000000000000Cython ------ .. cmake-module:: ../../skbuild/resources/cmake/FindCython.cmake .. cmake-module:: ../../skbuild/resources/cmake/UseCython.cmakescikit-build-0.11.1/docs/cmake-modules/F2PY.rst000066400000000000000000000002101365474353700211170ustar00rootroot00000000000000F2PY ---- .. cmake-module:: ../../skbuild/resources/cmake/FindF2PY.cmake .. cmake-module:: ../../skbuild/resources/cmake/UseF2PY.cmakescikit-build-0.11.1/docs/cmake-modules/NumPy.rst000066400000000000000000000001141365474353700214520ustar00rootroot00000000000000NumPy ----- .. cmake-module:: ../../skbuild/resources/cmake/FindNumPy.cmakescikit-build-0.11.1/docs/cmake-modules/PythonExtensions.rst000066400000000000000000000002701365474353700237460ustar00rootroot00000000000000PythonExtensions ---------------- .. cmake-module:: ../../skbuild/resources/cmake/FindPythonExtensions.cmake .. cmake-module:: ../../skbuild/resources/cmake/UsePythonExtensions.cmakescikit-build-0.11.1/docs/cmake-modules/targetLinkLibrariesWithDynamicLookup.rst000066400000000000000000000002451365474353700277030ustar00rootroot00000000000000targetLinkLibrariesWithDynamicLookup ------------------------------------ .. cmake-module:: ../../skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmakescikit-build-0.11.1/docs/cmake.py000066400000000000000000000365351365474353700165740ustar00rootroot00000000000000#============================================================================= # CMake - Cross Platform Makefile Generator # Copyright 2000-2013 Kitware, Inc., Insight Software Consortium # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= import os import re # Monkey patch for pygments reporting an error when generator expressions are # used. # https://bitbucket.org/birkenfeld/pygments-main/issue/942/cmake-generator-expressions-not-handled from pygments.lexers import CMakeLexer from pygments.token import Name, Operator from pygments.lexer import bygroups CMakeLexer.tokens["args"].append(('(\\$<)(.+?)(>)', bygroups(Operator, Name.Variable, Operator))) # Monkey patch for sphinx generating invalid content for qcollectiongenerator # https://bitbucket.org/birkenfeld/sphinx/issue/1435/qthelp-builder-should-htmlescape-keywords from sphinx.util.pycompat import htmlescape from sphinx.builders.qthelp import QtHelpBuilder old_build_keywords = QtHelpBuilder.build_keywords def new_build_keywords(self, title, refs, subitems): old_items = old_build_keywords(self, title, refs, subitems) new_items = [] for item in old_items: before, rest = item.split("ref=\"", 1) ref, after = rest.split("\"") if ("<" in ref and ">" in ref): new_items.append(before + "ref=\"" + htmlescape(ref) + "\"" + after) else: new_items.append(item) return new_items QtHelpBuilder.build_keywords = new_build_keywords from docutils.parsers.rst import Directive, directives from docutils.transforms import Transform try: from docutils.utils.error_reporting import SafeString, ErrorString except ImportError: # error_reporting was not in utils before version 0.11: from docutils.error_reporting import SafeString, ErrorString from docutils import io, nodes from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.roles import XRefRole from sphinx.util.nodes import make_refnode from sphinx import addnodes class CMakeModule(Directive): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = {'encoding': directives.encoding} def __init__(self, *args, **keys): self.re_start = re.compile(r'^#\[(?P=*)\[\.rst:$') Directive.__init__(self, *args, **keys) def run(self): settings = self.state.document.settings if not settings.file_insertion_enabled: raise self.warning('"%s" directive disabled.' % self.name) env = self.state.document.settings.env rel_path, path = env.relfn2path(self.arguments[0]) path = os.path.normpath(path) encoding = self.options.get('encoding', settings.input_encoding) e_handler = settings.input_encoding_error_handler try: settings.record_dependencies.add(path) f = io.FileInput(source_path=path, encoding=encoding, error_handler=e_handler) except UnicodeEncodeError as error: raise self.severe('Problems with "%s" directive path:\n' 'Cannot encode input file path "%s" ' '(wrong locale?).' % (self.name, SafeString(path))) except IOError as error: raise self.severe('Problems with "%s" directive path:\n%s.' % (self.name, ErrorString(error))) raw_lines = f.read().splitlines() f.close() rst = None lines = [] for line in raw_lines: if rst is not None and rst != '#': # Bracket mode: check for end bracket pos = line.find(rst) if pos >= 0: if line[0] == '#': line = '' else: line = line[0:pos] rst = None else: # Line mode: check for .rst start (bracket or line) m = self.re_start.match(line) if m: rst = ']%s]' % m.group('eq') line = '' elif line == '#.rst:': rst = '#' line = '' elif rst == '#': if line == '#' or line[:2] == '# ': line = line[2:] else: rst = None line = '' elif rst is None: line = '' lines.append(line) if rst is not None and rst != '#': raise self.warning('"%s" found unclosed bracket "#[%s[.rst:" in %s' % (self.name, rst[1:-1], path)) self.state_machine.insert_input(lines, path) return [] class _cmake_index_entry: def __init__(self, desc): self.desc = desc def __call__(self, title, targetid, main = 'main'): return ('pair', u'%s ; %s' % (self.desc, title), targetid, main, None) _cmake_index_objs = { 'command': _cmake_index_entry('command'), 'generator': _cmake_index_entry('generator'), 'manual': _cmake_index_entry('manual'), 'module': _cmake_index_entry('module'), 'policy': _cmake_index_entry('policy'), 'prop_cache': _cmake_index_entry('cache property'), 'prop_dir': _cmake_index_entry('directory property'), 'prop_gbl': _cmake_index_entry('global property'), 'prop_inst': _cmake_index_entry('installed file property'), 'prop_sf': _cmake_index_entry('source file property'), 'prop_test': _cmake_index_entry('test property'), 'prop_tgt': _cmake_index_entry('target property'), 'variable': _cmake_index_entry('variable'), } def _cmake_object_inventory(env, document, line, objtype, targetid): inv = env.domaindata['cmake']['objects'] if targetid in inv: document.reporter.warning( 'CMake object "%s" also described in "%s".' % (targetid, env.doc2path(inv[targetid][0])), line=line) inv[targetid] = (env.docname, objtype) class CMakeTransform(Transform): # Run this transform early since we insert nodes we want # treated as if they were written in the documents. default_priority = 210 def __init__(self, document, startnode): Transform.__init__(self, document, startnode) self.titles = {} def parse_title(self, docname): """Parse a document title as the first line starting in [A-Za-z0-9<] or fall back to the document basename if no such line exists. The cmake --help-*-list commands also depend on this convention. Return the title or False if the document file does not exist. """ env = self.document.settings.env title = self.titles.get(docname) if title is None: fname = os.path.join(env.srcdir, docname+'.rst') try: f = open(fname, 'r') except IOError: title = False else: for line in f: if len(line) > 0 and (line[0].isalnum() or line[0] == '<'): title = line.rstrip() break f.close() if title is None: title = os.path.basename(docname) self.titles[docname] = title return title def apply(self): env = self.document.settings.env # Treat some documents as cmake domain objects. objtype, sep, tail = env.docname.rpartition('/') make_index_entry = _cmake_index_objs.get(objtype) if make_index_entry: title = self.parse_title(env.docname) # Insert the object link target. if objtype == 'command': targetname = title.lower() else: targetname = title targetid = '%s:%s' % (objtype, targetname) targetnode = nodes.target('', '', ids=[targetid]) self.document.note_explicit_target(targetnode) self.document.insert(0, targetnode) # Insert the object index entry. indexnode = addnodes.index() indexnode['entries'] = [make_index_entry(title, targetid)] self.document.insert(0, indexnode) # Add to cmake domain object inventory _cmake_object_inventory(env, self.document, 1, objtype, targetid) class CMakeObject(ObjectDescription): def handle_signature(self, sig, signode): # called from sphinx.directives.ObjectDescription.run() signode += addnodes.desc_name(sig, sig) return sig def add_target_and_index(self, name, sig, signode): if self.objtype == 'command': targetname = name.lower() else: targetname = name targetid = '%s:%s' % (self.objtype, targetname) if targetid not in self.state.document.ids: signode['names'].append(targetid) signode['ids'].append(targetid) signode['first'] = (not self.names) self.state.document.note_explicit_target(signode) _cmake_object_inventory(self.env, self.state.document, self.lineno, self.objtype, targetid) make_index_entry = _cmake_index_objs.get(self.objtype) if make_index_entry: self.indexnode['entries'].append(make_index_entry(name, targetid)) class CMakeXRefRole(XRefRole): # See sphinx.util.nodes.explicit_title_re; \x00 escapes '<'. _re = re.compile(r'^(.+?)(\s*)(?$', re.DOTALL) _re_sub = re.compile(r'^([^()\s]+)\s*\(([^()]*)\)$', re.DOTALL) def __call__(self, typ, rawtext, text, *args, **keys): # Translate CMake command cross-references of the form: # `command_name(SUB_COMMAND)` # to have an explicit target: # `command_name(SUB_COMMAND) ` if typ == 'cmake:command': m = CMakeXRefRole._re_sub.match(text) if m: text = '%s <%s>' % (text, m.group(1)) # CMake cross-reference targets frequently contain '<' so escape # any explicit `` with '<' not preceded by whitespace. while True: m = CMakeXRefRole._re.match(text) if m and len(m.group(2)) == 0: text = '%s\x00<%s>' % (m.group(1), m.group(3)) else: break return XRefRole.__call__(self, typ, rawtext, text, *args, **keys) # We cannot insert index nodes using the result_nodes method # because CMakeXRefRole is processed before substitution_reference # nodes are evaluated so target nodes (with 'ids' fields) would be # duplicated in each evaluted substitution replacement. The # docutils substitution transform does not allow this. Instead we # use our own CMakeXRefTransform below to add index entries after # substitutions are completed. # # def result_nodes(self, document, env, node, is_ref): # pass class CMakeXRefTransform(Transform): # Run this transform early since we insert nodes we want # treated as if they were written in the documents, but # after the sphinx (210) and docutils (220) substitutions. default_priority = 221 def apply(self): env = self.document.settings.env # Find CMake cross-reference nodes and add index and target # nodes for them. for ref in self.document.traverse(addnodes.pending_xref): if not ref['refdomain'] == 'cmake': continue objtype = ref['reftype'] make_index_entry = _cmake_index_objs.get(objtype) if not make_index_entry: continue objname = ref['reftarget'] targetnum = env.new_serialno('index-%s:%s' % (objtype, objname)) targetid = 'index-%s-%s:%s' % (targetnum, objtype, objname) targetnode = nodes.target('', '', ids=[targetid]) self.document.note_explicit_target(targetnode) indexnode = addnodes.index() indexnode['entries'] = [make_index_entry(objname, targetid, '')] ref.replace_self([indexnode, targetnode, ref]) class CMakeDomain(Domain): """CMake domain.""" name = 'cmake' label = 'CMake' object_types = { 'command': ObjType('command', 'command'), 'generator': ObjType('generator', 'generator'), 'variable': ObjType('variable', 'variable'), 'module': ObjType('module', 'module'), 'policy': ObjType('policy', 'policy'), 'prop_cache': ObjType('prop_cache', 'prop_cache'), 'prop_dir': ObjType('prop_dir', 'prop_dir'), 'prop_gbl': ObjType('prop_gbl', 'prop_gbl'), 'prop_inst': ObjType('prop_inst', 'prop_inst'), 'prop_sf': ObjType('prop_sf', 'prop_sf'), 'prop_test': ObjType('prop_test', 'prop_test'), 'prop_tgt': ObjType('prop_tgt', 'prop_tgt'), 'manual': ObjType('manual', 'manual'), } directives = { 'command': CMakeObject, 'variable': CMakeObject, # Other object types cannot be created except by the CMakeTransform # 'generator': CMakeObject, # 'module': CMakeObject, # 'policy': CMakeObject, # 'prop_cache': CMakeObject, # 'prop_dir': CMakeObject, # 'prop_gbl': CMakeObject, # 'prop_inst': CMakeObject, # 'prop_sf': CMakeObject, # 'prop_test': CMakeObject, # 'prop_tgt': CMakeObject, # 'manual': CMakeObject, } roles = { 'command': CMakeXRefRole(fix_parens = True, lowercase = True), 'generator': CMakeXRefRole(), 'variable': CMakeXRefRole(), 'module': CMakeXRefRole(), 'policy': CMakeXRefRole(), 'prop_cache': CMakeXRefRole(), 'prop_dir': CMakeXRefRole(), 'prop_gbl': CMakeXRefRole(), 'prop_inst': CMakeXRefRole(), 'prop_sf': CMakeXRefRole(), 'prop_test': CMakeXRefRole(), 'prop_tgt': CMakeXRefRole(), 'manual': CMakeXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype } def clear_doc(self, docname): to_clear = set() for fullname, (fn, _) in self.data['objects'].items(): if fn == docname: to_clear.add(fullname) for fullname in to_clear: del self.data['objects'][fullname] def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): targetid = '%s:%s' % (typ, target) obj = self.data['objects'].get(targetid) if obj is None: # TODO: warn somehow? return None return make_refnode(builder, fromdocname, obj[0], targetid, contnode, target) def get_objects(self): for refname, (docname, type) in self.data['objects'].items(): yield (refname, refname, type, docname, refname, 1) def setup(app): app.add_directive('cmake-module', CMakeModule) app.add_transform(CMakeTransform) app.add_transform(CMakeXRefTransform) app.add_domain(CMakeDomain) scikit-build-0.11.1/docs/conf.py000066400000000000000000000214251365474353700164310ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- # # complexity documentation build configuration file, created by # sphinx-quickstart on Tue Jul 9 22:26:36 2013. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another # directory, add these directories to sys.path here. If the directory is # relative to the documentation root, use os.path.abspath to make it # absolute, like shown here. sys.path.insert(0, os.path.abspath('.')) # Get the project root dir, which is the parent dir of this cwd = os.getcwd() project_root = os.path.dirname(cwd) # Insert the project root dir as the first element in the PYTHONPATH. # This lets us ensure that the source package is imported, and that its # version is used. sys.path.insert(0, project_root) import skbuild # -- General configuration --------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'cmake', 'sphinx.ext.autodoc', 'sphinx.ext.viewcode', 'sphinx_issues' ] issues_github_path = 'scikit-build/scikit-build' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'scikit-build' copyright = u'2016, the scikit-build team' # The version info for the project you're documenting, acts as replacement # for |version| and |release|, also used in various other places throughout # the built documents. # # The short X.Y version. version = skbuild.__version__ # The full version, including alpha/beta/rc tags. release = skbuild.__version__ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to # some non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built # documents. #keep_warnings = False # -- Options for HTML output ------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a # theme further. For a list of options available for each theme, see the # documentation. html_theme_options = { 'logo_only': True, } # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as # html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the # top of the sidebar. html_logo = "logo/scikit_build_logo.svg" # The name of an image file (within the static path) to use as favicon # of the docs. This file should be a Windows icon file (.ico) being # 16x16 or 32x32 pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) # here, relative to this directory. They are copied after the builtin # static files, so a file named "default.css" will overwrite the builtin # "default.css". #html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names # to template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. # Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. # Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages # will contain a tag referring to it. The value of this option # must be the base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'scikit-build-doc' # -- Options for LaTeX output ------------------------------------------ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'scikit-build.tex', u'scikit-build Documentation', u'scikit-build team', 'manual'), ] # The name of an image file (relative to this directory) to place at # the top of the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings # are parts, not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output ------------------------------------ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'scikit-build', u'scikit-build Documentation', [u'scikit-build team'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ---------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'scikit-build', u'scikit-build Documentation', u'scikit-build team', 'scikit-build', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False # -- Read The Docs ----------------------------------------------------- # on_rtd is whether we are on readthedocs.io on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]scikit-build-0.11.1/docs/contributing.rst000066400000000000000000000000401365474353700203610ustar00rootroot00000000000000.. include:: ../CONTRIBUTING.rstscikit-build-0.11.1/docs/generators.rst000066400000000000000000000437231365474353700200420ustar00rootroot00000000000000============================================== C Runtime, Compiler and Build System Generator ============================================== scikit-build uses sensible defaults allowing to select the C runtime matching the `official CPython `_ recommendations. It also ensures developers remain productive by selecting an alternative environment if recommended one is not available. The table below lists the different C runtime implementations, compilers and their usual distribution mechanisms for each operating systems. .. table:: +------------------+---------------------------+-------------------------+-----------------------------------+ | | Linux | MacOSX | Windows | +==================+===========================+=========================+===================================+ | **C runtime** | `GNU C Library (glibc)`_ | `libSystem library`_ | `Microsoft C run-time library`_ | +------------------+---------------------------+-------------------------+-----------------------------------+ | **Compiler** | `GNU compiler (gcc)`_ | `clang`_ | Microsoft C/C++ Compiler (cl.exe) | +------------------+---------------------------+-------------------------+-----------------------------------+ | **Provenance** | `Package manager`_ | OSX SDK within `XCode`_ | - `Microsoft Visual Studio`_ | | | | | - `Microsoft Windows SDK`_ | +------------------+---------------------------+-------------------------+-----------------------------------+ .. _GNU C Library (glibc): https://en.wikipedia.org/wiki/GNU_C_Library .. _Package manager: https://en.wikipedia.org/wiki/Package_manager .. _Microsoft C run-time library: https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#Runtime_libraries .. _libSystem library: https://www.safaribooksonline.com/library/view/mac-os-x/0596003560/ch05s02.html .. _XCode: https://en.wikipedia.org/wiki/Xcode#Version_comparison_table .. _Microsoft Windows SDK: https://en.wikipedia.org/wiki/Microsoft_Windows_SDK .. _Microsoft Visual Studio: https://en.wikipedia.org/wiki/Microsoft_Visual_Studio .. _GNU compiler (gcc): https://en.wikipedia.org/wiki/GNU_Compiler_Collection .. _clang: https://en.wikipedia.org/wiki/Clang Build system generator ---------------------- Since scikit-build simply provides glue between `setuptools` and `CMake`, it needs to choose a `CMake generator`_ to configure the build system allowing to build of CPython C extensions. .. _CMake generator: https://cmake.org/cmake/help/v3.7/manual/cmake-generators.7.html The table below lists the generator supported by scikit-build: .. table:: +----------------------+---------+------------+--------------------------------------------------+ | **Operating System** | Linux | MacOSX | Windows | +======================+=========+============+==================================================+ | **CMake Generator** | 1. `Ninja`_ | 1. `Ninja`_ | | | 2. `Unix Makefiles`_ | 2. `Visual Studio`_ | | | | 3. `NMake Makefiles`_ | | | | 4. :ref:`NMake Makefiles JOM ` | +----------------------+----------------------+--------------------------------------------------+ When building a project, scikit-build iteratively tries each generator (in the order listed in the table) until it finds a working one. For more details about CMake generators, see `CMake documentation `_. .. _Ninja: Ninja ^^^^^ - Supported platform(s): Linux, MacOSX and Windows - If `ninja executable `_ is in the ``PATH``, the associated generator is used to setup the project build system based on ``ninja`` files. - In a given python environment, installing the `ninja python package `_ with ``pip install ninja`` will ensure that ninja is in the ``PATH``. .. note:: **Automatic parallelism** An advantage of ninja is that it automatically parallelizes the build based on the number of CPUs. See :ref:`usage_enabling_parallel_build`. .. note:: **Ninja on Windows** When `Ninja` generator is used on Windows, scikit-build will make sure the project is configured and built with the appropriate [#automaticvsenv]_ environment (equivalent of calling ``vcvarsall.bat x86`` or ``vcvarsall.bat amd64``). When Visual Studio >= 2017 is used, ninja is available by default thanks to the Microsoft CMake extension: :: C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe .. _Unix Makefiles: Unix Makefiles ^^^^^^^^^^^^^^ - Supported platform(s): Linux, MacOSX - scikit-build uses this generator to generate a traditional ``Makefile`` based build system. .. _Visual Studio: Visual Studio IDE ^^^^^^^^^^^^^^^^^ - Supported platform(s): Windows - scikit-build uses the generator corresponding to selected version of Visual Studio and generate a ``solution file`` based build system. .. table:: +-------------------+------------------------------------------------------+ | | Architecture | +-------------------+------------------------+-----------------------------+ | CPython Version | x86 (32-bit) | x64 (64-bit) | +===================+========================+=============================+ | **3.5 and above** | Visual Studio 15 2017 | Visual Studio 15 2017 Win64 | | | Visual Studio 14 2015 | Visual Studio 14 2015 Win64 | +-------------------+------------------------+-----------------------------+ | **3.3 to 3.4** | Visual Studio 10 2010 | Visual Studio 10 2010 Win64 | +-------------------+------------------------+-----------------------------+ | **2.7 to 3.2** | Visual Studio 9 2008 | Visual Studio 9 2008 Win64 | +-------------------+------------------------+-----------------------------+ .. note:: The Visual Studio generators can not be used when only :ref:`alternative environments ` are installed, in that case :ref:`Ninja` or :ref:`NMake Makefiles` are used. .. _NMake Makefiles: NMake Makefiles ^^^^^^^^^^^^^^^ - Supported platform(s): Windows - scikit-build will make sure the project is configured and built with the appropriate [#automaticvsenv]_ environment (equivalent of calling ``vcvarsall.bat x86`` or ``vcvarsall.bat amd64``). .. note:: **NMake Makefiles JOM** The `NMake Makefiles JOM` generator is supported **but** it is not automatically used by scikit-build (even if `jom executable `_ is in the ``PATH``), it always needs to be explicitly specified. For example:: python setup.py build -G "NMake Makefiles JOM" For more details, see :ref:`usage_scikit-build_options`. Linux ----- scikit-build uses the toolchain set using ``CC`` (and ``CXX``) environment variables. If no environment variable is set, it defaults to ``gcc``. To build compliant Linux wheels, scikit-build also supports the ``manylinux`` platform described in `PEP-0513 `_. We recommend the use of `dockcross/manylinux-x64 `_ and `dockcross/manylinux-x86 `_. These images are optimized for building Linux wheels using scikit-build. MacOSX ------ scikit-build uses the toolchain set using ``CC`` (and ``CXX``) environment variables. If no environment variable is set, it defaults to the `Apple compiler`_ installed with XCode. .. _Apple compiler: https://en.wikipedia.org/wiki/Xcode#Toolchain_versions Default Deployment Target and Architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 The default deployment target and architecture selected by scikit-build are hard-coded for MacOSX and are respectively ``10.6`` and ``x86_64``. This means that the platform name associated with the `bdist_wheel` command is:: macosx-10.6-x86_64 and is equivalent to building the wheel using:: python setup.py bdist_wheel --plat-name macosx-10.6-x86_64 Respectively, the values associated with the corresponding `CMAKE_OSX_DEPLOYMENT_TARGET`_ and `CMAKE_OSX_ARCHITECTURES`_ CMake options that are automatically used to configure the project are the following:: CMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.6 CMAKE_OSX_ARCHITECTURES:STRING=x86_64 .. _CMAKE_OSX_DEPLOYMENT_TARGET: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_DEPLOYMENT_TARGET.html .. _CMAKE_OSX_ARCHITECTURES: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html As illustrated in the table below, choosing ``10.6`` as deployment target to build MacOSX wheels will allow them to work on `System CPython`, the `Official CPython`, `Macports` and also `Homebrew` installations of CPython. .. table:: List of platform names for each CPython distributions, CPython and OSX versions. +----------------------+-------------------------+--------------+--------------------------------+ | CPython Distribution | CPython Version | OSX Version | ``get_platform()`` [#getplat]_ | +======================+=========================+==============+================================+ | Official CPython | 3.8 | 10.13 | macosx-10.9-x86_64 | | +-------------------------+--------------+--------------------------------| | | 3.7, 3.6, 3.5, 3.4, 2.7 | 10.12 | macosx-**10.6**-intel | | +-------------------------+--------------+ | | | 3.4, 2.7 | 10.9 | | | +-------------------------+--------------+ | | | 2.7 | 10.7 | | +----------------------+-------------------------+--------------+--------------------------------+ | System CPython | 2.7 | 10.12 | macosx-10.12-intel | | | +--------------+--------------------------------+ | | | 10.9 | macosx-10.9-intel | | | +--------------+--------------------------------+ | | | 10.7 | macosx-10.7-intel | +----------------------+-------------------------+--------------+--------------------------------+ | Macports CPython | 2.7 | 10.9 | macosx-10.9-x86_64 | +----------------------+-------------------------+--------------+ | | Homebrew CPython | 2.7 | 10.9 | | +----------------------+-------------------------+--------------+--------------------------------+ The information above have been adapted from the excellent `Spinning wheels`_ article written by Matthew Brett. .. _Spinning wheels: https://github.com/MacPython/wiki/wiki/Spinning-wheels Default SDK and customization ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 By default, scikit-build lets CMake discover the most recent SDK available on the system during the configuration of the project. CMake internally uses the logic implemented in the `Platform/Darwin-Initialize.cmake`_ CMake module. .. _Platform/Darwin-Initialize.cmake: https://github.com/Kitware/CMake/blob/master/Modules/Platform/Darwin-Initialize.cmake Customizing SDK ^^^^^^^^^^^^^^^ .. versionadded:: 0.7.0 If needed, this can be overridden by explicitly passing the CMake option `CMAKE_OSX_SYSROOT`_. For example:: python setup.py bdist_wheel -- -DCMAKE_OSX_SYSROOT:PATH=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk .. _CMAKE_OSX_SYSROOT: https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_SYSROOT.html Customizing Deployment Target and Architecture ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.11.0 Deployment target can be customized by setting the `MACOSX_DEPLOYMENT_TARGET` environment variable. .. versionadded:: 0.7.0 Deployment target and architecture can be customized by associating the ``--plat-name macosx--`` option with the `bdist_wheel` command. For example:: python setup.py bdist_wheel --plat-name macosx-10.9-x86_64 scikit-build also sets the value of `CMAKE_OSX_DEPLOYMENT_TARGET`_ and `CMAKE_OSX_ARCHITECTURES`_ option based on the provided platform name. Based on the example above, the options used to configure the associated CMake project are:: -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.9 -DCMAKE_OSX_ARCHITECTURES:STRING=x86_64 libstdc++ vs libc++ ^^^^^^^^^^^^^^^^^^^ Before OSX 10.9, the default was ``libstdc++``. With OSX 10.9 and above, the default is ``libc++``. Forcing the use of ``libstdc++`` on newer version of OSX is still possible using the flag ``-stdlib=libstdc++``. That said, doing so will report the following warning:: clang: warning: libstdc++ is deprecated; move to libc++ * `libstdc++ `_: This is the GNU Standard C++ Library v3 aiming to implement the ISO 14882 Standard C++ library. * `libc++ `_: This is a new implementation of the C++ standard library, targeting C++11. Windows ------- Microsoft C run-time and Visual Studio version ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ On windows, scikit-build looks for the version of Visual Studio matching the version of CPython being used. The selected Visual Studio version also defines which Microsoft C run-time and compiler are used: .. table:: +---------------------------+----------------+-----------------+-----------------+ | Python version | 2.7 to 3.2 | 3.3 to 3.4 | 3.5 and above | +===========================+================+=================+=================+ | **Microsoft C run-time** | `msvcr90.dll`_ | `msvcr100.dll`_ | `ucrtbase.dll`_ | +---------------------------+----------------+-----------------+-----------------+ | **Compiler version** | MSVC++ 9.0 | MSVC++ 10.0 | MSVC++ 14.0 | +---------------------------+----------------+-----------------+-----------------+ | **Visual Studio version** | 2008 | 2010 | 2015 | +---------------------------+----------------+-----------------+-----------------+ .. _msvcr90.dll: https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.90).aspx .. _msvcr100.dll: https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.100).aspx .. _ucrtbase.dll: https://msdn.microsoft.com/en-us/library/abx4dbyh(v=vs.140).aspx Installing compiler and Microsoft C run-time ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ As outlined above, installing a given version of Visual Studio will automatically install the corresponding compiler along with the Microsoft C run-time libraries. This means that if you already have the corresponding version of Visual Studio installed, your environment is ready. Nevertheless, since older version of Visual Studio are not available anymore, this next table references links for installing alternative environments: .. _table-vs_download_links: .. table:: Download links for Windows SDK and Visual Studio. +-------------------+-------------------------------------------------+ | CPython version | Download links for Windows SDK or Visual Studio | +===================+=================================================+ | **3.5 and above** | - `Visual C++ Build Tools 2015`_ | | | | | | or | | | | | | - `Visual Studio 2015`_ | +-------------------+-------------------------------------------------+ | **3.3 to 3.4** | `Windows SDK for Windows 7 and .NET 4.0`_ | +-------------------+-------------------------------------------------+ | **2.7 to 3.2** | `Microsoft Visual C++ Compiler for Python 2.7`_ | +-------------------+-------------------------------------------------+ These links have been copied from the great article [#alternativevs]_ of Steve Dower, engineer at Microsoft. .. _Visual C++ Build Tools 2015: http://go.microsoft.com/fwlink/?LinkId=691126 .. _Visual Studio 2015: https://visualstudio.com/ .. _Windows SDK for Windows 7 and .NET 4.0: https://www.microsoft.com/download/details.aspx?id=8279 .. _Microsoft Visual C++ Compiler for Python 2.7: http://aka.ms/vcpython27 .. rubric:: Footnotes .. [#getplat] ``from distutils.util import get_platform; print(get_platform())`` .. [#alternativevs] `How to deal with the pain of “unable to find vcvarsall.bat†`_ .. [#automaticvsenv] Implementation details: This is made possible by internally using the function ``query_vcvarsall`` from the ``distutils.msvc9compiler`` (or ``distutils._msvccompiler`` when visual studio ``>= 2015`` is used). To ensure, the environment associated with the latest compiler is properly detected, the ``distutils`` modules are systematically patched using ``setuptools.monkey.patch_for_msvc_specialized_compiler()``. scikit-build-0.11.1/docs/hacking.rst000066400000000000000000000021401365474353700172610ustar00rootroot00000000000000======= Hacking ======= Controlling CMake using scikit-build ------------------------------------ You can drive CMake directly using scikit-build:: """ Use scikit-build's `cmaker` to control CMake configuration and build. 1. Use `cmaker` to define an object that provides convenient access to CMake's configure and build functionality. 2. Use defined object, `maker`, to call `configure()` to read the `CMakeLists.txt` file in the current directory and generate a Makefile, Visual Studio solution, or whatever is appropriate for your platform. 3. Call `make()` on the object to execute the build with the appropriate build tool and perform installation to the local directory. """ from skbuild import cmaker maker = cmaker.CMaker() maker.configure() maker.make() See :obj:`skbuild.cmaker.CMaker` for more details. .. _internal_api: Internal API ------------ .. include:: modules.rst .. _internal_cmake_modules: Internal CMake Modules ---------------------- .. toctree:: :maxdepth: 1 cmake-modules/targetLinkLibrariesWithDynamicLookup scikit-build-0.11.1/docs/history.rst000066400000000000000000000000341365474353700173560ustar00rootroot00000000000000.. include:: ../HISTORY.rst scikit-build-0.11.1/docs/index.rst000066400000000000000000000026301365474353700167700ustar00rootroot00000000000000.. complexity documentation master file, created by sphinx-quickstart on Tue Jul 9 22:26:36 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to scikit-build ======================= **scikit-build** is an improved build system generator for CPython C/C++/Fortran/Cython extensions. It provides better support for additional compilers, build systems, cross compilation, and locating dependencies and their associated build requirements. The **scikit-build** package is fundamentally just glue between the `setuptools` Python module and `CMake `_. To get started, see :ref:`this example `. For more examples, see `scikit-build-sample-projects `_. .. toctree:: :maxdepth: 2 :caption: User guide installation usage generators cmake-modules contributing hacking authors history changes .. toctree:: :maxdepth: 2 :caption: For maintainers make_a_release notes Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` Resources ========= * Free software: MIT license * Documentation: http://scikit-build.readthedocs.io/en/latest/ * Source code: https://github.com/scikit-build/scikit-build * Mailing list: https://groups.google.com/forum/#!forum/scikit-build scikit-build-0.11.1/docs/installation.rst000066400000000000000000000041061365474353700203620ustar00rootroot00000000000000============ Installation ============ Install package with pip ------------------------ To install with pip:: $ pip install scikit-build Install from source ------------------- To install scikit-build from the latest source, first obtain the source code:: $ git clone https://github.com/scikit-build/scikit-build $ cd scikit-build then install with:: $ pip install . or:: $ pip install -e . for development. Dependencies ------------ Python Packages ^^^^^^^^^^^^^^^ The project has a few common Python package dependencies. The runtime dependencies are: .. include:: ../requirements.txt :literal: The build time dependencies (also required for development) are: .. include:: ../requirements-dev.txt :literal: Compiler Toolchain ^^^^^^^^^^^^^^^^^^ The same compiler toolchain used to build the CPython interpreter should also be available. Refer to the `CPython Developer's Guide `_ for details about the compiler toolchain for your operating system. For example, on *Ubuntu Linux*, install with:: $ sudo apt-get install build-essential On *Mac OSX*, install `XCode `_ to build packages for the system Python. On Windows, install `the version of Visual Studio used to create the target version of CPython `_ .. _installation_cmake: CMake ^^^^^ The easiest way to get `CMake `_ is :ref:`to add it to the pyproject.toml file `. With pip 10 or later, this will cause the CMake Python package to be downloaded and installed when your project is built. To manually install the *cmake* package from PyPI:: $ pip install cmake To install the *cmake* package in conda:: $ conda install -c conda-forge cmake You can also `download the standard CMake binaries `_ for your platform. Alternatively, `build CMake from source `_ with a C++ compiler if binaries are not available for your operating system. scikit-build-0.11.1/docs/logo/000077500000000000000000000000001365474353700160665ustar00rootroot00000000000000scikit-build-0.11.1/docs/logo/scikit_build_logo.png000066400000000000000000001244371365474353700222740ustar00rootroot00000000000000‰PNG  IHDRd°I”¸ïsBIT|dˆ IDATxœìÝwœ\Uýÿñ×)÷ÞÙ’-³%! ¤ å $›Pƒ ‚+RDýYiúµ£”t¤Ù¿~ * _A’¾;»‹B€$„©³©»;·œóûcƒŠÒ2swg39ÏÇcBösß³åÞ;Ÿ{Š`7ÓÑÙõs|'ؤÜY‘´ÆÜ2eÊäÿ.µÐà 8"S}7Pd„¶ôx˵VAÁZ¢°~ò°Ã¾§Ü™ÇqÇqÇqœÊ¦Ë`(-Îuýw&ÈœE•×V³ãÿ¥R$qüA ä†Ìˆ ú^­õØÄ@”Znx±°1𺔒ø™àf ±¬¹ÇqÇqÇqœŠ·[5d¶"š1vÇÇ ^žÈJ|ß'‰“‹K­›ËuüÉóüFƒ@>û7ÄÒËÀC釃ÂãØc¾ÍŽÁZA¦gu_Ád&’Y}=õ+æc¤B)U•ëì^Xã8Žã8Žã8Neªø†L®³ûI¥”TÖОŒàq«+fXÐfgy›ð1hÏ£PèpêÔ¶o•RsÉ¢ùçûAõÛ£8†¸€ü݇¡zŸ´"——éƒæãHy/ÄKë_>ÉŒøÏÁx= Ý¿…81ApX{®kf™S;Žã8Žã8Žã8¨¢2í]?ö}ÿ 6IØd=nkAe¬S@p´,°·êÅJEÇ›Û&O:¦Ôº^Pû(ŠAy¨û¯“¡RH\nzŸ!9~6$ Fû´,½.ñâÏ«³´>|9F)Â0D 1£¬±ÇqÇqÇqœŠT± ™‡µ¿QKõñ( X~5RS!ÍTc9Îë!²5°Íõ¹¥ÖÍår‹•ÖUVJäS‹_ º5…ÄÃ@ߘwÿ[“Å AÍÚ¿QóøeX=ñ¥O6àmø OÜG"5J)™ëì^VžÐŽã8Žã8Žã8N¥ªØ†L}àÝ ­á‘¸ž¬¢Æyl¶’3ý$Ï÷)„ÑS§¶ý²”šíK–|=ª‰ƒèÛ‚¼ç ¨Þ7­Èå•lÆŽ;sÀÑ`bDÒòÐ ˜Ì^/û×­?†Åçl[Gl,¾ïïÓÑÑ}í§vÇqÇqÇq*XE6dÚ;»n÷}L“áwI5Õ2:¦Á{u/£E?HE†k§¶tb©u•\F(…º÷› Gƒ¨€k zä]_„8"Ñ>#ç_ Vx¥Õ„&Eëƒ_ÃJIE(-Ïž?¿½BÓqÇqÇqÇqÊ­Þq¿ÔÂŽŽSÏ?) C,‚£Fê*¤“ÍŽÒ›ˆ…DAXè{©us¹ÜãR*m•Fv=ÏÞª.…ÄÃ@ï2Ìñ¿‚ +õÏ,"óì±jÜ«ž¨Fm{„æÎÛ1Ê ªÚ{p;Žã8Žã8Žã8»ŠkÈR_—$ Ã}Q[ó"7#9ÓÏcÏó‰âè†Ã;¬¤­™sW™êýc›×!ï?ªöK)q™Åk±|³÷¡`Ta+ÙG>Œ Æ¿®O·ÞDFt]DÕÆ$|ßߣ#×uÛà†vÇqÇqÇqv•Ò« ½ó±¿xžW/LÂJSÍŸM†L…ŒŽÙ†àT½F!”& £åSÛ&¬”š‹þzßT©½s aR îž™q€H't9Ùäv’· Qa`ªÒÃW€n`g~ìMf,­ƒ° aáyþ/n/yŠ˜ã8Žã8Žã8޳{«˜†Ì¢ŽÎ/d|ï-q"¹1ª§¾Bš110QĦ6“I’$´M>pïRëú5-¿EÈ-®ý6Þ²*…ÄÃÀ¶'IN¸¤ÆHMvÙýxëÿ+³;WGøˆp-‹†Ñ>Æ$x¾÷“Á í8Žã8Žã8Žãì.*¦!ãKyyEh wG$ˆJçzœæç‰‘ø¾5æŠRëær¹_ûAÕcA¬_‰xäöO!ñ0>Ž=bvô~Xkñ·o aÁ9XBQ嬞HõSß öù.$žç5æ:»ÿ”rjÇqÇqÇqg7R ™öÎî­u ­áñ¤–EÆÇ¯Ñ1[œ©·PC‚ÒšB¡Ð1eÊä K©Ù¾à¯'j/85Œ"01ê·_„ª‰iE./BÍ’7~¢«#ü*&3ŠR¦b™`/šçŒŒûˆâ˜ ÞÞÞž;7½àŽã8Žã8Žã8Îîd—oÈ,iïº*ãûmIÓ‹âæ¸®bvUŠLém$BÇqØ6ù ©¥Ö•™?5Æ€öPü zé§¹ü¶/'9á;íÑÜujëà ªK«+4àÑ:ÿjŒòéԷKì8Žã8Žã8ŽãìŽvù†Œ§å£(Ba¹=Ìâa+fª’NðóDv`ª’I’¯”Z·££ãÏ÷ ¹úïˆ%_ÿ ¥úÇûlv A¦g#r_ÅzéŒþ±jO2«¯§~åŒTh­3¹\÷âTŠ;Žã8Žã8Žã8»•]ºw‘ëì~R)õ‘Ä´'#¸-®eD…ŒŽÙ„àloãeÒó‰¢ðÁ¶É“Ž)¥fûâ…ÿÔÔ/ŠcH"ô¦€Ú„J)u™^¨?€øôïAc%ìyçûIDš£ "\És'.$ñkñ=þBÿì©m“f¥xÇqœA—Ÿ3¡C±XSd …53ž9.Õ`Žã¥gîÄ<ØB %´Ãßfg?÷ñÔB9޳SzæNø›µ¢Iˆâ®Í<‡?iœýÜigs^*?gÜ­ ‚¸˜Ï·VH„]£SÎ5d–vvýÈ÷ý7ÄaMÖã–¸¶bvU* 8JØ[õ ME[JmÆH¿êŠ8N¦*ýñû@Se4c°Ð»ŠäŒ_A’`´ÏÈE?F†OcuÚkãHÐYZ¾ŒçÞq%Q¢¤œÌJù@Žã8ƒJÀH%E«)òÒ©¤ JlKº©Ç)VÆ“…¸Øþ*h%Ù˜b$Çqv’…QJ‰F[äµÙW‚þX4¤›Êyy¢Éײ9.òFJJAb°»ä”¥.=Ò“êQ"€_GÔVH3Æ,ïòzˆ¬D)EÅŸ.µn.×¹Xk¯ÊJ‰|râñkAWÈ}tߘwÿ[Û„‚šµ£zكЌ`eÞ†;h|â>©‘RÊ\®û‰A9˜ã8Îà1ÆZŒ¥èl…\|§ØŸ~§‹mÑ:Ž“ ‹-õwá®ÍCÃÚR¾OÖœtwɆLCÆ¿@[Ã#q=Ï[E%Œóè±’ú=,ÚóÃð®C™òëRj¶·/™dª‰}›‘¿?ª÷M+ry%›`ܘŽ#’–OÀ{ êa­?†%çl]Kl,~àï›ëìþá ÔqÇqÇqÇ©»\C¦½³ëvß÷GaÖØ ¿Kª©®&`‚÷é^öý Q­›Ò6é„Rë*\†!(…¾÷Ûà±Ë}ëÿ“5¯!~×!I´ÏÈùWzÇ®HƒI`üQ´>t!AE!>ýÈÂ…SùÀŽã8Žã8Žã8NØ¥Þ•/hïü`àù'EaˆApSØP1[\'@³0¼Eo"F"„ Ž ï(µn.×ù¸TJY¥‘]À³¿U—Bâa wæ}¿† +õÏ,$óì±jpGÇüƒ¨Fm{„¦îß`”‡’Úªêß ÍÁÇqÇqÇqœ]Ù.ÕÉ(õ£$IÐî؂ܵ^À«Øl%gzy àù>qÿøCé,¥fgGÇ5A¦j¿ÄXĦµÈûÏ…ªýRJ\fÑ Øÿ3ñ`0 ª°•ì#g`‚ñCÃzÑuU—“¾çéÈuÝ6¤!ÇqÇqÇqœ]Î.ÓÏèÈu?èyºN˜„•¦š?'22:f‚ÓôVE„Pš0 WL™<é“¥Ô\ôÇ_$µN! A ÔÝs 3Ž]|§ó6¼õ“‡å1òá+@7PŽiŒ¡õ¡ó&&Œ"<ÏûÀÂ¥¹“‡<ˆã8Žã8Žã8Ž³ËØ%2‹::¿àûþÑq"¹1ª§^TF3&ö1‡ê-$B’$ m“*y{ `Ô°ÊC-þ-äïYUrÞaaÛS$ïÿ H#5Oþoýÿae¶Ž6 ;z_lão_Oó1™ eeõDªŸ¼ŠÚ=§±}äøžjÌåºÿÔÖvPÉë9Ž3<¼sÞ Ç4ú=X[(ºˆ@ýúðÖ‹RŒå”ÑÉ®ý°–Lˆ¨ÈÂZ¶ß2­µäk¿ã8Îîìˆ×ï3>ÃÙ`ûŠ.bQ¿žæ®ÑÎÐö ™\gw»Ö:°qÄß“ZŸú hÆlEð½…Ð…B¡cÊ”É%Úç?t‚çW Œ"À îúÔîRâ23!ÔL%9òƒGX©9ïbŒ?’á0Ë{Ñ<ïdúNl'²>~࿽££ó3S¦Lþßrgs§t8j¿œµhýTñ­-î:$µæô…ë÷üåá-g¥Ï)ƒ>c׌ª©¹ÉSô‰TŠSlÈßvDóS ç8޳Yð––'÷]´þÓÊÏŒ(åýáEë¸ù°–SŽç8¯hXOYZ’ëºÒ÷ƒ)IÓ‹âæ¸®bvUŠL‘êm$BÇqØ6ù ©¥Ö•5ÙŒ1 =Ôÿ„¾§@xiD.¿Þå$'| åÑÜ}j˃ kÊl€Ð€Oëü`”GEH¥®*w,ÇqÒSH¸ÀDaQqZpF¹_‡“Ž{Þ4êB¡ÿ‹-úg" TIæ”ûµ8Žãìê¶'æËQâ5Zœð¾ÅÏW—ûµ8»áÛ±Vh!¾E!Ãma; ÆA”Î2°Íõ‰^žÈLU2IrA©u;::ð´×`ÈgC,¹ ü Óÿ8öØïb³c±ªòÏ0¢ó¬Wòr;©²j,™Õ?¢~åŒT(¥ª:rÝKÊËqœtÜ2­en…+…TŰ©µ8sц‡ÒMæ”Ë͇µœ”Dq¯ÅÝ¡XcP¾ßú¡Eë¯I9ÚëMÒ‡ã8NyÝ>mäuq¡?'T‘“@¬©¨·þ£é&sœW6l§,åº[¦”"‰Y’Ôñ¤ÕÔVÈ“•œë÷àažO¡ÐÿàÔ©mß+¥fÇÒŸ 2ÕdžQI„üíiP³OZ‘ËËôBë{H¦¾ ’iú-‹ Æ”9ØË3Áx=‹ÞÖÄÁ‚À?¸=×uéÔ¶I—”;›ã8¥ÛÐoÎ5›—„IQŸoâíùG?í;~;mäŸRŽç”Adíª‚à+I¡¸å…’(ăO禛ìµåçL|¡ÊW£L‘CüµlïæegrtÊÑÇqvZ×–þ÷¶5Ö¬.öómãyþäSl8ÛM%u†Â°!³´³ëGA¼Á&1=Ö㶸¶bš1G«~&ª^¬TÄq¼¥mò¤cJ­+tõ7¢(˜ªtÿµ`G€(ò î°b¡É{/Ä`”OËÒ‘…§@å÷ $èFZ¹£Q¡¤üZ¹S9Ž“Ž{ßÜúp†‘ºøg&I¨Wòúc9eôëÃ[.ˆû «…,ò¶ÊZ¤ö¼3­/Gƒ®Pˆ …Øý„eÈí8ŽóroÛó¹8I~'½â—l0ILF1;ÅXŽóІ]Cf~.w„¯ô'ÂBü*j¤¦Bš1È`y÷Ž©JZkÂØ|´Ôº¹\ç"­½Œ• ùäBÄׂnI!ñ0Ð÷æ?ÆÖ6a… fÍcÔ,»«‡×T¥ge#Þ†ÛÉ>q‰ÔH)UgW÷²rçr'7Ör¬‰cKÑÓT´Œ;uþ:·vH…ؖ𥢇É&ŽÐZ¿ý¸¿®}cбÇqv;7Ör|Å…â¯Ñíù#O[¸þ)Gsœÿ0ì2UBÝa­Ecx$®ç«¨„q=VòQ¯íù …»:éŽRjæÚ—Ì 2U‡Æ‰AômAþþL¨Þ7•¼e—ô`'|sÀQ`bDR eÞ‰˜`¯r'{]¬?ú%çl]Cl,žçïӞ뺮ܹÇIGlÅ J—ð. ñ•øRŠ‘œ2ºãÈ–[â(\(KhÊXkiõäORŒå8޳[Ьý_åùE~…ø‚O¦Éq^Ö°jÈ´wvÝîûþ(L›á·I Õ2:¦Áñº—=d?HEE릴M:¡ÔºB…aZ£îùx£fßÖâXñZ’ã> q„Ñ>#ç_ VíØÍhW 0ÞHZçÍáÀÔ¥O-X°ôÐr's§t¿<¼ùãIõQÜ9×Z‹òüêÓm¸5åhN™ÜtXË&‰)ú©l’ ý`¿S¬+y¡ÇqœÝÙ¯oùrž+q*©ÿáEëïM7™ã¼Ô°y羨£ë´ÀóOŠÂ‹àưzL¹c¥"š…áh½‰‰‚þ¾ðØRëærˤRÒ*ì¼Vß ª.…ÄÃ@ï2Ìñ·@P‹•Šºg’yöZ¬Wîd;GÖ ¶>HS×å!„$Sí—4*Êqœá£?¶³•_ü8GhÁ))FrÊ,±âW¥œŠ¨’âÂ#9Žãì–¶$É—JZï-Žð´~çÛ^;-ÅXŽóæ!ãKq]’$h ÷E lEŸp%Úl%gyy àqÿäˆ#¦þ­”šK—^dªöIŒEl^‹üÓ¹Pµ_J‰Ë,z{Ðç0¦€1èþ-dÿz&_îdE±ÞDFt_Dõ†å$€ïùc:r]ãT€[§µ|/*ôÿM¨¶Á–Š3­_”n2§\n>¼ùô$ ·½ ¶5(Ïo8}ÁúŸ¥Íqg·rç´Q¿ŽÃÂâR§’Ž äOSŒå8/1,zKsÝzžW‡IXnª¹'©BQ|l²‚Ó¼m4ˆ¡4…Báé)S&}¢”¯×Ây÷í«ýÌ9…0êÎ À×÷@œßµ?¢@’c>10UI)Zù&¨z†ÉkQL0†æyç#LLEø¾Ê’ŽŽ“ÊËqœÒ-ÛVxgÑC¢ØÍAûÁ¡'Î_{fбœ2ê7æÛ²”µ â­Ä)FrÇÙ-ÝxXëaÖ ”©¤þþ\¸þ‹é&sœe_ŒcI®ëËUt†€àïIïPýEþÊ /„åµ…DHl’Ð6ù 7”Z7Sßú $rE;¶õPý¦’ó Iˆ™ò^PAã²ðÖß‚õ‡÷®J¯IÈðiZ–ÞÈÚÃ?I’Dh© ü¦ÜÑÇ)ÍÂcÇ®ÞgÑú?jíg⨨&ލUò*àÆtÓ9åpë#g±pý™J©½­Iv¾€µH­Õ ×?|Óá-oN?¡ã8Îî#Jì-¾ïj¢°¨Ï7qLFˆ‹€o§›Ìq†ACF qiýóö=^¾ŒiGŒ ôöõ]Vj­\GÇ­žŸÙc fÂTØû’3+&“`•GÍŠ»@7”;Q*¬žHõ“WR;öp¶:ßS¹®û§´Mz[¹³9ŽSš›kyçY‹Ö'Êó¥-r1z?S½Çé ×_ûËÃ[ÎI9žS›b{~K ïµVAQ?¿ºúM'Í_÷±ßLkuÃåÇqŠôËÛO;kÑú÷(?¨-³ót&húЂu·üêˆÖSSŽçìæÊÚÉuv·k­ƒ$ŽÑ²›Ò‹,ì磵¦¿Ð¿tjÛ¤‹K©¹tÑÃï÷‚ªSÂ0¥‹ÞÅaØJb°;~„@š˜µGÏḟƒl`Wž²ô"ìEÓ# ï„¥D6 ‚·.éè<ÿ)“PîlŽã”f[b?é›ÂTE ‰Q¥…-içrÊãî7¶þáÔùë.’RŽ´˜¢nrd))le<•pÇ)£-±9;cú§}c¢?í\ŽS¶†L{î±+ƒÀŸ ôYÅäa,*¨ÿy’$ }Ô¢»`Ý E.$9œX °󎳱UuX!ñz7Ue‰3u休¦GÎÄ»ø´%¡Æ£uþ5¬9êKDQ„–ò›€kÈ8Î.nÇ(7’Áù‡[¦µ^^î Žã8ÜyäÈ››ËÃqþ]y2´BˆÇ¾†!Zn›Yn5Úîú ‹ NÐy ‚ ØÞ·ýË¥ÖÍårö<¿>J,rõcˆ¿ 5£RH< ô¯Áõ-lMk ™M«u÷;yöÔ%a˸éYuÁ ×ïzÛ^¿ «Æ’yæz¼ƒ?J\ÕˆÖ:Ó‘ë^2¥­ô¦ã8Žã8Žã8޳k(KC&7ý±eJ)!’˜%Iˬ¦¶B¦,å­à<¿‡ƒô|úûú<¸­íû¥ÔìX²èü S}L! !‰w#öQ£c’í0ú½$‡¼LŒÀÒòа>´þõ{<ì%È$bÝç°çׂA”}飒ˆp›ý.aM 2‰ˆã˜ ð^ÚÑyÙÁS&_Tî|Žã8Žã8Žã8ÎàòE9::»~ÁlÓc=nk+¦S@p¬êg‚ìÅJEE[ÚÚ&Sj]éU}3 éJ÷] 4WF3 …gIÞs1ƒQ>ÍKoBþ†Õ ^ø9 ËÆHÑ6¼ùvdaU¹C—Æl&ξŸüþïF&1ÂLc¢­ÔeNç8Žã8Žã8Žã ‘!mÈüuÑ¢7j¥? àWQcÅ4c Árœ×CŒDkM”DŸ(µn.—[¬´ÎX©ËE,»ts ‰‡¾'0ïü1¶¶ +$µkº©Yv9V¬c‚ñ4,ø8^o¬aÛèIôîûUD¼¼ÌÁ‹eñFÖ¾ùB„I0Ú£eñOixò>©‘Rª\g÷²r§tÇqÇqÇqß6dj‚ªÿ³Ö¢1<×ó‚UTÂ8€+ù~‹çûú ¿9têÔÛJ©Ù±téœ S}HœDßä=ê}ÓŠ\^qvüY˜Ž#ã~šæ„ öú—¿$±º™‘óæ`•BF!ëþÆŸ¶P¶èÅ’…•ôL»¨: BRûÂcT-ÿ.õíŸ'غ†ØX|ßß§=×u]¹³:Žã8Žã8Žã8ƒkÈ2íÝwø¾? “°Ædøm\Cu…ŒŽéEp¼îe”è©ÃÂÚ)S&\j]¡½K¶¸V¨{¾ Þ*aëglv-É;?q„Q­ ˆ°ò?ׇ‘uèž{húû½å¬˵ÈþçÊ“½H"yžÂ˜³y‘H#ã>šh@$­] RL]úÔ¢E‡—;³ã8Žã8Žã8Ž3x†duÔE¹Ü‡Ï;! C@pcÔH½0CqèA—­ŽÖ›ˆ‘H!èÝ^xG©us¹ÎeJi‘‰ìzVßUû§xè}sÒ-Ô`ºg‘YuÍ+nkmý‰Ô-ùÛGO¦P;’¾ìx¶¶]Aíc_Ãz»ÀVØ6úY7í|dc”ÇýÂj¬Ô€Fm{˜æÎß°¾í¤‰ñ3ÞíÀØ2'w°lœ>jÒ+”œ`!ê$¢Ñ20ÀOµÀl¶`Mh].l´ÆôÇ75_µñ‰ò¾†òzásõãýºÌÿÒË"‡# ÖSÖöaí³–d£ O5]žw[H;ŽãT€ 5}Dèà¡äh!ÔkíÀu@ ¤ÅZLAam’¼ 0«m>½tÃõåÎí8Îð‘ÿBã$ê2BODˆ‘ jbB4ØìÖ,ؼ-š~Œ]iM´²iîš9e ÿ*†¤!ã õÃ$IÐî²lE©Ñ1›‘œï¯Ç~Ð×ßwÝ‘GÚUJÍŽŽŽk‚LÕ>…0Bl[‡üÓ¹P»_J‰Ë,z;éó˜ S ŽQá6²ž ƿ꧙ÌhZüϾïfd±á ©Zõ;dßS ª‡&{‘dø Þr‰WêŸY@æÙë^Ò€²ÞDj»/bû˜©ôeÇxzLG®ëÖ)m“>XÆè»5¿ú$!å!丌÷mÌÀJW/žçþÙ"þåó„!ô?þLÞ&ULßtiÖ˜5`W˜$ì[û~žývÏ_‡ö• üj´uŸ—JM²¨ J‰V-¾vÿú5„—0ð5“ÿœ¼*ÄŽ?ËTÓ{EãO ±-`Í:kì LÜÅ–Þk²ßéyl(_×ëÕ3{ì¯jª2§ÅIq×7)½‘éÉN_žM9Únaã̱××Öd>™ùõÿWž–lë‹—5ÎXQÒÅ·g΄¿WjÿÄ—IIAo!þ{㌕”’Ãq†Òš³FÖx{{ßBŒ2Z6¾x ø÷kÁ?®;1zǵ·Ÿ: IDATTTÑwEÃuýQ²Å`WË$îŽû·ßÑrEÏ/‡øå%?{Ü¢šŒwh±¿ûR úB³¶qúòQ)GKÕÆ™{þ~DMðîb¯{'Q_~R¼öß,MÏ̱×ÖÖdÎŽŠÍ*Qb¨¿xù guôLoùŒÕ™w!ôAH±WF aþå<2ð´X;ð€ôEBˆ=$Úq_éÓ{ÅÞ³ ‘íÚåÖÄívã¦Ë›¾¿íoezi/1è ™Ž®Çò´®3QÈJSͦŠF*ctÌ6Ò[iVi …þ§§¶M>»”šÞóÓñÚ Î)„!H‰ºëB¨TÀï¾Á«%yËÇwLUÒìñÈU xÍ©X"ƒìë 9w ë§~‘Ĭ=ú ÆÜõfLfÃõë#âåôMü[ÇLF&1ª+žõ² (Œ¡eÞóìñ·Fß÷OYº4wòÁ·Ý>ôÉw/lù˜ÌÔ|)öµôÆœÜû£¸hÛWþ»RŠQBÈQZW©ªªÎî™Û°Ýó&ücvö ÓSxe³ñ’æO¿úL!ÔÁ'ë¾~M«ÄX’O÷B!÷TZì)„>ZÌœŸŸÛØ‹IºIÂ?dç¼0#WR:k‰ ±¥Ø›=),Ø]p¬a`ã̱×UWe>¹½Púý…§ý}ñãÙ+þ«ÔZÖš°?–ß‹ KÍá8C¡göØ_!½£Oîñ¯×‚þ¸Ø7ë!Dâ©‚ü 85?·ákÌõÞÔxéú¤úRd!,Ä–¸Ø†Œ°;FßoÂþØRJC†!z_h±Q ×h1ЈÒMåü»—Œüœð2g ¥¦úJ¨—Ü—¿Þs‰ýÇÿüSBP%¥.ÐrDl ±vçX¯Ó‹O@ÌŽ†×À×j©õáÊׇç/x ‰i7aïõÍ__wmêœaoãÌ1?¨®Ê|ª/J§%æ™4š1»×4Ú}­ûJÝqzDö ­ÕT$&ÝkµXHv¼¹BøJë#µ_wdÏÜÚ+lß›õ¬Mì8»¸ü¬=oBê÷ž¬ “þ}ù¿žO¢¤¤UÉà´-_ßû´$‰ÄùMçµ|wóÒTú: jCÆâëQ¡1Ü5cè ˜ªd~ôóÄH‚  ·¯ïë¥Öíìèø?/ÈŒ ã±~%bÞ×`D…4cÂDZGÌÆî±6‰ ¶¯£añ¹˜`ÂN•1™=iyè4ž=á„PôìûvªW}¯ç¬l¤ðÅ‘…Õ¬=în¬ô°BÒøÄý¯Ù€²z"ÕO]EížÓØ>ê|_5täºï›ÒvÐÛ‡0zEËÏØãRáe>çkY%ƒÓDx-„Ñ#RM ªk§ôÌ­™‰‰îiœùì)CêuÈŸ_s hm¾NjýF) 6¥<ù,Þ¿}ý„ÔêàŒ?âšüÜš¯‹$¼¥qÖsçy(§,zf޾ÒÏdÎK££¥ NÌsÙé+Æ—žl÷‹§½w÷^¾wQ'%½aÜÝ8}头³9ƒgíé5#½ý[î |5%6”8Bâõ{Éù_ˆZÏ÷OÙtéD›Äñ½M³V½{HB8Ž“šžY{Þj•w²§„Œ¡ ójŒ³ãÜ¥•>ÂÙ¼¤gvã_g®|Ó…`·ìÉuv·k­i OjYl|ü hÆlAp†ÞB5 Jkúûú—Nm›tI)5ÛÜw¢ªN£¬AÝñi¨Ý;­Èåe 0bÉ‘§@a¥¤õ¡éo$;=ÕHxˆd+­‹~ŒQ>2‰Xû¦¯BÜÃP y|=D´œmÌ¡·e_°–`ë–œ‡õ_»e‚½hzäddÜGÇÿ¶¥KsŸ‚ØÍ~zêüœñOeª«/FˆÚBl)r$qº¹x¢Xˆ-ªµç`Ó¥{ÛžÙãî+w¶•Ÿ=îA=fT·Òú‰òû*³´†Ì‹_¿þØ"…ÈzA朞K'önœ9ö‡åÎæ ®3G_dª.HãæMKAlÌúÆé+ÜbêEx±9[ÌÇöÐàkuІ‹›Î)÷ëp^ŸžÙ{Þ\uШ5Z«)…Ø=%¯TÖòk‘ï{ïÊ_:Ñäg޽¡,aÇÙ)=3G_‘Ÿ;¡àùþ)d¹ï+_l)­Þ˜Ÿ;ÑægŽùþP{P2Kr]WA0%‰czQÜ×QW!͘ÁÁ"â@µ #$qÚÚ:¤ÔºªjÔ I’€öQÝáZ^ ‰‡¾$Ç_Œòhî¾µõ/ kŠ*gÕDª–Ïu`…"ÉÔ‘ãÈÂÊ4SÏö’ÔÁ†¶SqZç]òúPB#¬OëüÿÅ((ŠPZí2SY†£žé{\Ô³WÕ6¥ÔÞýÑðh$¼œÞ\Z”§ß¶éÒ‰6?k¯ß”3S~Ö˜äçN´ÚÓGÇIñóᇂ±¼9¯Êd‚OççNظá’7Z¦õÌ}E&SuA#´”ÄÆnÎN_ÑšB4§Qb‘Aýìrçp^ÝÀƒ Ïù¾zl†ÏõÀ2ð3$,"ÈÍϰaãWêŽ+w.Çq^^~ÎøeAUÕW…~˜ØaÕ%ˆÍÀÆ™ªÌgz排E¥!£…øJ†h ·…Y2ØaºäêÎyñ‡å?O„ÄóÛhÿxãJ-;<ô?ŽyËw±Ù±XUW0¢ókXoç¦*ý;“GÓ¼"£^„IØ<þú÷ü4"Y•RðbYd¸†uG_Œöhê¾ µuÞN5 ¬Kfõ©_9#5ZëLG®{É ¯Xgîy³_]ýu¢\Oñv–e`ø÷ާ~'ö̰mãôÖÏuŽž¹–g‚Ìy‚¡Žžk¡°cÄLumÝÕg´Ü™œôlœ1ú ¿ªê«iŒŒQŒ1Û³Ó—7¤Í)’±à{¢µgÖ˜kÊÅyyù‹[ÏéÙ³j›RrôPN)Ø–œû›‚lËò³öüE¹39ŽóO=3F]œ¿t¢QJí3¬2°±‡’ê¿zæNܾöË#ŽÌã¥ÞÉuv?¥”Ê–Äu&È^¬TDQ´¥mò¤cK­«½Ì·Â0˜ªô§k€f*…Äåf!|–ä=A’`”OëÒ›…¿È¤sÕJðÂ4<ýFjŒ XôÈB™FɘÍÄÙ÷‘ßÿH!lLËCçb‚b—$XÕHë#—c~æJ|%ÕÌlã̱×UþIåX´7uz#c³³ŸÿÂ`ªgö^¿ÊT×^)e[ mQ2pA êoÉÏ3l·Iu^]~zë—½ zF˜R3Æb#³aÃ1¥'sÒ`-xJz=³Çý±ÜYœÚ8sÌ·ýLÕá.ø`£[´Vÿ•Ÿ;a}¹³8Îî,?{|{xïïßÏ#/Þ ûU W÷\ÜrÞ`#µ†Ì£‹s{J<,À/£,µbûŠ¿Tc9ÎÛDŒD)E'/µn.×¹Xk/°R!Ÿ\€xò:ÐÍ)$zŸÀ÷#lmVHj×N~ö¸yAà¶+^0_‹±£2™Ìy=sÆÝ]î<ÎÎÙ0½õË^ÕˆoÄ)Ì5bàçÁnØð–æïmY”J@'QbÑž~džÏÕVî,ägì1'“É|¡° ”Œ‹’²9?wbïL;³Üqg·“Ÿ3á ßX|Wõâîn²ºî/̦¾jj ™*_ßc­Ecx8®g•TÂ8Ø1UÉïA`ñ|Ÿ0 sðÁmÿWJÍŽ¥‹ggªª‰“ú¶ ï>ª÷K+ryÅyØûÿa8LŒŒûhšw"&Øk&0^ #çÍÄ*…ŒCÖrÆßl8Ç{y²°’ži7U5‚Ô¾ÐMõ“W¤Ò€²þê—|–`ëbc 2Á¾í¹®ëRˆ]±TUݵÃe±ÁR †Lfg>ó¾Á¥”Æ*ì|ž¿Ôˆ½ObÞ{ µX©¨{f™gˆUƒ»k”õ&R·ôó[žkéËŽgkÛ•ˆh§.Ùègí´Ï"“ ¨ùW#¬N·%jP[¦¹ë7;šN©nKï•CiýÞ(Å“¿Ûâj%ð”À™O ´(ñÏ'ð©›Ñ13V½;½ª/µqö¸ßù¾>¼ž‚îŒBl© ¼·ägîù“rgq^^~zë9^Õˆo¥ÒŒ´D}[Îk¾lãÏÓÈç ž(±àU_Xî»%k·õÌߥ¥ô+åÁÆ‹ZŠúž9ž.wÇ©dùY{ÞjZ%Þ[&Æâi9*?k¯ßbIeÔ]Éï«<}}’$h ¿²ô"*¤Ÿ¾ÉyÞz,à}ý}×½éM‡u—R3×¾ä‡A¦jïB!¶­Cþñ\±J‰Ë,Zü%Ì„)ǨpÙGÏÀã‡äð&3š‘~–gß÷kd±á ¨Zu²ïiÕƒrLYx† ÇÜŠñªA@ýÊùdV_ Ò]+šNµÝ³}Ì!ôeÇxþ˜Ž\×mSÚ&¥>—qWÕ3{¯ßK1ÐL-•’OB!6Û“<5«¬1Oaâ^ìŽV„BªÀ"÷JŽ9F1*ãÉÆ tÒµ{S«” Š¢?¤ðr^Ö†cÿ§*ÐïêiJB¼òx@kÓúò”ô†ÉÚììgK^ÌI߯ ³U™×Ä)<¡€¯ýÛ·~±ùÒõn[å]ÀÀ6ز!?kì/²³VŸUî<» c,(ÿÍZɉ¥>Ø;F¤ ñÏsþ¿Ÿ÷-ÿ<ç›Ó» Kàɉ=³÷ú}ãÌUïÜ£9ÎîgÃ%Mçúÿ᡺·ý ñÏsÍ¿l^<ÏØ÷å¥Þk†‰Ejý>cìã&…EKjÈtäºò»”š‹îüî~ʯþt! A)Ô_š iE./ƒ×@rôGvLUÒìñÈU „ÝÕ_žÈ ûºhÎÝÂú©F$1kßr%cîx3¦*ý‰ˆ—Ó»÷Ù:f 2‰P…­4>ú‘Am@™` ÍóÎcõñwFÏ÷?ÐÞÞyÊÔ©“ÝhÀJ}L©kÇH1ð$½%óã¾-W5]‘ÿM1uò3G_†ÒG‚šœñe61]õ‰g-4Í\õ®bŽÿZÖ~¹þí~|¶0ÈL!@í¸)ß±Í0±±=`û±ÖÚ¿b-B"ÐјÑB Üœÿ³©•æ"ÃR€1¶¿qúŠQéUuҲᢦ蚆Òú¾{ZPèëýzÓ¥ë†dÛx'QlAù\Cfˆ RŠ¢›1jLjRc!N’UÆš'¬‰:ˆ kº,Ã~†%?}ä—…ò÷AªIVÈý3žlŒw\/kqùBlÉÞ»7Noý|ÓÜußœ£8ÎîIeê¿“æHõ—óâ½¥–ÐÛ>cÍÓ&1+±ÉßmöÈ(îÌ^™¿{ýWZöQ2™Šïh¥n–Rïg…œˆ-ˆÍ@#º˜´;Ηû§ÑD.º!³8×õ¥L&8*,Hü,j ±Bš11°ˆ9Do! ›$¦mòAû”Z×ÿÖ­µ =ÔüÛ`ó|*dtÌö§HN¿”’Æeà­¿ ë§ßy5VO¤ö±Yl{8}MkZØtĵÔ/>'Ý,6ÄzcYèÇIˆQ{<|%èFµ%d¸œ–%7²öˆOb’©ÕõÀnßÙxÉÈÏZT•²Š’c̖¦ §4]µåO¥äÉÎ~þ¢ÿyù#ö“#f ô±Oî';n6_åóµÄqô`)^Wßt›±ƒ·>ÀKF%IÏ·=½7dÿ§çu2ÜpIËùBùS…ÔmBÊý3ž¬“UîK¹IxJ+0[ò(¾Š3Xz.n:KV7ü,­fLÆô÷¾ŸóÂ%¥Ws^‰–5—>O ™Ÿ3~qvÆÊCӯbÞ\(9ðÆ(Œ’§’(¼;;ûùÏ¿¾ÏdçòÍý“uŸ«Ÿ¢ë¦ ¥ßh9"JgÔL!¶¿æJÀ5d'%ù9ã+)‚x2R€§al¶Æ&¾?Þ´õúì7z^aRAË7xxòåþëÆé­Ÿ—ºêƒV©#})DTD8­sSÑ OˆËÂ0ÄÃp{8°Us,I‹úœâ牑ø¾O_ÿ¥Öíèè¸Ý2#Ã8A¬[‰˜wÔUH3&|;m.vô>Ø8&ض–†Åçb‚òŒþ1™=iyð=<{bIÏ>o¥zåð6=€•©CV³ö¸ßc¥Æ Iö‰?ám¸}HPVO¤ú©«¨ÝkÛG€¯EC®³ëÏm“';èƄμ/)¡'<0zÃnÎN_Ñ^ªÍ×l}¶ž°ñ u‡ËÆÆ¯[¡Žñ•Ðñ+Œš±@væªcÒΟ3î~O‰úÁx‚¡åÀ• NâE¦wÛ÷¿¾±¨ÝRš/]ÿƒý÷ Ô¿MÖÔ©ÞZÊMº§áöm_{å ¸S..l:KV7ü<­fL•'éïï¿.;{õ—^Íy%Z ’$¾¿îâgÞ>7\Ø\!7K•çÅ7G…(YÒß³ñ³#¿YúÎe­ßÛÜ›?Ð3}Ô üªÏZ¶„)o½m-xJúg{¸iæ3oN±´ãì–6^Òú…Œ¯Œ©JB@ ýQ²ª¯Ð÷ƒæ¹k¯*µæŽÑqßÈÏûS”š¯DU9!.êyF{gw‡ÖÚ—Öð·¤–¥Öǯuc¶ 8Co¡š¥5…°°tjÛ¤‹K©¹xÁŸOôƒª“Â(› ~ó)±wZ‘ËË îM$ÓN†8)h}xÆk¥l-:á! ´.¼žÄó‘IÄÚ£¾QPú(.-gÛsèmÙ¬%غ†úÅçcý¡k@™`/šçŒŒz‰âÏ Žioï|O¤*“rÿRæq*)H¶÷œ›b¤—Õô- g<óŽìôå^Tè¿ÖX6Z ÿå×eà ÎàŒŽéùjà §ßšv3F ð• Nâyu?-²3ž9¼ØfÌËi¾jóýÙ™«NÌN_QWèÝú%cÌòŒ÷Ò¯ÛkÉx‚°¿ðóì¥k¯L+—“Ž_Ë~À«M¯“ñ…þðç³V—4ÕØy}þù=©4_¾ññ¡zÎëh±ö…¦ü{²3VšF3æß5Î]3§qúŠÖB¡ÿj+°jgNø¯C”X_¿iÃM'¦ZØqvC¯™1‹øzJ€¥·oûöK²3VŽK£óï²³V,;}yu!ŠnU;6éJ;ÝYÚÙ}Yu&Ó–Ä1½(~×QW!͘Á¡2äµ #$I’ô·M:èRëúÕ-7$IÚG=ôsˆÖðÒˆ\fúV¼o. 0Ê£©ëNÔ–¿€¬-o25‘ªÿCݳíX¡H‚äß|²°²ÄÂ}$5‡±aò‘qˆ•‚Ö‡.Á£Ò”ЀOë‚`”GG(­.ºÃ¢¹Ø3‘ÅfyË=¿L5ÔkhœýܹÙéËúûû®2–;XkÅÙ髎ŒcښƫÓnÆøJ`¬][ÈoxGvÆ3G§Züedç®ûvvÆÊ½û¶õ|ÜZû\ ÅkþöùZÐÆfg¯þè`çsvÎú¯6¾WÔ4Ü–Z3F ú Ñ ³V¹ïµã¤LˆfLF·f§¯½²çžÁ>fvÖsçg/Y.“Ĭôuº÷Z‰±ÈÚºo¥ZÔqv3g޽6ðdCÚÓ -ˆãø‘ÆéËkš.]óõt«ÿ§¦™«Ní_½aocì»2;ÝQB\X(Ðn ³Ta+fª’Þçõ#ñ<(NJÞr±££ó­½zƒD®êF´_Þàn=dúŸÀó=lv,AÕÆŒèúÖ ›Ìxš>õ"LÂæq‡Ó¿ç§ɳEV´È ¬;ú`-F{4wþµmÞ íâôªiÔX2«LýÊù©QJUutv/ò ÃÀÚÓkFZdŠ}3§¤ÏO7Õë×4ûù¯f§/oˆú ×$qüð`œXó3ö˜“ñäè4/˜Dq|_vúŠQMßÜ|_z•_[Ó¥ùŸ6N_1¶Ðß÷]Ë+?=ÕRFÉÓÙÏ3”ùœ×¶þ+ïQµ¿“‚Tš1Á@ãíOÙ™«ÜoÇIÙ‹¿§áö-Ÿiœ¹êÔ¡>~vÆŠ Qÿ)H±)“ÈørâÆKF~6µ¢Ž³›‘Ú?+Í©>bÇtÈþþþo7ÎÚ)…#ÿ?{gWUþÿÏsι÷NöÌdéJ›”­@›´¥ÊÊ¢ÊW”ůËWA6•­Mš¦Š(üÜ7Q‘}ki“.´¥¤If²gæÞ{ÎóûcZ­XJ“{“I¦÷ýz•ÊÌs>¯é̽÷|γ|»{m¼aÝ$ÏóŸ óZ³+†dȬZýâZ)$¬Ð¥xUð¹Ùc„.|Âê‚ eYp]ïïÏ­ ÔèkÅŠg>+(8Îó}ÀOCüît (poà±î&~zÎI€ö@ì£êñË`œ);Ý„ K0ñÉ›`” é»h;ì"ÙÒ±¡Bî:tÏ»™ÒÉm7 ®[£Û¸xGŒSƒøÓŸ€LÂ׎íÌ]µjÍ—)#kc³‚¼ŸÀx«ÂQ3|âM›>_´–J^5ñÉŠý_˜7LG2éÌ-ñ† '„t$šÞ¼,±xç§§’ßpW¢aý>9’ñ$¯Œ¿O–ÆNc¼ì©½ÿd¢qÉÁ£EDDìÈ63ÆçÞŽãoëñ5šÄ7œ˜qÝ»b!n”\Ÿ!ìÂ=ºì;"b¸t6Nù–¥¨0¬©h´m„uº¯û3M›¯'êЉ7n8Â͸÷†)³Û†ÌªÖ¿³ìZÖ>ºØÂ=~ Šó¤T) Âqb5b,$|ßﮯ;(pƒTeÝäº.`Ù߈*€d’s î&è÷] #-T½ðsˆÌK9¹÷°¨‚ýÖ](íq¡ÀÒFû1¿ƒHoZ Ó ?ñ$8Âx Ö¨zìÓ0±©##|·`Gõ“_ƒžçBHúrŽE:Drv «0è IN`7÷>vÌΆ‰¶•aeÇ8Šà¦Ó7&š6™‡Ø·ŸžRv£ï«WÛ#3fŒ‘üRéÉ( ÏŒ±³EWƣ朡³íZ ÓÝù¾ à ѸñÜŒëÝÖFÉ0àØbFòªøÉ¡ŒˆØƒ é|8¬©JÛ'aêîOV]×qG(A_²ñ#×ýÕH›2»eÈ<¶¼ež¥ä']7p——@YÍQÇ@1ﵺàC@ßøÛºjÕ JY ñÊÓ W¿¨Š^9é‡àâ 0 ¿õ ÿy=Xå.SdW§ñg.„5˜Ñ?á@ôï%È_»[>ú’ IDAT» ¼N´µ¤uÖ€Zñw@öˆjß-DVÇoxå/ЂB¶´®ùg®e.&Љ )êÃR3!Up~X½c²™1éÛãM›ÇœùoÜpb:¹ÃQK0Ø}AéO{;s­+âßt\V:e•…aÆX’àùúåDÃúyÁ£EDD¼K8Ý{ñh—¥îŠxãg¹žÿdX}| PAYc(Á""ö:U]³©:¬Ã>[tºIÅu? 'bpÏp]ýŒ5‚=evË)µåƒÆ(<á—£ò!ϺYàãV  ˶á¹ÞoæÕ×ÿ:HÌ–•+›í‚¢¹¾ÖÀ`Ä>î–äÜâ§€½Ï‡™y`|Æ™–ke»€`ì*Lxl XJßEûÁç­)»ïún‘YäÂÃ/ˆ$P¼åEþóÆ1e@±]‹².‚Óó&|Ãpgß•-«sî,ì*i`a/KÎX£ýŠò£c¶¨ +Áõü¿&š6_<ÚÈPÑ´é3™túv73xsüºÎ;s­'âß´ý_QLT<²óZ¼aýÁ£EDD¼[<Ï»·â+í·åZËÛ‰7l8ÒÓæ­0†/iÃ`) )"bÏAXEŸö†Þb§ØŠq½ßW,{«)œˆáo\¿ÐÓ¦=äAoÿâ] ™U-k~g[VŒÆ[ÃïüBæI©Ò§¨~Li@H¸®ûæœ9³ÿ'p`¡¹® ( êë€ØTŒ¾*` ˜6ø'\hFZ¨~îËmSÆ0¢ªû!T¼ô'e ´÷ˆô¦]¾ô›ÈL¹Ýµ‡ƒŒá§Qùøé0±±Ö˜™`쉨~|@@Æu¡¤üÔòåË÷ˆc6~g_˜6 ¥ä¼¶ËKMÔB•–^ã‡pø¾éˆ7lxoðh#K¢ióE‰¦7/ËµŽˆÓvQÑl{rõóDd‡aÆ(Ið´ÙhXŸ'ÍÙ""Æ’Ï×ãoœ•k-ï„ÛÙñ?DÁŸ±€-‰:&…>R7""ob¾ á†.ð<óf¢ñÓBP5"pªã´0®5;c—†Ì3/´œeÛöi¾çAø‰G‚ò£TI˜HGªnøÛ>†Át`3¦eUËëR)°T­¶ü¹¯Âœzàƒ…Dé†ç{ãÛ`9Ö̉ÃÖ ”¾p)bÝ›f¤ãÓÐ[ÿ5÷¥K¬¤ÑvØÅÚƒ‘6ªŸ¹€=6{Q!dߨXý[™«nÛ±?äXÕ¨ ûukÐK¤a†U^ù«P5„:Z‡ÐmM t6E{o}¶h–5¡zyhfŒ ø¾ÞšX¼®&x´ˆˆˆ!ÁëJ~*×:vÅ„›{ŸÖž÷Û0Ê ´H9Q™ˆˆÝ ¹xâmE*Œ4 )Üßù¿!„1*¾ÑûŒñÜ_Ä8ì]2JÞ¡µƒ?{q ‚†>'{ŒÒÂÙV2ëˆ;Œ6wvØügƒÄlYõÂ÷ìXÁ mÔ½âÏŸb3CRœc¼ÍàÙ—ÃÔÔÆ@¥»‘xú\§&×ʆ„‰MFõß¿ |³Nƒ.9 0ýÿõZ‘Ù€Î# ÁB¡lýSˆmúX溑ï;ÃÖ ”¬¹…k¡A°l{ÒªÖóÓdØ ·tµ¦}”‡fPRLL5×¼š°1@òšÊ³EAý%žç?UqC2ï¿OáòÖ狲§T¯!™1R|cR‰†õ‚G‹ˆˆØÛ®ùOTßÔóP®µ¼‰%oœîkN=¼6†!MmŒˆØc°ìèò4¶]kK|µçOÁ£,ñ%?ì ;Qæý•U­kµ,«„ŒÆ:SˆGt Nž”*õðÕ‡8y ©Éd^­¯Ÿõ™ 1—?ôݙҊý¯ëy€ Èû¯ŠjBRœcجRècÎ|FHT?y# Ë1ÄÉ鹇béÕ¨\uŒ²AÆ í˜ë!Ü6`‡ï7ùk1°÷eè™2Ä*Ý…øÓç Ê8SQõø§@ƃëy°”üŸç_xáùÖ5òp*èÒ7 !ä^Éæéäâ‰_ GWna§à\?à “Í Š7n8"Q{ÎÄ !š1€6ÜX¼.Rû¯ê IB¤7¡ýèïIÁ…Ä+êü- ÆÏ€Õ ¾ú5¿Ù ¥TYKëêGr­kDÑú©°Ê:™ŒÏBìSX^qgª¹ædãä[‰>ºH!÷ zØtÏMá(ŠØSH5×öAÅa¤4  ³—X¼¶0x´ˆˆˆ]! €Ûÿ“\ë*¬õÓ2à.É0BæÉC|DÄÈlœÔBkBº¯;ð^|ÔaóXÐkÍŽü—!³²uÍ*K)K°Á‹~1^0ì<)UêáÕƒBÒJ!“μ0oNÝ5Ab.ö©3,»àC®ç¬!ïÿP´wX’s‹I¥‡Cö?€ïD¨~¬ƪƸ·èÈ™4ªŸ»Z9ÚCÛ‘W‚Ò]蛵•ûÌpzÛP¶üóãÒ€2Î4T>q„7Ï÷aÛα+V¶\’k]#E¢iÓ|î7S3öDb/Û)øB²y§š¦?ß¹x⢗1:¯­ºHLê¸ÚtV\Ÿ¼/U{Û̘’0Ì"À02ƒovÌ-""bWž6ýñeí·æZËP1™¾ï­$Èn2Å´0ôDDä+$Õ¼0û|mÞªº±kÜ ¡Á¾[ìZúYÅ+VµÞPèÄê3™4Ò,ñ3¿¥ybƸ Ì.}ðIZÖ×Ï:8h\» äûZ@Ù»ȼ XãcêЮa`p=ôG€`¤Beë¯!{[3r-.XÎ@Áº[Q:íHôM®‡¶KÐñž_¢¿z&„ï‚¥@õcWÂ81. (RlT?{;¶}%<σ¥Ô Æe¦Çî`´ß*•ªÓaÔGì—Wg› ¥æ[¶šŸlžÑ £[¡½GKß¼4ÔC‚,çÐÀ7LA0ž?®ÒI#rK˜f H"híýmÊ·{_ 'bÄxæœç:ž"â2ùPK¯î\Pu@®Eìˆ$‚oô ¹Ö1*¯ëüy²¹ô"*îé=ˆYdeÿm>ûEDŒ 1%è“¶ßõ EÐ(¿¾óþdsY7ÊÂÈúCÆ’òÊŒ›"ƒ_¸•(D°©%c À8ÅJÁ‡€eYH^4nkkëß”e—y†!6¬­º(Ê“,Çô+àco'¦‚FAç:”¬¾ &6þ2Ev…‰MGÅgbðô0*†¾ ØÀ( •-÷Aö==® (–SÛô”­?=ÓƒE[Õ²fÅœÌÈ1I¦çze'îk#øvÙ3z[&ŽPªNZª®÷º½/ñ|³žØ{Vwõ|½ò›=ËGFÁÐ !kƒÞ(Nþ.AyO²yFWÖŒ Ïõ ö¬÷%¯Ž¿?q}ꡎ—hðŠ‚‚Â‹t&“k)RâœçÛŸ¾kAÕÂ\kÙŽ €½ôßr­c¸›5B¨Cu€›Ÿ1@çâ‰K*š±$ðŠÔŽŒÞç0pQÌ@ñ[/¢èŸ×ÃÄòÓŒÂëÄ›G/1À$@Æ oòlî5 ×^ŸF”(‡êüÿx/’ûŸ šZZW¿Z_7;OÒºþÍŒ»ûÛ’éœXÁÕi?7=° g'5ø ¢KY'YUU'¥šoíý±bɦÀסÝA *²AD00kC”‘O0J5×¾9’fÌv\Ÿ¡,ç£ÉÏ-MÜÞÿâˆ.1¦ùõÂÊŸŸ»¼ý2)Õ|£Ç­w`c œØ´>·õ+¿8¤úÚ\j!†¹=—‚Ry]çO»–•ʦcx ‘¯$¯ŒôÀ“ˆÀ쿎¢Ü¿©ë±ä²@AŸ~TÂ*xÀ˜là ±ª+oÌ˜íø H¥àùÞÏçέûeX­Ï>úuÛ)˜“ÙV»ÌU5Ðï“ý<‡1€Ñî*;ÆÉßfó"³É…?_Ø  óu &j!|íóÎÅ^›ïù9¹–¶kQ¶âb V?ŒtÙÄœØ>«ZVß8§~ö—r­-lMo^“\:í ˜mÄÁfÀg†o‚h²c;ÿ›lžñIbÿáxÃ'äÚJ~~:@s*,=ù…f@’˜ÇØVR4 0˜Xý°nê¨,1fùÙüªç-ïàl*ßø@aÜ ¢ËäÔÉšð<î³"™1H„‚a7öe˜ªÂU‘K- nÈ0z}zr 1o!¢ÉÁû5ëp$]¶L¬¹ h,Í4þ»Èí @°ÛþGþAú-d&Ÿî‡ƒŒá§1áϧ ña”@û1߆HoεÔð €E¶;ebÇ÷Ñâ.H4¼ñÁLÆ{(f‹y‡iM$•ub÷WöæÔÒ鉵Ö}szyÐ_n¶É˜Ž&ÛD¼#š³ßëQ[ÏŽSRSï½U#Æ"À3æBY¹–f†°¬ØÇžkÿm®µ<þëï™;ƒÞù‰ EKDDžA$+ƒÇ`ü„ '§pH^Ôo; g~¤:=ŒÇýrô°øw·ßq8Áê|¶cŸ¾bÕªsž3çÎáÆ›{ØÑ×¶´´œ ,g¯ ¨s#ÄòßvQˆªsˆñÁ3Àì½Z¢ãèß¡úáSó°dI<€¶Ã/ð]icò7-¥+¯@ÿ”yÈ”LÀ`|z뿆â¿<îü’»] ¾‹tÙdX0H§_ž3§.ð´±±L¼ñ“;§Ý‹YgºšÇÌ!*ðuVŒ¥ÔñÉe3 |ïG‰%?ÖE[zG<<1ÁƒDD„‡ë3Èr>ð§ó¯Ñ|ÄP¸ûê3?þ|ûûˆ¨$Œ‰¹Äx>”Ì© õ¸?¨eÀáÊ/[¡ˆˆp2œß†Ÿî %N.aÓN¤¦Ý`¨¦ãjÓlYýíÂXìÿÜLu²K2UHƒò 3½rœjuBk %Ô· Û€úúúCZW¯é'P!O¨`@/ݨêpDçzñð…Åè¯Þý,Aá«Kò£—Ê6DzÚ»FD([ÿ4œÍ?„qf€ciT?úl<õ^ßCǬÓP°ñOý/b|od:àUŸ…Ô¾ÇBjšÙ¯¯›}`®uMoœÕ±hÂJé-S‚Ôh•Vì.Þ¶ŒÛ±/L-­=Y¾ÖQWúÓÞÀ& ^gG?o³¨"Æ'ŒìhÞÔÒÚWã È»>XCÃÕüÍ‚§A»ã=™A$qîó+î\Pyp®ÕŒgl‚šµ Ê—a³¡"BÈ‹Õ0}Þª0ôäBwÇBæÕÏþ\ÆÍ¼*¤D9y¸ÀêE€•ŠÀxÂ8xÝ‚Œ†RªxUëš'‚~pÞ`ߥ¶m¾}ÂE€%Y¨Ä8ÿS LƒüãWAÚCûœÀÌx0èÇ6& -ö¾½SêAl Ò]ˆ?õ §fÛ béÕ¨ZuŒ²AÆ í¨f· ã³|Kºm‡_¡5lÛ˜›s­j4©\ÖöÕøâµ–¯ý–˜"ŒÂTì!Á2>CHš¬÷«êH.žpUИҶ çÀØéB1"hÃpl¹OªqʹÖ‘[î=¬ºÑ÷2ëHÈ\K ŒÑ>,Ç™÷¡§·ž—k-»E>œÞGäb‘ËPvÿJ9ªŸ=k?_kh˜#{0“|ä˱h?óÊ‘†€ö}8¶}ÄÊ–ÕJ5>ä°ï¥ÓYJÒ‚>í7@ÿ«aIÎ-¢èxrÅ€´ØzÔ™-Ÿ†Ä°VÅh_p!¤çÂH‰ _¶*°cv*«(zy) ·¾Á-®B×!ß¹ër§}˜ˆô$ÿ9t¬J ¤Ó™gëëg/͵®\hØ0'3Ðs±1fëX4f´À€SX|}gã”ï‰eÒéåÎm"JÛŽ“d<[NžŒ8ŒBO—Rž\ª´ç¢XÑ×r­c<àüø2ŒˆäxßD ÒÓ³+Ìø,;øOBùöÿÇËÓà8|œa'‘ÎÛ“0î÷P0ð<‚hIиsæÔìynR€ÁSÏ_¸ybÊ83AÔ¾D@:¾zæ|ä?CbGDz#¶{˜$ŒTH¼ò¨Îߢì¿^kœ½PõøY ãA©}ƒWõQÐ8j©Az=ÒÓÿ=ÓæCß÷ûçÔÏ:,׺rI¼¹ý¶DÃú éôÀRü%färf¶gË|2Ù8õÇÃC2„_H¨(m;"tÂøÅ1EDɦé-!„‹ÇÜXåýžç=•—+6ÊvªÏzvë-£¿8Âë‘CˆBºGÙ¡»åÁÒˆ¡atà߆ ÀrÔ>aÈÉ1Åax%ÿqÑ7§®y0=¸\*…4γºÑ' ó,-ÆÂj]ÉJ){UËšÕAãfºÛÎB¾ }äÙ@á»!(Ì€üÃUkí¡óÀ÷Ã/ÿ`Æg~òÖ¢oÖ2 V̘áô¶¡ìùσíwèCH÷¡ú¹ïÃ(B{h;òK€î0îÓ샅ÁÖC>¡=(%¡Y1×²Æ ‰¦-‰Åë&öô\ ^%Á’c'kfÐ3ˆÅœó’ “†•ÍTqÝÅ¿C þŒ•ý‘…Ñt¾a8V]ªaR^7(xw~~HÕFûá¤æí¹°ý_N'”ædÝa ð”ABÞ Œ(D˜œ‡÷CÀø7‚D€tœDrr *Âh(ÿ_.øœºÙ |­Ó†ö—ý8T¸póÄ”)ãn¿½¬ }±˜3«¥eÍ7‚Ä<ä¨÷>èga[ýÁ›¾µ!)Î1ÂúWA>y/ ,1Øzä5 ¿ãÂØ„)š‡ŽúCøXª½&6i×o“3P°þÿ¡dã `’ÐN ’‡ß‘Y?:º 2o ã¨ûa”K)øž÷мúúïäZ×X£âºö'¯Ÿ[víëäe2?a6cjl˜3Ÿ¡œÂÅí—–Íò›©)ð¾Ä ȨijDhX¾ëÞeÒýWØ*œXÚ7`+ÖJ°ˆq¯q—UXéÄÆ÷ÛUTl}ì¹ö'GóóËn,ÄÔÑ\s$D%÷HÌã~ÚÔhÀL¥ã}ÂYÄÑæ¹À¾7$­š0ä䊅e§¹žç_V\Tøm7“Á)V/f&‚1þ‡K€0~é%p¡½®ë„K~ø‰›O8áÈ7†·~îÜ3[Z[Òšj*öóuÐÓ_œ™!ªÏö~ Í óÉûÁ-ªDׂï!þܧ`œñ3uI¤·àÍï Ã( U-¿„ì~·FYg:*ž<ƒ\c gÚ!(šöpÞüXÖŽ‚ú¡CþZôï-ú'Ì„2><ÏKÖ×Í:9׺Æ:‰¦Mç7hw‰Þ3©ÃmKLg 3ŒÝ.JŒì²L$ºwíî7èù,DeÀ(€˜E̸¿O4m<’ͱO*¡ê‚N>c,)¬ÔÒš§â ëElĸäîC«Î5Ä& DãúÔ:ueù©RŒþu…ˆ`€¶e ‡Ÿ$¦D~ÌžEÅ É¿ ÜÃ0À¤ÆýF™‰&†±1ØièüyõßLgþ¦, Œó­’<îKJd¨u,ñ¬_ ÉRJªžÿ{иƒ¿ÿÔ|6¤=èù§‰ã“Í£îù‡ó?a|tís42“Îé­¹V¶[»ÝóoE¦d@„‚ε(^}ín™1Y$@Ũ~úfiAh[ý Xà1˜ÑÊi˜ØhŸ{6„ö „@ÚË|,ײÆÔô’oÜø±Dúšâ«_'7“þ¶¯Í‹L`G” QËŠ× 8–˜˜lœzóPßËÌÉ Y>†BPÍð#DDdq!íê¿'ß8mûÿ—X¼¡Þ7lÂø)yšáØraǵUŸ !\Dĉa@UäZGL¬àè ×æÞäÔAŹְ+Ú¯(;VI’‘³ç‘ö™ƒüζ™¿5¡ˆÉíŸ)ÙW R¡÷Ù‘¹õ³Þãy^7„Ä^b'ÉA¤Ç}ŽL–"0~£‹ÑÉ6Xû°-«¶¥eÍ‚Ä<ìÚgÛŒïÞfÙ6 5ôiË€ ÷S‰€`ò‘ïÊ‚Ð>¶.¼à>c<£ÓôÀO¼É™'Bh`꿟ÛkHaXVÃÙr'Ê_F(é ã¨ßBd†X5B0DæM´u @¶eÁó¼»=øà‡r­l9Ô÷1‚=P2Lã?}="·Ø’ñü‰†õÇý×ô3ß«t)ã3d¬8šN% ‹&\’kÃ…¤5?`Ò]öžÎ¹=udD4¦§ÐÈâ¢åÇq}Ä1œ òìk˜A$jBÓ“Duñ9aµ6Øåï(mø)%|o¥P 3’çv‹ÜåeÓ­<×…²Ôù+V´$朹s/vÓÿ‚À%0'|%½9GM½|;ÄkË!¡íb$¿"½!×ÊvCxh;ºBkËFõòü·²†Í8µˆ?ûIØI€ý@ÿþׂü±Ó3ˆ¼uè­¿ƒ‰éPxž»aNýì(;&DÍm_«hÜpd|ñÚ¢t_ò,­½?3cÀQ#7­‰°•(N6NúÊÐÞhÞî˜ ÀDQÖAÄp±$Áóõˉ† ówößM›/Êxú0æº0JŠ¢äÒé±gb eŸ˜kÆä,°††1¯‡#h˜0À€S ŽP1Ž!`kC†R”¦.)œžªÑ…,û¨ æïvvùtèœÙ¤]÷W–½|ÂN¡+OJ—$€NxÔ/‡‚1–­~4n}}ýZû†´sÐ1àigo›Ê“ìñÇ{@lг×< Ö^ÒcÇØ‘Y‡äÂÕƒI øÍV¾vX ·÷ ÁXU¨~¼,„öÐ>÷lg&À™Pµ Ó]útt ¤ö`ŒAª¯÷}¹–•Ï$¾’º7ÞðÆI‰Åk‹Üþž‹´¯W)AÛ§È„Šo±éMÚ¼¸±/Â.ˆúE K|߬7¬?pW¯Ë$;N¦2p=Íp,õÞΫ %`DĆa†êà\ë[/)›cIQs¼ìy IDATF†  þ.UÃ(AÔqyÙ{r©cW³ƒš_ã6›ƒÞ·‰Sž¿~$êÃúþ¿«»2¯~ö®ën¨¢ NWýȓҥ0þäb3FC)UÕÒºúAãÏ]dÛà{Ð'_x[΃Ü"€šùÐÍ€”ÚÃÖ‚¥ °—kuÿé·™|>ºf2>„ŸFåc†‰MX”@u=ŒŠ—ÿ#-€$Ú¾"½9áÆ!¼­h;j `–eÃ0ßzÌÂ…/çXØC|Yûí‰ÆõsK¯}|ßÿ«áfÌ!öÊ{Øí<¨à yd°({J|ßlŠ7¬ÛûÝ^;éæ¾—}Ͻϖá™2TTöÝP‚EDìa˜l¦Ù„ö/•ÊÏV¼ôÊ 1ÙòÇÄ×SO‡ )‚**8"×:vFgÃÄÅ–"'òcöLX{k‚øiÃRýw)ó8 ýKåÇØRTŒJ†ÌvÞÊð¡ ‚…ªSHõÎ!»M9üÔ-‡Á÷Å`@”^,JÄž€_›®DÃÚ C~ó`×笲d\Ͱ,ëƒ_,;>”€»†(øiGĘA3Cul®u …ÎE¯µ•ÍØD€1ÿªG„0ÕÃ0ƒ„s=6Ú.)¯³•œ­ÃÜ‘FŒ+¦ßðF*ã™þ0†ZÐÄâï2º°´Ž×!¦‡í¶·5¯~öçÓ÷!%JÉÇYV/zó¤tÉãqíà5Sb¥TqKëê'ƒÆõ3ƒÛ¶ x.ô{?Pà|È-" 6 òËAÙ^*õ…)œpnG}“¿{_޾IuÙ¿ËtOãÔ†¼P b°U+ï†Q6`mG-…ðÚ0º“µ4`zÐvø!´˲`´nEïB¢iÓü¾kÂØh2DbòÞ£ý•AK§´aH¥æw~bübDŒ‚cx Ѱ.>œ÷'®Ký"ãú ˔ц!Ëw†,b×0«\KˆfÀ’ä$÷úa®µì.d|ʡì€ñÓOÃÌOè J‰‰í—–Í +L¬DÙ"/&‚™ÿdp½OCªqÕ§0Ù8å6[R,Ìr½!%Í©;h¦ÖÚh˜£zqù[C†O91îôÊ€ö}ضsøÊ–ÕW‰9oÁ‚º™Á‡,¥aÁ|ð^ ïÕ°$çQt<±ü÷€´l=ò«™-ÈÙ¨oöÀª í .€ð]©0áñe0V0æ!«(úG3жþ ‚[\…®ß¹ëB_ëé H.ü9t¬J ¸÷Ù¹së—Žš€ˆÝ"¾¬ízÏËÜ´G3 !2¦·çö0&Øí]•€DìA€aöz^î 4&=Ѹ὞æÁ0NÝLv:٤䒩? -âÈÞñ©0Ç2"BÆ3 Rêœ\ëØ:O\äXbz&6@EsÛ’À˜7‡qQ^:fÚ:¯J|ضÕü ¥ay€v z~²ÍüUɦi¿GÔÈCÊ>ß ùû?äÇtŸ¹Ùvø,ða; ”«íw¨ÆoÝxžAÔ4n]]ÝÉžç&fÊLð‚E€›'¦Œ3â‰/ƒÚ7€HÇ÷BÏœ›@Þè;"Ò±õØ;Á¤`¤BâAuþe#¶¦qöBåãŸiÂøHí{,¼ê³A¦cÄÖÜ鵬ùì?æ¦ ›‚¸` ¡=tøøåïLÏ¨Ê o-úf-Ã`E-À §· eË/ÛÃq½» [ Bõó?€‘„Öh;ü2@÷#Ùúš=°Ú| B{°,£õ¥#·`D(ÿáÀ“—†ÓŽFû†1ñÉÓ QXvkà@£DêÚªñ;Nqœ@”}„Ò=©S«oXFÌÊæ·¾žvõÊ0ÆÇó¶òÞ‰•ƒå)Ný¤‡jGŒ<Ͱ-õÞÔ5UççZË;‘lšö7%Eq$!d¼Ç‚G8ã>F“òl°Rc ƒ ³iÚƒ¶¢ê(9fìàâÑXGkóÏ YØŒìo/¹tÆ+¡ˆ!Ú/.œ+-û o Éa}„õu³ñµ4$°ŸìÇaÂ…›'¦L wû%èeíûp»nU뚯‰9ÿˆ÷ÿÙKÜc[À€>ý6 ÷õ°$çaý« Ÿ¼PÈl=r„×`”Ú¯ó tÑ|´×Ÿá{€¨~ôr˜XXM|ßey9ëÿJ6­“€Ž•"yøÏ!ÒFlM‘ÙˆŽ£î‡Q,¥Édœ;·þû#¶à8¡óK¥'rãv®u¼#núoAж• ¹yfœÅ9a•€X’Ê:›jr>ôÝè¼*qº,*½=Õ\óF®µä+ ˜þ® «nLý1ÌØ‰†õó|áŒÐp,Q“lœr{áò‘n¸@K$BÔ1†p5ƒ JÆdÓÍÎE;¶u\X$f ¾dó'ÈUùÕÎ߇e\xš!”uzûeåÇ„qè$&-‹ÙÖIad"EŒ,Ì•ŽÊZÚ} Œ?mŽ%öëlœzG²FU5áà‘I=¶§•aþ¢eYð!ð+ …QÈ@÷z H0\×… ºâ‘G‰‰;gÞ¼³ÝÌàf'¦‚» Ènâ>6°÷-_ÚôÜ¢J¤ýDfý¨,/Ò[°õ˜›@ÆÀ( •­÷Aö/ÇhØg:*ž<Òížió1Xó9¿|‹üµèßÿZôO˜ †ïûsêg¿/ô…Æ!Tœøj—Jwµ]Z2&ÇDú}ƒkLŸ2»fÈïkÚÎxúÅ0zÉxš³åaÉ%S<Úȼ¦âcTTþkmJʽ’KkFÎ݃‚ÖÏW\Ÿ‘ïù™›íK— œ(cj'0£'p† ‹ª/ EPĘ‚‚œäÒÚM¹Ö²#É+ã±¢¥®Îî#;!Μ¿#žoºÂê#Ã̉ø¯Â‰64:®­úœ+¼6¬Ï:â]\Ú¨"-ïBEÓæË]Ÿu_óŒÏ°çSc0#/Ù4ýY¥ÄÄ‘ò#‡ýx~Hݬoe<÷¯Ê² Àø„Õ…‡Ô92Ç(Xâ9] ÉBTTT½4n}}ýTÚƒ>ø â=€ÉíT¢Ð(Úò÷ü „ñѵ÷QÈL:d¶Žè²ä®E÷‚Û)™¡ s-ŠW/[#\ªô_H€ŠQýô-0Re'O-ø4X00ôýó;Ãi˜XÚçž ¡=!Ig>Þã—äñ#-KÎ!BSYýTªqò7s­éí%O"!ð°,N÷~Í©»oÆgÄçüTã”@Ùƒ#A²aÒRYT~Qv#ái†¥ä´dsíú\kËG4bÓuâM›/Ïxf}_[ !µ´æ¥àÑò 6&4†a€ìظš”±ûh𔘒j®y1×Z uEÙ±(Žßƒ- °ÛnI.óÚ Sh¶“ÍPÉ¥5÷#C¡sñħ¨ôv_çÇÁû¸€èšÌÙ‘œ{…%çÝ ãÿY†4Ñ7 *,ùáÖKãG„0’MÓîwluÈH”*m'ÐcΜٳÞëº^ ‰½Ä NTƒHçIéR¿ò‹ÑÉ6Xû°l»¦¥åÅŸ«½ÌÍŽmZCŸÚ lÀxhbô®L!äß=l]x9`ú0b½TL7üŠS‘Üÿí¬QýèÇab£v úXVÃÙò3”¿þ8ŒP0ÊAÇQ¿‡È„U1Á™7Ñ~ÔMIØ–ßóïZ°`ÞŸBZ`\CåeßdÎ>´03ìXÁ¥©æšÕ¹Öµ#‹Í®ºDoÎ{+¯ëüiÚÕkCºg"í3œ‚ØÉ%{ý œˆÁI6Mÿ³SP¸ØÆŽý<Ͱ¤˜™2ãõZf~XSò|ð-y@²qòWB ˜7˜—‚fœkfPG…£'b,âi†’òÀdsíÚ\êh¿*~&—WüUDX%A‚€Œg¶T,ë÷~Æ~k}d¶³í€afriÍ?ËúÎ$›öº/VXÔ™1£ !äí†rÔ6Cñ† ÞÖ„©Šø£íWÆ?NÔá“5c¬S3#œøÜ©ßð9BJø8^¥P3ZCFœ2ÜéÆA<×…²ä'V¬Xè£~îÜË2éÁ—¥ pIÌÉßÇt£ÝGUƒ^þÄkÏBBÛEè<â^ˆÌHT ߉¶#Ah m9¨~þ‡ ¯ kÖÛMUN âÏþ/ìþ€ýöGÿþׂüàÏ/ä­CoÝ×0˜˜E€ëºoÔ×Ï:7Ùãžäå%‡+)ÿ5†‘‘ÍâBÎJ6Ïà±R^#,礠%KÄþ¯LOƒ¥ÂËdL{ DZ/ÌuO™Î«I5×¶Û–:!ãïüÁñ_¦ÌÒÈ”O”þôÍNßËü"´Ò%Í€Š}9”`ù‚;øÄ}ÛèÒ‚äÌLŒO3”µÉ¥µ©ŽKJŒöúÉÆÉß´K÷Pˆf Ø’wà¶ð"fñ:Rßëä_1³ÆØ¾Éæ]_*=1ÜèYÚ./9¬siÍFǶ?üN÷Ôˆ‘ƒØxA«¤RK¦ŒÎ5Aûú©0ñYCI¤*‰ÿ¡cѤ«C : ’K§?7f ‚!sÄÜÙt=ï–í£y®BWž”. I<â—CÁÀËvw9¯¯¯;Pkßöa:˜ö1@w<(Üâçé^kôN‹ÁÚKA:Ü‘YÔŸÀ/(“@ÉæU(|ý`5Ú¥Jÿ¥ ƪDõÍ`!²¥KsφqfÀð6½ðËN@Ç¬Ó µc úÝ(=|;e‰[v6aÁ7 `;Îù©æÚžÎÆ)7º¶‘êX`„ À˜á׸ǿÒyWÚõZºiÛj~-yX²yFWꪊsB ¼t~¢`Jjéôçbeñ_Qå»5Ìž.F¦Ìx£bɦ3]Ït„5±D RɦéÏ–T\ŸüQ™ØžfŠ]£ÿnú»®¯üZwh'L;£³iÚƒŽ­Ž â´gkÜ9”.Ùì»w8ŽóétˆÎ¿ál_©äRÊ©‚²} †sm„Ý>‰ØMŒûŒ ûì a|ÃPJÌH5×nEºçŠøW:¶³×u\]ñ!QPt „:F ")}h¢+”XòÆ©ÉæÚ>ATæwÂÓ !¨´ °ä›É¥…—ÁKßšh~+Ô¡©¦©÷°°?lIR£iÄl'TËveË‹·ÄìϹ™ úYâ· ¥yRy˜ažpqšÕM†¹¯¾nVIи­­kúATÈʆ|ð› ×ïÔ„0$ç^¾àqpqŒ´PõÂ(zµ)PY‘H¯Eûñ¿Bÿ„ƒ"”®‰§Ïƒqr]ª´xlOÂÆS~bÀ½üD_ Šw+™vx§`óñK µföëëfå®IΣó’’C­ ÕÏøÃ¸x²c.•2žéë'üþÞïW}5õ@hú¾Xr(•VÞc[¢&è¸HK<ÏýU¢qãahK5×üS ¹ïH<äÙúe%ͱYm|ÿö­¼®óÎw{oç—Ps„šÍû !övøÛNeÃ’lI‚ç›õa™2É%SfÙιý™gKÒø­DúIaèy7RKk7“ ÉÃý<¥ h­W&ÖÏ WÙ;“jžÑBa% Ù¿ÿÑDÆcƒGÛ9ɦé-R©:=Ì9ûû-‰† sB–öt.ª¾Ì)*ùFXcm‰²}9\ßô²1-dô& cU 1•ˆ¦ÛJØÚd_ïi¿5±xC}(öP®ß›Ã4Ú‡‚ ì5ßÕ bó"ïY“éÿ]åõ]÷ïÎû;U_F*v, 9ß±ÄdßdM‚0Ë“vdÛ$>“X¼vÄOó’Kk^–RÌÔ£O”ý» ¶'ælßWó¶0²ûmÃÁî§Jâ¥ÉÆ)·ZNì¢áÞ£³Ù‹/^g‡©kgt-›Áa6­¶%!ã›f~àn0$HL"¢iŽ¢˜o²ÓÔۮݙe‰¦-CÎë¼¶ê¢Xqé­#Uê#({ïv}ÓËZ?Ê龟VÜüåpbu4L¼N¨ØÉBȹRl+óåËgÖÐä-¡Ž«œ[ÐçWµ®>^I5³Tû8Sõá~1JòÀ”qÀxBÛ8Pa†€RVqKëê'ëëfËåy}%~èz.ôñŸ…Z÷c€õ˜ÊöĦA>°þÙÿB»è˜óQlzän¨`èýµØç‹è›T¡=Èt/âÏœãÔ„/? ¨bðT­¼[þ8H{h;²S~ŒS„w÷C5`z±uáeZò,dÒé¦Ñ>^ ŠÄ­Ã½¼0²_ß‚¨RëCNyâC©æø ؼ£[´;ðÈîo'Ù8ùH礘%ö÷4#ŒM3–ñÅë÷K-›‘g$<½íæNK9O9ržˆ9ÿ;pCùÏ22s?>`€¶uã.‰YÂâmáÙ‰YÛ³oÂÆÓŒBGÖ¤&]_ºåúЈŸLÿU±â’ÿ7èßÙxšáXê˜Îk*/¬¸®cÔzaŒE*–mýf²¹è«Dd…ñ@º=#‘ˆJ”RG‚Ô¿îx¼mžAö5Ûq”ªK.ªøLbYçwƒ+ˆ.ÃÍx0œmšÍÖ ;H8± {¾RߘN0gˆ‘dâA€ *'B‘c‰’lTÖ$ SÉQ™¾†_§ûn´JË~0\cvHk1¶•ðT|m6Àø-B9³‚f×Eü'†y‹ šÆWfû3•J!þ÷µ8{~ûïÍ©YÃY«â+í·%—|ØVÖ±ïÖ×o8˜ï-–uŠt⧤–•{lÌz€7@û¯‚MŒß í¿Èšb°d)Ëb’3ˆÄT±·%Å$Bö·¢ à c 0"û¼”P ˜S7û€–Õ/úDBÎQ=xQÇð:KäÑ~91~ê•áj' Ë÷á8±Ã_XÕzå¼9u_ñ>ìG«V®<ËŽžèi}Ú½wŸ ”äAé’(:†\þè´ÆÖÿßÞGÉU–ùÿ¾Ë½Õk’®î$û’tWw'aIe_Ft@è(óSDpAF:ADPAEÅв( *ˆÀ!é,F„tVHzߪ«î}·ßÕ  ]Õ}«*ÏçœpÈ9éç~S©º÷Ösßå˜ë1í‘Öì‹4@Ë…pÞ4tÌý¸ a…Äž¾NV#GK! '÷Cùúe(Ÿ1C5 ¬˜‚Þy·aâKŸ…óÿù¨lB×Ñ÷B—L€—ÙUéùúúºkÆ)zÞë¼tÂ<)å¼ÑŒŽy';ü¤HgžÔ–r.æp)æø1ÿÓË'Ý­Œí‚s½€{ °½p. ƧÁcà“ÁØ1É«ËíM¥Ì<%6'Åvâ†z>%+â÷¹1ÜÒ2ssè°óµŽ1”rövWvç/jé|ÑÞU%’a(<§fLÁ¨ºfÇ÷»[J.ò½×ú ”q`¥¾ìÞ €3Ï î«søÅÊ9`Wë)ãÀbZjÈDEpkô“œ‹ãGûñrnÈïtUáŒUƒ10†=w:ãgšsÈìØ7ž|ÉÂ?Ä—m¿v<ŽW½¼óÎîeW ÁQ2ãA † «ëBVQùù"Y64¿ó2÷½Óm›#÷c»òçÀù^£=N|Éæãz®Þ÷MÁùcÕ„¹¶h 0ÀœÈ;IyüÈÛñ­‘aÃv–í´$–©ç¬±‚/Èös=&ßds×ú¾í8Îô»aÀŠ`ŒLæÅ—pxHÅ!™E†œ_mÝú††“´ »9Ü^ñ‹d+ìØ!`Ï|¬cÒ“f ¿þF0õÁÖÓäé­h_ôS8&`¹D|ýï!»~ð‰cÇŒœÌü>ïóÉBm7Å›7çüšúϸþžÏólWjÍž`Cõ§êo÷= WðCúó’IößÕéØ90žÕæ UM¦YçÔ¬gý2#]ôð½{8ü+Й_#¿WÃkQå¢G“ ÐÁ­v Û±€1jÈ4ÖÍnNé…”(…Áy^ú\‘œ„¬¶VëJg!¥ôV®Zdæƒ IDAT³:Ûºzðrέ`柔Õ.ÌAâù†Þ?EgwP½ìͯÚöDÑu°•eYOéŽ_ÓñÃp¨ÿ œ½½ëW¡Þ^…ëÚÊyí\ÜÙ܆Ù?LáÿHσ€Ã^/z\¶…ßAØì^Ð œçþ{®Ì¶n"Q7΂Óx Ps"`S9HœÊøc€À­FïþG#˜v˜Ùñ®œ…mè›s ‚Ê=ÆPÖù*Ö.óòp‹ë]"^‰)¹qxê’Bûៅãœ\¶$Žú³ÁMÎ9´2çE:ŸU5oZ¤Rßb,³a±à™í8ujKû¸m' ñ–7›Óý½‹ƒ.¶×Ó— éTú¶ñ^7€ŒY-ëBwçlê’vð<ï¬ÎK'ÌËIÁUµdÓ ÚØþ(OμªúïpGr‹µ¬ ù¦®YÆ!U¨×_0cߨjj«Œ: TÝÖ·Ñ õNòÂy˜Ä¹†ªtúÆø5;F½‘ ùà\¼:йu€ã2‘‹Z5×vü@ô|Üð½%g€Œéí>qê}ÉwÿâšMý\|§ÆÄìãC¥zÀfðN’)¤ æôÏ•ÃáA].çÁ Ïóf®l]sw¶uu|×÷}ÀX˜Ó—C›0¾Ë]Ž&7â©Û™YK¥ý¨/.‰XKÅö@Õ|݇œn5˜Õ˜üôçacÓ#‰ž+ŽOFlû}˜ôúÓ°\  ¶€o cá·&à{B¥ï3'ñ»¨3绪–7®˜xÐaù{‰Wø£;$gp)ÛÞžØóöÁq"Ws]×/âM_²qŸ¨óŒˆ/ÝrþІ®™Æ˜õ¥/ˆ‹'g@‰dpν™ìùx¼y}–ŠÜ”úê÷¹jÊ„Ú{±Ï´5ïùzQª¹zÇ·ÒCÉë¢zŠ?¼®OUÏÒ÷ÿÑÉ»©^²qv*Þ!ó´QÏPâ1c_MÿmÇÔxË—E韉_ÓñCìØQçœëΧQ ™ƘÍñ«^ñkºn‹:ÓîN¿Ö9×Zãõ±Ëì´Ä¦ŒEíø’M’ý;ç|‘ŸS÷Fêz¼jIÛŒ1?ÞX`D}ݬOA¸q),ÀGEɼü'øàJáð¸.Ã6[XÏóªWµ®y4ÛºF¥šb± ôÉ—êMÀÁØ"Æ9 òw7BdÖR™÷i81Lo@zúgÐ7ó(0«Áõjþü¯°±¼ùŽš|dïã¨^÷¬pLŒÃóñTk70å®þ×Ó[Û­Cz¼Îždèº2>&÷¥U×tÜZÕÔ6!T჌1xyÒ˜aoK\w8Ø{AõÒÍ㲱ʸ~ERîx@3Ž#eöfæ+‡¬*fñ3U ­|ß?å•WZ?™M͆9ó¾™N ='…b°gÜ ½š«ÈÑÍ÷¯ù# $œðѹðN0´y1¸Q°ÂÃÔçnP’Y¦È8o_LXñeÄú߃* ×6$j/Ž:[±ˆ·l½ ª©­2J^e]ï‹ÌI?ÊH†ÌRÃ9»%58py|IÛ¾ñ›{ž.Õû«¾fÇ÷«šÚ¦§’—k_/‘ ’G;‰³ÌÍcL2k_O ^oj›<©åÍæèR‘¨Øôà•1™›Ûc|ÔÓ¼×9)XÀj®íúQ¼iÓÆnɱýÌ3dÖÑŠIkÌÚÁž®Ó«–l¤Ý–òÌÔŸömˆ/i;8ø²sn{‰ÍCÁ‡ñ½A:ø~¼iä꫷_3þI²oÞü¡t_çÇŒu›ÆûÁc™õÖ$gÐJ=]Õ´Å[¶|zü]1í¶Áuñ¦ ¥ÖÚm±1šN:röÃPZý9½mpÍæ-ñæ-ŸtÕëLiõ{‘Ý£lþ Áíñ¦¶êªk»²^vWÉñ:ÌŸ[»þ•Ö5·”Æb‡A€³¼n´Sà³"X°@Ú1<¨ªq¶× ¥¸à·¸+›š‰DÝüU­«åv¿9`³.[õýqþ—;ü¡ÏÂMû`Ò4 ÕˆmyVÄ.0±í9Ä¶Ý +–©Jÿ³]öúÂÊ©`ÖBJ‰TÞu®bT½lûµ®€î¥ÓïbÂ[à ¾?CfX¦uv OCŒež¸ ž™aŒ~15Ðcüºž‚Ûb·úêö›ÜÜuyå‘|RU‹cra‰d¥ÚŽÃëˆÌE“óÌk™]¯5ê™Tw×US¿›l»#“BP}mÇ-Ý-eçK)×9x#†ÆÁÉØ—|9ût…¯zIÛŒîæ½¾éÖÜÓ67Ÿw€óL£:ÔÎj£ž6í]—VÑg:ïůnÿ€ït}còż¤üó¾‡9dšcq-xë½Â¬Œ1kÓ©¡ŸÆ¯i/ŠÆiõu}}u^5ù Ü/ÿRÌØ1x=Ë¼Ž‚vi¥ÔñæÍÿ’Û£±PÕÔ6½«yï_Jßû7†Ìç-Û·Þ­ÔX­õÓñ%ç·ø’Í'@÷Òéw3áRâñjmÆö¾rä3 m§RáƒQmþɳ͕­kÿ*?Ä ãòaRnX1f3F)¡µùK¢nÖ‚lj®xñ/Ÿ.­¬¹# ÀXºýÓ@ÀÅ*2ÿ?üáÁöúõ‘pþLŒó@®qbÀÔ&l=óEX¯¾'‘‚–†DíÒ¨“íN:—ì±œËØÑ€88æ±)™íVÝp“Àº0dÞÂŒ ™'É‚²IX»Ö™ð‰ø²7¯“¿L„:ÿ³úæ—‚ ‘ð%Ÿæ0¼e¢Ë, 7òºîªw{9ã ¬{Y½Â%o‰ß_£Šº—ÎøEI‰®åÐOÎelG¼©mLæm¿S÷²™;<)¦ŒöfGr uk¼ySN¶ÆÌ•îköÓ’3ñAÞsïEr Ôf]UÓÆY£ùù®e3×Ä<1ËŒr¶±à€ Íšªæµ£«06zšgÜé„üH‰Ïã#7ÍÎíÚ~#Ÿï·šÔÆή±*x¬zÙ›_ëìÅ,¸þ—Ö£ã{HÁÃñ¥[?2ÚÝÍÓ!B ø;®»øFÎÿ™÷É[×í sv½5êéê–m6c¡h¿hÂrê¤o3.ŠI6ÅìtòA®«ï|-‡ïKàl« SeÓÐên™þp‰;Ce±¢B‰dˆ]ñÚ˜±é^º×­¥%¥…ftŸ@[çâMòæ‹IwËÞdB.ò8ƒn^ìÊûbäÌGÞ¡ÝêŒúŸxËÖOyè]Ðueü㼤âBÇÅÜW;º÷þˆ·ï+wú;+;à¬yÉ·O^Þsÿhrv·ìóǘ/éQ¾ÿ9´q푌³¨¯›uhëêµ ŒKé$s`Ű­ó[2­5b±Øü•­­_«¯«ûÖh«5ΛgëÊ•gy±Ò“”Rp%0ϼήÐÎÁ {þi œŒ£8›16¡ká}0±Jx ‚ðjÆŒ¿šeÛÿsçßwcÊåðü01Œí Æ+gqO¼}òçE`äcÈVκNÀvØâŒ^ݺô¯ÇZŠev滪YÞõs ëç#¿ïnšúu&ýÃÁøt€ÏpŒM,õxéH“ë.¤ìíóðë¨áÐm­éÃVX³éäïã×÷<2N¥QqV­O‡üE¦GóóܹŽ\çzoîy­Ì;Ê °5ŽÃ™¿æ:U¶X0´Ôø±3¬cYî´C¬ók«ùVß>pk_VÊ Z°QÝ®Y㸃³F«jx:C××ãÿÊJ+> Æ`|ïÌ:N™?³ó›Š ÿ‡g>ßÊY·ÍA¯×©ô“Å2º!$ý²ÂÑþ¼µNX«×f“!Þ²õ‚‘ÿïnšr9Dl„ØŽï †šñ÷½ž´pÎõiç6ÂÚ¿;|dw[lÊú_úÏù}wóž7AÈz@ìËâ%’Wîtûü¶ák*` ŒëqÎöXg7ÁÚ5©Žþïejç€Õk-¦fs¾MŒËªN«×R¡ý5z¸JNCe)Þ¼ùXèjÞë\z ø%‹Ùá+Î;ÏÃ#»ÒÊõkÚ¬Q+*[¶]8Þ¹ßOõ7»ÿèþï‘ßw-Ùë;\ŠÇľ lr©ÇËFÿØèe;ýÏðupn»3v£±j]8úqõwºŸÏ:¨5­AˆRÇFùþw` ®#²oô+Z×´”ÆbK Ðå<E4îÓyË8èºÚY^¶5[[Ww2.ª­u`AÈ,[\Y%+‡cñu rÍ—á¼â›®Ä̤÷ºÛ\a4œ³ÉDÝ슨s‘îÍ+¦Îô™šOÔÂeæY2ƸSªuHàù×vo‹:cþsèøjüT“³œC9s`ŽÁºn£Íß§\ßýXñ\Ùýliž/·êRÇecÌ0½€M9¦ó»Qg$Ñ{óË“öñ$[À}ÿ`眅sŒ1$èÕñz:_aqèúzõGáÉZdšúŒ9û&”]¿¾ç/Q§#QpèjšzçÞD ”ˆ1Æ`MÒêÔúšå½÷D0:¿6ñÃóäñeÕÓŸÐÆVCþ8(ŽA$@å¾Ðÿþ – LÿÝ¥+^uºÜq ÀlýÈJ8îÁó<¤‡R66&îˆ:!„B!„ñù£ÈU­k†c¥ÒüJÕ`•õáÉô¥^p|ÃoG3ðc1¤SÁõõ³¿’MÍW^Yñ‹’ÒŠsC­Áz¶CÜõ! â\EŽVø\Ý0‹> §5¼t/öúõ|ØØ¾Èƒ·jNðô´ÿ0†&_p„¡z¬>1ûÔ¨sB!„B_‘­PÎ].¥„Ç©^7`,ÌißÒ›±ë› æ1&çCÿ2À (ÔjxðÚùÀ|ÏƒÑæÞ¹s:!„B!„häECBmÏãœCƒãXÙƒ8³(Žå}8³øyX@…!¤'Ï{þ¥—ŽÎ¦fCÜ˂tz®²ö¤;€Ôßr”8br Ø_þ÷.`¼rt-x<Øu²Qaª ý‰®Ú’anJ$fŸu.B!„B!ÑÉ›†Ì¼ÆºÇB­ð| À¹^ú‹dêGfß?¨I°°Ö¢,Vò«lë&µµF+ÅŒ†=l!ÜÌóÓ›ƒÄy ì ðGƒ¥úÁœÁÀŒF¤f~Ìlˆ:Ùc '®Yg@c z{’ÇE‹B!„BH´ò¦! uµç„a¸… ‰),À™2‰d‘L]*…Ãc¦ [m)` ¤ôªW­Z“õ”§U‹ïû€V0'~0ÛW¨S{vÂ8àí ñØ·éëí‡_'ª†·Ž.\u }a ˜µð<ÎÚ[-:¢8V)&„B!„2jyÕ€AêTk-4ã8Röcof våwŠ3‹Ÿ©I°`ÐJÁù§¬X±êüljÖ76^¤‡ž“B%•°§= ý=W‘£%&[ Þú $÷бðnð`KÔÉv ÛÐsø–×@r†0 ÖÕ××]u.B!„B!ÑË»†Ì‰óæ­ÑÆþÐ÷c0`8ÛëÆX1,W À€á·*Y(¥ =ñ£lë&‰ùZ©$sv¿¸Y—j{çÒƒÁŸøX_;˜sšr0mÓù=u‰ÙN¨©ç ÷€c ¬†1F%êjgE‹B!„BH~È»† 4Ö×^”Ò빘À4Ζ,’©K>ž·>^5åàÎBY¶ªuõ³ÙÖµ:y™"3ué¸ÿ$œÎEäˆ1 lˆß6œƒ›õgÖ$—Ž:Ü{0€íÇŽù_7žçÁ³4êT„B!„BòG^6d ¾®öPc­6Œ#!0‹)ÊÊ!ïg"îR€Ãh’’Òù+W¶~-›š sŽü± R{Rƒù—»äk¹Š-^ t?ñâ¯áh?öfðôÈÇ­¾y° ÝóU@ 0 ŸkhH,:!„B!„ü‘· 0Î-÷<õ{`Šhê’‡ÃjxÌ"p!²þž¨¯?E…A7‡ƒ›vÜKp}öóAì°g®ëØÆÒ÷BãM`*¿ÖÇefRû\ŒþsÀ­…Öz0Q7{~Ô¹!„B!„ä—¼nÈ4ÖÍnBõ’%0X,ûÐ_$S—<ëœÄ*] á,8çbUëšµÙÖ ‡úþ•ssÔ¿€ s8TìñðÅ€³àF¡ë°S¡'žØ¨“e8ÇËÐ~ø€Ï“0Ö]u,B!„B!ù'¯2P_7kžÒzÈ2ŽEGñA‘4e*ápž€'aA,;låÊ57fSsÞQ ÿ¨Âô=¾ç`0g|Êïpwó€tÄŸï¤f v³ \u ¦.ñ` :Þ'|xR" ƒGëkïŒ:!„B!„ü“÷ ÐÎ}YJ ŽS¼^øp°Q‡Ê‘ °¸WÅ!a†!„ä—g[³¾¾þü0HoŒÁUO‡=ö& ]$S—¼ýÀ^þ&ø–u`ŒA•ÅÑsÄàA´S—˜Þ€äÁW!9õP8(¥:uµ§GŠB!„BHÞ*ˆ†ÌÜDí‚0|Bz,Î÷z‹fê’°Õ <§'A:ÆVµ®ÙœmÝäÖÿ]d3 ¶ád`Ê)€Mæ q(?üá3‚[ÞŽF0í|0³#š<. [R‹ŽÆsÁMÆ8´2‹£ C!„B!¤DCµ'*­»ÀfðNi¤Š¤)S‡u9:] Îhø¾?cÕª5÷dSsþinrFÝìû>`ÌiWÁäÃÔž¬1 â‰[éƒk…ó¿¸$3ÎaxðÚ¹ ƒïyPZÝ;gNâwã„B!„BH)˜† Jÿ;çÇÉ^Äa‹fêR³¸+¬ ÂÒ“ç­X±âÃÙÔL44\¤Ók„àp•5°'üHý-G‰#&kÀ^½üï/\ÀzåèZðxzÓ¸Æ`ª ý‰®Ú’annHÔž7®!!„B!„œ‚jÈÞ˜øM¨Õžïp8×ïÁ@‘Œ’áúÁñ”®‚„…µBúd[7‘¨­5Z)f4ì¬EÀÌóÓ›ƒÄy ì ðGƒ¥úÁœAÿŒ9HíûE03N‹Ûè‰Ç£kÖFÁÎÚ|N!„B!¤TCêjÏ U¸™ ‰),ÀGÄ’EÒ”)Ãït)¶ÚRÀxžW½rÕêDz.lÂ¥¾ïZAŸt)`¶®ÆqÀß â±oRBèí‡_Ç'Nñ±¸ê@ûÂ0káy¬uß›;—é1>0!„B!„"Pp èLgXk¡Ç‘²û0ƒbù\Å,îR“`Á •BIIÉÉ/¿òʧ²©™h˜³ Ç=t,º<½eLËÂ6ô~;ÂòHΆáºúúÚKÇô „B!„BŠFA6d>|äœVmí}?†³¼n¤ÁŠa¹Z0 ¨8<6¼¶ðnͶn"‘8J«0Éœ…ݯnö¥€z#‰ó@éÁàO|¬¯Ì9 M>ƒ‡5ƒé±™ºÄlÔÔsÑ{À"«a­Õ‰ºÙ³Æä`„B!„BŠRA6d 1Q{Q:×s!0iœ-0X$S—|8ôdèªS;]]§à8Gûáÿ¡Ã‘]•¾“Ì„B!„Bv;߀†ºÙG(c†,ã8'±€‡Ф)S ‡{u%œ„5¾ïöÊÊÖïgSsÎG<¥Tp·ïyÌéËT[ŽGŒy@¸âéŸÒ3;ŽYvYŒâÁt.üœðáI‰0mh¨»+wÁ !„B!„ìNŠ¢!Zé/L]:ÙëF 6êP9R‡ûTJ)!.~üñçâÙÔ¬¯¯¿ Ò›cpÕ3àݤ‹dê’7lÅõà[Ö‚1UGÏQ?F×tbz’‡4!9õP8h­;‰Ù§å85!„B!„ÝHÑ4dæ6&~¨ðqéypXìõ¢¿HFÉ[œÀóz¤3àœcÚž•+³­;ø×ç>l3 ¦ñ`êé€Iæ q(?ü×g:·=û`Å`¶ãƒÕqiØ’zt4œnBpΤÃsÇ&4!„B!„ÝEÑ4d ¾vö)¡RÝà3D 'ˆ4REÒ”)‡ÃC¦.g4|ߟ±²uõ}ÙÔ\pÎg_·:ý=ß÷caN½· ›©=yƒ €Õ@<ùCÀó!tˆ .ÌðÆNñà ´s#à8|σVúóæ5<9vÁ !„B!„슪!A¨/àB@;Žãdªa‹fêÒDXÜVT“Þ9/¾øÊñÙÔ¬oœ{i¤Ö Áá*k`O¼Hý-'y#'kÀ^½ üÕ.`½2t-ü%xzã.ý8SÐ_#ÒU3 9†Á–Dböâ± M!„B!dwPt ™#æÖ?¨ð~Ï÷Ÿð{ŠfêÐ Ž§t$,¬µˆ•øY’€ºººÙF)ÅŒ†=t!°ï€îÉAâ`Œ ¿nº3Q7{rÄÁ!„B!„9u€ñ4·1qÇÊÖÕGÁ±“C‘l´³LsÁZ'Œuä¢â«;‚úCö`/ˆ90"ld€³Ö:gMVgE…B!„BHñûÿxK=1Ž IEND®B`‚scikit-build-0.11.1/docs/logo/scikit_build_logo.svg000066400000000000000000000143031365474353700222750ustar00rootroot00000000000000 scikit-build-0.11.1/docs/logo/scikit_build_mark.png000066400000000000000000002333531365474353700222640ustar00rootroot00000000000000‰PNG  IHDRèèM£ÔäKiTXtXML:com.adobe.xmp ž`ï IDATxœìÝ{˜\e™ïýß³êÜ]ÕÝI0 Æh%‚" $nÅê{½#ÎÞŽ#¨#Ž3*8Îèü!ó^ºk‹³B8 "ˆÆ„á¨Èù”ÎiõZ!H€œ:‡î®®îªµÞ?2{ǫ̈ô³ OºkÕ÷ó§Ü÷Õ¿+ö¡îªgÝRÀ¼ÜŒãø@å„yI_‘ô’Ž”äMo" ÕÖKºJÒõZurºÃÌT~Húº¤ÿ.éðiŽt›QIHúv½V}hºÃøýŒyÙœèD~–%Ý-éÄéÎt™{$^¯U'¦;ÈLãá<í^7ÝY€.Iú|½V½bºƒø]S è|ât¦‹ÄpL‡wKúÿ¦;ÄLãaFÒOÅpÌž¤Kü |Ût:Ðaü üˆök0=>ï!?ÿ³¯I:iºCøwYI×ûAXšî ’áÐAü <$ŽõÃéÎt¹ƒ$Ížî3…„ÇIúÆtçð;Þ(é»Ó@2 è@iDæ—Æ03@{ºÌ~ÕØu«ö}Z`æ9ÇÂ÷NwöбÌ_snÉ‹ß9Ý9h]½Vžî3ÂØÎKUxítÇð²®ñƒ7÷Á€t?Ê}ºsÄ‘QI’¿jÕ{Õ3›}ÀÌ7WÒ¥Ó€t`†óƒ03yK²&æ)0ýîtõt‡˜n~Ä­ñ§;kóƒðãÓÀÔxÁÌpã‘÷E/z“mýHäi’÷Þ€)åM¤^%iÙ)éìz­;ŠÔ9FwüÔôÎ鷯߮8[1‡¡€ð²R6o_µ%Ïúçê2?¨×ªÏ½’hŒ—½%=Žy L'?‹b3è™øeVÿÝQV?ï×$/‚)}E'κ<ÓØ©þ‹ÿà÷âhßÑ÷–õ¯-OÒµ~–­CpŽ˜!ü <Ì“±ÞŽÜˆŒn›ào*`ã}ùQõ{ÖÏgJÒ7êµj×?@íá‰*ö}ÙºaÛz™•7;L¤„‘âS¿ •¬[V]®ìäÞ—­)Œ>§¾5×'Ir¤¤ï%ià:0øAh‘ù—¬‰¬·#?8YÑne]ÆRáMÞ¸æg}þ¤v§cøAXÖøž_ÉËØ­Ô›—wß")Š'RàM§)~õÎEõ>}§J/=fU[Þ¸TÅm‰Þ_üœ„NÒÀt`˜ˆÍWJ^lý—ú©VQ¾ý,t­^µõ®üh’–IgÕkÕD·§ÒèŽ+Tì;ضܼVÚõ’ËD@:ô¬øíŸ².Ͼ ¾¡«’}‰ÁEòš{’´üÐÂ×$ú"œ`@¦™„ó3ÒwlëG"OwNr´°qz~¯zL¢YûËõZu“«<Â_¹üÃêóqÛzóü iÍ].#éàyŠ~Iq®dWµÕ?¸X™v3Ñ—É5wªßÿa’–C$Y?fÀt`ùA˜¼[3&¶ÚŽÇÒ=5øÑ¦t|¦¡y™‰$-Kêµj²©Rȃâ¨õSë†ñ=2÷_"YïߺW|ìŸHÏ·®¯õiã£.#éÍí;ÚîÙ-w5­qõ/»@^üê–.fZcê_qq’–ФûAÈŒL~ø€iàaïDl~eŒ¬¶#·b£;›eµø‘¦ôNoT‡f&“´\U¯U—ºÊÓ)ü œg2¹KlëÍÈVé¡Dϸ]+>áÒÀë¬ëûž¸A…‘Íûåk÷l_¥òÆ[“´œ*éï÷˯öiЈ̕y[oG^9Ù£ç•w H…¹šÐq¹F’–’ìïùN)?= /Q®hÿ‹æþK¤f¢k ;^Wüfû[ÌŠÛV©òÔ¯ök„ÊÐ5Êïy6IË?úAø–ý€tà[1´æÃ%/¶Þ޼µÓƒGÛ©déýù½ÊÚm$}²^«Ž8ŒÕ»þA=³ê¶å&ø•Ì ¡ËD@*˜bIÑ)Ÿ·®÷š{Ô7xÑ~Ï‘‰'Õ¿|‘Ld}º(/é:?Y|` èÀäáAQ[o|™ˆn›(+b;20¥÷dÇ4;“èJµïÖkÕ‡\åé~Ö•ëýŸÖ ÃÏÈ Ú/yºYôÎÏIeûëÅû‡~¤|s‡“,…ÝOª²îÆ$-ÇHú¶“0þ tàjDæçyOÛúG&ËÚ¡œËH@*¼Á4UÍ&:n½JÒ×Åé~ÔØµTÙœÝëÖ¤Ì}I­–ãd@ õNÅGœj]Þóüê}î·Iåu¿PaçIZ¾âáBWyü.tàY>´æì’[oG~¶]Ð`»ä2 EE:-¿Wž±>jÒ”tf½VM´I.•Æv~_¥×Û–›U?“ÙþŒËD@:”dscfl›úV^î0Ð>žbõ.’7i} ¥Ñ¾­î}cøÐÀÂyFºÔ¶~,òtçÏ6Þ—QÅKtÑ×êµj×?@í¯ZµPÅë‡cÍÖ'dü%.#é`ŒâçHësXu™²­³#?¶E}áµIZæIZì(€ÿ‚pÌB¯™%Y[oG¾o²¬=²»+èfU3®£³ãIZ~+éBGq:†„}j5n–çY]õh&Ædî]$bS‹«ïW<÷XëúÞÿªÒÖåý®òÓw¨øâ²$-Ÿôƒð Wyü_ è€cã‘w^É‹­·#¯oµ&*ºŒ¤BŸZZXHô‰ÓnIŸª×ªL™£;®V±o¶uýã×H{¶»Ë¤Å¬¹Š?˺<¿w³ú‚«úÃúW,Vf|8IË~ê*€}Їü <&oâoÚÖï‰2º{²ì2§çGU²¿RM’Ωת›]åéC+ÿT½sì? {ö1™u÷8L¤D&£há¹RÎîf2µÔ7¸Hû«Ïö«ÜÄnõ¯¾2IËIWùAhuòÀ+À8âa¡{K=[ýœE±Ñ¯›eóc Lé„̘Ï4“´ÜX¯U¯w•§SøAxˆâøÇÖ ]2\.qæ˜RüÖJm]_Y£Š»78L4µž-©÷Ùß$iù ¤¿r€Ðg‘ù~ÁDólëƒVI›d÷®;ÐÍÒ¤Þ‘³Þ@,I/H²^†–jc;ç{zl˽/—iìu™H‡CŽV\ÿ¨uyaç:•×þÂa {•ÕW(;úb’–ÿí¡ý;a@X5´fAÑÄÖÁŽ(«{ZÖ¯™®åéôˆòöWªIÒÙõZu‡«LÂ_öð¹ê™ýNÛz³áné™A—‘€tÈå/b]^ت¼þf‡¼¾¡)7’è–‡ïúAhý €ÿ‹HhÕКÓJ^üÛúíQV÷·8ÚL%£H§ö*g02–ô©z­ºË]ª1¾ç+ê™ý6Ûr³ö6™Í«\&Ò!_Ptê­Ë½‰õ/»PžÒµ#Ónªùb)jÛ¶$]ï¡Ý3þ:€„-Éz;òD$ÝÖ¬¨Í0¥…Ù1ä%z^óÂz­ú[Wy:…„ó•)|Ǻa×s2uöâ*à@‰O>[ªj]ßüXùñ­MŸâðZõm°~ $IÇIú†£8@j15 4"󓼉­·#¶ÊÚ&Þ<¦2OM½%›èžàP’õ’Æ´òƒ0 ߪ\ÁnÁEÔ–wÿbi2Ñ3þ@w:âÅG½Ûº¼´å1•Ÿ½Ëa éW^û3åwmHÒò5?Or•H#tÀÒàК3K^üAÛúÚy=Òæh;0•\ÜÖ{ #òŒõ‘ÐIIgÖkÕq‡±:ÃØðwLϬ#mËÍꥭO¹L¤COŸ¢“?g]žVÿŠÅÍ ^í;Âß²þõëIºÎ²ÃX@ª0 ü <Ì“¹Ò¶¾Ý6Áß"ÀÆûò£ê÷¬Ÿk”¤¯×kÕ®€ÚÂUìû[Ûz³}ƒÌÊt-®œ0R|ê¤Ò€uËÀÊË”Üë0ÔÌQ}N}k®OÒr¤¤ï9Ф:0?ÍxäÝœ5‘õvä'+Ú­¬ËX@*Ì7㚟MôAøC’þÙQœŽáa¯Æ÷üJ^Æn¥Þä¸Ì½JQ¢»åî4ÿ4ů³Þ¹¨ò3w©ôÒcÍ<åKUÜ–è}ÒÏùAøaWy€4a@¦0›¯½èÛú§ZEùö³<еzÕÖ+Œ&i‘ôÉz­šèãöTÝq¥Š}Û–{ƒ×I»^r™H‡þƒŸð)ëòÜè ªø?thæê\$¯¹'IËý |«<@Z0 /ÃÂùÉz;òHäéÎI޶6NÏïUI4k¹^«nt•§Sø+—X½s>n[ož_!­¹Óe$ <£há—çJvõQ[ýƒ‹•i7Ýæš¡òÍêOöæÄ!’~à( èÀàan<ò–fLlµ9Ž¥{&*jðcLéøLCó2IZ–ÖkÕ«\åé~G­¬Æ÷È܉Rv%3àD|ìG¤ƒç[×Wž¼EÅá5Í|½Ïß§žçïOÒò?ÿÂU ˜$€?`<ò¾Sô¢£lë×´KÚ\FRa¶Z:)—èhû6Ig;ŠÓYFwüÜÊÛrï‘+¥ÑÝ.éðš#¤cÿ‡uy~÷Sª<ñ‡:GÿÊ˔ۖ¤e‘„ä(ÐñÐßÃÂwLd½y8Êêד½.#©à™}GÛ &Ѳ²ÏÖkÕD¯þÒhõà£g«wŽõ¥ÌÞÆû¤§q H‡lNÑÂs{vË]Mk\ýË.³tQ’2­1õ¯¼$IKEÒµ~2‡¿?ÀáaïDl–#«íÈ­Øè®fY-~œ€)½ÓÕ¡™É$-WÕkÕ%®òt ?ç™LîRÛz3²Uæáî\\$Ÿð iàõÖõ}OÜ ÂÈf‡‰:O϶•*o¼5IË©’¾ê(Ðј(€ÿ¢™+ò&¶Þ޼r²GÏ)ï2 s5¡ãr$-›$}ÙQœŽá¡§±K”+Úÿ¢¹ÿÅã‰þ­îtø1ŠßlûWqûjUžú•Ã@«2tò{½qñ-?ßâ*ЩÐÿ`ÅК—¼øÏmë·¶sz0âh;0•Œ"½?¿WYû£í‘¤³êµêˆÃX¡±ëÔ3»n[nÂ%2/„.©`Š%E§|ÁºÞkîQß²Eu¶L<©þÁ e"ëSRyI×ùAÈà?`@þ„s¢8¶ÞŽ<Ý6QVÄvd`JïÎŽiv&Ñ•j߭ת¹ÊÓ)ü k]žÛ¦¾•—; ”.ž"õ.’7i}¦‘ôc?ûÆ::ºž„ó<ëíÈc‘§;'xî°ñ¾Üˆ*^¢«ˆÎ«×ª]ÿµ¿jÕB>o[o¶>!ãwý²{`jÆ(^pŽT°>0§U—)ÛbFù±-ê ¯KÒ2OÒbGq€ŽÂ€Ž®æ¡7y¿Ê˜Èz;òý“eí‘Ý]©@7«šqOÒò[I8ŠÓ1ü ìS«q³<ÏêªG31&sïEb!0µ¸ú~Åsµ®ïÝø¯*m]î0Qz•Ÿ¾]Å—%iù¤„g¸Êt ttµñÈ;¯èEÖW|¬oFE—‘€TèSK ò‰>qÚ-éÓõZ•)stÇÕ*öͶ®üiÏ6wy€´˜5WññgZ—çGžS_pµÃ@é׿b±2ã»’´\áá¡®ò€]ËÂcò&þ¦mýÞ(£»'Ë.#©qz~T=ÉŽ¶ŸS¯UŸu•§S ­üSõαÿéÙÇeÖÝã0™Œ¢…çJ9»7ÙMÔRß² •±¿2 ¿Gnb·úW_‘¤eޤ«ü ´:A¤:º’„…fì-õLlõ3ÅFw7ËçG˜ÒÛ½1ži&i¹±^«^ï*O§ðƒðÅñ­»d¸LâÌ0¥ø­gHm]_Y£Š»78LÔ=z¶<¤Þ͉ÞHü ¤Ï9ŠÌxLèJÈ|¿`¢y¶õA«¤M*¸Œ¤ÂAšÔ‰yëͽ’´E’õ2´TÛùË8ßÓc[î=t¹Lc¯ËD@:r´âúŸZ—†×«¼öuŸÊª(;úb’–ïùAhÿŽ " èè:«†Ö,(šØz ØeuOËú53е<#^QÞþJ5IúL½VÝá*S§ð—=|®zf¿Ó¶Þl¸[zzÐe$ L.¯xá¹’—±ª÷Z õ-»@ž=¢ƒ)dÛãê_¾XЬÿ]{%]ë¡Ýÿq@Š0 £«øAØ×’n1FVÏ6µbOw4Ëjó£LiAfT{‰ž×¼¬^«Þá*O§ðƒð(e ß·nØû¢Ì#׸ ¤HôŽO*î›k]ß^«ÂØ u¯ÒÎ@•¿JÒr¢¤óÅf,¦t•Fd®Î›Øz;òàdI/Êú6 k® ½%;–¤e½¤¯:ŠÓ1ü ÌÄcÃK”+ÚÝÝEòî[,M$zÆèN¯«âù°./¾4¨ò¦ÛBeÍõÊíÞ”¤åë~ï*01 £k¬z⌒[oGÞÒÎ鑨×e$ òŠô¾Âˆ²öGÛےΪתcSž IDAT‰&ú4ŠÇ†¿izf½É¶Þ o‘^\ç2¥²¢S¾`]î5w«ÅÅA’¼¨¥å‹dZÖo2f%]ïaÉa,`Fa@GWðƒðXÑu¶õÍØÓíeElG¦ôîܨfy­$-߮ת»ÊÓ)ü <Î+VìoîØ( ²¸ °ŸòWRÏ,ëúÕW*×v˜ÿGaÏ&UÖþ,IË|Iç;ŠÌ8 èè ÈÜ”3²ÞôöÐD¯†•s H…£MSoÎ4’´,“ômGq:†„E5v-½¬Ý]¿“M™û.’ÚmÇÉ€ÎÿñBÅóN²®ïÙü[õ¼ð€ÃDø¯úž¼E…íA’–sý <ÍU`&a@Gê ­ùbÉ‹O±­º•×ʈ“TÀTJq[ïÎ1%©¡}GÛ}ÜžJc;/QiÀzs•YyƒÌÎç\&Ò¡2G:ñ3ÖåÙ±—Ô¿ú ‡ðûÅX~¡2#Iš~ì¡ý± C1 #Õü <Ò“.´­3º}²â2(Œªì%ºŠè«õZµë öW­:M=³­'³Å—bq0%c-8GqÞrL©ùÅÊ´º~Æ´È5¶©\“¤e®¤Kݤft¤–„™ñÈ[š5±Õvä8–îmöjL\¹ Lå-^CoÈŒ'i¹SÒeŽât ?Կɶ>nŽÈÜwñ¾_P^V|̇¤×c]_Ù´T¥¾ÃD˜JùÙ»Uzáá$-æáÇ]åft¤Öxä}³èEÖÛ‘×µKZ]FR¡_-’MÒ²SÒgêµ*SæèŽT¨ôÛ–{^%ìt™H‡9‡+>îÖå¹=O«\ë0lõ¯¼T™ÆŽ$-—úAx˜«<Àtc@G*ùAøÖ¼‰­·#ºkÂz‡ÐÕ>˜ß«b²£íŸ¯×ª/¸ÊÓ)üåž©Þ9§ÛÖ›§–ÙÀâ*`J™Œ¢…#eí–»šö„–]¨LÌ:Œ™ ;¹W«.OÒ2 }Ï£Ûo@::RÇÂb32·z&¶úÅÝŽînöjÒp´˜ÊI™1ÍÍL&i¹¾^«výÝ`~f¼ì•Ö £;dLô‚èZñÛ>&Í>º¾²îg*ìÝä0’*½ô¸zŸ¾#IË{$ë(0­Б:È\\ðbëíÈ«[%=£‚ËH@*¬I½=—hãîfñJ~í¼%Ε¬Ÿ¡1^&³¸ ˜ÒÜ7)>æ ëòÂŽPåõ7; „WªoèGÊ<Ÿ¤å|?­e::ReÕКÓJ^|¶mýö(«û[m¦’Q¤Ó {•³?PKút½VÝå.U‡ßóõÌ~»m¹Y{»ÌæU.é/(:õ‹ÖåÞĈúÉë0f¢L»©þåIQÛ¶¥(éz?ížm::RÖd½y"’noVÔæÇ˜ÒÂì˜ò=¯ya½V½ÇUžNáá|e ß±nØõœÌc,®lÄ'Fªj]ßüXùÆKáÕ*¯Uß“¿LÒrœ¤¯;ŠL &¤F#ö~’7±õväÁVY[Å›®ÀTæ©©·dIZÖH²^Ò˜V~æâ±á¥Êì\Dmy÷/–&=ãt§#NP|Ô{¬Ë‹/>¦ò³w9 „ý¥üÄO•ßµ!IËy~žä*p 1 #‡ÖœY2ÑmëŸoçôH›£íÀTrq[ï-ŒÈ3ÖGB'%}¢^«&º$=•Ɔ¿czfe[nü›¤­O¹L¤COŸ¢“?g]žÖÀòÅaòâHýË.”iYÿñ$]ëaÙa,à€a@GÇóƒð0£Øz;r#2º}¢â2ï˪߳~P’¾Q¯U»þj?ß¡bßßÚÖ›ídV$:Ö t'#ŧ~A* X·ô¯¼\ÙɽCa+Œ>§þ5×'i9JÒ÷Å(tt4?ÍxäÝœ3²ÞŽüàdY»•u H…ùf\ó³‰>HÒwÅé~öj|Ïy»•z“ã2÷.’¢DwËÝiþiŠ_÷6ëòò3w©ç¥G‚+åKUÜ–èýÞÏùAø!Wy€…­™¿+zÑ ¶õOµŠò£’ËH@*ôª­wF“´ŒJúd½VMôq{*î¸Bžƒm˽Áë¤]/ºL¤CÿÁŠOø”uynt‹*þ‚k}ƒ‹ä5÷$i¹ÊÂ׸Ê èèX~ÎÏo[?{ºs’Ç“§ç÷ª×$šµÿ¦^«nt•§Sø+—X½sþܶ޼°RZs§ËH@:xFÑÂ/)ÎY¾ÉµÕ?x‘2í¦Û\p*ßÜ©þdo²"éŽâ::’„ÙñÈ[š1±Õvä8–~Ó¬¨Á·<0¥ã3 ÍËL$iYZ¯U¯r•§SøA8'ŽZ7X7Œï‘¹ïbq%30µøØ?‘žo]_yò‡×8L„¥÷ùûÔóüýIZ>âá§ÅœcZAG¼ó‹^d½yM»¤ qÁe$ f«¥“r‰Ž¶o“ôYGq:ËèŽ_˜BÙz¥yäJit·ËD@:¼æéØY—çw?¥ÊZû÷Ê0óõ¯¼L™±mIZ.òƒðÅœb@GÇñƒðYoG޲úõd¯ËH@*xfßÑö‚I´¬ì³õZu««Lbõà£g«wλmë½÷É<õˆËH@:d³Šž«Ø³[îjZãê_v¼ˆui’i©å%IZ*’~ì!³:ß´è(~ö6#³ÄYmGnÅFw5Ëjñ­Léą̈ÍL&iùQ½V]â*O§ðƒpžÉd/µ­7#[efq`#>áLiàõÖõ}OÜ ÂÈf‡‰0]z¶­Tyã­IZHúª£8€3L-è(È\QðbëíÈ«Z=zNy—‘€T˜« Ÿm$iÙ$éoÅé~zþ•r%ë_4æK'ú·ºÓáÇ(~ó‡­Ë‹ÛW«òÔ¯Ât« ]£üÞDoÀ|˺«<€ èè+†Ö|¨äÅÖÛ‘·¶sz ÍÑv`*EzaDYû£í‘ö]©6â0VGˆÇ†ÏSϬ·ØÖ›p‰ô|à2 ¦XR|Ê¬ë½æõ^ä0f‚L<©þÁ e"ëÓ^yI×ûAÈ""t tt?çDqüSÛú‰Øèö‰²"¶#SzwvL³½V’–ïÖkÕ]åé~còåoZ7ìzVf‹«Ñ;ÿRqÙþ:ëþ¡)7¾Ýa"Ì…ÝOª²îÆ$-ÇHú–£8À~Ç€ŽŽÐˆÌÏ󞬷#?:YÖvå\FRá ¦©Z²£í«%}ÃQœŽáaA]K•ÍYý5QKæÞER+Ñ!@WŠë*O§Z9øQõαÿäåÙÇeÖÝã0™Œ¢w}IÊÙ½Én¢–ú)cÕR,7±[}«¯HÒr¤«ü ´: h è˜qü ,4coiÆÄVߟQlô뉲Æùv¦ôvoL‡gšIZn¬×ª‰òK#?Q_kÝÐØ%óàe'Û)Åo=C:èhëúÊúUܵÞa"tšÞ-©ws¢7D?(ésŽâ¯  fœFd¾_0‘õvä UÒÆ¸à2 iR'æ­7ÞJÒIÖËÐRmlçMq¾§Ç¶Ü{èr™±½.épÈQŠëj]^^¯òÚ_8 „NUYõeG_LÒò=?íßtÌ(«†Ö,(šØz ØeuOËú53е<#^QÞþJ5I:»^«îp•©SøË=G=³O±­7~-=hiЕL.¯xá—$/cUïµê¼@žX‡ß•m«ùb)²þþè•t­„v߀À€ŽÃÂJKºÅY=ÔŠ=ÝÞ,«Í·10¥S3£:ØKô¼æeõZõvWy:…„GÊË]`ݰ÷E™GX\؈Nü¤â¾¹Öõ}áµ*Œ¾à0:]ig ÊÆD×Zž(é$½öëúʦ¥*m_í0ºEùÙ»UÚòH’–?óƒðÏ\å¦Â€Ži1yß,z‘õväõí’ÖÆE—‘€TèWK§æF“´ì”tv½VeÊÛù*ý¶åÞc?’FvºL¤ÃœÃ÷ ëòüž§U®uݦÅ%Ê4v$i¹ÌÂÃ\å^:8?ßš7±õväÝQVwNXïºÚéù½*z‰Ž¶¾^«>ï*O§ð—?z¦zfжÞ<ý°Ìúû]FÒ!“Q´ðo¤¬ÝrWÓžPÿ² •‰Y‡ý';¹W«.OÒ2 }Ï£ÛorötP~›‘¹Õ3±Õ/¼vltw³W“†£íÀTNÊŒé°Ìd’–ŸÔkÕ®¿ÌÂÃŒÉ\iÝ0¶SæÁD/ô€®¿ícÒì#¬ë+ë~®ÂÞM¡[•^z\½Oß‘¤å4Iç:ŠüA è8 ‘¹¸àÅÖÛ‘W·JzF—‘€T8X“z{.ѦÚÍ’Îq§cøAh46|sœï±~†ÆnO¥ÑW¨Ô°m¹7x´ëE—‰€tè?Xñ Ÿ².ÏnQÅ¿Òa ÀNßà"yÍD§8®òƒð Wyt8áá³FÖÛ‘GcOwNòX`ãôü^õšD³ö—ëµêFWy:…¿rù‡Ô;çÏmëÍ +¥5wºŒ¤ƒg-ü’âœå›ìQ[ýƒ)ÓnºÍXÈ7wªß·~S’‘t…£8:ö??³ã‘wkÆÄVÛ‘ãXúM³¢ߎÀ”ŽÏ44/3‘¤ei½VMôÊ#ü œG­ŸZ7Œï‘¹ÿq%30µøØ?‘žo]_yò‡×8L$Óûü}êyþþ$-ñƒÐþÈö»ñÈûNÑ‹¬·#¯i—´!.¸Œ¤Â,Mê¤\¢£íÛ%}ÖQœÎ2ºãç¦P¶Þ@i¹RÙå2 ñA$û1ëúÜ®§TY{ƒ»@À+Ô·òreƶ%iYìá9Šƒ.Æ€ŽýÊÂwLôw¶õÃQV¿žìu HÏH̨`-+;»^«nu•©S¬|ôlõÎym½yê>™§q H‡lVñ»¾¤Ø³[îjZã¼@^Ä: Ì<ÙÖ¨úW^’¤¥"éÇ~2Oa¿â û„=ÍÈ,1FVÛ‘[±Ñ]ͲZ|S:93ªC3“IZ~T¯U—¸ÊÓ)ü |½Éd/µ­7#[å=ÒõOVâ>! ¼Þº¾ï‰TÙì0ðêôl[©òÆ[“´,ôGqÐ¥˜Œ°ß4"seÁ‹­·#¯jõè9å]FRa®&t|²+Õ6Iú²£8ÃBOcÃK”+Yÿ¢1\ªx<Ñ¿5Ð?Fñ›ÿëòâöÕª<Õõï¢T†®Q~o¢7’¾íaÝUttì+†Ö|¨äÅÖÛ‘·¶sz ÍÑv`*EzaDYû£í‘¤OÖkÕ½cu„xlø<õÌz‹m½ —HÏ.#©`Š%ŧ|ÁºÞkîQÿàEbë":A&žTÿà…2‘õ©µ¼¤ëü d¡ö t¼j~Ή%ëíȱÑíeEü¦ôîì˜f{­$-ÿ\¯Ut•§SøAxŒÉ÷~Óºa׳2ËX\؈NþKÅå×X×÷ ]­Üøv‡‰€ý«°ûIUÖݘ¤¥.é[Žâ Ë0 ãUkDæç9[oG~t¢WÛ•s H…7˜¦jÉŽ¶¯–ôuGq:†„5v-U6oõ7ÎD-™{/’Z‰ÞºR|äÉŠß°Àº¾çùU~7ÊënTaçÚ$-_õƒÐþ‡øÐñª,Zó™’[oGÞÜ.hYÔã2 EE:-?"ÏX5iJ:³^«&º$=•Fw|_¥yÖõ+&³ýiwy€´((>ÉþæÆLc»úV^æ0àŽ§HýƒÊ›´¾ÞÔHºÖÂ>‡±ÐÐñŠùAøzOÆú/ïXäéŽ ž;l¼/7¢Š—è*¢óêµj×?@í¯Zµ@¥YŸ·­7[×ʬþ•ËH@:£xÁ9RÑ~öXy©²­‡¡·òc[Ô^—¤ež¤EŽâ K0 ãñƒÐkDfIÆDÖÛ‘ïŸ,kìîJºYÕ×ÑÙñ$-÷JºÐMšÎáaE­Æ-ò<««ÍdCæÞEb!0µøÍïS<÷XëúÞM·©´u¹ÃDÀQ~úv•^LÒòi?Ïp•éÇ€ŽWd<òÎ+y±õvä ­¢Â¨è2 }jiA.Ñ'N»%}ª^«Z¯yO­ÑרØ7Û¶Ü<~µ´g›ËD@: ¼VñÛβ.Ï<§¾¡9 X}Ë/Rf|W’–øAx¨«å¯¥žYÖõ«¯T®¹Óa"`f*ì٤ʺD[æKú®£8Ht¼¬Fôÿ³wçaR•gÞø¿Ï9µ¯ ˆ¢ˆ(¢TQ²i‚Qðu‰[&ëdƸ$jÌ¢1™üfÞ$×;ÉÌ\3o23‰,î{·‰KÜbŒt7ÍésdGÙmº»ºöí<¿?È;W2Qú>ÐOwÕ©ûó_Æû¡¿6NußUϹoñ´W€<émM%Œ>xUFb̦ˆ¦é'GÚü‹¢8 Ã0­ ý/HÍCÛ-[-C¬ZÔjŠ“1Öøä©ó!OœK®}ð{„ö¼¡0cõ-²ùø{L'G¦u¡ª≮Ag«­«û¦ &ÉÓ‘wT}h¯UFbÌ‚°q?Ak1 àêT2áèãvWÊ÷ÞŽ`ËqÔrÑñ(Dï.•‰s‡èhà“דË=ùýˆ¯¿Wa ÆêŸ‰xÛbè嬓c¦E¿¦Âš7èì#¦u²§#礎+Q•‘sK|Y„…£UDŸJ&šþj£³óB„FZ/öuAtýVe$ÆÜAØó@úèócâw@¯6ý8 Æà+@Ô|ØÉ‘ îT“†¹7èì/¦¥mm….$鮺”Àë¥0ràŒ &¥0Y/:9ò€;Åi†iµ V|ŠZ/KYˆUK½@1ÆKN¿8v:¹>ºõ9{Ö+LÄXc‰¼ÿ ‚{×89r…aZW¨ÊÃ7èì/mí'Í&OGÞT b£ä¡”Œ &Ž*Îñ:º× àë©d‚»Ì\Ï£ðGãÔrí\ÅØ Z‡œu%¹Ü7°ó…kLñŽ; ýܹË0­ ªò°ÆÅ :û3†iÍô ù#j}ÚÖñR™‘†5…Ãþ!ùj`S1L+P²Å¿&IïæI |(½¨H~]al0!a;]©öh*™¸ZUžFa˜ÖQÎo‘¾íšN¥ì5€ZEq22xë IDATÆ\`Ô‰@ËñäòX÷2Ä6‘Ÿ4a¬)õ͸ ¹I;9²0•L,Q•‡Õ1È”`nÐÙûƒÑ}P“äLŒ1ev˜žJ&úG:ÈH2LK ß÷B£Îé,Œ5;o7ZWÿøwCƧ¦ûÑó¿nC%B¾½^03•LlT‹Õ‘Át¾âÎ]ÝpsÎXÝøZ³7ç€Bú{Üœ36ò„]EÔxˆ›sÆôZ ñö¥€]£ xÔ0-Òpfæ~Ü 3¦ÕRžéŒ1À¢T2ñÚH‡i†iOàg#ƒ1H̓9 QóðÌÆ(}ÝüŒ“#³ü£¢8¬ÁpƒÎPÚrŸäéÈŒ1eºü`¤CŒ4ô<2ß·^?ïnd¬N”#Ç#=óÆ‘ŽÁXÈn| ¾þ-NŽüÀ0­OªÊÃ7èM®½Ëº*(ìËG:c UW§’ GKÒ])ß÷3uÊHÇ`Œý¹ü„yÈM˜?Ò1kš´o[Q%ÿX×,3L+¢0kÜ 7±?î^¼¤s0Æÿ˜J&Ötˆ‘f˜Ö'ˆ}w¤s0Æ>Z:uÊþÑ#ƒ±†àÏ~€x÷£NŽœà?Åa ‚ô&e˜–(Øâ¯mucL¥·üûH‡i†i…PH?MçÝŒÕ)ÛÃÀœ…#ƒ±†Ùö<v:9ò-ô.S•‡Õ?nЛTÉß j’§#36òr¾šJ&Èã^]+wð>ããF:cìðŠcg ;ù¯F:c #Ö¶ZiÀÉ‘ Ó£*«oÜ 7!ô¦zx:2cõáÖT2±u¤CŒ4c]ûå·^9Ò9c4éiW£>~¤c0Ö|¥^Äœ9À=Šâ°:wØk„Rò¾K·1LËS´µî€fO¡ÔK ¼^Žb—Í«ÌTO gysN޼J&šþc(ôZe1³]¢QÒRÚKÿä{'c¬ñÉäåÉÏÓØ6 Ñ>¿ñõoƘUMÚG˜Ž±æÒ{æß!?až“#צ’‰_ªÊÃF†‡’Ï3L9X(ÚÚO©Í9tׂ调*#1æ £PÁž‚“#=¾®(NcÉ|R„[iÍ9ñö=À¦¿tÀØ ä˜IÀ´Ïë½ý[8ÐÌ©_&Õ—[¦ {úWs6‹±¦[w7J£NG-4–zd©aZ¯§’‰*s±úÂWÜ›ˆaZgù…ý=j}ŸíÁ«•°ÊHŒ¹‚&€K}Yø…£O‘nH%TejëÛÞ¹áÖ ¨õbë*ˆ­kTFbÌ<ÈónÔhŸÅˆj-m‹Ýð|ý›É_fà”/¡8ê´#MÉXSñTsˆ¯»ÓÉ‘(€G Óâž­‰ð_v“0L+T²Å !ÿXÃÿS•/—"¨ò"Œ êl=‡cõŠ“#¦’‰çTåi†iMºç.j½È€¶†7C2F!Ϻ h™H®mx þìû‡v7¯½ uw³¦#=ûÔtÿ&e¬¹„>ì@dÛ NŽÌ@þ€5>DÁ÷ú5IžŽÜY a|*#1æ ãQÆlgWÛw¸UMšÆa˜–†|ïóðÉ/4â;!‹Ž¾×Œ5§ IÈiôñÑ­Ïÿ÷ÿöçv#Ö½œ|¾™€é×;ŠÈX3 ì|íмº5L+¥*«/Ü 7Ž®î˃š¼ŠZ æÅ5¾ÚÎØ`tظԟ…‡~µÝpM*™È(ŒÕd¾ï‡>ƒZ/¬çݦÊHŒ¹‚!Ͻ™\¯•o[ àÏG¶­@ðÃuä?'7éŽáí­Œ ÆfÞDÆøG>Ë Óâ«*M€t—3L«USëËR`e9›ø36¨ó=yŒÖªNŽüG*™xSUžFa˜VRøÂÿD>Ðÿ>ÄÚÇ&bÌ=ì¹7@FȨëzÞbÏGÿ³¶%Žv7÷Ïø6ª^ò¼GÆšRæô+Qn9åHަüóÇauˆt—+Øâ ¯äŸ–ï”Ãè¯Tcl0“E IgWÛ×ø±¢8 Ã0-? ý/Àã#ýüvâõ%@ÕÑ!Œ5%yòÙ'Ï'ׇö¼…È®×>öŸ{K½ˆ÷‘ÿ¼Z°é™7‘ëk6ÅQ§#sÊæøÿ Ór´§5nÐ]¬½«ûú &/¤Öïªù±Ö©ŒÄ˜+`ãB_š _5)¸:•L”Æj ¹ƒ¿@°åDrýº' zv¨ËØ[DZ çÞ@.× =ˆu >M:¼{5B»V“ÿÜÂqg#;ñ"r=cÍ¢¦û‘žs  éGóÇ¿4L+6D±XâÝ¥ Óš¨A÷8äm +ËüÜ9cy³ˆj5'G~”J&šþj£³ó\GÝH­6B¬oúa÷Œ NÈy7úïì-ëî‚§š%ÕÆ;§o…Ì$¯E%H¿fÏX3˜þuTÂÇ Å5 Àâ¡øƒX}âÝ… ÓÒ ¶x^6yÄêr íJe¬™%´"Nõ×ò:€ÛÔ¤i†iEQ+> M#­z•Ī%àŒ NNû4äq3Éõáí¿Eð@¹^¯æ︃\_óEÐ?ûV€¶Ù•1×+ó ä&]<”䵆i}a(ÿ@V?¸Aw¡¢­ý ¨IòtäÍÕ,P‰1Wˆ¡Šy^Ú'N4àk©dÂÑ.WÊ|þh+µ\¼û¦bÇXÓj9çr¹7³ ±®™PO§£ÝÍ¥1I œÂýcUoý3¾­â¾×0­ñ*þ`6²¸Awô’>!ÉÓ‘3¶†—+•‘sK|9„4G½öwRÉÄûªò4Š®um_D¸õKÔzñÁZ`ãÇ®bŒý‘¦Á>o!ॽÉ.ì*âí‹¡Û•#úrÑ®‡á ¿¤eN»¥ØIGôµs‹ô¬ï ­âà~0YÜ »ˆaZ¾’Ô^Є$MŸ°¥À«å(ŠüŸcƒ:S/àÝÑŒ·§SÉÄ2Uy…aZã %ýûPèÞ¸ó®dfŒ}9ëKÀ˜)äú覧èßtÄ_O—ÄÛC|éñ£öBØ?BÇšSvâE(ûIr}h×*„?pôõå†i}Ëq0V׸3s‘‚-~á6y:²Y b›$?¦ÎXÓƒ >éìjû>üò½OK_ˆ¼B¼uD>£2cîpÌ)©¿&—ûz7!²ñÉ£þ²þôDßû¹¾? ™iWõ×e¬Ñ”ãI^K®÷ Þy7¢÷À“ÛçäKýÜ0­#Z¬Îê7è.ÑÙÕ}n@HòòÑ^ۃת¼R±Áh¸Ô—…¾R ®O%UejFû;7!4új½Øü*ÄŽµ*#1æ^/ä¼äuMZµ€xûmÐ04ã0"ïýü½Èõ™ÉŸCatrH¾6cÀ†@úÌ[QóÑ#·ß½š‡§VD¼ãvÀ&ÿÿkÀ#†iÕþ6V?¸Awô¢UàY!hãR«RÃK¥jü×ÏØ ÎÕs§;z^óîT2±RUžFa˜ÖÉÐ|‹È2û Ö<¤0cî!?ù5Èør}¬{9ü¹=Cöõ5HÄÛC«äˆ4¤g/@Uç´¬9d§|¥Ö¹>²u‚=ÿý¿ƒ»ÝæhÍè\?pr€Õ/îÐ\ `‹‡|B’§#·WƒØŸÊHŒ¹Âñ(c†'ïäÈfßW§a¦¥#ß·^¿—tÀ¶¡­¾(;zÆŸ±æ4qäÔKÈåý펦¯Sùò{³!×WÃã‘™ÁOþ0÷+EOBfêß’ë};5þ‹ÿ{´{9¼éíN¾ô Óšåä«OÜ 7¸Ž® _j’<y_Í‹·ka•‘sl|ÚŸ…‡~µ½àšT2AüHɽd¾ï':Z/¬_{7ªŒÄ˜;ðÏ!?Í­”>tUV‘ÈŽØG,%wÂùÈû)eyi¶æAÿœ…ÚŒ'aWo[ ]VÿâŸiv-í‹!ªä7¯=5L‹¯ª48nИaZã$ìåÔú’Ô°²…ÍÓ‘Ô…ÞFiùó0þ-•LüAUžFa˜ÖL-ýù@ïvˆ¶£\ÅX3ç|Ñ×5µ÷Ã[êU˜ˆw,…^ì#×§Ïø&*¾¸ÂDŒœÌ´kP‰ÓW F6> ÿÀÖýçþ툾çègäi~æä«?Ü 7°‚-žò '½­©„Ñ ^uÂØ`¦ˆ¦é'GÚü³¢8 Ã0­ ý+¤æ!ÍÃ@µ ñúb VSœŒ±Æ'Oyâ\r}èƒß#´{µÂD‡xËiÄ×ßG®¯Zžµ@a"ÆFF¡u:2“?K®÷ìFtÓSƒÖE6?ÿAËI”[ ÓºÐÉV_¸AoPm]Ý75y.µ~g͇öZPe$Æ\!ø3´ 8tµÝÑÇí®”ï½Áòä*ÑñDï.•‰s‡èhà“_'—{ _¯Â@.´÷-„ßÿ¹¾8þLd']ª0cëæ "=ë;€Fk­´JñöÅÐ0øµV ñµ‹ —­{}Ø0­'Xýས5YÈÓ‘sRÇÊrTe$Æ\ãboaáhÑß§’‰¦€Úè켡ÑäBìë‚èúÊHŒ¹ƒó@úèócâí·C¯:pyÔ¢ëïu´»y q Ê¡c&blø œñMTÃãÉõqëøòôÿñ|ä ¹Ã˜à.'Xýà½Á¦¥mm….$m:2€×KaäÀ«LJ+àdOÑÉ‘—Ü¡(NÃ0L«µâÓÔzQÎA¬Z HˆÁØ`äôË!ÇO'×G¶<‡`Ïz…‰>š§VD¼})yw³í #=g!lþU”5¸Ü±ŸBî„óÉõ}kÞñ¢ã¯yÿ÷¾ãäȆi]áø ±Ç¯Š ¦hk? hö4jý{Õ 6JæÈØ`â¨â¯£ëc½®O%Üeæz–Ã¥O}zç £vpcn G9ëJr½o`¢VŸ µ`¯‰èÖ_“ëK£OGvê—&bL­Š¿iëõbâKøëÅ;n‡^pôóóNôȞ±úÀ z1Lk¦OÈRëÓ¶Ž—ÊärŒ5µK}5G½öM©db·ª<¢«ãÝ«s9µ^ì\±Iýà*Æž®Cž·ðøHå‡[×4œ¢u´»93õË(ÅOQ˜ˆ1uf-€í§¿?_/¼åô=O%ƒ–λ…CÏ£Ó'ë°Ç zƒ0L+P²Å MHÒßYM ¼RŠ "øj;cƒ™«ç1A¯89òX*™húÝ`‡Þ•÷“ä{!Þtô‹cMKÎù[`4}]StãðlS˜ˆF³«hi[DÞÝ,5/ÒsnEMŸÜc¬.dOº …cæëÃ;_AhïÛGýuƒûÿ€ðŽ—œ¹ÀwŽú ³aà zƒ(Øb©_“ä+*F5ˆð«ŒÄ˜+ŒCs<Ž®¶ïp³¢8 Ã0-ÜÁg¤/D~†F{óN S‹1w8ö4ÈÄçÉåþÞnD6‘Ç@(çÏì@tãäúrôd¦_«.cC¬:Ó®!×{²{5èïg&Öõ¼¹=NŽü»aZ§ Y¦7è  ³«û‚ &o Ö÷جªòÕvÆ£ÃÆ¥þ |Î^ ¯M%ýŠ"5ŽBú{·žE-シߩ2cîàóÞçp]SÛ"Òº¦áÛò,ü=]äúìäÏ ?v¦ÂDŒ Òsn…í%þ®m×ÐÒq;<5GChK¯•o[Ø5ê‘€å†iñU•À z3L«¥ ![¨õk+¿9ÆØ`ND gx NŽlðEq†aZ™ï{^?mÀ…]ƒX½¨8zÆŸ±¦$' 9åBr}`ß»ïtô,ê°ò>D´ë!r}-4é™7*LÄØÑ)ÅOAÆÁæoz¢U–'ºá1øú·89òô>©*Ü ×±ö.ëÊ &?C­ß]óbMßyfl0^YÃEþtA¾ZpU*™ºûi*ß÷SE¹,Œ§€ýŽ~y`¬)ÉPòSÎÖ5µź¦áùàU÷Ðcå'ÌCnÂ|…‰;2µ?4”íƒ0Q-¡eí"h¶ºÍ š´o[Q%ÿz¢Xf˜VDY(vÔ¸A¯SÜYøµ¾hkXYŽ*LĘ{\ìË#îìjûSÉÄ:Uy…aZŸ@ ö=òžÍõ3¸Š±º%œ{$_˜C|ÝÝð”ÔeBñuwB/$×§S7 ì­0cÎe’×¢=\Ûø8üÙ âÏ~€¸³OéOðŸŠâ°!À z2LKlñŒW€<ùJixTÆbÌNEœª;ºÚþ6òóÖne˜V…ôóÐtÚ.ÕJÚë‹ÛVœŒ18í|ÈÎ$—Gv¾‚ÐþwZžJ-ëî"×Ûþæ,T˜ˆ1gòcg!;™|©ž.D·üZa¢?Ùú<:Äú-ô.S•‡nÐëPÉß j’<y[-êŒÄ˜+„QÃyþ­Å€€¯¦’ ò˜T×ʼÁø8j¹h[ôïS™ˆ1wˆƒ<ë:r¹'»ã>…ÔX‹ðŽÉõű3üW 1FSõ„‘žy¹^/go[ óf…XÛh%G·j4LkŒª<ìÈqƒ^g Óšêøwj}Njx±V‰1׸ěAX8굿›J&¶ªÊÓ(Œuí—#Üz%µ^ìYÑý²ÊHŒ¹ƒ& çßé%¾Én×ÐÒ¾žZIm.Eb]›ÝE®OO»¥ðñ 16¸™ßF-4–\5€·Ø£0ÑGó•"nŸŽ€cÜ£(; Ü ×ô¸çmDv½¦0Ñá…w¿ŽÐî7œù¢aZ_S•‡~Å«#E[ûi@³§Pë»kAl–~•‘s…Q¨àl_ÎÉ‘7(ŠÓXrŸ(y¥Xs?íW™ˆ1Wc&3¯ ×{û·"ºá1u†I o#b›Ÿ"×—[¦ {ýûÄØP)ûG#ú:¹^/D|Ý ÑÄÖÝ=ÿ¡“#K Ó:QUæ7èuÂ0­³üÂ&OGî³=xµÂWÛŒ&€K}Yø…£ae7¤’‰ýª25Šõmï\pëÔz±uÄVú:%Æš–ÇyÞ-m¸«¨ÑÒ¦v]ÓpŠl|¾þÍäú)â(úMƆÂÀœ…°ý1r}˺»à©d&¢ñTshé¤eðKô¸/¬üQ Ó •¥x^FWU¥ÀË¥ªü×ÇØ ÎÖs8V¯89òP*™xNUžFa˜ÖD¡{È?áEöChkîW‰1×g]´L$×Z×ô¾ÂDÃK“6âkA£înÖt¤g/@Mç[ƒlxd'ű3Èõáí/"x`­ÂDδ#²í'Gæ ¯QeJq‡W ¶¸×'ä1ÔúÎj»àS‰1W2f{­TÛ éwû¦¥!ßû<¼Aò xãÈ¢£ï5cÍiBr}:y Ç@t‹ûÞ3ôçv!Ö½œ\_‰é×+LÄØ!¥È HO»Š\ïÍîB¬ËÑp¶a5†/ó“#ÿj˜ÖtUy7è#¬£«ûò &ɯl/Þ¨ñÕvÆ£ÃÆ%þ,<ô«í6­Tùûi#Læû~ˆÐè3¨õ¢{°ÛT‰1W ä¹NÖ5eo[Œá^×4\"ÛV8ÚÝœ›t ãèûâsÊÒsn…ôHõ®"Þ¶ºíhí°Ðí bm‹!lò-B€å†iñU•Æ ú2L«UäéÈe)°²íΟӌ ©ó=y´jŽž×üT2áhô©¦•¾ð?‘ô¿ñî£ 1æöÜ #ãÈõQãÁY×4œâm‹ínîŸy#ª^òÜJÆÉœv%Ê-§ë£›žF ÿ=…‰ŽN ½Ñ÷~åäH À?+ŠÃˆ¸AA[<î’üSær=ðªŒÄ˜+œ„’ή¶¯ðcEq†aZ>ú_€ÇGúÙ ì*ÄëK€ª;W1¦’ Óš¨Aòæm +ËüÜ9cy³ˆj5'G~”J&ºTåiFg繎º‘Z/l„Xï¾ÁUŒ 9Èy7‡ëšªY…¡êO¼ÓÙîæÌôëP ŽU˜ˆ5“é_G%2\í^n·ÂDCË—ß‹X÷2'G&X¤&  7èÃÌ0-­`‹çta“0¬.G0Ú®TÆšYB+âTqmÏ!«Ü¦(NÃ0L+ŠjáYhiÕ£¨ V-Ä`lprÚÅÇÍ$ׇ·¯Dð@›ÂDõI¯æï¸\_óEÐ?{!@ÛPËØÇ*sr“.&׬CÔÙ ³ºÙþ[÷;zm¹Î0­/¨ÊÃ>7èìhk?j’¼Xqs5KÒ&I2ÖÌb¨bž×Ñ'N84µ½þï§©–;ø±Vj¹x÷! }@e"ÆÜ¡e<äœkÈåÞL}®k.¡žNG»›Kc¦càîØ‘«z£èŸA¾<­˜F¬}‰ÂDjÅ:–B+¥¹×0-ò*h64¸AF†i%|B’§#gl /W"*#1æûriŽzí©dâ}UyE׺¶/ Üú%j½ø`-°Ñýƒ«;jšyÞBÀë`]Sûbèô•H®íz¾úKsæ´+PŠNRˆ¹ZzÖͨG“ëã]÷Ã[êU˜H-o©ñÎ{œ yß5!Ü ô|%©ýF’4}–¯–£(ò_cƒ:S/`¢îhÆÛÓ©dâUy…aZã åròB?ðÆn]ÉÌØ’³¾9f ¹>ºé)ú7)LÔtYA¼¾»YzüèŸs+ldÎd'^„±sÉõ¡]«ÞMfX¯Â{ßBøGo´_n˜Ö7Uåa‰»¿aR°ÅÏýÂ&OG6«Al“äÇÔkZ­¨à“ή¶ïð-EqK¾÷)é ‘×Cˆ·îÈgT&bÌÆ ™úkr¹¯w"ŸT¨±øÓ[ín®ÄOBæô«&bnSŒC&y-¹^Ï@¼ónu†Ylý½ðäö99ò ôè âÙQá}tvuŸòfj}oMÇkU^©ÆØ`4\æËÂG_©_O%UejFû;7!4ú\j½Øü*ÄŽµ*#1æ^/äü[œ­kj_ <ãOEÞû/ø{7ë3'…ÑI…‰˜[ØHÏYˆšþiKÇЫä5euO¯ e´É¯;a¦E{acG…tÅ ÓŠTg… ­J /•£¨ñ_ cƒ:GËaœîèyÍ»SÉÄoUåi†iM†æ£¯OÉìƒXóÂDŒ¹‡üÄW!ãôuM±[×4\4HÄÛC«äˆ4¤g/@UçÁºìð²S¾ˆÒú›9‘­+ìéT˜hdv!ºíy'GæøßŠâ°?Á] b[<ì’<¹£ÄøTFbÌŽG3½ŽÞÍÞàûŠâ4 ôt™ï[¯ßK=£­¾(;zÆŸ±æ4qäi—’˃:M-o6¾ü^Ä,ú¸jx<2gð£²ìã•b'!3õoÉõ¾ˆš« 4¢ÝËàMowrä'†iÍR•‡ ºB]¾Ô$y:ò¾šoÙa•‘sl|ÚŸ…‡~µ½àêT2Aü(ƽd¾ï'"4jµ^t=ìݨ2cîÁ>ç&r¹VJ#Ö¾Ta wˆìx}ôÇkr/@þØO)LÄ•­yÐ?{!¤‡6ãIØÄÛC—UÅÉFŽfWÑÒ¶¢J~Þ`¹aZ|UE!nÐ1Lkœ„MžŽ\’V–£°y:2cƒºÐ›Ã(ÍÑÌK%P•§Q¦5S DH>л¢W1F!ϽÑ×5µ½®i8Å;–B/ö‘ëÓg|_\a"Öˆ2Ó®F%~¹>²ñIø¶*LTü™íˆ¾çègýé~¦(7èÊlñ”W€<émM%Œ^ðŠÆ3E”0M/89Òà_Åi†iPè_!5íu¿Z†¶j1P«)NÆX㓧΃<Ñɺ¦×rÁº¦áâ-§_¹¾…ô¬ ±FShŽÌäÏ‘ëý»Ýô”ÂDõ%²ùøZNŽÜb˜Öªò4;nÐhëê¾1¨Iòr&Þ IDATtä5ÚkA•‘s… lœïË@F. ®I%Ž&ɹR¾w)‚-äÉU¢ã1àà.•‰s‡èhØŸ¸ž\î)@¼ó…Ü)äpwsqü™ÈNºDa"Ö(ªzéYß4ZÛ£Urˆ·/††æ¹ÖªA"¾vô²£µµ¿4L«EU¦fÆ ú3Lk²,¦Ö祎•å¨ÊHŒ¹ÆÅÞ,"š£UDÿJ&šþj£³ó„Fß@>°· ¢ë7 1æB@Î_᧯kŠ·»k]ÓpŠvÞãhwó@â«(‡ŽU˜ˆ5‚ÌŒo¡O®[À—w´#Ü|ÅNâMp§š4Íô!d˜–^´µºäéÈ¿/…‘¯dl0)­ˆ“=E'G^p»¢8 Ã0­ÔŠä{z¢œƒ¶z) ›ç“ÆŽ”œ~äøéäúÈ–ç\¹®i¸xjEÄÛ—’w7ÛÞ0Òs¦mºe.”;öSÈp>¹>°o-Â;^T˜¨¾EÞÁ½ï89òô®P•§Yqƒ>„жö“€f“§#o¬°QòDÆGçx3NŽô¸>•Lp—™ï]”~í ®bl0rôñ³®"×ûv"ê`eûhÁ^ÑmÏ‘ëK£OGvêß(LÄêUÅß‚k÷ôbâ¼Y!Þq;ô‚£ßî4L‹ü7èCÄ0­>!ÉÓ‘Ó¶Ž—Ë¼R1ŠË|Y5G½ö©db·ª<¢«ãÝ+ýj½Ø¹bÓ*•‘s]‡œ àñ‘Ê­kZäêuMÃ)Ú½ÜÑîæÌÔ/£?Ea"Vf-@-@:¾þ^xËi…‰ƒ§’AKçÝNŽŒðaZ|Ueˆpƒ> Ó ”lñ‚&$éûiKWJT_mgl0sõ<ŽÓËNŽ<žJ&š~7Ø¡w³Åäù>ˆ7ý@f¬iÉ9 ´N&×G7> ÿÀ6…‰š‹fWÑÒNßÝ,5/Ò³¢&ÈO ²—t) ÇÌ!ׇw¾‚ÐÞ·&j,Áý@xÇKNŽ\à;Šâ4nЇ@ÁKýš$_íX_ b'ü*#1æ cQÁ£‰¢»Ü¤(NÃ0LK wðé ‘Ÿ¡ÑÞ¼(äTÆbÌÆO…L|ž\îïíF¤‰Ö5 ÿÀvD7>A®/Ç&"3ýZuXÝ(‡ŽÅ@â«äzOv/¢Æý 5¦X×ðæö89ò3ôNS•§™pƒ~”:»º/IžŽÜc{°ªJ^ÎXÓÒaã2>g¯Rצ’‰~E‘G!ý]„[Ï¢–‹÷^ÞçÁUŒ Êç‡=³uMm‹šj]ÓpŠmyþ“\ŸüäÇÌP˜ˆ4Òsn…í%þ®mÛh鸞š£!´MA¯•o[ Ø5ê‘ €å†iñU•£Ä úQ0L«¥ óVØ‚%n4¹ ç!?a¹>¸çmD>húm¬G-¼ë÷í~ÃÉ‘/¦õUUy܈_ˆŠ¶öšMžŽÜ] b³ô«ŒÄ˜+ŒBŸòåœépƒ¢8%wð ˆ’'PŠ5÷Ù~•‰s9f0ó r½·+¢Î(1…}ÛB‚Yn™‚ìiô¿o6òÊþÑH§¾N®× _w§ÂDÍ%¶înèù¹Ý0­UåqnÐ Ó:Ë/ì¿£Ö÷Ù¼Vá«íŒ FÀ¥¾,üÂѰ²o¤’‰ýª25Šõmï\p+y)³Ø¶b+} cMËã<ïH6ÜUT‹¼®©E6<_ÿfrýÀ”¿FqýÆYsÂöÇÈõ-ëî‚§’Q˜¨¹xªY´tÞåäHÀ/ ÓâÞ“€¿Iƒ0L+T²ÅóB€4ºª*^.EPæo-cƒš«åp¬^qrä¡T2Ñô£W Óš(tù£‘ýÚÛ<¸Š1 yæ•@ËDr}lããðgw*LÄŽÄ¡Ý͋軛5éÙ PÓùöc½ËNþ,ŠcgëÃÛ_DðÀZ…‰šSð@;ÂÛ~ãäÈ|ôu°MŒ»ÈAlq¯_“äéÈë«!ì‚Oe$Æ\a<ʘãu´Rm€[Õ¤i†iiÈ÷>oü[¤xóNÈ¢£ï5cÍiB2ñYry § Ñ-Ï) ÄŽ†?· ñîåäúJäx L¿^a"v´Já㑞Fž‹ ovb¦£¡f̘ù|™œùWô¦«ÊãÜ FGW÷eAM’_Ø^¬®ñÕvÆ£ÃÆ%þ,<ô«í6€¯¦’ G»=ÜHæû~€Ðè3¨õ¢{°«Ke$Æ\A‚ç:Y×”Aœ×5սȶŽv7ç&]‚¸3&bGÊÒg~Ò Õ »ŠxÛè¼YAÝ® Ö¶‚þˆÀrôøªÊapƒþ1 Ój•ÀÔú²XYŠÀæŸÓŒ ê|O­š£ç5ÿ3•L8êF†i%…/òOäýïC¼û¨ÂDŒ¹‡<ûÈÈ8r}ÔàuM"Ö¶ØÑîæþ™7¢ê%ÏßdÃ${úWPn9…\Ýü4ýï)LÄ ÞŒè{ÿåäH À?+Šã Ü Œ‚-÷ I~u~§F¼*#1æ '¡„¤ÇÑuëõþQQœ†a˜–…þàñ’v7 » ñú Êƒ«Œ¨É‹¨õ»j~¬µC*#1æ ظП…&ÈWMÊ®I%|?-ßû [è+J:Ÿ„èÙ¡.cnŽCÎý¹\/D¬ƒ×55šðîUí^M®/w6²'e0…jºéÙ ôþ4´jñ¶EФ£ 1ì(h°k[­’§94Õ>Š¿‰pƒþ?¦5Qƒ ÿäÍÛV–ù¹sÆ(.òfÓjNŽü0•L4ýÔFgç¹´?Î6Bt6ý°{Æ'9ÿ;@Àɺ¦;á©f†bªÄ×Ýåhwsfúu¨Ç*LÄ(¦_Jd¹>Ú½ þÜn…‰ØGñç÷ Ö½ÌÉ‘I©IÓØ¸Aÿ†ii[<§ ›<¸`u9‚Ðv¥2Ö̦iEœê!®»9d€ÛÅi†iEQ-< M#­z•Ī%àŒL»ò¸™äòðö•hSˆ©¤W󈯻ƒ\_óEÐ?{!@Û´Ë(sr“.!×?\‡è¶&b‡Ùþ[ö;z¼Î0­Ï«ÊÓ¨¸AÿE[ûAP“äÅŠ›«X’6I’±fCó½Ž>qðµT2Á÷ÓrB ÖJ-ï> ¤( ĘK´Œ‡=çr¹7³ ±.^×ÔèB®CÄAW3™)_P˜ˆ}œª7Šþß&×kÅ4bmK&bñŽ¥ÐJi'Gî3L‹¼ÒºpƒþG†i%|B’§#gl /W"*#1æûriŽzí©db§ª<¢k]Ûn%OŬ6þNe$ÆÜAÓ Ï[x¬kj_ Ý.+ƆC´ëaG»›¦^Rt’º@ì#¥gÝŒZüþ4â]÷Ã[êU˜ˆQxKýˆwÞëäÈô)ŽM€tšŽ\’Úo4!IÓ'l)ðj9Š"ûÔ½€‰º£oϤ’‰GTåi†iƒ”ËÉ ýoÜÅ+™#³¾9f ¹>ºéiú7)LĆ“.+ˆ·-‚°+¤zéñ£ÎBØ?Ò8\²/BáØ¹äúЮÕ;ÈÔ ï}ámºøŒaZßT•§Ñp‡  `‹Ÿû…MžŽlVƒØ&É©3Ö´ZQÁ\gWÛ÷àhÈ÷>%}!òzñÖ=@ž¾ç—±¦5îdÈÔ_“Ë}½›y×5¹?½Ñ÷~E®¯Ä'#súU ±ÿ§‡LòZr½ž?€xç]ê±#[/<ùýNŽüÂ0­“Uåi$Mß wvuŸòfj}oMÇkU^©ÆØ`4\æËÂG_©_O%UejFÛš}.µ^l~bÇZ•‘s¯ö<'ëš ˆ·óº&·:´»y¹>sòçQT˜ˆé9 QóÑ#wܽJ^ïņ‰^- Þ¾°É¯ŸaË Ó¢½@»XS7è†iEªÀ³BÐÆsV¥†—ÊQÔšûÛÆÉ9ZãtÚõÁ?º'•LüVUžFa˜ÖdèþÅä™}kR˜ˆ1÷Ÿø*Ðr<¹>Ú½œ×5¹˜ñ¶ÅÐ*9â ý³ ªó€`U¦|¥1ô7A"[W ÔÓ©0;Áƒ]ˆn{ÞÉ‘¹þAQœ†ÑÔfÁù„$OŸè¨±>•‘s… (c¦×Ñ»Ù[ü¢8 Ã0-ù¾ðú½Ô3ÚêÛ²£gükJò„§]J®èàuMMÀ—ß‹˜EßÝ\ GæŒo(LÔ¼J±“™ú·äzßÀûˆš« ĆDÄZoz»“#ÿd˜Ö,UyAÓ6è]¾Ô$ù!´}5/Þ²Ã*#1æ ظ؟…‡~µ½àšT2Aüýd¾ï'šF­]Ï{7ªŒÄ˜;BçÞD.׊iÄÚ—* ÄêIdÇJöÑÊM¼ùc?¥0Qó±5úg/„ôÐf< ûР?]V'cGK—U´´-†¨’?LðXn˜VÓ^UiÊÝ0­±6ùíÒ’Ô°²…ÍÓ‘ÔžFiŽ~`þ[*™xGUžFa˜Ö -ý!ù@ïvˆ¶'&bÌ=ä9ßB£Éõ-¼®©éÄ;–B/ö“ëÓg|_\a¢æ’™v5*ñ“ÈõÑÿÿÀV…‰ØPòg¶#ºÉÑï,§ø©¢8u¯)ô‚-žö ?_S £¼Zƒ±ÁL%$<'GÚü‹¢8 Ã0­ ý/HÍC{M®–¡­Z ÔjŠ“1Öøä©ó 'M®íZ…¯kj:ÞrñõôÝ͵À(¤g-P˜¨yF'‘™ü9r½ÿ`7"›èøY}ˆlzþƒ–“# Óº@UžzÖt z[W÷AM’§#ï¬ùÐ^ ªŒÄ˜+aã|_‚4rPpu*™p4IΕò½Kl™@-w©LĘ;DFÃþÄõärOáâw+ ÄêYhï[Žv7ÇŸ‰ì¤K&r¿ª@zö@£µ$Z%‡xûbhàk­FƒD|í"heGëw6L«EU¦zÕT ºaZ“5€<9/u¬,GUFbÌ5.öfÑ­"ú‡T2ÑôP ÐrùÀÞ.ˆ®ß(LĘKyÞ¿ƒuMí¼®©ÙE;ï'·\?ø*Ê¡ñ ¹[fÆ·P Ó¿1ëøòô¿V_|ň9ìw<€;Õ¤©_MÓ ¦¥mm…GHòtä×ËaäÐô«øTJ+âdOÑÉ‘WÜ®(NÃ0L«µâSÔOD9whj»äOŒœ~äøéäúÈÖçäuMMÏS+:ÚÝl{ÃHϹ6mc/û¹c?…Ü ç“ëûÖ"²ãE…‰Øpˆ¼ÿ ‚{ÿàäÈW Ó¢÷w¦iЋ¶öã€f“§#o¬°ÁnÚ጑ÅQÅ9ÞŒ“#}®K%Üeæz–Á¥_ÝzçA sPa ÆÜAŽ>ræ•äzßÀNDÍ_*LÄI°×DtÛsäúÒèÓ‘=õË ¹OÅÇÀß$×ëÅ>Ä;x³‚[Ä;–B/8Äy—aZäG]S4è†iÍð ù#j}ÚÖñr™Wª1Fq™/‹ æ¨×¾)•LìV•§Qtu¼{%Âc>C­;×@lZ¥2cî ëóo¼NÖ5-æuMìÏD»—;ÚÝœ9íoPŠŸ¢0‘» ̾µýýéøú{á-§&bÃÉSÉ ÅÙ¼Q4L«)®ª¸¾A7L+P’Ú š¤W[ ¼RŠ "øj;cƒù„žÇqzÙÉ‘ÇSÉĪò4 ôŽÄäù>ˆ7ypc$sþhL.n|’×5±¿ ÙU´´Ów7KÍ‹ôì…¨ Þú3˜ì¤KQ8f¹>üþ«í}[a"6‚ûÿ€ðŽ—œù4€ï(ŠSW\ß l±Ô/lò•ˆõÕ v‚ö®;cÍl,*8Ëãhçn7)ŠÓ0 ÓÈ|VúBägh´7ï 9•±s‡ñSa'¾@.?´®é)…X#ólGt#ý=årl"2Ó¯S˜¨ñ•CÇb q ¹Þ“Ý‹èúû&b#)Öõ¼¹=NŽüÌ0­ÓTå©®nÐ;»ºÏIžŽÜc{°ªR‰1WÐaã2>g¯ _K%ýŠ"5ŽBúV„[Ï¢–‹÷^Þ_§2cîàóÞït]Ó"^×Ä+¶åYø{Lr}vòg3Ca¢ÆeC;4PÏK|ŒÔ¶ÑÒq;<5GChYÑk%ÄÛ–vz$`™aZä¡ßȵ ºaZ-UàiêNæŠV–¢¨¹÷[ÂØ™çÉcŒæèyÍ%©dâwªò4 ô¦Âøj½ØíGTFbÌ5äÜë€(}]SÜz¾ü~…‰˜;H´´/‚î`wszÖͨyøŸÿ);õË(¦øÝúkzéoްÆèëFtË3NŽÌðÅ© ®íF R[æ’<}¢­Á¸úÍƆĉ(á OÁÉ‘ þAQœ†a˜–GæûVÀë§ ¸°k«–@V=ãÏXsš4rÊ…äòÀ¾wæuMŒÈ[øQ»›k¡qHϸQ] TŠŸ‚ÌTú¤{oz¢U˜ˆÕ“è†ÇàK;šòCô>¡*ÏHseƒÞÞe}%(lòtä=5/Þ®ñ;Œ Æ+k¸ÈŸƒ.ÈWB«®N%|?-ß÷EhÔj¹0žöoQ™ˆ1W¡(ìO}›\h]Óí 17:´»y ¹>ü<ä&ÌS˜¨qÔ„é9·Bj´ÂDµ„–¶ÅÐlÞ¬Ð,4i#¾ö6ˆ*ù×EÀrô\¹vËu ú¡éÈxZ_´5¬,E&bÌ=>íË#îìjûORÉD‡ª<Â0­³ˆýù@ÏfˆuŽ®{1Öœ Ϲ:X×Ôy¯kbG$ÞqôÂAr}:õ Tü£&j ™é×¢=\Ûø8ü™ê±ºäÏ~€Ø†Çœ9À*Š3¢\Õ ¦% ¶xÆ+@žŽüf%Œ~ÁWÛÌ©¢ˆ©º£«íküTQœ†a˜V…ôóÐtÚDŒJÚëK€y` cMKN=˜Hž¹ˆðÎWÚGÿ”±?åtw³ía`Î- Õ¿üØ™ÈN&_j…¿§ Ñ-¿V˜ˆÕ³èÖçø°ÓÉ‘o¦u™ª<#ÅU zÉ·5I~a[ÕõvPe$Æ\!„Î÷ç@º à«©d‚»ÌÜÁ{ŒC-íËþ½*1æ±±À'è+­<Ù½ˆ÷+ ÄšAp¿³ù…±3‘üW Õ¯ª'ŒôÌ›Éõz9‹–¶EoVhj±¶%ÐJNŽ<`˜V«ª<#Á5 ºaZ§zÈÓ‘³¶†Ë|µ1ŠK}Y„…£^û{©d¢é 6Öµ_†pëUÔz±§ÂzIe$ÆÜAçÝé%¾Én×ÐÒ±”×5±!ëzÞìnrýÀ´«Q OP˜¨> Ìü6j¡±äúh׃ð{&bÀW:ˆxùieà^EqF„+tôPÊ@¬¾ýÐ cì°äŸƒw:¹>ºõ9z-…‰X3Ñk%ÄÛé»›mOé3¿ [¸â×n’Ü„ùÈ;’Üó6"4ý6VöGá]¿Gh÷NŽ|Ñ0­¯ªÊ3Ü\ñJQ´µ h6y:rw-ˆMÒ¯2c®0 |Ê—sr¤À×Åi,¹ƒ‹@4J-oßdûU&bÌ䘙Wë½ý[y]r¾ ˆmyš\_n™‚ìiôÿnYÙ?éÔ äz½pñuw*LÄQlÝÝÐó:9²Ô0­UåN ß ¦u–_Øß§Ö÷Ù¼VqåD~Ɔ”&]m÷ Ûɱo¦’‰ýª25Šõmï\pëEÔz±}5ÄÖ·UFbÌ<ÈóBjR9¯kb*E6<_?ýi®Ì”/¡8ê4…‰êÃÀœ…°ý1r}˺»à©d&bÈSÍ¢¥ó.'Gb~i˜VÃ÷· ý/`˜V¨d‹ç…itUU ¼RŠ ÜØÿÚŒ ‹¹ZÇê'GJ%ϪÊÓ( Óš(tù£‘ýÚ[÷©ŒÄ˜kÈ3¯Z&’ëcƒ?»Sa"ÖÌœîn–šéÙ PÓÝ{‹3;ù³(ŽA®oÁk&b,x ám¿qrd>€ï*Š3lºS-Øâ¿&ÉÓ‘×WCø>•‘s…ñ(c¶×ÑÕönU“¦q¦¥!ßû¼Aòo_âÍ;!‹ŽÖ×1Öœ&$!Ÿ%—zºÝòœÂ@ŒþÜ.Ä»éPT"Çc y½ÂD#§>éi乨ðfw!f:ÆšPÌ|¾ì.'GþÍ0­éªò ‡†mÐ;ºº/ jòjjýÛ‹Õ5¾ÚÎØ`tظğ…—¾RÍðµT2áh'†É|ßMþè@t¿ìêR‰1W ä¹7‘ëõrq^×ĆIdÛóŽv7çNº…qg*L4ül¡!}æw!=R½°«ˆ·/…^s4„–5!Ý® ¶výQ%€e†i5ìU•†lÐ Ój•ÀãÔú²XYŠÀæŸÓŒ ê|O­š£ç5ÿ3•L¬V•§Q¦•¾È?‘ô¿ñîr…‰syö ‘qäú¨ÁëšØðе-v´»¹æ¨zÉsDë^öô¯ Ür ¹>ºùiú6*LÄÜ$ÞŒè¦_99r€VG¹†lÐ ¶xÜ+$yúÄ*aôÀ«2c®pJHz]·6ü£¢8 Ã0- ý¿ÇKÚÝ(ì*´UK*®bl0òä¹°'Ï'×÷¼È®×&bì/ùJ½ˆ÷“ëkÁV¤gÒo…Ô³â¨Ó1pÊ—Èõ¾þ͈l¤o!e "ÿ þÞ÷œù¾aZçªÊ£RÃ5èí]Ý×5IžŽ¼«æÇ»µÊHŒ¹B6.ôg¡ òU“2€«SÉßOË÷þÁújÎ'·+ ĘK„ãs¿A.× ï¸Ca Æ>^x÷*„vÓ/”Ž;Ù.P˜H½šîGzö@#½? ­ZD|ímФ£ 1ŒAƒXÛ"hUòI€G Ó¢¯¨ Õ ¦5Qƒ ÏÛ/H +ËüÜ9cy³ˆi5'G~”J&šþj£³ó\Zn&8°¢ó× 1æón×5U³ C1vxNw7g¦_Jp¬ÂDj L¿•Èr}¬{9ü¹Ý 17óç÷ f=âäÈ$‹Ô¤Q§atôDÁÏéÂ&?ð¿ªÁh»RkfÓ´"NõÐÖÄüÑ*¿P9÷:y IDAT§a¦Aµð,44ROT ÐV-Ä`Œ`ÚÅf‘ËÃÛWòº&6â<Õâëè·8j¾úg/hƒëJᘳ›t ¹>øá:D¶­P˜ˆ5ƒÈöß"°¿ÍÉ‘ë Óú¼ª<*4Lƒ^´µ5IžŽ¼¹€%i“$kfYÅ|¯£Oœ284µï§å>Œ@¬•Z.Þý%> 2cîÐr ì9×˽Ù]ˆuñº&VB®CdÛ äúÒ˜éÈLù‚ÂDC¯ê¢Æ·ÉõZi±¶% ±fï¸Z)íäÈ}†i‘Ws´†hÐ ÓJø„$OGÎØ^®DTFbÌ5.õçÒõÚßI%;Uåi]ëÚ¾€p+y*Žø`-°ñU•‘sMƒ<ïVÀë`]SÛbèvYq0Æè¢]×ù€\?0õ ”¢“ÔbéY7£$¿?¸q¼¥^…‰X3ñ–úï¼×É‘1èSGXÝ7è†iùJR{A’4}–¯–£(Öÿ¿c#nŽ^ÀDÝÑŒ·gRÉ„£‡ÜÈ0­qrù@¡â»x%3cræ!ÇL!×G7=@ÿ&…‰sN—ÄÛAØR½ôøÑ?g!l­þÍÌN¼…cç’ëC»V#ì`xcá½o"ôÁïùŒaZßT•g(Õ}[°ÅÏýžD­·ªAl“ »—ž±aÓŠ æ:»Ú¾À·Åi,ùÞ§¤/Dž@©½}/§ïÇe¬i;òŒ/“Ë}}›y×5±úäOoAô=úîæJ|22§_¥0ÑÑ«Ç"“¼–\¯ç ÞIžï̘#ñõ÷À“ßïäÈ/ Ó:YUž¡R× zgW÷9!ÉÓ‘{k:~Wå•jŒ FÀe¾,|ô•jp}*™èQ•©QmknDh4y¯¦Øü*°ý]•‘s¯ö<'ëš ˆ·-âuM¬®EÞûü½Éõ™“?âè¤ÂDGC ö­¨ùè‘Æ;î€^Í+ÌÄš™^- Þ~;`“„,3L‹öƒf„üÿìÝyœTÕ7þϹµo]M7 *‹l}o_šîÆ]ȸEK\’·¨1*F„ÌäIæ—¼f’Ìó›øüž™I@ŒKbÔ¸ÅL¢Ù\ã2Š *ÍbQÅ"ˆ"tÓÕµ/÷žç’ç73Qú{¡OwÕ­ïûOýž®O tÕ¹uÎ÷[³ôD2­¿‚ÖÖ²*5ÆãšXÐ`xTÉhèïZˆª§ö{J£é¢[ŸDxßZ…‰B½ ĶþÁÉ’|[Qœ!Q³»Ù‚-î÷ Iî>±ºÂGð«ŒÄ˜+L@^GO³·ø¦¢8u#‘LyßÿGø>êmù@ÙÑÆ’<ªræ¹äúО5ˆ9è’ÍØHòçw¡)Eo[RŒCfö×&r®Ô4™™—‘ëý;[ǨÙðˆ¦‚o`»“%?H$Sô9žÃ¬&7è«×m¸(¤ÉK©õ»-^·É×AkX^Øøl /ýh»à*ÓÐ]Vw#™ßÿ}„GµQëź'€]ôcŒ5¬`ò”är­˜FÓ*×ÄêKtû39˜Ýœ;ú äÇŸ¤0­yÑßµÒKëñ$ì â«–Â#«Š“1v€GVѼr D•ü¥ˆÀÉdªöŽª 7è‰djŒ„M~ÌX’ž)Ç`swdÆuº7‡Qš£7ÌÛLCSUžz‘H¦:´`ì;ä}Û z~¥0cî!O¹ ˆ8×´î^×ÄêRÓªÛá)ö“ëÓ³o@ÅW˜ˆ&Óv%*ñÉäúØÆG ½Ea"ÆþZ ³ ±w}öšà6EqKÍmÐ ¶xÜ'@þ:|E%‚>ÔþH ÆFÚTQ‚î-8Y² À?)ŠS7ÉT…þ'¥æ¥ý¾¬V ½²°,ÅÉ«rÚ©“è߆?|…Ç5±ºå+§Ñô}v³…tçB…‰Wh1™r!¹>зÑwéëJÑwŸ@ 7ådÉâD2uºª<‡ª¦6è=ëÖ/i’Üù}ËUVHe$Æ\!gø3¤–‹€"€+MCwÔIΕò}Ëjž@-«z?T™ˆ1wˆ¶À>á«äòãšîVˆ1õ"»^G䃗ÈõÅqs‘t¶ÂDŸ®ê "ݵÐhÛ­’;Ð|¬• ñ•K •ÝÌ| ‘L5«Êt(jfƒžH¦¦h¹;r^zðL9¦2c®q–/‹¨æhÑ·LCoø Ô‰µkOG°ùzò‚]ë Ö=¥0c.!äü[ ôqMÍ<®‰¹Dlí=ðæv“ëô«QS˜è“e:nD5Bݦԃðçéÿ»SÁ_܃¦ä/œ,™à'Šâ’šØ '’)OÑÖþè’Üùår9Ôô;Æj‚©q¬·èdÉ îP§n$’©fXÅßP¿9åܮ풿9`l0²ý\Èñ&¹>úÞâqMÌ%¼VñUËȳ›m_é®E°i“‡‡D~üÉÈu¹>¸{%¢ÛŸU˜ˆ1ºèŽ?!´ë-'K.O$S_R•Ç©šØ mí{AÍ&wGÞX bƒ]“M÷«)qTqŠ/ãdÉ~ט†Î»Ìܾ‡ˆÑ<½yéUˆ1w-!ç\N®÷¼Xòu¡¾$b[O®/µ¶!;ý ýÿ*þ8Ò³o ×{Šû_½La"Æœ‹¯^Oq¿“%w&’)ò•F•F|ƒžH¦:üB~—ZŸ¶=øS™Gª1FqŽ?‹æh¯}³iè;Uå©ëV¿ýeDFŸG­﯀x÷•‘srþ­€ÏÁ¸¦×ÄÜ)¶þaøÒÛÈõ™™_D©iªÂD¤; ҟOÇßù|å´ÂDŒ9ç­dмæ.'KZÜ—H¦†ï¨Ê§Ñ z"™ –¤ö¤&$)‡-ž/EQ|´±ÁïÉc‚§ìdÉc¦¡?¦*O½H$SGâ>ò‚ü~ˆ×¸qc$Ý_Z§Ë£…ÀÀ{ 16r4»ŠæUKɳ›¥æCº{1,¡nzQvÒ9(Ž›K®ìxá]¯+ËÃØá}ü¢ïÿÉÉ’³|]Q²Ý lq{@Øä£ïTCx´§îŒ5²1¨àx_ÎÉ’(ŠS7É”@®÷ é“ïÐh¯Ý ý·f¬1›[¿ˆ\è]Ø»¿Qˆ±‘؆Ø&úìærÓÑÈ×(ÉRÇ€~¹Þ›ÛØ;?S’…±¡KÜ _î#'Kþw"™š©*ňmÐ×®[ZPȯQë÷Ù^¼R «ŒÄ˜+x`ãÜ@>áèhû5¦¡÷«ÊT7 éň´O-›žv¬Q™ˆ1wð`Ïw8®iÕR×ÄBÓæ'Ø—$×g§žüèŽ!Í`CCº{lñ©m£yÕ2x-GMhv«„xÏ2À¶¨KBJ$Sꎪ bD6è‰d*^§Îd®Hà™R ÖÈ_™g¬æÍóæ1Zst_óvÓÐ_P•§^$’©éðÿ…Z/>‚öæƒ*#1æòÄk€}\SœÇ5±†"ôZp0»9ÝùuXÞ¡ûâ*;ã (µÌ"×ÇÞû‚}ô‡ Œ¤àþõˆmù­“%ÝþQQœAÈŽ· µ‡ýB’»OôT¢Øò6ÆÖ1(a¶·àdÉFßV§n$’)¯Ìï¾­Á…mA¼² ²âèŽ?ciR7ä´3ÉåÁÝ+áqM¬Áø‹{M+°Âc‘îš›i¥ø±ÈÌ wˆ÷¥·"¶á‘!ymƆKlÃ#ð§õ4ùN"™"ŸªJþAïI¤. ›Üù#ˇ7,>ÚÎØ`ü°qf ýh{À¦¡óù´üþŠð¨iÔr‘ø ðñf•‰sŠÁ>ù&r=kb,ºãy„v­ ×ç'ÎCn¼ÃzMKh<'5Úa¢ZBsÏRh6OV`õE“6â+ Q%ìõàÀQ÷a6¬ôD2u¤¸ŸZ_´5o%ƒæµôÑv0Žî[éµòcæ ;…|¨}ëÛìè/c5'öÞïÜ÷Ž“%7%’©sTåù$öA/ÙbQH“äsü[«¼c‡TFb̰pš? jÓE9W›†Î»Ì\ï=Å –‹Uûê`¬15Ž»†\îÍîB,q¯º<ŒÕ‘ÐÇo;êÃP3m°¼a¤çÐÇ={ÊY4¯Z ðdæM+—B+ 8Yr_"™jU•ç¿– z"™šîôãY[Ãs>ÚÎÅ9þ,"šídÉß™†¾EUžz‘X³ê\DZ¯¤Ö‹ÖB¤žS‰1wÐägn…ôûÇØšWßÁãšûOšÖÝ_v'¹>ÓvJ‘ äúôœ°ÂcÈõ±u÷ÁWØK®g¬–ùK½ˆ¯»ÏÉ’qîQç¯(ß '’)oÑÖþè’ÔYJà?Ê1äAk¦ÌX#›£0ÉSr²ä)ÓЇíL­J$S­Òªü’¼ ”X~Ç_PŒ±ƒ’æcŒkú=kbì¿ñX%ÄWÑg7ÛÞ ÒÝ‹a‹Á?Úç&ÌGÞAs¹ÐGo úÁ‹äzÆêAäÃÿ@xç«N–\’H¦®V•ç?S¾A/ÚÚƒš=Z¿Á á]P‰1W… Nöçœ,ÙàzEqêK®÷—"k¢–k+î²ý*1æ rô1@ç—Éõ<®‰±OÜ¿M['×—GMGvæe¯ ´ mÒ? x ½ˆ¯¹“\ÏX=iZs7€_UÆ\c*èò9:Ú¾À"5iêG"™È÷ý¾ùöÚEGãëkHòHR¿€\Ü·±-¿S˜ˆ1÷ä>D|=ý*H%:Æuÿ埕"‘n#÷E…/û!š’Žšh1V·š’÷ßýÐÉ’N$Síªò(Ù ¯^·þÜ&¯¢Öï±}XnñÑvÆã³øè#Õ$€¯˜†îh–„Éüþï ÜBþê@¬øpÊHŒ¹‚†€yÎÆ5Å{–€Ç51FÝú÷®%×ç&ŸÂع[hHÏ] é ’Ö »ŠøªeðXŽšÐ2V·ÿI­ÏØþTá£íŒQœÈ!¬9Úk/4 ý}Uyêź5=!Òz)µ^|Ðl|Ae$ÆÜAÓ`Ï_øŒkê¹»¬8c'–|þ̇ýs≟ÁWê‚DŒ¹‡¯´ÍïüÌÉ’ÑîŠ×>¬ z"™ò•¤ö¤&$©û„-^(ÇPT3~1Wéöp´ÇQ·'LCÿ…ª<õ"‘L”‘ú!^½“G23F ç\ Œ™N®½û8‚ý›&b¬qyì â=K ìCŸ:þp9"šÎ1ÖH½ŠðÿádÉy‰dŠÞ=õSÖN¹`‹„=‰ZŸª†°U*™çΘ«´È Ntv´ýc7*ŠS_ò}K8B-×Þø)§Ï•e¬a 9û ärÿþwÝÄãšS)Þ‚Ø&G³›ÿ/O~/âkÉýkHñw~ oþc'K~œH¦¦Îkò}íºõ§…$wGî³ßÀv'K~H¦æÊk9Þ ¯Z—ú|H“äîÈ»-^·É×AkX^Øøl /ýh»…#Õ]Vw¥üþï!<ªZ.Ö=|´Ae"ÆÜ!†¡û-À;T/8Ë4ô†îìH¦ZQ)í„/à¼+clHEßû#š×Ý;Ò1kùñ'£ïøo ùÏ ô®Gë«ß††þˆÁÙ¾¾ƒâ¸ã,¹Ì4ô_‰AŽÍz.Æ’Ú“¼9g¬&ô¸¦Ñ7ç€Jq5oÎyþˆ%éŒ5ŒŠ?Ž´ƒñhZ•ÞߦÔÚ†ìô/J,ÆRóªeð÷;Yrg"™?XÑA7è‰djŠGØ|)…±Ú°À4ô#b¤­[ýö5ðéŒ5:aWïYt4}‚1vÒ aG‘ë›×Ü _z¹>3ó‹(5M=”hŒ5o%ƒøš»,ið½ÁŠºAï³½÷8yEƘ2™†îh®ƒ[Éüþÿw¤30Æ€ØÆG`དŽÁXÃÈN:ÅqsÉõ‘/"¼s9šW-…¨–Hk¤æCº{,ÁS˜£ü&¢ïÿÉÉ’K+8è½$ÅIN^1¦ÌwF:@-H.ž5 GŽtÆV@tËã#ƒ±†QÇ€~5¹Þ›ÛØ;?¶!¶éWô×j:ã§kX‘ÔÀMnøræ‘ëk$éÎ…°B-N–Ülú W5úA_Êã–ÉÔKŠ!I¿!ÊR U Á:øgŒ˜ì)¡Us4¦èVÓЗ©ÊS/kמ¯ïj½HïÖ? #ÏØÁH í\È8ýÀNëkßChßZ…©k<ýæ×r¹¾)ù 4môô,ª¾öžv;yc¡Óó‹á+õ‘³0ævÙ£ÏDç-N–üÒ4ôË@ˆƒï‘ItèY·~_È;© Þ·üøM¹™ZÎXà ÁÆÕ>D5r÷Ç€NÓÐ7*ŒUo¿ú3„[®§Ö‹·€H<©2cîmuÑ QR¹·°c^\O5¯8c!?º}§ü€\Ø·c^û€øºpÄqè=ñ»äŸÚ³­o|Ÿ\Ϙ›•ƒc±ï´ÃöÓÞ#ì`˜†Þ ¾A'·›ëno»«`‹W©õÇxÊèò¨åŒ5¬4¼TŽAÒ¿Ø x8‘LÑμ¹Y¸e! ý;©å²ór u¢ÊDŒ¹C¶Ú[÷‘Ë«!¾«ÊØP±¼a¤|3ç)gѼj)¨›s}ü6"Ûém¦ cç ãàÛ|ÆÜʆ@zîb'›søÊ_6çä :„4yiE"G­?Ñ—C Ýe¬!m–¤ª!'Kº4|7sÓЋ5Ÿ/ì*íø×{þ"ÀãQœŒ±ú'Þ]±>m5?q>ò|W•±Ã–îX+<†\[w?|…½Ž_§iÝÏáË’Ÿq#Óv%J‘†ïUË\vúÅ(µêN–Ünú‹N8Ú ›†¾G@»ŠZ6Îñg ñUtÆõb5‚ý¶£nÈßI$S'¨ÊS/LC_c3?$/h™ Ùý%…‰sñÚÝ@ž~ï´¿ýzTŽæ0Æþ“Ü„ùÈO¤?è }ô¢Û±ü«„øªe€m‘êmoéîŰ…£íc®QŠMFfº£Ï|Ûéë8þÖÙ>ë·[Šc:Èõ‘íÏ"´gå¼¶&mÄ{–@Ti_ôIÍ‹t÷­°4ÿ¼>cµ®ÐÚŽÌ” œ,Yàêëò%’Žö¶W‹Rü„Zßâ±pš—ǯ06[O—£(ÓºÀÏÉÔhU™ê…ÙuÂÈ÷‘§MÈig@Nš«2cîP©@[îð®jßUeŒ¢™ˆ¶+Éõ¾ì‡hZGŸ²@È~€øúGÈõ•èD ´uH30V‹,oé®…€F~?˸Ú4tÚæ'8¬wÎ&ÿ®$µ÷©õ†·€)¢t8/ÉXCè…+*ŽÆ7Œp¢8õ%Ür©(çÉOåÉ7aú‘BÆÖž÷ ¿!——[ø®*cƒ±…v`d“7H\`!¾j<ÖОŽný‚{×’ës“ÏFa,?äfî60ûTÃG8Yò·¦¡ÓïŒ|‚ÃÚ ›†^ûs¶¤'š8ßAº36¨+„–£;Ò'’©«U婦¡ïô¯"Bͧ.xÚcƒ«‡Ø·™\à®êt…‰«oÙ™—¡Ü<\ß´ù7îߨ,OSÏRh¥r}ÿœ¨úbÊò06’rãOAî¨Óœ,yÊ4ôŸîëöÙ3ÓÐSe)¾G­i6Îòe÷ekÏ•#ÈÛŽþš.K$SǨÊS/Úçtÿ¹^ò´ yÔ\`æé*#1æ¶ ñòR âà®j×"¾«ÊØ'(Žš‰i—’ëýý›Ýø˜ÂD€¿Ô‡xâçäz+ÔŠôœ›&bldTÍHwÜàdÉ>CrïcH.‡5û¶‚-Ègb¦y‹Ð…ãŽóŒ5œxñг£îM~‘H¦øâg¤õZÈÓ&äq×ñ± 1æý»¡õãÖlœãÏ ÕË3æjÏW¢°iOÙÿ쇉dª]Užzavt¼ŠÂþ»¨õrìLÈŽÏ«ŒÄ˜;H@¼rPtrWõfT½ŽN1æJ…±s‘›t6¹>¸w-¢[ÿ¨0Ñ_óVsˆ¯¹“\où£Hw/7taõ®>mW9Y²}Ì/Áƒíjo»¯`‹ç©õ=%ÌÕxôcƒ)Bà ¥(lúè5?€‡É”£.s®iý ýäièøäèIêò0æ¹4ÄŠŸ‘Ë­P+:ù®*klU_ ýsëµÒâ=K&útὫÝú$¹¾8º™cù!7«_64 t/†í Ó—_1 þ´š`È渚4ùåŠjý þF£2Ô1sm Y 9Y2À?)ŠS7LC/#Ô|ªeÒÅ=©y!?s+àõªŽÆXÝï­€xïr}þÈ“ù®*khé97à µ’ëã‰{á+õ)Ltp±äðg> ×Ìü2JцïUËêTvÆPj™ádÉ¿š†NoØ@4ätÓÐ{@|êç²|í“1‚—ªaôÚŽ6ŽßL$S§ªÊS/LCOÊrîÉ š†<î …‰smŽÙ=äúŒyßUe ){Ô(y¹>üárDvÒ€©à±+ˆ÷,°i_¦IoýsÃÖø!7«/Åø4df|ÑÉ’úgK”tzîlo{º`‹G¨õcµ æyø>:cƒ± áÙRUIþ««x0‘L5)ŒUDxÔmÈ÷½C­—mçþ?cƒ’ÅÄ«NîªÆø®*k8•ÐdÚ¯%×{ò{_Kn¡¢T ½±M¿&×WâS™Å¹Yý°4ºAÒ,•\i:¹AºÊF1…4yCÉäVó³½yLDYUÆ\c7üè©8:ê> À5iê‡iè6Â- R ÿ2•§Ü tôßš±Æ´3 ‘ú¹üÀ]Õ b¬–ôw-‚å§7IŒ¯¾žjíôiŠnú5}Éõ™©¢Øb(LÄØÐ0®E9v”“%ß5 }ª<Ê6覡çš¼@JFy…ÄY,üP?ß‘±z·ÂŽ`—ås²äÚD2Õð[LCß!­*¹K•ŒŽ}Ò×TFbÌ5ÄÊGþäz¾«ÊÅÀ±¡4š~"+ºõI„÷­U˜È9 6â=K Uˆ 4ú;oAÕãhL,cî0¶ ¹)Ÿs²ä?R€Â :˜†þvIjÿF­¥Uqšº36[Ï”£(ѺÀÏÉÔª2Õ‹ÙÝ'܇\ï Ôz9eäTúAÆVµ ñòív•T.½Aôw/⻪ÌÕJ±IÈÌ$·f‚`bëPè0øó»Ð´þ!r}5:óz…‰;º÷)ß ›†^ jöù–¥^à4!I*g¬¡­±CØ^õ;Yr^"™ºAUžzaz¯ðø¾L^l‚œwË_PŒ±ƒïüb“»ªŸG±EW˜ˆ±‘‘™u*ñÉäúئ_#Þ¢0ÑÐhZ½ Z)M®OϾ\a"ÆœËMüä'8tô„ièªÊóŸ)ß €i蛪ÿƒZÕlœà£îŒQ™-4¤ç~ÒKþØk¸Ê4ôaoŒ6"ç4C¾ª,ù‘ú\_cQ»#'«ï#€wªŽŽºÏð¿Å©¦¡WExÔù¨”hã#4ä¼…>GôkLÛVBl~\^wrÇ|Va ÆÔHw.„E®¿óSøÊônèµ*¶þ!øÒÛÈõÙ™_B©©á{Õ²a–™u9ÊqGî~hú›ªò̈lÐMCï÷—R›ZùpN ”΄gÌ–WÃØçì¨û¢D2E?çR¦¡oBµ@¾—/ã`ŸpµÊHŒ¹†Xq?qpWÕø Êá#&blhe'⸹äúÈ/!¼ëu…‰†fWÑܳ¢Z"ÕK͇t÷"XÂÑgÆYqT2Ç^ìdÉ*ÿSQœAX§£Žö¶‹RÜK­­U1ßËGÝŒ O—b(;{žõ@"™jV©~„š„\/¹«•œq6p´£F#Œ5¦r Ú+ËœÝUí^ÌwUY](‡Çc@§?°õæv#æ`LY=d¶!¶éWäúrÓ1Èר ÄØŸYžÒÝ <¼¨àJÓÐGìøöˆ¶"iraÉ;©õ³½ÚÓ9ÆÙ^øÐS:Y2ÀŠâÔ ÓÐ%"­‹ržåë@(¢2cî°{DêwäòRK²|W•Õ8éîE°}Ä÷ÛF|Õ2x-òÛL݈n~Þ¹>;õ|FóCn¦Ö@ûWQ‰édÉ·LCߨ*ňnÐMC/4yž-鑺&$Î dá“´k¢Œ5²VYŽîH9‘L}IUžzaúN@’§M < ò”›&bÌ=Dϯ€Þ­äúÌÌ/¡Ô4Ea"ÆOvÆQj™E®mý=B}I…‰FމøÊ%ð”³ä5ý_‡å +LÅYáˆã‘›ä¨§É îP‡lćùš†¾¶,Å©õqÍÂYþao¦ÇX]zºEÁvtDô®D25AUžzÑÞyÜ£Èí{ŠZ/9rú|•‘sË‚xåv Z&•¸«º˜ïª²šTŠ‹ÌŒ/ë}émˆ­Xa¢‘ç/îA,ù¹Þ Eºƒr³¡WõÅÐïìÏÖ~ט†N쒦ΈoÐ ¨Ùß/ÚÚzjýLo3…ûŽ16ÔÒðâµJÌÉ’QîK$S|ñ32úJ”2ôÎ'\ÄZbÌD߇«!×—›ŽAÆÁý^Ɔƒ%|Hw-‚Ô|¤zQ-¡yÕRhvUq²‘Ýñß’‚|ÿo9DÀGÝLÂ⽪£QçghøAĦ¡÷Ã$_€•þìy ÁÏ6ŒX÷4Äîuäúì±ò]UVS2í× Üt4¹>¶ñ1è£Èê]|õðúÈõéöëQ ðCn64²GŸ‰Âøã,yÌ4ôÇTåqª&6è`úV XD­ çø3*#1æÏU¢ÈÚŽþºÿ‰dj¦ª<õÂìèxù>ò´ Œo‡lÿœÂDŒ¹„”¯,ƒ,Ñ益»ø®*« ùÑÈN9\Ø—DÓ–ß*LT{¼• š×ÞM®·ƒq tݪ0kåàX 8›°À5iMÍlР»½í®‚-^¥Öã)£ËSP‰1W(@ÃKå$ýVMÀC‰dŠvvÏÍÂ- Qè'y’—C¶LT™ˆ1wÈôA{ë>ry54éŽblp–7Œt'ý™§œEóª%FüZë° }ü"ÛŸ#ׯÎAÆÁƒÆþ;鹋aûM2ºÆ4tú•ÆaPStiòÒŠyàù‰¾Zàþû<Œ®Í2€õVÈÉ’nÿ (NÝ0 ½ˆPóù®Ò8{ýŸYxÈó6kXâÝåï¯ ×ç'~y¾«ÊFPºc¬ðr},ù|…½ Õ¶¦u?‡/÷¹>Óv%J‘†ïUËQvúÅ(µêN–Ünú ªòªšÛ ›†¾G@»’Z6Îñg ñµOÆõB%‚ý¶£nÈßI$SŽ.ñ¸‘ièkìb†„k*Ž- à_Å©¦¡[:•mÚ„¦Ážw à'¿Y0Ö¸v¬…Øø ¹¼0¶ÓQ“.Æ×@÷­°MäúæµwÃ[áfÆÛð(üý[Èõ™é— Ø<]a"æ&Yý*Tâ“,ùiè«Uå9\5»A7 =ë.’’ÖUÃ+l|6…´k¢Œ5²×ìöXŽú¿Ý”H¦ÎU•§^˜†¾vy1yAlä‰×*LĘ{ˆ·„HÓGÐð]U6L²SÎGaÌr}dû³}ü¶ÂDõG“6â=K ª´/,¥æEº{,ͯ8«w…Övd¦\àdÉ ·)Š3$jvƒím¯¥ ŸjѪ8ÍKî/ÇXò%ðt9Š2ý¨;ü<‘LµªÊT/Ì®îD¾…±ü›}²Ù7 >ÂÉ’¿3 ~×b„ÔüÝ4ôr@ØçÙRžthBâ A>êÎØ VZ!ì°Ý‘¾8‘L]­*O½0 }„¸Š¼ Ô œz3ÀÓ&”Xý8ľÍäz¾«ÊTÉμ åæiäú¦-#¸£ÂDîÐÔs;´Ò¹¾ÎT½Ž¾P` 7þdäŽ:ÍÉ’§LC¿GUž¡Tót0 =Y–â{Ôú˜fã,_Ve$Æ\ã¹ryÛѯ‚e‰dêUyêEûœî'ë%O›GÍf:z#a¬1Ù6ÄËKŠƒ»ª]|W• ­â¨™ÈL»„\ïïߌè†_*LäþR/≟“ë­P+Ò_W˜ˆÕ›J éŽ,ÙàzEq†\]lÐ ¨Ù·lñµ~š·]Ôdç|ÆjʼXîì¨{€_$’©ºùý¡L¤õZ”2äiò¸køX•‰s‡þÝ=‘Ë+1¾«Ê†Žå ݵRó’êEµˆøÊ%Ð$ŸÞ¤Šì|áäv.(y²ù!7;` s!ì@ÜÉ’LCß­*ÏP«›ئ¡Û!M^`I|±gž?‹&TUÆbÌRvïV‰wì˜àŠâÔ ÓÐ3ð/‚m“¦MH_rþ­€ÆgÝŒXÿÄGkÈõîªv+LÄÅ€q*щäúøú‡È}¨0‘;5­¹ žü^r}Ƽ•àh…‰X=ÈN>…#ý®À4ôߪʣBÝlÐÀ4ô6äÍÔú°fãNe$Æ\ãùJÛãdÉÉT»ª<õÂìèx…ýwQë娙³/T‰1w€Xþ èä®êÍ|W•–ÂØ¹ÈM>›\ܻѭT˜È½¼Õš×’ß>aùcHw/7ti\åðx ´Ñ[Ø`‘š4êÔÕºÚÛî+ØâjýDO s5½ÆØ`ŠÐð|) ›>zÍà¡D2å¨Ëœ+EZÿ…~ò´ ̹ rô$uys‹l?ÄŠŸ‘Ë­P+:ÉÏñû/ª¾úç, ×k¥4õ,U˜ÈýB{V!ºõIr}qt;2ÇòCîFdCCºû°}aê à+¦¡ÓŸòÖˆºÛ @H“—U¤ÈPëOðç0•‘s…m Y 9Y2À?)ŠS7LC/!Ô|ªeÒD©y!?s+à¥Ýod¬‘‰÷V@¼÷ ¹>äÉ|W•’ôœ›a…ZÉõñĽð—ú&j ±äðg> ×̼¥hÃ÷ªm8Ù_@©e†“%ÿbúrUyTªË ºiè½ø2µÞ/$Î dùÚ'c/UÃèµm¿™H¦NU•§^˜†ž”åyÚš†<î …‰smŽÙ=äz¾«ÊœÊu: GžD®ï\ŽÈNúƒ#öé‰ð^úø?F§ÁF¼çÇÐ*ćšý Qõ8ËêHal²SÎs²d9€)Š3lêzƒ¦¡¿]’ùÿˆQZgøø¨;cƒ±%ðL9Šý¨;Ü›H¦ŽP•©^Ìî>á>äz_¤ÖË©ó!§Òï>2Ö°ªUˆ—o‡°«¤ré ò]Uö©J±I˜q¹ÞŸù±u¨ ÄàÏïBÓú‡ÈõÕèxdL~ÈíFUoýô© 2®6 ½î¿‰­û :5ûï‹¶¶™Zßæ)`š¨Ûk Œ ›ýðáõrÄÉ’ÑîU§¾DZ¿$‹ò´ yâõ@´Ye"Æ\AìÛ¬yŒ\à®êå걺dk^ôw/‚ôÒ&… »‚xÏx$OR-ºíi„>î!×çŽ9ùq'*LÄFÂÀœ°Âcœ,Yh:}äm sÅÝ4ôjP³Ï·¤°(õB§2IR9c mÂöªßÉ’óÉÔ×T婦¡÷ ñº³»ªý &bµ¨â#=›~ÅÁSìG|õ2…‰U¨wb[ÿ@®/µ¶!;ýR…‰ØP³…†t÷bH/ùã¢à*Óг cWnÐ ¤É«ÊR©Ïõe1<:ƒ±Á¼Þ©::ê> ÀmŠâÔ ÓЫ"<ê;õFóCîZ5ÐþUT"G:Yò-ÓÐ7ªÊS+\¿A7 ½Ðäù¶¤Gê!qf Ÿ¤]e¬‘­°ÂØi9º#}y"™ú’ª<õÂ4ôRZônEáÈSnR˜ˆ1÷=¿úœÜU½ ¥¦) ±‘’þ”Zf‘ëc[P_Ra"v¸4HÄW.V¦÷Kw}–7¬0;…#ŽGn’£^ /¸CQœšâú :˜†¾¦,Å©õqÍÂgý|Ô1ŠgÊ1lGGDïL$STå©f× #·ï)j½<æDÈéóTFbÌ, âå¥@µL*绪îTŠ‹ÌÌ/’ë}émˆ­Xa"6TüÅ=hJ>@®¯†Æ"ÝÁ¹kIÕC¿³ÿOö¸Æ4t©(RMiˆ :5ûûE[[O­Ÿá-`¦à#NŒ & /^«8:êÞà¾D2Å?#£¯D)Co|ÂWX‹Â@Œ¹ƒèûbõ£äúrÓ$dÜSfµÍ^¤»Aj´^¢ZBóª¥Ðìªâdl¨Dw<Ю·Èõù‰ó‘ŸÀ¹kEºóX!GŸgn6 |e¹Þ5ÌÝ4t+¨Ùç[R› |&C|Ô±Á$ìÞ«:u~€¯+ŠS7LCï‡'H¾+ýÈù ÁÏ6ŒX÷Äîuäúì±¢0z¶ÂDl¸dÚ¯E¹éhr}lãc ЯE°Ú_½ žB¹¾¿ýzTü{¤e>…ñ'8Yò˜iè©ÊS‹fƒ¦¡oµ€ÅÔúˆ°pŽ?£2c®ñ\%Šœtô+å'’©™ªòÔ ³£ãEäû~N­—ãÚ!Û?§2cî %Ä+Ë Ê9ò’t×-|WµÎåGw ;åÔßð]Õ:•îX+<–\K>_a¯ÂDl84­û9|¹Èõ™¶+QŠ4|¯Úa—v1J­º“%·›†þ‚ª<µ¬!7覡ïЮ¤Ö„süh|퓱A½P‰`¿íèKñï$’©ãU婦¡¯±‹™&/h™ ÙÝðë#¯Ý äÜU5ù®j½ÉM˜‡üDúƒ•Юˆîx^a"6\øðfÅÑ8qîQ§¾„[.å<ùi <ùFÈpLe"ÆÜáã-‰ßËË-Ó‘É k-4¤ç~¶7H\`!¾j<~s»Ø{¿GpïZr}nò9(Œå‡ÜCm`ö ¨FÆ9Yòw¦¡oQ•§žð€iè倰ϳ¤ ]ׄÄþ ‚|Ô±A­´BøÀrtGú’D2uµª<õÂ4ô=â*ò‚P3pêÍO›`lPbõãÀ¾ÍäúÌôKù®jÉμ åæiäú¦-#HïÁÉê\SÏíÐJäúþ9 Põ:úBDnüÉÈuš“%O™†Î_ÐüoÐÿÌ4ôdE ò<æ˜fã,_Ve$Æ\ãÙryÛѯ›e‰dêhUyêEûœî'ë%O›GÍf:zCd¬1Ù6´——wU»þ{÷eGuå ÿêæÐQ­œ%”ºªKˆ$ŒÁ ÉDcE4`fŒß5öë±ße¼Öó`ƘA6Ñcžã  $!®îÅ‘Pè›cÕy?hžYXw—ÔGÝUwÿ¾ÍÌÞÝÿѲoßSuÎÙ+`iŽîÕ`Š”Úf#;ã\™Þe°C-NZ®5 ýSUy܈¿üþÓÐíˆ&[R#P:)˜C3h‘kd);Œ·jij‚,p“¢8®azþÈÙ°mÒ´ ˆ@.Xh¼×±zÄOC|ü¹þÀYÕ~…‰X=ãJTãÈõ-o<‚Pþ#…‰ØpÖüÚ=ð>#×gÍ+Q w(Läm¹©g¢8ÚÑgäý¦¡ÿFU·âúŸ1 ý’¼Ç%ªÙXÌ«ŒÄ˜gü®GÖö9iù~"™êR•Ç-ÌîîçQÜOž6!G͆œûe•‘ó)!Öÿ(ñYU7(Žš‡üÔ3Èõá϶ þî(LĆ;-‡Ö-ä?Ÿ°‚MH÷¯_èâ\%:™NúÕ9Þ@+Û@xþ9úº:^´ÅZjý_ó4½ÆX=%hX[‰Ã¦^ x(‘L9ºeΓb#nFq€öÉÁ³µ(öÙŽŽŸH¦NT•Ç-LCOÊJþ;ä†ÖIG_¬0cÞ¡m¸"ÇgU‡£ÜÄ/¢8î8r}tçzÄvÒ¸0ïóÙU4o¼¦Ý%ýa ô¯€M¼Œ°‘•Zf ;ë'- äÉYˆèÑÛÕùxÑPëGiUœèãóèŒÕcAÚr5IþÒpàV÷&…±\ADÛnEayÚ„ì\Œ7TFbÌd©ñüOÈõΪ®ŸUU«î@¶ëJr½¯ðš_»Ga"æVáôv4½ùÈõÕÖéÈιHa"÷³´2ý+ é#(+.5 |!w#âzM^[¶Ånj}·¿€ ¨¨ŒÄ˜'|Š 69Ûê>@Ã(4 ÝF´}1ªEò<ñˆ°£kÆÓÎ$ÄÿI./u˜|VU)þ•°‚ôKùZ^û ü5~YÂ>_üÍAhß›äúìô³Qj×&r·¬±•¦‰NZ¾ezBU¯àz¦¡Bš\$%H#ŽüBâK¡üày›ŒÕó¢Ã'ù©+\‘H¦þÛ°ièH«F¾-IÆGžµÊHŒy†xå`àrý³ª“&j\Ù£ÎF¹ƒ>È#þîoýŒ>65 6š7Þ­J¼ÜYóa wj>¾«öÏGõ!7í,'-ëüPQOá:i诔¥ö#j}›Vé~zËX=¶ÖTâ(Ó·ºÀ}‰dj´ªLn1·ÿØŸ#¿÷j½œ¾rú|•‘ó†Z â¹;žU]ÉgUY¹i 2³ÉW!˜ýM[ïWˆyF¨ð1šßxˆ\_‹EÖ¼Fa"÷©ùcè¦OUÅ[Ûù &/ЉšýÍ’­m§ÖwúŠ˜!øxcõìG/VbNZ:Ðg"yYlÄ…²”%O›ó¯â­*1æ bÏûÀk¿"×óYÕÁek~ ô¯€ôÓÞZ »Š–·Ã'yš£‰¿÷»6’ëó“OCaô± ¹K¦g)¬èH'-ËLC_QÏá:‘ièµ°f/²¤°(õB_ eá­îŒÕµÙŽàýZÐIËY‰dªág›†¾WøôWLáfÈ“nà;­#¯ÿ;Äîmäúìô³QjëT˜¨qdç\ŒjË4r}Ó›ÿ‚Púm…‰˜µl^­œ&×§{®C5Ø¢0‘;äÇŸŒÂxGƒu~cúªòx/Ð0 ýÍÄ-Ôú˜°qF0§2cžñdµ yésÒòãD25]U·0{úG~ï/©õr\dçé*#1æ ¶„Xw'DµH«×|è[ÎgUS©Ý@vúÙäúоmˆ¿ù/ 1¯ ”вå^r½nCº÷F…‰†¿JhÒæUNZv¸VQÏâºC!aÿ¨h‹W¨õÓ|%˜ñ;c ,ž+Ç I×1b80zÍѪޓb#®A1Mž6!û/ZǨLĘ7¤wC¼ò ry->9>«zÈj¾0zo4Ú×S­šGËÆÛ¡ñnEvˆbŸ¼€Ø‡Ï’ëKcŽF~rã>äÎô/‡jvÒr•iè{Tåñ*^ ;dºŒhòܪD‰ÚsB ‡Ð.›a¬‘m“a¼e9vò®¯2 ½€HËbØíñF yò ò—`ÆÚ¶g!>|•\žã³ª‡,;÷ÔâcÉõÍ©‡,|¢0kÍ¯ß a¹>m\ŽJ´ñîªÍM_ŒÒÈn'-?3 ýqUy¼Œ¿ÓÐw ïïˆh ƒä{œkhOU¢HÛŽnCþn"™êQ•Ç-LC¥ yÚ„ì˜Ù{žÊHŒyƒðüÝ@q€Ü’î¹µ £·L ¯0ö8ä'J®ìÚˆøûk&bÂW+¢eÓ*À¦íİ1¤ûWÂn  ]Êñ‰HϹØIËÛþNQÏãú!êëÒY”ù©Ðx_ó}Ä™‹Œ5°ªðáwå,IþÃçðH"™ +ŒåѶoÊÂ~òMIÒ<ÕðÇø«K²¿ÿ¹Þ ·a w™ÂDÞR ¶ =—~LÕW@ó¦;&b&²w+šÞýr}¹½¹™ç+L4|ØBCº%¤Ÿü5Ëp™iè|×!âúaˆû’ŠäëçrÂX=;Âë5G[Ýçø¢8®azMDÛ¡Z&M›€æƒ\°'cÌýÄû¯Bl_K®oô³ªN¤{—Á ·‘ë›_¿ ýömÆ(⩇H¿G®Ïξåfú´·Êξ•Ö£œ´üÀ4ô ªò4^ ÓÐüyh@ CYøø2ÆêZ_‹b³­î+ÉÔUåq Óз¡V"ŸË—-ã!¹Le$ÆL¼@MÞX¶ÅNj½é/b2hOçkd»ÀÆZÜIËw)Šã¦¡KDÛΕyÚ„}Âõ@8ª2cÞðÉ6ˆÔ¿‘ËœUå ÿœ tß Ø±ÁF˦Uð[ä5ÆIü­ÇÚ›"×çŽú2ŠÞ{Èéº Õøx'-·˜†¾MUžF ôA`z)¤ÉE¶¤G>!qZ(‡€¤e¬‘m°¢Øi9:#}q"™ºPU·0 }§”} s´òÄ¥ 1æbã£À>gUg5ÆYU'r3/@yD'¹¾éÝGd_Ra"ÆÐ ÑòêíÐ*ô;ÎÒ}7Àò{ç!wqôÑÈOqt‡ÆZ«Åi8¼@$¦¡¿V‘âûÔúÍÂéAÞêÎÅšJж£-¢w'’)G}½Èì;öaä÷y™\_˜p2 ãÝÿ;Ý{¬ˆ£ïKMC'õeõñ}™†n…5{‘%ér!€“CyÄÀ[Ý«'aGðNÍѨóÓÜ (Žk˜†>_˜<¬U„â –ø€bŒ”Øú8ħ[ÉõÙé_F±c®ÂDÃ_ÖX‚Jó$r}Ó›"”¡'`l0µl^_q¹~À¼Õ{rç&†âØùNZ~eú¯TåiT¼@d¦¡¿c+©õ1aáŒ@Ve$Æ<ã©jyéècë%’©Yªò¸…Ùݽ…}äirLd×™*#1æ RB¬[QÉ“[Ò½Þ:«êD¡£¹é‹Èõ¡=I4oLa"ÆÎ_Í¢uË=äz;Ô‚Lß2…‰Ô©„G!k,qÒ²ÀõjÒ46^ +ÐßÕywÑ/Pë§ø+èóUFbÌŠÐðL¹ ’~Ê)àáD2Å?£í7¢8ð1µ\ö^ Ù>Ae"Ƽ!»x‰üü µèh¤ç^«0Ððdù£H÷Ò75ù*9´l¼keC+²ëeÄÞŠ\_Õ‹Ü´³&|6Ä{2‚ŽŽ.1 }¿ªLŒèŠD4y^U‚| Üü@mౌճ]†ð†qÒÒàۊ⸆iè%DZÏvömׄ\°ð‘çŸ2Ö°Ä[ë!vl ×&~…q'*L4ü¤»—ŠŽ"×7%ïG°´[a"Æèš·®F O~ÆLç%(ÇÜsWmnƹ(wNZV™†¾VUžFÇ tELCß- ]J­ gsÐøØ'cu­­Æ°ßvôRüÉÔ1ªò¸…iè¯Ù¥ì­ä†Ó û~bc$â…{€ýeÒÀÜk\}VÕ‰üø“P˜@¿<+òÉÄ?øÂDŒ9ã³ÊhÙ¸ °i÷FÙþÒ}+a»`©UnžŠì,Gë·ø†¢8 ¼@Wª·kÎcE[üšZ?ÆWÅq>ú96ÆU ž.ÇQ“ä'Z>%’©Æ<øùGD´í;(ì'O›úÙÀ˜†?ÆÏX}Å<´~B.·C-H÷Þ¨0ÐðP µ#Ýu5¹ÞWÜ‹–ÍôGÆŽ”ðþ7Ðô6ýN„JûLäfUa¢Ãgk~ ô­€ô‡¨-5—˜†Îc¨âºbM^Q‘b/µ¾Ï_Ä8Ðæª2ÖÈ>B[jŽÖÛ3üPQ×0 ÝB´mª%ÚÌ"Mƒ½`$ÿñf¬q}°âÍ'Éå¥Ñ}ÈMý…†^¦9ìp ¹¾uË=ðWùò\6<5ýá—¦ß!×ggž‡RëL…‰O¶óT[¦:iù®iè›Tåað]1Óг~à)i·œø…ÓC9ø`«ŽÆ˜ë=oŰۢÍÒýo×%’©…ªò¸…ièïÀ®’§M i äü+&bÌ;´—„HÓGgôKQŽS˜hèd§…âÈr}ìý'ÙõŠÂDŒMÚhyõÇ5Ú d©ù‘î[K *Næ\qD²Ó¾ì¤å%?P‡ý^ Ý]Ï—¤ø)µ¾]«á?ù~9Æ–-5•8*ô­î°:‘LP•É-̾c‚Â>ò´ 9ãT`ê<•‘óY­@¬wvV5Ó“+Ϊ:QŽG¶“|¹hÞús…‰¡Ü‡hþÃ/ÉõÕ¦ Èt]©0‘s5_øÀü¹“p©iè´6vX¼õ×`‹hòæ²ÔvPë ÓDYe$ÆØø¹|8žUuÂò…î_©ùIõ¢VBË«?†&y· s§ØGÿ…èÎçÉõ…qÇ#7aèrgz—Á ·:iùºi蟪ÊÃ>1=ÂLC·#š\lIF›œmuŸàv5iÜÃ4tÑöE¨É4òÄë!ÂŽþ­kLm…xã?Éå¥Ùé‹:Tý+aé—Ùµ¼v7ü5~ÉÀ¼-¾íÿ ´ïMr}vúÙ(µuzެ±•¦‰NZ¾e:ùá3ç"”ã“&r.{ÔÙ(wt‘ëãïþÑÏ6+LÄØð ÁFóÆÛ¡ÕŠÄú–£æ¼»j #{‘›v–“–õ~8hØaáú0aúËe©ýˆZߦÕpj€ŸB3V-'*q”é[Ýà¾D25JU&·˜Ûìjä÷’§MÈé §ÏW‰1o¨Õ ž»ÓáYÕ•°5G#$•)7MAf6ù ³¢)y¿º@Œ 3¡ÂÇhN=H®¯ÅÇ"g^3(¿»æ!Ýãh DÀ妡ó›¿a‚èÃHX³¿Y²µ·©õ¾"f>&ÂX=ûÀ†jÌIK€ÕŠâ¸KlÄ…²œ#O›ó¯â­*1æ bÏûÀ–GÉõΪ^¬.‘­ù1пÒO{Û'ì*Z6ÞŸÍShXc‰¿÷»6’ës“OCaô±‡ý{3=×ÁŠŽtÒ²Ì4ô÷û³Aà ôaÄ4ôZX³Ï²¤°(õB_ eá­îŒÕµÉŠàýZÐIËY‰dªá÷l›†¾Wh~ú«²p3äI7 ×;­VÄ–ƒØ½\¯ê¬ªÙ9£Ú2\ßôæ¿ ”&¿{`ÌSZ6ß­œ&×§{®C-Ø|È¿/?þdÆŸä¤å7¦¡?pÈ¿)Á ôaÆ4ô7k·PëcÂÆÁœÊHŒyÆ“Õ&䥣-¢·'’©éªò¸…ÙÓ÷8ò{ÉÓ&ä¸ÈÎÓUFbÌl ±îNˆ*ý¬jºpϪ:Qj7~6¹>´oâoÒo´fÌkåýhÙr/¹Þ ·a wÙ!ý®J¨ió*'-»\{H¿Œ)Å ôa(¤Émñ µ~š¯S#þqg¬åáÃså$é:F@ Àƒ‰djxüJ±×¢˜&O›ý—­cT&bÌÒ»!^¹Ÿ\^ ÞYU'j¾0zo4ÚWG­Z@ËÆÛ¡ñ.?ÖàbŸ¼€è‡ÿE®/9ùÉÎrgúWÀ9zû~•iè{ÿ"¦/Ї!ÓÐeD“çV%JÔž9´€vÙ cl› ã-ËÑ8°ã|CQ×0 =HËbØíñF yò ò—yÆÚ¶g >|•\>XgUÈšW£K®oN=ˆ`á…‰s–×a¹>m\ŽJt4¹>7m1J#»Dú™iè;i`Gs¦LCß €|þ5¢I, ’ïqb¬¡=U‰"m;z)þ½D2Õ£*[˜†þ2J™SëeÇ ÈÞóTFbÌ$ žÿ)P ·îYU' cC~òiäúÈ®ˆ¿¿Fa"ÆÜÅW+¢eÓ]€MÛQbbH÷¯„M¸Ð¥Ÿˆt§£ $ßðwNؑŠôa¬¯K¤(5òÓ­ñ¾*æû *#1æ UáÃïÊqX’|“™ÀÉd*¬0–;DÛn‘…ý䟤y>0ªáñ3V_!ñûŸ‘ËœU½Qa ªÁ¤çÒ©úJhÞ¼Ja"ÆÜ)²7¦wÿƒ\_nïDnæÁrÛBCº%¤ŸüõÄp™iè|Õ0Æ ôa."ìK*R¯œÈax” cõì@‰š£­î~ (Žk˜†^ѶE¨–IÓ& ù ,ÅÉs?ñþ«Û×’ëKcŽ9¤³ªN¤{—Á ·‘ë›_¿2}'c$žzÌûäúì쯢Üü×§&dg_„JëQN"üÀ4ô NØ‘Ç ôaÎ4ô?@Þ#ÀÂP>¾”…±ºÖÕ¢Øcû´¬L$S§¨Ê㦡oC­Dž6![ÆCs™ÊHŒy†Øð û)¹>m\ŽJ„~VÕ‰ü”3P3\ûðYÄ>ù½’,ŒyOÖÐúêíµ2©^j¤ûWÀù]¥Ô6Ùç:ùõ›|ÏI¼@wî®ÎgжXM­ïÐjXàç­îŒÕcAÚrªô[ÝàD2Õª(’{DZ~„Â>ò­VröB`’£ lkL•2´õw‘Ëí@ éy´³ªŽbDÇ ­Ó¬ùóŸ¢i }‹>c*”}Mo=J®¯4OAöÏþ»hùBH÷-4ò}:%›†ÎÛl]€è.Ñäe[ì¤Ö›þ"&ƒötޱF¶¼Z;i™€þíÙ£LC—ˆ¶Ÿ#*ò´ û„ëpTe,Ƽá“m[#—SΪ:aC Ý·v Fl°Ñ²ù.ø-òÇc -þÖcíM‘ësG}ÅŽ¹ÿó?gº®B5>Þɯ¼Å4ômNØÐáºK˜†^ ir‘-é]ŸOHœÊ! iÇDkd¬(vZŽÎH_œH¦.T•Ç-LCß)¥EÈm‡æJ ©]e"ƼaïG›I.¯4OAÎ8ô -áGº¤FÛQ$je´nºš];äßÉX#‹ð4"Ÿ¼L®/Œ;VÄÑßÏ¥¦¡“Ȳáè.cºÖìE5)H …Nåoug¬ž„Á»–£Q秸AQ×0 }¾ðùÔzŠC.XvàŠ1vPbëãÀ'ôíãÙézVÕ‰¬±•æÉäú¦7E(C߆ÏûK-›WÁWÚ¯âGÿÊ4ô_©øÁL-^ »ièïØÀ j}LX8#U‰1Ïx²G^:úhü_‰dj–ªà¥Ÿ“ËkÑÑHϽ–\où£è¥oòUrhÙx4ð±VÆCd×ˈïxz0äÓЕ¼–gêñÝÅ"š<¯*A¾n~ 6ðøCÆêÙ.CxÊ8iéðmEq\Ã4ô"­g »FûÖîB.XøÈs\kXâ­u;6ë ¿€Â¸Iµéîë`EG‘vSò~K»ÉõŒ±úš÷!'?ã>˜U¦¡¯ŒĆ/Ð]Ì4ôÝÚ¥Ôú°qf0}2V×Új ûmG/Åÿ!‘L£*[˜†þš]ÊÞJn1 ²¿á'Ö1F"^¸(Ð_Š Ì½ÕÐÁ/”Ê? … È?3òÉÄ?ø¹ž1Fã³ÊhÙ¸ °ëÞ¨m¾1H‘ØáºËõvÍy¬h‹_SëÇøª8ÎG?ÇÆX£ªAÃÓå8j’üDËà¡D2åì৉hÛwPØOž6!õ³1 ŒŸ±úŠyh/ü„\n‡Zî½ñ¯þß«¡v¤»®&ÿ<_qZ6Ó?cÌ™ðþ7Ðôöoµ½àÓÐy|“ËñÝ"š¼¢"Å>j}Ÿ¿ˆ1 ÍUe¬‘}„ ¶Ô­·g¸MQ×0 ÝB´mª%Úì%Mƒ½` )NƘ|°âÍ'Éå¥Ñ}ÈMý›Ïý¿eú–÷Vë–ŸÂ_åKgS©é ˜~çPZ¿kú¦ÁÎÃŽ<^ {€ièY?p¶”´ÛZüÂÆÂP>ت£1æzÏ[1ì¶h3ÿÛÒD2µPU·0 ýÔJ7‘šÆ@οBa"ƼC{éAˆ4}´qF¿娏?ùße§…â¨òψ½ÿ"»^!×3Æ&mDßyÂi[ÀÄaC€èÑÝÕù|I òŒ†v­†SüäûåkX¶ÖTâ¨Ð·ºÀêD25BU&·0ç ûÈÓ&äŒS)ý*#1æ ²ZXO?«jû#Èôßû¿¿ö•cã‘í¼„üû¹hÞJž¢È; 5_¹Y8mû¡iè‡ux ¼@÷ˆ&o.KmµÞð1M”UFbÌö €—*1'-cÜ£(Ž»DÛÏ•ùi }üuÑ&•‰ó†]oC$þ•\^n›‰Üì¯ÀÒý+aû‰“*l -›VÁgñ÷ÆŽ„l÷×Q‹qÚÆ77z/Ð=Ä4ôrHØgYRö®kBâÔ`aÞêÎX]¯ÚQ|h9:#}~"™"OYð*ÓÐwAú¿C¤ò„¥O›`¬.±ù×ÀžíäúìÌ é^ŠJÛLrOÓöǦßùÈ; ù±Ç#?ñ§mOš†N?ó†=^ {Œièɪß¡Ö7iN äTFbÌ3ž¬ÄP°}l®J$S“Tåq‹®žþÇßû¹aÒѳAa¬ñØ6´çK›¥æGnò—È?>8ð6š¶ýòPÓ1ƨ†Z‘™{­Ó¶O8nbÃ/Ð=(¬Ù·mñ:µ~¦¿]㉠ŒÕ“ë«q'--H$SüY±¥ yÚ޹h©0c1ð ÄÆ‡ýÇŠZ -o‡&y—cGB¦w¬p«“–çô™†þ¡šDl¨t¡”¤KÁÙ0”H¦&YRÛîvR/%På=¥Œ‘øø„£ÏÇ¿5 ýGŠâ¸FbË–“ ùžƒ¦‘>l„]ƒ¬ðe–Œ‘ø#€ßÑĉƒjM¬FüÝÿ´ŸÇûërSb û:'-™†~™ª¤ÉÅR‚¯æglè]™H¦uˆ¡fúiÕøFwƆ„“QŽ;Z00ÆAadrÓÎrÒ²ÀmŠâ°aˆè Ä4ô—ËRûñPç`ŒV'’©QCb¨Íí?v5ò{ŸêŒ5:éc ÿ&Øšo¨£0æY5 éžœ´dqàÖv¾Xµø‡:;²Âš}KÉÖ‡5û(jOUŸ×Ç4HøéÿUép€†“ŽØˆ¯Èrî=ŠÓÎæÛ6d%Á‚«ÏaRiµu:²³/BË)ÅXcÊô\+:ÒIËrÓÐßW‡ Sý*)%ùñ¢D25Û’"é’ô˜ëÿFG9Ù:žÿÂûßP›‹±“¿ûçÝì¤å7¦¡Ÿ«*:Büm¯¸ièÛj·PëcÂÆéœÊHŒyÆšJò´g_ÿ×í‰djšª¤û—Ãò…Ôæb¬TBíH›W;iÙàëŠâ°aŽè *¤É•líjýt ¦FüãÎXËÇçÊ1'-1%’)>øq-Jò´ »ÿR uŒÊDŒyCz7Ä+Ë«±±Èš×( ÄXcÉô¯€jvÒr•i蟩ÊÆ7^ 7(ÓÐeX³Ï­J”¨='rhAMe,Æ\”ÚÔúñ¾*æû *#1æ UáÃïÊqX’|­{ÀÉdŠvݲ—EÛ¾) ûߦ–Kó|`Ôt•‰ó†BÚ‹÷’Ë­pzoTˆ1ï²…†ô¼› ýä?ë6€ËLC狟/Ð"¾¸"EšZßïÏaª*#1æ ;BÂÙV÷N·*Šã¦¡WE´mªe‹Ô ù`Ÿ´ 'cÌÞ{bûZryiÌ1ÈMú’Â@ŒySnÎ×Pi%O5€[MCß *s^ 3˜†>àΧÖ5`a( l•±ó„uµ(öØ~'-7%’©STåq ÓзÁ*“ÜÐ:ò˜Ë&bÌ;Ć_ÙOÉõ™®%¨DF+LĘ·”Úæ s”£ãW›|OQæ2¼@g€î®ÎµE[¬¦Öwh5œäç­îŒÕcAÚrªÎæuߟH¦ZErpóQØG¾ÕJÎ^9ÑÑE<Œ5¦JÚú»Èåv †tÿ Ø Ùa¬aY¾Ò}Ë|M À%¦¡óöT€èìD4ycÙSëçú‹˜Œ²ÊHŒyÂnðj5î¤e"ú·g2 ]"Ú~ލÉÓ&ä‰×á¨ÊXŒyÃ'Û ¶>F./Б›É22VO¦ëJTãã´ÜbúTåaîà tö?LC/…4y–-é]ŸOHœÊ# iÇDkd¬(vZŽÎH_œH¦¾¢*[˜†¾SÚ5ú@æh;ä ×)LĘwˆûÞ#×gg]ˆrÓT…‰s·â裑Ÿr†“–g¬R‡¹/ÐÙŸ0 ýµŠäKªZ´NòVwÆ(ÖTšP²}ìþ4‘L9z ïEfß±#¿w µ^N9ræI*#1æ –mÝ@¶³Vú‚HÏ[ K8ºWƒ±†P 4a ÛÑâ—›†îìó<^ ³¿Öìï”l¼Õf¦¯ˆÙ‚¼•±†•†ÏWcNZÚ¬N$S|ð36â"”³äiö1Wñv•‰ó†½Al~„\^iž‚œÁ22öçÒ½7ÀŠŒpÒr½iè;UåaîÅ töLC·Âš½¨&ER/pr(x«;cõ$ìÞµ:?ÀõŠâ¸†ièð…ÉÓ&D(yò²PŒ±ƒ[>ÙJ®ÏN]„bÇ\…‰s—ܤÓP;ßI˯LCÿgUy˜»ñ}.ÓÐß±•Ôú˜°pF «2cžñd9†¼tôñû¿ÉÔ,UyÜÂìî^‹Â¾ŸSëå˜.È®3UFb̤„¶þ.ˆJžV¯iÞúùBFÆ*áQÈKœ´| ~ð΂èì¯êïêüIÑ/Pë§ø+èÕŠ*#1æ EáÃ3å&Hú©³€‡ÉüŒ¶ß€âyÚ„ì¹²}‚ÊDŒyCv/ðùùjÑÑHϽVa ÆÜ@ Ý¿VÐѤ–ËMC߯*s?^ ³ƒŠhòüªù¸ã‚y´Ç82VÏvÂVÄIË<ßVÇ5LC/!ÒºHØ5Úã@rÁrÀGžGËXÃo­ƒØ±\_˜øƨ0cÃ[fƹ(wNZV™†¾VUæ ¼@geú.íRj}HØXÌAãcŸŒÕµ¶Ã~ÛÑKñH$SG«Ê㦡o¶KYò´ Œ˜ô7üÄ:ÆHÄ ÷ú˽¹× jS˜ˆ±á©Ü<ÙY:iÙàEq˜‡ðÕÕÛ5ç±¢-£ÖõUqœxޱVƒ†§ËqÔ$ù‰–ÀÉdªá~ŠhÛwPØ¿Zoëçcþ?cõóÐ^¸›\n‡Zî]¦0cíù1зÒ¢¶Ô\b:Ÿeuñ‘D4¹¤"Å>j}Ÿ¿ˆ1¨¨ŒÄ˜'|„ ¶Ô­·g¸MQ×0 ÝB´í,TK¤iÐ4Ø –Aò—)Æ×¯A¼ù$¹¼4º¹©|!#kÙÎKPm™ê¤å{¦¡oR•‡y /ЉièY?pŽ” ûô  C9ø`«ŽÆ˜ë=oŰÛ8iYšH¦ªÊ㦡¿ƒZùfrCÓÈùW(LĘwh/=‘!ßLjŒ~ʱq 16<Û d§}ÙIËKèDzXÃã:#ëîê\_’â§Ôúv­†SüäûåkX¶Ö”ã¨Ð·ºÀêD25BU&·0ç· …}äirÆ©À”~•‘óY­@¬»°-R½í ÓlþjÉ<¬æ #Ý· ÐÈÿ9ϸÌ4tÚ‘/ЙCMÞ\–Új½á/bš(«ŒÄ˜'ìA/UbNZƸGQw‰¶Ÿ/*òÓ@ûøë £M*1æ »Þ†Hü+¹¼Ü6¹Ù|!#ó®l÷×Q‹qÒò·¦¡oW•‡y/Й#¦¡—CÂ^dIAÚ»® ‰Sƒ9„y«;cu½jGñ‘åèŒôù‰dŠ™®+bìÈÊô-‡nuÒòuÓÐ?U•‡y¡c‡Ä4t;¬Ù‹-©‘g©È¡´‰HŒ5²” c{-ì¤åd+Õ¤qÓÐ3ðGÎm“ÞXÈ@òä倯{Ý«G¤ž‚øx ¹>?õLGõ)LÄØ‘‘›²ÅÑŽ.}À4túŽ.Æþ /ÐÙ!3 ýòzj}T³qF0¯2cžñt5ެ³­î·&’)CU·0»»×£4@ž6!GÍœëh\cIJˆõwå,¹e çzÔüŽv16¬T¢c‘Ñ]õ²ÀrEqXƒà:;,}]«‹¶x†Z?ÑWÆ<_Qe$Æ<¡ k+M°é£×BN$SA…±Ü!Ú~3Šäièù*dÇd…óˆÜÄ‹ÿD.·"Èô,Uˆ1ulhH÷¯„ OX‘.7 =£0k¼@g‡-¢É +6ÈÔ äЪÊHŒy»2„d-â¤e.€ï)Šã¦¡—i]„Z•4>Bj~È“W~¿êhŒ¹žxçEˆ÷Ö“ë ãO@n_ÈÈÜ'7ë”Ûg;i¹Í4ôuªò°ÆÁ tvØLCß« ñ5j}PH, æøØ'cÏÖ¢Øg;Z8þ}"™:AU·0 }«¬äÈÓ&Ð: rÞE 1æÚïÿ "÷¹>Óuªá…‰\å–£u“–­¾­(k0¼@gƒ¢·«óñ¢-~I­å«âDŸGg¬ ž*ÇQ“äk Àƒ‰dªá~ŠhÛ­(ìKP륾ßðÇø«K–Š/ÜM®·CÍH÷ó±\æ– Ý¿’>*°àÓÐË c± t6h"š¼¶l‹ÝÔún@¾ž±†õ1‚Øäl«ûTw(Šã¦¡Ûˆ¶/FµHþ ‘'^vôoÍXcúh+Ä¿%——:æ";/ddÃ_¶k *M´|Û4tòÃ`Æêá:4¦¡çCš\,%H#ŽüBâK¡ü e¬¡½hÅð©E~šW&’©Åªò¸…iè;¤U#O›ñQÇ]­2cž!^yø€\Ÿ™sÊqG ÆŽ¨ÂÈä¦å¤e=€ÛÅa ŠèlP™†þrYj?¦Ö·i5œà­îŒÕcK`M¥ eúVw¸/‘LR•É-æö»ù½ÏRëíi §ÏW‰1o¨Õ ­[a×HåÒFzÞM°5Ÿâ`Œ9WóÇî¹ÁIKnmç7MlPñ º°fßR²µ·©õ¾"f>¶ÃX=ûàdž*yÜ ŒpŸ¢8îñYΑ§MÈù×±•‰ó†ÏÞ¶&lœÈ©ŒÄ˜g¬©4!/m½#‘LMS•Ç-Ìž¾ß"¿—rbÃãg¬¾JÚó?!—ÛÁ8Òý+`ƒ|d‡1Ç,_é¾e€F¾¿¥àÓÐy[';bxÎŽ¨ˆ&o,Ûâcjý\“QV‰1OØ^­Æ´L°JQ×0 ]"Ú~ލÉÓ&ä KpTe,Ƽáã?@l}Œ\^¡#7“/ddêdº®D5>ÞIË-¦¡ÿAUÆ>/ÐÙez)¤ÉE¶¤w}>!qZ(€¤e¬‘m°¢øØrtFú’D2õUyÜÂ4ôÒ®‘§M 6ò„ë&bÌ;ÄÆG}ï‘ë³³¾ŠrÓT…‰X£*Ž>ù)g8iyü › ^ ³#Î4ôÍ)È—Tµh5|)È[Ý£x¢Ò„’íè£ý§‰djœª üaò´ ŠC.¸ñÀcì ÄÖÇO¶’ë³S¡8ÂT˜ˆ5ŠÜ¤ÓPwœ“–GMCÿgUy«‡èlȘ†þŽ%q3µ>&,, dUFbÌ3ž,Çs¶Õý¶D25KU·0»»×¢°ïÔz9Ö„ì:Se$ƼAJhëäiõš†tß°ü|!#;tÕÈHd%NZ>?°fCŒèlHÍ3;Wmñµ~Š¿‚­¨2cžP><[i‚¤Ÿž‹x(‘LñÁÏhûõ(§MÈž‹ Û'¨LĘ7d÷/ýœ\^‹ŽFzîµ 1oè[ +èhÂÉÓÐ÷«JÄ/ÐÙ‹hòüªù¸ãƒy´ÇQ2VÏvÂVÄIË<ßRÇ5LC/!ÒºHØ5Úã@rÁrÀGž«ËXÃo­ƒØ±\_˜øÆñ…ŒÌ¹ÌQç Üa8iYeúïTåaŒŠèlÈ™†¾K@#ß6sÐøØ'cu=[a¿íè¥ø·ÉÔѪò¸…iè›íRöä†Ó€þ†ŸXljxág@þ’r`î5¨†Ú&b^SnžŠìì¯:iyÀ-Šâ0æ/ÐÙ°ÐÛ5ç×E[CɆ^ ³a#¢É+*Rì£Ö÷ŠƒŠÊHŒyÂGâõš£õöL·)Šã¦¡[ˆ¶-BµDš6Mƒ½`$)d¬q}ðĶ'Éå¥ÑýÈMå Y}ÙÎKPm™ê¤å{¦¡oR•‡1§xΆ ÓÐ3~à)A:÷é6Îåàƒ­:c®·ÞŠa·pÒ²4‘L¡*[˜†þ6jeò´ 4œ¿D] ÆFdôËPŽŽS˜ˆ¹]±Ý@vÚ—´¼€~œ‰±#€èlXéîê\_’â§ÔúZ §øÉ÷Ë1Ö°l ¬)ÇQ¡ou€Ÿ'’©ª2¹…9ï¸U(ìû=µ^Î8 ˜Ò¯2cž «ˆu«Û"ÕÛþ2ón‚Í__Ùç¨ùÂH÷-4ò>ò.3 ¶Kб#„?áØ°ÑäÍe©í Öþ"¦¢¬2cž°¼T‰9i €üÀÌÓ¢íç‰Jü4Ð>þ:ÈH“ÊDŒyîí‰%——Ûf"7›/dd)ÛýuÔbcœ´üièÛUåaìPñ ;¦¡—CÂ^dKAÚ»® ‰ÓB9„y«;cu½jGñ‘åèŒô‰dêRUyÜÂ4ô]ârrC¤òÄ¥O›`¬.ñÚcÀú:);ó”Zg*LÄܦ0öxä'žâ¤å ?S‡±Ã t6,™†¾µ"Åw¨õMš…Ó9•‘óŒ5• ¶£ÿU‰dj’ª™®+bn‘î]+Üê¤åZÓÐ?U•‡±ÃÅ_¶Ø°eºÑäbKjäYj'rhßõÁX=)ÆöZØIˬTÇ5LCÏÀ9¶Mzó"ƒQÈ“—ïug¬‘z âã-äúüÔ¿AqTŸÂDl¸ËMYˆÒ˜yNZ4 ¾б!À t6¬™†¾Ã†¼žZÕlœÌ«ŒÄ˜g<]#ël«û­‰dJW•Ç-Ìîîu( /Ï“£æ@š‹UFb̤„XPÎ’[º—¢æw´#ˆyD%:ÝÑ);,S‡±Aà t6ìõuu®.ÚâYjý$_ó|E•‘ó„4¬­4Á¦^ x$‘LÆr‡hûÍ(|@®ïýdÇd…óˆÜ´ ÷‘Ë­èHdz–* Ć#Òý+`È“I$€ËMCÏ(ŒÅØ à:s…ˆ&¿R±A~¤~l ‡¨ªŒÄ˜'¼+CHÖ"NZæø®¢8®az‘Ö³P«’ÆGH͹`9à÷«ŽÆ˜û½ý{ˆ÷Ö“Ë ãO@~ÂbÃMnÖ(·ÏqÒòCÓÐשÊÃØ`â:sÓÐ÷jB\D­ ‰3ƒ9>öÉÁ³µ(öY>'-ßH$S'¨Ê㦡oE5ÿ䆶ÉóÈcŒ54í÷ÿä>#×§»®D5Ü¡0.Ê-G!;ë'-[|KQÆ/Йkôvuþ¶h‹_RëGùª8AãóèŒÕcAÃS•&Ô$ùO‚àÁD2Å?#­ßGayÚ„ÔCŽkøcüŒÕ%KEh/ÜM®·CÍH÷/W˜ˆ – Ý¿’>b¯àRÓÐË c16¨xÎ\%¢Ék+Rì¦Ö÷ ˜ò%ðŒ5¬Ä檣­îSÜ¡(Žk˜†n#Ú¾Õýƒæ¤ ÂŽþ­kLm…xã·äòRÇ\d§Ya 6Ô²]KPišè¤åÛ¦¡¿®*c*ð¹Šièù ‹¥iÄ‘_H|)”ƒ¤c¢Œ5´ßÛ1|j‘ßJÀ•‰dªá¯'7 }‡´ªäi2> ò¸«UFbÌ3Ä+îcÌ̹帣s‰ÂÈä¦å¤åy·)ŠÃ˜2¼@g®cúËe©ý˜ZߦÕpj€·º3V-5•&”é[Ýà¾D2õÿ·w¯av•õÝÇkÍžÙ³çÃL œ 0kÍÊ$™L„AJ@¬x@@<€œb¢¶jÛǶzUy®§ô©«ÕJ­B/k¯Z}a¤FÎ ›½K¬(E0LÂìÙ{fŸ×ý¼Hû\­Uç^;3{íïç%üÿ×þ]y1{ÿ׺+\eêk'O»E3û¬o›ˆWm‘N8Ýe$ ZMù»n–·¬ÊM¦_¥ïUì':W \;3 Òºë’´”%]…ohÐqÐÑ‘úýøCµØÒ¶~¬§ªÕÛ€¹ìWF÷7­¯­‘¤å’ìïDJ³ÁÑ7™zÅú¶‰øô+¥ÁÅ.é𳧤Gÿκ¼±ø•Oæ@Æ4)­»FíåIZ¶GaðoŽâN1 £#EaÐì÷ã­mãµmê=O:«¯¬KÝ9=ÜÎéév¢«Î·æ ÅwºÊÓ)¢0Øëùû© ‘Ìæë$n›æä=úUé…ÝÖõå/Rmé˜ÃD8TfŽÜ¢Ù#7'iùjŸw•p+ ƒÝméC¶õC~¬sz+.#©±³1¬Y“h‰èŸå ÅU®òtŠh݆¯kfß—lë͑륱s\FÒ!6òwÝ$¯Yµ«÷{Tš|Ú=Y·¹àT#;¢R”èÌŽç%½ÛQà`@GGëóÌŸTcï{¶õ'djŠüšËH@*̨Gÿ\O´Ô}P®^cãçà蕪M[ß6O^&-9Ìe" J/Èûî¬Ë›ƒG¨œl¸Ã3=¹]qvQ’–wEað3Wy€C- “óÍE-c?uŸÑ[ÖbÙ6t³Ý¦_»[ýIZ6Iú€£8# ƒõ/ºPqÛê¶ õö˼z‡äó• Ìi÷òžyȺ¼rìk4{ØiÁ•ʪ­ª-ŸHÒòÙ( ìïå(~  ãEaðÓXæJÛúœot^KÝßl ª'z)þ‘|¡˜èUEað€jÓÖ·M˜e«eÖ½Þe$ ŒäÝý)©:eÝRZwµZ½ÃCá`«¥ÒØ¥IZ~$éýŽâ‡:Rar|ì¶jìí´­_ÙÓÐé=³.#©ÐôztG}H±±>ɬWÒmùB1Ñ«÷TXú!3ûâlËÍÚ‹¥'¸L¤Ãì´üû>k]Þî_ª© ÛÂÁ{¾JwÈd¬¿FbI—EaÀÛ¤:R#ç›KÆ+ÙÖOf*Z®¦ËH@*<­¬kå’´’>î(NLj é ,=_ͺÕmò{oÞ&õö:N¤ÀSß•÷ä·¬Ëk‡ŸªÊ1¯qKå”·¨±du’–¢0¸ßUàPc@GjDa0•‘Þ`[ßçKçeËêáê5`N»ZÚg’´ìÈŠgºÊÓ)¢0Ø­Võw¬–%sêeéáÝ÷WRù9ëúéðmjô¯p˜/WméÉš>ñ7“´<"é#Žâó‚©21>vg5öþʶ~™ßÒæ Kݹ´åkg}XM»cϤ·{!_(.q—ªCä–ܨÙýÖ§Z™“Ï“9ºë·ñskÔåßýçÖåqßJw(–õ–B힬JÞ#ùÖçžÔ%]…Ë!‘* èHœo®«ÇÞÛúµ™ªŽUÝe$ ^P¯¾×JÒr´¤›ÅéQ Œ¼ÎkV­o›0g\#õ¸Œ¤Ãž'ä=þÖåõÑ@•5ȸM¿CÍ¡#“´| ƒ'\åæ :R' ƒZÖ7[cãY½ëëñŒÎÎÎ¨×ØmºÙýííi'Ú#}i¾P¼ØUžN…ÁOMܺʺapT挫&ÒÃ{èËÒþ§¬ëËkÞ¤úðñ!©êa¯ÐÌqç&iù–xŒ”b@G*EaðHÃxÖ‡T-ö[zMKÝÿÔV-NôõñùBq¥«<"ÚpÚ­šÙg}Û„9î•2«_å2í¶ü]&µìV:›LVSw¨í%:WŽ´z‡55‘è䔤+¢0°ßttt¤V¿ÿA-ö­—>ÔSÕÉžõ T k•”Ñ=ÍÁ$-#’nÉŠlü½ÄÔÊÖ·Mħ½Sq™H‡}ÏÊ{äo¬Ë›‹ŽS%à@Æ… ´îZµs£IZ®ÂàYWy€ùÆ€ŽÔŠÂ ÝïÇ[[ÆkÙÔ{žôêìŒÄRw`.Å9ý¸•MÒr®¤kÅéQLy™~ëÛ&¼ìÌ–ëüð+yÿ£ô\Áº¾¼êUG#‡‰0—Ê1g«ºò•IZ¾…Á—\åt¤Z?j½Ï¶~Ðkëµ½e—‘€Ô¸½1¤J²¥î7æ Å5®òtŠhbâNÍîÿ¼m½9"’?Ïe$ Œ‘¿ëfy»zßWiÃõjgrnsájæ–«^‘¤exЋ.À€ŽÔÛÝ\½{lëË4´Î¯ºŒ¤BÕëÑ?7†eìwæ$Ý–/Ùø90r­ªSÖ·M˜u—ÈŒå2å}ò´~þ¥ÖÀa*­}·Ã@øÅùÍIZ~ 郎â :ºÆúñS¾R½°­?¢§©Ó}Ë}l@kÈ×õ!µŒõ­I·æ ұ:‚7°ô÷5ûânÛú8¸H:ü$—‘€t¨Väßóiëò8»X¥õ×; IŠýŒ¦6l—ÉX2Ú’ôÖ( Ø{ˆ®Á€Ž®’óÍÛÆÛo[?Ù[Õáj¸Œ¤Â3êÓc­DóöIì(NLj ­¥[Õ¬YÝ6!ßW¼e›Ô—è} ;ýäy»o·.¯6©ÊñÈèRyìR5Ÿ¤å£Q<ì*°1 £«Da0‘.2FVû>3^¬s³õ(v èxßiê…¸7I˵ùBñ\Wy:EOªU·¾mBǘ~…»@@Šx~QÞ´õyŒš.W}`¥ÃDÝ«:ª¼êÂ$-H²ß¤:ºÎÄøØwjƳ^÷6ê·tVÆú|9 kÅFÚYRÃ~©»$Ý’/G]eêÑÆWÞ¬Ùý÷ÚÖ›ÕgKÇMºŒ¤C³!o×ÍRܶ*39MOîPÌO䃪ÕÓ¯Ò†m’oýï:#éò( ìV)Â_t¥œoÞW7þÓ¶õa¦ªãUw H…½êÕƒÍÁ$-+%ÙoM³‘ßô³ÖOãMWËä†]&ÒáùÊËź¼>r’*'s ãÁTžx·Zƒ‡'iù­( ~è*°1 £+EaPÏzñÖØxVk×}Ïè׳õ³Ô˜ÓwÛz¶hôÅùBñRWy:EÏËóÞfÝ["óª«%n›æä}ÿ+Ò^ûy¯¼æbÕ¯v˜¨{̱I3GŸ•¤åŸ$}ÆQ`Ác@G׊Âàñ†ñþÀ¶~‘ßÖÙ½—‘€ÔØÙTÕ$úŠùd¾P<ÆUžN1¾nòï5³Ïú¶ sªÌI‰~øݩݖ¿ëf©i·ÎøMoÜ¡¶Ÿè\ üœfßb•Ö^•¤eŸ¤wFa`uVF èèjý~üñjìåmë×djók.#©0­ŒvÕ‡’´,–ô×ùB‘÷Áƒ£oWmÚú¶ ½â iÑ2wy€´xq¼‡oµ.o ¥éðí¥_iý6µû—$i¹* ƒç\å::ºZqÎ7´o}—Ú–ÞŠ‰3K€¹M¿~ØêOÒr¦¤÷:ŠÓ1¢0˜V&÷zűÕ$Ó7 óêí’ϳ `.^ñòöž/Wy:E41±Kµ)ëÃóÌŠSd¢ \FÒÁyßù¤T/[·LM\£V&ÑŠ ®×8BÓÁeIZž–´ÍQ £0 ’6ŒÝR½»lëé©k²§ê2 5ùº³1¬Øþ구¤Ûò…bŸÃXa`ä}ªNýĶÜL¼YfÙ±.éP™’ÿç¬ËÛË5½îj‡Ò%–¯ÒävŽÖ7zIo‹Â`Úa, c0 ÿ!ç›76bY?R?½·¢Q5]FRáÇ&«b+—¤eBÒGÅéQÔ•[r¾ZM»ë#2½2[Þ#e2Ž“)ðä½òžºÛº|öÈWiæ¨3JÊI«>rJ’–?‰Â`—«<@§a@þCû|ϻ;Ï3:¯¯Â¶OÀ·ZÚßîIÒò|¡¸ÉUžN…ÁãjÎü¡uÃÒce&ßâ."þ½Ÿ•*?³®/¿Cì¨ÃD¯¾øD•Oº8IËã’þ—£8@Gb@þ‹õãc_¯ÆÞ—lëWô4u†Ï~t`.mùúFcX-û«×|I·æ E6~æ–|L³û­o›0á…2+»~?0'S«Ê¿Çú¨ÅÙEšÞ¸Ýa¢ÎÖözUÚ°]Æþjº†¤Ë¢0°»ûè èÀÏÉùæÊ†ñ^°­_×;«#e}<еö¨O4-u?^Ò'ÅéQĹ@ÍšýšÍ×IÙDÿÖ@wz6/ï_¾n]^[¶Vå.t¨s•ǯPcÑ1IZ>…Ác®òŠø9QÌôyæBcduÅQÆ3:'[QFvÛDnvo<¨çÚÖoW$éùB±ë'ÂàiÓn^g[o†VH›Þå2ÞwÿFšzƺ~ú”KT:Úa¢Î3»lB•Uç'i¹[ÒŽâø¢0x nü?µ­_ê·ôk–ºs‰´³1¬ºýRwIú\¾P\î*S§X;yÚç4³Ïú¶‰xÕiÕi.#éÐjÊßu“¼¸eUn2ý*m|¯bŸÑ’ÔÎ ¨´þú$-e8µ7À/À_à—è÷ãÕbÿG¶õA¦ªÕÛ¨€¹ìWF÷7­¯ß‘¤å’nq§³ ޾ÉÔ+Ûòø•WIƒ‹]&ÒágOIþuycñ *ŸòV‡:Giâµ=CÝ…ÁS®òŽø%¢0höûñùmãµmê=O:«¯¬KÝ9=ÜÎéév¢«Î·æ ÅwºÊÓ)¢0ØëùûcÚûÉl¾Nâ¶ `NÞ£_“^Øm]_>ñ"Õ–Ž9L´ð͹E³GmNÒòµ( >ï* èÀ¯…Áî–ÑïØÖù±Îéµ~¹tµaÍšDW¯}"_(®r•§SDë6|]3{ÿÖ¶Þ¹^{ËH@:Äñ¥îͪ]½ß£Òä6µ{²ns-PÍìˆJQ¢³.ž—t•£8@j0 sÈúæÆjì=d[B¦¦È¯¹Œ¤ÂŒzôíF¢¥îC’¾˜/Mõ©4¸ìJÕ¦­o›ˆ'/—–æ2¥ä}ï ÖåÍÁ•*'RS£4¹]qvQ’–wEa`ñ<Ð¥Ð9Da`r¾y]ËØOÝgô–µXv‡ÍÝ쉸_»[ýIZ6IúmGq:Fõ/ºPqÛê¶ õö+Þ²]òùÚæôÄòž±~.¯Ê±¯Qõ°SZx*«¶ª¶|"IËg£0°¿Ïèb|S¢0øi,c½,+ç×ÇRwÀÆ7ƒ*ʼn^Š4_(&úe˜FQ< Ú´ý=ñË×Ȭ{½ÃD@JÉ»ûSRuʺejÝ5jõ; µpÔÒôØ¥IZ~$éýŽâ©Ã€Xš»µ{;mëWö4tjϬËH@*4½ÝQRl¬O2ë•t[¾PLôê=•–~Ð̾h}Û„Y{±´â—‰€t˜–ßg­ËÛýKUZ¿Ía …!ö|•6îPœ±þóKº< ÞZ–Ðr¾¹¤i¼’mý+2-WÓe$ žVVµrIZIw§cDaÐô–ž¯VÃê¶ ù=Š7o“z{'Rà©ïÊ{ò.ëòꧪrLºd¬œüf5–¬NÒrC÷¹Ê¤:@S=Òlëû|é¼lY=\½ÌiWk@{ãL’–ùBñLWy:E»Õœµ¾mBKŽ’9õ2‡‰€ôðî»E*?g]?¾MþÍŸÚÒ“5½Úú'$}_ÒGÅR‹Hhb|ìÎjìYßá¹Ìois†¥îÀ\Úòµ³>¬¦Ý±gÒÛ½¿/—¸KÕ!rKnÔìþ‡mËÍÉçÉÝõÛŒy´8ïIDATø¹5êòïþsëò¸oèÀpYoÙé힬J¶I¾õy!uIo€e„@B èÀKó͵õØÛc[¿6SÕ±ª»Œ¤Â êÕCÍ¡$-GKºÙQœŽ…ÑÀÈ…^³j}Û„9ã©Àe, ö|¼ÃD‡NuÅFÍwn’–oIºÉQ õЗ( ƒGÆ»Á¶~±ßÒkúXêØØYR-NôõùBq¥«<"ÚpÚ­šÙw»uÃñ›dV¿Êa" %Úmù»n’Zv+¶M&«©;Ôö«±à´z‡5µîš$-%IWDa`¿Y ÀÀ¼ ý~üûµØßm[ROUk<ë¨@ךòzuOs0Iˈ¤[ò…bº6~¾ƒ£o1µ²õmñ©ï†F\&Òaß3òùëòæ¢ãT :û@ÆÒºkÕÎ&i¹& ƒg]åº:ð2DaÐî÷ã­-ãµlê=O:+;£±Ô˜ËcqN?ne“´œ+)Ñ«ž4ŠÂ`ÊËô[/ù÷ú‡e¶\à€_É{ü¥ç ÖõåU¨:9LäNåè_Wuå+“´|9 ƒ/¹Êt tàeŠÂàɶÑûlë½¶^ÛWq HÛCª$[ê~c¾P\ã*O§ˆ&&îÐì~ëÛ&Ì‘4~žËH@:#×Íò3võ¾¯Ò†ëÕÎäÜæ:Èš¹å*¿=IËI×:Šttà ØÝ\½{mëë©k_u H…ª×£n ËØïfÌIº-_(vöÆÏƒa`äZU§þݶ<^w‰ÌH¢SšîTÞ'ïAëç_j ¦ÒÚN:ÑÓÔ†íj÷%ºQãŠ( ö»Jttà Éùæ7›FÖ§Àmê›ÑRq=(0—5YýK;ÑÛ§’~ÏQœŽ…AM¹%ç{qËîñFoVfË{¤ë{ŽîõƒoË{ú~ëòÙ£ÏÔÌg8 tðLŸx‘êËÆ“´|2 ƒ;\åº :pDað¼'ÿm¶õY/Ökû*òÙö Ì鮿 ^Œ½ÿp¾PÜè*O§ˆÂà‘¸VþßÖ £«¤É7:L¤‡wÏg¤Ù­ëKW©™]â0ÑËW>Nå“ßœ¤å’>à(ЕЃhýø)_½°­?¢§©Ó}Ë}l@kÈ×õ!µŒõ­Xê>à0VGð–~X³/Zß6I‡Ÿä2ÕŠü{>m]g«´~›Ã@/Oìg45¹C&c}8gKÒ¥Q°g8ˆЃ,ç›·7Œg½kCïŒWÃe$ žQŸk%š·×Hú?ŽâtŒ( ÚXºUÍšÕmò}Å›¯—ú t§Ÿ<"o÷íÖåµÃ&U9~aÈX>å­j.>>IËG£0xÈU [1 YÓéõÆÈjßg¯'›­¨G±ëh@ÇûN{P/ĽIZ®ËŠçºÊÓ)¢0xR­úû­!súî)â=øEyÓ{¬ë§Ç.S}`¥ÃDÉUGB•Ox]’–%Ýà(ÐÕÐ&ÆÇvÕŒg½îmÔo鬌õùr@׊´³>¤†ýRwIº%_(ŽºÊÔ)¢¯¼I³û­o›0«Ï–Žt H‡fCÞ®›¥¸mU÷hzr‡âò3¼ÕÓ¯Ò†m’ogFÒeQØ­ÊÈÂøË¤PÎ7ï«ÿ'¶õa¦ªãUw H…½êÕƒÍÁ$-+%}ÊQœÎ20ò¯1ký40>ãj™Ü°ËD@:<ÿCyù¯X—×GNR夋²W^{•Zƒ‡'iù­( ~è*ÐíÐG¢0¨g½øüØxVk×}Ïè׳õ³Ô˜ÓwÛz¶hôó…⥮òtŠ( ž“çYß6¡Ü™W]-qÛ0'ïû_‘öÚÏ­å“Þ¨ÚâÕÍmöˆMš9æ×’´ì”ôGqˆp* ƒÇ›òþж~‘ßÖÙ½‡‰€ôØÙTÕ$úûd¾P<ÚUžN1¾nòï5³ï«Ö Çœ*sÒY)ÑnËßu³Ô´[ güŒ¦'·«í':Wã iö-Vií•IZöIzGVgìxiÐDz^ü±jìåmë×djók.#©0­Œ¾ÓJÒ²XÒò…"ïƒG¯PmÚú¶ ½â iÑ2wy€´xq¼‡o³.o ­éðíýr¥õÛÔî_š¤åÝQ<ç*€ÐÇ¢0ˆs¾¹ e<ë»Ô¶ôV´Hœ½Ì¥÷뇭þ$-gJÚá(NLjÂ`Z™ÜëÛm©1}2¯Þ.ù<Ûæâo—·çQëú™U¿¡êŠ ýO•ãÎUíðIZ¾…ý&{/:pDað´‘®µ­ðcÓ7ã2ßl©'ú:»!_(®òtŠhbb—jSև癧ÈD¸Œ¤ƒ1ò¾óI™ZÙºejâµ2‰V½d#4\ž¤å'’¶9Šàç0 ‡È†ñ±[ª±w—mý1=uMöT]FR¡&_w6†Û_½–•t[¾PœŸŸ ÉÀÈûT²¾mÂL¼YfÙ±.éP™RÏŸ³.o,×ôº«: –§ÒävŽÖ7aI—Ga0í0€ÿ‚8„r¾yS#–õ#õÓ{+UÓe$ ~l²*¶rIZ&$}ÔQœŽ…A]¹%[ÕjÚ­uÏôÊly”É8N¤À“÷Ê{ênëòÙ#_¥™£ÎtHªœôFÕGNIÒò£0Øå*€ÿ‰8„¢0Øë{Þ%¶õ}žÑy}¶}îj h»'IËò…â&Wy:Ey5gþкaé±2“oqHïž¿”*?³®/¿C쨓,õÅ'ªœìîõÇ%ýž“0~)tà[?>öõjì}ɶ~EOSgøìGæÒ’¯o4†Õ²¿zÍ—ôÅ|¡xh6~.d¹%ÓìþÇmËMx¡ÌÊ®ßÆÌ­>+ÿžO[—ÇÙEšž|ÏAÑözUÚ°]ÆþJ·†¤Ë¢0°»3ÀAÀ̃œo®lïÛúu½³:RÖ‡À]kúôH3ÑR÷U’>á(NLj ÖÀÈV5köh6_'eý[ÝéÙ¼¼'þѺ¼¶|Bå.<¨ÊãW¨±è˜$-¿…Ác5+ èÀ<ˆÂ`¦Ï3#cSŸñŒÎÉV”‘Ý6Q ›Ýê¹v¢óßÞ™/·ºÊÓ)¢0xÚ´›×ÙÖ›¡Ò¦w¹Œ¤†÷àmÒÔ3ÖõÓ§\¢úÐÑå³g—M¨²êü$-wKúãƒòác@æIÔÿ§¶õKý–~-ÃRw`.±‘v6†UÞpK¾P\î*S§X;yÚç4³Ïú¶‰xÕiÕi.#éÐjÊÛu“¼¸eUn2ý*m|¯bïåýTogTZ}’–²¤·EaÀ`ž0 ó¨ß?T‹ýÙÖ™ªV{læ²_ÝßJ´µ|¹$û;‘ÒlpôM¦^©Ø–ǯ¼J\ì2 ÞÏž’ý;ëúÆâT>Åú\Ù_¨4qÚ‰ž=nÂà©—õ¡^t`EaÐì÷ãóÛÆkÛÔ{žtV_Y9–ºsz¸ÓOÚ}IZ.ÈŠït•§SDa°×ó3öÇ´÷/’Ù|ÄmÀœ¼G¿&½°Ûº¾|âëU[:ö’>kæÈ-š=js’–¯Eaðù—ôat`žEa°»eô;¶õC~¬sz­_n]íŸÚ5‰®^ûD¾P<ÞUžN­ÛðuÍìý[Ûzsäziì5.#éÇòwÝ$¯Yµ«÷{TšÜ¦vO6ÑÇ4³#*E‰Îˆx^ÒU‰>€ èÀõÍÕØ{ȶþ„LM‘_s H…õèÛÁ$-C’nÍЉ¦úT\v¥ª%ë œãÉË¥%‡¹L¤Céyßû‚uysp¥¦Ç“-î)MnWœ]”¤å]QØ_ØÀt`ˆÂÀä|óº–±ŸºÏè-k±ì›ºÙq¿~ÐêOÒ²IÒo;ŠÓ1¢0¨(·øÅm«Û&ÔÛ¯xËvÉç§0§'î”÷ŒõsyÍwŽª‡jU[YµUµåIÒ|6 ƒ¯'iàߢÀ…ÁOcëåe9ßè¼>–º6¾ÑT)NôRü#ùB1Ñ/Ü4ŠÂàÕJfݰ|̺×;L¤„‘¼»?%U§¬[¦&®V«wøWÖÔÒôØ¥I’üHÒû“4p‹X@&ÇÇn­ÆÞNÛú•= Ú3ë2 M¯Gw6†ë“Ìú$Ý–/½zO¥‘˜Ù­o›0k/–Vœà2³ÓòïÿKëòvnD¥õÛ~éÿ=_¥;g¬ÿlÅ’.€§ýÀ€,09ß\Ò4^ɶþÔÞ-WÓe$ þÍôé±V.IK écŽâtŒ( šÞÀÒóÕjXÝ6!¿GñæmRo¯ãd@ üøAyOÞe]^=âTUŽ9ûþ¿ÊÉoVcÉê$Ÿ~C÷%ià:°ÀDa0Õ#½Á¶¾×3:/[VW¯sÚÕÐÞv&IË{ó…â™®òtŠ( v«9k}Û„–%sêeéáÝw‹T~Þº~:¼Bþÿí¿Õ–ž¬éÕÖ?$éû’>’¤À¡Á€,@ãcwVcÏú.Òe~K›3,uæÒ–¯a5íŽ=“Üîý…|¡¸Ø]ª‘[r£f÷?l[nN>Oæèµ.éШ˿û“ÖåqßÐ¥ì:°e§Ý“UiÃ6É·>g£.é­Q°üX€Ð*ç›k뱷Ƕ~m¦ªcUw H…Ô«‡šCIZŽ–dÿë9¥¢00¹ÐkV­o›0g\+õ¸Œ¤Ãž'ä¾f]^ TYsà@Æéñw¨9tT’Oû`O$ àPa@¨( jYßlgõ®¯Ç3:;;£>–ºsº¯= =íD{¤/ÍЉ֦Q?5qËú¶ Ž*Þd_t3ï{_’ö?e]_^ó&•W¯™ãÎMò1ß’tSÒl_yœ­1ök¸ñÝüë÷ãßµ­¯Ä¾š<{æÔçÅô=ÐÚ/i< ë•-i•ð;;58j?Ìì•Édåy‰®ººŸ‘2}öõq;ÉÒö’¤0 ƒg_B2‰çýêeÐ._(öÔb¿ÐïÇ'Ïwº]ÒyQtõd¾P\bjåóú‡Ù›tŽK¢0øÒ|‡ºÝ\:¯Ù€. ƒv¿om¯5ßYè\IoŸïó- ƒ)/Óñ|ç`íË ç@g`@:@O¶Þ7ß9H’>0ß‚hbâÍî·¾mÀ¼Ù#éÚùÀ:Ð!6Fc7WcïÞùÎ@'å Å¥óbA¹VÕ©Ÿï~¥+¢0Ø?ß!Øa@:HÎ7o0F|ÉóÓÎtà¶ å–œ/‰-8ÀÂôÉ( î˜ïì1 $ ƒç scikit-build-0.11.1/docs/make.bat000066400000000000000000000145021365474353700165350ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :endscikit-build-0.11.1/docs/make_a_release.rst000066400000000000000000000174501365474353700206040ustar00rootroot00000000000000.. _making_a_release: ================ Making a release ================ A core developer should use the following steps to create a release `X.Y.Z` of **scikit-build** on `PyPI`_ and `Conda`_. ------------- Prerequisites ------------- * All CI tests are passing on `AppVeyor`_, `Azure Pipelines`_, `CircleCI`_ and `Travis CI`_. * You have a `GPG signing key `_. ------------------------- Documentation conventions ------------------------- The commands reported below should be evaluated in the same terminal session. Commands to evaluate starts with a dollar sign. For example:: $ echo "Hello" Hello means that ``echo "Hello"`` should be copied and evaluated in the terminal. ---------------------- Setting up environment ---------------------- 1. First, `register for an account on PyPI `_. 2. If not already the case, ask to be added as a ``Package Index Maintainer``. 3. Create a ``~/.pypirc`` file with your login credentials:: [distutils] index-servers = pypi pypitest [pypi] username= password= [pypitest] repository=https://test.pypi.org/legacy/ username= password= where ```` and ```` correspond to your PyPI account. --------------------- `PyPI`_: Step-by-step --------------------- 1. Make sure that all CI tests are passing on `AppVeyor`_, `Azure Pipelines`_, `CircleCI`_ and `Travis CI`_. 2. Download the latest sources .. code:: $ cd /tmp && \ git clone git@github.com:scikit-build/scikit-build && \ cd scikit-build 3. List all tags sorted by version .. code:: $ git fetch --tags && \ git tag -l | sort -V 4. Choose the next release version number .. code:: $ release=X.Y.Z .. warning:: To ensure the packages are uploaded on `PyPI`_, tags must match this regular expression: ``^[0-9]+(\.[0-9]+)*(\.post[0-9]+)?$``. 5. In `README.rst`, update `PyPI`_ download count after running `this big table query `_ and commit the changes. .. code:: $ git add README.rst && \ git commit -m "README: Update download stats [ci skip]" .. note:: To learn more about `pypi-stats`, see `How to get PyPI download statistics `_. 6. In `CHANGES.rst` replace ``Next Release`` section header with ``Scikit-build X.Y.Z`` and commit the changes. .. code:: $ git add CHANGES.rst && \ git commit -m "Scikit-build ${release}" 7. Tag the release .. code:: $ git tag --sign -m "Scikit-build ${release}" ${release} master .. warning:: We recommend using a `GPG signing key `_ to sign the tag. 8. Create the source distribution and wheel .. code:: $ python setup.py sdist bdist_wheel 9. Publish the both release tag and the master branch .. code:: $ git push origin ${release} && \ git push origin master 10. Upload the distributions on `PyPI`_ .. code:: twine upload dist/* .. note:: To first upload on `TestPyPI`_ , do the following:: $ twine upload -r pypitest dist/* 11. Create a clean testing environment to test the installation .. code:: $ pushd $(mktemp -d) && \ mkvirtualenv scikit-build-${release}-install-test && \ pip install scikit-build && \ python -c "import skbuild" .. note:: If the ``mkvirtualenv`` command is not available, this means you do not have `virtualenvwrapper`_ installed, in that case, you could either install it or directly use `virtualenv`_ or `venv`_. To install from `TestPyPI`_, do the following:: $ pip install -i https://test.pypi.org/simple scikit-build 12. Cleanup .. code:: $ popd && \ deactivate && \ rm -rf dist/* && \ rmvirtualenv scikit-build-${release}-install-test 13. Add a ``Next Release`` section back in `CHANGES.rst`, commit and push local changes. .. code:: $ git add CHANGES.rst && \ git commit -m "CHANGES.rst: Add \"Next Release\" section [ci skip]" && \ git push origin master .. _virtualenvwrapper: https://virtualenvwrapper.readthedocs.io/ .. _virtualenv: http://virtualenv.readthedocs.io .. _venv: https://docs.python.org/3/library/venv.html .. _AppVeyor: https://ci.appveyor.com/project/scikit-build/scikit-build/history .. _Azure Pipelines: https://dev.azure.com/scikit-build/scikit-build/_build .. _CircleCI: https://circleci.com/gh/scikit-build/scikit-build .. _Travis CI: https://travis-ci.org/scikit-build/scikit-build/builds .. _PyPI: https://pypi.org/project/scikit-build .. _TestPyPI: https://test.pypi.org/project/scikit-build ----------------------- `Conda`_: Step-by-step ----------------------- .. warning:: Publishing on conda requires to have corresponding the corresponding Github release. After a GitHub release is created in the `scikit-build `_ project and after the conda-forge `Autoticking Bot `_ creates a pull request on the `scikit-build-feedstock`_ , follow these steps to finalize the conda package release: 1. Review and update scikit-build-feedstock pull request to include Python 3.5 support (see `here `_ for an example) 2. Merge pull-request In case the bot failed (e.g because of GH rate limitation) and in order to explicitly release a new version on conda-forge, follow the steps below: 1. Choose the next release version number (that matches with the PyPI version last published) .. code:: $ release=X.Y.Z 2. Fork scikit-build-feedstock First step is to fork `scikit-build-feedstock`_ repository. This is the recommended `best practice `_ by conda. 3. Clone forked feedstock Fill the YOURGITHUBUSER part. .. code:: $ cd /tmp && git clone https://github.com/YOURGITHUBUSER/scikit-build-feedstock.git 4. Download corresponding source for the release version .. code:: $ cd /tmp && \ wget https://github.com/scikit-build/scikit-build/archive/$release.tar.gz 5. Create a new branch .. code:: $ cd scikit-build-feedstock && \ git checkout -b $release 6. Modify ``meta.yaml`` Update the `version string `_ and `sha256 `_. We have to modify the sha and the version string in the ``meta.yaml`` file. For linux flavors: .. code:: $ sed -i "2s/.*/{% set version = \"$release\" %}/" recipe/meta.yaml $ sha=$(openssl sha256 /tmp/scikit-build-$release.tar.gz | awk '{print $2}') $ sed -i "3s/.*/{$ set sha256 = \"$sha\" %}/" recipe/meta.yaml For macOS: .. code:: $ sed -i -- "2s/.*/{% set version = \"$release\" %}/" recipe/meta.yaml $ sha=$(openssl sha256 /tmp/scikit-build-$release.tar.gz | awk '{print $2}') $ sed -i -- "3s/.*/{$ set sha256 = \"$sha\" %}/" recipe/meta.yaml 7. Push the changes .. code:: $ git push origin $release 8. Create a Pull Request Create a pull request against the `main repository `_. If the tests are passed a new release will be published on Anaconda cloud. .. _Conda: https://anaconda.org/conda-forge/scikit-build .. _scikit-build-feedstock: https://github.com/conda-forge/scikit-build-feedstock scikit-build-0.11.1/docs/modules.rst000066400000000000000000000000721365474353700173270ustar00rootroot00000000000000skbuild ======= .. toctree:: :maxdepth: 4 skbuild scikit-build-0.11.1/docs/notes.rst000066400000000000000000000105101365474353700170050ustar00rootroot00000000000000===== Notes ===== `sysconfig` vs `distutils.sysconfig` ------------------------------------ After installing CPython, two sysconfig modules are available: * `sysconfig` * `distutils.sysconfig` A difference is the value associated with the ``EXT_SUFFIX`` and ``SO`` configuration variables. .. table:: +----------------+-------------------------+----------------------------------+------------------------+---------------------+ | | | Linux | macOS | Windows | +----------------+-------------------------+----------------------------------+------------------------+---------------------+ | | | CPython 2.7 | +================+=========================+==================================+========================+=====================+ | **SO** | **sysconfig** | .so | .so | .pyd | | +-------------------------+ | | | | | **distutils.sysconfig** | | | | +----------------+-------------------------+----------------------------------+------------------------+---------------------+ | **EXT_SUFFIX** | **sysconfig** | None | None | None | | +-------------------------+ | | | | | **distutils.sysconfig** | | | | +----------------+-------------------------+----------------------------------+------------------------+---------------------+ | | | **CPython >= 3.5** | +----------------+-------------------------+----------------------------------+------------------------+---------------------+ | **SO** | **sysconfig** | .cpython-37m-x86_64-linux-gnu.so | .cpython-37m-darwin.so | .pyd | | +-------------------------+ | +---------------------+ | | **distutils.sysconfig** | | | .cp37-win_amd64.pyd | +----------------+-------------------------+ | +---------------------+ | **EXT_SUFFIX** | **sysconfig** | | | .pyd | | +-------------------------+ | +---------------------+ | | **distutils.sysconfig** | | | .cp37-win_amd64.pyd | +----------------+-------------------------+----------------------------------+------------------------+---------------------+ .. note:: The ``EXT_SUFFIX`` was introduced in Python 3.4 and is functionally equivalent to ``SO`` configuration variable. The ``SO`` configuration variable has been deprecated since Python 3.4. .. note:: The information reported in the table have been collected execution the following python snippet. .. code-block:: python def display_ext_suffix_config_var(): import platform import sys import sysconfig from distutils import sysconfig as du_sysconfig details = (platform.python_implementation(),) + sys.version_info[:3] print("%s %s.%s.%s" % details) print(" SO") print(" sysconfig : %s" % sysconfig.get_config_var('SO')) print(" distutils.sysconfig : %s" % du_sysconfig.get_config_var('SO')) print(" EXT_SUFFIX") print(" sysconfig : %s" % sysconfig.get_config_var('EXT_SUFFIX')) print(" distutils.sysconfig : %s" % du_sysconfig.get_config_var('EXT_SUFFIX')) display_ext_suffix_config_var() scikit-build-0.11.1/docs/skbuild.command.rst000066400000000000000000000037371365474353700207440ustar00rootroot00000000000000skbuild.command package ======================= .. automodule:: skbuild.command :members: :undoc-members: :show-inheritance: Submodules ---------- skbuild.command.bdist module ---------------------------- .. automodule:: skbuild.command.bdist :members: :undoc-members: :show-inheritance: skbuild.command.bdist_wheel module ---------------------------------- .. automodule:: skbuild.command.bdist_wheel :members: :undoc-members: :show-inheritance: skbuild.command.build module ---------------------------- .. automodule:: skbuild.command.build :members: :undoc-members: :show-inheritance: skbuild.command.build_py module ------------------------------- .. automodule:: skbuild.command.build_py :members: :undoc-members: :show-inheritance: skbuild.command.clean module ---------------------------- .. automodule:: skbuild.command.clean :members: :undoc-members: :show-inheritance: skbuild.command.egg_info module ------------------------------- .. automodule:: skbuild.command.egg_info :members: :undoc-members: :show-inheritance: skbuild.command.generate_source_manifest module ----------------------------------------------- .. automodule:: skbuild.command.generate_source_manifest :members: :undoc-members: :show-inheritance: skbuild.command.install module ------------------------------ .. automodule:: skbuild.command.install :members: :undoc-members: :show-inheritance: skbuild.command.install_lib module ---------------------------------- .. automodule:: skbuild.command.install_lib :members: :undoc-members: :show-inheritance: skbuild.command.install_scripts module -------------------------------------- .. automodule:: skbuild.command.install_scripts :members: :undoc-members: :show-inheritance: skbuild.command.sdist module ---------------------------- .. automodule:: skbuild.command.sdist :members: :undoc-members: :show-inheritance: scikit-build-0.11.1/docs/skbuild.platform_specifics.rst000066400000000000000000000030111365474353700231630ustar00rootroot00000000000000skbuild.platform_specifics package ================================== .. automodule:: skbuild.platform_specifics :members: :undoc-members: :show-inheritance: Submodules ---------- skbuild.platform_specifics.abstract module ------------------------------------------ .. automodule:: skbuild.platform_specifics.abstract :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.bsd module ------------------------------------- .. automodule:: skbuild.platform_specifics.bsd :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.linux module --------------------------------------- .. automodule:: skbuild.platform_specifics.linux :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.osx module ------------------------------------- .. automodule:: skbuild.platform_specifics.osx :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.platform_factory module -------------------------------------------------- .. automodule:: skbuild.platform_specifics.platform_factory :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.unix module -------------------------------------- .. automodule:: skbuild.platform_specifics.unix :members: :undoc-members: :show-inheritance: skbuild.platform_specifics.windows module ----------------------------------------- .. automodule:: skbuild.platform_specifics.windows :members: :undoc-members: :show-inheritance: scikit-build-0.11.1/docs/skbuild.rst000066400000000000000000000017101365474353700173140ustar00rootroot00000000000000skbuild package =============== .. automodule:: skbuild :members: :undoc-members: :show-inheritance: Subpackages ----------- .. toctree:: skbuild.command skbuild.platform_specifics skbuild.utils Submodules ---------- skbuild.cmaker module --------------------- .. automodule:: skbuild.cmaker :members: :undoc-members: :show-inheritance: skbuild.compat module --------------------- .. automodule:: skbuild.compat :members: :undoc-members: :show-inheritance: skbuild.constants module ------------------------ .. automodule:: skbuild.constants :members: :undoc-members: :show-inheritance: skbuild.exceptions module ------------------------- .. automodule:: skbuild.exceptions :members: :undoc-members: :show-inheritance: skbuild.setuptools\_wrap module ------------------------------- .. automodule:: skbuild.setuptools_wrap :members: :undoc-members: :show-inheritance: scikit-build-0.11.1/docs/skbuild.utils.rst000066400000000000000000000002051365474353700204510ustar00rootroot00000000000000skbuild.utils package ===================== .. automodule:: skbuild.utils :members: :undoc-members: :show-inheritance: scikit-build-0.11.1/docs/usage.rst000066400000000000000000000433051365474353700167710ustar00rootroot00000000000000 .. _why: =============================== Why should I use scikit-build ? =============================== Scikit-build is a replacement for `distutils.core.Extension `_ with the following advantages: - provide better support for :doc:`additional compilers and build systems ` - first-class :ref:`cross-compilation ` support - location of dependencies and their associated build requirements =========== Basic Usage =========== .. _basic_usage_example: Example of setup.py, CMakeLists.txt and pyproject.toml ------------------------------------------------------ To use scikit-build in a project, place the following in your project's `setup.py` file:: from skbuild import setup # This line replaces 'from setuptools import setup' Your project now uses scikit-build instead of setuptools. Next, add a ``CMakeLists.txt`` to describe how to build your extension. In the following example, a C++ extension named ``_hello`` is built:: cmake_minimum_required(VERSION 3.11.0) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello MODULE hello/_hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) Then, add a ``pyproject.toml`` to list the build system requirements:: [build-system] requires = ["setuptools", "wheel", "scikit-build", "cmake", "ninja"] .. note:: By default, scikit-build looks in the project top-level directory for a file named ``CMakeLists.txt``. It will then invoke ``cmake`` executable specifying a :doc:`generator ` matching the python being used. .. _usage-setup_options: Setup options ------------- setuptools options ^^^^^^^^^^^^^^^^^^ The section below documents some of the options accepted by the ``setup()`` function. - ``packages``: Explicitly list of all packages to include in the distribution. Setuptools will not recursively scan the source tree looking for any directory with an ``__init__.py`` file. To automatically generate the list of packages, see `Using find_package()`_. - ``package_dir``: A mapping of package to directory names - ``include_package_data``: If set to ``True``, this tells setuptools to automatically include any data files it finds inside your package directories that are specified by your ``MANIFEST.in`` file. For more information, see the setuptools documentation section on `Including Data Files`_. - ``package_data``: A dictionary mapping package names to lists of glob patterns. For a complete description and examples, see the setuptools documentation section on `Including Data Files`_. You do not need to use this option if you are using include_package_data, unless you need to add e.g. files that are generated by your setup script and build process. (And are therefore not in source control or are files that you don’t want to include in your source distribution.) - ``exclude_package_data``: Dictionary mapping package names to lists of glob patterns that should be excluded from the package directories. You can use this to trim back any excess files included by include_package_data. For a complete description and examples, see the setuptools documentation section on `Including Data Files`_. - ``py_modules``: List all modules rather than listing packages. More details in the `Listing individual modules`_ section of the distutils documentation. - ``data_files``: Sequence of `(directory, files)` pairs. Each `(directory, files)` pair in the sequence specifies the installation directory and the files to install there. More details in the `Installing Additional Files`_ section of the setuptools documentation. - ``entry_points``: A dictionary mapping entry point group names to strings or lists of strings defining the entry points. Entry points are used to support dynamic discovery of services or plugins provided by a project. See `Dynamic Discovery of Services and Plugins`_ for details and examples of the format of this argument. In addition, this keyword is used to support `Automatic Script Creation`_. - ``scripts``: List of python script relative paths. If the first line of the script starts with ``#!`` and contains the word `python`, the Distutils will adjust the first line to refer to the current interpreter location. More details in the `Installing Scripts `_ section of the distutils documentation. .. versionadded:: 0.8.0 - ``zip_safe``: A boolean indicating if the Python packages may be run directly from a zip file. If not already set, scikit-build sets this option to ``False``. See `Setting the zip_safe flag`_ section of the setuptools documentation. .. note:: As specified in the `Wheel documentation`_, the ``universal`` and ``python-tag`` options have no effect. .. _Using find_package(): https://setuptools.readthedocs.io/en/latest/setuptools.html#using-find-packages .. _Including Data Files: https://setuptools.readthedocs.io/en/latest/setuptools.html#including-data-files .. _Installing Additional Files: https://docs.python.org/3/distutils/setupscript.html#installing-additional-files .. _Listing individual modules: https://docs.python.org/3/distutils/setupscript.html#listing-individual-modules .. _Dynamic Discovery of Services and Plugins: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins .. _Automatic Script Creation: https://setuptools.readthedocs.io/en/latest/setuptools.html#automatic-script-creation .. _Setting the zip_safe flag: https://setuptools.readthedocs.io/en/latest/setuptools.html#setting-the-zip-safe-flag .. _Wheel documentation: https://wheel.readthedocs.io/en/stable/ scikit-build options ^^^^^^^^^^^^^^^^^^^^ Scikit-build augments the ``setup()`` function with the following options: - ``cmake_args``: List of `CMake options `_. For example:: setup( [...] cmake_args=['-DSOME_FEATURE:BOOL=OFF'] [...] ) - ``cmake_install_dir``: relative directory where the CMake artifacts are installed. By default, it is set to an empty string. - ``cmake_source_dir``: Relative directory containing the project ``CMakeLists.txt``. By default, it is set to the top-level directory where ``setup.py`` is found. - ``cmake_manifest_process_hook`: Python function consumming the list of files to be installed produced by cmake. For example, `cmake_manifest_process_hook` can be used to exclude static libraries from the built wheel. For example:: def exclude_static_libraries(cmake_manifest): return list(filter(lambda name: not (name.endswith('.a')), cmake_manifest)) setup( [...] cmake_process_manifest_hook=exclude_static_libraries [...] ) .. _usage-cmake_with_sdist: .. versionadded:: 0.5.0 - ``cmake_with_sdist``: Boolean indicating if CMake should be executed when running `sdist` command. Setting this option to ``True`` is useful when part of the sources specified in ``MANIFEST.in`` are downloaded by CMake. By default, this option is ``False``. .. _usage-cmake_languages: .. versionadded:: 0.7.0 - ``cmake_languages``: Tuple of languages that the project use, by default `('C', 'CXX',)`. This option ensures that a generator is chosen that supports all languages for the project. - ``cmake_minimum_required_version``: String identifying the minimum version of CMake required to configure the project. Scikit-build changes the following options: .. versionadded:: 0.7.0 - ``setup_requires``: If ``cmake`` is found in the list, it is explicitly installed first by scikit-build. Command line options -------------------- :: usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...] [skbuild_opts] [cmake_configure_opts] [-- [cmake_opts] [-- [build_tool_opts]]] or: setup.py --help [cmd1 cmd2 ...] or: setup.py --help-commands or: setup.py cmd --help There are few types of options: - :ref:`setuptools options `: - ``[global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]`` - ``--help [cmd1 cmd2 ...]`` - ``cmd --help`` - :ref:`scikit-build options `: ``[skbuild_opts]`` - :ref:`CMake configure options `: ``[cmake_configure_opts]`` - :ref:`CMake options `: ``[cmake_opts]`` - :ref:`build tool options`:``[build_tool_opts]`` setuptools, scikit-build and CMake configure options can be passed normally, the cmake and build_tool set of options needs to be separated by ``--``:: Arguments following a "--" are passed directly to CMake (e.g. -DSOME_FEATURE:BOOL=ON). Arguments following a second "--" are passed directly to the build tool. .. _usage-setuptools_options: setuptools options ^^^^^^^^^^^^^^^^^^ For more details, see the `official documentation `_. scikit-build extends the global set of setuptools options with: .. versionadded:: 0.4.0 :: Global options: [...] --hide-listing do not display list of files being included in the distribution .. versionadded:: 0.5.0 :: Global options: [...] --force-cmake always run CMake --skip-cmake do not run CMake .. note:: As specified in the `Wheel documentation`_, the ``--universal`` and ``--python-tag`` options have no effect. .. _usage_scikit-build_options: scikit-build options ^^^^^^^^^^^^^^^^^^^^ :: scikit-build options: --build-type specify the CMake build type (e.g. Debug or Release) -G , --generator specify the CMake build system generator -j N allow N build jobs at once [...] .. versionadded:: 0.7.0 :: scikit-build options: [...] --cmake-executable specify the path to the cmake executable .. versionadded:: 0.8.0 :: scikit-build options: [...] --skip-generator-test skip generator test when a generator is explicitly selected using --generator .. _usage_cmake_configure_options: CMake Configure options ^^^^^^^^^^^^^^^^^^^^^^^ .. versionadded:: 0.10.1 These options are relevant when configuring a project and can be passed as global options using `setup.py` or `pip install`. The CMake options accepted as global options are any of the following: :: -C = Pre-load a script to populate the cache. -D[:]= = Create or update a cmake cache entry. .. warning:: The CMake configure option should be passed without spaces. For example, use `-DSOME_FEATURE:BOOL=ON` instead of `-D SOME_FEATURE:BOOL=ON`. .. _usage_cmake_options: CMake options ^^^^^^^^^^^^^ These are any specific to CMake. See list of `CMake options `_. For example:: -DSOME_FEATURE:BOOL=OFF .. _usage_build_tool_options: build tool options ^^^^^^^^^^^^^^^^^^ These are specific to the underlying build tool (e.g msbuild.exe, make, ninja). ============== Advanced Usage ============== How to test if scikit-build is driving the compilation ? -------------------------------------------------------- To support the case of code base being built as both a standalone project and a python wheel, it is possible to test for the variable ``SKBUILD``: .. code-block:: cmake if(SKBUILD) message(STATUS "The project is built using scikit-build") endif() Adding cmake as building requirement only if not installed or too low a version ------------------------------------------------------------------------------- If systematically installing cmake wheel is not desired, the ``setup_requires`` list can be set using the following approach:: from packaging.version import LegacyVersion from skbuild.exceptions import SKBuildError from skbuild.cmaker import get_cmake_version # Add CMake as a build requirement if cmake is not installed or is too low a version setup_requires = [] try: if LegacyVersion(get_cmake_version()) < LegacyVersion("3.4"): setup_requires.append('cmake') except SKBuildError: setup_requires.append('cmake') .. _usage_enabling_parallel_build: Enabling parallel build ----------------------- Ninja ^^^^^ If :ref:`Ninja` generator is used, the associated build tool (called ``ninja``) will automatically parallelize the build based on the number of available CPUs. To limit the number of parallel jobs, the build tool option ``-j N`` can be passed to ``ninja``. For example, to limit the number of parallel jobs to `3`, the following could be done:: python setup.py bdist_wheel -- -- -j3 For complex projects where more granularity is required, it is also possible to limit the number of simultaneous link jobs, or compile jobs, or both. Indeed, starting with CMake 3.11, it is possible to configure the project with these options: * `CMAKE_JOB_POOL_COMPILE `_ * `CMAKE_JOB_POOL_LINK `_ * `CMAKE_JOB_POOLS `_ For example, to have at most `5` compile jobs and `2` link jobs, the following could be done:: python setup.py bdist_wheel -- \ -DCMAKE_JOB_POOL_COMPILE:STRING=compile \ -DCMAKE_JOB_POOL_LINK:STRING=link \ '-DCMAKE_JOB_POOLS:STRING=compile=5;link=2' Unix Makefiles ^^^^^^^^^^^^^^ If :ref:`Unix Makefiles` generator is used, the associated build tool (called ``make``) will **NOT** automatically parallelize the build, the user has to explicitly pass option like ``-j N``. For example, to limit the number of parallel jobs to `3`, the following could be done:: python setup.py bdist_wheel -- -- -j3 Visual Studio IDE ^^^^^^^^^^^^^^^^^ If :ref:`Visual Studio` generator is used, there are two types of parallelism: * target level parallelism * object level parallelism .. warning:: Since finding the right combination of parallelism can be challenging, whenever possible we recommend to use the `Ninja`_ generator. To adjust the object level parallelism, the compiler flag ``/MP[processMax]`` could be specified. To learn more, read `/MP (Build with Multiple Processes) `_. For example:: set CXXFLAGS=/MP4 python setup.py bdist_wheel Starting with Visual Studio 2010, the target level parallelism can be set from command line using ``/maxcpucount:N``. This defines the number of simultaneous ``MSBuild.exe`` processes. To learn more, read `Building Multiple Projects in Parallel with MSBuild `_. For example:: python setup.py bdist_wheel -- -- /maxcpucount:4 .. _support_isolated_build: Support for isolated build -------------------------- .. versionadded:: 0.8.0 As specified in `PEP 518`_, dependencies required at install time can be specified using a ``pyproject.toml`` file. Starting with pip 10.0, pip reads the ``pyproject.toml`` file and installs the associated dependencies in an isolated environment. See the `pip build system interface`_ documentation. An isolated environment will be created when using pip to install packages directly from source or to create an editable installation. scikit-build supports these use cases as well as the case where the isolated environment support is explicitly disabled using the pip option ``--no-build-isolation`` available with the `install`, `download` and `wheel` commands. .. _PEP 518: https://www.python.org/dev/peps/pep-0518/ .. _pip build system interface: https://pip.pypa.io/en/stable/reference/pip/#build-system-interface .. _optimized_incremental_build: Optimized incremental build --------------------------- To optimize the developer workflow, scikit-build reconfigures the CMake project only when needed. It caches the environment associated with the generator as well as the CMake execution properties. The CMake properties are saved in a :func:`CMake spec file ` responsible to store the CMake executable path, the CMake configuration arguments, the CMake version as well as the environment variables ``PYTHONNOUSERSITE`` and ``PYTHONPATH``. If there are no ``CMakeCache.txt`` file or if any of the CMake properties changes, scikit-build will explicitly reconfigure the project calling :meth:`skbuild.cmaker.CMaker.configure`. If a file is added to the CMake build system by updating one of the ``CMakeLists.txt`` file, scikit-build will not explicitly reconfigure the project. Instead, the generated build-system will automatically detect the change and reconfigure the project after :meth:`skbuild.cmaker.CMaker.make` is called. .. _cross_compilation: Cross-compilation ----------------- See `CMake Toolchains `_. Introduction to dockross ^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Using dockcross-manylinux to generate Linux wheels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Using dockcross-mingwpy to generate Windows wheels ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. note:: *To be documented.* See :issue:`227`. Examples for scikit-build developers ------------------------------------ .. note:: *To be documented.* See :issue:`227`. Provide small, self-contained setup function calls for (at least) two use cases: - when a `CMakeLists.txt` file already exists - when a user wants scikit-build to create a `CMakeLists.txt` file based on the user specifying some input files. scikit-build-0.11.1/pytest.ini000066400000000000000000000000771365474353700162330ustar00rootroot00000000000000[pytest] testpaths = tests addopts = -v --cov --cov-report xml scikit-build-0.11.1/requirements-dev.txt000066400000000000000000000003531365474353700202370ustar00rootroot00000000000000codecov>=2.0.5 coverage>=4.2 cython>=0.25.1 flake8>=3.0.4 pathlib2; python_version < '3.0' path.py>=11.5.0 pytest>=4.5.0 pytest-cov>=2.7.1 pytest-mock>=1.10.4 pytest-runner>=5.1 pytest-virtualenv>=1.2.5 requests six>=1.10.0 virtualenv scikit-build-0.11.1/requirements-docs.txt000066400000000000000000000001751365474353700204130ustar00rootroot00000000000000docutils pygments sphinx<=1.8.5; python_version <= '3.5' sphinx>1.8.5; python_version > '3.5' sphinx-issues sphinx_rtd_theme scikit-build-0.11.1/requirements.txt000066400000000000000000000001601365474353700174570ustar00rootroot00000000000000wheel>=0.29.0 setuptools>=28.0.0;python_version>='3' setuptools>=28.0.0,<45;python_version<'3' packaging distro scikit-build-0.11.1/scikit-ci.yml000066400000000000000000000034341365474353700166040ustar00rootroot00000000000000schema_version: "0.5.0" before_install: appveyor: environment: PATH: $;$\\Scripts;$ commands: - python ../addons/appveyor/patch_vs2008.py travis: osx: environment: PATH: $/.pyenv/versions/$/bin:$ commands: - python ../addons/travis/install_pyenv.py azure: Windows_NT: commands: - python: | import os paths = os.environ['PATH'].split(os.pathsep) paths.remove('C:\\Program Files\\Git\\mingw64\\bin') paths.remove('C:\\ProgramData\\Chocolatey\\bin') paths.remove('C:\\Strawberry\\c\\bin') os.environ['PATH'] = os.pathsep.join(paths) import ci.driver ci.driver.Driver.save_env(os.environ) install: commands: - python -c "import os; from pprint import pprint as pp; pp(os.environ['PATH'].split(os.pathsep))" - python -m pip install --disable-pip-version-check --upgrade pip - pip install cmake - pip install -r requirements.txt - pip install -r requirements-dev.txt circle: commands: - sudo apt-get update - sudo apt-get install gfortran before_build: commands: - flake8 build: commands: - python setup.py sdist - python setup.py bdist_wheel test: commands: - python setup.py test after_test: commands: - python: | import os, subprocess if 'CODECOV_TOKEN' in os.environ and os.environ['CODECOV_TOKEN'] != '$(codecov_token)': subprocess.check_call("codecov -X gcov --required --file ./tests/coverage.xml", shell=True) else: print("Skipping codecov: CODECOV_TOKEN env. variable is not set") scikit-build-0.11.1/setup.cfg000066400000000000000000000002671365474353700160240ustar00rootroot00000000000000[wheel] universal = 1 [aliases] test = pytest [versioneer] VCS = git versionfile_source = skbuild/_version.py versionfile_build = skbuild/_version.py style = pep440 tag_prefix = '' scikit-build-0.11.1/setup.py000077500000000000000000000044461365474353700157230ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- import sys import versioneer try: from setuptools import setup except ImportError: from distutils.core import setup with open('README.rst', 'r') as fp: readme = fp.read() with open('HISTORY.rst', 'r') as fp: history = fp.read().replace('.. :changelog:', '') with open('requirements.txt', 'r') as fp: requirements = list(filter(bool, (line.strip() for line in fp))) with open('requirements-dev.txt', 'r') as fp: dev_requirements = list(filter(bool, (line.strip() for line in fp))) # Require pytest-runner only when running tests pytest_runner = (['pytest-runner>=2.9'] if any(arg in sys.argv for arg in ('pytest', 'test')) else []) setup_requires = pytest_runner setup( name='scikit-build', version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), description='Improved build system generator for Python C/C++/Fortran/Cython extensions', long_description_content_type='text/x-rst; charset=UTF-8', long_description=readme + '\n\n' + history, author='The scikit-build team', author_email='scikit-build@googlegroups.com', url='https://github.com/scikit-build/scikit-build', packages=[ 'skbuild', 'skbuild.command', 'skbuild.platform_specifics', 'skbuild.utils' ], package_dir={'skbuild': 'skbuild', 'skbuild.platform_specifics': 'skbuild/platform_specifics', 'skbuild.command': 'skbuild/command'}, package_data={'skbuild': ['resources/cmake/*.cmake']}, include_package_data=True, install_requires=requirements, license="MIT", zip_safe=False, keywords='scikit-build', classifiers=[ 'Development Status :: 2 - Pre-Alpha', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', ], tests_require=dev_requirements, setup_requires=setup_requires ) scikit-build-0.11.1/skbuild/000077500000000000000000000000001365474353700156335ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/__init__.py000066400000000000000000000007121365474353700177440ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ scikit-build is an improved build system generator for CPython C extensions. This module provides the *glue* between the setuptools Python module and CMake. """ from ._version import get_versions from .setuptools_wrap import setup # noqa: F401 __author__ = 'The scikit-build team' __email__ = 'scikit-build@googlegroups.com' __version__ = '0.7.1' __version__ = get_versions()['version'] __all__ = ["setup"] del get_versions scikit-build-0.11.1/skbuild/_version.py000066400000000000000000000440751365474353700200430ustar00rootroot00000000000000 # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build # directories (produced by setup.py build) will contain a much shorter file # that just contains the computed version number. # This file is released into the public domain. Generated by # versioneer-0.18 (https://github.com/warner/python-versioneer) """Git implementation of _version.py.""" import errno import os import re import subprocess import sys def get_keywords(): """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = " (tag: 0.11.1)" git_full = "b46dd85f5169cd3a616ea523b2a2bc0df61f23c7" git_date = "2020-05-07 04:31:27 -0400" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_config(): """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "pep440" cfg.tag_prefix = "" cfg.parentdir_prefix = "None" cfg.versionfile_source = "skbuild/_version.py" cfg.verbose = False return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %s" % dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) print("stdout was %s" % stdout) return None, p.returncode return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %s but none started with prefix %s" % (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%s'" % describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%d" % pieces["distance"] else: # exception #1 rendered = "0.post.dev%d" % pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%s" % pieces["short"] else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%s" % pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%s'" % style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} def get_versions(): """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which # case we can only use expanded keywords. cfg = get_config() verbose = cfg.verbose try: return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass try: root = os.path.realpath(__file__) # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. for i in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to find root of source tree", "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) return render(pieces, cfg.style) except NotThisMethod: pass try: if cfg.parentdir_prefix: return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) except NotThisMethod: pass return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} scikit-build-0.11.1/skbuild/cmaker.py000066400000000000000000000471161365474353700174600ustar00rootroot00000000000000""" This module provides an interface for invoking CMake executable. """ from __future__ import print_function import argparse import distutils.sysconfig as du_sysconfig import glob import io import itertools import os import os.path import platform import re import subprocess import shlex import sys import sysconfig from .constants import (CMAKE_BUILD_DIR, CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR, SETUPTOOLS_INSTALL_DIR) from .platform_specifics import get_platform from .exceptions import SKBuildError RE_FILE_INSTALL = re.compile( r"""[ \t]*file\(INSTALL DESTINATION "([^"]+)".*"([^"]+)"\).*""") def pop_arg(arg, args, default=None): """Pops an argument ``arg`` from an argument list ``args`` and returns the new list and the value of the argument if present and a default otherwise. """ parser = argparse.ArgumentParser(add_help=False) parser.add_argument(arg) namespace, args = parser.parse_known_args(args) namespace = tuple(vars(namespace).items()) if namespace and namespace[0][1] is not None: val = namespace[0][1] else: val = default return args, val def _remove_cwd_prefix(path): cwd = os.getcwd() result = path.replace("/", os.sep) if result.startswith(cwd): result = os.path.relpath(result, cwd) if platform.system() == "Windows": result = result.replace("\\\\", os.sep) result = result.replace("\n", "") return result def has_cmake_cache_arg(cmake_args, arg_name, arg_value=None): """Return True if ``-D:TYPE=`` is found in ``cmake_args``. If ``arg_value`` is None, return True only if ``-D:`` is found in the list.""" for arg in reversed(cmake_args): if arg.startswith("-D%s:" % arg_name): if arg_value is None: return True elif "=" in arg: return arg.split("=")[1] == arg_value return False def get_cmake_version(cmake_executable=CMAKE_DEFAULT_EXECUTABLE): """Runs CMake and extracts associated version information. Raises :class:`skbuild.exceptions.SKBuildError` if it failed to execute CMake. """ try: version_string = subprocess.check_output([cmake_executable, '--version']) except (OSError, subprocess.CalledProcessError): raise SKBuildError( "Problem with the CMake installation, aborting build. CMake executable is %s" % cmake_executable) if sys.version_info > (3, 0): version_string = version_string.decode() return version_string.splitlines()[0].split(' ')[-1] class CMaker(object): """Interface to CMake executable.""" def __init__(self, cmake_executable=CMAKE_DEFAULT_EXECUTABLE): self.cmake_executable = cmake_executable self.cmake_version = get_cmake_version(self.cmake_executable) self.platform = get_platform() def get_cached_generator_name(self): """Reads and returns the cached generator from the :func:`skbuild.constants.CMAKE_BUILD_DIR()`:. Returns None if not found. """ try: cmake_generator = 'CMAKE_GENERATOR:INTERNAL=' with open(os.path.join(CMAKE_BUILD_DIR(), 'CMakeCache.txt')) as fp: for line in fp: if line.startswith(cmake_generator): return line[len(cmake_generator):].strip() except (OSError, IOError): pass return None def get_cached_generator_env(self): """If any, return a mapping of environment associated with the cached generator. """ generator_name = self.get_cached_generator_name() if generator_name is not None: return self.platform.get_generator(generator_name).env return None def configure(self, clargs=(), generator_name=None, skip_generator_test=False, cmake_source_dir='.', cmake_install_dir='', languages=('C', 'CXX'), cleanup=True): """Calls cmake to generate the Makefile/VS Solution/XCode project. clargs: tuple List of command line arguments to pass to cmake executable. generator_name: string The string representing the CMake generator to use. If None, uses defaults for your platform. skip_generator_test: bool If set to True and if a generator name is specified (either as a keyword argument or as `clargs` using `-G `), the generator test is skipped. cmake_source_dir: string Path to source tree containing a ``CMakeLists.txt`` cmake_install_dir: string Relative directory to append to :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. languages: tuple List of languages required to configure the project and expected to be supported by the compiler. The language identifier that can be specified in the list corresponds to the one recognized by CMake. cleanup: bool If True, cleans up temporary folder used to test generators. Set to False for debugging to see CMake's output files. Return a mapping of the environment associated with the selected :class:`skbuild.platform_specifics.abstract.CMakeGenerator`. Mapping of the environment can also be later retrieved using :meth:`.get_cached_generator_env`. """ # if no provided default generator_name, check environment if generator_name is None: generator_name = os.environ.get("CMAKE_GENERATOR") # if generator_name is provided on command line, use it clargs, cli_generator_name = pop_arg('-G', clargs) if cli_generator_name is not None: generator_name = cli_generator_name generator = self.platform.get_best_generator( generator_name, skip_generator_test=skip_generator_test, cmake_executable=self.cmake_executable, cmake_args=clargs, languages=languages, cleanup=cleanup) if not os.path.exists(CMAKE_BUILD_DIR()): os.makedirs(CMAKE_BUILD_DIR()) if not os.path.exists(CMAKE_INSTALL_DIR()): os.makedirs(CMAKE_INSTALL_DIR()) if not os.path.exists(SETUPTOOLS_INSTALL_DIR()): os.makedirs(SETUPTOOLS_INSTALL_DIR()) python_version = CMaker.get_python_version() python_include_dir = CMaker.get_python_include_dir(python_version) python_library = CMaker.get_python_library(python_version) cmake_source_dir = os.path.abspath(cmake_source_dir) cmd = [ self.cmake_executable, cmake_source_dir, '-G', generator.name, ("-DCMAKE_INSTALL_PREFIX:PATH=" + os.path.abspath( os.path.join(CMAKE_INSTALL_DIR(), cmake_install_dir))), ("-DPYTHON_EXECUTABLE:FILEPATH=" + sys.executable), ("-DPYTHON_VERSION_STRING:STRING=" + sys.version.split(' ')[0]), ("-DPYTHON_INCLUDE_DIR:PATH=" + python_include_dir), ("-DPYTHON_LIBRARY:FILEPATH=" + python_library), ("-DSKBUILD:BOOL=" + "TRUE"), ("-DCMAKE_MODULE_PATH:PATH=" + os.path.join(os.path.dirname(__file__), "resources", "cmake")) ] cmd.extend(clargs) cmd.extend( filter(bool, shlex.split(os.environ.get("SKBUILD_CONFIGURE_OPTIONS", ""))) ) # changes dir to cmake_build and calls cmake's configure step # to generate makefile print( "Configuring Project\n" " Working directory:\n" " {}\n" " Command:\n" " {}\n".format( os.path.abspath(CMAKE_BUILD_DIR()), self._formatArgsForDisplay(cmd) )) rtn = subprocess.call(cmd, cwd=CMAKE_BUILD_DIR(), env=generator.env) if rtn != 0: raise SKBuildError( "An error occurred while configuring with CMake.\n" " Command:\n" " {}\n" " Source directory:\n" " {}\n" " Working directory:\n" " {}\n" "Please see CMake's output for more information.".format( self._formatArgsForDisplay(cmd), os.path.abspath(cmake_source_dir), os.path.abspath(CMAKE_BUILD_DIR()))) CMaker.check_for_bad_installs() return generator.env @staticmethod def get_python_version(): """Get version associated with the current python interpreter.""" python_version = sysconfig.get_config_var('VERSION') if not python_version: python_version = sysconfig.get_config_var('py_version_short') if not python_version: python_version = ".".join(map(str, sys.version_info[:2])) return python_version # NOTE(opadron): The try-excepts raise the cyclomatic complexity, but we # need them for this function. @staticmethod # noqa: C901 def get_python_include_dir(python_version): """Get include directory associated with the current python interpreter.""" # determine python include dir python_include_dir = sysconfig.get_config_var('INCLUDEPY') # if Python.h not found (or python_include_dir is None), try to find a # suitable include dir found_python_h = ( python_include_dir is not None and os.path.exists(os.path.join(python_include_dir, 'Python.h')) ) if not found_python_h: # NOTE(opadron): these possible prefixes must be guarded against # AttributeErrors and KeyErrors because they each can throw on # different platforms or even different builds on the same platform. include_py = sysconfig.get_config_var('INCLUDEPY') include_dir = sysconfig.get_config_var('INCLUDEDIR') include = None plat_include = None python_inc = None python_inc2 = None try: include = sysconfig.get_path('include') except (AttributeError, KeyError): pass try: plat_include = sysconfig.get_path('platinclude') except (AttributeError, KeyError): pass try: python_inc = sysconfig.get_python_inc() except AttributeError: pass if include_py is not None: include_py = os.path.dirname(include_py) if include is not None: include = os.path.dirname(include) if plat_include is not None: plat_include = os.path.dirname(plat_include) if python_inc is not None: python_inc2 = os.path.join( python_inc, ".".join(map(str, sys.version_info[:2]))) candidate_prefixes = list(filter(bool, ( include_py, include_dir, include, plat_include, python_inc, python_inc2, ))) candidate_versions = (python_version,) if python_version: candidate_versions += ('',) pymalloc = None try: pymalloc = bool(sysconfig.get_config_var('WITH_PYMALLOC')) except AttributeError: pass if pymalloc: candidate_versions += (python_version + 'm',) candidates = ( os.path.join(prefix, ''.join(('python', ver))) for (prefix, ver) in itertools.product( candidate_prefixes, candidate_versions ) ) for candidate in candidates: if os.path.exists(os.path.join(candidate, 'Python.h')): # we found an include directory python_include_dir = candidate break # TODO(opadron): what happens if we don't find an include directory? # Throw SKBuildError? return python_include_dir @staticmethod def get_python_library(python_version): """Get path to the python library associated with the current python interpreter.""" # determine direct path to libpython python_library = sysconfig.get_config_var('LIBRARY') # if static (or nonexistent), try to find a suitable dynamic libpython if (not python_library or os.path.splitext(python_library)[1][-2:] == '.a'): candidate_lib_prefixes = ['', 'lib'] candidate_implementations = ['python'] if hasattr(sys, "pypy_version_info"): candidate_implementations = ['pypy-c', 'pypy3-c'] candidate_extensions = ['.lib', '.so', '.a'] if sysconfig.get_config_var('WITH_DYLD'): candidate_extensions.insert(0, '.dylib') candidate_versions = [python_version] if python_version: candidate_versions.append('') candidate_versions.insert( 0, "".join(python_version.split(".")[:2])) abiflags = getattr(sys, 'abiflags', '') candidate_abiflags = [abiflags] if abiflags: candidate_abiflags.append('') # Ensure the value injected by virtualenv is # returned on windows. # Because calling `sysconfig.get_config_var('multiarchsubdir')` # returns an empty string on Linux, `du_sysconfig` is only used to # get the value of `LIBDIR`. libdir = du_sysconfig.get_config_var('LIBDIR') if sysconfig.get_config_var('MULTIARCH'): masd = sysconfig.get_config_var('multiarchsubdir') if masd: if masd.startswith(os.sep): masd = masd[len(os.sep):] libdir = os.path.join(libdir, masd) if libdir is None: libdir = os.path.abspath(os.path.join( sysconfig.get_config_var('LIBDEST'), "..", "libs")) candidates = ( os.path.join( libdir, ''.join((pre, impl, ver, abi, ext)) ) for (pre, impl, ext, ver, abi) in itertools.product( candidate_lib_prefixes, candidate_implementations, candidate_extensions, candidate_versions, candidate_abiflags ) ) for candidate in candidates: if os.path.exists(candidate): # we found a (likely alternate) libpython python_library = candidate break # TODO(opadron): what happens if we don't find a libpython? return python_library @staticmethod def check_for_bad_installs(): """This function tries to catch files that are meant to be installed outside the project root before they are actually installed. Indeed, we can not wait for the manifest, so we try to extract the information (install destination) from the CMake build files ``*.cmake`` found in :func:`skbuild.constants.CMAKE_BUILD_DIR()`. It raises :class:`skbuild.exceptions.SKBuildError` if it found install destination outside of :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. """ bad_installs = [] install_dir = os.path.join(os.getcwd(), CMAKE_INSTALL_DIR()) for root, _, file_list in os.walk(CMAKE_BUILD_DIR()): for filename in file_list: if os.path.splitext(filename)[1] != ".cmake": continue with io.open(os.path.join(root, filename), encoding="utf-8") as fp: lines = fp.readlines() for line in lines: match = RE_FILE_INSTALL.match(line) if match is None: continue destination = os.path.normpath( match.group(1).replace("${CMAKE_INSTALL_PREFIX}", install_dir)) if not destination.startswith(install_dir): bad_installs.append( os.path.join( destination, os.path.basename(match.group(2)) ) ) if bad_installs: raise SKBuildError("\n".join(( " CMake-installed files must be within the project root.", " Project Root:", " " + install_dir, " Violating Files:", "\n".join( (" " + _install) for _install in bad_installs) ))) def make(self, clargs=(), config="Release", source_dir=".", env=None): """Calls the system-specific make program to compile code. """ clargs, config = pop_arg('--config', clargs, config) if not os.path.exists(CMAKE_BUILD_DIR()): raise SKBuildError(("CMake build folder ({}) does not exist. " "Did you forget to run configure before " "make?").format(CMAKE_BUILD_DIR())) cmd = [self.cmake_executable, "--build", source_dir, "--target", "install", "--config", config, "--"] cmd.extend(clargs) cmd.extend( filter(bool, shlex.split(os.environ.get("SKBUILD_BUILD_OPTIONS", ""))) ) rtn = subprocess.call(cmd, cwd=CMAKE_BUILD_DIR(), env=env) if rtn != 0: raise SKBuildError( "An error occurred while building with CMake.\n" " Command:\n" " {}\n" " Source directory:\n" " {}\n" " Working directory:\n" " {}\n" "Please see CMake's output for more information.".format( self._formatArgsForDisplay(cmd), os.path.abspath(source_dir), os.path.abspath(CMAKE_BUILD_DIR()))) def install(self): """Returns a list of file paths to install via setuptools that is compatible with the data_files keyword argument. """ return self._parse_manifests() def _parse_manifests(self): paths = \ glob.glob(os.path.join(CMAKE_BUILD_DIR(), "install_manifest*.txt")) try: return [self._parse_manifest(path) for path in paths][0] except IndexError: return [] @staticmethod def _parse_manifest(install_manifest_path): with open(install_manifest_path, "r") as manifest: return [_remove_cwd_prefix(path) for path in manifest] return [] @staticmethod def _formatArgsForDisplay(args): """Format a list of arguments appropriately for display. When formatting a command and its arguments, the user should be able to execute the command by copying and pasting the output directly into a shell. Currently, the only formatting is naively surrounding each argument with quotation marks. """ try: from shlex import quote except ImportError: from pipes import quote return ' '.join(quote(arg) for arg in args) scikit-build-0.11.1/skbuild/command/000077500000000000000000000000001365474353700172515ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/command/__init__.py000066400000000000000000000012021365474353700213550ustar00rootroot00000000000000"""Collection of objects allowing to customize behavior of standard distutils and setuptools commands. """ from .. import cmaker class set_build_base_mixin(object): """Mixin allowing to override distutils and setuptools commands. """ def finalize_options(self, *args, **kwargs): """Override built-in function and set a new `build_base`. """ try: if not self.build_base or self.build_base == 'build': self.build_base = cmaker.SETUPTOOLS_INSTALL_DIR() except AttributeError: pass super(set_build_base_mixin, self).finalize_options(*args, **kwargs) scikit-build-0.11.1/skbuild/command/bdist.py000066400000000000000000000006401365474353700207300ustar00rootroot00000000000000"""This module defines custom implementation of ``bdist`` setuptools command.""" try: from setuptools.command.bdist import bdist as _bdist except ImportError: from distutils.command.bdist import bdist as _bdist from . import set_build_base_mixin from ..utils import new_style class bdist(set_build_base_mixin, new_style(_bdist)): """Custom implementation of ``bdist`` setuptools command.""" pass scikit-build-0.11.1/skbuild/command/bdist_wheel.py000066400000000000000000000063501365474353700221200ustar00rootroot00000000000000"""This module defines custom implementation of ``bdist_wheel`` setuptools command.""" import os import sys try: from wheel.wheelfile import WheelFile _USE_WHEELFILE = True except ImportError: from wheel import archive as _wheel_archive # Not available with wheel >= 0.32.0 _USE_WHEELFILE = False from wheel.bdist_wheel import bdist_wheel as _bdist_wheel from . import set_build_base_mixin from ..utils import distribution_hide_listing, new_style class bdist_wheel(set_build_base_mixin, new_style(_bdist_wheel)): """Custom implementation of ``bdist_wheel`` setuptools command.""" def run(self, *args, **kwargs): """Handle --hide-listing option.""" if _USE_WHEELFILE: old_write_files = WheelFile.write_files def update_write_files(wheelfile_self, base_dir): with distribution_hide_listing(self.distribution) as hide_listing: if hide_listing: zip_filename = wheelfile_self.filename print("creating '%s' and adding '%s' to it" % (zip_filename, base_dir)) old_write_files(wheelfile_self, base_dir) WheelFile.distribution = self.distribution WheelFile.write_files = update_write_files try: super(bdist_wheel, self).run(*args, **kwargs) finally: WheelFile.write_files = old_write_files del WheelFile.distribution else: old_make_wheelfile_inner = _wheel_archive.make_wheelfile_inner def _make_wheelfile_inner(base_name, base_dir='.'): with distribution_hide_listing(self.distribution) as hide_listing: if hide_listing: zip_filename = base_name + ".whl" print("creating '%s' and adding '%s' to it" % (zip_filename, base_dir)) old_make_wheelfile_inner(base_name, base_dir) _wheel_archive.make_wheelfile_inner = _make_wheelfile_inner try: super(bdist_wheel, self).run(*args, **kwargs) finally: _wheel_archive.make_wheelfile_inner = old_make_wheelfile_inner def finalize_options(self, *args, **kwargs): """Ensure MacOSX wheels include the expected platform information.""" # pylint:disable=attribute-defined-outside-init,access-member-before-definition if sys.platform == 'darwin' and self.plat_name is None and self.distribution.has_ext_modules(): # The default value is duplicated in setuptools_wrap # pylint:disable=access-member-before-definition self.plat_name = os.environ.get('_PYTHON_HOST_PLATFORM', 'macosx-10.6-x86_64') super(bdist_wheel, self).finalize_options(*args, **kwargs) def write_wheelfile(self, wheelfile_base, _=None): """Write ``skbuild `` as a wheel generator. See `PEP-0427 `_ for more details. """ from .. import __version__ as skbuild_version generator = "skbuild %s" % skbuild_version super(bdist_wheel, self).write_wheelfile(wheelfile_base, generator) scikit-build-0.11.1/skbuild/command/build.py000066400000000000000000000006401365474353700207220ustar00rootroot00000000000000"""This module defines custom implementation of ``build`` setuptools command.""" try: from setuptools.command.build import build as _build except ImportError: from distutils.command.build import build as _build from . import set_build_base_mixin from ..utils import new_style class build(set_build_base_mixin, new_style(_build)): """Custom implementation of ``build`` setuptools command.""" pass scikit-build-0.11.1/skbuild/command/build_ext.py000066400000000000000000000036121365474353700216040ustar00rootroot00000000000000"""This module defines custom implementation of ``build_ext`` setuptools command.""" import os from distutils.file_util import copy_file try: from setuptools.command.build_ext import build_ext as _build_ext except ImportError: from distutils.command.build_ext import build_ext as _build_ext from . import set_build_base_mixin from ..constants import CMAKE_INSTALL_DIR from ..utils import new_style class build_ext(set_build_base_mixin, new_style(_build_ext)): """Custom implementation of ``build_ext`` setuptools command.""" def copy_extensions_to_source(self): """This function is only-called when doing inplace build. It is customized to ensure the extensions compiled using distutils are copied back to the source tree instead of the :func:`skbuild.constants.CMAKE_INSTALL_DIR()`. """ build_py = self.get_finalized_command('build_py') for ext in self.extensions: fullname = self.get_ext_fullname(ext.name) filename = self.get_ext_filename(fullname) modpath = fullname.split('.') package = '.'.join(modpath[:-1]) package_dir = build_py.get_package_dir(package) # skbuild: strip install dir for inplace build package_dir = package_dir[len(CMAKE_INSTALL_DIR()) + 1:] dest_filename = os.path.join(package_dir, os.path.basename(filename)) src_filename = os.path.join(self.build_lib, filename) # Always copy, even if source is older than destination, to ensure # that the right extensions for the current Python/platform are # used. copy_file( src_filename, dest_filename, verbose=self.verbose, dry_run=self.dry_run ) if ext._needs_stub: self.write_stub(package_dir or os.curdir, ext, True) scikit-build-0.11.1/skbuild/command/build_py.py000066400000000000000000000074261365474353700214430ustar00rootroot00000000000000"""This module defines custom implementation of ``build_py`` setuptools command.""" import os from distutils import log as distutils_log from setuptools.command.build_py import build_py as _build_py from . import set_build_base_mixin from ..constants import CMAKE_INSTALL_DIR from ..utils import distribution_hide_listing, new_style class build_py(set_build_base_mixin, new_style(_build_py)): """Custom implementation of ``build_py`` setuptools command.""" def initialize_options(self): """Handle --hide-listing option. Initializes ``outfiles_count``. """ super(build_py, self).initialize_options() # pylint:disable=attribute-defined-outside-init self.outfiles_count = 0 def build_module(self, module, module_file, package): """Handle --hide-listing option. Increments ``outfiles_count``. """ super(build_py, self).build_module(module, module_file, package) self.outfiles_count += 1 def run(self, *args, **kwargs): """Handle --hide-listing option. Display number of copied files. It corresponds to the value of ``outfiles_count``. """ with distribution_hide_listing(self.distribution): super(build_py, self).run(*args, **kwargs) distutils_log.info("copied %d files" % self.outfiles_count) def find_modules(self): """Finds individually-specified Python modules, ie. those listed by module name in 'self.py_modules'. Returns a list of tuples (package, module_base, filename): 'package' is a tuple of the path through package-space to the module; 'module_base' is the bare (no packages, no dots) module name, and 'filename' is the path to the ".py" file (relative to the distribution root) that implements the module. """ # Map package names to tuples of useful info about the package: # (package_dir, checked) # package_dir - the directory where we'll find source files for # this package # checked - true if we have checked that the package directory # is valid (exists, contains __init__.py, ... ?) packages = {} # List of (package, module, filename) tuples to return modules = [] # We treat modules-in-packages almost the same as toplevel modules, # just the "package" for a toplevel is empty (either an empty # string or empty list, depending on context). Differences: # - don't check for __init__.py in directory for empty package for module in self.py_modules: path = module.split('.') package = '.'.join(path[0:-1]) module_base = path[-1] try: (package_dir, checked) = packages[package] except KeyError: package_dir = self.get_package_dir(package) checked = 0 if not checked: init_py = self.check_package(package, package_dir) packages[package] = (package_dir, 1) if init_py: modules.append((package, "__init__", init_py)) # XXX perhaps we should also check for just .pyc files # (so greedy closed-source bastards can distribute Python # modules too) module_file = os.path.join(package_dir, module_base + ".py") # skbuild: prepend CMAKE_INSTALL_DIR if file exists in the # CMake install tree. if os.path.exists(os.path.join(CMAKE_INSTALL_DIR(), module_file)): module_file = os.path.join(CMAKE_INSTALL_DIR(), module_file) if not self.check_module(module, module_file): continue modules.append((package, module_base, module_file)) return modules scikit-build-0.11.1/skbuild/command/clean.py000066400000000000000000000020571365474353700207110ustar00rootroot00000000000000"""This module defines custom implementation of ``clean`` setuptools command.""" import os try: from setuptools.command.clean import clean as _clean except ImportError: from distutils.command.clean import clean as _clean from distutils import log from shutil import rmtree from . import set_build_base_mixin from ..constants import (CMAKE_BUILD_DIR, CMAKE_INSTALL_DIR, SKBUILD_DIR) from ..utils import new_style class clean(set_build_base_mixin, new_style(_clean)): """Custom implementation of ``clean`` setuptools command.""" def run(self): """After calling the super class implementation, this function removes the directories specific to scikit-build.""" super(clean, self).run() for dir_ in (CMAKE_INSTALL_DIR(), CMAKE_BUILD_DIR(), SKBUILD_DIR()): if os.path.exists(dir_): log.info("removing '%s'", dir_) if not self.dry_run and os.path.exists(dir_): rmtree(dir_) scikit-build-0.11.1/skbuild/command/egg_info.py000066400000000000000000000035761365474353700214130ustar00rootroot00000000000000"""This module defines custom implementation of ``egg_info`` setuptools command.""" import os import os.path from setuptools.command.egg_info import egg_info as _egg_info from . import set_build_base_mixin from ..constants import CMAKE_INSTALL_DIR from ..utils import new_style, to_unix_path class egg_info(set_build_base_mixin, new_style(_egg_info)): """Custom implementation of ``egg_info`` setuptools command.""" def finalize_options(self, *args, **kwargs): # pylint:disable=access-member-before-definition if self.egg_base is None: if self.distribution.package_dir is not None and len(self.distribution.package_dir) == 1: # Recover directory specified in setup() function # using `package_dir={'':}` # This is required to successfully update the python path when # running the test command. package_name = list(self.distribution.package_dir.keys())[0] egg_base = to_unix_path(list(self.distribution.package_dir.values())[0]) cmake_install_dir = to_unix_path(CMAKE_INSTALL_DIR()) if egg_base.startswith(cmake_install_dir): egg_base = egg_base[len(cmake_install_dir) + 1:] if package_name and egg_base.endswith(package_name): egg_base = egg_base[:-len(package_name) - 1] if egg_base == "": egg_base = "." # pylint:disable=attribute-defined-outside-init self.egg_base = egg_base else: script_path = os.path.abspath(self.distribution.script_name) script_dir = os.path.dirname(script_path) # pylint:disable=attribute-defined-outside-init self.egg_base = os.path.join(script_dir, self.egg_base) super(egg_info, self).finalize_options(*args, **kwargs) scikit-build-0.11.1/skbuild/command/generate_source_manifest.py000066400000000000000000000060711365474353700246670ustar00rootroot00000000000000"""This module defines custom ``generate_source_manifest`` setuptools command.""" import os import subprocess import sys from distutils.cmd import Command from . import set_build_base_mixin from ..constants import SKBUILD_DIR, SKBUILD_MARKER_FILE from ..utils import new_style class generate_source_manifest(set_build_base_mixin, new_style(Command)): """Custom setuptools command generating a `MANIFEST` file if not already provided.""" description = "generate source MANIFEST" # pylint:disable=no-self-use def initialize_options(self): """Set default values for all the options that this command supports.""" pass def run(self): """ If neither a `MANIFEST`, nor a `MANIFEST.in` file is provided, and we are in a git repo, try to create a `MANIFEST` file from the output of `git ls-tree --name-only -r HEAD`. We need a reliable way to tell if an existing `MANIFEST` file is one we've generated. distutils already uses a first-line comment to tell if the `MANIFEST` file was generated from `MANIFEST.in`, so we use a dummy file, `_skbuild_MANIFEST`, to avoid confusing distutils. """ do_generate = ( # If there's a MANIFEST.in file, we assume that we had nothing to do # with the project's manifest. not os.path.exists('MANIFEST.in') # otherwise, we check to see that there is no MANIFEST, ... and not os.path.exists('MANIFEST') # ... or ... # ... (if there is one,) that we created it or os.path.exists(SKBUILD_MARKER_FILE()) ) if do_generate: try: with open('MANIFEST', 'wb') as manifest_file: # Since Git < 2.11 does not support --recurse-submodules option, fallback to # regular listing. try: manifest_file.write( subprocess.check_output(['git', 'ls-files', '--recurse-submodules']) ) except subprocess.CalledProcessError: manifest_file.write( subprocess.check_output(['git', 'ls-files']) ) except subprocess.CalledProcessError: sys.stderr.write( '\n\n' 'Since scikit-build could not find MANIFEST.in or ' 'MANIFEST, it tried to generate a MANIFEST file ' 'automatically, but could not because it could not ' 'determine which source files to include.\n\n' 'The command used was "git ls-files"\n' '\n\n' ) raise if not os.path.exists(SKBUILD_DIR()): os.makedirs(SKBUILD_DIR()) with open(SKBUILD_MARKER_FILE(), 'w'): # touch pass def finalize_options(self, *args, **kwargs): """Set final values for all the options that this command supports.""" pass scikit-build-0.11.1/skbuild/command/install.py000066400000000000000000000016761365474353700213030ustar00rootroot00000000000000"""This module defines custom implementation of ``install`` setuptools command.""" from setuptools.command.install import install as _install from . import set_build_base_mixin from ..utils import new_style class install(set_build_base_mixin, new_style(_install)): """Custom implementation of ``install`` setuptools command.""" def finalize_options(self, *args, **kwargs): """Ensure that if the distribution is non-pure, all modules are installed in ``self.install_platlib``. .. note:: `setuptools.dist.Distribution.has_ext_modules()` is overridden in :func:`..setuptools_wrap.setup()`. """ # pylint:disable=access-member-before-definition if self.install_lib is None and self.distribution.has_ext_modules(): # pylint:disable=attribute-defined-outside-init self.install_lib = self.install_platlib super(install, self).finalize_options(*args, **kwargs) scikit-build-0.11.1/skbuild/command/install_lib.py000066400000000000000000000013171365474353700221210ustar00rootroot00000000000000"""This module defines custom implementation of ``install_lib`` setuptools command.""" from distutils import log as distutils_log from setuptools.command.install_lib import install_lib as _install_lib from . import set_build_base_mixin from ..utils import distribution_hide_listing, new_style class install_lib(set_build_base_mixin, new_style(_install_lib)): """Custom implementation of ``install_lib`` setuptools command.""" def install(self): """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): outfiles = super(install_lib, self).install() if outfiles is not None: distutils_log.info("copied %d files" % len(outfiles)) scikit-build-0.11.1/skbuild/command/install_scripts.py000066400000000000000000000013431365474353700230410ustar00rootroot00000000000000"""This module defines custom implementation of ``install_scripts`` setuptools command.""" from distutils import log as distutils_log from setuptools.command.install_scripts import ( install_scripts as _install_scripts) from . import set_build_base_mixin from ..utils import distribution_hide_listing, new_style class install_scripts(set_build_base_mixin, new_style(_install_scripts)): """Custom implementation of ``install_scripts`` setuptools command.""" def run(self, *args, **kwargs): """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): super(install_scripts, self).run(*args, **kwargs) distutils_log.info("copied %d files" % len(self.outfiles)) scikit-build-0.11.1/skbuild/command/sdist.py000066400000000000000000000043351365474353700207560ustar00rootroot00000000000000"""This module defines custom implementation of ``sdist`` setuptools command.""" import contextlib import os from distutils import log as distutils_log from distutils.command.sdist import sdist as _sdist from . import set_build_base_mixin from ..utils import distribution_hide_listing, new_style class sdist(set_build_base_mixin, new_style(_sdist)): """Custom implementation of ``sdist`` setuptools command.""" def make_distribution(self): """This function was originally re-implemented in setuptools to workaround https://github.com/pypa/setuptools/issues/516 and later ported to scikit-build to ensure symlinks are maintained. """ with self._remove_os_link(): super(sdist, self).make_distribution() @staticmethod @contextlib.contextmanager def _remove_os_link(): """In a context, remove and restore ``os.link`` if it exists. """ # copied from setuptools.sdist class NoValue: pass orig_val = getattr(os, 'link', NoValue) try: del os.link except Exception: pass try: yield finally: if orig_val is not NoValue: setattr(os, 'link', orig_val) def make_release_tree(self, base_dir, files): """Handle --hide-listing option.""" with distribution_hide_listing(self.distribution): super(sdist, self).make_release_tree(base_dir, files) distutils_log.info("copied %d files" % len(files)) # pylint:disable=too-many-arguments, unused-argument def make_archive(self, base_name, _format, root_dir=None, base_dir=None, owner=None, group=None): """Handle --hide-listing option.""" distutils_log.info("creating '%s' %s archive and adding '%s' to it", base_name, _format, base_dir) with distribution_hide_listing(self.distribution): super(sdist, self).make_archive( base_name, _format, root_dir, base_dir) def run(self, *args, **kwargs): """Force :class:`.egg_info.egg_info` command to run.""" self.run_command('generate_source_manifest') super(sdist, self).run(*args, **kwargs) scikit-build-0.11.1/skbuild/command/test.py000066400000000000000000000007431365474353700206060ustar00rootroot00000000000000"""This module defines custom implementation of ``test`` setuptools command.""" from setuptools.command.test import test as _test from . import set_build_base_mixin from ..utils import new_style class test(set_build_base_mixin, new_style(_test)): """Custom implementation of ``test`` setuptools command.""" def run(self, *args, **kwargs): """Force ``develop`` command to run.""" self.run_command('develop') super(test, self).run(*args, **kwargs) scikit-build-0.11.1/skbuild/compat.py000066400000000000000000000014401365474353700174670ustar00rootroot00000000000000import os def which(name, flags=os.X_OK): """Analogue of unix 'which'. Borrowed from the Twisted project, see their licence here: https://twistedmatrix.com/trac/browser/trunk/LICENSE Copied from ``pytest_shutil.cmdline.which`` to allow testing on conda-forge where ``pytest-shutil`` is not available. """ result = [] exts = filter(None, os.environ.get('PATHEXT', '').split(os.pathsep)) path = os.environ.get('PATH', None) if path is None: return [] for p in os.environ.get('PATH', '').split(os.pathsep): p = os.path.join(p, name) if os.access(p, flags): result.append(p) for e in exts: pext = p + e if os.access(pext, flags): result.append(pext) return result scikit-build-0.11.1/skbuild/constants.py000066400000000000000000000062031365474353700202220ustar00rootroot00000000000000""" This module defines constants commonly used in scikit-build. """ import os import platform import sys from distutils.util import get_platform CMAKE_DEFAULT_EXECUTABLE = "cmake" """Default path to CMake executable.""" def _default_skbuild_plat_name(): """Get default platform name. On linux and windows, it corresponds to :func:`distutils.util.get_platform()`. On macOS, it corresponds to the version and machine associated with :func:`platform.mac_ver()`. """ if sys.platform == 'darwin': # If the MACOSX_DEPLOYMENT_TARGET environment variable is defined, use # it, as it will be the most accurate. Otherwise use the value returned # by platform.mac_ver() provided by the platform module available in # the Python standard library. # # Note that on macOS, distutils.util.get_platform() is not used because # it returns the macOS version on which Python was built which may be # significantly older than the user's current machine. release, _, machine = platform.mac_ver() release = os.environ.get("MACOSX_DEPLOYMENT_TARGET", release) split_ver = release.split('.') return 'macosx-{}.{}-{}'.format(split_ver[0], split_ver[1], machine) else: return get_platform() _SKBUILD_PLAT_NAME = _default_skbuild_plat_name() def set_skbuild_plat_name(plat_name): """Set platform name associated with scikit-build functions returning a path: * :func:`SKBUILD_DIR()` * :func:`SKBUILD_MARKER_FILE()` * :func:`CMAKE_BUILD_DIR()` * :func:`CMAKE_INSTALL_DIR()` * :func:`CMAKE_SPEC_FILE()` * :func:`SETUPTOOLS_INSTALL_DIR()` """ global _SKBUILD_PLAT_NAME _SKBUILD_PLAT_NAME = plat_name def skbuild_plat_name(): """Get platform name formatted as `[-]-`. Default value corresponds to :func:`_default_skbuild_plat_name()` and can be overridden with :func:`set_skbuild_plat_name()`. Examples of values are `macosx-10.6-x86_64`, `linux-x86_64`, `linux-i686` or `win-am64`. """ return _SKBUILD_PLAT_NAME def SKBUILD_DIR(): """Top-level directory where setuptools and CMake directories are generated.""" return os.path.join( "_skbuild", "{}-{}".format(_SKBUILD_PLAT_NAME, '.'.join(map(str, sys.version_info[:2]))), ) def SKBUILD_MARKER_FILE(): """Marker file used by :func:`skbuild.command.generate_source_manifest.generate_source_manifest.run()`.""" return os.path.join(SKBUILD_DIR(), "_skbuild_MANIFEST") def CMAKE_BUILD_DIR(): """CMake build directory.""" return os.path.join(SKBUILD_DIR(), "cmake-build") def CMAKE_INSTALL_DIR(): """CMake install directory.""" return os.path.join(SKBUILD_DIR(), "cmake-install") def CMAKE_SPEC_FILE(): """CMake specification file storing CMake version, CMake configuration arguments and environment variables ``PYTHONNOUSERSITE`` and ``PYTHONPATH``. """ return os.path.join(CMAKE_BUILD_DIR(), "CMakeSpec.json") def SETUPTOOLS_INSTALL_DIR(): """Setuptools install directory.""" return os.path.join(SKBUILD_DIR(), "setuptools") scikit-build-0.11.1/skbuild/exceptions.py000066400000000000000000000005651365474353700203740ustar00rootroot00000000000000""" This module defines exceptions commonly used in scikit-build. """ class SKBuildError(RuntimeError): """Exception raised when an error occurs while configuring or building a project. """ pass class SKBuildGeneratorNotFoundError(SKBuildError): """Exception raised when no suitable generator is found for the current platform. """ pass scikit-build-0.11.1/skbuild/platform_specifics/000077500000000000000000000000001365474353700215075ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/platform_specifics/README.rst000066400000000000000000000002361365474353700231770ustar00rootroot00000000000000This folder contains files the define CMake's defaults for given platforms. Any of them can be overridden by either command line or by environment variables.scikit-build-0.11.1/skbuild/platform_specifics/__init__.py000066400000000000000000000004571365474353700236260ustar00rootroot00000000000000"""This package provides :func:`get_platform()` allowing to get an instance of :class:`.abstract.CMakePlatform` matching the current platform. """ from .abstract import CMakeGenerator # noqa: F401 from .platform_factory import get_platform # noqa: F401 __all__ = ["CMakeGenerator", "get_platform"] scikit-build-0.11.1/skbuild/platform_specifics/abstract.py000066400000000000000000000222251365474353700236670ustar00rootroot00000000000000"""This module defines objects useful to discover which CMake generator is supported on the current platform.""" from __future__ import print_function import os import shutil import subprocess import textwrap from ..constants import CMAKE_DEFAULT_EXECUTABLE from ..exceptions import SKBuildGeneratorNotFoundError from ..utils import push_dir test_folder = "_cmake_test_compile" class CMakePlatform(object): """This class encapsulates the logic allowing to get the identifier of a working CMake generator. Derived class should at least set :attr:`default_generators`. """ def __init__(self): self._default_generators = list() @property def default_generators(self): """List of generators considered by :func:`get_best_generator()`.""" return self._default_generators @default_generators.setter def default_generators(self, generators): self._default_generators = generators @property def generator_installation_help(self): """Return message guiding the user for installing a valid toolchain.""" raise NotImplementedError # pragma: no cover @staticmethod def write_test_cmakelist(languages): """Write a minimal ``CMakeLists.txt`` useful to check if the requested ``languages`` are supported.""" if not os.path.exists(test_folder): os.makedirs(test_folder) with open("{:s}/{:s}".format(test_folder, "CMakeLists.txt"), "w") as f: f.write("cmake_minimum_required(VERSION 2.8)\n") f.write("PROJECT(compiler_test NONE)\n") for language in languages: f.write("ENABLE_LANGUAGE({:s})\n".format(language)) @staticmethod def cleanup_test(): """Delete test project directory.""" if os.path.exists(test_folder): shutil.rmtree(test_folder) def get_generator(self, generator_name): """Loop over generators and return the first that matches the given name. """ for default_generator in self.default_generators: if default_generator.name == generator_name: return default_generator return CMakeGenerator(generator_name) # TODO: this method name is not great. Does anyone have a better idea for # renaming it? def get_best_generator( self, generator_name=None, skip_generator_test=False, languages=("CXX", "C"), cleanup=True, cmake_executable=CMAKE_DEFAULT_EXECUTABLE, cmake_args=()): """Loop over generators to find one that works by configuring and compiling a test project. :param generator_name: If provided, uses only provided generator, \ instead of trying :attr:`default_generators`. :type generator_name: string or None :param skip_generator_test: If set to True and if a generator name is \ specified, the generator test is skipped. If no generator_name is specified \ and the option is set to True, the first available generator is used. :type skip_generator_test: bool :param languages: The languages you'll need for your project, in terms \ that CMake recognizes. :type languages: tuple :param cleanup: If True, cleans up temporary folder used to test \ generators. Set to False for debugging to see CMake's output files. :type cleanup: bool :param cmake_executable: Path to CMake executable used to configure \ and build the test project used to evaluate if a generator is working. :type cmake_executable: string :param cmake_args: List of CMake arguments to use when configuring \ the test project. Only arguments starting with ``-DCMAKE_`` are \ used. :type cmake_args: tuple :return: CMake Generator object :rtype: :class:`CMakeGenerator` or None :raises skbuild.exceptions.SKBuildGeneratorNotFoundError: """ candidate_generators = [] if generator_name is None: candidate_generators = self.default_generators else: # Lookup CMakeGenerator by name. Doing this allow to get a # generator object with its ``env`` property appropriately # initialized. candidate_generators = [] for default_generator in self.default_generators: if default_generator.name == generator_name: candidate_generators.append(default_generator) if not candidate_generators: candidate_generators = [CMakeGenerator(generator_name)] self.write_test_cmakelist(languages) if skip_generator_test: working_generator = candidate_generators[0] else: working_generator = self.compile_test_cmakelist( cmake_executable, candidate_generators, cmake_args) if working_generator is None: raise SKBuildGeneratorNotFoundError(textwrap.dedent( """ {line} scikit-build could not get a working generator for your system. Aborting build. {installation_help} {line} """).strip().format( # noqa: E501 line="*"*80, installation_help=self.generator_installation_help) ) if cleanup: CMakePlatform.cleanup_test() return working_generator @staticmethod @push_dir(directory=test_folder) def compile_test_cmakelist( cmake_exe_path, candidate_generators, cmake_args=()): """Attempt to configure the test project with each :class:`CMakeGenerator` from ``candidate_generators``. Only cmake arguments starting with ``-DCMAKE_`` are used to configure the test project. The function returns the first generator allowing to successfully configure the test project using ``cmake_exe_path``.""" # working generator is the first generator we find that works. working_generator = None # Include only -DCMAKE_* arguments cmake_args = [arg for arg in cmake_args if arg.startswith("-DCMAKE_")] # Do not complain about unused CMake arguments cmake_args.insert(0, "--no-warn-unused-cli") def _generator_discovery_status_msg(_generator, suffix=""): outer = "-" * 80 inner = ["-" * ((idx * 5) - 3) for idx in range(1, 8)] print(outer if suffix == "" else "\n".join(inner)) print("-- Trying \"%s\" generator%s" % (_generator.description, suffix)) print(outer if suffix != "" else "\n".join(inner[::-1])) for generator in candidate_generators: print("\n") _generator_discovery_status_msg(generator) # clear the cache for each attempted generator type if os.path.isdir('build'): shutil.rmtree('build') with push_dir('build', make_directory=True): # call cmake to see if the compiler specified by this # generator works for the specified languages if generator.toolset: toolset_arg = ['-T', generator.toolset] else: toolset_arg = [] cmd = [cmake_exe_path, '../', '-G', generator.name] cmd.extend(toolset_arg) cmd.extend(cmake_args) status = subprocess.call(cmd, env=generator.env) _generator_discovery_status_msg( generator, " - %s" % ("success" if status == 0 else "failure")) print("") # cmake succeeded, this generator should work if status == 0: # we have a working generator, don't bother looking for more working_generator = generator break return working_generator class CMakeGenerator(object): """Represents a CMake generator. .. automethod:: __init__ """ def __init__(self, name, env=None, toolset=None): """Instantiate a generator object with the given ``name``. By default, ``os.environ`` is associated with the generator. Dictionary passed as ``env`` parameter will be merged with ``os.environ``. If an environment variable is set in both ``os.environ`` and ``env``, the variable in ``env`` is used. Some CMake generators support a ``toolset`` specification to tell the native build system how to choose a compiler. """ self._generator_name = name self.env = dict( list(os.environ.items()) + list(env.items() if env else [])) self._generator_toolset = toolset if toolset is None: self._description = name else: self._description = "%s %s" % (name, toolset) @property def name(self): """Name of CMake generator.""" return self._generator_name @property def toolset(self): """Toolset specification associated with the CMake generator.""" return self._generator_toolset @property def description(self): """Name of CMake generator with properties describing the environment (e.g toolset)""" return self._description scikit-build-0.11.1/skbuild/platform_specifics/bsd.py000066400000000000000000000003431365474353700226310ustar00rootroot00000000000000"""This module defines object specific to BSD platform.""" from . import unix # pylint:disable=abstract-method class BSDPlatform(unix.UnixPlatform): """BSD implementation of :class:`.abstract.CMakePlatform`.""" pass scikit-build-0.11.1/skbuild/platform_specifics/linux.py000066400000000000000000000055201365474353700232220ustar00rootroot00000000000000"""This module defines object specific to Linux platform.""" import distro import platform import sys import textwrap from . import unix class LinuxPlatform(unix.UnixPlatform): """Linux implementation of :class:`.abstract.CMakePlatform`""" @staticmethod def build_essential_install_cmd(): """Return a tuple of the form ``(distribution_name, cmd)``. ``cmd`` is the command allowing to install the build tools in the current Linux distribution. It set to an empty string if the command is not known. ``distribution_name`` is the name of the current distribution. It is set to an empty string if the distribution could not be determined. """ # gentoo, slackware: Compiler is available by default. distribution_name = distro.id() cmd = "" if distribution_name in [ 'debian', 'Ubuntu', 'mandrake', 'mandriva']: cmd = "sudo apt-get install build-essential" elif distribution_name in [ 'centos', 'fedora', 'redhat', 'turbolinux', 'yellowdog', 'rocks']: # http://unix.stackexchange.com/questions/16422/cant-install-build-essential-on-centos#32439 cmd = "sudo yum groupinstall 'Development Tools'" elif distribution_name in ['SuSE']: # http://serverfault.com/questions/437680/equivalent-development-build-tools-for-suse-professional-11#437681 cmd = "zypper install -t pattern devel_C_C++" return distribution_name, cmd @property def generator_installation_help(self): """Return message guiding the user for installing a valid toolchain.""" distribution_name, cmd = self.build_essential_install_cmd() install_help = "" if distribution_name: install_help = ( "But scikit-build does *NOT* know how to install it on %s\n" % distribution_name ) if distribution_name and cmd: install_help = ( "It can be installed using %s package manager:\n" "\n" " %s\n" % (distribution_name, cmd) ) arch = "x64" if platform.architecture()[0] == "64bit" else "x86" return textwrap.dedent( """ Building Linux wheels for Python %s requires a compiler (e.g gcc). %s To build compliant wheels, consider using the manylinux system described in PEP-513. Get it with "dockcross/manylinux-%s" docker image: https://github.com/dockcross/dockcross#readme For more details, please refer to scikit-build documentation: http://scikit-build.readthedocs.io/en/latest/generators.html#linux """ # noqa: E501 ).strip() % ("%s.%s" % sys.version_info[:2], install_help, arch) scikit-build-0.11.1/skbuild/platform_specifics/osx.py000066400000000000000000000011571365474353700226760ustar00rootroot00000000000000"""This module defines object specific to OSX platform.""" import sys import textwrap from . import unix class OSXPlatform(unix.UnixPlatform): """OSX implementation of :class:`.abstract.CMakePlatform`.""" @property def generator_installation_help(self): """Return message guiding the user for installing a valid toolchain.""" return textwrap.dedent( """ Building MacOSX wheels for Python {pyver} requires XCode. Get it here: https://developer.apple.com/xcode/ """ ).format(pyver="%s.%s" % sys.version_info[:2]).strip() scikit-build-0.11.1/skbuild/platform_specifics/platform_factory.py000066400000000000000000000016771365474353700254470ustar00rootroot00000000000000"""This modules implements the logic allowing to instantiate the expected :class:`.abstract.CMakePlatform`.""" import platform def get_platform(): """Return an instance of :class:`.abstract.CMakePlatform` corresponding to the current platform.""" this_platform = platform.system().lower() if this_platform == "windows": from . import windows return windows.WindowsPlatform() elif this_platform == "linux": from . import linux return linux.LinuxPlatform() elif this_platform == "freebsd": from . import bsd return bsd.BSDPlatform() elif this_platform == "darwin": from . import osx return osx.OSXPlatform() elif this_platform == "os400": from . import bsd return bsd.BSDPlatform() else: raise RuntimeError("Unsupported platform: {:s}. Please contact " "the scikit-build team.".format(this_platform)) scikit-build-0.11.1/skbuild/platform_specifics/unix.py000066400000000000000000000007221365474353700230450ustar00rootroot00000000000000"""This module defines object specific to Unix platform.""" from .abstract import CMakeGenerator from . import abstract # pylint:disable=abstract-method class UnixPlatform(abstract.CMakePlatform): """Unix implementation of :class:`.abstract.CMakePlatform`.""" def __init__(self): super(UnixPlatform, self).__init__() self.default_generators = [ CMakeGenerator("Ninja"), CMakeGenerator("Unix Makefiles") ] scikit-build-0.11.1/skbuild/platform_specifics/windows.py000066400000000000000000000306441365474353700235620ustar00rootroot00000000000000"""This module defines object specific to Windows platform.""" from __future__ import print_function import os import platform import re import subprocess import sys import textwrap from itertools import count from .abstract import CMakeGenerator from . import abstract class WindowsPlatform(abstract.CMakePlatform): """Windows implementation of :class:`.abstract.CMakePlatform`.""" def __init__(self): super(WindowsPlatform, self).__init__() version = sys.version_info self._vs_help = "" vs_help_template = textwrap.dedent( """ Building windows wheels for Python {pyver} requires Microsoft Visual Studio %s. Get it with "%s": %s """ # noqa: E501 ).strip().format(pyver="%s.%s" % sys.version_info[:2]) # For Python 2.7 to Python 3.2: VS2008 if ( (version.major == 2 and version.minor >= 7) or (version.major == 3 and version.minor <= 2) ): supported_vs_years = [("2008", None)] self._vs_help = vs_help_template % ( supported_vs_years[0][0], "Microsoft Visual C++ Compiler for Python 2.7", "http://aka.ms/vcpython27" ) # For Python 3.3 to Python 3.4: VS2010 elif ( version.major == 3 and ( version.minor >= 3 and version.minor <= 4 ) ): supported_vs_years = [("2010", None)] self._vs_help = vs_help_template % ( supported_vs_years[0][0], "Windows SDK for Windows 7 and .NET 4.0", "https://www.microsoft.com/download/details.aspx?id=8279" ) # For Python 3.5: VS2017, VS2015 elif version.major == 3 and version.minor == 5: supported_vs_years = [("2017", "v140"), ("2015", None)] self._vs_help = vs_help_template % ( supported_vs_years[0][0], "Visual Studio 2015", "https://visualstudio.microsoft.com/vs/older-downloads/" ) self._vs_help += "\n\n" + textwrap.dedent( """ Or with "Visual Studio 2017": https://visualstudio.microsoft.com/vs/ """ ).strip() # For Python 3.6 and above: VS2017 elif version.major == 3 and version.minor >= 6: supported_vs_years = [("2017", "v141")] self._vs_help = vs_help_template % ( supported_vs_years[0][0], "Visual Studio 2017", "https://visualstudio.microsoft.com/vs/" ) else: raise RuntimeError("Only Python >= 2.7 is supported on Windows.") for vs_year, vs_toolset in supported_vs_years: self.default_generators.extend([ CMakeVisualStudioCommandLineGenerator("Ninja", vs_year, vs_toolset), CMakeVisualStudioIDEGenerator(vs_year, vs_toolset), CMakeVisualStudioCommandLineGenerator("NMake Makefiles", vs_year, vs_toolset), CMakeVisualStudioCommandLineGenerator("NMake Makefiles JOM", vs_year, vs_toolset) ]) @property def generator_installation_help(self): """Return message guiding the user for installing a valid toolchain.""" return self._vs_help VS_YEAR_TO_VERSION = { "2008": 9, "2010": 10, "2012": 11, "2013": 12, "2015": 14, "2017": 15 } """Describes the version of `Visual Studio` supported by :class:`CMakeVisualStudioIDEGenerator` and :class:`CMakeVisualStudioCommandLineGenerator`. The different version are identified by their year. """ class CMakeVisualStudioIDEGenerator(CMakeGenerator): """ Represents a Visual Studio CMake generator. .. automethod:: __init__ """ def __init__(self, year, toolset=None): """Instantiate a generator object with its name set to the `Visual Studio` generator associated with the given ``year`` (see :data:`VS_YEAR_TO_VERSION`), the current platform (32-bit or 64-bit) and the selected ``toolset`` (if applicable). """ vs_version = VS_YEAR_TO_VERSION[year] vs_base = "Visual Studio %s %s" % (vs_version, year) # Python is Win64, build a Win64 module if platform.architecture()[0] == "64bit": vs_base += " Win64" super(CMakeVisualStudioIDEGenerator, self).__init__(vs_base, toolset=toolset) def _find_visual_studio_2010_to_2015(vs_version): """Adapted from https://github.com/python/cpython/blob/3.5/Lib/distutils/_msvccompiler.py The ``vs_version`` corresponds to the `Visual Studio` version to lookup. See :data:`VS_YEAR_TO_VERSION`. Return Visual Studio installation path found by looking up all key/value pairs associated with the ``Software\\Microsoft\\VisualStudio\\SxS\\VC7`` registry key. If no install is found, returns an empty string. Each key/value pair is the visual studio version (e.g `14.0`) and the installation path (e.g `C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/`). """ # winreg module try: import winreg except ImportError: # Support Python 2.7 try: import _winreg as winreg except ImportError: return "" # get registry key associated with Visual Studio installations try: key = winreg.OpenKey( winreg.HKEY_LOCAL_MACHINE, r"Software\Microsoft\VisualStudio\SxS\VC7", 0, winreg.KEY_READ | winreg.KEY_WOW64_32KEY ) except OSError: return "" with key: for i in count(): try: v, vc_dir, vt = winreg.EnumValue(key, i) except OSError: break # winreg.REG_SZ means "A null-terminated string" if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir): try: version = int(float(v)) except (ValueError, TypeError): continue if version == vs_version: return vc_dir return "" def _find_visual_studio_2017_or_newer(vs_version): """Adapted from https://github.com/python/cpython/blob/3.7/Lib/distutils/_msvccompiler.py The ``vs_version`` corresponds to the `Visual Studio` version to lookup. See :data:`VS_YEAR_TO_VERSION`. Returns `path` based on the result of invoking ``vswhere.exe``. If no install is found, returns an empty string. ..note: If ``vswhere.exe`` is not available, by definition, VS 2017 or newer is not installed. """ root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles") if not root: return "" try: extra_args = {} if sys.version_info[:3] >= (3, 6, 0): extra_args = {'encoding': 'mbcs', 'errors': 'strict'} path = subprocess.check_output([ os.path.join(root, "Microsoft Visual Studio", "Installer", "vswhere.exe"), "-version", "[%.1f, %.1f)" % (vs_version, vs_version + 1), "-prerelease", "-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64", "-property", "installationPath", "-products", "*", ], **extra_args).strip() if (3, 0) <= sys.version_info[:2] <= (3, 5): path = path.decode() except (subprocess.CalledProcessError, OSError, UnicodeDecodeError): return "" path = os.path.join(path, "VC", "Auxiliary", "Build") if os.path.isdir(path): return path return "" def find_visual_studio(vs_version): """Return Visual Studio installation path associated with ``vs_version`` or an empty string if any. The ``vs_version`` corresponds to the `Visual Studio` version to lookup. See :data:`VS_YEAR_TO_VERSION`. .. note:: - For VS 2017 and newer, returns `path` based on the result of invoking ``vswhere.exe``. - For VS 2010 to VS 2015, returns `path` by looking up all key/value pairs associated with the ``Software\\Microsoft\\VisualStudio\\SxS\\VC7`` registry key. Each key/value pair is the visual studio version (e.g `14.0`) and the installation path (e.g `C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/`). """ if 15 <= vs_version: return _find_visual_studio_2017_or_newer(vs_version) elif 10 <= vs_version <= 14: return _find_visual_studio_2010_to_2015(vs_version) else: return "" # To avoid multiple slow calls to ``subprocess.check_output()`` (either directly or # indirectly through ``query_vcvarsall``), results of previous calls are cached. __get_msvc_compiler_env_cache = dict() def _get_msvc_compiler_env(vs_version, vs_toolset=None): """ Return a dictionary of environment variables corresponding to ``vs_version`` that can be used with :class:`CMakeVisualStudioCommandLineGenerator`. The ``vs_toolset`` is used only for Visual Studio 2017 or newer (``vs_version >= 14``). If specified, ``vs_toolset`` is used to set the `-vcvars_ver=XX.Y` argument passed to ``vcvarsall.bat`` script. """ # pylint:disable=global-statement global __get_msvc_compiler_env_cache # Set architecture arch = "x86" if platform.architecture()[0] == "64bit": if vs_version < 14: arch = "amd64" else: arch = "x86_amd64" # If any, return cached version cache_key = ",".join([str(vs_version), arch, str(vs_toolset)]) if cache_key in __get_msvc_compiler_env_cache: return __get_msvc_compiler_env_cache[cache_key] from setuptools import monkey monkey.patch_for_msvc_specialized_compiler() if vs_version < 14: try: import distutils.msvc9compiler cached_env = distutils.msvc9compiler.query_vcvarsall(vs_version, arch) __get_msvc_compiler_env_cache[cache_key] = cached_env return cached_env except ImportError: print("failed to import 'distutils.msvc9compiler'") else: vc_dir = find_visual_studio(vs_version) vcvarsall = os.path.join(vc_dir, "vcvarsall.bat") if not os.path.exists(vcvarsall): return {} # Set vcvars_ver argument based on toolset vcvars_ver = "" if vs_toolset is not None and vs_version >= 15: match = re.findall(r"^v(\d\d)(\d+)$", vs_toolset)[0] if match: vcvars_ver = "-vcvars_ver=%s.%s" % match try: out = subprocess.check_output( 'cmd /u /c "{}" {} {} && set'.format(vcvarsall, arch, vcvars_ver), stderr=subprocess.STDOUT, ) if sys.version_info[0] >= 3: out = out.decode('utf-16le', errors='replace') vc_env = { key.lower(): value for key, _, value in (line.partition('=') for line in out.splitlines()) if key and value } cached_env = { 'PATH': vc_env.get('path', ''), 'INCLUDE': vc_env.get('include', ''), 'LIB': vc_env.get('lib', '') } __get_msvc_compiler_env_cache[cache_key] = cached_env return cached_env except subprocess.CalledProcessError as exc: print(exc.output) return {} class CMakeVisualStudioCommandLineGenerator(CMakeGenerator): """ Represents a command-line CMake generator initialized with a specific `Visual Studio` environment. .. automethod:: __init__ """ def __init__(self, name, year, toolset=None): """Instantiate CMake command-line generator. The generator ``name`` can be values like `Ninja`, `NMake Makefiles` or `NMake Makefiles JOM`. The ``year`` defines the `Visual Studio` environment associated with the generator. See :data:`VS_YEAR_TO_VERSION`. If set, the ``toolset`` defines the `Visual Studio Toolset` to select. The platform (32-bit or 64-bit) is automatically selected based on the value of ``platform.architecture()[0]``. """ vc_env = _get_msvc_compiler_env(VS_YEAR_TO_VERSION[year], toolset) env = {str(key.upper()): str(value) for key, value in vc_env.items()} super(CMakeVisualStudioCommandLineGenerator, self).__init__(name, env) self._description = "%s (%s)" % (self.name, CMakeVisualStudioIDEGenerator(year, toolset).description) scikit-build-0.11.1/skbuild/resources/000077500000000000000000000000001365474353700176455ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/resources/cmake/000077500000000000000000000000001365474353700207255ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/resources/cmake/FindCython.cmake000066400000000000000000000051161365474353700237770ustar00rootroot00000000000000#.rst: # # Find ``cython`` executable. # # This module will set the following variables in your project: # # ``CYTHON_EXECUTABLE`` # path to the ``cython`` program # # ``CYTHON_VERSION`` # version of ``cython`` # # ``CYTHON_FOUND`` # true if the program was found # # For more information on the Cython project, see http://cython.org/. # # *Cython is a language that makes writing C extensions for the Python language # as easy as Python itself.* # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= # Use the Cython executable that lives next to the Python executable # if it is a local installation. find_package(PythonInterp) if(PYTHONINTERP_FOUND) get_filename_component(_python_path ${PYTHON_EXECUTABLE} PATH) find_program(CYTHON_EXECUTABLE NAMES cython cython.bat cython3 HINTS ${_python_path} DOC "path to the cython executable") else() find_program(CYTHON_EXECUTABLE NAMES cython cython.bat cython3 DOC "path to the cython executable") endif() if(CYTHON_EXECUTABLE) set(CYTHON_version_command ${CYTHON_EXECUTABLE} --version) execute_process(COMMAND ${CYTHON_version_command} OUTPUT_VARIABLE CYTHON_version_output ERROR_VARIABLE CYTHON_version_error RESULT_VARIABLE CYTHON_version_result OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT ${CYTHON_version_result} EQUAL 0) set(_error_msg "Command \"${CYTHON_version_command}\" failed with") set(_error_msg "${_error_msg} output:\n${CYTHON_version_error}") message(SEND_ERROR "${_error_msg}") else() if("${CYTHON_version_output}" MATCHES "^[Cc]ython version ([^,]+)") set(CYTHON_VERSION "${CMAKE_MATCH_1}") endif() endif() endif() include(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Cython REQUIRED_VARS CYTHON_EXECUTABLE) mark_as_advanced(CYTHON_EXECUTABLE) include(UseCython) scikit-build-0.11.1/skbuild/resources/cmake/FindF2PY.cmake000066400000000000000000000101441365474353700232500ustar00rootroot00000000000000#.rst: # # The purpose of the F2PY –Fortran to Python interface generator– project is to provide a # connection between Python and Fortran languages. # # F2PY is a Python package (with a command line tool f2py and a module f2py2e) that facilitates # creating/building Python C/API extension modules that make it possible to call Fortran 77/90/95 # external subroutines and Fortran 90/95 module subroutines as well as C functions; to access Fortran # 77 COMMON blocks and Fortran 90/95 module data, including allocatable arrays from Python. # # For more information on the F2PY project, see http://www.f2py.com/. # # The following variables are defined: # # :: # # F2PY_EXECUTABLE - absolute path to the F2PY executable # # :: # # F2PY_VERSION_STRING - the version of F2PY found # F2PY_VERSION_MAJOR - the F2PY major version # F2PY_VERSION_MINOR - the F2PY minor version # F2PY_VERSION_PATCH - the F2PY patch version # # # .. note:: # # By default, the module finds the F2PY program associated with the installed NumPy package. # # Example usage # ^^^^^^^^^^^^^ # # Assuming that a package named ``method`` is declared in ``setup.py`` and that the corresponding directory # containing ``__init__.py`` also exists, the following CMake code can be added to ``method/CMakeLists.txt`` # to ensure the C sources associated with ``cylinder_methods.f90`` are generated and the corresponding module # is compiled: # # .. code-block:: cmake # # find_package(F2PY REQUIRED) # # set(f2py_module_name "_cylinder_methods") # set(fortran_src_file "${CMAKE_CURRENT_SOURCE_DIR}/cylinder_methods.f90") # # set(generated_module_file ${CMAKE_CURRENT_BINARY_DIR}/${f2py_module_name}${PYTHON_EXTENSION_MODULE_SUFFIX}) # # add_custom_target(${f2py_module_name} ALL # DEPENDS ${generated_module_file} # ) # # add_custom_command( # OUTPUT ${generated_module_file} # COMMAND ${F2PY_EXECUTABLE} # -m ${f2py_module_name} # -c # ${fortran_src_file} # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} # ) # # install(FILES ${generated_module_file} DESTINATION methods) # # .. warning:: # # Using ``f2py`` with ``-c`` argument means that f2py is also responsible to build the module. In that # case, CMake is not used to find the compiler and configure the associated build system. # find_program(F2PY_EXECUTABLE NAMES f2py${PYTHON_VERSION_MAJOR} f2py) # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT F2PY_EXECUTABLE) find_package(NumPy) set(F2PY_EXECUTABLE "${PYTHON_EXECUTABLE}" "-m" "numpy.f2py.__main__") endif() if(NOT F2PY_INCLUDE_DIR) execute_process( COMMAND "${PYTHON_EXECUTABLE}" -c "import os; from numpy import f2py; print(os.path.dirname(f2py.__file__))" OUTPUT_VARIABLE _f2py_directory OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) string(REPLACE "\\" "/" _f2py_directory ${_f2py_directory}) set(F2PY_INCLUDE_DIR "${_f2py_directory}/src" CACHE STRING "F2PY source directory location" FORCE) endif() # Set-up the F2PY libraries and include directories file(GLOB _f2py_sources "${F2PY_INCLUDE_DIR}/*.c") add_library(_f2py_runtime_library STATIC ${_f2py_sources}) target_include_directories( _f2py_runtime_library PRIVATE ${PYTHON_INCLUDE_DIRS} ${NumPy_INCLUDE_DIRS} ) set(F2PY_LIBRARIES _f2py_runtime_library) set(F2PY_INCLUDE_DIRS "${F2PY_INCLUDE_DIR}" "${NumPy_INCLUDE_DIRS}") if(F2PY_EXECUTABLE) # extract the version string execute_process(COMMAND "${F2PY_EXECUTABLE}" -v OUTPUT_VARIABLE F2PY_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE) if("${F2PY_VERSION_STRING}" MATCHES "^([0-9]+)(.([0-9+]))?(.([0-9+]))?$") set(F2PY_VERSION_MAJOR ${CMAKE_MATCH_1}) set(F2PY_VERSION_MINOR "${CMAKE_MATCH_3}") set(F2PY_VERSION_PATCH "${CMAKE_MATCH_5}") endif() endif() # handle the QUIETLY and REQUIRED arguments and set F2PY_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(F2PY REQUIRED_VARS F2PY_EXECUTABLE VERSION_VAR F2PY_VERSION_STRING ) mark_as_advanced(F2PY_EXECUTABLE) include(UseF2PY)scikit-build-0.11.1/skbuild/resources/cmake/FindNumPy.cmake000066400000000000000000000077531365474353700236140ustar00rootroot00000000000000#.rst: # # Find the include directory for ``numpy/arrayobject.h`` as well as other NumPy tools like ``conv-template`` and # ``from-template``. # # This module sets the following variables: # # ``NumPy_FOUND`` # True if NumPy was found. # ``NumPy_INCLUDE_DIRS`` # The include directories needed to use NumpPy. # ``NumPy_VERSION`` # The version of NumPy found. # ``NumPy_CONV_TEMPLATE_EXECUTABLE`` # Path to conv-template executable. # ``NumPy_FROM_TEMPLATE_EXECUTABLE`` # Path to from-template executable. # # The module will also explicitly define one cache variable: # # ``NumPy_INCLUDE_DIR`` # # .. note:: # # To support NumPy < v0.15.0 where ``from-template`` and ``conv-template`` are not declared as entry points, # the module emulates the behavior of standalone executables by setting the corresponding variables with the # path the the python interpreter and the path to the associated script. For example: # :: # # set(NumPy_CONV_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/conv_template.py CACHE STRING "Command executing conv-template program" FORCE) # # set(NumPy_FROM_TEMPLATE_EXECUTABLE /path/to/python /path/to/site-packages/numpy/distutils/from_template.py CACHE STRING "Command executing from-template program" FORCE) # if(NOT NumPy_FOUND) set(_find_extra_args) if(NumPy_FIND_REQUIRED) list(APPEND _find_extra_args REQUIRED) endif() if(NumPy_FIND_QUIET) list(APPEND _find_extra_args QUIET) endif() find_package(PythonInterp ${_find_extra_args}) find_package(PythonLibs ${_find_extra_args}) find_program(NumPy_CONV_TEMPLATE_EXECUTABLE NAMES conv-template) find_program(NumPy_FROM_TEMPLATE_EXECUTABLE NAMES from-template) if(PYTHON_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy; print(numpy.get_include())" OUTPUT_VARIABLE _numpy_include_dir OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "import numpy; print(numpy.__version__)" OUTPUT_VARIABLE NumPy_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT NumPy_CONV_TEMPLATE_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "from numpy.distutils import conv_template; print(conv_template.__file__)" OUTPUT_VARIABLE _numpy_conv_template_file OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) set(NumPy_CONV_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_conv_template_file}" CACHE STRING "Command executing conv-template program" FORCE) endif() # XXX This is required to support NumPy < v0.15.0. See note in module documentation above. if(NOT NumPy_FROM_TEMPLATE_EXECUTABLE) execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "from numpy.distutils import from_template; print(from_template.__file__)" OUTPUT_VARIABLE _numpy_from_template_file OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) set(NumPy_FROM_TEMPLATE_EXECUTABLE "${PYTHON_EXECUTABLE}" "${_numpy_from_template_file}" CACHE STRING "Command executing from-template program" FORCE) endif() endif() endif() find_path(NumPy_INCLUDE_DIR numpy/arrayobject.h PATHS "${_numpy_include_dir}" "${PYTHON_INCLUDE_DIR}" PATH_SUFFIXES numpy/core/include ) set(NumPy_INCLUDE_DIRS ${NumPy_INCLUDE_DIR}) # handle the QUIETLY and REQUIRED arguments and set NumPy_FOUND to TRUE if # all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(NumPy REQUIRED_VARS NumPy_INCLUDE_DIR NumPy_CONV_TEMPLATE_EXECUTABLE NumPy_FROM_TEMPLATE_EXECUTABLE VERSION_VAR NumPy_VERSION ) mark_as_advanced(NumPy_INCLUDE_DIR) scikit-build-0.11.1/skbuild/resources/cmake/FindPythonExtensions.cmake000066400000000000000000000520411365474353700260730ustar00rootroot00000000000000#.rst: # # This module defines CMake functions to build Python extension modules and # stand-alone executables. # # The following variables are defined: # :: # # PYTHON_PREFIX - absolute path to the current Python # distribution's prefix # PYTHON_SITE_PACKAGES_DIR - absolute path to the current Python # distribution's site-packages directory # PYTHON_RELATIVE_SITE_PACKAGES_DIR - path to the current Python # distribution's site-packages directory # relative to its prefix # PYTHON_SEPARATOR - separator string for file path # components. Equivalent to ``os.sep`` in # Python. # PYTHON_PATH_SEPARATOR - separator string for PATH-style # environment variables. Equivalent to # ``os.pathsep`` in Python. # PYTHON_EXTENSION_MODULE_SUFFIX - suffix of the compiled module. For example, on # Linux, based on environment, it could be ``.cpython-35m-x86_64-linux-gnu.so``. # # # # The following functions are defined: # # .. cmake:command:: python_extension_module # # For libraries meant to be used as Python extension modules, either dynamically # loaded or directly linked. Amend the configuration of the library target # (created using ``add_library``) with additional options needed to build and # use the referenced library as a Python extension module. # # python_extension_module( # [LINKED_MODULES_VAR ] # [FORWARD_DECL_MODULES_VAR ] # [MODULE_SUFFIX ]) # # Only extension modules that are configured to be built as MODULE libraries can # be runtime-loaded through the standard Python import mechanism. All other # modules can only be included in standalone applications that are written to # expect their presence. In addition to being linked against the libraries for # these modules, such applications must forward declare their entry points and # initialize them prior to use. To generate these forward declarations and # initializations, see ``python_modules_header``. # # If ```` does not refer to a target, then it is assumed to refer to an # extension module that is not linked at all, but compiled along with other # source files directly into an executable. Adding these modules does not cause # any library configuration modifications, and they are not added to the list of # linked modules. They still must be forward declared and initialized, however, # and so are added to the forward declared modules list. # # If the associated target is of type ``MODULE_LIBRARY``, the LINK_FLAGS target # property is used to set symbol visibility and export only the module init function. # This applies to GNU and MSVC compilers. # # Options: # # ``LINKED_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose libraries # must be linked into the executables of any stand-alone applications that use # them. By default, the global property ``PY_LINKED_MODULES_LIST`` is used. # # ``FORWARD_DECL_MODULES_VAR `` # Name of the variable referencing a list of extension modules whose entry # points must be forward declared and called by any stand-alone applications # that use them. By default, the global property # ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``MODULE_SUFFIX `` # Suffix appended to the python extension module file. # The default suffix is retrieved using ``sysconfig.get_config_var("SO")"``, # if not available, the default is then ``.so`` on unix and ``.pyd`` on # windows. # Setting the variable ``PYTHON_EXTENSION_MODULE_SUFFIX`` in the caller # scope defines the value used for all extensions not having a suffix # explicitly specified using ``MODULE_SUFFIX`` parameter. # # # .. cmake:command:: python_standalone_executable # # python_standalone_executable() # # For standalone executables that initialize their own Python runtime # (such as when building source files that include one generated by Cython with # the --embed option). Amend the configuration of the executable target # (created using ``add_executable``) with additional options needed to properly # build the referenced executable. # # # .. cmake:command:: python_modules_header # # Generate a header file that contains the forward declarations and # initialization routines for the given list of Python extension modules. # ```` is the logical name for the header file (no file extensions). # ```` is the actual destination filename for the header file # (e.g.: decl_modules.h). # # python_modules_header( [HeaderFilename] # [FORWARD_DECL_MODULES_LIST ] # [HEADER_OUTPUT_VAR ] # [INCLUDE_DIR_OUTPUT_VAR ]) # # without the extension is used as the logical name. If only ```` is # # If only ```` is provided, and it ends in the ".h" extension, then it # is assumed to be the ````. The filename of the header file # provided, and it does not end in the ".h" extension, then the # ```` is assumed to ``.h``. # # The exact contents of the generated header file depend on the logical # ````. It should be set to a value that corresponds to the target # application, or for the case of multiple applications, some identifier that # conveyes its purpose. It is featured in the generated multiple inclusion # guard as well as the names of the generated initialization routines. # # The generated header file includes forward declarations for all listed # modules, as well as implementations for the following class of routines: # # ``int _(void)`` # Initializes the python extension module, ````. Returns an integer # handle to the module. # # ``void _LoadAllPythonModules(void)`` # Initializes all listed python extension modules. # # ``void CMakeLoadAllPythonModules(void);`` # Alias for ``_LoadAllPythonModules`` whose name does not depend on # ````. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_LOAD_ALL_FUNCTION`` is defined. # # ``void Py_Initialize_Wrapper();`` # Wrapper arpund ``Py_Initialize()`` that initializes all listed python # extension modules. This function is excluded during preprocessing if the # preprocessing macro ``EXCLUDE_PY_INIT_WRAPPER`` is defined. If this # function is generated, then ``Py_Initialize()`` is redefined to a macro # that calls this function. # # Options: # # ``FORWARD_DECL_MODULES_LIST `` # List of extension modules for which to generate forward declarations of # their entry points and their initializations. By default, the global # property ``PY_FORWARD_DECL_MODULES_LIST`` is used. # # ``HEADER_OUTPUT_VAR `` # Name of the variable to set to the path to the generated header file. By # default, ```` is used. # # ``INCLUDE_DIR_OUTPUT_VAR `` # Name of the variable to set to the path to the directory containing the # generated header file. By default, ``_INCLUDE_DIRS`` is used. # # Defined variables: # # ```` # The path to the generated header file # # ```` # Directory containing the generated header file # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonInterp) # find_package(PythonLibs) # find_package(PythonExtensions) # find_package(Cython) # find_package(Boost COMPONENTS python) # # # Simple Cython Module -- no executables # add_cython_target(_module.pyx) # add_library(_module MODULE ${_module}) # python_extension_module(_module) # # # Mix of Cython-generated code and C++ code using Boost Python # # Stand-alone executable -- no modules # include_directories(${Boost_INCLUDE_DIRS}) # add_cython_target(main.pyx CXX EMBED_MAIN) # add_executable(main boost_python_module.cxx ${main}) # target_link_libraries(main ${Boost_LIBRARIES}) # python_standalone_executable(main) # # # stand-alone executable with three extension modules: # # one statically linked, one dynamically linked, and one loaded at runtime # # # # Freely mixes Cython-generated code, code using Boost-Python, and # # hand-written code using the CPython API. # # # module1 -- statically linked # add_cython_target(module1.pyx) # add_library(module1 STATIC ${module1}) # python_extension_module(module1 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module2 -- dynamically linked # include_directories({Boost_INCLUDE_DIRS}) # add_library(module2 SHARED boost_module2.cxx) # target_link_libraries(module2 ${Boost_LIBRARIES}) # python_extension_module(module2 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # module3 -- loaded at runtime # add_cython_target(module3a.pyx) # add_library(module3 MODULE ${module3a} module3b.cxx) # target_link_libraries(module3 ${Boost_LIBRARIES}) # python_extension_module(module3 # LINKED_MODULES_VAR linked_module_list # FORWARD_DECL_MODULES_VAR fdecl_module_list) # # # application executable -- generated header file + other source files # python_modules_header(modules # FORWARD_DECL_MODULES_LIST ${fdecl_module_list}) # include_directories(${modules_INCLUDE_DIRS}) # # add_cython_target(mainA) # add_cython_target(mainC) # add_executable(main ${mainA} mainB.cxx ${mainC} mainD.c) # # target_link_libraries(main ${linked_module_list} ${Boost_LIBRARIES}) # python_standalone_executable(main) # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= find_package(PythonInterp REQUIRED) find_package(PythonLibs) include(targetLinkLibrariesWithDynamicLookup) set(_command " import distutils.sysconfig import itertools import os import os.path import site import sys result = None rel_result = None candidate_lists = [] try: candidate_lists.append((distutils.sysconfig.get_python_lib(),)) except AttributeError: pass try: candidate_lists.append(site.getsitepackages()) except AttributeError: pass try: candidate_lists.append((site.getusersitepackages(),)) except AttributeError: pass candidates = itertools.chain.from_iterable(candidate_lists) for candidate in candidates: rel_candidate = os.path.relpath( candidate, sys.prefix) if not rel_candidate.startswith(\"..\"): result = candidate rel_result = rel_candidate break ext_suffix_var = 'SO' if sys.version_info[:2] >= (3, 5): ext_suffix_var = 'EXT_SUFFIX' sys.stdout.write(\";\".join(( os.sep, os.pathsep, sys.prefix, result, rel_result, distutils.sysconfig.get_config_var(ext_suffix_var) ))) ") execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c "${_command}" OUTPUT_VARIABLE _list RESULT_VARIABLE _result) list(GET _list 0 _item) set(PYTHON_SEPARATOR "${_item}") mark_as_advanced(PYTHON_SEPARATOR) list(GET _list 1 _item) set(PYTHON_PATH_SEPARATOR "${_item}") mark_as_advanced(PYTHON_PATH_SEPARATOR) list(GET _list 2 _item) set(PYTHON_PREFIX "${_item}") mark_as_advanced(PYTHON_PREFIX) list(GET _list 3 _item) set(PYTHON_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_SITE_PACKAGES_DIR) list(GET _list 4 _item) set(PYTHON_RELATIVE_SITE_PACKAGES_DIR "${_item}") mark_as_advanced(PYTHON_RELATIVE_SITE_PACKAGES_DIR) if(NOT DEFINED PYTHON_EXTENSION_MODULE_SUFFIX) list(GET _list 5 _item) set(PYTHON_EXTENSION_MODULE_SUFFIX "${_item}") endif() function(_set_python_extension_symbol_visibility _target) if(PYTHON_VERSION_MAJOR VERSION_GREATER 2) set(_modinit_prefix "PyInit_") else() set(_modinit_prefix "init") endif() message("_modinit_prefix:${_modinit_prefix}") if("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") set_target_properties(${_target} PROPERTIES LINK_FLAGS "/EXPORT:${_modinit_prefix}${_target}" ) elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") set(_script_path ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${_target}-version-script.map ) file(WRITE ${_script_path} "{global: ${_modinit_prefix}${_target}; local: *; };" ) set_property(TARGET ${_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--version-script=\"${_script_path}\"" ) endif() endfunction() function(python_extension_module _target) set(one_ops LINKED_MODULES_VAR FORWARD_DECL_MODULES_VAR MODULE_SUFFIX) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) set(_lib_type "NA") if(TARGET ${_target}) get_property(_lib_type TARGET ${_target} PROPERTY TYPE) endif() set(_is_non_lib TRUE) set(_is_static_lib FALSE) if(_lib_type STREQUAL "STATIC_LIBRARY") set(_is_static_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_shared_lib FALSE) if(_lib_type STREQUAL "SHARED_LIBRARY") set(_is_shared_lib TRUE) set(_is_non_lib FALSE) endif() set(_is_module_lib FALSE) if(_lib_type STREQUAL "MODULE_LIBRARY") set(_is_module_lib TRUE) set(_is_non_lib FALSE) endif() if(_is_static_lib OR _is_shared_lib OR _is_non_lib) if(_is_static_lib OR _is_shared_lib) if(_args_LINKED_MODULES_VAR) set(${_args_LINKED_MODULES_VAR} ${${_args_LINKED_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_LINKED_MODULES_LIST ${_target}) endif() endif() if(_args_FORWARD_DECL_MODULES_VAR) set(${_args_FORWARD_DECL_MODULES_VAR} ${${_args_FORWARD_DECL_MODULES_VAR}} ${_target} PARENT_SCOPE) else() set_property(GLOBAL APPEND PROPERTY PY_FORWARD_DECL_MODULES_LIST ${_target}) endif() endif() if(NOT _is_non_lib) include_directories("${PYTHON_INCLUDE_DIRS}") endif() if(_is_module_lib) set_target_properties(${_target} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") endif() if(_is_module_lib OR _is_shared_lib) if(_is_module_lib) if(NOT _args_MODULE_SUFFIX) set(_args_MODULE_SUFFIX "${PYTHON_EXTENSION_MODULE_SUFFIX}") endif() if(_args_MODULE_SUFFIX STREQUAL "" AND WIN32 AND NOT CYGWIN) set(_args_MODULE_SUFFIX ".pyd") endif() if(NOT _args_MODULE_SUFFIX STREQUAL "") set_target_properties(${_target} PROPERTIES SUFFIX ${_args_MODULE_SUFFIX}) endif() endif() target_link_libraries_with_dynamic_lookup(${_target} ${PYTHON_LIBRARIES}) if(_is_module_lib) _set_python_extension_symbol_visibility(${_target}) endif() endif() endfunction() function(python_standalone_executable _target) include_directories(${PYTHON_INCLUDE_DIRS}) target_link_libraries(${_target} ${PYTHON_LIBRARIES}) endfunction() function(python_modules_header _name) set(one_ops FORWARD_DECL_MODULES_LIST HEADER_OUTPUT_VAR INCLUDE_DIR_OUTPUT_VAR) cmake_parse_arguments(_args "" "${one_ops}" "" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if present, use arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) get_filename_component(_name "${_source_file}" NAME_WE) # otherwise, assume the source file is ${_name}.h else() set(_source_file ${_name}.h) endif() endif() if(_args_FORWARD_DECL_MODULES_LIST) set(static_mod_list ${_args_FORWARD_DECL_MODULES_LIST}) else() get_property(static_mod_list GLOBAL PROPERTY PY_FORWARD_DECL_MODULES_LIST) endif() string(REPLACE "." "_" _header_name "${_name}") string(TOUPPER ${_header_name} _header_name_upper) set(_header_name_upper "_${_header_name_upper}_H") set(generated_file ${CMAKE_CURRENT_BINARY_DIR}/${_source_file}) set(generated_file_tmp "${generated_file}.in") file(WRITE ${generated_file_tmp} "/* Created by CMake. DO NOT EDIT; changes will be lost. */\n") set(_chunk "") set(_chunk "${_chunk}#ifndef ${_header_name_upper}\n") set(_chunk "${_chunk}#define ${_header_name_upper}\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#include \n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}extern \"C\" {\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") set(_chunk "${_chunk}#if PY_MAJOR_VERSION < 3\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC init${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() file(APPEND ${generated_file_tmp} "#else /* PY_MAJOR_VERSION >= 3*/\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} "PyMODINIT_FUNC PyInit_${PYTHON_MODULE_PREFIX}${_module}(void);\n") endforeach() set(_chunk "") set(_chunk "${_chunk}#endif /* PY_MAJOR_VERSION >= 3*/\n\n") set(_chunk "${_chunk}#ifdef __cplusplus\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* __cplusplus */\n") set(_chunk "${_chunk}\n") file(APPEND ${generated_file_tmp} "${_chunk}") foreach(_module ${static_mod_list}) set(_import_function "${_header_name}_${_module}") set(_prefixed_module "${PYTHON_MODULE_PREFIX}${_module}") set(_chunk "") set(_chunk "${_chunk}int ${_import_function}(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} static char name[] = \"${_prefixed_module}\";\n") set(_chunk "${_chunk} #if PY_MAJOR_VERSION < 3\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, init${_prefixed_module});\n") set(_chunk "${_chunk} #else /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk} return PyImport_AppendInittab(") set(_chunk "${_chunk}name, PyInit_${_prefixed_module});\n") set(_chunk "${_chunk} #endif /* PY_MAJOR_VERSION >= 3 */\n") set(_chunk "${_chunk}}\n\n") file(APPEND ${generated_file_tmp} "${_chunk}") endforeach() file(APPEND ${generated_file_tmp} "void ${_header_name}_LoadAllPythonModules(void)\n{\n") foreach(_module ${static_mod_list}) file(APPEND ${generated_file_tmp} " ${_header_name}_${_module}();\n") endforeach() file(APPEND ${generated_file_tmp} "}\n\n") set(_chunk "") set(_chunk "${_chunk}#ifndef EXCLUDE_LOAD_ALL_FUNCTION\n") set(_chunk "${_chunk}void CMakeLoadAllPythonModules(void)\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_LOAD_ALL_FUNCTION */\n\n") set(_chunk "${_chunk}#ifndef EXCLUDE_PY_INIT_WRAPPER\n") set(_chunk "${_chunk}static void Py_Initialize_Wrapper()\n") set(_chunk "${_chunk}{\n") set(_chunk "${_chunk} ${_header_name}_LoadAllPythonModules();\n") set(_chunk "${_chunk} Py_Initialize();\n") set(_chunk "${_chunk}}\n") set(_chunk "${_chunk}#define Py_Initialize Py_Initialize_Wrapper\n") set(_chunk "${_chunk}#endif /* !EXCLUDE_PY_INIT_WRAPPER */\n\n") set(_chunk "${_chunk}#endif /* !${_header_name_upper} */\n") file(APPEND ${generated_file_tmp} "${_chunk}") # with configure_file() cmake complains that you may not use a file created # using file(WRITE) as input file for configure_file() execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${generated_file_tmp}" "${generated_file}" OUTPUT_QUIET ERROR_QUIET) set(_header_output_var ${_name}) if(_args_HEADER_OUTPUT_VAR) set(_header_output_var ${_args_HEADER_OUTPUT_VAR}) endif() set(${_header_output_var} ${generated_file} PARENT_SCOPE) set(_include_dir_var ${_name}_INCLUDE_DIRS) if(_args_INCLUDE_DIR_OUTPUT_VAR) set(_include_dir_var ${_args_INCLUDE_DIR_OUTPUT_VAR}) endif() set(${_include_dirs_var} ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) endfunction() scikit-build-0.11.1/skbuild/resources/cmake/UseCython.cmake000066400000000000000000000325561365474353700236630ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_cython_target # # Create a custom rule to generate the source code for a Python extension module # using cython. # # add_cython_target( [] # [EMBED_MAIN] # [C | CXX] # [PY2 | PY3] # [OUTPUT_VAR ]) # # ```` is the name of the new target, and ```` # is the path to a cython source file. Note that, despite the name, no new # targets are created by this function. Instead, see ``OUTPUT_VAR`` for # retrieving the path to the generated source for subsequent targets. # # If only ```` is provided, and it ends in the ".pyx" extension, then it # is assumed to be the ````. The name of the input without the # extension is used as the target name. If only ```` is provided, and it # does not end in the ".pyx" extension, then the ```` is assumed to # be ``.pyx``. # # The Cython include search path is amended with any entries found in the # ``INCLUDE_DIRECTORIES`` property of the directory containing the # ```` file. Use ``include_directories`` to add to the Cython # include search path. # # Options: # # ``EMBED_MAIN`` # Embed a main() function in the generated output (for stand-alone # applications that initialize their own Python runtime). # # ``C | CXX`` # Force the generation of either a C or C++ file. By default, a C file is # generated, unless the C language is not enabled for the project; in this # case, a C++ file is generated by default. # # ``PY2 | PY3`` # Force compilation using either Python-2 or Python-3 syntax and code # semantics. By default, Python-2 syntax and semantics are used if the major # version of Python found is 2. Otherwise, Python-3 syntax and sematics are # used. # # ``OUTPUT_VAR `` # Set the variable ```` in the parent scope to the path to the # generated source file. By default, ```` is used as the output # variable name. # # Defined variables: # # ```` # The path of the generated source file. # # Cache variables that effect the behavior include: # # ``CYTHON_ANNOTATE`` # whether to create an annotated .html file when compiling # # ``CYTHON_FLAGS`` # additional flags to pass to the Cython compiler # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(Cython) # # # Note: In this case, either one of these arguments may be omitted; their # # value would have been inferred from that of the other. # add_cython_target(cy_code cy_code.pyx) # # add_library(cy_code MODULE ${cy_code}) # target_link_libraries(cy_code ...) # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= # Configuration options. set(CYTHON_ANNOTATE OFF CACHE BOOL "Create an annotated .html file when compiling *.pyx.") set(CYTHON_FLAGS "" CACHE STRING "Extra flags to the cython compiler.") mark_as_advanced(CYTHON_ANNOTATE CYTHON_FLAGS) string(REGEX REPLACE " " ";" CYTHON_FLAGS_LIST "${CYTHON_FLAGS}") find_package(PythonLibs REQUIRED) set(CYTHON_CXX_EXTENSION "cxx") set(CYTHON_C_EXTENSION "c") get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) function(add_cython_target _name) set(options EMBED_MAIN C CXX PY2 PY3) set(options1 OUTPUT_VAR) cmake_parse_arguments(_args "${options}" "${options1}" "" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if provided, use _arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) get_filename_component(_name "${_source_file}" NAME_WE) # otherwise, assume the source file is ${_name}.pyx else() set(_source_file ${_name}.pyx) endif() endif() set(_embed_main FALSE) if("C" IN_LIST languages) set(_output_syntax "C") elseif("CXX" IN_LIST languages) set(_output_syntax "CXX") else() message(FATAL_ERROR "Either C or CXX must be enabled to use Cython") endif() if("${PYTHONLIBS_VERSION_STRING}" MATCHES "^2.") set(_input_syntax "PY2") else() set(_input_syntax "PY3") endif() if(_args_EMBED_MAIN) set(_embed_main TRUE) endif() if(_args_C) set(_output_syntax "C") endif() if(_args_CXX) set(_output_syntax "CXX") endif() if(_args_PY2) set(_input_syntax "PY2") endif() if(_args_PY3) set(_input_syntax "PY3") endif() set(embed_arg "") if(_embed_main) set(embed_arg "--embed") endif() set(cxx_arg "") set(extension "c") if(_output_syntax STREQUAL "CXX") set(cxx_arg "--cplus") set(extension "cxx") endif() set(py_version_arg "") if(_input_syntax STREQUAL "PY2") set(py_version_arg "-2") elseif(_input_syntax STREQUAL "PY3") set(py_version_arg "-3") endif() set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}.${extension}") set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) set(_output_var ${_name}) if(_args_OUTPUT_VAR) set(_output_var ${_args_OUTPUT_VAR}) endif() set(${_output_var} ${generated_file} PARENT_SCOPE) file(RELATIVE_PATH generated_file_relative ${CMAKE_BINARY_DIR} ${generated_file}) set(comment "Generating ${_output_syntax} source ${generated_file_relative}") set(cython_include_directories "") set(pxd_dependencies "") set(c_header_dependencies "") # Get the include directories. get_source_file_property(pyx_location ${_source_file} LOCATION) get_filename_component(pyx_path ${pyx_location} PATH) get_directory_property(cmake_include_directories DIRECTORY ${pyx_path} INCLUDE_DIRECTORIES) list(APPEND cython_include_directories ${cmake_include_directories}) # Determine dependencies. # Add the pxd file with the same basename as the given pyx file. get_filename_component(pyx_file_basename ${_source_file} NAME_WE) unset(corresponding_pxd_file CACHE) find_file(corresponding_pxd_file ${pyx_file_basename}.pxd PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(corresponding_pxd_file) list(APPEND pxd_dependencies "${corresponding_pxd_file}") endif() # pxd files to check for additional dependencies set(pxds_to_check "${_source_file}" "${pxd_dependencies}") set(pxds_checked "") set(number_pxds_to_check 1) while(number_pxds_to_check GREATER 0) foreach(pxd ${pxds_to_check}) list(APPEND pxds_checked "${pxd}") list(REMOVE_ITEM pxds_to_check "${pxd}") # look for C headers file(STRINGS "${pxd}" extern_from_statements REGEX "cdef[ ]+extern[ ]+from.*$") foreach(statement ${extern_from_statements}) # Had trouble getting the quote in the regex string(REGEX REPLACE "cdef[ ]+extern[ ]+from[ ]+[\"]([^\"]+)[\"].*" "\\1" header "${statement}") unset(header_location CACHE) find_file(header_location ${header} PATHS ${cmake_include_directories}) if(header_location) list(FIND c_header_dependencies "${header_location}" header_idx) if(${header_idx} LESS 0) list(APPEND c_header_dependencies "${header_location}") endif() endif() endforeach() # check for pxd dependencies # Look for cimport statements. set(module_dependencies "") file(STRINGS "${pxd}" cimport_statements REGEX cimport) foreach(statement ${cimport_statements}) if(${statement} MATCHES from) string(REGEX REPLACE "from[ ]+([^ ]+).*" "\\1" module "${statement}") else() string(REGEX REPLACE "cimport[ ]+([^ ]+).*" "\\1" module "${statement}") endif() list(APPEND module_dependencies ${module}) endforeach() # check for pxi dependencies # Look for include statements. set(include_dependencies "") file(STRINGS "${pxd}" include_statements REGEX include) foreach(statement ${include_statements}) string(REGEX REPLACE "include[ ]+[\"]([^\"]+)[\"].*" "\\1" module "${statement}") list(APPEND include_dependencies ${module}) endforeach() list(REMOVE_DUPLICATES module_dependencies) list(REMOVE_DUPLICATES include_dependencies) # Add modules to the files to check, if appropriate. foreach(module ${module_dependencies}) unset(pxd_location CACHE) find_file(pxd_location ${module}.pxd PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(pxd_location) list(FIND pxds_checked ${pxd_location} pxd_idx) if(${pxd_idx} LESS 0) list(FIND pxds_to_check ${pxd_location} pxd_idx) if(${pxd_idx} LESS 0) list(APPEND pxds_to_check ${pxd_location}) list(APPEND pxd_dependencies ${pxd_location}) endif() # if it is not already going to be checked endif() # if it has not already been checked endif() # if pxd file can be found endforeach() # for each module dependency discovered # Add includes to the files to check, if appropriate. foreach(_include ${include_dependencies}) unset(pxi_location CACHE) find_file(pxi_location ${_include} PATHS "${pyx_path}" ${cmake_include_directories} NO_DEFAULT_PATH) if(pxi_location) list(FIND pxds_checked ${pxi_location} pxd_idx) if(${pxd_idx} LESS 0) list(FIND pxds_to_check ${pxi_location} pxd_idx) if(${pxd_idx} LESS 0) list(APPEND pxds_to_check ${pxi_location}) list(APPEND pxd_dependencies ${pxi_location}) endif() # if it is not already going to be checked endif() # if it has not already been checked endif() # if include file can be found endforeach() # for each include dependency discovered endforeach() # for each include file to check list(LENGTH pxds_to_check number_pxds_to_check) endwhile() # Set additional flags. set(annotate_arg "") if(CYTHON_ANNOTATE) set(annotate_arg "--annotate") endif() set(no_docstrings_arg "") if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel") set(no_docstrings_arg "--no-docstrings") endif() set(cython_debug_arg "") set(embed_pos_arg "") set(line_directives_arg "") if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo") set(cython_debug_arg "--gdb") set(embed_pos_arg "--embed-positions") set(line_directives_arg "--line-directives") endif() # Include directory arguments. list(REMOVE_DUPLICATES cython_include_directories) set(include_directory_arg "") foreach(_include_dir ${cython_include_directories}) set(include_directory_arg ${include_directory_arg} "--include-dir" "${_include_dir}") endforeach() list(REMOVE_DUPLICATES pxd_dependencies) list(REMOVE_DUPLICATES c_header_dependencies) # Add the command to run the compiler. add_custom_command(OUTPUT ${generated_file} COMMAND ${CYTHON_EXECUTABLE} ARGS ${cxx_arg} ${include_directory_arg} ${py_version_arg} ${embed_arg} ${annotate_arg} ${no_docstrings_arg} ${cython_debug_arg} ${embed_pos_arg} ${line_directives_arg} ${CYTHON_FLAGS_LIST} ${pyx_location} --output-file ${generated_file} DEPENDS ${_source_file} ${pxd_dependencies} IMPLICIT_DEPENDS ${_output_syntax} ${c_header_dependencies} COMMENT ${comment}) # NOTE(opadron): I thought about making a proper target, but after trying it # out, I decided that it would be far too convenient to use the same name as # the target for the extension module (e.g.: for single-file modules): # # ... # add_cython_target(_module.pyx) # add_library(_module ${_module}) # ... # # The above example would not be possible since the "_module" target name # would already be taken by the cython target. Since I can't think of a # reason why someone would need the custom target instead of just using the # generated file directly, I decided to leave this commented out. # # add_custom_target(${_name} DEPENDS ${generated_file}) # Remove their visibility to the user. set(corresponding_pxd_file "" CACHE INTERNAL "") set(header_location "" CACHE INTERNAL "") set(pxd_location "" CACHE INTERNAL "") endfunction() scikit-build-0.11.1/skbuild/resources/cmake/UseF2PY.cmake000066400000000000000000000113741365474353700231320ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_f2py_target # # Create a custom rule to generate the source code for a Python extension module # using f2py. # # add_f2py_target( [] # [OUTPUT_VAR ]) # # ```` is the name of the new target, and ```` # is the path to a pyf source file. Note that, despite the name, no new # targets are created by this function. Instead, see ``OUTPUT_VAR`` for # retrieving the path to the generated source for subsequent targets. # # If only ```` is provided, and it ends in the ".pyf" extension, then it # is assumed to be the ````. The name of the input without the # extension is used as the target name. If only ```` is provided, and it # does not end in the ".pyf" extension, then the ```` is assumed to # be ``.pyf``. # # # Options: # # ``OUTPUT_VAR `` # Set the variable ```` in the parent scope to the path to the # generated source file. By default, ```` is used as the output # variable name. # # ``DEPENDS [source [source2...]]`` # Sources that must be generated before the F2PY command is run. # # Defined variables: # # ```` # The path of the generated source file. # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(F2PY) # # # Note: In this case, either one of these arguments may be omitted; their # # value would have been inferred from that of the other. # add_f2py_target(f2py_code f2py_code.pyf) # # add_library(f2py_code MODULE ${f2py_code}) # target_link_libraries(f2py_code ...) # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= get_property(languages GLOBAL PROPERTY ENABLED_LANGUAGES) function(add_f2py_target _name) set(options ) set(oneValueArgs OUTPUT_VAR) set(multiValueArgs DEPENDS) cmake_parse_arguments(_args "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) list(GET _args_UNPARSED_ARGUMENTS 0 _arg0) # if provided, use _arg0 as the input file path if(_arg0) set(_source_file ${_arg0}) # otherwise, must determine source file from name, or vice versa else() get_filename_component(_name_ext "${_name}" EXT) # if extension provided, _name is the source file if(_name_ext) set(_source_file ${_name}) string(REGEX REPLACE "\\.[^.]*$" "" _name ${_source}) # otherwise, assume the source file is ${_name}.pyf else() set(_source_file ${_name}.pyf) endif() endif() set(_embed_main FALSE) if("C" IN_LIST languages) set(_output_syntax "C") else() message(FATAL_ERROR "C must be enabled to use F2PY") endif() set(extension "c") set(generated_file "${CMAKE_CURRENT_BINARY_DIR}/${_name}module.${extension}") set(generated_wrapper "${CMAKE_CURRENT_BINARY_DIR}/${_name}-f2pywrappers.f") get_filename_component(generated_file_dir ${generated_file} DIRECTORY) set_source_files_properties(${generated_file} PROPERTIES GENERATED TRUE) set_source_files_properties(${generated_wrapper} PROPERTIES GENERATED TRUE) set(_output_var ${_name}) if(_args_OUTPUT_VAR) set(_output_var ${_args_OUTPUT_VAR}) endif() set(${_output_var} ${generated_file} ${generated_wrapper} PARENT_SCOPE) file(RELATIVE_PATH generated_file_relative ${CMAKE_BINARY_DIR} ${generated_file}) set(comment "Generating ${_output_syntax} source ${generated_file_relative}") # Get the include directories. get_source_file_property(pyf_location ${_source_file} LOCATION) get_filename_component(pyf_path ${pyf_location} PATH) # Create the directory so that the command can cd to it file(MAKE_DIRECTORY ${generated_file_dir}) # Add the command to run the compiler. add_custom_command(OUTPUT ${generated_file} ${generated_wrapper} COMMAND ${F2PY_EXECUTABLE} ${pyf_location} COMMAND ${CMAKE_COMMAND} -E touch ${generated_wrapper} DEPENDS ${_source_file} ${_args_DEPENDS} WORKING_DIRECTORY ${generated_file_dir} COMMENT ${source_comment}) endfunction() scikit-build-0.11.1/skbuild/resources/cmake/UsePythonExtensions.cmake000066400000000000000000000236311365474353700257520ustar00rootroot00000000000000#.rst: # # The following functions are defined: # # .. cmake:command:: add_python_library # # Add a library that contains a mix of C, C++, Fortran, Cython, F2PY, Template, # and Tempita sources. The required targets are automatically generated to # "lower" source files from their high-level representation to a file that the # compiler can accept. # # # add_python_library( # SOURCES [source1 [source2 ...]] # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] # [LINK_LIBRARIES [lib1 [lib2 ...]] # [DEPENDS [source1 [source2 ...]]]) # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) # # add_python_library(arpack_scipy # SOURCES ${arpack_sources} # ${g77_wrapper_sources} # INCLUDE_DIRECTORIES ARPACK/SRC # ) # # .. cmake:command:: add_python_extension # # Add a extension that contains a mix of C, C++, Fortran, Cython, F2PY, Template, # and Tempita sources. The required targets are automatically generated to # "lower" source files from their high-level representation to a file that the # compiler can accept. # # # add_python_extension( # SOURCES [source1 [source2 ...]] # [INCLUDE_DIRECTORIES [dir1 [dir2 ...]] # [LINK_LIBRARIES [lib1 [lib2 ...]] # [DEPENDS [source1 [source2 ...]]]) # # # Example usage # ^^^^^^^^^^^^^ # # .. code-block:: cmake # # find_package(PythonExtensions) # # file(GLOB arpack_sources ARPACK/SRC/*.f ARPACK/UTIL/*.f) # # add_python_extension(arpack_scipy # SOURCES ${arpack_sources} # ${g77_wrapper_sources} # INCLUDE_DIRECTORIES ARPACK/SRC # ) # # #============================================================================= # Copyright 2011 Kitware, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. #============================================================================= macro(_remove_whitespace _output) string(REGEX REPLACE "[ \r\n\t]+" " " ${_output} "${${_output}}") string(STRIP "${${_output}}" ${_output}) endmacro() function(add_python_library _name) set(options STATIC SHARED MODULE) set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) cmake_parse_arguments(_args "${options}" "" "${multiValueArgs}" ${ARGN} ) # Validate arguments to allow simpler debugging if(NOT _args_SOURCES) message( FATAL_ERROR "You have called add_python_library for library ${_name} without " "any source files. This typically indicates a problem with " "your CMakeLists.txt file" ) endif() # Initialize the list of sources set(_sources ${_args_SOURCES}) # Generate targets for all *.src files set(_processed ) foreach(_source IN LISTS _sources) if(${_source} MATCHES .pyf.src$ OR ${_source} MATCHES \\.f\\.src$) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.src Template files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) add_custom_command( OUTPUT ${_source_we} COMMAND ${NumPy_FROM_TEMPLATE_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} DEPENDS ${_source} ${_args_DEPENDS} COMMENT "Generating ${_source_we} from template ${_source}" ) list(APPEND _processed ${_source_we}) elseif(${_source} MATCHES \\.c\\.src$) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.src Template files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) add_custom_command( OUTPUT ${_source_we} COMMAND ${NumPy_CONV_TEMPLATE_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source_we} DEPENDS ${_source} ${_args_DEPENDS} COMMENT "Generating ${_source_we} from template ${_source}" ) list(APPEND _processed ${_source_we}) elseif(${_source} MATCHES \\.pyx\\.in$) if(NOT Cython_FOUND) message( FATAL_ERROR "Cython is required to process *.in Tempita files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _source_we ${_source}) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/${_source} ${CMAKE_CURRENT_BINARY_DIR}/${_source} COPYONLY ) set(_tempita_command " import os; import sys; from Cython.Tempita import Template; cwd = os.getcwd(); open(os.path.join(cwd, '${_source_we}'), 'w+') .write( Template.from_filename(os.path.join(cwd, '${_source}'), encoding=sys.getdefaultencoding()).substitute() ) " ) _remove_whitespace(_tempita_command) add_custom_command( OUTPUT ${_source_we} COMMAND ${PYTHON_EXECUTABLE} -c "${_tempita_command}" DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${_source}" ${_args_DEPENDS} ) list(APPEND _processed ${_source_we}) else() list(APPEND _processed ${_source}) endif() endforeach() set(_sources ${_processed}) # If we're building a Python extension and we're given only Fortran sources, # We can conclude that we need to generate a Fortran interface file list(FILTER _processed EXCLUDE REGEX "(\\.f|\\.f90)$") if(NOT _processed AND _args_MODULE) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.pyf F2PY files" ) endif() set(_sources_abs ) foreach(_source IN LISTS _sources) list(APPEND _sources_abs ${CMAKE_CURRENT_SOURCE_DIR}/${_source}) endforeach() add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_name}.pyf WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${F2PY_EXECUTABLE} ARGS -h ${_name}.pyf -m ${_name} --overwrite-signature ${_sources_abs} DEPENDS ${_sources} ${_args_DEPENDS} COMMENT "Generating ${_name} Fortan interface file" ) list(APPEND _sources ${_name}.pyf) endif() # Are there F2PY targets? set(_has_f2py_targets OFF) set(_has_cython_targets OFF) # Generate targets for all *.pyx and *.pyf files set(_processed ) foreach(_source IN LISTS _sources) if(${_source} MATCHES \\.pyx$) if(NOT Cython_FOUND) message( FATAL_ERROR "Cython is required to process *.pyx Cython files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _pyx_target_name ${_source}) set(_has_cython_targets ON) add_cython_target(${_pyx_target_name} ${_source} OUTPUT_VAR _pyx_target_output DEPENDS ${_args_DEPENDS} ) list(APPEND _processed ${_pyx_target_output}) elseif(${_source} MATCHES \\.pyf$) if(NOT NumPy_FOUND) message( FATAL_ERROR "NumPy is required to process *.pyf F2PY files" ) endif() string(REGEX REPLACE "\\.[^.]*$" "" _pyf_target_name ${_source}) set(_has_f2py_targets ON) add_f2py_target(${_pyf_target_name} ${_source} OUTPUT_VAR _pyf_target_output DEPENDS ${_args_DEPENDS} ) list(APPEND _processed ${_pyf_target_output}) else() list(APPEND _processed ${_source}) endif() endforeach() set(_sources ${_processed}) if(_args_SHARED) add_library(${_name} SHARED ${_sources}) elseif(_args_MODULE) add_library(${_name} MODULE ${_sources}) else() # Assume static add_library(${_name} STATIC ${_sources}) endif() target_include_directories(${_name} PRIVATE ${_args_INCLUDE_DIRECTORIES}) target_link_libraries(${_name} ${_args_LINK_LIBRARIES}) if(_has_f2py_targets) target_include_directories(${_name} PRIVATE ${F2PY_INCLUDE_DIRS}) target_link_libraries(${_name} ${F2PY_LIBRARIES}) endif() if(_args_COMPILE_DEFINITIONS) target_compile_definitions(${_name} PRIVATE ${_args_COMPILE_DEFINITIONS}) endif() if(_args_DEPENDS) add_custom_target( "${_name}_depends" DEPENDS ${_args_DEPENDS} ) add_dependencies(${_name} "${_name}_depends") endif() endfunction() function(add_python_extension _name) # FIXME: make sure that extensions with the same name can happen # in multiple directories set(multiValueArgs SOURCES INCLUDE_DIRECTORIES LINK_LIBRARIES COMPILE_DEFINITIONS DEPENDS) cmake_parse_arguments(_args "" "" "${multiValueArgs}" ${ARGN} ) # Validate arguments to allow simpler debugging if(NOT _args_SOURCES) message( FATAL_ERROR "You have called add_python_extension for library ${_name} without " "any source files. This typically indicates a problem with " "your CMakeLists.txt file" ) endif() add_python_library(${_name} MODULE SOURCES ${_args_SOURCES} INCLUDE_DIRECTORIES ${_args_INCLUDE_DIRECTORIES} LINK_LIBRARIES ${_args_LINK_LIBRARIES} COMPILE_DEFINITIONS ${_args_COMPILE_DEFINITIONS} DEPENDS ${_args_DEPENDS} ) python_extension_module(${_name}) file(RELATIVE_PATH _relative "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") install( TARGETS ${_name} LIBRARY DESTINATION "${_relative}" RUNTIME DESTINATION "${_relative}" ) endfunction()scikit-build-0.11.1/skbuild/resources/cmake/targetLinkLibrariesWithDynamicLookup.cmake000066400000000000000000000404351365474353700312310ustar00rootroot00000000000000#.rst: # # Public Functions # ^^^^^^^^^^^^^^^^ # # The following functions are defined: # # .. cmake:command:: target_link_libraries_with_dynamic_lookup # # :: # # target_link_libraries_with_dynamic_lookup( []) # # # Useful to "weakly" link a loadable module. For example, it should be used # when compiling a loadable module when the symbols should be resolve from # the run-time environment where the module is loaded, and not a specific # system library. # # Like proper linking, except that the given ```` are not necessarily # linked. Instead, the ```` is produced in a manner that allows for # symbols unresolved within it to be resolved at runtime, presumably by the # given ````. If such a target can be produced, the provided # ```` are not actually linked. # # It links a library to a target such that the symbols are resolved at # run-time not link-time. # # The linker is checked to see if it supports undefined # symbols when linking a shared library. If it does then the library # is not linked when specified with this function. # # On platforms that do not support weak-linking, this function works just # like ``target_link_libraries``. # # .. note:: # # For OSX it uses ``undefined dynamic_lookup``. This is similar to using # ``-shared`` on Linux where undefined symbols are ignored. # # For more details, see `blog `_ # from Tim D. Smith. # # # .. cmake:command:: check_dynamic_lookup # # Check if the linker requires a command line flag to allow leaving symbols # unresolved when producing a target of type ```` that is # weakly-linked against a dependency of type ````. # # ```` # can be one of "STATIC", "SHARED", "MODULE", or "EXE". # # ```` # can be one of "STATIC", "SHARED", or "MODULE". # # Long signature: # # :: # # check_dynamic_lookup( # # # []) # # # Short signature: # # :: # # check_dynamic_lookup() # set to "MODULE" # # set to "SHARED" # # # The result is cached between invocations and recomputed only when the value # of CMake's linker flag list changes; ``CMAKE_STATIC_LINKER_FLAGS`` if # ```` is "STATIC", and ``CMAKE_SHARED_LINKER_FLAGS`` otherwise. # # # Defined variables: # # ```` # Whether the current C toolchain supports weak-linking for target binaries of # type ```` that are weakly-linked against a dependency target of # type ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # # ``HAS_DYNAMIC_LOOKUP__`` # Cached, global alias for ```` # # ``DYNAMIC_LOOKUP_FLAGS__`` # Cached, global alias for ```` # # # Private Functions # ^^^^^^^^^^^^^^^^^ # # The following private functions are defined: # # .. warning:: These functions are not part of the scikit-build API. They # exist purely as an implementation detail and may change from version # to version without notice, or even be removed. # # We mean it. # # # .. cmake:command:: _get_target_type # # :: # # _get_target_type( ) # # # Shorthand for querying an abbreviated version of the target type # of the given ````. # # ```` is set to: # # - "STATIC" for a STATIC_LIBRARY, # - "SHARED" for a SHARED_LIBRARY, # - "MODULE" for a MODULE_LIBRARY, # - and "EXE" for an EXECUTABLE. # # Defined variables: # # ```` # The abbreviated version of the ````'s type. # # # .. cmake:command:: _test_weak_link_project # # :: # # _test_weak_link_project( # # # ) # # # Attempt to compile and run a test project where a target of type # ```` is weakly-linked against a dependency of type ````: # # - ```` can be one of "STATIC", "SHARED", "MODULE", or "EXE". # - ```` can be one of "STATIC", "SHARED", or "MODULE". # # Defined variables: # # ```` # Whether the current C toolchain can produce a working target binary of type # ```` that is weakly-linked against a dependency target of type # ````. # # ```` # List of flags to add to the linker command to produce a working target # binary of type ```` that is weakly-linked against a dependency # target of type ````. # function(_get_target_type result_var target) set(target_type "SHARED_LIBRARY") if(TARGET ${target}) get_property(target_type TARGET ${target} PROPERTY TYPE) endif() set(result "STATIC") if(target_type STREQUAL "STATIC_LIBRARY") set(result "STATIC") endif() if(target_type STREQUAL "SHARED_LIBRARY") set(result "SHARED") endif() if(target_type STREQUAL "MODULE_LIBRARY") set(result "MODULE") endif() if(target_type STREQUAL "EXECUTABLE") set(result "EXE") endif() set(${result_var} ${result} PARENT_SCOPE) endfunction() function(_test_weak_link_project target_type lib_type can_weak_link_var project_name) set(gnu_ld_ignore "-Wl,--unresolved-symbols=ignore-all") set(osx_dynamic_lookup "-undefined dynamic_lookup") set(no_flag "") foreach(link_flag_spec gnu_ld_ignore osx_dynamic_lookup no_flag) set(link_flag "${${link_flag_spec}}") set(test_project_dir "${PROJECT_BINARY_DIR}/CMakeTmp") set(test_project_dir "${test_project_dir}/${project_name}") set(test_project_dir "${test_project_dir}/${link_flag_spec}") set(test_project_dir "${test_project_dir}/${target_type}") set(test_project_dir "${test_project_dir}/${lib_type}") set(test_project_src_dir "${test_project_dir}/src") set(test_project_bin_dir "${test_project_dir}/build") file(MAKE_DIRECTORY ${test_project_src_dir}) file(MAKE_DIRECTORY ${test_project_bin_dir}) set(mod_type "STATIC") set(link_mod_lib TRUE) set(link_exe_lib TRUE) set(link_exe_mod FALSE) if("${target_type}" STREQUAL "EXE") set(link_exe_lib FALSE) set(link_exe_mod TRUE) else() set(mod_type "${target_type}") endif() if("${mod_type}" STREQUAL "MODULE") set(link_mod_lib FALSE) endif() file(WRITE "${test_project_src_dir}/CMakeLists.txt" " cmake_minimum_required(VERSION ${CMAKE_VERSION}) project(${project_name} C) include_directories(${test_project_src_dir}) add_library(number ${lib_type} number.c) add_library(counter ${mod_type} counter.c) ") if("${mod_type}" STREQUAL "MODULE") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES PREFIX \"\") ") endif() if(link_mod_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(counter number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " set_target_properties(counter PROPERTIES LINK_FLAGS \"${link_flag}\") ") endif() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " add_executable(main main.c) ") if(link_exe_lib) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main number) ") elseif(NOT link_flag STREQUAL "") file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main \"${link_flag}\") ") endif() if(link_exe_mod) file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main counter) ") else() file(APPEND "${test_project_src_dir}/CMakeLists.txt" " target_link_libraries(main \"${CMAKE_DL_LIBS}\") ") endif() file(WRITE "${test_project_src_dir}/number.c" " #include static int _number; void set_number(int number) { _number = number; } int get_number() { return _number; } ") file(WRITE "${test_project_src_dir}/number.h" " #ifndef _NUMBER_H #define _NUMBER_H extern void set_number(int); extern int get_number(void); #endif ") file(WRITE "${test_project_src_dir}/counter.c" " #include int count() { int result = get_number(); set_number(result + 1); return result; } ") file(WRITE "${test_project_src_dir}/counter.h" " #ifndef _COUNTER_H #define _COUNTER_H extern int count(void); #endif ") file(WRITE "${test_project_src_dir}/main.c" " #include #include #include ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " #include ") endif() file(APPEND "${test_project_src_dir}/main.c" " int my_count() { int result = get_number(); set_number(result + 1); return result; } int main(int argc, char **argv) { int result; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " void *counter_module; int (*count)(void); counter_module = dlopen(\"./counter.so\", RTLD_LAZY | RTLD_GLOBAL); if(!counter_module) goto error; count = dlsym(counter_module, \"count\"); if(!count) goto error; ") endif() file(APPEND "${test_project_src_dir}/main.c" " result = count() != 0 ? EXIT_FAILURE : my_count() != 1 ? EXIT_FAILURE : my_count() != 2 ? EXIT_FAILURE : count() != 3 ? EXIT_FAILURE : count() != 4 ? EXIT_FAILURE : count() != 5 ? EXIT_FAILURE : my_count() != 6 ? EXIT_FAILURE : EXIT_SUCCESS; ") if(NOT link_exe_mod) file(APPEND "${test_project_src_dir}/main.c" " goto done; error: fprintf(stderr, \"Error occured:\\n %s\\n\", dlerror()); result = 1; done: if(counter_module) dlclose(counter_module); ") endif() file(APPEND "${test_project_src_dir}/main.c" " return result; } ") set(_rpath_arg) if(APPLE AND ${CMAKE_VERSION} VERSION_GREATER 2.8.11) set(_rpath_arg "-DCMAKE_MACOSX_RPATH='${CMAKE_MACOSX_RPATH}'") endif() try_compile(project_compiles "${test_project_bin_dir}" "${test_project_src_dir}" "${project_name}" CMAKE_FLAGS "-DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS}'" "-DCMAKE_ENABLE_EXPORTS=ON" ${_rpath_arg} OUTPUT_VARIABLE compile_output) set(project_works 1) set(run_output) if(project_compiles) execute_process(COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} "${test_project_bin_dir}/main" WORKING_DIRECTORY "${test_project_bin_dir}" RESULT_VARIABLE project_works OUTPUT_VARIABLE run_output ERROR_VARIABLE run_output) endif() set(test_description "Weak Link ${target_type} -> ${lib_type} (${link_flag_spec})") if(project_works EQUAL 0) set(project_works TRUE) message(STATUS "Performing Test ${test_description} - Success") else() set(project_works FALSE) message(STATUS "Performing Test ${test_description} - Failed") file(APPEND ${CMAKE_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/CMakeError.log "Performing Test ${test_description} failed with the " "following output:\n" "BUILD\n-----\n${compile_output}\nRUN\n---\n${run_output}\n") endif() set(${can_weak_link_var} ${project_works} PARENT_SCOPE) if(project_works) set(${project_name} ${link_flag} PARENT_SCOPE) break() endif() endforeach() endfunction() function(check_dynamic_lookup) # Two signatures are supported: if(ARGC EQUAL "1") # # check_dynamic_lookup() # set(target_type "MODULE") set(lib_type "SHARED") set(has_dynamic_lookup_var "${ARGV0}") set(link_flags_var "unused") elseif(ARGC GREATER "2") # # check_dynamic_lookup( # # # []) # set(target_type "${ARGV0}") set(lib_type "${ARGV1}") set(has_dynamic_lookup_var "${ARGV2}") if(ARGC EQUAL "3") set(link_flags_var "unused") else() set(link_flags_var "${ARGV3}") endif() else() message(FATAL_ERROR "missing arguments") endif() _check_dynamic_lookup( ${target_type} ${lib_type} ${has_dynamic_lookup_var} ${link_flags_var} ) set(${has_dynamic_lookup_var} ${${has_dynamic_lookup_var}} PARENT_SCOPE) if(NOT "x${link_flags_var}x" STREQUAL "xunusedx") set(${link_flags_var} ${${link_flags_var}} PARENT_SCOPE) endif() endfunction() function(_check_dynamic_lookup target_type lib_type has_dynamic_lookup_var link_flags_var ) # hash the CMAKE_FLAGS passed and check cache to know if we need to rerun if("${target_type}" STREQUAL "STATIC") string(MD5 cmake_flags_hash "${CMAKE_STATIC_LINKER_FLAGS}") else() string(MD5 cmake_flags_hash "${CMAKE_SHARED_LINKER_FLAGS}") endif() set(cache_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}") set(cache_hash_var "HAS_DYNAMIC_LOOKUP_${target_type}_${lib_type}_hash") set(result_var "DYNAMIC_LOOKUP_FLAGS_${target_type}_${lib_type}") if( NOT DEFINED ${cache_hash_var} OR NOT "${${cache_hash_var}}" STREQUAL "${cmake_flags_hash}") unset(${cache_var} CACHE) endif() if(NOT DEFINED ${cache_var}) set(skip_test FALSE) if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) set(skip_test TRUE) endif() if(skip_test) set(has_dynamic_lookup FALSE) set(link_flags) else() _test_weak_link_project(${target_type} ${lib_type} has_dynamic_lookup link_flags) endif() set(caveat " (when linking ${target_type} against ${lib_type})") set(${cache_var} "${has_dynamic_lookup}" CACHE BOOL "linker supports dynamic lookup for undefined symbols${caveat}") mark_as_advanced(${cache_var}) set(${result_var} "${link_flags}" CACHE STRING "linker flags for dynamic lookup${caveat}") mark_as_advanced(${result_var}) set(${cache_hash_var} "${cmake_flags_hash}" CACHE INTERNAL "hashed flags for ${cache_var} check") endif() set(${has_dynamic_lookup_var} "${${cache_var}}" PARENT_SCOPE) set(${link_flags_var} "${${result_var}}" PARENT_SCOPE) endfunction() function(target_link_libraries_with_dynamic_lookup target) _get_target_type(target_type ${target}) set(link_props) set(link_items) set(link_libs) foreach(lib ${ARGN}) _get_target_type(lib_type ${lib}) check_dynamic_lookup(${target_type} ${lib_type} has_dynamic_lookup dynamic_lookup_flags) if(has_dynamic_lookup) if(dynamic_lookup_flags) if("${target_type}" STREQUAL "EXE") list(APPEND link_items "${dynamic_lookup_flags}") else() list(APPEND link_props "${dynamic_lookup_flags}") endif() endif() elseif(${lib} MATCHES "(debug|optimized|general)") # See gh-255 else() list(APPEND link_libs "${lib}") endif() endforeach() if(link_props) list(REMOVE_DUPLICATES link_props) endif() if(link_items) list(REMOVE_DUPLICATES link_items) endif() if(link_libs) list(REMOVE_DUPLICATES link_libs) endif() if(link_props) set_target_properties(${target} PROPERTIES LINK_FLAGS "${link_props}") endif() set(links "${link_items}" "${link_libs}") if(links) target_link_libraries(${target} "${links}") endif() endfunction() scikit-build-0.11.1/skbuild/setuptools_wrap.py000066400000000000000000001114111365474353700214560ustar00rootroot00000000000000"""This module provides functionality for wrapping key infrastructure components from distutils and setuptools. """ from __future__ import print_function import copy import os import os.path import sys import argparse import json import platform import stat from contextlib import contextmanager from distutils.errors import (DistutilsArgError, DistutilsError, DistutilsGetoptError) from glob import glob from shutil import copyfile, copymode from packaging.requirements import Requirement from packaging.version import parse as parse_version # XXX If 'six' becomes a dependency, use 'six.StringIO' instead. try: from StringIO import StringIO except ImportError: from io import StringIO try: from shutil import which except ImportError: from .compat import which from setuptools import setup as upstream_setup from setuptools.dist import Distribution as upstream_Distribution from . import cmaker from .command import (build, build_ext, build_py, clean, install, install_lib, install_scripts, bdist, bdist_wheel, egg_info, sdist, generate_source_manifest, test) from .constants import (CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR, CMAKE_SPEC_FILE, set_skbuild_plat_name, skbuild_plat_name) from .exceptions import SKBuildError, SKBuildGeneratorNotFoundError from .utils import (mkdir_p, parse_manifestin, PythonModuleFinder, to_platform_path, to_unix_path) def create_skbuild_argparser(): """Create and return a scikit-build argument parser. """ parser = argparse.ArgumentParser(add_help=False) parser.add_argument( '--build-type', default='Release', metavar='', help='specify the CMake build type (e.g. Debug or Release)') parser.add_argument( '-G', '--generator', metavar='', help='specify the CMake build system generator') parser.add_argument( '-j', metavar='N', type=int, dest='jobs', help='allow N build jobs at once') parser.add_argument( '--cmake-executable', default=None, metavar='', help='specify the path to the cmake executable' ) parser.add_argument( '--skip-generator-test', action='store_true', help='skip generator test when a generator is explicitly selected using --generator' ) return parser def _is_cmake_configure_argument(arg): """Return True if ``arg`` is a relevant argument to pass to cmake when configuring a project.""" for cmake_arg in ( '-C', # initial-cache '-D', # [:]= ): if arg.startswith(cmake_arg): return True return False def parse_skbuild_args(args, cmake_args, build_tool_args): """ Parse arguments in the scikit-build argument set. Convert specified arguments to proper format and append to cmake_args and build_tool_args. Returns the tuple ``(remaining arguments, cmake executable, skip_generator_test)``. """ parser = create_skbuild_argparser() # Consider CMake arguments passed as global setuptools options cmake_args.extend([arg for arg in args if _is_cmake_configure_argument(arg)]) # ... and remove them from the list args = [arg for arg in args if not _is_cmake_configure_argument(arg)] namespace, remaining_args = parser.parse_known_args(args) # Construct CMake argument list cmake_args.append('-DCMAKE_BUILD_TYPE:STRING=' + namespace.build_type) if namespace.generator is not None: cmake_args.extend(['-G', namespace.generator]) # Construct build tool argument list build_tool_args.extend(['--config', namespace.build_type]) if namespace.jobs is not None: build_tool_args.extend(['-j', str(namespace.jobs)]) if namespace.generator is None and namespace.skip_generator_test is True: sys.exit("ERROR: Specifying --skip-generator-test requires --generator to also be specified.") return remaining_args, namespace.cmake_executable, namespace.skip_generator_test def parse_args(): """This function parses the command-line arguments ``sys.argv`` and returns the tuple ``(setuptools_args, cmake_executable, skip_generator_test, cmake_args, build_tool_args)`` where each ``*_args`` element corresponds to a set of arguments separated by ``--``.""" dutils = [] cmake = [] make = [] argsets = [dutils, cmake, make] i = 0 separator = '--' for arg in sys.argv: if arg == separator: i += 1 if i >= len(argsets): sys.exit( "ERROR: Too many \"{}\" separators provided " "(expected at most {}).".format(separator, len(argsets) - 1)) else: argsets[i].append(arg) dutils, cmake_executable, skip_generator_test = parse_skbuild_args(dutils, cmake, make) return dutils, cmake_executable, skip_generator_test, cmake, make @contextmanager def _capture_output(): oldout, olderr = sys.stdout, sys.stderr try: out = [StringIO(), StringIO()] sys.stdout, sys.stderr = out yield out finally: sys.stdout, sys.stderr = oldout, olderr out[0] = out[0].getvalue() out[1] = out[1].getvalue() def _parse_setuptools_arguments(setup_attrs): """This function instantiates a Distribution object and parses the command line arguments. It returns the tuple ``(display_only, help_commands, commands, hide_listing, force_cmake, skip_cmake, plat_name)`` where - display_only is a boolean indicating if an argument like '--help', '--help-commands' or '--author' was passed. - help_commands is a boolean indicating if argument '--help-commands' was passed. - commands contains the list of commands that were passed. - hide_listing is a boolean indicating if the list of files being included in the distribution is displayed or not. - force_cmake a boolean indicating that CMake should always be executed. - skip_cmake is a boolean indicating if the execution of CMake should explicitly be skipped. - plat_name is a string identifying the platform name to embed in generated filenames. It defaults to :func:`skbuild.constants.skbuild_plat_name()`. - build_ext_inplace is a boolean indicating if ``build_ext`` command was specified along with the --inplace argument. Otherwise it raises DistutilsArgError exception if there are any error on the command-line, and it raises DistutilsGetoptError if there any error in the command 'options' attribute. The code has been adapted from the setup() function available in distutils/core.py. """ setup_attrs = dict(setup_attrs) setup_attrs['script_name'] = os.path.basename(sys.argv[0]) dist = upstream_Distribution(setup_attrs) # Update class attribute to also ensure the argument is processed # when ``upstream_setup`` is called. # pylint:disable=no-member upstream_Distribution.global_options.extend([ ('hide-listing', None, "do not display list of files being " "included in the distribution"), ('force-cmake', None, "always run CMake"), ('skip-cmake', None, "do not run CMake"), ]) # Find and parse the config file(s): they will override options from # the setup script, but be overridden by the command line. dist.parse_config_files() # Parse the command line and override config files; any # command-line errors are the end user's fault, so turn them into # SystemExit to suppress tracebacks. with _capture_output(): result = dist.parse_command_line() display_only = not result if not hasattr(dist, 'hide_listing'): dist.hide_listing = False if not hasattr(dist, 'force_cmake'): dist.force_cmake = False if not hasattr(dist, 'skip_cmake'): dist.skip_cmake = False plat_names = set() for cmd in [dist.get_command_obj(command) for command in dist.commands]: if getattr(cmd, 'plat_name', None) is not None: plat_names.add(cmd.plat_name) if not plat_names: plat_names.add(None) elif len(plat_names) > 1: raise SKBuildError( "--plat-name is ambiguous: %s" % ", ".join(plat_names)) plat_name = list(plat_names)[0] build_ext_inplace = dist.get_command_obj('build_ext').inplace return (display_only, dist.help_commands, dist.commands, dist.hide_listing, dist.force_cmake, dist.skip_cmake, plat_name, build_ext_inplace) def _check_skbuild_parameters(skbuild_kw): cmake_install_dir = skbuild_kw['cmake_install_dir'] if os.path.isabs(cmake_install_dir): raise SKBuildError(( "\n setup parameter 'cmake_install_dir' is set to " "an absolute path. A relative path is expected.\n" " Project Root : {}\n" " CMake Install Directory: {}\n").format( os.getcwd(), cmake_install_dir )) cmake_source_dir = skbuild_kw['cmake_source_dir'] if not os.path.exists(os.path.abspath(cmake_source_dir)): raise SKBuildError(( "\n setup parameter 'cmake_source_dir' set to " "a nonexistent directory.\n" " Project Root : {}\n" " CMake Source Directory: {}\n").format( os.getcwd(), cmake_source_dir )) def strip_package(package_parts, module_file): """Given ``package_parts`` (e.g. ``['foo', 'bar']``) and a ``module_file`` (e.g. ``foo/bar/jaz/rock/roll.py``), starting from the left, this function will strip the parts of the path matching the package parts and return a new string (e.g ``jaz/rock/roll.py``). The function will work as expected for either Windows or Unix-style ``module_file`` and this independently of the platform. """ if not package_parts or os.path.isabs(module_file): return module_file package = "/".join(package_parts) module_dir = os.path.dirname(module_file.replace("\\", "/")) module_dir = module_dir[:len(package)] return (module_file[len(package) + 1:] if package != "" and module_dir.startswith(package) else module_file) def _package_data_contain_module(module, package_data): """Return True if the ``module`` is contained in the ``package_data``. ``module`` is a tuple of the form ``(package, modulename, module_file)``. """ (package, _, module_file) = module if package not in package_data: return False # We need to strip the package because a module entry # usually looks like this: # # ('foo.bar', 'module', 'foo/bar/module.py') # # and the entry in package_data would look like this: # # {'foo.bar' : ['module.py']} if (strip_package(package.split("."), module_file) in package_data[package]): return True return False def _should_run_cmake(commands, cmake_with_sdist): """Return True if at least one command requiring ``cmake`` to run is found in ``commands``.""" for expected_command in [ "build", "build_ext", "develop", "install", "install_lib", "bdist", "bdist_dumb", "bdist_egg", "bdist_rpm", "bdist_wininst", "bdist_wheel", "test", ]: if expected_command in commands: return True if "sdist" in commands and cmake_with_sdist: return True return False def _save_cmake_spec(args): """Save the CMake spec to disk""" # We use JSON here because readability is more important than performance try: os.makedirs(os.path.dirname(CMAKE_SPEC_FILE())) except OSError: pass with open(CMAKE_SPEC_FILE(), 'w+') as fp: json.dump(args, fp) def _load_cmake_spec(): """Load and return the CMake spec from disk""" try: with open(CMAKE_SPEC_FILE()) as fp: return json.load(fp) except (OSError, IOError, ValueError): return None # pylint:disable=too-many-locals, too-many-branches def setup(*args, **kw): # noqa: C901 """This function wraps setup() so that we can run cmake, make, CMake build, then proceed as usual with setuptools, appending the CMake-generated output as necessary. The CMake project is re-configured only if needed. This is achieved by (1) retrieving the environment mapping associated with the generator set in the ``CMakeCache.txt`` file, (2) saving the CMake configure arguments and version in :func:`skbuild.constants.CMAKE_SPEC_FILE()`: and (3) re-configuring only if either the generator or the CMake specs change. """ sys.argv, cmake_executable, skip_generator_test, cmake_args, make_args = parse_args() # work around https://bugs.python.org/issue1011113 # (patches provided, but no updates since 2014) cmdclass = kw.get('cmdclass', {}) cmdclass['build'] = cmdclass.get('build', build.build) cmdclass['build_py'] = cmdclass.get('build_py', build_py.build_py) cmdclass['build_ext'] = cmdclass.get('build_ext', build_ext.build_ext) cmdclass['install'] = cmdclass.get('install', install.install) cmdclass['install_lib'] = cmdclass.get('install_lib', install_lib.install_lib) cmdclass['install_scripts'] = cmdclass.get('install_scripts', install_scripts.install_scripts) cmdclass['clean'] = cmdclass.get('clean', clean.clean) cmdclass['sdist'] = cmdclass.get('sdist', sdist.sdist) cmdclass['bdist'] = cmdclass.get('bdist', bdist.bdist) cmdclass['bdist_wheel'] = cmdclass.get( 'bdist_wheel', bdist_wheel.bdist_wheel) cmdclass['egg_info'] = cmdclass.get('egg_info', egg_info.egg_info) cmdclass['generate_source_manifest'] = cmdclass.get( 'generate_source_manifest', generate_source_manifest.generate_source_manifest) cmdclass['test'] = cmdclass.get('test', test.test) kw['cmdclass'] = cmdclass # Extract setup keywords specific to scikit-build and remove them from kw. # Removing the keyword from kw need to be done here otherwise, the # following call to _parse_setuptools_arguments would complain about # unknown setup options. parameters = { 'cmake_args': [], 'cmake_install_dir': '', 'cmake_source_dir': '', 'cmake_with_sdist': False, 'cmake_languages': ('C', 'CXX'), 'cmake_minimum_required_version': None } skbuild_kw = {param: kw.pop(param, parameters[param]) for param in parameters} # ... and validate them try: _check_skbuild_parameters(skbuild_kw) except SKBuildError as ex: import traceback print("Traceback (most recent call last):") traceback.print_tb(sys.exc_info()[2]) print('') sys.exit(ex) # Convert source dir to a path relative to the root # of the project cmake_source_dir = skbuild_kw['cmake_source_dir'] if cmake_source_dir == ".": cmake_source_dir = "" if os.path.isabs(cmake_source_dir): cmake_source_dir = os.path.relpath(cmake_source_dir) # Skip running CMake in the following cases: # * flag "--skip-cmake" is provided # * "display only" argument is provided (e.g '--help', '--author', ...) # * no command-line arguments or invalid ones are provided # * no command requiring cmake is provided # * no CMakeLists.txt if found display_only = has_invalid_arguments = help_commands = False force_cmake = skip_cmake = False commands = [] try: (display_only, help_commands, commands, hide_listing, force_cmake, skip_cmake, plat_name, build_ext_inplace) = \ _parse_setuptools_arguments(kw) except (DistutilsArgError, DistutilsGetoptError): has_invalid_arguments = True has_cmakelists = os.path.exists( os.path.join(cmake_source_dir, "CMakeLists.txt")) if not has_cmakelists: print('skipping skbuild (no CMakeLists.txt found)') skip_skbuild = (display_only or has_invalid_arguments or not _should_run_cmake(commands, skbuild_kw["cmake_with_sdist"]) or not has_cmakelists) if skip_skbuild and not force_cmake: if help_commands: # Prepend scikit-build help. Generate option descriptions using # argparse. skbuild_parser = create_skbuild_argparser() arg_descriptions = [ line for line in skbuild_parser.format_help().split('\n') if line.startswith(' ') ] print('scikit-build options:') print('\n'.join(arg_descriptions)) print('') print('Arguments following a "--" are passed directly to CMake ' '(e.g. -DMY_VAR:BOOL=TRUE).') print('Arguments following a second "--" are passed directly to ' ' the build tool.') print('') return upstream_setup(*args, **kw) developer_mode = "develop" in commands or "test" in commands or build_ext_inplace packages = kw.get('packages', []) package_dir = kw.get('package_dir', {}) package_data = copy.deepcopy(kw.get('package_data', {})) py_modules = kw.get('py_modules', []) new_py_modules = {py_module: False for py_module in py_modules} scripts = kw.get('scripts', []) new_scripts = {script: False for script in scripts} data_files = { (parent_dir or '.'): set(file_list) for parent_dir, file_list in kw.get('data_files', []) } # Since CMake arguments provided through the command line have more # weight and when CMake is given multiple times a argument, only the last # one is considered, let's prepend the one provided in the setup call. cmake_args = skbuild_kw['cmake_args'] + cmake_args if sys.platform == 'darwin': # If no ``--plat-name`` argument was passed, set default value. if plat_name is None: plat_name = skbuild_plat_name() (_, version, machine) = plat_name.split('-') # The loop here allows for CMAKE_OSX_* command line arguments to overload # values passed with either the ``--plat-name`` command-line argument # or the ``cmake_args`` setup option. for cmake_arg in cmake_args: if 'CMAKE_OSX_DEPLOYMENT_TARGET' in cmake_arg: version = cmake_arg.split('=')[1] if 'CMAKE_OSX_ARCHITECTURES' in cmake_arg: machine = cmake_arg.split('=')[1] set_skbuild_plat_name("macosx-{}-{}".format(version, machine)) # Set platform env. variable so that commands (e.g. bdist_wheel) # uses this information. The _PYTHON_HOST_PLATFORM env. variable is # used in distutils.util.get_platform() function. os.environ['_PYTHON_HOST_PLATFORM'] = skbuild_plat_name() # Set CMAKE_OSX_DEPLOYMENT_TARGET and CMAKE_OSX_ARCHITECTURES if not already # specified (_, version, machine) = skbuild_plat_name().split('-') if not cmaker.has_cmake_cache_arg( cmake_args, 'CMAKE_OSX_DEPLOYMENT_TARGET'): cmake_args.append( '-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=%s' % version ) if not cmaker.has_cmake_cache_arg( cmake_args, 'CMAKE_OSX_ARCHITECTURES'): cmake_args.append( '-DCMAKE_OSX_ARCHITECTURES:STRING=%s' % machine ) # Install cmake if listed in `setup_requires` for package in kw.get('setup_requires', []): if Requirement(package).name == 'cmake': setup_requires = [package] dist = upstream_Distribution({'setup_requires': setup_requires}) dist.fetch_build_eggs(setup_requires) # Considering packages associated with "setup_requires" keyword are # installed in .eggs subdirectory without honoring setuptools "console_scripts" # entry_points and without settings the expected executable permissions, we are # taking care of it below. import cmake for executable in ['cmake', 'cpack', 'ctest']: executable = os.path.join(cmake.CMAKE_BIN_DIR, executable) if platform.system().lower() == 'windows': executable += '.exe' st = os.stat(executable) permissions = ( st.st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH ) os.chmod(executable, permissions) cmake_executable = os.path.join(cmake.CMAKE_BIN_DIR, 'cmake') break # Languages are used to determine a working generator cmake_languages = skbuild_kw['cmake_languages'] try: if cmake_executable is None: cmake_executable = CMAKE_DEFAULT_EXECUTABLE cmkr = cmaker.CMaker(cmake_executable) if not skip_cmake: cmake_minimum_required_version = skbuild_kw['cmake_minimum_required_version'] if cmake_minimum_required_version is not None: if parse_version(cmkr.cmake_version) < parse_version(cmake_minimum_required_version): raise SKBuildError( "CMake version %s or higher is required. CMake version %s is being used" % ( cmake_minimum_required_version, cmkr.cmake_version)) # Used to confirm that the cmake executable is the same, and that the environment # didn't change cmake_spec = { 'args': [which(CMAKE_DEFAULT_EXECUTABLE)] + cmake_args, 'version': cmkr.cmake_version, 'environment': { 'PYTHONNOUSERSITE': os.environ.get("PYTHONNOUSERSITE"), 'PYTHONPATH': os.environ.get("PYTHONPATH") } } # skip the configure step for a cached build env = cmkr.get_cached_generator_env() if env is None or cmake_spec != _load_cmake_spec(): env = cmkr.configure(cmake_args, skip_generator_test=skip_generator_test, cmake_source_dir=cmake_source_dir, cmake_install_dir=skbuild_kw['cmake_install_dir'], languages=cmake_languages ) _save_cmake_spec(cmake_spec) cmkr.make(make_args, env=env) except SKBuildGeneratorNotFoundError as ex: sys.exit(ex) except SKBuildError as ex: import traceback print("Traceback (most recent call last):") traceback.print_tb(sys.exc_info()[2]) print('') sys.exit(ex) # If any, strip ending slash from each package directory package_dir = {package: prefix[:-1] if prefix[-1] == "/" else prefix for package, prefix in package_dir.items()} # If needed, set reasonable defaults for package_dir for package in packages: if package not in package_dir: package_dir[package] = package.replace(".", "/") if '' in package_dir: package_dir[package] = to_unix_path(os.path.join(package_dir[''], package_dir[package])) package_prefixes = _collect_package_prefixes(package_dir, packages) # This hook enables custom processing of the cmake manifest cmake_manifest = cmkr.install() process_manifest = kw.get('cmake_process_manifest_hook') if process_manifest is not None: if callable(process_manifest): cmake_manifest = process_manifest(cmake_manifest) else: raise SKBuildError('The cmake_process_manifest_hook argument should be callable.') _classify_installed_files(cmake_manifest, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files, cmake_source_dir, skbuild_kw['cmake_install_dir']) original_manifestin_data_files = [] if kw.get("include_package_data", False): original_manifestin_data_files = parse_manifestin(os.path.join(os.getcwd(), "MANIFEST.in")) for path in original_manifestin_data_files: _classify_file(path, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files) if developer_mode: # Copy packages for package, package_file_list in package_data.items(): for package_file in package_file_list: package_file = os.path.join(package_dir[package], package_file) cmake_file = os.path.join(CMAKE_INSTALL_DIR(), package_file) if os.path.exists(cmake_file): _copy_file(cmake_file, package_file, hide_listing) # Copy modules for py_module in py_modules: package_file = py_module + ".py" cmake_file = os.path.join(CMAKE_INSTALL_DIR(), package_file) if os.path.exists(cmake_file): _copy_file(cmake_file, package_file, hide_listing) else: _consolidate_package_modules( cmake_source_dir, packages, package_dir, py_modules, package_data, hide_listing) original_package_data = kw.get('package_data', {}).copy() _consolidate_package_data_files(original_package_data, package_prefixes, hide_listing) for data_file in original_manifestin_data_files: dest_data_file = os.path.join(CMAKE_INSTALL_DIR(), data_file) _copy_file(data_file, dest_data_file, hide_listing) kw['package_data'] = package_data kw['package_dir'] = { package: ( os.path.join(CMAKE_INSTALL_DIR(), prefix) if os.path.exists(os.path.join(CMAKE_INSTALL_DIR(), prefix)) else prefix) for prefix, package in package_prefixes } kw['scripts'] = [ os.path.join(CMAKE_INSTALL_DIR(), script) if mask else script for script, mask in new_scripts.items() ] kw['data_files'] = [ (parent_dir, list(file_set)) for parent_dir, file_set in data_files.items() ] if 'zip_safe' not in kw: kw['zip_safe'] = False # Adapted from espdev/ITKPythonInstaller/setup.py.in # pylint: disable=missing-docstring class BinaryDistribution(upstream_Distribution): def has_ext_modules(self): # pylint: disable=no-self-use return has_cmakelists kw['distclass'] = BinaryDistribution print("") return upstream_setup(*args, **kw) def _collect_package_prefixes(package_dir, packages): """ Collect the list of prefixes for all packages The list is used to match paths in the install manifest to packages specified in the setup.py script. The list is sorted in decreasing order of prefix length so that paths are matched with their immediate parent package, instead of any of that package's ancestors. For example, consider the project structure below. Assume that the setup call was made with a package list featuring "top" and "top.bar", but not "top.not_a_subpackage". :: top/ -> top/ __init__.py -> top/__init__.py (parent: top) foo.py -> top/foo.py (parent: top) bar/ -> top/bar/ (parent: top) __init__.py -> top/bar/__init__.py (parent: top.bar) not_a_subpackage/ -> top/not_a_subpackage/ (parent: top) data_0.txt -> top/not_a_subpackage/data_0.txt (parent: top) data_1.txt -> top/not_a_subpackage/data_1.txt (parent: top) The paths in the generated install manifest are matched to packages according to the parents indicated on the right. Only packages that are specified in the setup() call are considered. Because of the sort order, the data files on the bottom would have been mapped to "top.not_a_subpackage" instead of "top", proper -- had such a package been specified. """ return list(sorted( ( (package_dir[package].replace('.', '/'), package) for package in packages ), key=lambda tup: len(tup[0]), reverse=True )) def _classify_installed_files(install_paths, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files, cmake_source_dir, cmake_install_dir): assert not os.path.isabs(cmake_source_dir) assert cmake_source_dir != "." install_root = os.path.join(os.getcwd(), CMAKE_INSTALL_DIR()) for path in install_paths: # if this installed file is not within the project root, complain and # exit if not to_platform_path(path).startswith(CMAKE_INSTALL_DIR()): raise SKBuildError(( "\n CMake-installed files must be within the project root.\n" " Project Root : {}\n" " Violating File: {}\n").format( install_root, to_platform_path(path))) # peel off the 'skbuild' prefix path = to_unix_path(os.path.relpath(path, CMAKE_INSTALL_DIR())) _classify_file(path, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files) def _classify_file(path, package_data, package_prefixes, py_modules, new_py_modules, scripts, new_scripts, data_files): found_package = False found_module = False found_script = False path = to_unix_path(path) # check to see if path is part of a package for prefix, package in package_prefixes: if path.startswith(prefix + "/"): # peel off the package prefix path = to_unix_path(os.path.relpath(path, prefix)) package_file_list = package_data.get(package, []) package_file_list.append(path) package_data[package] = package_file_list found_package = True break if found_package: return # If control reaches this point, then this installed file is not part of # a package. # check if path is a module for module in py_modules: if path.replace("/", ".") == ".".join((module, "py")): new_py_modules[module] = True found_module = True break if found_module: return # If control reaches this point, then this installed file is not a # module # if the file is a script, mark the corresponding script for script in scripts: if path == script: new_scripts[script] = True found_script = True break if found_script: return # If control reaches this point, then this installed file is not a # script # If control reaches this point, then we have installed files that are # not part of a package, not a module, nor a script. Without any other # information, we can only treat it as a generic data file. parent_dir = os.path.dirname(path) file_set = data_files.get(parent_dir) if file_set is None: file_set = set() data_files[parent_dir] = file_set file_set.add(os.path.join(CMAKE_INSTALL_DIR(), path)) def _copy_file(src_file, dest_file, hide_listing=True): """Copy ``src_file`` to ``dest_file`` ensuring parent directory exists. By default, message like `creating directory /path/to/package` and `copying directory /src/path/to/package -> path/to/package` are displayed on standard output. Setting ``hide_listing`` to False avoids message from being displayed. """ # Create directory if needed dest_dir = os.path.dirname(dest_file) if dest_dir != "" and not os.path.exists(dest_dir): if not hide_listing: print("creating directory {}".format(dest_dir)) mkdir_p(dest_dir) # Copy file if not hide_listing: print("copying {} -> {}".format(src_file, dest_file)) copyfile(src_file, dest_file) copymode(src_file, dest_file) def _consolidate_package_modules( cmake_source_dir, packages, package_dir, py_modules, package_data, hide_listing ): """This function consolidates packages having modules located in both the source tree and the CMake install tree into one location. The one location is the CMake install tree (see :func:`.constants.CMAKE_INSTALL_DIR()`). Why ? This is a necessary evil because ``Setuptools`` keeps track of packages and modules files to install using a dictionary of lists where the key are package names (e.g ``foo.bar``) and the values are lists of module files (e.g ``['__init__.py', 'baz.py']``. Since this doesn't allow to "split" files associated with a given module in multiple location, one location is selected, and files are copied over. How? It currently searches for modules across both locations using the :class:`.utils.PythonModuleFinder`. then with the help of :func:`_package_data_contain_module`, it identifies which one are either already included or missing from the distribution. Once a module has been identified as ``missing``, it is both copied into the :func:`.constants.CMAKE_INSTALL_DIR()` and added to the ``package_data`` dictionary so that it can be considered by the upstream setup function. """ try: # Search for python modules in both the current directory # and cmake install tree. modules = PythonModuleFinder( packages, package_dir, py_modules, alternative_build_base=CMAKE_INSTALL_DIR() ).find_all_modules() except DistutilsError as msg: raise SystemExit("error: {}".format(str(msg))) print("") for entry in modules: # Check if module file should be copied into the CMake install tree. if _package_data_contain_module(entry, package_data): continue (package, _, src_module_file) = entry # Copy missing module file if os.path.exists(src_module_file): dest_module_file = os.path.join(CMAKE_INSTALL_DIR(), src_module_file) _copy_file(src_module_file, dest_module_file, hide_listing) # Since the mapping in package_data expects the package to be associated # with a list of files relative to the directory containing the package, # the following section makes sure to strip the redundant part of the # module file path. # The redundant part should be stripped for both cmake_source_dir and # the package. package_parts = [] if cmake_source_dir: package_parts = cmake_source_dir.split(os.path.sep) package_parts += package.split(".") stripped_module_file = strip_package(package_parts, src_module_file) # Update list of files associated with the corresponding package try: package_data[package].append(stripped_module_file) except KeyError: package_data[package] = [stripped_module_file] def _consolidate_package_data_files(original_package_data, package_prefixes, hide_listing): """This function copies package data files specified using the ``package_data`` keyword into :func:`.constants.CMAKE_INSTALL_DIR()`. :: setup(..., packages=['mypkg'], package_dir={'mypkg': 'src/mypkg'}, package_data={'mypkg': ['data/*.dat']}, ) Considering that (1) the packages associated with modules located in both the source tree and the CMake install tree are consolidated into the CMake install tree, and (2) the consolidated package path set in the ``package_dir`` dictionary and later used by setuptools to package (or install) modules and data files is :func:`.constants.CMAKE_INSTALL_DIR()`, copying the data files is required to ensure setuptools can find them when it uses the package directory. """ project_root = os.getcwd() for prefix, package in package_prefixes: if package not in original_package_data: continue raw_patterns = original_package_data[package] for pattern in raw_patterns: expanded_package_dir = os.path.join(project_root, prefix, pattern) for src_data_file in glob(expanded_package_dir): full_prefix_length = len(os.path.join(project_root, prefix)) + 1 data_file = src_data_file[full_prefix_length:] dest_data_file = os.path.join(CMAKE_INSTALL_DIR(), prefix, data_file) _copy_file(src_data_file, dest_data_file, hide_listing) scikit-build-0.11.1/skbuild/utils/000077500000000000000000000000001365474353700167735ustar00rootroot00000000000000scikit-build-0.11.1/skbuild/utils/__init__.py000066400000000000000000000175631365474353700211200ustar00rootroot00000000000000"""This module defines functions generally useful in scikit-build.""" import errno import os from collections import namedtuple from contextlib import contextmanager from distutils import log as distutils_log from distutils.command.build_py import build_py as distutils_build_py from distutils.errors import DistutilsTemplateError from distutils.filelist import FileList from distutils.text_file import TextFile from functools import wraps class ContextDecorator(object): """A base class or mixin that enables context managers to work as decorators.""" def __init__(self, **kwargs): self.__dict__.update(kwargs) def __enter__(self): # Note: Returning self means that in "with ... as x", x will be self return self def __exit__(self, typ, val, traceback): pass def __call__(self, func): @wraps(func) def inner(*args, **kwds): # pylint:disable=missing-docstring with self: return func(*args, **kwds) return inner def mkdir_p(path): """Ensure directory ``path`` exists. If needed, parent directories are created. Adapted from http://stackoverflow.com/a/600612/1539918 """ try: os.makedirs(path) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST and os.path.isdir(path): pass else: # pragma: no cover raise class push_dir(ContextDecorator): """Context manager to change current directory. """ def __init__(self, directory=None, make_directory=False): """ :param directory: Path to set as current working directory. If ``None`` is passed, ``os.getcwd()`` is used instead. :param make_directory: If True, ``directory`` is created. """ self.directory = None self.make_directory = None self.old_cwd = None super(push_dir, self).__init__( directory=directory, make_directory=make_directory) def __enter__(self): self.old_cwd = os.getcwd() if self.directory: if self.make_directory: mkdir_p(self.directory) os.chdir(self.directory) return self def __exit__(self, typ, val, traceback): os.chdir(self.old_cwd) def new_style(klass): """distutils/setuptools command classes are old-style classes, which won't work with mixins. To work around this limitation, we dynamically convert them to new style classes by creating a new class that inherits from them and also . This ensures that is always at the end of the MRO, even after being mixed in with other classes. """ return type("NewStyleClass<{}>".format(klass.__name__), (klass, object), {}) class PythonModuleFinder(new_style(distutils_build_py)): """Convenience class to search for python modules. This class is based on ``distutils.command.build_py.build_by`` and provides a specialized version of ``find_all_modules()``. """ def __init__(self, packages, package_dir, py_modules, alternative_build_base=None): """ :param packages: List of packages to search. :param package_dir: Dictionary mapping ``package`` with ``directory``. :param py_modules: List of python modules. :param alternative_build_base: Additional directory to search in. """ self.distribution = namedtuple('Distribution', 'script_name') self.distribution.script_name = 'setup.py' self.packages = packages self.package_dir = package_dir self.py_modules = py_modules self.alternative_build_base = alternative_build_base def find_all_modules(self, project_dir=None): """Compute the list of all modules that would be built by project located in current directory, whether they are specified one-module-at-a-time ``py_modules`` or by whole packages ``packages``. By default, the function will search for modules in the current directory. Specifying ``project_dir`` parameter allow to change this. Return a list of tuples ``(package, module, module_file)``. """ with push_dir(project_dir): return super(PythonModuleFinder, self).find_all_modules() def find_package_modules(self, package, package_dir): """Temporally prepend the ``alternative_build_base`` to ``module_file``. Doing so will ensure modules can also be found in other location (e.g ``skbuild.constants.CMAKE_INSTALL_DIR``). """ if (package_dir != "" and not os.path.exists(package_dir) and self.alternative_build_base is not None): package_dir = os.path.join(self.alternative_build_base, package_dir) modules = super(PythonModuleFinder, self).find_package_modules( package, package_dir) # Strip the alternative base from module_file def _strip_directory(entry): module_file = entry[2] if (self.alternative_build_base is not None and module_file.startswith(self.alternative_build_base)): module_file = module_file[len(self.alternative_build_base) + 1:] return entry[0], entry[1], module_file return map(_strip_directory, modules) def check_module(self, module, module_file): """Return True if ``module_file`` belongs to ``module``. """ if self.alternative_build_base is not None: updated_module_file = os.path.join( self.alternative_build_base, module_file) if os.path.exists(updated_module_file): module_file = updated_module_file if not os.path.isfile(module_file): distutils_log.warn( "file %s (for module %s) not found", module_file, module) return False return True def to_platform_path(path): """Return a version of ``path`` where all separator are :attr:`os.sep` """ return (path.replace("/", os.sep).replace("\\", os.sep) if path is not None else None) def to_unix_path(path): """Return a version of ``path`` where all separator are ``/``""" return path.replace("\\", "/") if path is not None else None @contextmanager def distribution_hide_listing(distribution): """Given a ``distribution``, this context manager temporarily sets distutils threshold to WARN if ``--hide-listing`` argument was provided. It yields True if ``--hide-listing`` argument was provided. """ # pylint:disable=protected-access old_threshold = distutils_log._global_log.threshold hide_listing = False if (hasattr(distribution, "hide_listing") and distribution.hide_listing): hide_listing = True distutils_log.set_threshold(distutils_log.WARN) yield hide_listing distutils_log.set_threshold(old_threshold) def parse_manifestin(template): """This function parses template file (usually MANIFEST.in)""" if not os.path.exists(template): return [] template = TextFile(template, strip_comments=1, skip_blanks=1, join_lines=1, lstrip_ws=1, rstrip_ws=1, collapse_join=1) file_list = FileList() try: while True: line = template.readline() if line is None: # end of file break try: file_list.process_template_line(line) # the call above can raise a DistutilsTemplateError for # malformed lines, or a ValueError from the lower-level # convert_path function except (DistutilsTemplateError, ValueError) as msg: print("%s, line %d: %s" % (template.filename, template.current_line, msg)) return file_list.files finally: template.close() scikit-build-0.11.1/tests/000077500000000000000000000000001365474353700153405ustar00rootroot00000000000000scikit-build-0.11.1/tests/__init__.py000066400000000000000000000241171365474353700174560ustar00rootroot00000000000000# -*- coding: utf-8 -*- import _pytest.tmpdir import distutils import os import os.path import pkg_resources try: import pathlib except ImportError: import pathlib2 as pathlib # Python 2.7 import py.path import re import requests import six import subprocess import sys from contextlib import contextmanager from mock import patch from skbuild.compat import which # noqa: F401 from skbuild.utils import push_dir from skbuild.platform_specifics import get_platform SAMPLES_DIR = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'samples', ) @contextmanager def push_argv(argv): old_argv = sys.argv sys.argv = argv yield sys.argv = old_argv @contextmanager def push_env(**kwargs): """This context manager allow to set/unset environment variables. """ saved_env = dict(os.environ) for var, value in kwargs.items(): if value is not None: os.environ[var] = value elif var in os.environ: del os.environ[var] yield os.environ.clear() for (saved_var, saved_value) in saved_env.items(): os.environ[saved_var] = saved_value @contextmanager def prepend_sys_path(paths): """This context manager allows to prepend paths to ``sys.path`` and restore the original list. """ saved_paths = list(sys.path) sys.path = paths + saved_paths yield sys.path = saved_paths def _tmpdir(basename): """This function returns a temporary directory similar to the one returned by the ``tmpdir`` pytest fixture. The difference is that the `basetemp` is not configurable using the pytest settings.""" # Adapted from _pytest.tmpdir.tmpdir() basename = re.sub(r"[\W]", "_", basename) max_val = 30 if len(basename) > max_val: basename = basename[:max_val] # Adapted from _pytest.tmpdir.TempdirFactory.getbasetemp() try: basetemp = _tmpdir._basetemp except AttributeError: temproot = py.path.local.get_temproot() user = _pytest.tmpdir.get_user() if user: # use a sub-directory in the temproot to speed-up # make_numbered_dir() call rootdir = temproot.join('pytest-of-%s' % user) else: rootdir = temproot rootdir.ensure(dir=1) basetemp = py.path.local.make_numbered_dir(prefix='pytest-', rootdir=rootdir) # Adapted from _pytest.tmpdir.TempdirFactory.mktemp return py.path.local.make_numbered_dir(prefix=basename, keep=0, rootdir=basetemp, lock_timeout=None) def _copy(src, target): """ Copies a single entry (file, dir) named 'src' to 'target'. Softlinks are processed properly as well. Copied from pytest-datafiles/pytest_datafiles.py (MIT License) """ if not src.exists(): raise ValueError("'%s' does not exist!" % src) if src.isdir(): src.copy(target / src.basename) elif src.islink(): (target / src.basename).mksymlinkto(src.realpath()) else: # file src.copy(target) def _copy_dir(target_dir, src_dir, on_duplicate='exception', keep_top_dir=False): """ Copies all entries (files, dirs) from 'src_dir' to 'target_dir' taking into account the 'on_duplicate' option (which defines what should happen if an entry already exists: raise an exception, overwrite it or ignore it). Adapted from pytest-datafiles/pytest_datafiles.py (MIT License) """ src_files = [] if isinstance(src_dir, six.string_types): src_dir = py.path.local(src_dir) if keep_top_dir: src_files = src_dir else: if src_dir.isdir(): src_files.extend(src_dir.listdir()) else: src_files.append(src_dir) for entry in src_files: target_entry = target_dir / entry.basename if not target_entry.exists() or on_duplicate == 'overwrite': _copy(entry, target_dir) elif on_duplicate == 'exception': raise ValueError( "'%s' already exists (src %s)" % ( target_entry, entry, ) ) else: # ignore continue def initialize_git_repo_and_commit(project_dir, verbose=True): """Convenience function creating a git repository in ``project_dir``. If ``project_dir`` does NOT contain a ``.git`` directory, a new git repository with one commit containing all the directories and files is created. """ if isinstance(project_dir, six.string_types): project_dir = py.path.local(project_dir) if project_dir.join('.git').exists(): return # If any, exclude virtualenv files project_dir.join(".gitignore").write(".env") with push_dir(str(project_dir)): for cmd in [ ['git', 'init'], ['git', 'config', 'user.name', 'scikit-build'], ['git', 'config', 'user.email', 'test@test'], ['git', 'config', 'commit.gpgsign', 'false'], ['git', 'add', '-A'], ['git', 'reset', '.gitignore'], ['git', 'commit', '-m', 'Initial commit'] ]: do_call = (subprocess.check_call if verbose else subprocess.check_output) do_call(cmd) def prepare_project(project, tmp_project_dir, force=False): """Convenience function setting up the build directory ``tmp_project_dir`` for the selected sample ``project``. If ``tmp_project_dir`` does not exist, it is created. If ``tmp_project_dir`` is empty, the sample ``project`` is copied into it. Specifying ``force=True`` will copy the files even if ``tmp_project_dir`` is not empty. """ if isinstance(tmp_project_dir, six.string_types): tmp_project_dir = py.path.local(tmp_project_dir) # Create project directory if it does not exist if not tmp_project_dir.exists(): tmp_project_dir = _tmpdir(project) # If empty or if force is True, copy project files and initialize git if not tmp_project_dir.listdir() or force: _copy_dir(tmp_project_dir, os.path.join(SAMPLES_DIR, project)) @contextmanager def execute_setup_py(project_dir, setup_args, disable_languages_test=False): """Context manager executing ``setup.py`` with the given arguments. It yields after changing the current working directory to ``project_dir``. """ # See https://stackoverflow.com/questions/9160227/dir-util-copy-tree-fails-after-shutil-rmtree distutils.dir_util._path_created = {} # Clear _PYTHON_HOST_PLATFORM to ensure value sets in skbuild.setuptools_wrap.setup() does not # influence other tests. if '_PYTHON_HOST_PLATFORM' in os.environ: del os.environ['_PYTHON_HOST_PLATFORM'] with push_dir(str(project_dir)), push_argv(["setup.py"] + setup_args), prepend_sys_path([str(project_dir)]): # Restore master working set that is reset following call to "python setup.py test" # See function "project_on_sys_path()" in setuptools.command.test pkg_resources._initialize_master_working_set() with open("setup.py", "r") as fp: setup_code = compile(fp.read(), "setup.py", mode="exec") if setup_code is not None: if disable_languages_test: platform = get_platform() original_write_test_cmakelist = platform.write_test_cmakelist def write_test_cmakelist_no_languages(_self, _languages): original_write_test_cmakelist([]) with patch.object(type(platform), 'write_test_cmakelist', new=write_test_cmakelist_no_languages): six.exec_(setup_code) else: six.exec_(setup_code) yield def project_setup_py_test(project, setup_args, tmp_dir=None, verbose_git=True, disable_languages_test=False): def dec(fun): @six.wraps(fun) def wrapped(*iargs, **ikwargs): if wrapped.tmp_dir is None: wrapped.tmp_dir = _tmpdir(fun.__name__) prepare_project(wrapped.project, wrapped.tmp_dir) initialize_git_repo_and_commit( wrapped.tmp_dir, verbose=wrapped.verbose_git) with execute_setup_py(wrapped.tmp_dir, wrapped.setup_args, disable_languages_test=disable_languages_test): result2 = fun(*iargs, **ikwargs) return wrapped.tmp_dir, result2 wrapped.project = project wrapped.setup_args = setup_args wrapped.tmp_dir = tmp_dir wrapped.verbose_git = verbose_git return wrapped return dec def get_cmakecache_variables(cmakecache): """Returns a dictionary of all variables found in given CMakeCache.txt. Dictionary entries are tuple of the form ``(variable_type, variable_value)``. Possible `variable_type` are documented `here `_. """ results = {} cache_entry_pattern = re.compile(r"^([\w\d_-]+):([\w]+)=") with open(cmakecache) as content: for line in content.readlines(): line = line.strip() result = cache_entry_pattern.match(line) if result: variable_name = result.group(1) variable_type = result.group(2) variable_value = line.split("=")[1] results[variable_name] = (variable_type, variable_value) return results def is_site_reachable(url): """Return True if the given website can be accessed""" try: request = requests.get(url) return request.status_code == 200 except requests.exceptions.ConnectionError: return False def list_ancestors(path): """Return logical ancestors of the path. """ return [str(parent) for parent in pathlib.PurePosixPath(path).parents if str(parent) != "."] def get_ext_suffix(): """Return python extension suffix. """ ext_suffix_var = 'SO' if sys.version_info[:2] >= (3, 5): ext_suffix_var = 'EXT_SUFFIX' return distutils.sysconfig.get_config_var(ext_suffix_var) scikit-build-0.11.1/tests/conftest.py000066400000000000000000000001061365474353700175340ustar00rootroot00000000000000import pytest pytest.register_assert_rewrite('tests.pytest_helpers') scikit-build-0.11.1/tests/pytest_helpers.py000066400000000000000000000074031365474353700207700ustar00rootroot00000000000000 import six import sys import tarfile import wheel from pkg_resources import parse_version from zipfile import ZipFile from skbuild import __version__ as skbuild_version from . import list_ancestors def check_sdist_content(sdist_archive, expected_distribution_name, expected_content): """This function raises an AssertionError if the given sdist_archive does not have the expected content. """ sdist_zip = sdist_archive.endswith('.zip') sdist_tar = sdist_archive.endswith('.tar.gz') assert sdist_zip or sdist_tar expected_content = list(expected_content) expected_content += [ '%s/PKG-INFO' % expected_distribution_name, ] if sdist_zip and ( (2, 7, 15) < sys.version_info[:3] < (3, 0, 0) or (3, 6, 7) < sys.version_info[:3] < (3, 7, 0) or (3, 7, 1) < sys.version_info[:3] ): # Add directory entries in ZIP files created by distutils. # See https://github.com/python/cpython/pull/9419 directories = set() for entry in expected_content: directories = directories.union([entry + "/" for entry in list_ancestors(entry)]) expected_content += directories member_list = [] if sdist_zip: member_list = ZipFile(sdist_archive).namelist() elif sdist_tar: with tarfile.open(sdist_archive) as tf: member_list = [member.name for member in tf.getmembers() if not member.isdir()] assert sorted(expected_content) == sorted(member_list) def check_wheel_content(wheel_archive, expected_distribution_name, expected_content, pure=False): """This function raises an AssertionError if the given wheel_archive does not have the expected content. Note that this function already takes care of appending the ``.dist-info`` files to the ``expected_content`` list. """ expected_content = list(expected_content) expected_content += [ '%s.dist-info/top_level.txt' % expected_distribution_name, '%s.dist-info/WHEEL' % expected_distribution_name, '%s.dist-info/RECORD' % expected_distribution_name, '%s.dist-info/METADATA' % expected_distribution_name ] if parse_version(wheel.__version__) < parse_version('0.31.0'): # These files were specified in the now-withdrawn PEP 426 # See https://github.com/pypa/wheel/issues/195 expected_content += [ '%s.dist-info/DESCRIPTION.rst' % expected_distribution_name, '%s.dist-info/metadata.json' % expected_distribution_name ] if parse_version('0.33.1') < parse_version(wheel.__version__) < parse_version('0.33.4'): # Include directory entries when building wheel # See https://github.com/pypa/wheel/issues/287 and https://github.com/pypa/wheel/issues/294 directories = set() for entry in expected_content: directories = directories.union([entry + "/" for entry in list_ancestors(entry)]) expected_content += directories if pure: assert wheel_archive.endswith('-none-any.whl') else: assert not wheel_archive.endswith('-none-any.whl') archive = ZipFile(wheel_archive) member_list = archive.namelist() assert sorted(expected_content) == sorted(member_list) # PEP-0427: Generator is the name and optionally the version of the # software that produced the archive. # See https://www.python.org/dev/peps/pep-0427/#file-contents current_generator = None with archive.open("%s.dist-info/WHEEL" % expected_distribution_name) as wheel_file: for line in wheel_file: if line.startswith(b"Generator"): current_generator = line.split(b":")[1].strip() break assert current_generator == six.b("skbuild %s" % skbuild_version) scikit-build-0.11.1/tests/samples/000077500000000000000000000000001365474353700170045ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/000077500000000000000000000000001365474353700250265ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/hello/000077500000000000000000000000001365474353700261315ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/hello/CMakeLists.txt000066400000000000000000000003311365474353700306660ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/hello/__init__.py000066400000000000000000000000001365474353700302300ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/hello/__main__.py000066400000000000000000000001271365474353700302230ustar00rootroot00000000000000 if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/hello/_hello.cxx000066400000000000000000000030211365474353700301130ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.11.1/tests/samples/cmakelists-not-in-top-level-dir/setup.py000066400000000000000000000004061365474353700265400ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package (CMakeLists not in top-level dir)", author='The scikit-build team', license="MIT", packages=['hello'], cmake_source_dir='hello' ) scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/000077500000000000000000000000001365474353700245465ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/CMakeLists.txt000066400000000000000000000001701365474353700273040ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_subdirectory(hello) scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/hello/000077500000000000000000000000001365474353700256515ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/hello/CMakeLists.txt000066400000000000000000000001711365474353700304100ustar00rootroot00000000000000add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/hello/__init__.py000066400000000000000000000000001365474353700277500ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/hello/__main__.py000066400000000000000000000001271365474353700277430ustar00rootroot00000000000000 if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/hello/_hello.cxx000066400000000000000000000031151365474353700276370ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL // Purposely removed the semicolon to cause a compiler error // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.11.1/tests/samples/fail-hello-with-compile-error/setup.py000066400000000000000000000004651365474353700262650ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description=("a minimal example packagze that should always fail to build " "because it provides a _hello.cxx with a compile error"), author='The scikit-build team', license="MIT", packages=['hello'], ) scikit-build-0.11.1/tests/samples/fail-outside-project-root/000077500000000000000000000000001365474353700240165ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-outside-project-root/CMakeLists.txt000066400000000000000000000015011365474353700265530ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_outside_project_root NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") if(INSTALL_FILE) install(FILES dummy DESTINATION ..) endif() if(INSTALL_PROJECT) set(other_project_source_dir ${CMAKE_SOURCE_DIR}/other_project) set(other_project_build_dir ${CMAKE_BINARY_DIR}/../other_project-build) set(other_project_install_dir ${CMAKE_BINARY_DIR}/../other_project-install) file(MAKE_DIRECTORY ${other_project_build_dir}) # Configure execute_process( COMMAND ${CMAKE_COMMAND} ${other_project_source_dir} WORKING_DIRECTORY ${other_project_build_dir} ) # Install other_project install(CODE " set(CMAKE_INSTALL_PREFIX \"${other_project_install_dir}\") include(\"${other_project_build_dir}/cmake_install.cmake\") ") endif() scikit-build-0.11.1/tests/samples/fail-outside-project-root/dummy000066400000000000000000000000001365474353700250620ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-outside-project-root/other_project/000077500000000000000000000000001365474353700266655ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-outside-project-root/other_project/CMakeLists.txt000066400000000000000000000001771365474353700314320ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(other_project NONE) install(FILES ${CMAKE_SOURCE_DIR}/../dummy DESTINATION .) scikit-build-0.11.1/tests/samples/fail-outside-project-root/setup.py000066400000000000000000000004551365474353700255340ustar00rootroot00000000000000from skbuild import setup setup( name="fail_outside_project_root", version="0.0.1", description=("test project that should always fail to build because it " "tries to CMake-install something outside of its root"), author="The scikit-build team", license="MIT", ) scikit-build-0.11.1/tests/samples/fail-unless-skbuild-set/000077500000000000000000000000001365474353700234525ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-unless-skbuild-set/CMakeLists.txt000066400000000000000000000003431365474353700262120ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_unless_skbuild_set NONE) if(NOT DEFINED SKBUILD) message(FATAL_ERROR "SKBUILD is not set!") endif() install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") scikit-build-0.11.1/tests/samples/fail-unless-skbuild-set/setup.py000066400000000000000000000004101365474353700251570ustar00rootroot00000000000000from skbuild import setup setup( name="fail_unless_skbuild_set", version="0.0.1", description=("test project that should fail unless the " "CMake variable \"SKBUILD\" is set"), author="The scikit-build team", license="MIT", ) scikit-build-0.11.1/tests/samples/fail-with-fatal-error-cmakelists/000077500000000000000000000000001365474353700252415ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-with-fatal-error-cmakelists/CMakeLists.txt000066400000000000000000000003141365474353700277770ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_with_fatal_error_cmakelists NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") message(FATAL_ERROR "Invalid CMakeLists.txt") scikit-build-0.11.1/tests/samples/fail-with-fatal-error-cmakelists/setup.py000066400000000000000000000004611365474353700267540ustar00rootroot00000000000000from skbuild import setup setup( name="fail_with_fatal_error_cmakelists", version="0.0.1", description=("test project that should always fail to build because it " "provides a CMakeLists.txt reporting a FATAL_ERROR"), author="The scikit-build team", license="MIT", ) scikit-build-0.11.1/tests/samples/fail-with-syntax-error-cmakelists/000077500000000000000000000000001365474353700255005ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/fail-with-syntax-error-cmakelists/CMakeLists.txt000066400000000000000000000003431365474353700302400ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(fail_with_syntax_error_cmakelists NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)") message(STATUS "This is incorrect # Parenthesis omitted on purpose scikit-build-0.11.1/tests/samples/fail-with-syntax-error-cmakelists/setup.py000066400000000000000000000004561365474353700272170ustar00rootroot00000000000000from skbuild import setup setup( name="fail_with_syntax_error_cmakelists", version="0.0.1", description=("test project that should always fail to build because it " "provides a CMakeLists.txt with a syntax error"), author="The scikit-build team", license="MIT", ) scikit-build-0.11.1/tests/samples/hello-cpp/000077500000000000000000000000001365474353700206675ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/CMakeLists.txt000066400000000000000000000010471365474353700234310ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_subdirectory(hello) file(WRITE "${CMAKE_BINARY_DIR}/helloModule.py" "# helloModule.py") install(FILES "${CMAKE_BINARY_DIR}/helloModule.py" DESTINATION ".") # # This code is here *ONLY* to test that the different signatures of # "check_dynamic_lookup" functions are not causing errors. # include(targetLinkLibrariesWithDynamicLookup) check_dynamic_lookup("MODULE" "SHARED" "HELLO_HAS_DYNAMIC_LOOKUP") check_dynamic_lookup("HELLO_HAS_DYNAMIC_LOOKUP") scikit-build-0.11.1/tests/samples/hello-cpp/bonjour/000077500000000000000000000000001365474353700223455ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/bonjour/__init__.py000066400000000000000000000000001365474353700244440ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/bonjour/data/000077500000000000000000000000001365474353700232565ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/bonjour/data/ciel.txt000066400000000000000000000000041365474353700247250ustar00rootroot00000000000000cielscikit-build-0.11.1/tests/samples/hello-cpp/bonjour/data/soleil.txt000066400000000000000000000000061365474353700253020ustar00rootroot00000000000000soleilscikit-build-0.11.1/tests/samples/hello-cpp/bonjour/data/terre.txt000066400000000000000000000000051365474353700251330ustar00rootroot00000000000000terrescikit-build-0.11.1/tests/samples/hello-cpp/bonjourModule.py000066400000000000000000000000001365474353700240530ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/hello/000077500000000000000000000000001365474353700217725ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/hello/CMakeLists.txt000066400000000000000000000003561365474353700245360ustar00rootroot00000000000000add_library(_hello MODULE _hello.cxx) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) set(world "${CMAKE_CURRENT_SOURCE_DIR}/world.py") file(WRITE ${world} "") install(FILES ${world} DESTINATION hello)scikit-build-0.11.1/tests/samples/hello-cpp/hello/__init__.py000066400000000000000000000000001365474353700240710ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cpp/hello/__main__.py000066400000000000000000000001271365474353700240640ustar00rootroot00000000000000 if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.11.1/tests/samples/hello-cpp/hello/_hello.cxx000066400000000000000000000030211365474353700237540ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello(void) { (void) Py_InitModule("_hello", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello", "Internal \"_hello\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.11.1/tests/samples/hello-cpp/setup.py000066400000000000000000000005021365474353700223760ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['bonjour', 'hello'], package_data={'bonjour': ['data/*.txt', 'data/terre.txt']}, py_modules=['bonjourModule', 'helloModule'] ) scikit-build-0.11.1/tests/samples/hello-cython/000077500000000000000000000000001365474353700214115ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cython/CMakeLists.txt000066400000000000000000000002351365474353700241510ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello_cython) find_package(PythonExtensions REQUIRED) find_package(Cython REQUIRED) add_subdirectory(hello) scikit-build-0.11.1/tests/samples/hello-cython/hello/000077500000000000000000000000001365474353700225145ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cython/hello/CMakeLists.txt000066400000000000000000000002271365474353700252550ustar00rootroot00000000000000 add_cython_target(_hello CXX) add_library(_hello MODULE ${_hello}) python_extension_module(_hello) install(TARGETS _hello LIBRARY DESTINATION hello) scikit-build-0.11.1/tests/samples/hello-cython/hello/__init__.py000066400000000000000000000000001365474353700246130ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-cython/hello/__main__.py000066400000000000000000000001271365474353700246060ustar00rootroot00000000000000 if __name__ == "__main__": from . import _hello as hello hello.hello("World") scikit-build-0.11.1/tests/samples/hello-cython/hello/_hello.pyx000066400000000000000000000003451365474353700245220ustar00rootroot00000000000000 cpdef void hello(str strArg): "Prints back 'Hello ', for example example: hello.hello('you')" print("Hello, {}! :)".format(strArg)) cpdef long size(): "Returns elevation of Nevado Sajama." return 21463L scikit-build-0.11.1/tests/samples/hello-cython/setup.py000066400000000000000000000005411365474353700231230ustar00rootroot00000000000000from skbuild import setup setup( name="hello-cython", version="1.2.3", description="a minimal example package (cython version)", author='The scikit-build team', license="MIT", packages=['hello_cython'], # The extra '/' was *only* added to check that scikit-build can handle it. package_dir={'hello_cython': 'hello/'}, ) scikit-build-0.11.1/tests/samples/hello-no-language/000077500000000000000000000000001365474353700223025ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-no-language/CMakeLists.txt000066400000000000000000000002011365474353700250330ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) install(CODE "execute_process(COMMAND \${CMAKE_COMMAND} -E sleep 0)")scikit-build-0.11.1/tests/samples/hello-no-language/setup.py000066400000000000000000000002721365474353700240150ustar00rootroot00000000000000from skbuild import setup setup( name="hello_no_language", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT" ) scikit-build-0.11.1/tests/samples/hello-pure/000077500000000000000000000000001365474353700210605ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-pure/hello/000077500000000000000000000000001365474353700221635ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-pure/hello/__init__.py000066400000000000000000000000001365474353700242620ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/hello-pure/setup.py000066400000000000000000000003141365474353700225700ustar00rootroot00000000000000from skbuild import setup setup( name="hello-pure", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['hello'], ) scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/000077500000000000000000000000001365474353700257475ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/CMakeLists.txt000066400000000000000000000004521365474353700305100ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION src/hello/) scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/hello_tests/000077500000000000000000000000001365474353700302745ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/hello_tests/__init__.py000066400000000000000000000007001365474353700324020ustar00rootroot00000000000000import unittest import hello from hello import cmake_generated_module class HelloTest(unittest.TestCase): def test_hello(self): self.assertEqual(hello.who(), "world") with open("test_hello.completed.txt", "w") as marker: marker.write("") def test_cmake_generated_module(self): self.assertEqual(cmake_generated_module.what(), "cmake_generated_module") if __name__ == '__main__': unittest.main() scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/setup.py000066400000000000000000000004011365474353700274540ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['hello'], package_dir={'': 'src'}, test_suite='hello_tests' ) scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/src/000077500000000000000000000000001365474353700265365ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/src/hello/000077500000000000000000000000001365474353700276415ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-default-package-dir/src/hello/__init__.py000066400000000000000000000000371365474353700317520ustar00rootroot00000000000000 def who(): return "world" scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/000077500000000000000000000000001365474353700301765ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/CMakeLists.txt000066400000000000000000000004461365474353700327420ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello NONE) set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION hello/) scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/hello/000077500000000000000000000000001365474353700313015ustar00rootroot00000000000000__init__.py000066400000000000000000000000371365474353700333330ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/hello def who(): return "world" scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/hello_tests/000077500000000000000000000000001365474353700325235ustar00rootroot00000000000000__init__.py000066400000000000000000000007001365474353700345520ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/hello_testsimport unittest import hello from hello import cmake_generated_module class HelloTest(unittest.TestCase): def test_hello(self): self.assertEqual(hello.who(), "world") with open("test_hello.completed.txt", "w") as marker: marker.write("") def test_cmake_generated_module(self): self.assertEqual(cmake_generated_module.what(), "cmake_generated_module") if __name__ == '__main__': unittest.main() scikit-build-0.11.1/tests/samples/issue-274-support-one-package-without-package-dir/setup.py000066400000000000000000000003441365474353700317110ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['hello'], test_suite='hello_tests' ) scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/000077500000000000000000000000001365474353700237535ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/CMakeLists.txt000066400000000000000000000003521365474353700265130ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(hello) find_package(PythonExtensions REQUIRED) add_library(_hello_sk MODULE hello/_hello_sk.cxx) python_extension_module(_hello_sk) install(TARGETS _hello_sk LIBRARY DESTINATION hello)scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/hello/000077500000000000000000000000001365474353700250565ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/hello/__init__.py000066400000000000000000000000001365474353700271550ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/hello/_hello_ext.cxx000066400000000000000000000030451365474353700277260ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello_ext(void) { (void) Py_InitModule("_hello_ext", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello_ext", "Internal \"_hello_ext\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello_ext(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/hello/_hello_sk.cxx000066400000000000000000000030401365474353700275360ustar00rootroot00000000000000 // Python includes #include // STD includes #include //----------------------------------------------------------------------------- static PyObject *hello_example(PyObject *self, PyObject *args) { // Unpack a string from the arguments const char *strArg; if (!PyArg_ParseTuple(args, "s", &strArg)) return NULL; // Print message and return None PySys_WriteStdout("Hello, %s! :)\n", strArg); Py_RETURN_NONE; } //----------------------------------------------------------------------------- static PyObject *elevation_example(PyObject *self, PyObject *args) { // Return an integer return PyLong_FromLong(21463L); } //----------------------------------------------------------------------------- static PyMethodDef hello_methods[] = { { "hello", hello_example, METH_VARARGS, "Prints back 'Hello ', for example example: hello.hello('you')" }, { "size", elevation_example, METH_VARARGS, "Returns elevation of Nevado Sajama." }, {NULL, NULL, 0, NULL} /* Sentinel */ }; //----------------------------------------------------------------------------- #if PY_MAJOR_VERSION < 3 PyMODINIT_FUNC init_hello_sk(void) { (void) Py_InitModule("_hello_sk", hello_methods); } #else /* PY_MAJOR_VERSION >= 3 */ static struct PyModuleDef hello_module_def = { PyModuleDef_HEAD_INIT, "_hello_sk", "Internal \"_hello_sk\" module", -1, hello_methods }; PyMODINIT_FUNC PyInit__hello_sk(void) { return PyModule_Create(&hello_module_def); } #endif /* PY_MAJOR_VERSION >= 3 */ scikit-build-0.11.1/tests/samples/issue-284-build-ext-inplace/setup.py000066400000000000000000000005541365474353700254710ustar00rootroot00000000000000 from setuptools import Extension from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['hello'], ext_modules=[ Extension( 'hello._hello_ext', sources=['hello/_hello_ext.cxx'], ) ] ) scikit-build-0.11.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/000077500000000000000000000000001365474353700276625ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/CMakeLists.txt000066400000000000000000000004111365474353700324160ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) # Sanskrit for "I can eat glass, it does not hurt me" # See http://kermitproject.org/utf8.html install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E echo \"kÄcaṃ Å›aknomyattum; nopahinasti mÄm\")")scikit-build-0.11.1/tests/samples/issue-334-configure-cmakelist-non-cp1252-encoding/setup.py000066400000000000000000000002771365474353700314020ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=[] ) scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/000077500000000000000000000000001365474353700253065ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/CMakeLists.txt000066400000000000000000000011271365474353700300470ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) project(hello NONE) # Headers file(WRITE "bar.h" "") file(WRITE "foo.h" "") install(FILES "bar.h" "foo.h" DESTINATION include) # Static libraries file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" DESTINATION lib/static ) # Executables file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/hello" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/hello" DESTINATION bin ) add_subdirectory(wrapping/python) scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/000077500000000000000000000000001365474353700271355ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/000077500000000000000000000000001365474353700304565ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/CMakeLists.txt000066400000000000000000000006261365474353700332220ustar00rootroot00000000000000# Use .pyd extension on all platforms. This will avoid failure # on macOS where wheel wheel/macosx_libfile.py attempts to extract # shared library information. file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd" "") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py ${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd DESTINATION hello ) scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/hello/000077500000000000000000000000001365474353700315615ustar00rootroot00000000000000__init__.py000066400000000000000000000000001365474353700336010ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/helloscikit-build-0.11.1/tests/samples/issue-335-support-cmake-source-dir/wrapping/python/setup.py000066400000000000000000000004411365474353700321670ustar00rootroot00000000000000 from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package (cpp version)", author='The scikit-build team', license="MIT", packages=['hello'], tests_require=[], setup_requires=[], cmake_source_dir="../../" ) scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/000077500000000000000000000000001365474353700244025ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/MANIFEST.in000066400000000000000000000000371365474353700261400ustar00rootroot00000000000000include README include VERSION scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/README000066400000000000000000000000271365474353700252610ustar00rootroot00000000000000This is a README file. scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/VERSION000077700000000000000000000000001365474353700277652VERSION.actualustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/VERSION.actual000066400000000000000000000000061365474353700267160ustar00rootroot000000000000001.2.3 scikit-build-0.11.1/tests/samples/issue-401-sdist-with-symlinks/setup.py000066400000000000000000000003051365474353700261120ustar00rootroot00000000000000from skbuild import setup setup( name='hello', version='1.2.3', author='John Doe', author_email='test@example.com', url='https://example.com', include_package_data=True, ) scikit-build-0.11.1/tests/samples/manifest-in/000077500000000000000000000000001365474353700212165ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/manifest-in/MANIFEST.in000066400000000000000000000000431365474353700227510ustar00rootroot00000000000000include setup.py hello/__init__.py scikit-build-0.11.1/tests/samples/manifest-in/hello/000077500000000000000000000000001365474353700223215ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/manifest-in/hello/__init__.py000066400000000000000000000000001365474353700244200ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/manifest-in/not_included.txt000066400000000000000000000000551365474353700244260ustar00rootroot00000000000000This file is not included in the MANIFEST.in scikit-build-0.11.1/tests/samples/manifest-in/setup.py000066400000000000000000000003401365474353700227250ustar00rootroot00000000000000from skbuild import setup setup( name="manifest-in", version="1.2.3", description="a minimal example package with a MANIFEST.in", author='The scikit-build team', license="MIT", packages=['hello'], ) scikit-build-0.11.1/tests/samples/test-filter-manifest/000077500000000000000000000000001365474353700230525ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-filter-manifest/CMakeLists.txt000066400000000000000000000011271365474353700256130ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.2) project(hello NONE) # Headers file(WRITE "bar.h" "") file(WRITE "foo.h" "") install(FILES "bar.h" "foo.h" DESTINATION include) # Static libraries file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libbar.a" "${CMAKE_CURRENT_BINARY_DIR}/libfoo.a" DESTINATION lib/static ) # Executables file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/hello" "") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/hello" DESTINATION bin ) add_subdirectory(wrapping/python) scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/000077500000000000000000000000001365474353700247015ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/python/000077500000000000000000000000001365474353700262225ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/python/CMakeLists.txt000066400000000000000000000006271365474353700307670ustar00rootroot00000000000000 # Use .pyd extension on all platforms. This will avoid failure # on macOS where wheel wheel/macosx_libfile.py attempts to extract # shared library information. file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py" "") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd" "") install(FILES ${CMAKE_CURRENT_BINARY_DIR}/swig_mwe.py ${CMAKE_CURRENT_BINARY_DIR}/_swig_mwe.pyd DESTINATION hello ) scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/python/hello/000077500000000000000000000000001365474353700273255ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/python/hello/__init__.py000066400000000000000000000000001365474353700314240ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-filter-manifest/wrapping/python/setup.py000066400000000000000000000007451365474353700277420ustar00rootroot00000000000000 from skbuild import setup def exclude_dev_files(cmake_manifest): return list(filter(lambda name: not (name.endswith('.a') or name.endswith('.h')), cmake_manifest)) setup( name="hello", version="1.2.3", description="a minimal example package (cpp version)", author='The scikit-build team', license="MIT", packages=['hello'], tests_require=[], setup_requires=[], cmake_source_dir="../../", cmake_process_manifest_hook=exclude_dev_files ) scikit-build-0.11.1/tests/samples/test-hide-listing/000077500000000000000000000000001365474353700223415ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/CMakeLists.txt000066400000000000000000000003511365474353700251000ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_hide_listing NONE) add_subdirectory(hello) file(WRITE "${CMAKE_BINARY_DIR}/helloModule.py" "# helloModule.py") install(FILES "${CMAKE_BINARY_DIR}/helloModule.py" DESTINATION ".") scikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/000077500000000000000000000000001365474353700240175ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/__init__.py000066400000000000000000000000001365474353700261160ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/data/000077500000000000000000000000001365474353700247305ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/data/ciel.txt000066400000000000000000000000041365474353700263770ustar00rootroot00000000000000cielscikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/data/soleil.txt000066400000000000000000000000061365474353700267540ustar00rootroot00000000000000soleilscikit-build-0.11.1/tests/samples/test-hide-listing/bonjour/data/terre.txt000066400000000000000000000000051365474353700266050ustar00rootroot00000000000000terrescikit-build-0.11.1/tests/samples/test-hide-listing/bonjourModule.py000066400000000000000000000000001365474353700255250ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/hello/000077500000000000000000000000001365474353700234445ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-hide-listing/hello/CMakeLists.txt000066400000000000000000000001641365474353700262050ustar00rootroot00000000000000set(world "${CMAKE_CURRENT_SOURCE_DIR}/world.py") file(WRITE ${world} "") install(FILES ${world} DESTINATION hello)scikit-build-0.11.1/tests/samples/test-hide-listing/hello/__init__.py000066400000000000000000000000371365474353700255550ustar00rootroot00000000000000def hello(msg): print(msg) scikit-build-0.11.1/tests/samples/test-hide-listing/hello/__main__.py000066400000000000000000000001151365474353700255330ustar00rootroot00000000000000 if __name__ == "__main__": from . import hello hello.hello("World") scikit-build-0.11.1/tests/samples/test-hide-listing/setup.py000066400000000000000000000005021365474353700240500ustar00rootroot00000000000000from skbuild import setup setup( name="hello", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", packages=['bonjour', 'hello'], package_data={'bonjour': ['data/*.txt', 'data/terre.txt']}, py_modules=['bonjourModule', 'helloModule'] ) scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/000077500000000000000000000000001365474353700256235ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/CMakeLists.txt000066400000000000000000000036731365474353700303740ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_include_exclude_data_with_base NONE) set(base "src/") set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION ${base}hello) # hello data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) # hello2 data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2)scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/MANIFEST.in000066400000000000000000000003561365474353700273650ustar00rootroot00000000000000include src/hello/hello_include_from_manifest.txt include src/hello2/hello2_include_from_manifest.txt include src/hello/data/subdata/*.txt include src/hello2/data2/subdata2/*.txt exclude src/*/data*/subdata*/*_exclude_from_manifest.txt scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/setup.py000066400000000000000000000006161365474353700273400ustar00rootroot00000000000000from skbuild import setup setup( name="test_include_exclude_data_with_base", version="0.1.0", cmake_languages=(), packages=['hello', 'hello2'], package_dir={'': 'src'}, include_package_data=True, exclude_package_data={'': [ '*/*/*_data4_include_from_manifest_and_exclude_from_setup.txt', '*/*/*_data4_cmake_generated_and_exclude_from_setup.txt' ]} ) scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/000077500000000000000000000000001365474353700264125ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/000077500000000000000000000000001365474353700275155ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/__init__.py000066400000000000000000000000001365474353700316140ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/000077500000000000000000000000001365474353700304265ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdata/000077500000000000000000000000001365474353700320515ustar00rootroot00000000000000hello_data1_include_from_manifest.txt000066400000000000000000000000271365474353700413230ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatathis is just data file hello_data2_include_from_manifest.txt000066400000000000000000000000001365474353700413130ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001365474353700465600ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001365474353700461130ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello/data/subdatahello_include_from_manifest.txt000066400000000000000000000000001365474353700357040ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/helloscikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/000077500000000000000000000000001365474353700275775ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/__init__.py000066400000000000000000000000001365474353700316760ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/000077500000000000000000000000001365474353700305725ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2/000077500000000000000000000000001365474353700322775ustar00rootroot00000000000000hello2_data1_include_from_manifest.txt000066400000000000000000000000271365474353700416330ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2this is just data file hello2_data2_include_from_manifest.txt000066400000000000000000000000001365474353700416230ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001365474353700470700ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001365474353700464230ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2/data2/subdata2hello2_include_from_manifest.txt000066400000000000000000000000001365474353700360500ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data-with-base/src/hello2scikit-build-0.11.1/tests/samples/test-include-exclude-data/000077500000000000000000000000001365474353700237425ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/CMakeLists.txt000066400000000000000000000036551365474353700265130ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.5.0) project(test_include_exclude_data NONE) set(base "") set(module "${CMAKE_CURRENT_BINARY_DIR}/cmake_generated_module.py") file(WRITE ${module} "# Generated from ${CMAKE_CURRENT_LIST_FILE} def what(): return \"cmake_generated_module\" ") install(FILES ${module} DESTINATION ${base}hello) # hello data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello/data/subdata) # hello2 data set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data1_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data2_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data3_cmake_generated.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2) set(data "${CMAKE_CURRENT_BINARY_DIR}/hello2_data4_cmake_generated_and_exclude_from_setup.txt") file(WRITE ${data} "# Generated from ${CMAKE_CURRENT_LIST_FILE}") install(FILES ${data} DESTINATION ${base}hello2/data2/subdata2)scikit-build-0.11.1/tests/samples/test-include-exclude-data/MANIFEST.in000066400000000000000000000003321365474353700254760ustar00rootroot00000000000000include hello/hello_include_from_manifest.txt include hello2/hello2_include_from_manifest.txt include hello/data/subdata/*.txt include hello2/data2/subdata2/*.txt exclude */data*/subdata*/*_exclude_from_manifest.txt scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/000077500000000000000000000000001365474353700250455ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/__init__.py000066400000000000000000000000001365474353700271440ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/000077500000000000000000000000001365474353700257565ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/subdata/000077500000000000000000000000001365474353700274015ustar00rootroot00000000000000hello_data1_include_from_manifest.txt000066400000000000000000000000271365474353700366530ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/subdatathis is just data file hello_data2_include_from_manifest.txt000066400000000000000000000000001365474353700366430ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/subdatahello_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001365474353700441100ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/subdatahello_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001365474353700434430ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/data/subdatascikit-build-0.11.1/tests/samples/test-include-exclude-data/hello/hello_include_from_manifest.txt000066400000000000000000000000001365474353700333130ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/000077500000000000000000000000001365474353700251275ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/__init__.py000066400000000000000000000000001365474353700272260ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/000077500000000000000000000000001365474353700261225ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2/000077500000000000000000000000001365474353700276275ustar00rootroot00000000000000hello2_data1_include_from_manifest.txt000066400000000000000000000000271365474353700371630ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2this is just data file hello2_data2_include_from_manifest.txt000066400000000000000000000000001365474353700371530ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2hello2_data3_include_from_manifest_and_exclude_from_manifest.txt000066400000000000000000000000001365474353700444200ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2hello2_data4_include_from_manifest_and_exclude_from_setup.txt000066400000000000000000000000001365474353700437530ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/data2/subdata2scikit-build-0.11.1/tests/samples/test-include-exclude-data/hello2/hello2_include_from_manifest.txt000066400000000000000000000000001365474353700334570ustar00rootroot00000000000000scikit-build-0.11.1/tests/samples/test-include-exclude-data/setup.py000066400000000000000000000005501365474353700254540ustar00rootroot00000000000000from skbuild import setup setup( name="test_include_exclude_data", version="0.1.0", cmake_languages=(), packages=['hello', 'hello2'], include_package_data=True, exclude_package_data={'': [ '*/*/*_data4_include_from_manifest_and_exclude_from_setup.txt', '*/*/*_data4_cmake_generated_and_exclude_from_setup.txt', ]} ) scikit-build-0.11.1/tests/test_broken_project.py000066400000000000000000000120061365474353700217560ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_broken_cmakelists ---------------------------------- Tries to build the `fail-with-*-cmakelists` sample projects. Ensures that the attempt fails with a SystemExit exception that has an SKBuildError exception as its value. """ import pytest from subprocess import (check_output, CalledProcessError) from skbuild.constants import CMAKE_DEFAULT_EXECUTABLE from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import CMakeGenerator, get_platform from skbuild.utils import push_dir from . import project_setup_py_test from . import push_env def test_cmakelists_with_fatalerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-with-fatal-error-cmakelists", ["build"], disable_languages_test=True) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed _, err = capfd.readouterr() assert "Invalid CMakeLists.txt" in err assert "An error occurred while configuring with CMake." in message def test_cmakelists_with_syntaxerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-with-syntax-error-cmakelists", ["build"], disable_languages_test=True) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed _, err = capfd.readouterr() assert "Parse error. Function missing ending \")\"" in err assert "An error occurred while configuring with CMake." in message def test_hello_with_compileerror_fails(capfd): with push_dir(): @project_setup_py_test("fail-hello-with-compile-error", ["build"]) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed out, err = capfd.readouterr() assert "_hello.cxx" in out or "_hello.cxx" in err assert "An error occurred while building with CMake." in message @pytest.mark.parametrize("exception", [CalledProcessError, OSError]) def test_invalid_cmake(exception, mocker): exceptions = { OSError: OSError('Unknown error'), CalledProcessError: CalledProcessError([CMAKE_DEFAULT_EXECUTABLE, '--version'], 1) } check_output_original = check_output def check_output_mock(*args, **kwargs): if args[0] == [CMAKE_DEFAULT_EXECUTABLE, '--version']: raise exceptions[exception] check_output_original(*args, **kwargs) mocker.patch('skbuild.cmaker.subprocess.check_output', new=check_output_mock) with push_dir(): @project_setup_py_test("hello-no-language", ["build"], disable_languages_test=True) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "Problem with the CMake installation, aborting build." in message def test_first_invalid_generator(mocker, capfd): platform = get_platform() default_generators = [CMakeGenerator('Invalid')] default_generators.extend(platform.default_generators) mocker.patch.object(type(platform), 'default_generators', new_callable=mocker.PropertyMock, return_value=default_generators) mocker.patch('skbuild.cmaker.get_platform', return_value=platform) with push_dir(), push_env(CMAKE_GENERATOR=None): @project_setup_py_test("hello-no-language", ["build"]) def run_build(): pass run_build() _, err = capfd.readouterr() assert "CMake Error: Could not create named generator Invalid" in err def test_invalid_generator(mocker, capfd): platform = get_platform() mocker.patch.object(type(platform), 'default_generators', new_callable=mocker.PropertyMock, return_value=[CMakeGenerator('Invalid')]) mocker.patch('skbuild.cmaker.get_platform', return_value=platform) with push_dir(), push_env(CMAKE_GENERATOR=None): @project_setup_py_test("hello-no-language", ["build"]) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) _, err = capfd.readouterr() assert "CMake Error: Could not create named generator Invalid" in err assert failed assert "scikit-build could not get a working generator for your system." \ " Aborting build." in message scikit-build-0.11.1/tests/test_cmakelists_not_in_top_level_dir.py000066400000000000000000000047561365474353700254010ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_cmakelists_not_in_top_level_dir ---------------------------------- Tries to build and test the `cmakelists_not_in_top_level_dir` sample project. It basically checks that using the `cmake_source_dir` setup keyword works. """ import glob import pytest import textwrap from skbuild.exceptions import SKBuildError from . import (_tmpdir, execute_setup_py, project_setup_py_test) from .pytest_helpers import check_sdist_content @project_setup_py_test("cmakelists-not-in-top-level-dir", ["build"], disable_languages_test=True) def test_build(capsys): out, err = capsys.readouterr() dist_warning = "Unknown distribution option: 'cmake_source_dir'" assert (dist_warning not in err and dist_warning not in out) @pytest.mark.parametrize("cmake_source_dir, expected_failed", ( ("invalid", True), ("", False), (".", False), )) def test_cmake_source_dir(cmake_source_dir, expected_failed): tmp_dir = _tmpdir('test_cmake_source_dir') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_source_dir", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_source_dir="{cmake_source_dir}" ) """.format(cmake_source_dir=cmake_source_dir) )) failed = False message = "" try: with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed == expected_failed if failed: assert "'cmake_source_dir' set to a nonexistent directory." in message @project_setup_py_test("cmakelists-not-in-top-level-dir", ["sdist"], disable_languages_test=True) def test_hello_sdist(): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'hello-1.2.3/hello/_hello.cxx', 'hello-1.2.3/hello/CMakeLists.txt', 'hello-1.2.3/hello/__init__.py', 'hello-1.2.3/hello/__main__.py', 'hello-1.2.3/setup.py', ] sdist_archive = None if sdists_tar: sdist_archive = 'dist/hello-1.2.3.tar.gz' elif sdists_zip: sdist_archive = 'dist/hello-1.2.3.zip' check_sdist_content(sdist_archive, 'hello-1.2.3', expected_content) scikit-build-0.11.1/tests/test_cmaker.py000066400000000000000000000152501365474353700202160ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_cmaker ---------------------------------- Tests for CMaker functionality. """ import os import pytest import re import textwrap from skbuild.cmaker import CMaker, has_cmake_cache_arg from skbuild.constants import CMAKE_BUILD_DIR, CMAKE_DEFAULT_EXECUTABLE, CMAKE_INSTALL_DIR from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir, to_unix_path from . import _tmpdir, get_cmakecache_variables def test_get_python_version(): assert re.match(r'^[23](\.?)[0-9]$', CMaker.get_python_version()) def test_get_python_include_dir(): python_include_dir = CMaker.get_python_include_dir(CMaker.get_python_version()) assert python_include_dir assert os.path.exists(python_include_dir) def test_get_python_library(): python_library = CMaker.get_python_library(CMaker.get_python_version()) assert python_library assert os.path.exists(python_library) def test_cmake_executable(): assert CMaker().cmake_executable == CMAKE_DEFAULT_EXECUTABLE def test_has_cmake_cache_arg(): cmake_args = ['-DFOO:STRING=42', '-DBAR', '-DCLIMBING:BOOL=ON'] assert has_cmake_cache_arg(cmake_args, "FOO", "42") assert not has_cmake_cache_arg(cmake_args, "foo", "42") assert not has_cmake_cache_arg(cmake_args, "FOO", "43") assert not has_cmake_cache_arg(cmake_args, "BAR") assert not has_cmake_cache_arg(cmake_args, "BA") assert not has_cmake_cache_arg(cmake_args, "BAR", None) assert not has_cmake_cache_arg(cmake_args, "BAR", "42") assert has_cmake_cache_arg(cmake_args, "CLIMBING") assert has_cmake_cache_arg(cmake_args, "CLIMBING", None) assert has_cmake_cache_arg(cmake_args, "CLIMBING", "ON") override = ['-DOTHER:STRING=C', '-DOVERRIDE:STRING=A', '-DOVERRIDE:STRING=B'] assert has_cmake_cache_arg(override, 'OVERRIDE') assert has_cmake_cache_arg(override, 'OVERRIDE', 'B') assert not has_cmake_cache_arg(override, 'OVERRIDE', 'A') # ensure overriding doesn't magically have side effects. assert has_cmake_cache_arg(override, 'OTHER') assert has_cmake_cache_arg(override, 'OTHER', 'C') assert not has_cmake_cache_arg(override, 'OTHER', 'A') assert not has_cmake_cache_arg(override, 'OTHER', 'B') def test_make_without_build_dir_fails(): src_dir = _tmpdir('test_make_without_build_dir_fails') with push_dir(str(src_dir)), pytest.raises(SKBuildError) as excinfo: CMaker().make() assert "Did you forget to run configure before make" in str(excinfo.value) def test_make_without_configure_fails(capfd): src_dir = _tmpdir('test_make_without_configure_fails') src_dir.ensure(CMAKE_BUILD_DIR(), dir=1) with push_dir(str(src_dir)), pytest.raises(SKBuildError) as excinfo: CMaker().make() _, err = capfd.readouterr() assert "An error occurred while building with CMake." in str(excinfo.value) assert "Error: could not load cache" in err @pytest.mark.parametrize("configure_with_cmake_source_dir", (True, False)) def test_make(configure_with_cmake_source_dir, capfd): tmp_dir = _tmpdir('test_make') with push_dir(str(tmp_dir)): src_dir = tmp_dir.ensure('SRC', dir=1) src_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.txt" "# foo") install(FILES "${CMAKE_BINARY_DIR}/foo.txt" DESTINATION ".") install(CODE "message(STATUS \\"Project has been installed\\")") message(STATUS "CMAKE_SOURCE_DIR:${CMAKE_SOURCE_DIR}") message(STATUS "CMAKE_BINARY_DIR:${CMAKE_BINARY_DIR}") """ )) src_dir.ensure(CMAKE_BUILD_DIR(), dir=1) with push_dir(str(src_dir) if not configure_with_cmake_source_dir else str(tmp_dir.ensure('BUILD', dir=1))): cmkr = CMaker() config_kwargs = {} if configure_with_cmake_source_dir: config_kwargs['cmake_source_dir'] = str(src_dir) env = cmkr.configure(**config_kwargs) cmkr.make(env=env) messages = ["Project has been installed"] if configure_with_cmake_source_dir: messages += [ "/SRC", "/BUILD/{}".format(to_unix_path(CMAKE_BUILD_DIR())), "/BUILD/{}/./foo.txt".format(to_unix_path(CMAKE_INSTALL_DIR())) ] else: messages += [ "/SRC", "/SRC/{}".format(to_unix_path(CMAKE_BUILD_DIR())), "/SRC/{}/./foo.txt".format(to_unix_path(CMAKE_INSTALL_DIR())), ] out, _ = capfd.readouterr() for message in messages: assert message in out def test_configure_with_cmake_args(capfd): tmp_dir = _tmpdir('test_configure_with_cmake_args') with push_dir(str(tmp_dir)): tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) # Do not complain about missing arguments passed to the main # project foreach(unused_var IN ITEMS ${CMAKE_EXPECTED_BAR} ${CMAKE_EXPECTED_FOO} ${PYTHON_EXECUTABLE} ${PYTHON_INCLUDE_DIR} ${PYTHON_LIBRARY} ${PYTHON_VERSION_STRING} ${SKBUILD} ) endforeach() """ )) with push_dir(str(tmp_dir)): cmkr = CMaker() cmkr.configure(clargs=[ '-DCMAKE_EXPECTED_FOO:STRING=foo', '-DCMAKE_EXPECTED_BAR:STRING=bar' ], cleanup=False) cmakecache = tmp_dir.join( "_cmake_test_compile", "build", "CMakeCache.txt") assert cmakecache.exists() variables = get_cmakecache_variables(str(cmakecache)) assert variables.get('CMAKE_EXPECTED_FOO', (None, None))[1] == "foo" assert variables.get('CMAKE_EXPECTED_BAR', (None, None))[1] == "bar" unexpected = "Manually-specified variables were not used by the project" _, err = capfd.readouterr() assert unexpected not in err def test_check_for_bad_installs(tmpdir): with push_dir(str(tmpdir)): tmpdir.ensure(CMAKE_BUILD_DIR(), "cmake_install.cmake").write(textwrap.dedent( """ file(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/../hello" TYPE FILE FILES "/path/to/hello/world.py") """ )) with pytest.raises(SKBuildError) as excinfo: CMaker.check_for_bad_installs() assert "CMake-installed files must be within the project root" in str(excinfo.value) scikit-build-0.11.1/tests/test_command_line.py000066400000000000000000000132761365474353700214070ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_command_line ---------------------------------- Tests for various command line functionality. """ import os import pytest from skbuild.constants import CMAKE_BUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir, to_platform_path from . import ( execute_setup_py, get_cmakecache_variables, initialize_git_repo_and_commit, prepare_project, project_setup_py_test, ) @project_setup_py_test("hello-no-language", ["--help"], disable_languages_test=True) def test_help(capsys): out, err = capsys.readouterr() assert "scikit-build options" not in out assert "Global options:" in out assert "usage:" in out @project_setup_py_test("hello-no-language", ["--help-commands"], disable_languages_test=True) def test_help_commands(capsys): out, err = capsys.readouterr() assert "scikit-build options" in out assert "--build-type" in out assert "Global options:" not in out assert "usage:" in out @project_setup_py_test("hello-no-language", ["--author", "--name"], disable_languages_test=True) def test_metadata_display(capsys): out, err = capsys.readouterr() assert "scikit-build options" not in out assert "Global options:" not in out assert "usage:" not in out assert "The scikit-build team" == out.splitlines()[0] assert "hello_no_language" == out.splitlines()[1] def test_no_command(): with push_dir(): @project_setup_py_test("hello-no-language", [], disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = 'error: no commands supplied' in e.args[0] assert failed assert not os.path.exists('_skbuild') def test_invalid_command(): with push_dir(): @project_setup_py_test("hello-no-language", ["unknown"], disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = 'error: invalid command' in e.args[0] assert failed assert not os.path.exists('_skbuild') def test_too_many_separators(): with push_dir(): @project_setup_py_test("hello-no-language", ["--"] * 3, disable_languages_test=True) def run(): pass failed = False try: run() except SystemExit as e: failed = e.args[0].startswith('ERROR: Too many') assert failed @project_setup_py_test("hello-no-language", ["build", "--", "-DMY_CMAKE_VARIABLE:BOOL=1"], disable_languages_test=True) def test_cmake_args(capfd): out, err = capfd.readouterr() assert "Manually-specified variables were not used by the project" in err assert "MY_CMAKE_VARIABLE" in err @project_setup_py_test("hello-no-language", ["-DMY_CMAKE_VARIABLE:BOOL=1", "build"], disable_languages_test=True) def test_cmake_cache_entry_as_global_option(capfd): out, err = capfd.readouterr() assert "Manually-specified variables were not used by the project" in err assert "MY_CMAKE_VARIABLE" in err def test_cmake_initial_cache_as_global_option(tmpdir): project = "hello-no-language" prepare_project(project, tmpdir) initialize_git_repo_and_commit(tmpdir, verbose=True) initial_cache = tmpdir.join("initial-cache.txt") initial_cache.write("""set(MY_CMAKE_VARIABLE "1" CACHE BOOL "My cache variable")""") try: with execute_setup_py(tmpdir, ["-C%s" % str(initial_cache), "build"], disable_languages_test=True): pass except SystemExit as exc: assert exc.code == 0 cmakecache_txt = tmpdir.join(CMAKE_BUILD_DIR(), "CMakeCache.txt") assert cmakecache_txt.exists() assert get_cmakecache_variables(str(cmakecache_txt)).get('MY_CMAKE_VARIABLE', (None, None)) == ('BOOL', '1') def test_cmake_executable_arg(): cmake_executable = "/path/to/invalid/cmake" @project_setup_py_test("hello-no-language", ["--cmake-executable", cmake_executable, "build"], disable_languages_test=True) def should_fail(): pass failed = False message = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "Problem with the CMake installation, aborting build. CMake executable is %s" % cmake_executable in message @pytest.mark.parametrize("action", ['sdist', 'bdist_wheel']) @pytest.mark.parametrize("hide_listing", [True, False]) def test_hide_listing(action, hide_listing, capfd): cmd = [action] if hide_listing: cmd.insert(0, "--hide-listing") @project_setup_py_test("test-hide-listing", cmd, verbose_git=False, disable_languages_test=True) def run(): pass run() out, _ = capfd.readouterr() if hide_listing: assert to_platform_path("bonjour/__init__.py") not in out else: assert to_platform_path("bonjour/__init__.py") in out if action == "sdist": assert "copied 10 files" in out elif action == "bdist_wheel": assert "copied 6 files" in out # build_py assert "copied 9 files" in out # install_lib assert "copied 0 files" in out # install_scripts @project_setup_py_test("hello-no-language", ["--force-cmake", "--help"], disable_languages_test=True) def test_run_cmake_arg(capfd): out, _ = capfd.readouterr() assert "Generating done" in out @project_setup_py_test("hello-no-language", ["--skip-cmake", "build"], disable_languages_test=True) def test_skip_cmake_arg(capfd): out, _ = capfd.readouterr() assert "Generating done" not in out scikit-build-0.11.1/tests/test_constants.py000066400000000000000000000006351365474353700207710ustar00rootroot00000000000000 import os import sys from skbuild.constants import SKBUILD_DIR, set_skbuild_plat_name, skbuild_plat_name def test_set_skbuild_plat_name(): try: previous_plat_name = skbuild_plat_name() set_skbuild_plat_name("plat-name") assert os.path.join("_skbuild", "plat-name-{}.{}".format(*sys.version_info[:2])) == SKBUILD_DIR() finally: set_skbuild_plat_name(previous_plat_name) scikit-build-0.11.1/tests/test_distribution.py000066400000000000000000000033101365474353700214650ustar00rootroot00000000000000 import os import pytest from path import Path from . import initialize_git_repo_and_commit, prepare_project DIST_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), '../dist')) # Test if package can be imported to allow testing on # conda-forge where ``pytest-virtualenv`` is not available. try: import pytest_virtualenv # noqa: F401 HAS_PYTEST_VIRTUALENV = True except ImportError: HAS_PYTEST_VIRTUALENV = False @pytest.mark.skipif(not HAS_PYTEST_VIRTUALENV, reason="pytest_virtualenv not available. See #228") def test_source_distribution(virtualenv): sdists = Path(DIST_DIR).files(match="*.tar.gz") if Path(DIST_DIR).exists() else [] if not sdists: pytest.skip("no source distribution available") assert len(sdists) == 1 virtualenv.run("pip install %s" % sdists[0]) assert "scikit-build" in virtualenv.installed_packages() prepare_project("hello-no-language", virtualenv.workspace, force=True) initialize_git_repo_and_commit(virtualenv.workspace, verbose=False) virtualenv.run("python setup.py bdist_wheel") @pytest.mark.skipif(not HAS_PYTEST_VIRTUALENV, reason="pytest_virtualenv not available. See #228") def test_wheel(virtualenv): wheels = Path(DIST_DIR).files(match="*.whl") if Path(DIST_DIR).exists() else [] if not wheels: pytest.skip("no wheel available") assert len(wheels) == 1 virtualenv.run("pip install %s" % wheels[0]) assert "scikit-build" in virtualenv.installed_packages() prepare_project("hello-no-language", virtualenv.workspace, force=True) initialize_git_repo_and_commit(virtualenv.workspace, verbose=False) virtualenv.run("python setup.py bdist_wheel") scikit-build-0.11.1/tests/test_filter_manifest.py000066400000000000000000000015431365474353700221270ustar00rootroot00000000000000 import glob from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project ) from .pytest_helpers import check_wheel_content def test_bdist_wheel_command(): project = "test-filter-manifest" expected_content = [ 'hello/__init__.py', 'hello/swig_mwe.py', 'hello/_swig_mwe.pyd', 'hello-1.2.3.data/data/bin/hello', ] expected_distribution_name = 'hello-1.2.3' tmp_dir = _tmpdir('test_bdist_wheel_command') prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) relative_setup_path = 'wrapping/python/' with execute_setup_py(tmp_dir.join(relative_setup_path), ["bdist_wheel"]): whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.11.1/tests/test_hello_cpp.py000066400000000000000000000136051365474353700207230ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_hello_cpp ---------------------------------- Tries to build and test the `hello-cpp` sample project. """ import glob import os import pytest from skbuild.constants import CMAKE_BUILD_DIR, CMAKE_INSTALL_DIR, SKBUILD_DIR from skbuild.utils import push_dir from . import get_ext_suffix, project_setup_py_test from . import (_copy_dir, _tmpdir, SAMPLES_DIR) from .pytest_helpers import check_sdist_content, check_wheel_content def test_hello_builds(): with push_dir(): @project_setup_py_test("hello-cpp", ["build"]) def run(): pass # Check that a project can be build twice in a row # See issue scikit-build#120 tmp_dir = run()[0] @project_setup_py_test("hello-cpp", ["build"], tmp_dir=tmp_dir) def another_run(): pass another_run() # @project_setup_py_test("hello-cpp", ["test"]) # def test_hello_works(): # pass @project_setup_py_test("hello-cpp", ["sdist"]) def test_hello_sdist(): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'hello-1.2.3/CMakeLists.txt', 'hello-1.2.3/bonjour/__init__.py', 'hello-1.2.3/bonjour/data/ciel.txt', 'hello-1.2.3/bonjour/data/soleil.txt', 'hello-1.2.3/bonjour/data/terre.txt', 'hello-1.2.3/bonjourModule.py', 'hello-1.2.3/hello/_hello.cxx', 'hello-1.2.3/hello/CMakeLists.txt', 'hello-1.2.3/hello/__init__.py', 'hello-1.2.3/hello/__main__.py', 'hello-1.2.3/setup.py', ] sdist_archive = 'dist/hello-1.2.3.zip' if sdists_tar: sdist_archive = 'dist/hello-1.2.3.tar.gz' check_sdist_content(sdist_archive, 'hello-1.2.3', expected_content) def test_hello_wheel(): expected_content = [ 'hello/_hello%s' % get_ext_suffix(), 'hello/__init__.py', 'hello/__main__.py', 'hello/world.py', 'helloModule.py', 'bonjour/__init__.py', 'bonjour/data/ciel.txt', 'bonjour/data/soleil.txt', 'bonjour/data/terre.txt', 'bonjourModule.py' ] expected_distribution_name = 'hello-1.2.3' @project_setup_py_test("hello-cpp", ["bdist_wheel"]) def build_wheel(): whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) os.remove(whls[0]) assert not os.path.exists(whls[0]) assert os.path.exists(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) os.remove(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) tmp_dir = build_wheel()[0] @project_setup_py_test("hello-cpp", ["--skip-cmake", "bdist_wheel"], tmp_dir=tmp_dir) def build_wheel_skip_cmake(): assert not os.path.exists(os.path.join(CMAKE_BUILD_DIR(), "CMakeCache.txt")) whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) build_wheel_skip_cmake() @pytest.mark.parametrize("dry_run", ['with-dry-run', 'without-dry-run']) def test_hello_clean(dry_run, capfd): with push_dir(): dry_run = dry_run == 'with-dry-run' @project_setup_py_test("hello-cpp", ["build"]) def run_build(): pass tmp_dir = run_build()[0] assert tmp_dir.join(SKBUILD_DIR()).exists() # XXX Since using capfd.disabled() context manager prevents # the output from being captured atfer it exits, we display # a separator allowing to differentiate the build and clean output. print("<<-->>") clean_args = ["clean"] if dry_run: clean_args.append("--dry-run") @project_setup_py_test("hello-cpp", clean_args, tmp_dir=tmp_dir) def run_clean(): pass run_clean() if not dry_run: assert not tmp_dir.join(SKBUILD_DIR()).exists() else: assert tmp_dir.join(SKBUILD_DIR()).exists() build_out, clean_out = capfd.readouterr()[0].split('<<-->>') assert 'Build files have been written to' in build_out assert 'Build files have been written to' not in clean_out def test_hello_cleans(capfd): with push_dir(): tmp_dir = _tmpdir("test_hello_cleans") _copy_dir(tmp_dir, os.path.join(SAMPLES_DIR, "hello-cpp")) @project_setup_py_test("hello-cpp", ["build"], tmp_dir=tmp_dir) def run_build(): pass @project_setup_py_test("hello-cpp", ["clean"], tmp_dir=tmp_dir) def run_clean(): pass # Check that a project can be cleaned twice in a row run_build() print("<<-->>") run_clean() print("<<-->>") run_clean() _, clean1_out, clean2_out = \ capfd.readouterr()[0].split('<<-->>') clean1_out = clean1_out.strip() clean2_out = clean2_out.strip() assert "running clean" == clean1_out.splitlines()[0] assert "removing '{}'".format(CMAKE_INSTALL_DIR()) == clean1_out.splitlines()[1] assert "removing '{}'".format(CMAKE_BUILD_DIR()) == clean1_out.splitlines()[2] assert "removing '{}'".format(SKBUILD_DIR()) == clean1_out.splitlines()[3] assert "running clean" == clean2_out @project_setup_py_test("hello-cpp", ["develop"]) def test_hello_develop(): for expected_file in [ # These files are the "regular" source files 'setup.py', 'CMakeLists.txt', 'bonjour/__init__.py', 'bonjourModule.py', 'hello/__init__.py', 'hello/__main__.py', 'hello/_hello.cxx', 'hello/CMakeLists.txt', # These files are "generated" by CMake and # are copied from CMAKE_INSTALL_DIR 'hello/_hello%s' % get_ext_suffix(), 'hello/world.py', 'helloModule.py' ]: assert os.path.exists(expected_file) scikit-build-0.11.1/tests/test_hello_cython.py000066400000000000000000000031641365474353700214440ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_hello_cython ---------------------------------- Tries to build and test the `hello-cython` sample project. """ import glob from . import get_ext_suffix, project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content @project_setup_py_test("hello-cython", ["build"]) def test_hello_cython_builds(): pass # @project_setup_py_test("hello-cython", ["test"]) # def test_hello_cython_works(): # pass @project_setup_py_test("hello-cython", ["sdist"]) def test_hello_cython_sdist(): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'hello-cython-1.2.3/CMakeLists.txt', 'hello-cython-1.2.3/hello/_hello.pyx', 'hello-cython-1.2.3/hello/CMakeLists.txt', 'hello-cython-1.2.3/hello/__init__.py', 'hello-cython-1.2.3/hello/__main__.py', 'hello-cython-1.2.3/setup.py', ] sdist_archive = 'dist/hello-cython-1.2.3.zip' if sdists_tar: sdist_archive = 'dist/hello-cython-1.2.3.tar.gz' check_sdist_content(sdist_archive, 'hello-cython-1.2.3', expected_content) @project_setup_py_test("hello-cython", ["bdist_wheel"]) def test_hello_cython_wheel(): expected_content = [ 'hello_cython/_hello%s' % get_ext_suffix(), 'hello_cython/__init__.py', 'hello_cython/__main__.py' ] expected_distribution_name = 'hello_cython-1.2.3' whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.11.1/tests/test_hello_pure.py000066400000000000000000000042011365474353700211040ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_hello_pure ---------------------------------- Tries to build and test the `hello-pure` sample project. """ import glob from skbuild.constants import SKBUILD_DIR from skbuild.utils import push_dir from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content @project_setup_py_test("hello-pure", ["build"], disable_languages_test=True) def test_hello_pure_builds(capsys): out, _ = capsys.readouterr() assert "skipping skbuild (no CMakeLists.txt found)" in out # @project_setup_py_test("hello-pure", ["test"]) # def test_hello_cython_works(): # pass @project_setup_py_test("hello-pure", ["sdist"], disable_languages_test=True) def test_hello_pure_sdist(): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'hello-pure-1.2.3/hello/__init__.py', 'hello-pure-1.2.3/setup.py', ] sdist_archive = 'dist/hello-pure-1.2.3.zip' if sdists_tar: sdist_archive = 'dist/hello-pure-1.2.3.tar.gz' check_sdist_content(sdist_archive, 'hello-pure-1.2.3', expected_content) @project_setup_py_test("hello-pure", ["bdist_wheel"], disable_languages_test=True) def test_hello_pure_wheel(): expected_content = [ 'hello/__init__.py' ] expected_distribution_name = 'hello_pure-1.2.3' whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content, pure=True) def test_hello_clean(capfd): with push_dir(): @project_setup_py_test("hello-pure", ["build"], disable_languages_test=True) def run_build(): pass tmp_dir = run_build()[0] assert tmp_dir.join(SKBUILD_DIR()).exists() @project_setup_py_test("hello-pure", ["clean"], tmp_dir=tmp_dir, disable_languages_test=True) def run_clean(): pass run_clean() assert not tmp_dir.join(SKBUILD_DIR()).exists() out = capfd.readouterr()[0] assert 'Build files have been written to' not in out scikit-build-0.11.1/tests/test_include_exclude_data.py000066400000000000000000000065131365474353700231030ustar00rootroot00000000000000import glob import os from skbuild.utils import to_unix_path from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content def check_whls(project_name): whls = glob.glob('dist/*.whl') assert len(whls) == 1 assert not whls[0].endswith('-none-any.whl') expected_content = [ 'hello/__init__.py', 'hello/cmake_generated_module.py', 'hello/data/subdata/hello_data1_include_from_manifest.txt', 'hello/data/subdata/hello_data2_include_from_manifest.txt', 'hello/data/subdata/hello_data3_cmake_generated.txt', 'hello/hello_data1_cmake_generated.txt', 'hello/hello_data2_cmake_generated.txt', 'hello/hello_include_from_manifest.txt', 'hello2/__init__.py', 'hello2/hello2_data1_cmake_generated.txt', 'hello2/hello2_data2_cmake_generated.txt', 'hello2/data2/subdata2/hello2_data3_cmake_generated.txt', 'hello2/data2/subdata2/hello2_data1_include_from_manifest.txt', 'hello2/data2/subdata2/hello2_data2_include_from_manifest.txt', 'hello2/hello2_include_from_manifest.txt', ] expected_distribution_name = project_name check_wheel_content(whls[0], expected_distribution_name, expected_content) def check_sdist(proj, base=''): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ to_unix_path(os.path.join(proj, 'setup.py')), to_unix_path(os.path.join(proj, base, 'hello/__init__.py')), to_unix_path(os.path.join(proj, base, 'hello/data/subdata/hello_data1_include_from_manifest.txt')), to_unix_path(os.path.join(proj, base, 'hello/data/subdata/hello_data2_include_from_manifest.txt')), to_unix_path(os.path.join( proj, base, 'hello/data/subdata/hello_data4_include_from_manifest_and_exclude_from_setup.txt')), to_unix_path(os.path.join(proj, base, 'hello/hello_include_from_manifest.txt')), to_unix_path(os.path.join(proj, base, 'hello2/__init__.py')), to_unix_path(os.path.join(proj, base, 'hello2/data2/subdata2/hello2_data1_include_from_manifest.txt')), to_unix_path(os.path.join(proj, base, 'hello2/data2/subdata2/hello2_data2_include_from_manifest.txt')), to_unix_path(os.path.join( proj, base, 'hello2/data2/subdata2/hello2_data4_include_from_manifest_and_exclude_from_setup.txt')), to_unix_path(os.path.join(proj, base, 'hello2/hello2_include_from_manifest.txt')) ] sdist_archive = 'dist/%s.zip' % proj if sdists_tar: sdist_archive = 'dist/%s.tar.gz' % proj check_sdist_content(sdist_archive, proj, expected_content) @project_setup_py_test("test-include-exclude-data", ["bdist_wheel"]) def test_include_exclude_data(): check_whls('test_include_exclude_data-0.1.0') @project_setup_py_test("test-include-exclude-data", ["sdist"]) def test_hello_sdist(): check_sdist('test_include_exclude_data-0.1.0') @project_setup_py_test("test-include-exclude-data-with-base", ["bdist_wheel"]) def test_include_exclude_data_with_base(): check_whls('test_include_exclude_data_with_base-0.1.0') @project_setup_py_test("test-include-exclude-data-with-base", ["sdist"]) def test_hello_sdist_with_base(): check_sdist('test_include_exclude_data_with_base-0.1.0', base='src') scikit-build-0.11.1/tests/test_issue274_support_default_package_dir.py000066400000000000000000000014251365474353700261510ustar00rootroot00000000000000 from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project, project_setup_py_test, push_dir ) @project_setup_py_test("issue-274-support-default-package-dir", ["install"], disable_languages_test=True) def test_install_command(): pass def test_test_command(): with push_dir(): tmp_dir = _tmpdir('test_test_command') project = "issue-274-support-default-package-dir" prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) try: with execute_setup_py(tmp_dir, ["test"], disable_languages_test=True): pass except SystemExit as exc: assert exc.code == 0 assert tmp_dir.join("test_hello.completed.txt").exists() scikit-build-0.11.1/tests/test_issue274_support_one_package_without_package_dir.py000066400000000000000000000014551365474353700305470ustar00rootroot00000000000000 from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project, project_setup_py_test, push_dir ) @project_setup_py_test("issue-274-support-one-package-without-package-dir", ["install"], disable_languages_test=True) def test_install_command(): pass def test_test_command(): with push_dir(): tmp_dir = _tmpdir('test_test_command') project = "issue-274-support-one-package-without-package-dir" prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) try: with execute_setup_py(tmp_dir, ["test"], disable_languages_test=True): pass except SystemExit as exc: assert exc.code == 0 assert tmp_dir.join("test_hello.completed.txt").exists() scikit-build-0.11.1/tests/test_issue284_build_ext_inplace.py000066400000000000000000000005341365474353700240730ustar00rootroot00000000000000 import os from . import get_ext_suffix, project_setup_py_test @project_setup_py_test("issue-284-build-ext-inplace", ["build_ext", "--inplace"], disable_languages_test=True) def test_build_ext_inplace_command(): assert os.path.exists('hello/_hello_sk%s' % get_ext_suffix()) assert os.path.exists('hello/_hello_ext%s' % get_ext_suffix()) scikit-build-0.11.1/tests/test_issue334_configure_cmakelists_non_cp1252_encoding.py000066400000000000000000000003021365474353700303220ustar00rootroot00000000000000 from . import project_setup_py_test @project_setup_py_test("issue-334-configure-cmakelist-non-cp1252-encoding", ["install"], disable_languages_test=True) def test_install_command(): pass scikit-build-0.11.1/tests/test_issue335_support_cmake_source_dir.py000066400000000000000000000020701365474353700255050ustar00rootroot00000000000000 import glob from . import ( _tmpdir, execute_setup_py, initialize_git_repo_and_commit, prepare_project ) from .pytest_helpers import check_wheel_content def test_bdist_wheel_command(): project = "issue-335-support-cmake-source-dir" expected_content = [ 'hello/__init__.py', 'hello/swig_mwe.py', 'hello/_swig_mwe.pyd', 'hello-1.2.3.data/data/bin/hello', 'hello-1.2.3.data/data/lib/static/libbar.a', 'hello-1.2.3.data/data/lib/static/libfoo.a', 'hello-1.2.3.data/data/include/bar.h', 'hello-1.2.3.data/data/include/foo.h' ] expected_distribution_name = 'hello-1.2.3' tmp_dir = _tmpdir('test_bdist_wheel_command') prepare_project(project, tmp_dir) initialize_git_repo_and_commit(tmp_dir, verbose=True) relative_setup_path = 'wrapping/python/' with execute_setup_py(tmp_dir.join(relative_setup_path), ["bdist_wheel"]): whls = glob.glob('dist/*.whl') assert len(whls) == 1 check_wheel_content(whls[0], expected_distribution_name, expected_content) scikit-build-0.11.1/tests/test_issue342_cmake_osx_args_in_setup.py000066400000000000000000000141601365474353700253070ustar00rootroot00000000000000import pytest import sys import textwrap import platform import skbuild.constants from . import _tmpdir, execute_setup_py, push_env params = "osx_deployment_target_env_var,cli_setup_args," \ "keyword_cmake_args,cli_cmake_args,expected_cmake_osx_deployment_target" @pytest.mark.parametrize(params, [ # default plat_name is 'macosx-10.6-x86_64' ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.6" ), ( # osx_deployment_target_env_var "10.7", # cli_setup_args [], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7" ), ( # osx_deployment_target_env_var "10.7", # cli_setup_args ['--plat-name', 'macosx-10.9-x86_64'], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.9" ), ( # osx_deployment_target_env_var None, # cli_setup_args ['--plat-name', 'macosx-10.6-x86_64'], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.6" ), ( # osx_deployment_target_env_var None, # cli_setup_args ['--plat-name', 'macosx-10.7-x86_64'], # keyword_cmake_args [], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7" ), ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7'], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7" ), ( # osx_deployment_target_env_var None, # cli_setup_args ['--plat-name', 'macosx-10.12-x86_64'], # keyword_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7'], # cli_cmake_args [], # expected_cmake_osx_deployment_target "10.7" ), ( # osx_deployment_target_env_var None, # cli_setup_args [], # keyword_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7'], # cli_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.8'], # expected_cmake_osx_deployment_target "10.8" ), ( # osx_deployment_target_env_var None, # cli_setup_args ['--plat-name', 'macosx-10.12-x86_64'], # keyword_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.7'], # cli_cmake_args ['-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=10.8'], # expected_cmake_osx_deployment_target "10.8" ), ]) def test_cmake_args_keyword_osx_default( osx_deployment_target_env_var, cli_setup_args, keyword_cmake_args, cli_cmake_args, expected_cmake_osx_deployment_target, mocker): tmp_dir = _tmpdir('cmake_args_keyword_osx_default') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_args_keyword_osx_default", version="1.2.3", description="A minimal example package", author="The scikit-build team", license="MIT", cmake_args=[{cmake_args}] ) """.format(cmake_args=",".join(["'%s'" % arg for arg in keyword_cmake_args])) )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ message(FATAL_ERROR "This error message should not be displayed") """ )) mock_configure = mocker.patch('skbuild.cmaker.CMaker.configure', side_effect=RuntimeError("exit skbuild")) try: # allow to run the test on any platform saved_platform = sys.platform sys.platform = "darwin" def mock_mac_ver(): return "10.6", None, "x84_64" saved_mac_ver = platform.mac_ver platform.mac_ver = mock_mac_ver saved_SKBUILD_PLAT_NAME = skbuild.constants._SKBUILD_PLAT_NAME with push_env(MACOSX_DEPLOYMENT_TARGET=osx_deployment_target_env_var): skbuild.constants._SKBUILD_PLAT_NAME = skbuild.constants._default_skbuild_plat_name() with pytest.raises(RuntimeError, match="exit skbuild"): with execute_setup_py(tmp_dir, ['build'] + cli_setup_args + ['--'] + cli_cmake_args): pass finally: sys.platform = saved_platform platform.mac_ver = saved_mac_ver skbuild.constants._SKBUILD_PLAT_NAME = saved_SKBUILD_PLAT_NAME assert mock_configure.call_count == 1 current_cmake_args = mock_configure.call_args[0][0] # Since additional cmake argument are appended, it is not possible to simply # compare lists. found_cmake_osx_deployment_target = False for cmake_arg in reversed(current_cmake_args): if cmake_arg.startswith("-DCMAKE_OSX_DEPLOYMENT_TARGET:STRING="): if cmake_arg.endswith(expected_cmake_osx_deployment_target): found_cmake_osx_deployment_target = True break assert found_cmake_osx_deployment_target, \ textwrap.dedent(""" Argument -DCMAKE_OSX_DEPLOYMENT_TARGET:STRING=%s is NOT found near the end of current list of arguments: keyword_cmake_args : %s cli_cmake_args : %s current_cmake_args: %s """ % (expected_cmake_osx_deployment_target, keyword_cmake_args, cli_cmake_args, current_cmake_args)) scikit-build-0.11.1/tests/test_issue352_isolated_environment_support.py000066400000000000000000000071261365474353700264450ustar00rootroot00000000000000import textwrap import os import pytest import skbuild from skbuild.constants import CMAKE_BUILD_DIR from . import _tmpdir, execute_setup_py def test_isolated_env_trigger_reconfigure(mocker): tmp_dir = _tmpdir('isolated_env_trigger_reconfigure') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_isolated_env_trigger_reconfigure", version="1.2.3", description="A minimal example package", author="The scikit-build team", license="MIT", ) """ )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ message(FATAL_ERROR "This error message should not be displayed") """ )) # # mock configure # def fake_configure(*args, **kwargs): # Simulate a successful configuration creating a CMakeCache.txt tmp_dir.ensure(CMAKE_BUILD_DIR(), dir=1).join('CMakeCache.txt').write(textwrap.dedent( """ //Name of generator. CMAKE_GENERATOR:INTERNAL=Ninja """ )) # Skip real configuration creating the CMakeCache.txt expected by # "skbuild.setuptools_wrap._load_cmake_spec()" function mocker.patch('skbuild.cmaker.CMaker.configure', new=fake_configure) # # mock _save_cmake_spec # _save_cmake_spec_original = skbuild.setuptools_wrap._save_cmake_spec exit_after_saving_cmake_spec = "exit skbuild saving cmake spec" def _save_cmake_spec_mock(args): _save_cmake_spec_original(args) raise RuntimeError(exit_after_saving_cmake_spec) mocker.patch('skbuild.setuptools_wrap._save_cmake_spec', new=_save_cmake_spec_mock) # # mock make # exit_before_running_cmake = "exit skbuild running make" mocker.patch('skbuild.cmaker.CMaker.make', side_effect=RuntimeError(exit_before_running_cmake)) # first build: "configure" and "_save_cmake_spec" are expected to be called with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass # second build: no reconfiguration should happen, only "make" is expected to be called with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass # since pip updates PYTHONPATH with the temporary path # where the project dependencies are installed, we simulate # this by updating the corresponding environment variable. os.environ["PYTHONPATH"] = "/path/to/anything" # after updating the env, reconfiguration is expected with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass # no reconfiguration should happen with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass # this is the other variable set by pip when doing isolated build os.environ["PYTHONNOUSERSITE"] = "1" # after updating the env, reconfiguration is expected with pytest.raises(RuntimeError, match=exit_after_saving_cmake_spec): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass # no reconfiguration should happen with pytest.raises(RuntimeError, match=exit_before_running_cmake): with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass scikit-build-0.11.1/tests/test_issue401_sdist_with_symlinks.py000066400000000000000000000020631365474353700245210ustar00rootroot00000000000000import glob import sys import tarfile import py.path import pytest from . import project_setup_py_test from .pytest_helpers import check_sdist_content @pytest.mark.skipif(sys.platform == 'win32', reason='Symlinks not supported on Windows') @project_setup_py_test("issue-401-sdist-with-symlinks", ["sdist"]) def test_sdist_with_symlinks(): assert py.path.local('hello/VERSION').islink sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'hello-1.2.3/README', 'hello-1.2.3/setup.py', 'hello-1.2.3/VERSION', ] if sdists_tar: check_sdist_content(sdists_tar[0], 'hello-1.2.3', expected_content) with tarfile.open(sdists_tar[0], 'r:gz') as tf: member_list = tf.getnames() assert 'hello-1.2.3/VERSION' in member_list mbr = tf.getmember('hello-1.2.3/VERSION') assert not mbr.issym() elif sdists_zip: check_sdist_content(sdists_zip[0], 'hello-1.2.3', expected_content) scikit-build-0.11.1/tests/test_manifest_in.py000066400000000000000000000023011365474353700212410ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_manifest_in ---------------------------------- Tries to build and test the `manifest-in` sample project. """ import glob from . import project_setup_py_test from .pytest_helpers import check_sdist_content, check_wheel_content @project_setup_py_test("manifest-in", ["sdist"], disable_languages_test=True) def test_manifest_in_sdist(): sdists_tar = glob.glob('dist/*.tar.gz') sdists_zip = glob.glob('dist/*.zip') assert sdists_tar or sdists_zip expected_content = [ 'manifest-in-1.2.3/hello/__init__.py', 'manifest-in-1.2.3/setup.py' ] sdist_archive = 'dist/manifest-in-1.2.3.zip' if sdists_tar: sdist_archive = 'dist/manifest-in-1.2.3.tar.gz' check_sdist_content(sdist_archive, 'manifest-in-1.2.3', expected_content) @project_setup_py_test("manifest-in", ["bdist_wheel"], disable_languages_test=True) def test_manifest_in_wheel(): whls = glob.glob('dist/*.whl') assert len(whls) == 1 expected_content = [ 'hello/__init__.py' ] expected_distribution_name = 'manifest_in-1.2.3' check_wheel_content(whls[0], expected_distribution_name, expected_content, pure=True) scikit-build-0.11.1/tests/test_outside_project_root.py000066400000000000000000000024551365474353700232240ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_outside_project_root ---------------------------------- Tries to build the `fail-outside-project-root` sample project. Ensures that the attempt fails with a SystemExit exception that has an SKBuildError exception as its value. """ import pytest from skbuild.exceptions import SKBuildError from skbuild.utils import push_dir from . import project_setup_py_test @pytest.mark.parametrize("option", [ None, '-DINSTALL_FILE:BOOL=1', '-DINSTALL_PROJECT:BOOL=1' ]) def test_outside_project_root_fails(option): with push_dir(): expected_failure = False cmd = ["install"] if option is not None: expected_failure = True cmd.extend(["--", option]) @project_setup_py_test("fail-outside-project-root", cmd, disable_languages_test=True) def should_fail(): pass failed = False msg = "" try: should_fail() except SystemExit as e: failed = isinstance(e.code, SKBuildError) msg = str(e) except SKBuildError as e: failed = True msg = str(e) assert expected_failure == failed if expected_failure: assert "CMake-installed files must be within the project root." in msg scikit-build-0.11.1/tests/test_platform.py000066400000000000000000000077061365474353700206070ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_platform ---------------------------------- Tests for platforms, to verify that CMake correctly does a test compilation. """ import os import sys import platform import pytest from skbuild.platform_specifics import get_platform from skbuild.utils import mkdir_p # XXX This should probably be a constant imported from skbuild.constants test_folder = "_cmake_test_compile" # skbuild_platform is shared across each test. It's a platform-specific object # that defines default CMake generator strings. skbuild_platform = get_platform() def test_platform_has_entries(): assert(len(skbuild_platform.default_generators) > 0) def test_write_compiler_test_file(): # write the file that CMake will use to test compile (empty list indicates # we're testing no languages.) skbuild_platform.write_test_cmakelist([]) try: # verify that the test file exists (it's not valid, because it has no # languages) assert(os.path.exists(os.path.join(test_folder, "CMakeLists.txt"))) finally: skbuild_platform.cleanup_test() def test_cxx_compiler(): # Create a unique subdirectory 'foo' that is expected to be removed. test_build_folder = os.path.join(test_folder, 'build', 'foo') mkdir_p(test_build_folder) generator = skbuild_platform.get_best_generator(languages=["CXX", "C"], cleanup=False) # TODO: this isn't a true unit test. It depends on the test CMakeLists.txt # file having been written correctly. # with the known test file present, this tries to generate a makefile # (or solution, or whatever). # This test verifies that a working compiler is present on the system, but # doesn't actually compile anything. try: assert(generator is not None) assert not os.path.exists(test_build_folder) finally: skbuild_platform.cleanup_test() @pytest.mark.skipif(platform.system().lower() in ["darwin", "windows"], reason="no fortran compiler is available by default") @pytest.mark.fortran def test_fortran_compiler(): generator = skbuild_platform.get_best_generator(languages=["Fortran"]) # TODO: this isn't a true unit test. It depends on the test # CMakeLists.txt file having been written correctly. # with the known test file present, this tries to generate a # makefile (or solution, or whatever). # This test verifies that a working compiler is present on the system, but # doesn't actually compile anything. try: assert(generator is not None) finally: skbuild_platform.cleanup_test() def test_generator_cleanup(): # TODO: this isn't a true unit test. It is checking that none of the # other tests have left a mess. assert(not os.path.exists(test_folder)) @pytest.mark.parametrize("supported_platform", ['darwin', 'freebsd', 'linux', 'windows', 'os400']) def test_known_platform(supported_platform, mocker): mocker.patch('platform.system', return_value=supported_platform) platforms = { 'freebsd': 'BSD', 'linux': 'Linux', 'darwin': 'OSX', 'windows': 'Windows', 'os400': 'BSD' } expected_platform_classname = "%sPlatform" % platforms[supported_platform] assert get_platform().__class__.__name__ == expected_platform_classname def test_unsupported_platform(mocker): mocker.patch('platform.system', return_value='bogus') failed = False message = "" try: get_platform() except RuntimeError as e: failed = True message = str(e) assert failed assert "Unsupported platform: bogus." in message @pytest.mark.skipif(sys.platform != 'win32', reason='Requires Windows') def test_cached_generator(): platform = get_platform() generator = platform.get_generator('Ninja') env = generator.env assert 'Visual Studio' in env['LIB'] or 'Visual C++' in env['LIB'] scikit-build-0.11.1/tests/test_setup.py000066400000000000000000001033131365474353700201120ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_setup ---------------------------------- Tests for `skbuild.setup` function. """ import textwrap import os import pprint import py.path import pytest from distutils.core import Distribution as distutils_Distribution from mock import patch from setuptools import Distribution as setuptool_Distribution from skbuild import setup as skbuild_setup from skbuild.constants import CMAKE_INSTALL_DIR, SKBUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import get_platform from skbuild.setuptools_wrap import strip_package from skbuild.utils import (push_dir, to_platform_path) from . import (_tmpdir, execute_setup_py, initialize_git_repo_and_commit, is_site_reachable, push_argv) @pytest.mark.parametrize("distribution_type", ['unknown', 'py_modules', 'packages', 'skbuild' ]) def test_distribution_is_pure(distribution_type, tmpdir): skbuild_setup_kwargs = {} if distribution_type == 'unknown': is_pure = False elif distribution_type == 'py_modules': is_pure = True hello_py = tmpdir.join("hello.py") hello_py.write("") skbuild_setup_kwargs["py_modules"] = ["hello"] elif distribution_type == 'packages': is_pure = True init_py = tmpdir.mkdir("hello").join("__init__.py") init_py.write("") skbuild_setup_kwargs["packages"] = ["hello"] elif distribution_type == 'skbuild': is_pure = False cmakelists_txt = tmpdir.join("CMakeLists.txt") cmakelists_txt.write( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ ) else: raise Exception( "Unknown distribution_type: {}".format(distribution_type)) platform = get_platform() original_write_test_cmakelist = platform.write_test_cmakelist def write_test_cmakelist_no_languages(_self, _languages): original_write_test_cmakelist([]) with patch.object(type(platform), 'write_test_cmakelist', new=write_test_cmakelist_no_languages): with push_dir(str(tmpdir)), push_argv(["setup.py", "build"]): distribution = skbuild_setup( name="test", version="0.0.1", description="test object returned by setup function", author="The scikit-build team", license="MIT", **skbuild_setup_kwargs ) assert issubclass(distribution.__class__, (distutils_Distribution, setuptool_Distribution)) assert is_pure == distribution.is_pure() @pytest.mark.parametrize("cmake_args", [ [], ['--', '-DVAR:STRING=43', '-DVAR_WITH_SPACE:STRING=Ciao Mondo'] ]) def test_cmake_args_keyword(cmake_args, capfd): tmp_dir = _tmpdir('cmake_args_keyword') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_args_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_args=[ "-DVAR:STRING=42", "-DVAR_WITH_SPACE:STRING=Hello World" ] ) """ )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) message(STATUS "VAR[${VAR}]") message(STATUS "VAR_WITH_SPACE[${VAR_WITH_SPACE}]") install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ )) with execute_setup_py(tmp_dir, ['build'] + cmake_args, disable_languages_test=True): pass out, _ = capfd.readouterr() if not cmake_args: assert "VAR[42]" in out assert "VAR_WITH_SPACE[Hello World]" in out else: assert "VAR[43]" in out assert "VAR_WITH_SPACE[Ciao Mondo]" in out @pytest.mark.parametrize( "cmake_install_dir, expected_failed, error_code_type", ( (None, True, str), ('', True, str), (str(py.path.local.get_temproot().join('scikit-build')), True, SKBuildError), ('banana', False, str) ) ) def test_cmake_install_dir_keyword( cmake_install_dir, expected_failed, error_code_type, capsys): # ------------------------------------------------------------------------- # "SOURCE" tree layout: # # ROOT/ # # CMakeLists.txt # setup.py # # apple/ # __init__.py # # ------------------------------------------------------------------------- # "BINARY" distribution layout # # ROOT/ # # apple/ # __init__.py # tmp_dir = _tmpdir('cmake_install_dir_keyword') setup_kwarg = '' if cmake_install_dir is not None: setup_kwarg = 'cmake_install_dir=r\'{}\''.format(cmake_install_dir) tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_cmake_install_dir", version="1.2.3", description="a package testing use of cmake_install_dir", author='The scikit-build team', license="MIT", packages=['apple', 'banana'], {setup_kwarg} ) """.format(setup_kwarg=setup_kwarg) )) # Install location purposely set to "." so that we can test # usage of "cmake_install_dir" skbuild.setup keyword. tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(banana NONE) file(WRITE "${CMAKE_BINARY_DIR}/__init__.py" "") install(FILES "${CMAKE_BINARY_DIR}/__init__.py" DESTINATION ".") """ )) tmp_dir.ensure('apple', '__init__.py') failed = False message = "" try: with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass except SystemExit as e: # Error is not of type SKBuildError, it is expected to be # raised by distutils.core.setup failed = isinstance(e.code, error_code_type) message = str(e) out, _ = capsys.readouterr() assert failed == expected_failed if failed: if error_code_type == str: assert message == "error: package directory " \ "'{}' does not exist".format( os.path.join(CMAKE_INSTALL_DIR(), 'banana')) else: assert message.strip().startswith( "setup parameter 'cmake_install_dir' " "is set to an absolute path.") else: init_py = to_platform_path("{}/banana/__init__.py".format(CMAKE_INSTALL_DIR())) assert "copying {}".format(init_py) in out @pytest.mark.parametrize("cmake_with_sdist", [True, False]) def test_cmake_with_sdist_keyword(cmake_with_sdist, capfd): tmp_dir = _tmpdir('cmake_with_sdist') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_with_sdist={cmake_with_sdist} ) """.format(cmake_with_sdist=cmake_with_sdist) )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ )) initialize_git_repo_and_commit(tmp_dir) with execute_setup_py(tmp_dir, ['sdist'], disable_languages_test=True): pass out, _ = capfd.readouterr() if cmake_with_sdist: assert "Generating done" in out else: assert "Generating done" not in out def test_cmake_minimum_required_version_keyword(): tmp_dir = _tmpdir('cmake_minimum_required_version') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", cmake_minimum_required_version='99.98.97' ) """ )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ )) try: with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass except SystemExit as e: # Error is not of type SKBuildError, it is expected to be # raised by distutils.core.setup failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "CMake version 99.98.97 or higher is required." in message @pytest.mark.skipif(os.environ.get("CONDA_BUILD", "0") == "1", reason="running tests expecting network connection in Conda is not possible. " "See https://github.com/conda/conda/issues/508") @pytest.mark.skipif(not is_site_reachable("https://pypi.org/simple/cmake/"), reason="pypi.org website not reachable") def test_setup_requires_keyword_include_cmake(mocker, capsys): mock_setup = mocker.patch('skbuild.setuptools_wrap.upstream_setup') tmp_dir = _tmpdir('setup_requires_keyword_include_cmake') setup_requires = ['cmake>=3.10'] tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="cmake_with_sdist_keyword", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", setup_requires=[{setup_requires}] ) """.format(setup_requires=",".join(["'%s'" % package for package in setup_requires])) )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ )) with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] assert setup_kw['setup_requires'] == setup_requires import cmake out, _ = capsys.readouterr() if "Searching for cmake>=3.10" in out: assert cmake.__file__.lower().startswith(str(tmp_dir).lower()) @pytest.mark.parametrize("distribution_type", ('pure', 'skbuild')) def test_script_keyword(distribution_type, capsys): # ------------------------------------------------------------------------- # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # foo.py # bar.py # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout is identical for both # # ROOT/ # foo.py # bar.py # tmp_dir = _tmpdir('script_keyword') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_script_keyword", version="1.2.3", description="a package testing use of script keyword", author='The scikit-build team', license="MIT", scripts=['foo.py', 'bar.py'] ) """ )) if distribution_type == 'skbuild': tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foo NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.py" "# foo.py") file(WRITE "${CMAKE_BINARY_DIR}/bar.py" "# bar.py") install( FILES "${CMAKE_BINARY_DIR}/foo.py" "${CMAKE_BINARY_DIR}/bar.py" DESTINATION "." ) """ )) messages = [ "copying {}/{}.py -> " "{}/setuptools/scripts-".format(CMAKE_INSTALL_DIR(), module, SKBUILD_DIR()) for module in ['foo', 'bar']] elif distribution_type == 'pure': tmp_dir.join('foo.py').write("# foo.py") tmp_dir.join('bar.py').write("# bar.py") messages = [ "copying {}.py -> " "{}/setuptools/scripts-".format(module, SKBUILD_DIR()) for module in ['foo', 'bar']] with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass out, _ = capsys.readouterr() for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize("distribution_type", ('pure', 'skbuild')) def test_py_modules_keyword(distribution_type, capsys): # ------------------------------------------------------------------------- # # "SOURCE" tree layout for "pure" distribution: # # ROOT/ # setup.py # foo.py # bar.py # # "SOURCE" tree layout for "skbuild" distribution: # # ROOT/ # setup.py # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout is identical for both # # ROOT/ # foo.py # bar.py # tmp_dir = _tmpdir('py_modules_keyword') tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_py_modules_keyword", version="1.2.3", description="a package testing use of py_modules keyword", author='The scikit-build team', license="MIT", py_modules=['foo', 'bar'] ) """ )) if distribution_type == 'skbuild': tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(foobar NONE) file(WRITE "${CMAKE_BINARY_DIR}/foo.py" "# foo.py") file(WRITE "${CMAKE_BINARY_DIR}/bar.py" "# bar.py") install( FILES "${CMAKE_BINARY_DIR}/foo.py" "${CMAKE_BINARY_DIR}/bar.py" DESTINATION "." ) """ )) messages = [ "copying {}/{}.py -> " "{}/setuptools/lib".format(CMAKE_INSTALL_DIR(), module, SKBUILD_DIR()) for module in ['foo', 'bar']] elif distribution_type == 'pure': tmp_dir.join('foo.py').write("# foo.py") tmp_dir.join('bar.py').write("# bar.py") messages = [ "copying {}.py -> " "{}/setuptools/lib".format(module, SKBUILD_DIR()) for module in ['foo', 'bar']] with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass out, _ = capsys.readouterr() for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize("package_parts, module_file, expected", [ ([], "", ""), ([""], "file.py", "file.py"), ([], "foo/file.py", "foo/file.py"), (["foo"], "", ""), (["foo"], "", ""), (["foo"], "foo/file.py", "file.py"), (["foo"], "foo\\file.py", "file.py"), (["foo", "bar"], "foo/file.py", "foo/file.py"), (["foo", "bar"], "foo/bar/file.py", "file.py"), (["foo", "bar"], "foo/bar/baz/file.py", "baz/file.py"), (["foo"], "/foo/file.py", "/foo/file.py"), ]) def test_strip_package(package_parts, module_file, expected): assert strip_package(package_parts, module_file) == expected @pytest.mark.parametrize("has_cmake_package", [0, 1]) # noqa: C901 @pytest.mark.parametrize("has_cmake_module", [0, 1]) @pytest.mark.parametrize("has_hybrid_package", [0, 1]) @pytest.mark.parametrize("has_pure_package", [0, 1]) @pytest.mark.parametrize("has_pure_module", [0, 1]) @pytest.mark.parametrize("with_package_base", [0, 1]) def test_setup_inputs( has_cmake_package, has_cmake_module, has_hybrid_package, has_pure_package, has_pure_module, with_package_base, mocker): """This test that a project can have a package with some modules installed using setup.py and some other modules installed using CMake. """ tmp_dir = _tmpdir('test_setup_inputs') package_base = 'to/the/base' if with_package_base else '' package_base_dir = package_base + '/' if package_base else '' cmake_source_dir = package_base if cmake_source_dir and (has_cmake_package or has_cmake_module): pytest.skip("unsupported configuration: " "python package fully generated by CMake does *NOT* work. " "At least __init__.py should be in the project source tree") # ------------------------------------------------------------------------- # Here is the "SOURCE" tree layout: # # ROOT/ # # setup.py # # [/] # # pureModule.py # # pure/ # __init__.py # pure.py # # data/ # pure.dat # # [/] # # hybrid/ # CMakeLists.txt # __init__.py # hybrid_pure.dat # hybrid_pure.py # # data/ # hybrid_data_pure.dat # # hybrid_2/ # __init__.py # hybrid_2_pure.py # # hybrid_2_pure/ # __init__.py # hybrid_2_pure_1.py # hybrid_2_pure_2.py # # # ------------------------------------------------------------------------- # and here is the "BINARY" distribution layout: # # The comment "CMake" or "Setuptools" indicates which tool is responsible # for placing the file in the tree used to create the binary distribution. # # ROOT/ # # cmakeModule.py # CMake # # cmake/ # __init__.py # CMake # cmake.py # CMake # # hybrid/ # hybrid_cmake.dat # CMake # hybrid_cmake.py # CMake # hybrid_pure.dat # Setuptools # hybrid_pure.py # Setuptools # # data/ # hybrid_data_pure.dat # CMake or Setuptools # hybrid_data_cmake.dat # CMake *NO TEST* # # hybrid_2/ # __init__.py # CMake or Setuptools # hybrid_2_pure.py # CMake or Setuptools # hybrid_2_cmake.py # CMake # # hybrid_2_pure/ # __init__.py # CMake or Setuptools # hybrid_2_pure_1.py # CMake or Setuptools # hybrid_2_pure_2.py # CMake or Setuptools # # pureModule.py # Setuptools # # pure/ # __init__.py # Setuptools # pure.py # Setuptools # # data/ # pure.dat # Setuptools tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup #from setuptools import setup setup( name="test_hybrid_project", version="1.2.3", description=("an hybrid package mixing files installed by both " "CMake and setuptools"), author='The scikit-build team', license="MIT", cmake_source_dir='{cmake_source_dir}', cmake_install_dir='{cmake_install_dir}', # Arbitrary order of packages packages=[ {p_off} 'pure', {h_off} 'hybrid.hybrid_2', {h_off} 'hybrid', {c_off} 'cmake', {p_off} 'hybrid.hybrid_2_pure', ], py_modules=[ {pm_off} '{package_base}pureModule', {cm_off} '{package_base}cmakeModule', ], package_data={{ {p_off} 'pure': ['data/pure.dat'], {h_off} 'hybrid': ['hybrid_pure.dat', 'data/hybrid_data_pure.dat'], }}, # Arbitrary order of package_dir package_dir = {{ {p_off} 'hybrid.hybrid_2_pure': '{package_base}hybrid/hybrid_2_pure', {p_off} 'pure': '{package_base}pure', {h_off} 'hybrid': '{package_base}hybrid', {h_off} 'hybrid.hybrid_2': '{package_base}hybrid/hybrid_2', {c_off} 'cmake': '{package_base}cmake', }} ) """.format( cmake_source_dir=cmake_source_dir, cmake_install_dir=package_base, package_base=package_base_dir, c_off='' if has_cmake_package else '#', cm_off='' if has_cmake_module else '#', h_off='' if has_hybrid_package else '#', p_off='' if has_pure_package else '#', pm_off='' if has_pure_module else '#' ) )) src_dir = tmp_dir.ensure(package_base, dir=1) src_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(hybrid NONE) set(build_dir ${{CMAKE_BINARY_DIR}}) {c_off} file(WRITE ${{build_dir}}/__init__.py "") {c_off} file(WRITE ${{build_dir}}/cmake.py "") {c_off} install( {c_off} FILES {c_off} ${{build_dir}}/__init__.py {c_off} ${{build_dir}}/cmake.py {c_off} DESTINATION cmake {c_off} ) {cm_off} file(WRITE ${{build_dir}}/cmakeModule.py "") {cm_off} install( {cm_off} FILES ${{build_dir}}/cmakeModule.py {cm_off} DESTINATION .) {h_off} file(WRITE ${{build_dir}}/hybrid_cmake.dat "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_cmake.dat {h_off} DESTINATION hybrid) {h_off} file(WRITE ${{build_dir}}/hybrid_cmake.py "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_cmake.py {h_off} DESTINATION hybrid) {h_off} file(WRITE ${{build_dir}}/hybrid_data_cmake.dat "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_data_cmake.dat {h_off} DESTINATION hybrid/data) {h_off} file(WRITE ${{build_dir}}/hybrid_2_cmake.py "") {h_off} install( {h_off} FILES ${{build_dir}}/hybrid_2_cmake.py {h_off} DESTINATION hybrid/hybrid_2) install(CODE "message(STATUS \\\"Installation complete\\\")") """.format( c_off='' if has_cmake_package else '#', cm_off='' if has_cmake_module else '#', h_off='' if has_hybrid_package else '#', p_off='' if has_pure_package else '#' ) )) # List path types: 'c', 'cm', 'h', 'p' or 'pm' try: path_types = list(zip(*filter( lambda i: i[1], [('c', has_cmake_package), ('cm', has_cmake_module), ('h', has_hybrid_package), ('p', has_pure_package), ('pm', has_pure_module)])))[0] except IndexError: path_types = [] def select_paths(annotated_paths): """Return a filtered list paths considering ``path_types``. `annotated_paths`` is list of tuple ``(type, path)`` where type is either `c`, 'cm', `h`, `p` or 'pm'. """ return filter(lambda i: i[0] in path_types, annotated_paths) # Commented paths are the one expected to be installed by CMake. For # this reason, corresponding files should NOT be created in the source # tree. for (_type, path) in select_paths([ # ('c', 'cmake/__init__.py'), # ('c', 'cmake/cmake.py'), # ('cm', 'cmakeModule.py'), ('h', 'hybrid/__init__.py'), # ('h', 'hybrid/hybrid_cmake.dat'), # ('h', 'hybrid/hybrid_cmake.py'), ('h', 'hybrid/hybrid_pure.dat'), ('h', 'hybrid/hybrid_pure.py'), # ('h', 'hybrid/data/hybrid_data_cmake.dat'), ('h', 'hybrid/data/hybrid_data_pure.dat'), ('h', 'hybrid/hybrid_2/__init__.py'), # ('h', 'hybrid/hybrid_2/hybrid_2_cmake.py'), ('h', 'hybrid/hybrid_2/hybrid_2_pure.py'), ('p', 'hybrid/hybrid_2_pure/__init__.py'), ('p', 'hybrid/hybrid_2_pure/hybrid_2_pure_1.py'), ('p', 'hybrid/hybrid_2_pure/hybrid_2_pure_2.py'), ('pm', 'pureModule.py'), ('p', 'pure/__init__.py'), ('p', 'pure/pure.py'), ('p', 'pure/data/pure.dat'), ]): assert _type in ['p', 'pm', 'h'] root = (package_base if (_type == 'p' or _type == 'pm') else cmake_source_dir) tmp_dir.ensure(os.path.join(root, path)) # Do not call the real setup function. Instead, replace it with # a MagicMock allowing to check with which arguments it was invoked. mock_setup = mocker.patch('skbuild.setuptools_wrap.upstream_setup') # Convenience print function def _pprint(desc, value=None): print( "-----------------\n" "{}:\n" "\n" "{}\n".format(desc, pprint.pformat( setup_kw.get(desc, {}) if value is None else value, indent=2))) with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] # packages expected_packages = [] if has_cmake_package: expected_packages += ['cmake'] if has_hybrid_package: expected_packages += ['hybrid', 'hybrid.hybrid_2'] if has_pure_package: expected_packages += ['hybrid.hybrid_2_pure', 'pure'] _pprint('expected_packages', expected_packages) _pprint('packages') # package dir expected_package_dir = { package: (os.path.join(CMAKE_INSTALL_DIR(), package_base, package.replace('.', '/'))) for package in expected_packages } _pprint('expected_package_dir', expected_package_dir) _pprint('package_dir') # package data expected_package_data = {} if has_cmake_package: expected_package_data['cmake'] = [ '__init__.py', 'cmake.py' ] if has_hybrid_package: expected_package_data['hybrid'] = [ '__init__.py', 'hybrid_cmake.dat', 'hybrid_cmake.py', 'hybrid_pure.dat', 'hybrid_pure.py', 'data/hybrid_data_cmake.dat', 'data/hybrid_data_pure.dat', ] expected_package_data['hybrid.hybrid_2'] = [ '__init__.py', 'hybrid_2_cmake.py', 'hybrid_2_pure.py' ] if has_pure_package: expected_package_data['hybrid.hybrid_2_pure'] = \ [ '__init__.py', 'hybrid_2_pure_1.py', 'hybrid_2_pure_2.py' ] expected_package_data['pure'] = [ '__init__.py', 'pure.py', 'data/pure.dat', ] if has_cmake_module or has_pure_module: expected_modules = [] if has_cmake_module: expected_modules.append(package_base_dir + 'cmakeModule.py') if has_pure_module: expected_modules.append(package_base_dir + 'pureModule.py') expected_package_data[''] = expected_modules _pprint('expected_package_data', expected_package_data) package_data = {p: sorted(files) for p, files in setup_kw['package_data'].items()} _pprint('package_data', package_data) # py_modules (corresponds to files associated with empty package) expected_py_modules = [] if '' in expected_package_data: expected_py_modules = [ os.path.splitext(module_file)[0] for module_file in expected_package_data['']] _pprint('expected_py_modules', expected_py_modules) _pprint('py_modules') # scripts expected_scripts = [] _pprint('expected_scripts', expected_scripts) _pprint('scripts') # data_files expected_data_files = [] _pprint('expected_data_files', expected_data_files) _pprint('data_files') assert sorted(setup_kw['packages']) == sorted(expected_packages) assert sorted(setup_kw['package_dir']) == sorted(expected_package_dir) assert package_data == {p: sorted(files) for p, files in expected_package_data.items()} assert sorted(setup_kw['py_modules']) == sorted(expected_py_modules) assert sorted(setup_kw['scripts']) == sorted([]) assert sorted(setup_kw['data_files']) == sorted([]) @pytest.mark.parametrize("with_cmake_source_dir", [0, 1]) def test_cmake_install_into_pure_package(with_cmake_source_dir, capsys): # ------------------------------------------------------------------------- # "SOURCE" tree layout: # # (1) with_cmake_source_dir == 0 # # ROOT/ # # CMakeLists.txt # setup.py # # fruits/ # __init__.py # # # (2) with_cmake_source_dir == 1 # # ROOT/ # # setup.py # # fruits/ # __init__.py # # src/ # # CMakeLists.txt # # ------------------------------------------------------------------------- # "BINARY" distribution layout: # # ROOT/ # # fruits/ # # __init__.py # apple.py # banana.py # # data/ # # apple.dat # banana.dat # tmp_dir = _tmpdir('cmake_install_into_pure_package') cmake_source_dir = 'src' if with_cmake_source_dir else '' tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="test_py_modules_keyword", version="1.2.3", description="a package testing use of py_modules keyword", author='The scikit-build team', license="MIT", packages=['fruits'], cmake_install_dir='fruits', cmake_source_dir='{cmake_source_dir}', ) """.format(cmake_source_dir=cmake_source_dir) )) cmake_src_dir = tmp_dir.ensure(cmake_source_dir, dir=1) cmake_src_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) file(WRITE "${CMAKE_BINARY_DIR}/apple.py" "# apple.py") file(WRITE "${CMAKE_BINARY_DIR}/banana.py" "# banana.py") install( FILES "${CMAKE_BINARY_DIR}/apple.py" "${CMAKE_BINARY_DIR}/banana.py" DESTINATION "." ) file(WRITE "${CMAKE_BINARY_DIR}/apple.dat" "# apple.dat") file(WRITE "${CMAKE_BINARY_DIR}/banana.dat" "# banana.dat") install( FILES "${CMAKE_BINARY_DIR}/apple.dat" "${CMAKE_BINARY_DIR}/banana.dat" DESTINATION "data" ) """ )) tmp_dir.ensure('fruits/__init__.py') with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass messages = [ "copying {}/{} -> " "{}/setuptools/lib".format(CMAKE_INSTALL_DIR(), module, SKBUILD_DIR()) for module in [ 'fruits/__init__.py', 'fruits/apple.py', 'fruits/banana.py', 'fruits/data/apple.dat', 'fruits/data/banana.dat', ]] out, _ = capsys.readouterr() for message in messages: assert to_platform_path(message) in out @pytest.mark.parametrize("zip_safe", [None, False, True]) def test_zip_safe_default(zip_safe, mocker): mock_setup = mocker.patch('skbuild.setuptools_wrap.upstream_setup') tmp_dir = _tmpdir('zip_safe_default') setup_kwarg = '' if zip_safe is not None: setup_kwarg = 'zip_safe={}'.format(zip_safe) tmp_dir.join('setup.py').write(textwrap.dedent( """ from skbuild import setup setup( name="zip_safe_default", version="1.2.3", description="a minimal example package", author='The scikit-build team', license="MIT", {setup_kwarg} ) """.format(setup_kwarg=setup_kwarg) )) tmp_dir.join('CMakeLists.txt').write(textwrap.dedent( """ cmake_minimum_required(VERSION 3.5.0) project(test NONE) install(CODE "execute_process( COMMAND \\${CMAKE_COMMAND} -E sleep 0)") """ )) with execute_setup_py(tmp_dir, ['build'], disable_languages_test=True): pass assert mock_setup.call_count == 1 setup_kw = mock_setup.call_args[1] assert "zip_safe" in setup_kw if zip_safe is None: assert not setup_kw["zip_safe"] elif zip_safe: assert setup_kw["zip_safe"] else: # zip_safe is False assert not setup_kw["zip_safe"] scikit-build-0.11.1/tests/test_skbuild.py000066400000000000000000000144311365474353700204110ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_skbuild ---------------------------------- Tests for `skbuild` module. """ import os import platform import pytest import sys from skbuild.constants import CMAKE_BUILD_DIR from skbuild.exceptions import SKBuildError from skbuild.platform_specifics import get_platform from skbuild.platform_specifics.windows import find_visual_studio, VS_YEAR_TO_VERSION from . import (get_cmakecache_variables, project_setup_py_test, push_dir, push_env, which) def test_generator_selection(): version = sys.version_info env_generator = os.environ.get("CMAKE_GENERATOR") this_platform = platform.system().lower() get_best_generator = get_platform().get_best_generator arch = platform.architecture()[0] if env_generator: assert(get_best_generator(env_generator).name == env_generator) if this_platform == "windows": # assert that we are running a supported version of python py_27_32 = ( (version.major == 2 and version.minor >= 7) or (version.major == 3 and version.minor <= 2) ) py_33_34 = ( version.major == 3 and ( 3 <= version.minor <= 4 ) ) py_35 = ( version.major == 3 and version.minor >= 5 ) assert(len(tuple(filter(bool, (py_27_32, py_33_34, py_35)))) == 1) # Expected Visual Studio version if py_27_32: vs_generator = "Visual Studio 9 2008" vs_version = 9 elif py_33_34: vs_generator = "Visual Studio 10 2010" vs_version = 10 else: vs_generator = "Visual Studio 14 2015" vs_version = 14 vs_generator += (" Win64" if arch == "64bit" else "") has_vs_2017 = find_visual_studio(vs_version=VS_YEAR_TO_VERSION["2017"]) # Apply to VS <= 14 (2015) has_vs_ide_vcvars = any([ os.path.exists(path_pattern % vs_version) for path_pattern in [ "C:/Program Files (x86)/Microsoft Visual Studio %.1f/VC/vcvarsall.bat" ] ]) # As of Dec 2016, this is available only for VS 9.0 has_vs_for_python_vcvars = any([ os.path.exists(os.path.expanduser(path_pattern % vs_version)) for path_pattern in [ "~/AppData/Local/Programs/Common/Microsoft/Visual C++ for Python/%.1f/vcvarsall.bat", "C:/Program Files (x86)/Common Files/Microsoft/Visual C++ for Python/%.1f/vcvarsall.bat" ] ]) generator = None # If environment exists, update the expected generator if ( has_vs_for_python_vcvars or has_vs_ide_vcvars ) and which("ninja.exe"): generator = "Ninja" elif has_vs_2017: # ninja is provided by the CMake extension bundled with Visual Studio 2017 # C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/Common7/IDE/CommonExtensions/Microsoft/CMake/Ninja/ninja.exe # noqa: E501 generator = "Ninja" elif has_vs_ide_vcvars or has_vs_2017: generator = vs_generator elif has_vs_for_python_vcvars: generator = "NMake Makefiles" assert (get_best_generator().name == generator) elif this_platform in ["darwin", "linux"]: generator = "Unix Makefiles" if which("ninja"): generator = "Ninja" assert get_best_generator().name == generator @pytest.mark.parametrize("generator, expected_make_program", [ ("NMake Makefiles", "nmake"), ("Unix Makefiles", "make") ]) def test_generator(generator, expected_make_program): generator_platform = { "NMake Makefiles": ["windows"], "Unix Makefiles": ["darwin", "linux"] } assert generator in generator_platform this_platform = platform.system().lower() if this_platform not in generator_platform[generator]: pytest.skip("%s generator is available only on %s" % ( generator, this_platform.title())) @project_setup_py_test("hello-cpp", ["build"]) def run_build(): pass with push_env(CMAKE_GENERATOR=generator): tmp_dir = run_build()[0] cmakecache = tmp_dir.join(CMAKE_BUILD_DIR()).join("CMakeCache.txt") assert cmakecache.exists() variables = get_cmakecache_variables(str(cmakecache)) make_program = (variables["CMAKE_MAKE_PROGRAM"][1] if "CMAKE_MAKE_PROGRAM" in variables else "") assert make_program.endswith(expected_make_program) or \ make_program.endswith("%s.exe" % expected_make_program) @pytest.mark.parametrize( "generator_args", [ ["-G", "invalid"], ["--", "-G", "invalid"], ] ) def test_invalid_generator(generator_args): with push_dir(): build_args = ["build"] build_args.extend(generator_args) @project_setup_py_test("hello-no-language", build_args, disable_languages_test=True) def run(): pass failed = False message = "" try: run() except SystemExit as e: failed = isinstance(e.code, SKBuildError) message = str(e) assert failed assert "scikit-build could not get a working generator " \ "for your system. Aborting build." in message @pytest.mark.parametrize( "vs_year", ["2008", "2010", "2012", "2013", "2015", "2017"] ) def test_platform_windows_find_visual_studio(vs_year): """If the environment variable ``SKBUILD_TEST_FIND_VS_INSTALLATION_EXPECTED`` is set, this test asserts the value returned by :func:`skbuild.platforms.windows.find_visual_studio()`. It skips the test otherwise. Setting the environment variable to 1 means that the corresponding Visual Studio version is expected to be installed. Setting it to 0, means otherwise. """ env_var = 'SKBUILD_TEST_FIND_VS%s_INSTALLATION_EXPECTED' % vs_year if env_var not in os.environ: pytest.skip("env. variable %s is not set" % env_var) valid_path_expected = bool(int(os.environ[env_var])) if valid_path_expected: assert os.path.exists(find_visual_studio(VS_YEAR_TO_VERSION[vs_year])) else: assert find_visual_studio(VS_YEAR_TO_VERSION[vs_year]) == "" scikit-build-0.11.1/tests/test_skbuild_variable.py000066400000000000000000000015001365474353700222470ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_skbuild_variable ------------------------ Tries to build the `fail-unless-skbuild-set` sample project. The CMake variable "SKBUILD" must be set in order for the build to succeed. """ from . import project_setup_py_test @project_setup_py_test("fail-unless-skbuild-set", ["build"], disable_languages_test=True) def test_skbuild_variable_builds(): pass # @project_setup_py_test("fail-unless-skbuild-set", ["test"], disable_languages_test=True) # def test_skbuild_variable_works(): # pass @project_setup_py_test("fail-unless-skbuild-set", ["sdist"], disable_languages_test=True) def test_skbuild_variable_sdist(): pass @project_setup_py_test("fail-unless-skbuild-set", ["bdist_wheel"], disable_languages_test=True) def test_skbuild_variable_wheel(): pass scikit-build-0.11.1/tests/test_utils.py000066400000000000000000000147361365474353700201240ustar00rootroot00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """test_utils ------------------------ Tests for utils functions. """ import os import pytest from skbuild.utils import (ContextDecorator, mkdir_p, PythonModuleFinder, push_dir, to_platform_path, to_unix_path) from . import (list_ancestors, push_env, SAMPLES_DIR) saved_cwd = os.getcwd() def setup_module(): """ setup any state specific to the execution of the given module.""" os.chdir(os.path.dirname(__file__)) def teardown_module(): """ teardown any state that was previously setup with a setup_module method. """ os.chdir(saved_cwd) def test_context_decorator(): with ContextDecorator(foo=42) as context: assert hasattr(context, 'foo') assert context.foo == 42 def test_push_dir(tmpdir): old_cwd = os.getcwd() try: level1 = tmpdir.mkdir("level1") level2 = level1.mkdir("level2") os.chdir(str(level2)) assert os.path.split(os.getcwd())[-1] == 'level2' # No directory with push_dir(): os.chdir(os.path.join(os.getcwd(), '..')) assert os.path.split(os.getcwd())[-1] == 'level1' assert os.path.split(os.getcwd())[-1] == 'level2' # With existing directory with push_dir(directory=os.path.join(os.getcwd(), '..')): assert os.path.split(os.getcwd())[-1] == 'level1' assert os.path.split(os.getcwd())[-1] == 'level2' foo_directory = os.path.join(str(tmpdir), 'foo') # With non existing directory failed = False try: with push_dir(directory=foo_directory): pass except OSError: failed = True assert failed assert not os.path.isdir(foo_directory) # With make_directory option with push_dir(directory=foo_directory, make_directory=True): assert os.getcwd() == foo_directory assert os.path.split(os.getcwd())[-1] == 'level2' assert os.path.isdir(foo_directory) finally: os.chdir(old_cwd) def test_push_dir_decorator(tmpdir): old_cwd = os.getcwd() try: level1 = tmpdir.mkdir("level1") level2 = level1.mkdir("level2") os.chdir(str(level2)) assert os.path.split(os.getcwd())[-1] == 'level2' # No directory @push_dir() def test_default(): os.chdir(os.path.join(os.getcwd(), '..')) assert os.path.split(os.getcwd())[-1] == 'level1' test_default() assert os.path.split(os.getcwd())[-1] == 'level2' # With existing directory @push_dir(directory=os.path.join(os.getcwd(), '..')) def test(): assert os.path.split(os.getcwd())[-1] == 'level1' test() assert os.path.split(os.getcwd())[-1] == 'level2' foo_directory = os.path.join(str(tmpdir), 'foo') # With non existing directory failed = False try: @push_dir(directory=foo_directory) def test(): pass test() except OSError: failed = True assert failed assert not os.path.isdir(foo_directory) # With make_directory option @push_dir(directory=foo_directory, make_directory=True) def test(): assert os.getcwd() == foo_directory test() assert os.path.split(os.getcwd())[-1] == 'level2' assert os.path.isdir(foo_directory) finally: os.chdir(old_cwd) def test_mkdir_p(tmpdir): tmp_dir = str(tmpdir) assert os.path.isdir(tmp_dir) foo_bar_dir = os.path.join(tmp_dir, 'foo', 'bar') mkdir_p(foo_bar_dir) assert os.path.isdir(foo_bar_dir) # Make sure calling function twice does not raise an exception mkdir_p(foo_bar_dir) assert os.path.isdir(foo_bar_dir) def test_push_env(): assert 'SKBUILD_NEW_VAR' not in os.environ os.environ['SKBUILD_ANOTHER_VAR'] = 'abcd' assert 'SKBUILD_ANOTHER_VAR' in os.environ saved_env = dict(os.environ) # Setting and un-setting variables can be done simultaneously with push_env(SKBUILD_NEW_VAR='1234', SKBUILD_ANOTHER_VAR=None): assert 'SKBUILD_NEW_VAR' in os.environ assert 'SKBUILD_ANOTHER_VAR' not in os.environ assert os.getenv('SKBUILD_NEW_VAR') == '1234' assert 'SKBUILD_NEW_VAR' not in os.environ assert 'SKBUILD_ANOTHER_VAR' in os.environ assert saved_env == os.environ # Trying to unset an unknown variable should be a no-op with push_env(SKBUILD_NOT_SET=None): assert saved_env == os.environ assert saved_env == os.environ # Calling without argument should be a no-op with push_env(): assert saved_env == os.environ assert saved_env == os.environ def test_python_module_finder(): modules = PythonModuleFinder(['bonjour', 'hello'], {}, []).find_all_modules( os.path.join(SAMPLES_DIR, 'hello-cpp') ) assert sorted(modules) == sorted([ ('bonjour', '__init__', to_platform_path('bonjour/__init__.py')), ('hello', '__init__', to_platform_path('hello/__init__.py')), ('hello', '__main__', to_platform_path('hello/__main__.py'))]) @pytest.mark.parametrize( "input_path, expected_path", ( (None, None), ('', ''), ('/bar/foo/baz', '{s}bar{s}foo{s}baz'.format(s=os.sep)), ('C:\\bar\\foo\\baz', 'C:{s}bar{s}foo{s}baz'.format(s=os.sep)), ('C:\\bar/foo\\baz/', 'C:{s}bar{s}foo{s}baz{s}'.format(s=os.sep)), ) ) def test_to_platform_path(input_path, expected_path): assert to_platform_path(input_path) == expected_path @pytest.mark.parametrize( "input_path, expected_path", ( (None, None), ('', ''), ('/bar/foo/baz', '/bar/foo/baz'), ('C:\\bar\\foo\\baz', 'C:/bar/foo/baz'), ('C:\\bar/foo\\baz/', 'C:/bar/foo/baz/'), ) ) def test_to_unix_path(input_path, expected_path): assert to_unix_path(input_path) == expected_path @pytest.mark.parametrize( "input_path, expected_ancestors", ( ('', []), ('.', []), ('part1/part2/part3/part4', ['part1/part2/part3', 'part1/part2', 'part1']), ('part1\\part2\\part3\\part4', []), ('/part1/part2/part3/part4', ['/part1/part2/part3', '/part1/part2', '/part1', '/']), ('C:/part1/part2/part3/part4', ['C:/part1/part2/part3', 'C:/part1/part2', 'C:/part1', 'C:']), ) ) def test_list_ancestors(input_path, expected_ancestors): assert list_ancestors(input_path) == expected_ancestors scikit-build-0.11.1/tox.ini000066400000000000000000000002741365474353700155140ustar00rootroot00000000000000[tox] envlist = py27, py34, py35, py36, py37 [testenv] setenv = PYTHONPATH = {toxinidir}:{toxinidir}/skbuild commands = python setup.py test deps = -r{toxinidir}/requirements.txt scikit-build-0.11.1/versioneer.py000066400000000000000000002060031365474353700167320ustar00rootroot00000000000000 # Version: 0.18 """The Versioneer - like a rocketeer, but for versions. The Versioneer ============== * like a rocketeer, but for versions! * https://github.com/warner/python-versioneer * Brian Warner * License: Public Domain * Compatible With: python2.6, 2.7, 3.2, 3.3, 3.4, 3.5, 3.6, and pypy * [![Latest Version] (https://pypip.in/version/versioneer/badge.svg?style=flat) ](https://pypi.python.org/pypi/versioneer/) * [![Build Status] (https://travis-ci.org/warner/python-versioneer.png?branch=master) ](https://travis-ci.org/warner/python-versioneer) This is a tool for managing a recorded version number in distutils-based python projects. The goal is to remove the tedious and error-prone "update the embedded version string" step from your release process. Making a new release should be as easy as recording a new tag in your version-control system, and maybe making new tarballs. ## Quick Install * `pip install versioneer` to somewhere to your $PATH * add a `[versioneer]` section to your setup.cfg (see below) * run `versioneer install` in your source tree, commit the results ## Version Identifiers Source trees come from a variety of places: * a version-control system checkout (mostly used by developers) * a nightly tarball, produced by build automation * a snapshot tarball, produced by a web-based VCS browser, like github's "tarball from tag" feature * a release tarball, produced by "setup.py sdist", distributed through PyPI Within each source tree, the version identifier (either a string or a number, this tool is format-agnostic) can come from a variety of places: * ask the VCS tool itself, e.g. "git describe" (for checkouts), which knows about recent "tags" and an absolute revision-id * the name of the directory into which the tarball was unpacked * an expanded VCS keyword ($Id$, etc) * a `_version.py` created by some earlier build step For released software, the version identifier is closely related to a VCS tag. Some projects use tag names that include more than just the version string (e.g. "myproject-1.2" instead of just "1.2"), in which case the tool needs to strip the tag prefix to extract the version identifier. For unreleased software (between tags), the version identifier should provide enough information to help developers recreate the same tree, while also giving them an idea of roughly how old the tree is (after version 1.2, before version 1.3). Many VCS systems can report a description that captures this, for example `git describe --tags --dirty --always` reports things like "0.7-1-g574ab98-dirty" to indicate that the checkout is one revision past the 0.7 tag, has a unique revision id of "574ab98", and is "dirty" (it has uncommitted changes. The version identifier is used for multiple purposes: * to allow the module to self-identify its version: `myproject.__version__` * to choose a name and prefix for a 'setup.py sdist' tarball ## Theory of Operation Versioneer works by adding a special `_version.py` file into your source tree, where your `__init__.py` can import it. This `_version.py` knows how to dynamically ask the VCS tool for version information at import time. `_version.py` also contains `$Revision$` markers, and the installation process marks `_version.py` to have this marker rewritten with a tag name during the `git archive` command. As a result, generated tarballs will contain enough information to get the proper version. To allow `setup.py` to compute a version too, a `versioneer.py` is added to the top level of your source tree, next to `setup.py` and the `setup.cfg` that configures it. This overrides several distutils/setuptools commands to compute the version when invoked, and changes `setup.py build` and `setup.py sdist` to replace `_version.py` with a small static file that contains just the generated version data. ## Installation See [INSTALL.md](./INSTALL.md) for detailed installation instructions. ## Version-String Flavors Code which uses Versioneer can learn about its version string at runtime by importing `_version` from your main `__init__.py` file and running the `get_versions()` function. From the "outside" (e.g. in `setup.py`), you can import the top-level `versioneer.py` and run `get_versions()`. Both functions return a dictionary with different flavors of version information: * `['version']`: A condensed version string, rendered using the selected style. This is the most commonly used value for the project's version string. The default "pep440" style yields strings like `0.11`, `0.11+2.g1076c97`, or `0.11+2.g1076c97.dirty`. See the "Styles" section below for alternative styles. * `['full-revisionid']`: detailed revision identifier. For Git, this is the full SHA1 commit id, e.g. "1076c978a8d3cfc70f408fe5974aa6c092c949ac". * `['date']`: Date and time of the latest `HEAD` commit. For Git, it is the commit date in ISO 8601 format. This will be None if the date is not available. * `['dirty']`: a boolean, True if the tree has uncommitted changes. Note that this is only accurate if run in a VCS checkout, otherwise it is likely to be False or None * `['error']`: if the version string could not be computed, this will be set to a string describing the problem, otherwise it will be None. It may be useful to throw an exception in setup.py if this is set, to avoid e.g. creating tarballs with a version string of "unknown". Some variants are more useful than others. Including `full-revisionid` in a bug report should allow developers to reconstruct the exact code being tested (or indicate the presence of local changes that should be shared with the developers). `version` is suitable for display in an "about" box or a CLI `--version` output: it can be easily compared against release notes and lists of bugs fixed in various releases. The installer adds the following text to your `__init__.py` to place a basic version in `YOURPROJECT.__version__`: from ._version import get_versions __version__ = get_versions()['version'] del get_versions ## Styles The setup.cfg `style=` configuration controls how the VCS information is rendered into a version string. The default style, "pep440", produces a PEP440-compliant string, equal to the un-prefixed tag name for actual releases, and containing an additional "local version" section with more detail for in-between builds. For Git, this is TAG[+DISTANCE.gHEX[.dirty]] , using information from `git describe --tags --dirty --always`. For example "0.11+2.g1076c97.dirty" indicates that the tree is like the "1076c97" commit but has uncommitted changes (".dirty"), and that this commit is two revisions ("+2") beyond the "0.11" tag. For released software (exactly equal to a known tag), the identifier will only contain the stripped tag, e.g. "0.11". Other styles are available. See [details.md](details.md) in the Versioneer source tree for descriptions. ## Debugging Versioneer tries to avoid fatal errors: if something goes wrong, it will tend to return a version of "0+unknown". To investigate the problem, run `setup.py version`, which will run the version-lookup code in a verbose mode, and will display the full contents of `get_versions()` (including the `error` string, which may help identify what went wrong). ## Known Limitations Some situations are known to cause problems for Versioneer. This details the most significant ones. More can be found on Github [issues page](https://github.com/warner/python-versioneer/issues). ### Subprojects Versioneer has limited support for source trees in which `setup.py` is not in the root directory (e.g. `setup.py` and `.git/` are *not* siblings). The are two common reasons why `setup.py` might not be in the root: * Source trees which contain multiple subprojects, such as [Buildbot](https://github.com/buildbot/buildbot), which contains both "master" and "slave" subprojects, each with their own `setup.py`, `setup.cfg`, and `tox.ini`. Projects like these produce multiple PyPI distributions (and upload multiple independently-installable tarballs). * Source trees whose main purpose is to contain a C library, but which also provide bindings to Python (and perhaps other langauges) in subdirectories. Versioneer will look for `.git` in parent directories, and most operations should get the right version string. However `pip` and `setuptools` have bugs and implementation details which frequently cause `pip install .` from a subproject directory to fail to find a correct version string (so it usually defaults to `0+unknown`). `pip install --editable .` should work correctly. `setup.py install` might work too. Pip-8.1.1 is known to have this problem, but hopefully it will get fixed in some later version. [Bug #38](https://github.com/warner/python-versioneer/issues/38) is tracking this issue. The discussion in [PR #61](https://github.com/warner/python-versioneer/pull/61) describes the issue from the Versioneer side in more detail. [pip PR#3176](https://github.com/pypa/pip/pull/3176) and [pip PR#3615](https://github.com/pypa/pip/pull/3615) contain work to improve pip to let Versioneer work correctly. Versioneer-0.16 and earlier only looked for a `.git` directory next to the `setup.cfg`, so subprojects were completely unsupported with those releases. ### Editable installs with setuptools <= 18.5 `setup.py develop` and `pip install --editable .` allow you to install a project into a virtualenv once, then continue editing the source code (and test) without re-installing after every change. "Entry-point scripts" (`setup(entry_points={"console_scripts": ..})`) are a convenient way to specify executable scripts that should be installed along with the python package. These both work as expected when using modern setuptools. When using setuptools-18.5 or earlier, however, certain operations will cause `pkg_resources.DistributionNotFound` errors when running the entrypoint script, which must be resolved by re-installing the package. This happens when the install happens with one version, then the egg_info data is regenerated while a different version is checked out. Many setup.py commands cause egg_info to be rebuilt (including `sdist`, `wheel`, and installing into a different virtualenv), so this can be surprising. [Bug #83](https://github.com/warner/python-versioneer/issues/83) describes this one, but upgrading to a newer version of setuptools should probably resolve it. ### Unicode version strings While Versioneer works (and is continually tested) with both Python 2 and Python 3, it is not entirely consistent with bytes-vs-unicode distinctions. Newer releases probably generate unicode version strings on py2. It's not clear that this is wrong, but it may be surprising for applications when then write these strings to a network connection or include them in bytes-oriented APIs like cryptographic checksums. [Bug #71](https://github.com/warner/python-versioneer/issues/71) investigates this question. ## Updating Versioneer To upgrade your project to a new release of Versioneer, do the following: * install the new Versioneer (`pip install -U versioneer` or equivalent) * edit `setup.cfg`, if necessary, to include any new configuration settings indicated by the release notes. See [UPGRADING](./UPGRADING.md) for details. * re-run `versioneer install` in your source tree, to replace `SRC/_version.py` * commit any changed files ## Future Directions This tool is designed to make it easily extended to other version-control systems: all VCS-specific components are in separate directories like src/git/ . The top-level `versioneer.py` script is assembled from these components by running make-versioneer.py . In the future, make-versioneer.py will take a VCS name as an argument, and will construct a version of `versioneer.py` that is specific to the given VCS. It might also take the configuration arguments that are currently provided manually during installation by editing setup.py . Alternatively, it might go the other direction and include code from all supported VCS systems, reducing the number of intermediate scripts. ## License To make Versioneer easier to embed, all its code is dedicated to the public domain. The `_version.py` that it creates is also in the public domain. Specifically, both are released under the Creative Commons "Public Domain Dedication" license (CC0-1.0), as described in https://creativecommons.org/publicdomain/zero/1.0/ . """ from __future__ import print_function try: import configparser except ImportError: import ConfigParser as configparser import errno import json import os import re import subprocess import sys class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_root(): """Get the project root directory. We require that all commands are run from the project root, i.e. the directory that contains setup.py, setup.cfg, and versioneer.py . """ root = os.path.realpath(os.path.abspath(os.getcwd())) setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): # allow 'python path/to/setup.py COMMAND' root = os.path.dirname(os.path.realpath(os.path.abspath(sys.argv[0]))) setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): err = ("Versioneer was unable to run the project root directory. " "Versioneer requires setup.py to be executed from " "its immediate directory (like 'python setup.py COMMAND'), " "or in a way that lets it use sys.argv[0] to find the root " "(like 'python path/to/setup.py COMMAND').") raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools # tree) execute all dependencies in a single python process, so # "versioneer" may be imported multiple times, and python's shared # module-import table will cache the first one. So we can't use # os.path.dirname(__file__), as that will find whichever # versioneer.py was first imported, even in later projects. me = os.path.realpath(os.path.abspath(__file__)) me_dir = os.path.normcase(os.path.splitext(me)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) if me_dir != vsr_dir: print("Warning: build in %s is using versioneer.py from %s" % (os.path.dirname(me), versioneer_py)) except NameError: pass return root def get_config_from_root(root): """Read the project setup.cfg file to determine Versioneer config.""" # This might raise EnvironmentError (if setup.cfg is missing), or # configparser.NoSectionError (if it lacks a [versioneer] section), or # configparser.NoOptionError (if it lacks "VCS="). See the docstring at # the top of versioneer.py for instructions on writing your setup.cfg . setup_cfg = os.path.join(root, "setup.cfg") parser = configparser.SafeConfigParser() with open(setup_cfg, "r") as f: parser.readfp(f) VCS = parser.get("versioneer", "VCS") # mandatory def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" cfg.versionfile_source = get(parser, "versionfile_source") cfg.versionfile_build = get(parser, "versionfile_build") cfg.tag_prefix = get(parser, "tag_prefix") if cfg.tag_prefix in ("''", '""'): cfg.tag_prefix = "" cfg.parentdir_prefix = get(parser, "parentdir_prefix") cfg.verbose = get(parser, "verbose") return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" # these dictionaries contain VCS-specific tools LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %s" % dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %s" % (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %s (error)" % dispcmd) print("stdout was %s" % stdout) return None, p.returncode return stdout, p.returncode LONG_VERSION_PY['git'] = ''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build # directories (produced by setup.py build) will contain a much shorter file # that just contains the computed version number. # This file is released into the public domain. Generated by # versioneer-0.18 (https://github.com/warner/python-versioneer) """Git implementation of _version.py.""" import errno import os import re import subprocess import sys def get_keywords(): """Get the keywords needed to look up the version information.""" # these strings will be replaced by git during git-archive. # setup.py/versioneer.py will grep for the variable names, so they must # each be defined on a line of their own. _version.py will just call # get_keywords(). git_refnames = "%(DOLLAR)sFormat:%%d%(DOLLAR)s" git_full = "%(DOLLAR)sFormat:%%H%(DOLLAR)s" git_date = "%(DOLLAR)sFormat:%%ci%(DOLLAR)s" keywords = {"refnames": git_refnames, "full": git_full, "date": git_date} return keywords class VersioneerConfig: """Container for Versioneer configuration parameters.""" def get_config(): """Create, populate and return the VersioneerConfig() object.""" # these strings are filled in when 'setup.py versioneer' creates # _version.py cfg = VersioneerConfig() cfg.VCS = "git" cfg.style = "%(STYLE)s" cfg.tag_prefix = "%(TAG_PREFIX)s" cfg.parentdir_prefix = "%(PARENTDIR_PREFIX)s" cfg.versionfile_source = "%(VERSIONFILE_SOURCE)s" cfg.verbose = False return cfg class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" LONG_VERSION_PY = {} HANDLERS = {} def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f return decorate def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, env=None): """Call the given command(s).""" assert isinstance(commands, list) p = None for c in commands: try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git p = subprocess.Popen([c] + args, cwd=cwd, env=env, stdout=subprocess.PIPE, stderr=(subprocess.PIPE if hide_stderr else None)) break except EnvironmentError: e = sys.exc_info()[1] if e.errno == errno.ENOENT: continue if verbose: print("unable to run %%s" %% dispcmd) print(e) return None, None else: if verbose: print("unable to find command, tried %%s" %% (commands,)) return None, None stdout = p.communicate()[0].strip() if sys.version_info[0] >= 3: stdout = stdout.decode() if p.returncode != 0: if verbose: print("unable to run %%s (error)" %% dispcmd) print("stdout was %%s" %% stdout) return None, p.returncode return stdout, p.returncode def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %%s but none started with prefix %%s" %% (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %%d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%%s', no digits" %% ",".join(refs - tags)) if verbose: print("likely tags: %%s" %% ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %%s" %% r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %%s not under git control" %% root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%%s*" %% tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%%s'" %% describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%%s' doesn't start with prefix '%%s'" print(fmt %% (full_tag, tag_prefix)) pieces["error"] = ("tag '%%s' doesn't start with prefix '%%s'" %% (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%%d.g%%s" %% (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%%d.g%%s" %% (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%%d" %% pieces["distance"] else: # exception #1 rendered = "0.post.dev%%d" %% pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%%s" %% pieces["short"] else: # exception #1 rendered = "0.post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%%s" %% pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%%d" %% pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%%d-g%%s" %% (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%%s'" %% style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} def get_versions(): """Get version information or return default if unable to do so.""" # I am in _version.py, which lives at ROOT/VERSIONFILE_SOURCE. If we have # __file__, we can work backwards from there to the root. Some # py2exe/bbfreeze/non-CPython implementations don't do __file__, in which # case we can only use expanded keywords. cfg = get_config() verbose = cfg.verbose try: return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, verbose) except NotThisMethod: pass try: root = os.path.realpath(__file__) # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. for i in cfg.versionfile_source.split('/'): root = os.path.dirname(root) except NameError: return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to find root of source tree", "date": None} try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) return render(pieces, cfg.style) except NotThisMethod: pass try: if cfg.parentdir_prefix: return versions_from_parentdir(cfg.parentdir_prefix, root, verbose) except NotThisMethod: pass return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} ''' @register_vcs_handler("git", "get_keywords") def git_get_keywords(versionfile_abs): """Extract version information from the given file.""" # the code embedded in _version.py can just fetch the value of these # keywords. When used from setup.py, we don't want to import _version.py, # so we do it with a regexp instead. This function is not used from # _version.py. keywords = {} try: f = open(versionfile_abs, "r") for line in f.readlines(): if line.strip().startswith("git_refnames ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["refnames"] = mo.group(1) if line.strip().startswith("git_full ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["full"] = mo.group(1) if line.strip().startswith("git_date ="): mo = re.search(r'=\s*"(.*)"', line) if mo: keywords["date"] = mo.group(1) f.close() except EnvironmentError: pass return keywords @register_vcs_handler("git", "keywords") def git_versions_from_keywords(keywords, tag_prefix, verbose): """Get version information from git keywords.""" if not keywords: raise NotThisMethod("no keywords at all, weird") date = keywords.get("date") if date is not None: # git-2.2.0 added "%cI", which expands to an ISO-8601 -compliant # datestamp. However we prefer "%ci" (which expands to an "ISO-8601 # -like" string, which we must then edit to make compliant), because # it's been around since git-1.5.3, and it's too difficult to # discover which version we're using, or to work around using an # older one. date = date.strip().replace(" ", "T", 1).replace(" ", "", 1) refnames = keywords["refnames"].strip() if refnames.startswith("$Format"): if verbose: print("keywords are unexpanded, not using") raise NotThisMethod("unexpanded keywords, not a git-archive tarball") refs = set([r.strip() for r in refnames.strip("()").split(",")]) # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d # expansion behaves like git log --decorate=short and strips out the # refs/heads/ and refs/tags/ prefixes that would let us distinguish # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". tags = set([r for r in refs if re.search(r'\d', r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: print("likely tags: %s" % ",".join(sorted(tags))) for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): r = ref[len(tag_prefix):] if verbose: print("picking %s" % r) return {"version": r, "full-revisionid": keywords["full"].strip(), "dirty": False, "error": None, "date": date} # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") return {"version": "0+unknown", "full-revisionid": keywords["full"].strip(), "dirty": False, "error": "no suitable tags", "date": None} @register_vcs_handler("git", "pieces_from_vcs") def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): """Get version from 'git describe' in the root of the source tree. This only gets called if the git-archive 'subst' keywords were *not* expanded, and _version.py hasn't already been rewritten with a short version string, meaning we're inside a checked out source tree. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True) if rc != 0: if verbose: print("Directory %s not under git control" % root) raise NotThisMethod("'git rev-parse --git-dir' returned error") # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", "--always", "--long", "--match", "%s*" % tag_prefix], cwd=root) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") describe_out = describe_out.strip() full_out, rc = run_command(GITS, ["rev-parse", "HEAD"], cwd=root) if full_out is None: raise NotThisMethod("'git rev-parse' failed") full_out = full_out.strip() pieces = {} pieces["long"] = full_out pieces["short"] = full_out[:7] # maybe improved later pieces["error"] = None # parse describe_out. It will be like TAG-NUM-gHEX[-dirty] or HEX[-dirty] # TAG might have hyphens. git_describe = describe_out # look for -dirty suffix dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: git_describe = git_describe[:git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? pieces["error"] = ("unable to parse git-describe output: '%s'" % describe_out) return pieces # tag full_tag = mo.group(1) if not full_tag.startswith(tag_prefix): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" % (full_tag, tag_prefix)) return pieces pieces["closest-tag"] = full_tag[len(tag_prefix):] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) # commit: short hex revision ID pieces["short"] = mo.group(3) else: # HEX: no tags pieces["closest-tag"] = None count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], cwd=root) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces def do_vcs_install(manifest_in, versionfile_source, ipy): """Git-specific installation logic for Versioneer. For Git, this means creating/changing .gitattributes to mark _version.py for export-subst keyword substitution. """ GITS = ["git"] if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] files = [manifest_in, versionfile_source] if ipy: files.append(ipy) try: me = __file__ if me.endswith(".pyc") or me.endswith(".pyo"): me = os.path.splitext(me)[0] + ".py" versioneer_file = os.path.relpath(me) except NameError: versioneer_file = "versioneer.py" files.append(versioneer_file) present = False try: f = open(".gitattributes", "r") for line in f.readlines(): if line.strip().startswith(versionfile_source): if "export-subst" in line.strip().split()[1:]: present = True f.close() except EnvironmentError: pass if not present: f = open(".gitattributes", "a+") f.write("%s export-subst\n" % versionfile_source) f.close() files.append(".gitattributes") run_command(GITS, ["add", "--"] + files) def versions_from_parentdir(parentdir_prefix, root, verbose): """Try to determine the version from the parent directory name. Source tarballs conventionally unpack into a directory that includes both the project name and a version string. We will also support searching up two directory levels for an appropriately named parent directory """ rootdirs = [] for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): return {"version": dirname[len(parentdir_prefix):], "full-revisionid": None, "dirty": False, "error": None, "date": None} else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: print("Tried directories %s but none started with prefix %s" % (str(rootdirs), parentdir_prefix)) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") SHORT_VERSION_PY = """ # This file was generated by 'versioneer.py' (0.18) from # revision-control system data, or from the parent directory name of an # unpacked source archive. Distribution tarballs contain a pre-generated copy # of this file. import json version_json = ''' %s ''' # END VERSION_JSON def get_versions(): return json.loads(version_json) """ def versions_from_file(filename): """Try to determine the version from _version.py if present.""" try: with open(filename) as f: contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", contents, re.M | re.S) if not mo: mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", contents, re.M | re.S) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) def write_to_version_file(filename, versions): """Write the given version number to the given _version.py file.""" os.unlink(filename) contents = json.dumps(versions, sort_keys=True, indent=1, separators=(",", ": ")) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) print("set %s to '%s'" % (filename, versions["version"])) def plus_or_dot(pieces): """Return a + if we don't already have one, else return a .""" if "+" in pieces.get("closest-tag", ""): return "." return "+" def render_pep440(pieces): """Build up version string, with post-release "local version identifier". Our goal: TAG[+DISTANCE.gHEX[.dirty]] . Note that if you get a tagged build and then dirty it, you'll get TAG+0.gHEX.dirty Exceptions: 1: no tags. git_describe was just HEX. 0+untagged.DISTANCE.gHEX[.dirty] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += plus_or_dot(pieces) rendered += "%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" else: # exception #1 rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered def render_pep440_pre(pieces): """TAG[.post.devDISTANCE] -- No -dirty. Exceptions: 1: no tags. 0.post.devDISTANCE """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += ".post.dev%d" % pieces["distance"] else: # exception #1 rendered = "0.post.dev%d" % pieces["distance"] return rendered def render_pep440_post(pieces): """TAG[.postDISTANCE[.dev0]+gHEX] . The ".dev0" means dirty. Note that .dev0 sorts backwards (a dirty tree will appear "older" than the corresponding clean one), but you shouldn't be releasing software with -dirty anyways. Exceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += plus_or_dot(pieces) rendered += "g%s" % pieces["short"] else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" rendered += "+g%s" % pieces["short"] return rendered def render_pep440_old(pieces): """TAG[.postDISTANCE[.dev0]] . The ".dev0" means dirty. Eexceptions: 1: no tags. 0.postDISTANCE[.dev0] """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"] or pieces["dirty"]: rendered += ".post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" else: # exception #1 rendered = "0.post%d" % pieces["distance"] if pieces["dirty"]: rendered += ".dev0" return rendered def render_git_describe(pieces): """TAG[-DISTANCE-gHEX][-dirty]. Like 'git describe --tags --dirty --always'. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] if pieces["distance"]: rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render_git_describe_long(pieces): """TAG-DISTANCE-gHEX[-dirty]. Like 'git describe --tags --dirty --always -long'. The distance/hash is unconditional. Exceptions: 1: no tags. HEX[-dirty] (note: no 'g' prefix) """ if pieces["closest-tag"]: rendered = pieces["closest-tag"] rendered += "-%d-g%s" % (pieces["distance"], pieces["short"]) else: # exception #1 rendered = pieces["short"] if pieces["dirty"]: rendered += "-dirty" return rendered def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: return {"version": "unknown", "full-revisionid": pieces.get("long"), "dirty": None, "error": pieces["error"], "date": None} if not style or style == "default": style = "pep440" # the default if style == "pep440": rendered = render_pep440(pieces) elif style == "pep440-pre": rendered = render_pep440_pre(pieces) elif style == "pep440-post": rendered = render_pep440_post(pieces) elif style == "pep440-old": rendered = render_pep440_old(pieces) elif style == "git-describe": rendered = render_git_describe(pieces) elif style == "git-describe-long": rendered = render_git_describe_long(pieces) else: raise ValueError("unknown style '%s'" % style) return {"version": rendered, "full-revisionid": pieces["long"], "dirty": pieces["dirty"], "error": None, "date": pieces.get("date")} class VersioneerBadRootError(Exception): """The project root directory is unknown or missing key files.""" def get_versions(verbose=False): """Get the project version from whatever source is available. Returns dict with two keys: 'version' and 'full'. """ if "versioneer" in sys.modules: # see the discussion in cmdclass.py:get_cmdclass() del sys.modules["versioneer"] root = get_root() cfg = get_config_from_root(root) assert cfg.VCS is not None, "please set [versioneer]VCS= in setup.cfg" handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose assert cfg.versionfile_source is not None, \ "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) # extract version from first of: _version.py, VCS command (e.g. 'git # describe'), parentdir. This is meant to work for developers using a # source checkout, for users of a tarball created by 'setup.py sdist', # and for users of a tarball/zipball created by 'git archive' or github's # download-from-tag feature or the equivalent in other VCSes. get_keywords_f = handlers.get("get_keywords") from_keywords_f = handlers.get("keywords") if get_keywords_f and from_keywords_f: try: keywords = get_keywords_f(versionfile_abs) ver = from_keywords_f(keywords, cfg.tag_prefix, verbose) if verbose: print("got version from expanded keyword %s" % ver) return ver except NotThisMethod: pass try: ver = versions_from_file(versionfile_abs) if verbose: print("got version from file %s %s" % (versionfile_abs, ver)) return ver except NotThisMethod: pass from_vcs_f = handlers.get("pieces_from_vcs") if from_vcs_f: try: pieces = from_vcs_f(cfg.tag_prefix, root, verbose) ver = render(pieces, cfg.style) if verbose: print("got version from VCS %s" % ver) return ver except NotThisMethod: pass try: if cfg.parentdir_prefix: ver = versions_from_parentdir(cfg.parentdir_prefix, root, verbose) if verbose: print("got version from parentdir %s" % ver) return ver except NotThisMethod: pass if verbose: print("unable to compute version") return {"version": "0+unknown", "full-revisionid": None, "dirty": None, "error": "unable to compute version", "date": None} def get_version(): """Get the short version string for this project.""" return get_versions()["version"] def get_cmdclass(): """Get the custom setuptools/distutils subclasses used by Versioneer.""" if "versioneer" in sys.modules: del sys.modules["versioneer"] # this fixes the "python setup.py develop" case (also 'install' and # 'easy_install .'), in which subdependencies of the main project are # built (using setup.py bdist_egg) in the same python process. Assume # a main project A and a dependency B, which use different versions # of Versioneer. A's setup.py imports A's Versioneer, leaving it in # sys.modules by the time B's setup.py is executed, causing B to run # with the wrong versioneer. Setuptools wraps the sub-dep builds in a # sandbox that restores sys.modules to it's pre-build state, so the # parent is protected against the child's "import versioneer". By # removing ourselves from sys.modules here, before the child build # happens, we protect the child from the parent's versioneer too. # Also see https://github.com/warner/python-versioneer/issues/52 cmds = {} # we add "version" to both distutils and setuptools from distutils.core import Command class cmd_version(Command): description = "report generated version string" user_options = [] boolean_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): vers = get_versions(verbose=True) print("Version: %s" % vers["version"]) print(" full-revisionid: %s" % vers.get("full-revisionid")) print(" dirty: %s" % vers.get("dirty")) print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools # # most invocation pathways end up running build_py: # distutils/build -> build_py # distutils/install -> distutils/build ->.. # setuptools/bdist_wheel -> distutils/install ->.. # setuptools/bdist_egg -> distutils/install_lib -> build_py # setuptools/install -> bdist_egg ->.. # setuptools/develop -> ? # pip install: # copies source tree to a tempdir before running egg_info/etc # if .git isn't copied too, 'git describe' will fail # then does setup.py bdist_wheel, or sometimes setup.py install # setup.py egg_info -> ? # we override different "build_py" commands for both environments if "setuptools" in sys.modules: from setuptools.command.build_py import build_py as _build_py else: from distutils.command.build_py import build_py as _build_py class cmd_build_py(_build_py): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() _build_py.run(self) # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: target_versionfile = os.path.join(self.build_lib, cfg.versionfile_build) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) cmds["build_py"] = cmd_build_py if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe # nczeczulin reports that py2exe won't like the pep440-style string # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. # setup(console=[{ # "version": versioneer.get_version().split("+", 1)[0], # FILEVERSION # "product_version": versioneer.get_version(), # ... class cmd_build_exe(_build_exe): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() target_versionfile = cfg.versionfile_source print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) _build_exe.run(self) os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) cmds["build_exe"] = cmd_build_exe del cmds["build_py"] if 'py2exe' in sys.modules: # py2exe enabled? try: from py2exe.distutils_buildexe import py2exe as _py2exe # py3 except ImportError: from py2exe.build_exe import py2exe as _py2exe # py2 class cmd_py2exe(_py2exe): def run(self): root = get_root() cfg = get_config_from_root(root) versions = get_versions() target_versionfile = cfg.versionfile_source print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) _py2exe.run(self) os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) cmds["py2exe"] = cmd_py2exe # we override different "sdist" commands for both environments if "setuptools" in sys.modules: from setuptools.command.sdist import sdist as _sdist else: from distutils.command.sdist import sdist as _sdist class cmd_sdist(_sdist): def run(self): versions = get_versions() self._versioneer_generated_versions = versions # unless we update this, the command will keep using the old # version self.distribution.metadata.version = versions["version"] return _sdist.run(self) def make_release_tree(self, base_dir, files): root = get_root() cfg = get_config_from_root(root) _sdist.make_release_tree(self, base_dir, files) # now locate _version.py in the new base_dir directory # (remembering that it may be a hardlink) and replace it with an # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, self._versioneer_generated_versions) cmds["sdist"] = cmd_sdist return cmds CONFIG_ERROR = """ setup.cfg is missing the necessary Versioneer configuration. You need a section like: [versioneer] VCS = git style = pep440 versionfile_source = src/myproject/_version.py versionfile_build = myproject/_version.py tag_prefix = parentdir_prefix = myproject- You will also need to edit your setup.py to use the results: import versioneer setup(version=versioneer.get_version(), cmdclass=versioneer.get_cmdclass(), ...) Please read the docstring in ./versioneer.py for configuration instructions, edit setup.cfg, and re-run the installer or 'python versioneer.py setup'. """ SAMPLE_CONFIG = """ # See the docstring in versioneer.py for instructions. Note that you must # re-run 'versioneer.py setup' after changing this section, and commit the # resulting files. [versioneer] #VCS = git #style = pep440 #versionfile_source = #versionfile_build = #tag_prefix = #parentdir_prefix = """ INIT_PY_SNIPPET = """ from ._version import get_versions __version__ = get_versions()['version'] del get_versions """ def do_setup(): """Main VCS-independent setup function for installing Versioneer.""" root = get_root() try: cfg = get_config_from_root(root) except (EnvironmentError, configparser.NoSectionError, configparser.NoOptionError) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): print("Adding sample versioneer config to setup.cfg", file=sys.stderr) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) return 1 print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] f.write(LONG % {"DOLLAR": "$", "STYLE": cfg.style, "TAG_PREFIX": cfg.tag_prefix, "PARENTDIR_PREFIX": cfg.parentdir_prefix, "VERSIONFILE_SOURCE": cfg.versionfile_source, }) ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: old = f.read() except EnvironmentError: old = "" if INIT_PY_SNIPPET not in old: print(" appending to %s" % ipy) with open(ipy, "a") as f: f.write(INIT_PY_SNIPPET) else: print(" %s unmodified" % ipy) else: print(" %s doesn't exist, ok" % ipy) ipy = None # Make sure both the top-level "versioneer.py" and versionfile_source # (PKG/_version.py, used by runtime code) are in MANIFEST.in, so # they'll be copied into source distributions. Pip won't be able to # install the package without this. manifest_in = os.path.join(root, "MANIFEST.in") simple_includes = set() try: with open(manifest_in, "r") as f: for line in f: if line.startswith("include "): for include in line.split()[1:]: simple_includes.add(include) except EnvironmentError: pass # That doesn't cover everything MANIFEST.in can do # (http://docs.python.org/2/distutils/sourcedist.html#commands), so # it might give some false negatives. Appending redundant 'include' # lines is safe, though. if "versioneer.py" not in simple_includes: print(" appending 'versioneer.py' to MANIFEST.in") with open(manifest_in, "a") as f: f.write("include versioneer.py\n") else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: print(" appending versionfile_source ('%s') to MANIFEST.in" % cfg.versionfile_source) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: print(" versionfile_source already in MANIFEST.in") # Make VCS-specific changes. For git, this means creating/changing # .gitattributes to mark _version.py for export-subst keyword # substitution. do_vcs_install(manifest_in, cfg.versionfile_source, ipy) return 0 def scan_setup_py(): """Validate the contents of setup.py against Versioneer's expectations.""" found = set() setters = False errors = 0 with open("setup.py", "r") as f: for line in f.readlines(): if "import versioneer" in line: found.add("import") if "versioneer.get_cmdclass()" in line: found.add("cmdclass") if "versioneer.get_version()" in line: found.add("get_version") if "versioneer.VCS" in line: setters = True if "versioneer.versionfile_source" in line: setters = True if len(found) != 3: print("") print("Your setup.py appears to be missing some important items") print("(but I might be wrong). Please make sure it has something") print("roughly like the following:") print("") print(" import versioneer") print(" setup( version=versioneer.get_version(),") print(" cmdclass=versioneer.get_cmdclass(), ...)") print("") errors += 1 if setters: print("You should remove lines like 'versioneer.VCS = ' and") print("'versioneer.versionfile_source = ' . This configuration") print("now lives in setup.cfg, and should be removed from setup.py") print("") errors += 1 return errors if __name__ == "__main__": cmd = sys.argv[1] if cmd == "setup": errors = do_setup() errors += scan_setup_py() if errors: sys.exit(1)