Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • inducer/islpy
  • isuruf/islpy
  • kaushikcfd/islpy
  • mattwala/islpy
4 results
Select Git revision
Show changes
Commits on Source (530)
Showing
with 1001 additions and 206 deletions
# https://editorconfig.org/
# https://github.com/editorconfig/editorconfig-vim
# https://github.com/editorconfig/editorconfig-emacs
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_size = 4
[*.rst]
indent_size = 4
[*.cpp]
indent_size = 2
[*.hpp]
indent_size = 2
# There may be one in doc/
[Makefile]
indent_style = tab
# https://github.com/microsoft/vscode/issues/1679
[*.md]
trim_trailing_whitespace = false
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Environment (please complete the following information):**
- OS: [e.g. Linux]
- Python version: [e.g. 3.10]
**Additional context**
Add any other context about the problem here.
blank_issues_enabled: true
contact_links:
- name: ❓ Question
url: https://github.com/inducer/islpy/discussions/categories/q-a
about: Ask and answer questions about islpy on Discussions
- name: 🔧 Troubleshooting
url: https://github.com/inducer/islpy/discussions/categories/troubleshooting
about: For troubleshooting help, see the Discussions
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
version: 2
updates:
# Set update schedule for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
# vim: sw=4
name: Gitlab mirror
on:
push:
branches:
- main
jobs:
autopush:
name: Automatic push to gitlab.tiker.net
if: startsWith(github.repository, 'inducer/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
mirror_github_to_gitlab
env:
GITLAB_AUTOPUSH_KEY: ${{ secrets.GITLAB_AUTOPUSH_KEY }}
# vim: sw=4
name: CI
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '17 3 * * 0'
jobs:
ruff:
name: Ruff
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: "Main Script"
run: |
pipx install ruff
ruff check
pytest:
name: Pytest Linux on Py${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.10', '3.x']
steps:
- uses: actions/checkout@v4
-
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: "Main Script"
run: |
EXTRA_INSTALL="numpy"
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
. ./build-and-test-py-project.sh
examples:
name: Examples Linux on Py${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.8', '3.10', '3.x']
steps:
- uses: actions/checkout@v4
-
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: "Main Script"
run: |
EXTRA_INSTALL="matplotlib numpy"
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
build_py_project_in_venv
run_examples
pytest_mac:
name: Pytest macOS
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
-
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: "Main Script"
run: |
export MACOS_DEPLOYMENT_TARGET=10.14
curl -L -O https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
. ./build-and-test-py-project.sh
docs:
name: Documentation
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
-
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: "Main Script"
run: |
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
build_py_project_in_venv
# https://github.com/sphinx-doc/sphinx/issues/9200
CI_SUPPORT_SPHINX_VERSION_SPECIFIER="!=4.0.0"
build_docs
downstream_tests:
strategy:
matrix:
downstream_project: [loopy]
name: Tests for downstream project ${{ matrix.downstream_project }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: "Main Script"
env:
DOWNSTREAM_PROJECT: ${{ matrix.downstream_project }}
run: |
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
git clone "https://github.com/inducer/$DOWNSTREAM_PROJECT.git"
cd "$DOWNSTREAM_PROJECT"
echo "*** $DOWNSTREAM_PROJECT version: $(git rev-parse --short HEAD)"
edit_requirements_txt_for_downstream_in_subdir
sed -i '/islpy/ d' .test-conda-env-py3.yml
export CONDA_ENVIRONMENT=.test-conda-env-py3.yml
# Avoid slow or complicated tests in downstream projects
export PYTEST_ADDOPTS="-k 'not (slowtest or octave or mpi)'"
build_py_project_in_conda_env
test_py_project
barvinok:
name: "Test barvinok build script"
runs-on: ubuntu-latest
env:
GITHUB_HEAD_REPOSITORY: ${{github.event.pull_request.head.repo.full_name}}
steps:
- uses: actions/checkout@v4
-
uses: actions/setup-python@v5
with:
python-version: "3.x"
- name: "Main Script"
run: |
python3 -m venv .env
source .env/bin/activate
python -m ensurepip
pip install pcpp pytest numpy
./build-with-barvinok.sh "$HOME/barvinok-build"
(cd test; LD_LIBRARY_PATH="$HOME/barvinok-build/lib" python -m pytest --tb=native -rxsw)
# vim: sw=4
name: Build wheels
on:
push:
branches:
- main
tags:
- v*
pull_request:
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-13, macos-14, ubuntu-22.04-arm]
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
persist-credentials: false
- name: Build wheels
uses: pypa/cibuildwheel@v2.23.2
# (here: set these in pyproject.toml to the extent possible)
# env:
# CIBW_SOME_OPTION: value
# CIBW_BUILD_VERBOSITY: 1
# VERBOSE: 1
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Build sdist
run: |
pipx --version
pipx run build --sdist
- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz
upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
# upload to PyPI on every tag starting with 'v'
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
# alternatively, to publish when a GitHub Release is created, use the following rule:
# if: github.event_name == 'release' && github.event.action == 'published'
environment:
name: pypi
url: https://pypi.org/p/islpy
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@v1.12.4
# with:
# user: __token__
# password: ${{ secrets.pypi_password }}
# To test: repository_url: https://test.pypi.org/legacy/
upload_testpypi:
if: startsWith(github.ref, 'refs/tags/testv')
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/islpy
permissions:
id-token: write # IMPORTANT: mandatory for trusted publishing
steps:
- name: Download all the dists
uses: actions/download-artifact@v4
with:
pattern: cibw-*
path: dist/
merge-multiple: true
- name: Publish distribution 📦 to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
...@@ -48,8 +48,18 @@ setuptools-*.tar.gz ...@@ -48,8 +48,18 @@ setuptools-*.tar.gz
core core
src/wrapper/gen-* src/wrapper/gen-*
.dirty-git-ok .dirty-git-ok
wrapped-functions.h
_isl_cffi.c # wheels
_isl.py arch_tmp
name_list.py archives
islpy_cffi_build.py downloads
gmp-*
isl-*
wheelhouse
venv
_skbuild/
libnanobind-static.a
CMakeFiles/
preproc-headers
Python 2.7: Python 3:
script: script: |
- export PY_EXE=python2.7 EXTRA_INSTALL="numpy"
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
- ". ./build-and-test-py-project.sh" . ./build-and-test-py-project.sh
tags: tags:
- python2.7 - python3
except: except:
- tags - tags
Python 3.4: artifacts:
script: reports:
- export PY_EXE=python3.4 junit: test/pytest.xml
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh
- ". ./build-and-test-py-project.sh" Examples:
script: |
EXTRA_INSTALL="matplotlib numpy"
curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/ci-support.sh
. ./ci-support.sh
build_py_project_in_venv
run_examples
tags: tags:
- python3.4 - python3
except: except:
- tags - tags
Python 2.6:
script: Python 3 without small-integer opt:
- export PY_EXE=python2.6 script: |
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
- ". ./build-and-test-py-project.sh" ./configure.py --no-use-imath-sio
. ./build-and-test-py-project.sh
tags:
- python3
except:
- tags
artifacts:
reports:
junit: test/pytest.xml
Python 3 + Barvinok:
script: |
git clean -fdx
python3 -m venv .env
source .env/bin/activate
python -m ensurepip
pip install pcpp numpy pytest
./build-with-barvinok.sh "$HOME/barvinok-build"
(cd test; LD_LIBRARY_PATH="$HOME/barvinok-build/lib" python -m pytest --tb=native -rxsw)
tags: tags:
- python2.6 - python3
except: except:
- tags - tags
PyPy: artifacts:
reports:
junit: test/pytest.xml
PyPy3:
script: script:
- export PY_EXE=pypy - export PY_EXE=pypy3
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-and-test-py-project.sh - curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/main/build-and-test-py-project.sh
- ". ./build-and-test-py-project.sh" - ". ./build-and-test-py-project.sh"
allow_failure: true
tags: tags:
- pypy - pypy
except: except:
- tags - tags
artifacts:
reports:
junit: test/pytest.xml
Documentation:
script: |
curl -L -O -k https://tiker.net/ci-support-v0
. ci-support-v0
build_py_project_in_venv
build_docs
maybe_upload_docs
tags:
- python3
only:
- main
Ruff:
script:
- pipx install ruff
- ruff check
tags:
- docker-runner
except:
- tags
[submodule "isl"] [submodule "isl"]
path = isl path = isl
url = http://repo.or.cz/isl.git url = https://github.com/inducer/isl.git
cff-version: 1.2.0
message: "If you use this software, please cite it as below."
authors:
- family-names: "Kloeckner"
given-names: "Andreas"
orcid: "https://orcid.org/0000-0003-1228-519X"
- family-names: "Fernando"
given-names: "Isuru"
- family-names: "Yang"
given-names: "Cambridge"
- family-names: "Kulkarni"
given-names: "Kaushik"
# Disabled pending https://github.com/zenodo/zenodo/issues/2343
# - alias: "iasakura"
- family-names: "Almahallawi"
given-names: "Deyaaeldeen"
- family-names: "Gao"
given-names: "Hao"
- family-names: "Stevens"
given-names: "James"
- family-names: "Fikl"
given-names: "Alex"
- family-names: "Wala"
given-names: "Matt"
# Disabled pending https://github.com/zenodo/zenodo/issues/2343
# - alias: "bhuztez"
title: "islpy"
version: 2022.1.2
doi: 10.5281/zenodo.6345184
date-released: 2022-03-10
url: "https://github.com/inducer/islpy"
license: MIT
cmake_minimum_required(VERSION 3.15...3.27)
project(islpy)
find_package(Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED)
# Force Release build by default
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
# Detect the installed nanobind package and import it into CMake
execute_process(
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE NB_DIR)
list(APPEND CMAKE_PREFIX_PATH "${NB_DIR}")
find_package(nanobind CONFIG REQUIRED)
nanobind_add_module(
_isl
NB_STATIC # Build static libnanobind (the extension module itself remains a shared library)
NOMINSIZE # Optimize for speed, not for size
LTO # Enable LTO
src/wrapper/wrap_isl.cpp
src/wrapper/wrap_isl_part1.cpp
src/wrapper/wrap_isl_part2.cpp
src/wrapper/wrap_isl_part3.cpp
${ISL_SOURCES}
)
# Work around https://github.com/inducer/islpy/issues/120.
# See https://stackoverflow.com/questions/43554227/extern-inline-func-results-in-undefined-reference-error
# for some context.
set_source_files_properties(${ISL_SOURCES} PROPERTIES COMPILE_DEFINITIONS __OPTIMIZE_SIZE__)
if(USE_IMATH_FOR_MP)
target_compile_definitions(_isl PRIVATE USE_IMATH_FOR_MP=1)
endif()
if(USE_IMATH_SIO)
target_compile_definitions(_isl PRIVATE USE_SMALL_INT_OPT=1)
endif()
if(USE_GMP_FOR_MP)
target_compile_definitions(_isl PRIVATE USE_GMP_FOR_MP=1)
endif()
if(USE_BARVINOK)
target_compile_definitions(_isl PRIVATE ISLPY_INCLUDE_BARVINOK=1)
target_include_directories(_isl PRIVATE ${BARVINOK_INC_DIRS})
target_link_directories(_isl PRIVATE ${BARVINOK_LIB_DIRS})
target_link_libraries(_isl PRIVATE ${BARVINOK_LIB_NAMES})
endif()
target_include_directories(_isl PRIVATE ${ISL_INC_DIRS})
if(USE_SHIPPED_ISL)
target_compile_definitions(_isl PRIVATE GIT_HEAD_ID="${ISL_GIT_HEAD_ID}")
else()
target_link_directories(_isl PRIVATE ${ISL_LIB_DIRS})
target_link_libraries(_isl PRIVATE ${ISL_LIB_NAMES})
endif()
install(TARGETS _isl LIBRARY DESTINATION .)
# vim: sw=2
include isl/*.ac
include isl/*.c include isl/*.c
include isl/*.h include isl/*.h
include isl/imath/*.c include isl/imath/*.c
include isl/imath/*.h include isl/imath/*.h
include isl/imath_wrap/*.h include isl/imath_wrap/*.h
include isl/imath_wrap/*.c
include isl/include/isl/*.h include isl/include/isl/*.h
include isl/include/isl/*.c
include isl/include/isl/deprecated/*.h include isl/include/isl/deprecated/*.h
include isl-supplementary/isl/*.h include isl-supplementary/isl/*.h
include isl-supplementary/*.h include isl-supplementary/*.h
include isl_declaration_macros_expanded*.h include build-with-barvinok.sh
include src/wrapper/*.h include src/wrapper/*.h
include src/wrapper/*.hpp include src/wrapper/*.hpp
include src/wrapper/*.cpp include src/wrapper/*.cpp
include gen_wrap.py include gen_wrap.py
include py_codegen.py
include test/*.py include test/*.py
include example/*.py include examples/*.py
include doc/*.rst include doc/*.rst
include doc/images/*png include doc/images/*png
...@@ -30,5 +33,11 @@ include configure.py ...@@ -30,5 +33,11 @@ include configure.py
include Makefile.in include Makefile.in
include aksetup_helper.py include aksetup_helper.py
include README_SETUP.txt include README_SETUP.txt
include CMakeLists.txt
include README.rst include README.rst
include CITATION.cff
include isl/LICENSE
include isl/imath/LICENSE
recursive-exclude preproc-headers *
.. image:: https://badge.fury.io/py/islpy.png islpy: Polyhedral Analysis from Python
:target: http://pypi.python.org/pypi/islpy ======================================
.. image:: https://gitlab.tiker.net/inducer/islpy/badges/main/pipeline.svg
:alt: Gitlab Build Status
:target: https://gitlab.tiker.net/inducer/islpy/commits/main
.. image:: https://github.com/inducer/islpy/actions/workflows/ci.yml/badge.svg
:alt: Github Build Status
:target: https://github.com/inducer/islpy/actions/workflows/ci.yml
.. image:: https://badge.fury.io/py/islpy.svg
:alt: Python Package Index Release Page
:target: https://pypi.org/project/islpy/
.. image:: https://zenodo.org/badge/2021524.svg
:alt: Zenodo DOI for latest release
:target: https://zenodo.org/badge/latestdoi/2021524
islpy is a Python wrapper around Sven Verdoolaege's `isl islpy is a Python wrapper around Sven Verdoolaege's `isl
<http://www.kotnet.org/~skimo/isl/>`_, a library for manipulating sets and <https://libisl.sourceforge.io/>`_, a library for manipulating sets and
relations of integer points bounded by linear constraints. relations of integer points bounded by linear constraints.
Supported operations on sets include Supported operations on sets include
...@@ -22,6 +35,12 @@ bounds on piecewise step-polynomials. ...@@ -22,6 +35,12 @@ bounds on piecewise step-polynomials.
Islpy comes with comprehensive `documentation <http://documen.tician.de/islpy>`_. Islpy comes with comprehensive `documentation <http://documen.tician.de/islpy>`_.
*Requirements:* islpy needs a C++ compiler to build. GMP, which used to be *Requirements:* islpy needs a C++ compiler to build. It can optionally make use
a dependency, is no longer required. of GMP for support of large integers.
One important thing to know about islpy is that it exposes every function in isl
that is visible in the headers, not just what isl's authors consider its
documented, public API (marked by ``__isl_export``). These (technically)
undocumented functions are marked in the islpy documentation. Many of them are useful
and essential for certain operations, but isl's API stability guarantees do not
apply to them. Use them at your own risk.
import setuptools # noqa import os
from setuptools import Extension import sys
try:
from setuptools import Extension
from setuptools.command.build_ext import ( # noqa: N812
build_ext as BaseBuildExtCommand)
except ImportError:
class Extension:
pass
class BaseBuildExtCommand:
pass
def count_down_delay(delay): def count_down_delay(delay):
from time import sleep from time import sleep
import sys
while delay: while delay:
sys.stdout.write("Continuing in %d seconds... \r" % delay) sys.stdout.write("Continuing in %d seconds... \r" % delay)
sys.stdout.flush() sys.stdout.flush()
...@@ -12,6 +22,7 @@ def count_down_delay(delay): ...@@ -12,6 +22,7 @@ def count_down_delay(delay):
sleep(1) sleep(1)
print("") print("")
DASH_SEPARATOR = 75 * "-" DASH_SEPARATOR = 75 * "-"
...@@ -23,7 +34,7 @@ def setup(*args, **kwargs): ...@@ -23,7 +34,7 @@ def setup(*args, **kwargs):
raise raise
except SystemExit: except SystemExit:
raise raise
except: except Exception:
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
print("Sorry, your build failed. Try rerunning configure.py with " print("Sorry, your build failed. Try rerunning configure.py with "
"different options.") "different options.")
...@@ -31,6 +42,24 @@ def setup(*args, **kwargs): ...@@ -31,6 +42,24 @@ def setup(*args, **kwargs):
raise raise
def get_numpy_incpath():
from os.path import join, dirname, exists
from importlib.util import find_spec
origin = find_spec("numpy").origin
if origin is None:
raise RuntimeError("origin of numpy package not found")
pathname = dirname(origin)
for p in [
join(pathname, "_core", "include"), # numpy 2 onward
join(pathname, "core", "include"), # numpy prior to 2
]:
if exists(join(p, "numpy", "arrayobject.h")):
return p
raise RuntimeError("no valid path for numpy found")
class NumpyExtension(Extension): class NumpyExtension(Extension):
# nicked from # nicked from
# http://mail.python.org/pipermail/distutils-sig/2007-September/008253.html # http://mail.python.org/pipermail/distutils-sig/2007-September/008253.html
...@@ -40,15 +69,11 @@ class NumpyExtension(Extension): ...@@ -40,15 +69,11 @@ class NumpyExtension(Extension):
self._include_dirs = self.include_dirs self._include_dirs = self.include_dirs
del self.include_dirs # restore overwritten property del self.include_dirs # restore overwritten property
def get_numpy_incpath(self): def get_additional_include_dirs(self):
from imp import find_module return [get_numpy_incpath()]
# avoid actually importing numpy, it screws up distutils
file, pathname, descr = find_module("numpy")
from os.path import join
return join(pathname, "core", "include")
def get_include_dirs(self): def get_include_dirs(self):
return self._include_dirs + [self.get_numpy_incpath()] return self._include_dirs + self.get_additional_include_dirs()
def set_include_dirs(self, value): def set_include_dirs(self, value):
self._include_dirs = value self._include_dirs = value
...@@ -59,38 +84,44 @@ class NumpyExtension(Extension): ...@@ -59,38 +84,44 @@ class NumpyExtension(Extension):
include_dirs = property(get_include_dirs, set_include_dirs, del_include_dirs) include_dirs = property(get_include_dirs, set_include_dirs, del_include_dirs)
class PyUblasExtension(NumpyExtension): class ExtensionUsingNumpy(Extension):
def get_module_include_path(self, name): """Unlike :class:`NumpyExtension`, this class does not require numpy to be
from pkg_resources import Requirement, resource_filename importable upon extension module creation, allowing ``setup_requires=["numpy"]``
return resource_filename(Requirement.parse(name), "%s/include" % name) to work. On the other hand, it requires the use of::
setup(...,
cmdclass={'build_ext': NumpyBuildExtCommand})
or
setup(...,
cmdclass={'build_ext': PybindBuildExtCommand})
"""
@property
def include_dirs(self):
return self._include_dirs + [
self.get_numpy_incpath(),
self.get_module_include_path("pyublas"),
]
class NumpyBuildExtCommand(BaseBuildExtCommand):
def build_extension(self, extension):
# We add the numpy include dir right before building the
# extension, in order to avoid having to import numpy when
# the setup script is imported, which would prevent
# installation before manual installation of numpy.
if isinstance(extension, ExtensionUsingNumpy):
numpy_incpath = get_numpy_incpath()
if numpy_incpath not in extension.include_dirs:
extension.include_dirs.append(numpy_incpath)
class HedgeExtension(PyUblasExtension): BaseBuildExtCommand.build_extension(self, extension)
@property
def include_dirs(self):
return self._include_dirs + [
self.get_numpy_incpath(),
self.get_module_include_path("pyublas"),
self.get_module_include_path("hedge"),
]
# {{{ tools # {{{ tools
def flatten(list): def flatten(lst):
"""For an iterable of sub-iterables, generate each member of each """For an iterable of sub-iterables, generate each member of each
sub-iterable in turn, i.e. a flattened version of that super-iterable. sub-iterable in turn, i.e. a flattened version of that super-iterable.
Example: Turn [[a,b,c],[d,e,f]] into [a,b,c,d,e,f]. Example: Turn [[a,b,c],[d,e,f]] into [a,b,c,d,e,f].
""" """
for sublist in list: for sublist in lst:
for j in sublist: for j in sublist:
yield j yield j
...@@ -128,7 +159,9 @@ def get_config(schema=None, warn_about_no_config=True): ...@@ -128,7 +159,9 @@ def get_config(schema=None, warn_about_no_config=True):
count_down_delay(delay=10) count_down_delay(delay=10)
return expand_options(schema.read_config()) config = expand_options(schema.read_config())
schema.update_config_from_and_modify_command_line(config, sys.argv)
return config
def hack_distutils(debug=False, fast_link=True, what_opt=3): def hack_distutils(debug=False, fast_link=True, what_opt=3):
...@@ -143,15 +176,16 @@ def hack_distutils(debug=False, fast_link=True, what_opt=3): ...@@ -143,15 +176,16 @@ def hack_distutils(debug=False, fast_link=True, what_opt=3):
break break
return optlist return optlist
import sys
if not sys.platform.lower().startswith("win"): if not sys.platform.lower().startswith("win"):
from distutils import sysconfig from distutils import sysconfig
cvars = sysconfig.get_config_vars() cvars = sysconfig.get_config_vars()
cflags = cvars.get('OPT')
bad_prefixes = ["-g", "-O", "-Wstrict-prototypes", "-DNDEBUG"]
cflags = cvars.get("OPT")
if cflags: if cflags:
cflags = remove_prefixes(cflags.split(), cflags = remove_prefixes(cflags.split(), bad_prefixes)
['-g', '-O', '-Wstrict-prototypes', '-DNDEBUG'])
if debug: if debug:
cflags.append("-g") cflags.append("-g")
else: else:
...@@ -161,16 +195,25 @@ def hack_distutils(debug=False, fast_link=True, what_opt=3): ...@@ -161,16 +195,25 @@ def hack_distutils(debug=False, fast_link=True, what_opt=3):
cflags.append("-O%s" % what_opt) cflags.append("-O%s" % what_opt)
cflags.append("-DNDEBUG") cflags.append("-DNDEBUG")
cvars['OPT'] = str.join(' ', cflags) cvars["OPT"] = str.join(" ", cflags)
cvars["CFLAGS"] = cvars["BASECFLAGS"] + " " + cvars["OPT"]
cflags = cvars.get("CONFIGURE_CFLAGS")
if cflags:
cflags = remove_prefixes(cflags.split(), bad_prefixes)
cvars["CONFIGURE_CFLAGS"] = str.join(" ", cflags)
if "BASECFLAGS" in cvars:
cvars["CFLAGS"] = cvars["BASECFLAGS"] + " " + cvars.get("OPT", "")
else:
assert "CFLAGS" in cvars
if fast_link: if fast_link:
for varname in ["LDSHARED", "BLDSHARED"]: for varname in ["LDSHARED", "BLDSHARED"]:
ldsharedflags = cvars.get(varname) ldsharedflags = cvars.get(varname)
if ldsharedflags: if ldsharedflags:
ldsharedflags = remove_prefixes(ldsharedflags.split(), ldsharedflags = remove_prefixes(ldsharedflags.split(),
['-Wl,-O']) ["-Wl,-O"])
cvars[varname] = str.join(' ', ldsharedflags) cvars[varname] = str.join(" ", ldsharedflags)
# }}} # }}}
...@@ -208,7 +251,7 @@ def expand_value(v, options): ...@@ -208,7 +251,7 @@ def expand_value(v, options):
for i in v: for i in v:
try: try:
exp_i = expand_value(i, options) exp_i = expand_value(i, options)
except: except Exception:
pass pass
else: else:
result.append(exp_i) result.append(exp_i)
...@@ -224,7 +267,7 @@ def expand_options(options): ...@@ -224,7 +267,7 @@ def expand_options(options):
class ConfigSchema: class ConfigSchema:
def __init__(self, options, conf_file="siteconf.py", conf_dir="."): def __init__(self, options, conf_file="siteconf.py", conf_dir=os.path.dirname(__file__)):
self.optdict = dict((opt.name, opt) for opt in options) self.optdict = dict((opt.name, opt) for opt in options)
self.options = options self.options = options
self.conf_dir = conf_dir self.conf_dir = conf_dir
...@@ -233,7 +276,6 @@ class ConfigSchema: ...@@ -233,7 +276,6 @@ class ConfigSchema:
from os.path import expanduser from os.path import expanduser
self.user_conf_file = expanduser("~/.aksetup-defaults.py") self.user_conf_file = expanduser("~/.aksetup-defaults.py")
import sys
if not sys.platform.lower().startswith("win"): if not sys.platform.lower().startswith("win"):
self.global_conf_file = "/etc/aksetup-defaults.py" self.global_conf_file = "/etc/aksetup-defaults.py"
else: else:
...@@ -282,7 +324,7 @@ class ConfigSchema: ...@@ -282,7 +324,7 @@ class ConfigSchema:
if value is not None: if value is not None:
filevars[key] = value filevars[key] = value
keys = filevars.keys() keys = list(filevars.keys())
keys.sort() keys.sort()
outf = open(filename, "w") outf = open(filename, "w")
...@@ -328,23 +370,44 @@ class ConfigSchema: ...@@ -328,23 +370,44 @@ class ConfigSchema:
import os import os
return os.access(self.get_conf_file(), os.R_OK) return os.access(self.get_conf_file(), os.R_OK)
def read_config(self, warn_if_none=True): def update_from_python_snippet(self, config, py_snippet, filename):
filevars = {}
exec(compile(py_snippet, filename, "exec"), filevars)
for key, value in filevars.items():
if key in self.optdict:
config[key] = value
elif key == "__builtins__":
pass
else:
raise KeyError("invalid config key in %s: %s" % (
filename, key))
def update_config_from_and_modify_command_line(self, config, argv):
cfg_prefix = "--conf:"
i = 0
while i < len(argv):
arg = argv[i]
if arg.startswith(cfg_prefix):
del argv[i]
self.update_from_python_snippet(
config, arg[len(cfg_prefix):], "<command line>")
else:
i += 1
return config
def read_config(self):
import os import os
cfile = self.get_conf_file() cfile = self.get_conf_file()
result = self.get_default_config_with_files() result = self.get_default_config_with_files()
if os.access(cfile, os.R_OK): if os.access(cfile, os.R_OK):
filevars = {} with open(cfile, "r") as inf:
exec(compile(open(cfile, "r").read(), cfile, "exec"), filevars) py_snippet = inf.read()
self.update_from_python_snippet(result, py_snippet, cfile)
for key, value in filevars.items():
if key in self.optdict:
result[key] = value
elif key == "__builtins__":
pass
else:
raise KeyError("invalid config key in %s: %s" % (
cfile, key))
return result return result
...@@ -479,12 +542,15 @@ class Libraries(StringListOption): ...@@ -479,12 +542,15 @@ class Libraries(StringListOption):
help=help or ("Library names for %s (without lib or .so)" help=help or ("Library names for %s (without lib or .so)"
% (human_name or humanize(lib_name)))) % (human_name or humanize(lib_name))))
# }}}
# {{{ configure options for specific software
class BoostLibraries(Libraries): class BoostLibraries(Libraries):
def __init__(self, lib_base_name, default_lib_name=None): def __init__(self, lib_base_name, default_lib_name=None):
if default_lib_name is None: if default_lib_name is None:
if lib_base_name == "python": if lib_base_name == "python":
import sys
default_lib_name = "boost_python-py%d%d" % sys.version_info[:2] default_lib_name = "boost_python-py%d%d" % sys.version_info[:2]
else: else:
default_lib_name = "boost_%s" % lib_base_name default_lib_name = "boost_%s" % lib_base_name
...@@ -492,7 +558,7 @@ class BoostLibraries(Libraries): ...@@ -492,7 +558,7 @@ class BoostLibraries(Libraries):
Libraries.__init__(self, "BOOST_%s" % lib_base_name.upper(), Libraries.__init__(self, "BOOST_%s" % lib_base_name.upper(),
[default_lib_name], [default_lib_name],
help="Library names for Boost C++ %s library (without lib or .so)" help="Library names for Boost C++ %s library (without lib or .so)"
% humanize(lib_base_name)) % humanize(lib_base_name))
def set_up_shipped_boost_if_requested(project_name, conf, source_path=None, def set_up_shipped_boost_if_requested(project_name, conf, source_path=None,
...@@ -506,7 +572,6 @@ def set_up_shipped_boost_if_requested(project_name, conf, source_path=None, ...@@ -506,7 +572,6 @@ def set_up_shipped_boost_if_requested(project_name, conf, source_path=None,
(only relevant in shipped mode) (only relevant in shipped mode)
""" """
from os.path import exists from os.path import exists
import sys
if source_path is None: if source_path is None:
source_path = "bpl-subset/bpl_subset" source_path = "bpl-subset/bpl_subset"
...@@ -582,7 +647,7 @@ def set_up_shipped_boost_if_requested(project_name, conf, source_path=None, ...@@ -582,7 +647,7 @@ def set_up_shipped_boost_if_requested(project_name, conf, source_path=None,
"BOOST_MULTI_INDEX_DISABLE_SERIALIZATION": 1, "BOOST_MULTI_INDEX_DISABLE_SERIALIZATION": 1,
"BOOST_PYTHON_SOURCE": 1, "BOOST_PYTHON_SOURCE": 1,
"boost": '%sboost' % project_name, "boost": "%sboost" % project_name,
} }
if boost_chrono is False: if boost_chrono is False:
...@@ -605,6 +670,10 @@ def make_boost_base_options(): ...@@ -605,6 +670,10 @@ def make_boost_base_options():
help="The compiler with which Boost C++ was compiled, e.g. gcc43"), help="The compiler with which Boost C++ was compiled, e.g. gcc43"),
] ]
# }}}
# {{{ configure frontend
def configure_frontend(): def configure_frontend():
from optparse import OptionParser from optparse import OptionParser
...@@ -619,8 +688,6 @@ def configure_frontend(): ...@@ -619,8 +688,6 @@ def configure_frontend():
print("*** %s." % schema.get_conf_file()) print("*** %s." % schema.get_conf_file())
print("************************************************************") print("************************************************************")
import sys
description = "generate a configuration file for this software package" description = "generate a configuration file for this software package"
parser = OptionParser(description=description) parser = OptionParser(description=description)
parser.add_option( parser.add_option(
...@@ -658,6 +725,8 @@ def configure_frontend(): ...@@ -658,6 +725,8 @@ def configure_frontend():
substitute(substs, "Makefile") substitute(substs, "Makefile")
# }}}
def substitute(substitutions, fname): def substitute(substitutions, fname):
import re import re
...@@ -665,19 +734,24 @@ def substitute(substitutions, fname): ...@@ -665,19 +734,24 @@ def substitute(substitutions, fname):
string_var_re = re.compile(r"\$str\{([A-Za-z_0-9]+)\}") string_var_re = re.compile(r"\$str\{([A-Za-z_0-9]+)\}")
fname_in = fname+".in" fname_in = fname+".in"
lines = open(fname_in, "r").readlines() with open(fname_in, "r") as inf:
lines = inf.readlines()
new_lines = [] new_lines = []
for l in lines: for line in lines:
made_change = True made_change = True
while made_change: while made_change:
made_change = False made_change = False
match = var_re.search(l) match = var_re.search(line)
if match: if match:
varname = match.group(1) varname = match.group(1)
l = l[:match.start()] + str(substitutions[varname]) + l[match.end():] line = (
line[:match.start()]
+ str(substitutions[varname])
+ line[match.end():])
made_change = True made_change = True
match = string_var_re.search(l) match = string_var_re.search(line)
if match: if match:
varname = match.group(1) varname = match.group(1)
subst = substitutions[varname] subst = substitutions[varname]
...@@ -686,23 +760,26 @@ def substitute(substitutions, fname): ...@@ -686,23 +760,26 @@ def substitute(substitutions, fname):
else: else:
subst = '"%s"' % subst subst = '"%s"' % subst
l = l[:match.start()] + subst + l[match.end():] line = line[:match.start()] + subst + line[match.end():]
made_change = True made_change = True
new_lines.append(l) new_lines.append(line)
new_lines.insert(1, "# DO NOT EDIT THIS FILE -- " new_lines.insert(1, "# DO NOT EDIT THIS FILE -- "
"it was generated by configure.py\n") "it was generated by configure.py\n")
import sys
new_lines.insert(2, "# %s\n" % (" ".join(sys.argv))) new_lines.insert(2, "# %s\n" % (" ".join(sys.argv)))
open(fname, "w").write("".join(new_lines)) with open(fname, "w") as outf:
outf.write("".join(new_lines))
from os import stat, chmod from os import stat, chmod
infile_stat_res = stat(fname_in) infile_stat_res = stat(fname_in)
chmod(fname, infile_stat_res.st_mode) chmod(fname, infile_stat_res.st_mode)
# {{{ git bits
def _run_git_command(cmd): def _run_git_command(cmd):
git_error = None git_error = None
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
stdout = None
try: try:
popen = Popen(["git"] + cmd, stdout=PIPE) popen = Popen(["git"] + cmd, stdout=PIPE)
stdout, stderr = popen.communicate() stdout, stderr = popen.communicate()
...@@ -722,8 +799,11 @@ def _run_git_command(cmd): ...@@ -722,8 +799,11 @@ def _run_git_command(cmd):
print(git_error) print(git_error)
print("Hit Ctrl-C now if you'd like to think about the situation.") print("Hit Ctrl-C now if you'd like to think about the situation.")
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
count_down_delay(delay=5) count_down_delay(delay=0)
return stdout.decode("utf-8"), git_error if stdout:
return stdout.decode("utf-8"), git_error
else:
return "", "(subprocess call to git did not succeed)"
def check_git_submodules(): def check_git_submodules():
...@@ -742,12 +822,12 @@ def check_git_submodules(): ...@@ -742,12 +822,12 @@ def check_git_submodules():
pkg_warnings = [] pkg_warnings = []
lines = stdout.split("\n") lines = stdout.split("\n")
for l in lines: for ln in lines:
if not l.strip(): if not ln.strip():
continue continue
status = l[0] status = ln[0]
sha, package = l[1:].split(" ", 1) sha, package = ln[1:].split(" ", 1)
if package == "bpl-subset" or ( if package == "bpl-subset" or (
package.startswith("boost") and package.endswith("subset")): package.startswith("boost") and package.endswith("subset")):
...@@ -767,36 +847,165 @@ def check_git_submodules(): ...@@ -767,36 +847,165 @@ def check_git_submodules():
% package) % package)
if pkg_warnings: if pkg_warnings:
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
print("git submodules are not up-to-date or in odd state") print("git submodules are not up-to-date or in odd state")
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
print("If this makes no sense, you probably want to say") print("If this makes no sense, you probably want to say")
print("") print("")
print(" $ git submodule update --init") print(" $ git submodule update --init")
print("") print("")
print("to fetch what you are presently missing and " print("to fetch what you are presently missing and "
"move on with your life.") "move on with your life.")
print("If you got this from a distributed package on the " print("If you got this from a distributed package on the "
"net, that package is") "net, that package is")
print("broken and should be fixed. Please inform whoever " print("broken and should be fixed. Please inform whoever "
"gave you this package.") "gave you this package.")
print("") print("")
print("These issues were found:") print("These issues were found:")
for w in pkg_warnings: for w in pkg_warnings:
print(" %s" % w) print(" %s" % w)
print("") print("")
print("I will try to initialize the submodules for you " print("I will try to initialize the submodules for you "
"after a short wait.") "after a short wait.")
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
print("Hit Ctrl-C now if you'd like to think about the situation.") print("Hit Ctrl-C now if you'd like to think about the situation.")
print(DASH_SEPARATOR) print(DASH_SEPARATOR)
from os.path import exists
if not exists(".dirty-git-ok"):
count_down_delay(delay=10)
stdout, git_error = _run_git_command(
["submodule", "update", "--init"])
if git_error is None:
print(DASH_SEPARATOR)
print("git submodules initialized successfully")
print(DASH_SEPARATOR)
# }}}
# {{{ pybind11
def check_pybind11():
try:
import pybind11 # noqa
except ImportError:
print(DASH_SEPARATOR)
print("Pybind11 is not installed.")
print(DASH_SEPARATOR)
print("Very likely, the build process after this message will fail.")
print("")
print("Simply press Ctrl+C and type")
print("python -m pip install pybind11")
print("to fix this. If you don't, the build will continue ")
print("in a few seconds.")
print("")
print("[1] https://pybind11.readthedocs.io/en/stable/")
print(DASH_SEPARATOR)
from aksetup_helper import count_down_delay
count_down_delay(delay=10)
# {{{ (modified) boilerplate from https://github.com/pybind/python_example/blob/2ed5a68759cd6ff5d2e5992a91f08616ef457b5c/setup.py # noqa
class get_pybind_include(object): # noqa: N801
"""Helper class to determine the pybind11 include path
The purpose of this class is to postpone importing pybind11
until it is actually installed, so that the ``get_include()``
method can be invoked. """
def __init__(self, user=False):
self.user = user
def __str__(self):
import pybind11
return pybind11.get_include(self.user)
# As of Python 3.6, CCompiler has a `has_flag` method.
# cf http://bugs.python.org/issue26689
def has_flag(compiler, flagname):
"""Return a boolean indicating whether a flag name is supported on
the specified compiler.
"""
import tempfile
with tempfile.NamedTemporaryFile("w", suffix=".cpp", delete=False) as f:
f.write("int main (int argc, char **argv) { return 0; }")
fname = f.name
import setuptools
try:
compiler.compile([fname], extra_postargs=[flagname])
except setuptools.distutils.errors.CompileError:
return False
return True
def cpp_flag(compiler):
"""Return the -std=c++[11/14] compiler flag.
C++14 is preferred over C++11 (when it is available).
"""
if has_flag(compiler, "-std=gnu++14"):
return "-std=gnu++14"
elif has_flag(compiler, "-std=c++14"):
return "-std=c++14"
elif has_flag(compiler, "-std=c++11"):
return "-std=c++11"
else:
raise RuntimeError("Unsupported compiler -- at least C++11 support "
"is needed!")
class PybindBuildExtCommand(NumpyBuildExtCommand):
"""A custom build extension for adding compiler-specific options."""
c_opts = {
"msvc": ["/EHsc"],
"unix": [],
}
def build_extensions(self):
ct = self.compiler.compiler_type
opts = self.c_opts.get(ct, [])
cxx_opts = []
if ct in ["unix", "mingw32"]:
opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version())
cxx_opts.append(cpp_flag(self.compiler))
if has_flag(self.compiler, "-fvisibility=hidden"):
opts.append("-fvisibility=hidden")
if sys.platform == "darwin":
if has_flag(self.compiler, "-stdlib=libc++"):
opts.append("-stdlib=libc++")
if has_flag(self.compiler, "-mmacosx-version-min=10.7"):
opts.append("-mmacosx-version-min=10.7")
elif ct == "msvc":
opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version())
for ext in self.extensions:
ext.extra_compile_args = ext.extra_compile_args + opts
prev__compile = self.compiler._compile
# -std=... used on C files causes an error on Apple LLVM
# https://gitlab.tiker.net/inducer/pymetis/-/jobs/102421
def _compile(obj, src, ext, cc_args, extra_postargs, pp_opts):
if ext == ".cpp":
cc_args = cc_args + cxx_opts
return prev__compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
self.compiler._compile = _compile
try:
NumpyBuildExtCommand.build_extensions(self)
finally:
self.compiler._compile = prev__compile
# }}}
# }}}
from os.path import exists # vim: foldmethod=marker
if not exists(".dirty-git-ok"):
count_down_delay(delay=10)
stdout, git_error = _run_git_command(
["submodule", "update", "--init"])
if git_error is None:
print(DASH_SEPARATOR)
print("git submodules initialized successfully")
print(DASH_SEPARATOR)
#! /bin/bash
set -e
set -x
BUILD_DIR=$(mktemp -d -t islpy-barvinok-build-XXXXXXX)
echo "BUILDING IN $BUILD_DIR"
if test "$1" = ""; then
echo "usage: $0 PREFIX_DIR [GMP_PREFIX_DIR]"
fi
PREFIX="$1"
GMP_PREFIX="${2:-$PREFIX}"
NTL_VER="10.5.0"
BARVINOK_GIT_REV="barvinok-0.41.8"
NPROCS=6
function with_echo()
{
echo "$@"
"$@"
}
if true; then
rm -Rf "$BUILD_DIR"
mkdir "$BUILD_DIR"
cd "$BUILD_DIR"
rm -Rf islpy
if test "$GITHUB_HEAD_REF" != "" && test "$GITHUB_HEAD_REPOSITORY" != ""; then
with_echo git clone --recursive https://github.com/$GITHUB_HEAD_REPOSITORY.git -b "$GITHUB_HEAD_REF"
elif test "$CI_SERVER_NAME" = "GitLab" && test "$CI_COMMIT_REF_NAME" != ""; then
with_echo git clone --recursive https://gitlab.tiker.net/inducer/islpy.git -b "$CI_COMMIT_REF_NAME"
else
with_echo git clone --recursive https://github.com/inducer/islpy.git
fi
curl -L -O --insecure http://shoup.net/ntl/ntl-"$NTL_VER".tar.gz
tar xfz ntl-"$NTL_VER".tar.gz
cd "$BUILD_DIR/ntl-$NTL_VER/src"
./configure NTL_GMP_LIP=on DEF_PREFIX="$PREFIX" GMP_PREFIX="$GMP_PREFIX" TUNE=x86 SHARED=on
make -j$NPROCS
make install
cd "$BUILD_DIR"
rm -Rf barvinok
git clone https://github.com/inducer/barvinok.git
cd barvinok
git checkout $BARVINOK_GIT_REV
numtries=1
while ! ./get_submodules.sh; do
sleep 5
numtries=$((numtries+1))
if test "$numtries" == 5; then
echo "*** getting barvinok submodules failed even after a few tries"
exit 1
fi
done
sh autogen.sh
./configure \
--prefix="$PREFIX" \
--with-ntl-prefix="$PREFIX" \
--with-gmp-prefix="$GMP_PREFIX" \
--enable-shared-barvinok \
--with-pet=no
BARVINOK_ADDITIONAL_MAKE_ARGS=""
if [ "$(uname)" == "Darwin" ]; then
BARVINOK_ADDITIONAL_MAKE_ARGS=CFLAGS="-Wno-error=implicit-function-declaration"
fi
make $BARVINOK_ADDITIONAL_MAKE_ARGS -j$NPROCS
make install
fi
cd "$BUILD_DIR"
cd islpy
./configure.py \
--no-use-shipped-isl \
--no-use-shipped-imath \
--isl-inc-dir=$PREFIX/include \
--isl-lib-dir=$PREFIX/lib \
--use-barvinok
python -m pip install .
# vim: sw=2
#! /usr/bin/env python #! /usr/bin/env python3
from aksetup_helper import configure_frontend from aksetup_helper import configure_frontend
configure_frontend() configure_frontend()
pre {
line-height: 110%;
}
.footer {
background-color: #eee;
}
body > div.container {
margin-top:10px;
}
dd {
margin-left: 40px;
}
tt.descname {
font-size: 100%;
}
code {
color: rgb(51,51,51);
}
h1 {
padding-bottom:7px;
border-bottom: 1px solid #ccc;
}
h2 {
padding-bottom:5px;
border-bottom: 1px solid #ccc;
}
h3 {
padding-bottom:5px;
border-bottom: 1px solid #ccc;
}
.rubric {
font-size: 120%;
padding-bottom:1px;
border-bottom: 1px solid #ccc;
}
.headerlink {
padding-left: 1ex;
padding-right: 1ex;
}
a.headerlink:hover {
text-decoration: none;
}
blockquote p {
font-size: 100%;
font-weight: normal;
line-height: normal;
};
{% extends "!layout.html" %}
{% set bootswatch_css_custom = ['_static/akdoc.css']%}