Skip to content

Commit 9500194

Browse files
committed
C extensions updated to use the new-style API
1 parent 29e1279 commit 9500194

11 files changed

Lines changed: 1768 additions & 1997 deletions

ci/test_wheel.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ do
1010
echo Installing for CPython $pydir
1111
export PATH=/opt/python/${pydir}/bin/:$ORIGINAL_PATH
1212
python -m pip install artifacts/dss_python_backend-*.whl
13-
python -c 'from dss_python_backend import lib, ffi; lib.ctx_DSS_NewCircuit(ffi.NULL, b"test123"); assert ffi.string(lib.ctx_Circuit_Get_Name(ffi.NULL)) == b"test123"'
13+
python -c 'from dss_python_backend import lib, ffi; lib.DSS_NewCircuit(ffi.NULL, b"test123"); assert ffi.string(lib.Circuit_Get_Name(ffi.NULL)) == b"test123"'
1414
done

dss_build.py

Lines changed: 74 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
1+
import numpy
12
from cffi import FFI
23
import sys, re, os
34
from dss_setup_common import PLATFORM_FOLDER, BUILD_ODDIE
5+
from setuptools import Extension
46

5-
def process_header(src, extern_py=False, implement_py=False, prefix='', flags=None, fn=''):
7+
def process_header(src, extern_py=False, implement_py=False, prefix='', flags=None, fn='', remove=[]):
68
'''Prepare the DSS C-API headers for parsing and building with CFFI'''
79

10+
src = src.replace('#endif // #ifdef HAS_ALTDSS_LOADER_FUNC', '')
11+
src = src.replace('#ifdef HAS_ALTDSS_LOADER_FUNC', '')
12+
813
if flags is not None:
914
definitions = [x[2:] for x in flags] # remove -D
1015
else:
1116
definitions = None
1217

1318
call_convention = '__stdcall ' if (sys.platform == 'win32') else ''
14-
19+
20+
for exp in remove:
21+
src = re.sub(exp, '', src, flags=re.MULTILINE)
22+
1523
if definitions is not None:
1624
# Since we cannot easily use a C preprocessor here, some custom replacements
1725
if 'ALTDSS_USERMODEL' in definitions:
@@ -107,7 +115,7 @@ def process_header(src, extern_py=False, implement_py=False, prefix='', flags=No
107115
))
108116

109117
src = '\n'.join(out_lines)
110-
118+
111119
return src
112120

113121
extra = {}
@@ -122,70 +130,62 @@ def process_header(src, extern_py=False, implement_py=False, prefix='', flags=No
122130
src_path = os.environ.get('SRC_DIR', '')
123131
DSS_CAPI_PATH = os.environ.get('DSS_CAPI_PATH', os.path.join(src_path, '..', 'dss_capi'))
124132

125-
VERSIONS = ['altdss_capi', 'altdss_capid']
126-
if BUILD_ODDIE:
127-
VERSIONS.append('altdss_oddie_capi')
133+
ffi_builder_dss = FFI()
134+
ffi_builders['dss_loader'] = ffi_builder_dss
128135

129-
for version in VERSIONS:
130-
ffi_builder_dss = FFI()
131-
debug = 'd' if version.endswith('d') else ''
136+
# main_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'dss_ctx.h')
137+
main_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'capi.h')
138+
common_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'common.h')
139+
enums_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'enums.h')
140+
# dss_capi_ctx_path = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'dss_ctx.h')
141+
extra_headers = [enums_header_fn, ]
132142

133-
if 'oddie' not in version:
134-
main_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'dss_ctx.h')
135-
common_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'common.h')
136-
enums_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'enums.h')
137-
# dss_capi_ctx_path = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'dss_ctx.h')
138-
extra_headers = [enums_header_fn, ]
139-
else:
140-
main_header_fn = os.path.join(DSS_CAPI_PATH, 'include', 'altdss', 'capi', 'oddie.h')
141-
dss_capi_ctx_path = None
142-
common_header_fn = None
143-
extra_headers = []
144-
145-
if common_header_fn:
146-
with open(common_header_fn, 'r') as f:
147-
cffi_header_dss = process_header(f.read(), fn=common_header_fn)
148-
else:
149-
cffi_header_dss = ''
143+
if common_header_fn:
144+
with open(common_header_fn, 'r') as f:
145+
# Remove the functions from the common header, only use the other defs
146+
cffi_header_dss = process_header(f.read(), fn=common_header_fn, remove=[r'^\s*ALTDSS_CAPI_DLL.*$', r'^\s*HMODULE.*$'])
147+
else:
148+
cffi_header_dss = ''
150149

