1 /**
2    Necessary boilerplate for pyd.
3 
4    To wrap all functions/return/parameter types and  struct/class definitions from
5    a list of modules, write this in a "main" module and generate mylib.{so,dll}:
6 
7    ------
8    mixin wrapAll(LibraryName("mylib"), Modules("module1", "module2", ...));
9    ------
10  */
11 module autowrap.python.boilerplate;
12 
13 import autowrap.reflection : Modules;
14 
15 /**
16    The name of the dynamic library, i.e. the file name with the .so/.dll extension
17  */
18 struct LibraryName {
19     string value;
20 }
21 
22 /**
23    Code to be inserted before the call to module_init
24  */
25 struct PreModuleInitCode {
26     string value;
27 }
28 
29 /**
30    Code to be inserted after the call to module_init
31  */
32 struct PostModuleInitCode {
33     string value;
34 }
35 
36 
37 /**
38    Returns a string to mixin that implements the necessary boilerplate
39    to create a Python library containing one Python module
40    wrapping all relevant D code and data structures.
41  */
42 string wrapDlang(
43     LibraryName libraryName,
44     Modules modules,
45     PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
46     PostModuleInitCode postModuleInitCode = PostModuleInitCode())
47     ()
48 {
49     return !__ctfe
50         ? null
51         : wrapAll(libraryName, modules, preModuleInitCode, postModuleInitCode);
52 }
53 
54 
55 /**
56    A string to be mixed in that defines all the necessary runtime pyd boilerplate.
57  */
58 string wrapAll(in LibraryName libraryName,
59                in Modules modules,
60                in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
61                in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
62     @safe pure
63 {
64     if(!__ctfe) return null;
65 
66     string ret =
67         pydMainMixin(modules, preModuleInitCode, postModuleInitCode) ~
68         pydInitMixin(libraryName.value);
69 
70     version(Have_excel_d) {
71         ret ~=
72         // this is needed because of the excel-d dependency
73         q{
74             import xlld.wrap.worksheet: WorksheetFunction;
75             extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow { return []; }
76         };
77     } else version(Windows) {
78         import autowrap.common : dllMainMixinStr;
79         ret ~= dllMainMixinStr;
80     }
81 
82     return ret;
83 }
84 
85 /**
86    A string to be mixed in that defines PydMain, automatically registering all
87    functions and structs in the passed in modules.
88  */
89 string pydMainMixin(in Modules modules,
90                     in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
91                     in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
92     @safe pure
93 {
94     import std.format: format;
95     import std.algorithm: map;
96     import std.array: join;
97 
98     if(!__ctfe) return null;
99 
100     const modulesList = modules.value.map!(a => a.toString).join(", ");
101 
102     return q{
103         extern(C) void PydMain() {
104             import std.typecons: Yes, No;
105             import pyd.pyd: module_init;
106             import autowrap.python.wrap: wrapAllFunctions, wrapAllAggregates;
107 
108             // this must go before module_init
109             wrapAllFunctions!(%s);
110 
111             %s
112 
113             module_init;
114 
115             // this must go after module_init
116             wrapAllAggregates!(%s);
117 
118             %s
119         }
120     }.format(modulesList, preModuleInitCode.value, modulesList, postModuleInitCode.value);
121 }
122 
123 /**
124    A string to be mixed in that defines the PyInit function for a library.
125  */
126 string pydInitMixin(in string libraryName) @safe pure {
127     import std.format: format;
128 
129     if(!__ctfe) return null;
130 
131     version(Python_3_0_Or_Later) {
132         return q{
133             import deimos.python.object: PyObject;
134             extern(C) export PyObject* PyInit_%s() {
135                 import pyd.def: pyd_module_name, pyd_modules;
136                 import pyd.exception: exception_catcher;
137                 import pyd.thread: ensureAttached;
138                 import core.runtime: rt_init;
139 
140                 rt_init;
141 
142                 return exception_catcher(delegate PyObject*() {
143                         ensureAttached();
144                         pyd_module_name = "%s";
145                         PydMain();
146                         return pyd_modules[""];
147                     });
148             }
149         }.format(libraryName, libraryName);
150     } else {
151         return q{
152             extern(C) export void init%s() {
153                 import pyd.exception: exception_catcher;
154                 import pyd.thread: ensureAttached;
155                 import pyd.def: pyd_module_name;
156                 import core.runtime: rt_init;
157 
158                 rt_init;
159 
160                 exception_catcher(delegate void() {
161                         ensureAttached();
162                         pyd_module_name = "%s";
163                         PydMain();
164                     });
165 
166             }
167         }.format(libraryName, libraryName);
168     }
169 }