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    A string to be mixed in that defines all the necessary runtime pyd boilerplate.
39  */
40 string wrapAll(in LibraryName libraryName,
41                in Modules modules,
42                in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
43                in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
44     @safe pure
45 {
46     if(!__ctfe) return null;
47 
48     string ret =
49         pydMainMixin(modules, preModuleInitCode, postModuleInitCode) ~
50         pydInitMixin(libraryName.value);
51 
52     version(Have_excel_d) {
53         ret ~=
54         // this is needed because of the excel-d dependency
55         q{
56             import xlld.wrap.worksheet: WorksheetFunction;
57             extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow { return []; }
58         };
59     } else version(Windows) {
60         ret ~= dllMainMixinStr;
61     }
62 
63     return ret;
64 }
65 
66 /**
67    A string to be mixed in that defines PydMain, automatically registering all
68    functions and structs in the passed in modules.
69  */
70 string pydMainMixin(in Modules modules,
71                     in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
72                     in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
73     @safe pure
74 {
75     import std.format: format;
76     import std.algorithm: map;
77     import std.array: join;
78 
79     if(!__ctfe) return null;
80 
81     const modulesList = modules.value.map!(a => a.toString).join(", ");
82 
83     return q{
84         extern(C) void PydMain() {
85             import std.typecons: Yes, No;
86             import pyd.pyd: module_init;
87             import autowrap.python.wrap: wrapAllFunctions, wrapAllAggregates;
88 
89             // this must go before module_init
90             wrapAllFunctions!(%s);
91 
92             %s
93 
94             module_init;
95 
96             // this must go after module_init
97             wrapAllAggregates!(%s);
98 
99             %s
100         }
101     }.format(modulesList, preModuleInitCode.value, modulesList, postModuleInitCode.value);
102 }
103 
104 /**
105    A string to be mixed in that defines the PyInit function for a library.
106  */
107 string pydInitMixin(in string libraryName) @safe pure {
108     import std.format: format;
109 
110     if(!__ctfe) return null;
111 
112     version(Python_3_0_Or_Later) {
113         return q{
114             import deimos.python.object: PyObject;
115             extern(C) export PyObject* PyInit_%s() {
116                 import pyd.def: pyd_module_name, pyd_modules;
117                 import pyd.exception: exception_catcher;
118                 import pyd.thread: ensureAttached;
119 
120                 return exception_catcher(delegate PyObject*() {
121                         ensureAttached();
122                         pyd_module_name = "%s";
123                         PydMain();
124                         return pyd_modules[""];
125                     });
126             }
127         }.format(libraryName, libraryName);
128     } else {
129         return q{
130             import pyd.exception: exception_catcher;
131             import pyd.thread: ensureAttached;
132             import pyd.def: pyd_module_name;
133             extern(C) export void init%s() {
134                 exception_catcher(delegate void() {
135                         ensureAttached();
136                         pyd_module_name = "%s";
137                         PydMain();
138                     });
139 
140             }
141         }.format(libraryName, libraryName);
142     }
143 }