151-
with open(main_header_fn, 'r') as f:
152-
cffi_header_dss += process_header(f.read(), fn=main_header_fn)
150+
with open(main_header_fn, 'r') as f:
151+
src = f.read().replace('ALTDSS_CAPI_DLL', '')
152+
cffi_header_dss += process_header(src, fn=main_header_fn, remove=[r'^\s*HMODULE.*$'])
153153

154154

155-
if 'oddie' not in version:
156-
for extra_fn in extra_headers:
157-
if os.path.exists(extra_fn):
158-
with open(extra_fn, 'r') as f:
159-
cffi_header_dss += process_header(f.read())
155+
for extra_fn in extra_headers:
156+
if os.path.exists(extra_fn):
157+
with open(extra_fn, 'r') as f:
158+
cffi_header_dss += process_header(f.read())
160159

161-
with open('cffi/dss_capi_custom.h', 'r') as f:
162-
extra_header_dss = f.read()
163-
164-
cffi_header_dss += extra_header_dss
165-
166-
with open('cffi/dss_capi_custom.c', 'r') as f:
167-
extra_source_dss = '#include <altdss/capi/dss_ctx.h>\n'
168-
extra_source_dss += f.read()
169-
else:
170-
extra_source_dss = '#include <altdss/capi/oddie.h>\n'
171-
172-
ffi_builder_dss.cdef(cffi_header_dss)
173-
174-
ffi_builder_dss.set_source(f"_{version}", extra_source_dss,
175-
libraries=[version],
176-
library_dirs=[
177-
os.path.join(DSS_CAPI_PATH, 'lib/{}'.format(PLATFORM_FOLDER))
178-
],
179-
include_dirs=[
180-
os.path.join(DSS_CAPI_PATH, 'include'),
181-
# os.path.join(DSS_CAPI_PATH, 'include/altdss'),
182-
],
183-
source_extension='.c',
184-
**extra
185-
)
160+
with open('cffi/dss_capi_custom.h', 'r') as f:
161+
extra_header_dss = f.read()
186162

187-
ffi_builders[version] = ffi_builder_dss
163+
cffi_header_dss += extra_header_dss
164+
extra_source_dss = ''
165+
with open('cffi/dss_capi_custom.c', 'r') as f:
166+
extra_source_dss = '#include <altdss/capi/enums.h>\n'
167+
extra_source_dss += '#include <altdss/capi/common.h>\n'
168+
extra_source_dss += '#include <altdss/capi/capi.h>\n'
169+
extra_source_dss += f.read()
170+
171+
ffi_builder_dss.cdef(cffi_header_dss)
172+
173+
lib_dir = os.path.join(DSS_CAPI_PATH, 'lib/{}'.format(PLATFORM_FOLDER))
174+
inc_dir = os.path.join(DSS_CAPI_PATH, 'include')
188175

176+
ffi_builder_dss.set_source(f"_altdss_capi_loader", extra_source_dss,
177+
libraries=['altdss_capi_loader'],
178+
define_macros = [('HAS_ALTDSS_LOADER_FUNC', 1)],
179+
library_dirs=[
180+
lib_dir
181+
],
182+
include_dirs=[
183+
inc_dir,
184+
# os.path.join(DSS_CAPI_PATH, 'include/altdss'),
185+
],
186+
source_extension='.c',
187+
**extra
188+
)
189189

