1 module gen_bindings; 2 3 import std; 4 5 alias writeText = std.file.write; 6 7 void main(string[] args) 8 { 9 void writeUsage() 10 { 11 stdout.writefln("\nUsage: %s [options] <blend2d_repo_path>", args[0].baseName); 12 } 13 14 if (args.length < 2) 15 { 16 writeUsage; 17 return; 18 } 19 20 string dirBlend2d = args[1]; 21 if (!dirBlend2d.exists || !dirBlend2d.isDir) 22 { 23 stderr.writeln("<blend2d_repo_path> is expected to be an existing directory: ", dirBlend2d); 24 writeUsage; 25 return; 26 } 27 28 auto gen = new GenBindings(dirBlend2d); 29 gen.bindStatic; 30 gen.bindDynamic; 31 gen.save; 32 } 33 34 class GenBindings 35 { 36 string dApi = "api.d"; 37 string versionStatic = "version(BindBlend2D_Static) "; 38 string[] skipFnList = [ 39 "blDefaultApproximationOptions", 40 "blFormatInfo", 41 "blMatrix2DMapPointDArrayFuncs", 42 "blPixelConverterConvert", 43 "blRuntimeAllocImpl", 44 "blRuntimeAllocAlignedImpl", 45 "blRuntimeFreeImpl" 46 ]; 47 48 string cwd; 49 string dirBlend2dSrc; 50 string dirBlend2dSrcBlend2d; 51 string dFile; 52 string[] symbols; 53 string fileSymbols; 54 string fileBindStatic; 55 string fileBindDynamic; 56 Appender!(string[]) bufBindStatic; 57 Appender!(string[]) bufBindDynamic; 58 Appender!(string[]) bufBindSymbol; 59 60 this(string dirBlend2d) 61 { 62 cwd = getcwd(); 63 dirBlend2dSrc = absolutePath(chainPath(dirBlend2d, "src").array, cwd); 64 dirBlend2dSrcBlend2d = absolutePath(chainPath(dirBlend2dSrc, "blend2d").array, cwd); 65 fileSymbols = chainPath(cwd, "symbols.txt").array; 66 string dirBindBC = chainPath(cwd, "../source/bindbc/blend2d").array; 67 fileBindStatic = chainPath(dirBindBC, "bindstatic.d").array; 68 fileBindDynamic = chainPath(dirBindBC, "binddynamic.d").array; 69 70 bufBindStatic = appender!(string[]); 71 bufBindDynamic = appender!(string[]); 72 bufBindSymbol = appender!(string[]); 73 74 initialize; 75 } 76 77 void initialize() 78 { 79 dFile = genDFile(); 80 symbols = readText(fileSymbols).splitLines; 81 } 82 83 string genDFile() 84 { 85 chdir(dirBlend2dSrc); 86 scope(exit) chdir(cwd); 87 88 auto tmpFile = chainPath(dirBlend2dSrcBlend2d, "blend2d-api.h").array; 89 append(tmpFile, "// The file is generated automatically"); 90 scope(exit) tmpFile.remove; 91 92 auto re = ctRegex!(`#include "(.+)"`); 93 foreach (line; readText("blend2d.h").splitLines) 94 { 95 auto m = matchFirst(line, re); 96 if (m.empty) 97 continue; 98 99 append(tmpFile, "\n\n//****** " ~ m[1] ~ " ******//\n\n"); 100 append(tmpFile, readText(m[1])); 101 } 102 103 chdir(dirBlend2dSrcBlend2d); 104 105 auto preprocessedFile = "blend2d-api.c"; 106 auto gccOut = File(preprocessedFile, "w"); 107 auto pidGcc = spawnProcess(["gcc", "-E", "-P", tmpFile], std.stdio.stdin, gccOut); 108 enforce(pidGcc.wait() == 0, "gcc preprocessor failed on " ~ tmpFile); 109 gccOut.close(); 110 scope(exit) remove(preprocessedFile); 111 112 string[] txt; 113 txt ~= "#include <stdarg.h>"; 114 txt ~= "#include <stdint.h>"; 115 auto lines = readText(preprocessedFile).splitLines; 116 foreach (i, line; lines) 117 { 118 if (indexOf(line, "typedef struct BL") != 0) 119 continue; 120 txt ~= lines[i..$]; 121 break; 122 } 123 writeText(preprocessedFile, txt.join("\n")); 124 125 auto pidDStep = spawnProcess(["dstep", 126 "--output=" ~ dApi, 127 "--collision-action=ignore", 128 "--single-line-function-signatures=true", 129 "--space-after-function-name=false", 130 "--comments=false", 131 preprocessedFile]); 132 enforce(pidDStep.wait() == 0, "dstep failed on " ~ preprocessedFile); 133 scope(exit) remove(dApi); 134 135 return readText(dApi); 136 } 137 138 void bindStatic() 139 { 140 bufBindStatic.put(q"EOL 141 module bindbc.blend2d.bindstatic; 142 143 version(BindBC_Static) version = BindBlend2D_Static; 144 145 import core.stdc.stdint; 146 import core.stdc.stdarg; 147 148 auto BL_MAKE_VERSION(uint MAJOR, uint MINOR, uint PATCH) 149 { 150 return (MAJOR << 16) | (MINOR << 8) | PATCH; 151 } 152 153 enum BL_VERSION = BL_MAKE_VERSION(0, 8, 0); 154 155 private 156 enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = (){ 157 string expandEnum = "enum {"; 158 foreach(m;__traits(allMembers, EnumType)) { 159 expandEnum ~= m ~ " = " ~ fqnEnumType ~ "." ~ m ~ ","; 160 } 161 expandEnum ~= "}"; 162 return expandEnum; 163 }(); 164 165 struct BLObjectImpl; 166 167 EOL"); 168 169 string[] enums; 170 auto re = ctRegex!(r"^enum ([^;{]+)$"); 171 foreach (line; dFile.splitLines) 172 { 173 auto m = matchFirst(line, re); 174 if (!m.empty) 175 enums ~= "mixin(expandEnum!" ~ m[1] ~ ");"; 176 } 177 enums ~= ""; 178 bufBindStatic.put(enums.join("\n")); 179 180 auto toBeCommentedOut = [ 181 ctRegex!(r"^(struct .+;)", "m"), 182 ctRegex!(r"^(union .+;)", "m"), 183 ctRegex!(r"^(.* _Pragma\(.+;)", "m"), 184 ctRegex!(r"^(BLResult blResultFromWinError.+;)", "m"), 185 ctRegex!(r"^(BLResult blResultFromPosixError.+;)", "m"), 186 ]; 187 188 bufBindStatic.put(""); 189 bufBindStatic.put("//****** " ~ dApi ~ " ******//"); 190 bufBindStatic.put(""); 191 192 foreach (ref rx; toBeCommentedOut) 193 dFile = replaceAll(dFile, rx, "//$&"); 194 195 foreach (ref fn; symbols) 196 dFile = replaceAll(dFile, regex(r"^(.* " ~ fn ~ r"\()", "m"), versionStatic ~ "$&"); 197 bufBindStatic.put(dFile.splitLines); 198 199 bufBindStatic.put(""); 200 bufBindStatic.put(versionStatic ~ "version(Windows) BLResult blResultFromWinError(uint e);"); 201 bufBindStatic.put(versionStatic ~ "version(Posix) BLResult blResultFromPosixError(int e);"); 202 } 203 204 void bindDynamic() 205 { 206 bufBindDynamic.put(q"EOL 207 module bindbc.blend2d.binddynamic; 208 209 version(BindBlend2D_Static) {} 210 else version = BindBlend2D_Dynamic; 211 212 version(BindBlend2D_Dynamic): 213 214 import core.stdc.stdint; 215 import core.stdc.stdarg; 216 217 import bindbc.loader; 218 import bindbc.blend2d.types; 219 import bindbc.blend2d.bindstatic; 220 221 extern(C) @nogc nothrow alias pblDefaultApproximationOptions = const BLApproximationOptions; 222 __gshared pblDefaultApproximationOptions blDefaultApproximationOptions; 223 224 extern(C) @nogc nothrow alias pblFormatInfo = const(BLFormatInfo)[BL_FORMAT_RESERVED_COUNT]; 225 __gshared pblFormatInfo blFormatInfo; 226 227 extern(C) @nogc nothrow alias pblMatrix2DMapPointDArrayFuncs = BLMapPointDArrayFunc[BL_MATRIX2D_TYPE_MAX_VALUE + 1]; 228 __gshared pblMatrix2DMapPointDArrayFuncs blMatrix2DMapPointDArrayFuncs; 229 230 extern(C) @nogc nothrow alias pblPixelConverterConvert = BLResult function(const(BLPixelConverterCore)* self, void* dstData, intptr_t dstStride, const(void)* srcData, intptr_t srcStride, uint w, uint h, const(BLPixelConverterOptions)* options); 231 __gshared pblPixelConverterConvert blPixelConverterConvert; 232 233 version(Windows) { 234 extern(C) @nogc nothrow alias pblResultFromWinError = BLResult function(uint e); 235 __gshared pblResultFromWinError blResultFromWinError; 236 } 237 version(Posix) { 238 extern(C) @nogc nothrow alias pblResultFromPosixError = BLResult function(int e); 239 __gshared pblResultFromPosixError blResultFromPosixError; 240 } 241 242 EOL"); 243 244 auto fns = symbols.filter!(a => skipFnList.find(a).empty); 245 auto re_fns = regex(" (" ~ fns.join("|") ~ r")\("); 246 foreach (line; bufBindStatic.data) 247 { 248 const m = line.matchFirst(re_fns); 249 if (m.empty) 250 continue; 251 252 string fn = m[1]; 253 string fn_def = line.replaceFirst(fn, "function")[versionStatic.length .. $]; 254 bufBindDynamic.put("extern(C) @nogc nothrow alias p" ~ fn ~ " = " ~ fn_def); 255 bufBindDynamic.put("__gshared p" ~ fn ~ " " ~ fn ~ ";"); 256 bufBindDynamic.put(""); 257 bufBindSymbol.put(" lib.bindSymbol_stdcall(" ~ fn ~ ", \"" ~ fn ~ "\");"); 258 } 259 260 bufBindDynamic.put(q"EOL 261 private { 262 SharedLib lib; 263 Blend2DSupport loadedVersion; 264 } 265 266 @nogc nothrow: 267 268 void unloadGLFW() 269 { 270 if(lib != invalidHandle) { 271 lib.unload(); 272 } 273 } 274 275 Blend2DSupport loadedBlend2DVersion() @safe 276 { 277 return loadedVersion; 278 } 279 280 bool isBlend2DLoaded() @safe 281 { 282 return lib != invalidHandle; 283 } 284 285 Blend2DSupport loadBlend2D() 286 { 287 version(Windows) { 288 const(char)[][1] libNames = ["blend2d.dll"]; 289 } 290 else version(OSX) { 291 const(char)[][1] libNames = [ 292 "blend2d.dylib" 293 ]; 294 } 295 else version(Posix) { 296 const(char)[][1] libNames = [ 297 "blend2d.so" 298 ]; 299 } 300 else static assert(0, "bindbc-blend2d is not yet supported on this platform."); 301 302 Blend2DSupport ret; 303 foreach(name; libNames) { 304 ret = loadBlend2D(name.ptr); 305 if(ret != Blend2DSupport.noLibrary) break; 306 } 307 return ret; 308 } 309 310 Blend2DSupport loadBlend2D(const(char)* libName) 311 { 312 lib = load(libName); 313 if(lib == invalidHandle) { 314 return Blend2DSupport.noLibrary; 315 } 316 317 auto errCount = errorCount(); 318 loadedVersion = Blend2DSupport.badLibrary; 319 320 lib.bindSymbol(cast(void**)&blDefaultApproximationOptions, "blDefaultApproximationOptions"); 321 lib.bindSymbol(cast(void**)&blFormatInfo, "blFormatInfo"); 322 lib.bindSymbol(cast(void**)&blMatrix2DMapPointDArrayFuncs, "blMatrix2DMapPointDArrayFuncs"); 323 324 lib.bindSymbol_stdcall(blPixelConverterConvert, "blPixelConverterConvert"); 325 326 version(Windows) lib.bindSymbol_stdcall(blResultFromWinError, "blResultFromWinError"); 327 version(Posix) lib.bindSymbol_stdcall(blResultFromPosixError, "blResultFromPosixError"); 328 329 EOL"); 330 331 bufBindDynamic.put(bufBindSymbol.data.join("\n")); 332 333 bufBindDynamic.put(q"EOL 334 335 if(errorCount() != errCount) return Blend2DSupport.badLibrary; 336 else loadedVersion = Blend2DSupport.bl00; 337 338 return loadedVersion; 339 } 340 EOL"); 341 342 } 343 344 void save() 345 { 346 writeText(fileBindStatic, bufBindStatic.data.join("\n")); 347 writeText(fileBindDynamic, bufBindDynamic.data.join("\n")); 348 } 349 }