Skip to content

Commit

Permalink
Setup build configuration for readthedocs (#3)
Browse files Browse the repository at this point in the history
* Build PortAudio lib if in readthedocs environment

* Ensure include paths are passed to the Extension

* Only copy the headers, no build

* Remove library links in readthedocs builds

* Avoid error if include dir exists

* copytree needs empty destination

* Override the distutils include and linker options in RTD builds

* Re-enable portaudio build and pass library_path to distutils

* Copy all headers to include path

* Exclude tools dir from package search

* Add runtime_library_dirs paths to Extension

* Set master_doc conf for sphinx<2.0
  • Loading branch information
nocarryr committed Jun 19, 2019
1 parent 333c247 commit 07e1da7
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 1 deletion.
2 changes: 2 additions & 0 deletions doc/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def isfunction(obj):

# autodoc_default_options = {'member-order':'groupwise'}

master_doc = 'index'

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

Expand Down
4 changes: 3 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ include_package_data = True
packages = find:

[options.packages.find]
exclude = tests
exclude =
tests
tools

[options.package_data]
* = LICENSE, README.md, requirements.txt
Expand Down
19 changes: 19 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys
import os
from setuptools import setup, find_packages
from Cython.Build import cythonize
from Cython.Build.Dependencies import default_create_extension
Expand All @@ -13,13 +14,31 @@
else:
INCLUDE_PATH = [numpy.get_include()]

LIB_PATH = []

def build_pa_lib():
from tools import build_portaudio
lib_base = build_portaudio.main()
INCLUDE_PATH.append(str(lib_base / 'include'))
LIB_PATH.append(str(lib_base / 'lib'))

RTFD_BUILD = 'READTHEDOCS' in os.environ.keys()
if RTFD_BUILD:
build_pa_lib()
print('INCLUDE_PATH: ', INCLUDE_PATH)

USE_CYTHON_TRACE = False
if '--use-cython-trace' in sys.argv:
USE_CYTHON_TRACE = True
sys.argv.remove('--use-cython-trace')

def my_create_extension(template, kwds):
name = kwds['name']
if RTFD_BUILD:
kwds['library_dirs'] = LIB_PATH
kwds['include_dirs'] = INCLUDE_PATH
kwds['runtime_library_dirs'] = LIB_PATH
print(kwds)
if USE_CYTHON_TRACE:
# avoid using CYTHON_TRACE macro for stream_callback module
if 'stream_callback' not in name:
Expand Down
Empty file added tools/__init__.py
Empty file.
110 changes: 110 additions & 0 deletions tools/build_portaudio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#! /usr/bin/env python3

import os
import sys
from pathlib import Path
import tempfile
import subprocess
import shlex
import shutil

EXEC_PREFIX = Path.home() / '.local'
EXEC_PREFIX.mkdir(exist_ok=True)
EXEC_PREFIX = EXEC_PREFIX.resolve()

def run_proc(cmdstr, show_output=False, show_stderr=True):
if show_stderr:
stderr = None
else:
stderr = subprocess.STDOUT
p = subprocess.run(
shlex.split(cmdstr),
check=True,
stdout=subprocess.PIPE,
stderr=stderr,
# universal_newlines=True,
)
if show_output:
if isinstance(p.stdout, bytes):
print(p.stdout.decode('UTF-8'))
else:
print(p.stdout)
return p

class Chdir(object):
def __init__(self, new_path):
self.prev_path = None
if not isinstance(new_path, Path):
new_path = Path(new_path)
self.new_path = new_path
def __enter__(self):
self.prev_path = Path.cwd()
os.chdir(self.new_path)
return self
def __exit__(self, *args):
os.chdir(self.prev_path)
def __repr__(self):
return f'Chdir: {self.prev_path} -> {self.new_path}'
def __str__(self):
return str(self.new_path)

class PaSource(object):
TARBALL_URL = 'http://github.com/nocarryr/portaudio/archive/master.tar.gz'
def __init__(self):
self.tempdir = None
self.base_path = None
self.src_path = None
def open(self):
self.tempdir = tempfile.mkdtemp()
self.base_path = Path(self.tempdir)
self.get_tarball()
def close(self):
if self.tempdir is not None:
if os.getcwd() == self.tempdir:
os.chdir(Path.home())
shutil.rmtree(self.tempdir)
self.tempdir = None
def get_tarball(self):
with Chdir(self.base_path):
run_proc(f'wget {self.TARBALL_URL}')
tarball_fn = self.base_path / 'master.tar.gz'
run_proc(f'tar -xvzf {tarball_fn}')
self.src_path = self.base_path / 'portaudio-master'
assert self.src_path.exists()
def __enter__(self):
self.open()
return self
def __exit__(self, *args):
self.close()

def copytree(src_dir, dst_dir):
dst_dir.mkdir(parents=True, exist_ok=True)
for src_p in src_dir.iterdir():
if not src_p.is_file():
continue
dst_p = dst_dir / src_p.name
if dst_p.exists():
print(f'{dst_p} exists')
continue
print(f'{src_p} -> {dst_p}')
with src_p.open('rb') as fd:
dst_p.write_bytes(fd.read())

def main():
src = PaSource()
with src:
with Chdir(src.src_path):
p = run_proc(f'./configure --prefix={EXEC_PREFIX}', show_output=True)
# print(p.stdout.decode('UTF-8'))
p = run_proc('make')#, show_output=True)
# print(p.stdout)
p = run_proc('make install', show_output=True)
# print(p.stdout)
src_incl = src.src_path / 'include'
dst_incl = EXEC_PREFIX / 'include'
run_proc(f'ls -al {dst_incl}', show_output=True)
copytree(src_incl, dst_incl)
return EXEC_PREFIX

if __name__ == '__main__':
main()

0 comments on commit 07e1da7

Please sign in to comment.