190190
# User-model modules/DLLs
191191
# Currently we build a separate DLL for each kind of model,
@@ -225,7 +225,7 @@ def process_header(src, extern_py=False, implement_py=False, prefix='', flags=No
225225
ffi_builder.set_source(f"_dss_{user_model}_{variant}", user_model_src,
226226
libraries=[],
227227
library_dirs=[],
228-
include_dirs=[os.path.join(DSS_CAPI_PATH, 'include')],
228+
include_dirs=[inc_dir],
229229
source_extension='.c',
230230
extra_compile_args=variant_flags,
231231
#extra_link_args=['/DYNAMICBASE:NO', '/NXCOMPAT:NO']
@@ -234,11 +234,6 @@ def process_header(src, extern_py=False, implement_py=False, prefix='', flags=No
234234

235235
# Is there a better way to do this? Unfortunately setup(cffi_modules=...)
236236
# needs a list of strings and cannot handle objects directly
237-
ffi_builder_ = ffi_builders['altdss_capi']
238-
ffi_builder_d = ffi_builders['altdss_capid']
239-
if BUILD_ODDIE:
240-
ffi_builder_odd = ffi_builders['altdss_oddie_capi']
241-
242237
ffi_builder_GenUserModel_altdss = ffi_builders['GenUserModel-AltDSS']
243238
ffi_builder_GenUserModel_v7 = ffi_builders['GenUserModel-OpenDSS_v7']
244239
ffi_builder_GenUserModel_v8v9 = ffi_builders['GenUserModel-OpenDSS_v8v9']
@@ -247,7 +242,20 @@ def process_header(src, extern_py=False, implement_py=False, prefix='', flags=No
247242
#ffi_builder_StoreDynaModel = ffi_builders['StoreDynaModel']
248243
#ffi_builder_StoreUserModel = ffi_builders['StoreUserModel']
249244
#ffi_builder_CapUserControl = ffi_builders['CapUserControl']
250-
245+
246+
247+
_fast_dss_options = dict(
248+
py_limited_api=True,
249+
include_dirs=[numpy.get_include(), inc_dir],
250+
libraries=['altdss_capi_loader'],
251+
library_dirs=[lib_dir],
252+
extra_link_args=["-Wl,-R,$ORIGIN/."],
253+
# extra_compile_args=['-g', '-O0'],
254+
# extra_link_args=['-g'],
255+
)
256+
257+
fastdss_extension = Extension('_fastdss', ["src/_fastdss.c"], **_fast_dss_options)
258+
251259
if __name__ == "__main__":
252260
for version, builder in ffi_builders.items():
253261
print('-' * 40)

dss_python_backend/__init__.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,46 @@
77
'''
88

99
import os
10+
from . import _altdss_capi_loader
11+
from ._altdss_capi_loader import ffi, lib as loader_lib
12+
from pathlib import Path
13+
14+
altdss_lib_parent_path = Path(_altdss_capi_loader.__file__).absolute().parent
1015

1116
if os.environ.get('DSS_EXTENSIONS_DEBUG', '') != '1':
12-
from ._altdss_capi import ffi, lib
17+
altdss_lib_path = altdss_lib_parent_path.joinpath('libaltdss_capi.so')
18+
pass
1319
else:
1420
import warnings
1521
warnings.warn('Environment variable DSS_EXTENSIONS_DEBUG=1 is set: loading the debug version of the DSS C-API library')
16-
from ._altdss_capid import ffi, lib
22+
altdss_lib_path = altdss_lib_parent_path.joinpath('libaltdss_capid.so')
23+
pass
24+
25+
26+
if not altdss_lib_path.exists():
27+
raise RuntimeError('AltDSS library not found!')
28+
29+
30+
# Basic initialization -- load the library and prepare the structures
31+
lib = _altdss_capi_loader.ffi.new('AltDSSCAPI*')
32+
33+
_init_result = loader_lib.AltDSSCAPILibInit(
34+
str(altdss_lib_path).encode(),
35+
ffi.NULL,
36+
b"AltDSSCAPIInit",
37+
lib,
38+
ffi.sizeof(lib[0]),
39+
0,
40+
0,
41+
ffi.NULL
42+
)
43+
44+
if _init_result != 1:
45+
raise RuntimeError(f'AltDSS library found but could not be loaded (code {_init_result})!')
1746

1847
# Ensure this is called at least once. This was moved from
1948
# CffiApiUtil so we call it as soon as the DLL/so is loaded.
20-
lib.ctx_DSS_Start(ffi.NULL, 0)
49+
lib.DSS_Start(ffi.NULL, 0)
2150

2251
__version__ = '0.14.6a1'
23-
__all__ = ['ffi', 'lib']
52+
__all__ = ['ffi', 'lib', 'loader_lib']

0 commit comments

Comments
 (0)