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 
14 public import autowrap.types: LibraryName, PreModuleInitCode, PostModuleInitCode;
15 import autowrap.reflection : Modules;
16 
17 
18 /**
19    Returns a string to mixin that implements the necessary boilerplate
20    to create a Python library containing one Python module
21    wrapping all relevant D code and data structures.
22  */
23 string wrapDlang(
24     LibraryName libraryName,
25     Modules modules,
26     PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
27     PostModuleInitCode postModuleInitCode = PostModuleInitCode())
28     ()
29 {
30     return __ctfe
31         ? wrapAll(libraryName, modules, preModuleInitCode, postModuleInitCode)
32         : null;
33 }
34 
35 
36 /**
37    A string to be mixed in that defines all the necessary runtime pyd boilerplate.
38  */
39 string wrapAll(in LibraryName libraryName,
40                in Modules modules,
41                in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
42                in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
43     @safe pure
44 {
45     if(!__ctfe) return null;
46 
47     string ret =
48         pydMainMixin(modules, preModuleInitCode, postModuleInitCode) ~
49         pydInitMixin(libraryName.value);
50 
51     version(Have_excel_d) {
52         ret ~=
53         // this is needed because of the excel-d dependency
54         q{
55             import xlld.wrap.worksheet: WorksheetFunction;
56             extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow { return []; }
57         };
58     } else version(Windows) {
59         import autowrap.common : dllMainMixinStr;
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                 import core.runtime: rt_init;
120 
121                 rt_init;
122 
123                 return exception_catcher(delegate PyObject*() {
124                         ensureAttached();
125                         pyd_module_name = "%s";
126                         PydMain();
127                         return pyd_modules[""];
128                     });
129             }
130         }.format(libraryName, libraryName);
131     } else {
132         return q{
133             extern(C) export void init%s() {
134                 import pyd.exception: exception_catcher;
135                 import pyd.thread: ensureAttached;
136                 import pyd.def: pyd_module_name;
137                 import core.runtime: rt_init;
138 
139                 rt_init;
140 
141                 exception_catcher(delegate void() {
142                         ensureAttached();
143                         pyd_module_name = "%s";
144                         PydMain();
145                     });
146 
147             }
148         }.format(libraryName, libraryName);
149     }
150 }