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:
15     Modules, LibraryName, PreModuleInitCode, PostModuleInitCode, RootNamespace;
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     RootNamespace _ = RootNamespace(),  // ignored in this backend
27     PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
28     PostModuleInitCode postModuleInitCode = PostModuleInitCode())
29     ()
30 {
31     return __ctfe
32         ? wrapAll(libraryName, modules, preModuleInitCode, postModuleInitCode)
33         : null;
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         import autowrap.common : dllMainMixinStr;
61         ret ~= dllMainMixinStr;
62     }
63 
64     return ret;
65 }
66 
67 /**
68    A string to be mixed in that defines PydMain, automatically registering all
69    functions and structs in the passed in modules.
70  */
71 string pydMainMixin(in Modules modules,
72                     in PreModuleInitCode preModuleInitCode = PreModuleInitCode(),
73                     in PostModuleInitCode postModuleInitCode = PostModuleInitCode())
74     @safe pure
75 {
76     import std.format: format;
77     import std.algorithm: map;
78     import std.array: join;
79 
80     if(!__ctfe) return null;
81 
82     const modulesList = modules.value.map!(a => a.toString).join(", ");
83 
84     return q{
85         extern(C) void PydMain() {
86             import std.typecons: Yes, No;
87             import pyd.pyd: module_init;
88             import autowrap.python.wrap: wrapAllFunctions, wrapAllAggregates;
89 
90             // this must go before module_init
91             wrapAllFunctions!(%s);
92 
93             %s
94 
95             module_init;
96 
97             // this must go after module_init
98             wrapAllAggregates!(%s);
99 
100             %s
101         }
102     }.format(modulesList, preModuleInitCode.value, modulesList, postModuleInitCode.value);
103 }
104 
105 /**
106    A string to be mixed in that defines the PyInit function for a library.
107  */
108 string pydInitMixin(in string libraryName) @safe pure {
109     import std.format: format;
110 
111     if(!__ctfe) return null;
112 
113     version(Python_3_0_Or_Later) {
114         return q{
115             import deimos.python.object: PyObject;
116             extern(C) export PyObject* PyInit_%s() {
117                 import pyd.def: pyd_module_name, pyd_modules;
118                 import pyd.exception: exception_catcher;
119                 import pyd.thread: ensureAttached;
120                 import core.runtime: rt_init;
121 
122                 rt_init;
123 
124                 return exception_catcher(delegate PyObject*() {
125                         ensureAttached();
126                         pyd_module_name = "%s";
127                         PydMain();
128                         return pyd_modules[""];
129                     });
130             }
131         }.format(libraryName, libraryName);
132     } else {
133         return q{
134             extern(C) export void init%s() {
135                 import pyd.exception: exception_catcher;
136                 import pyd.thread: ensureAttached;
137                 import pyd.def: pyd_module_name;
138                 import core.runtime: rt_init;
139 
140                 rt_init;
141 
142                 exception_catcher(delegate void() {
143                         ensureAttached();
144                         pyd_module_name = "%s";
145                         PydMain();
146                     });
147 
148             }
149         }.format(libraryName, libraryName);
150     }
151 }