Skip to content

Commit eecf269

Browse files
authored
Add support for Windows in the Jupyter kernel for Cling
* Use console scripts for cross-platform compatibility * Set the pipe to non-blocking with a cross-platform API * Load the default C library on Windows explicitly * Get pointers to standard devices in a Windows-specific way * Export all symbols * Peek at the pipes at every flush interval * Export symbols and use the standard exception handling model for MSVC * Align the exported symbols with the driver * Rename from cling_exports to jupyter_exports * Support std::cin
1 parent 62cec92 commit eecf269

File tree

4 files changed

+162
-17
lines changed

4 files changed

+162
-17
lines changed

interpreter/cling/tools/Jupyter/CMakeLists.txt

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ set(LLVM_NO_DEAD_STRIP 1)
1212
set(SOURCES
1313
Kernel.cpp
1414
)
15-
set_source_files_properties(Kernel.cpp COMPILE_FLAGS "-fexceptions -frtti")
15+
if(MSVC)
16+
set_source_files_properties(Kernel.cpp COMPILE_FLAGS "/EHsc /GR")
17+
else()
18+
set_source_files_properties(Kernel.cpp COMPILE_FLAGS "-fexceptions -frtti")
19+
endif()
1620
#Solve unresolved symbols bug in unix
1721
#See https://github.com/vgvassilev/cling/issues/114
1822
if(WIN32)
@@ -81,6 +85,71 @@ add_cling_library(libclingJupyter ${ENABLE_SHARED} ${ENABLE_STATIC}
8185
set_target_properties(libclingJupyter
8286
PROPERTIES ENABLE_EXPORTS 1)
8387

88+
if(MSVC)
89+
set_target_properties(libclingJupyter PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
90+
91+
# RTTI/C++ symbols
92+
set(jupyter_exports ${jupyter_exports} ??_7type_info@@6B@
93+
?__type_info_root_node@@3U__type_info_node@@A
94+
?nothrow@std@@3Unothrow_t@1@B
95+
__std_find_trivial_1
96+
__std_reverse_trivially_swappable_1
97+
)
98+
99+
# Compiler added symbols for static variables. NOT for VStudio < 2015
100+
set(jupyter_exports ${jupyter_exports} _Init_thread_abort _Init_thread_epoch
101+
_Init_thread_footer _Init_thread_header _tls_index
102+
)
103+
104+
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
105+
# new/delete variants needed when linking to static msvc runtime (esp. Debug)
106+
set(jupyter_exports ${jupyter_exports}
107+
??2@YAPEAX_K@Z
108+
??3@YAXPEAX@Z
109+
??_U@YAPEAX_K@Z
110+
??_V@YAXPEAX@Z
111+
??3@YAXPEAX_K@Z
112+
??2@YAPEAX_KAEBUnothrow_t@std@@@Z
113+
??_U@YAPEAX_KAEBUnothrow_t@std@@@Z
114+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@H@Z
115+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@M@Z
116+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@N@Z
117+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@PEBX@Z
118+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAAAEAV01@P6AAEAV01@AEAV01@@Z@Z
119+
??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@D@Z
120+
??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@PEBD@Z
121+
?_Facet_Register@std@@YAXPEAV_Facet_base@1@@Z
122+
)
123+
else()
124+
set(jupyter_exports ${jupyter_exports}
125+
??2@YAPAXI@Z
126+
??3@YAXPAX@Z
127+
??3@YAXPAXI@Z
128+
??_U@YAPAXI@Z
129+
??_V@YAXPAX@Z
130+
??_V@YAXPAXI@Z
131+
??2@YAPAXIABUnothrow_t@std@@@Z
132+
??_U@YAPAXIABUnothrow_t@std@@@Z
133+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
134+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z
135+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@N@Z
136+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PBX@Z
137+
??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
138+
??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@D@Z
139+
??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z
140+
?_Facet_Register@std@@YAXPAV_Facet_base@1@@Z
141+
)
142+
endif()
143+
144+
# List to '/EXPORT:sym0 /EXPORT:sym1 /EXPORT:sym2 ...'
145+
foreach(sym ${jupyter_exports})
146+
set(cling_link_str "${cling_link_str} /EXPORT:${sym}")
147+
endforeach(sym ${jupyter_exports})
148+
149+
set_property(TARGET libclingJupyter APPEND_STRING PROPERTY LINK_FLAGS ${cling_link_str})
150+
151+
endif(MSVC)
152+
84153
if(ENABLE_SHARED)
85154
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
86155
set(LIBCLINGJUPYTER_LINK_FLAGS " -Wl,-compatibility_version -Wl,1")

interpreter/cling/tools/Jupyter/kernel/clingkernel.py

Lines changed: 83 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@
1818
__version__ = '0.0.3'
1919

2020
import ctypes
21-
from contextlib import contextmanager
22-
from fcntl import fcntl, F_GETFL, F_SETFL
2321
import os
2422
import shutil
2523
import select
2624
import struct
2725
import sys
2826
import threading
27+
import time
2928

3029
from traitlets import Unicode, Float, Dict, List, CaselessStrEnum
3130
from ipykernel.kernelbase import Kernel
@@ -39,14 +38,44 @@
3938
class my_void_p(ctypes.c_void_p):
4039
pass
4140

42-
libc = ctypes.CDLL(None)
43-
try:
44-
c_stdout_p = ctypes.c_void_p.in_dll(libc, 'stdout')
45-
c_stderr_p = ctypes.c_void_p.in_dll(libc, 'stderr')
46-
except ValueError:
47-
# libc.stdout is has a funny name on OS X
48-
c_stdout_p = ctypes.c_void_p.in_dll(libc, '__stdoutp')
49-
c_stderr_p = ctypes.c_void_p.in_dll(libc, '__stderrp')
41+
if sys.platform == 'win32':
42+
import msvcrt
43+
libc = ctypes.cdll.msvcrt
44+
45+
class FILE(ctypes.Structure):
46+
pass
47+
48+
FILE_p = ctypes.POINTER(FILE)
49+
50+
libc._fdopen.argtypes = [ctypes.c_int, ctypes.c_char_p]
51+
libc._fdopen.restype = FILE_p
52+
53+
c_stdout_p = libc._fdopen(sys.stdout.fileno(), b"w")
54+
c_stderr_p = libc._fdopen(sys.stderr.fileno(), b"w")
55+
56+
peek_named_pipe = ctypes.windll.kernel32.PeekNamedPipe
57+
peek_named_pipe.argtypes = [
58+
ctypes.wintypes.HANDLE,
59+
ctypes.c_void_p,
60+
ctypes.wintypes.DWORD,
61+
ctypes.POINTER(ctypes.wintypes.DWORD),
62+
ctypes.POINTER(ctypes.wintypes.DWORD),
63+
ctypes.POINTER(ctypes.wintypes.DWORD),
64+
]
65+
peek_named_pipe.restype = ctypes.c_bool
66+
67+
libc.fflush.argtypes = [FILE_p]
68+
libc.fflush.restype = ctypes.c_int
69+
else:
70+
libc = ctypes.CDLL(None)
71+
72+
try:
73+
c_stdout_p = ctypes.c_void_p.in_dll(libc, 'stdout')
74+
c_stderr_p = ctypes.c_void_p.in_dll(libc, 'stderr')
75+
except ValueError:
76+
# libc.stdout is has a funny name on OS X
77+
c_stdout_p = ctypes.c_void_p.in_dll(libc, '__stdoutp')
78+
c_stderr_p = ctypes.c_void_p.in_dll(libc, '__stderrp')
5079

5180

5281
class FdReplacer:
@@ -59,8 +88,9 @@ def __init__(self, name):
5988
os.dup2(pipe_in, self.real_fd)
6089
os.close(pipe_in)
6190
# make pipe_out non-blocking
62-
flags = fcntl(self.pipe_out, F_GETFL)
63-
fcntl(self.pipe_out, F_SETFL, flags|os.O_NONBLOCK)
91+
# flags = fcntl(self.pipe_out, F_GETFL)
92+
# fcntl(self.pipe_out, F_SETFL, flags|os.O_NONBLOCK)
93+
os.set_blocking(self.pipe_out, False)
6494

6595
def restore(self):
6696
os.close(self.real_fd)
@@ -120,7 +150,7 @@ def __init__(self, **kwargs):
120150
else:
121151
raise RuntimeError('cling at ' + clingInPath + ' is unusable. No cling, no fun.')
122152

123-
for libFolder in ["/lib/libclingJupyter.", "/libexec/lib/libclingJupyter."]:
153+
for libFolder in ["/bin/libclingJupyter.", "/lib/libclingJupyter.", "/libexec/lib/libclingJupyter."]:
124154

125155
for ext in ['so', 'dylib', 'dll']:
126156
libFilename = clingInstDir + libFolder + ext
@@ -225,6 +255,44 @@ def forward_streams(self):
225255

226256
def handle_input(self):
227257
"""Capture stdout, stderr and sideband. Forward them as stream messages."""
258+
if sys.platform == 'win32':
259+
pipes = {"sideband": self.sideband_pipe}
260+
for rs in self.replaced_streams:
261+
if rs:
262+
pipes[rs.name] = rs.pipe_out
263+
264+
# wait for the flush interval before peeking at the pipe
265+
time.sleep(self.flush_interval)
266+
267+
pipe_bytes = {}
268+
total_bytes = 0
269+
for name, pipe in pipes.items():
270+
bytes_available = ctypes.wintypes.DWORD(0)
271+
peek_named_pipe(
272+
ctypes.wintypes.HANDLE(msvcrt.get_osfhandle(pipe)),
273+
None,
274+
0,
275+
None,
276+
ctypes.byref(bytes_available),
277+
None,
278+
)
279+
pipe_bytes[name] = bytes_available.value
280+
total_bytes += bytes_available.value
281+
282+
if total_bytes == 0:
283+
libc.fflush(c_stdout_p)
284+
libc.fflush(c_stderr_p)
285+
return False
286+
287+
for name, n_bytes in pipe_bytes.items():
288+
if n_bytes == 0:
289+
continue
290+
291+
if name == "sideband":
292+
self._process_sideband_data()
293+
else:
294+
self._process_stdio_data(pipes[name], name)
295+
return True
228296
# create pipe for stdout, stderr
229297
select_on = [self.sideband_pipe]
230298
for rs in self.replaced_streams:
@@ -287,7 +355,8 @@ def do_execute(self, code, silent, store_history=True,
287355
run_cell_thread.join()
288356

289357
# Any leftovers?
290-
while self.handle_input(): True
358+
while self.handle_input():
359+
pass
291360

292361
self.close_forwards()
293362
status = 'ok'

interpreter/cling/tools/Jupyter/kernel/setup.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#-----------------------------------------------------------------------------
3232

3333
import os
34-
from glob import glob
3534

3635
from distutils.core import setup
3736

@@ -44,7 +43,11 @@
4443
name = name,
4544
version = '0.0.2',
4645
py_modules = ['clingkernel'],
47-
scripts = glob(pjoin('scripts', '*')),
46+
entry_points = {
47+
'console_scripts': [
48+
'jupyter-cling-kernel=clingkernel:main'
49+
],
50+
},
4851
description = "C++ Kernel for Jupyter with Cling",
4952
author = 'Min RK, Axel Naumann',
5053
author_email = 'cling-dev@cern.ch',

interpreter/cling/tools/libcling/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ add_cling_library(libcling ${ENABLE_SHARED} ${ENABLE_STATIC}
9494
set_target_properties(libcling
9595
PROPERTIES ENABLE_EXPORTS 1)
9696

97+
if(MSVC)
98+
set_target_properties(libcling PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
99+
endif()
100+
97101
if(ENABLE_SHARED)
98102
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
99103
set(LIBCLING_LINK_FLAGS " -Wl,-compatibility_version -Wl,1")

0 commit comments

Comments
 (0)