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 "blNone", 43 "blPixelConverterConvert", 44 "blRuntimeAllocImpl", 45 "blRuntimeAllocAlignedImpl", 46 "blRuntimeFreeImpl" 47 ]; 48 49 string cwd; 50 string dirBlend2dRepoSrc; 51 string[string] dFiles; 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 dirBlend2dRepoSrc = chainPath(dirBlend2d, "src/blend2d").array; 64 fileSymbols = chainPath(cwd, "symbols.txt").array; 65 string dirBindBC = chainPath(cwd, "../source/bindbc/blend2d").array; 66 fileBindStatic = chainPath(dirBindBC, "bindstatic.d").array; 67 fileBindDynamic = chainPath(dirBindBC, "binddynamic.d").array; 68 69 bufBindStatic = appender!(string[]); 70 bufBindDynamic = appender!(string[]); 71 bufBindSymbol = appender!(string[]); 72 73 initialize; 74 } 75 76 void initialize() 77 { 78 dFiles = genDFiles; 79 symbols = readText(fileSymbols).splitLines; 80 } 81 82 string[string] genDFiles() 83 { 84 chdir(dirBlend2dRepoSrc); 85 scope(exit) chdir(cwd); 86 87 auto hFiles = dirEntries("", SpanMode.breadth).filter!( 88 f => f.name.endsWith(".h") 89 && !f.name.endsWith("_p.h") 90 && !f.name.endsWith("-impl.h") 91 ).array; 92 93 typeof(return) result; 94 95 foreach (i, ref h; parallel(hFiles, 1)) 96 { 97 auto pid = spawnProcess(["dstep", 98 "--collision-action=ignore", 99 "--space-after-function-name=false", 100 "--comments=false", 101 h.name]); 102 enforce(pid.wait() == 0, "dstep failed on " ~ h.name); 103 104 auto d = h.name.withExtension("d").array; 105 result[d] = readText(d); 106 remove(d); 107 } 108 109 return result; 110 } 111 112 void commentOutLines() 113 { 114 auto api = dFiles[dApi]; 115 auto toBeCommentedOut = [ 116 ctRegex!(r"^(struct .+;)", "m"), 117 ctRegex!(r"^(.* _Pragma\(.+;)", "m"), 118 ctRegex!(r"^(BLResult blResultFromWinError.+;)", "m"), 119 ctRegex!(r"^(BLResult blResultFromPosixError.+;)", "m"), 120 ]; 121 foreach (ref re; toBeCommentedOut) 122 api = replaceAll(api, re, "//$&"); 123 dFiles[dApi] = api; 124 } 125 126 void addVersionStatic() 127 { 128 auto api = dFiles[dApi]; 129 foreach (ref fn; symbols) 130 api = replaceAll(api, regex(r"^(.* " ~ fn ~ r"\()", "m"), versionStatic ~ "$&"); 131 api ~= "\n" ~ versionStatic ~ "version(Windows) BLResult blResultFromWinError(uint e);"; 132 api ~= "\n" ~ versionStatic ~ "version(Posix) BLResult blResultFromPosixError(int e);"; 133 dFiles[dApi] = api; 134 } 135 136 void bindStatic() 137 { 138 bufBindStatic.put(q"EOL 139 module bindbc.blend2d.bindstatic; 140 141 version(BindBC_Static) version = BindBlend2D_Static; 142 143 import core.stdc.stdint; 144 import core.stdc.stdarg; 145 146 auto BL_MAJOR_VERSION(uint ver) { return ver >> 16; } 147 auto BL_MINOR_VERSION(uint ver) { return (ver >> 8) & ((1 << 8) - 1); } 148 auto BL_PATCH_VERSION(uint ver) { return ver & ((1 << 8) - 1); } 149 150 enum BLEND2D_VERSION_MAJOR = BL_MAJOR_VERSION(BL_VERSION); 151 enum BLEND2D_VERSION_MINOR = BL_MINOR_VERSION(BL_VERSION); 152 enum BLEND2D_VERSION_REVISION = BL_PATCH_VERSION(BL_VERSION); 153 154 private 155 enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = (){ 156 string expandEnum = "enum {"; 157 foreach(m;__traits(allMembers, EnumType)) { 158 expandEnum ~= m ~ " = " ~ fqnEnumType ~ "." ~ m ~ ","; 159 } 160 expandEnum ~= "}"; 161 return expandEnum; 162 }(); 163 164 EOL"); 165 166 string[] enums; 167 auto re = ctRegex!(r"^enum ([^;{]+)$"); 168 foreach (d; dFiles.keys.sort) 169 foreach (line; dFiles[d].splitLines) 170 { 171 auto m = matchFirst(line, re); 172 if (!m.empty) 173 enums ~= "mixin(expandEnum!" ~ m[1] ~ ");"; 174 } 175 enums ~= ""; 176 bufBindStatic.put(enums.join("\n")); 177 178 commentOutLines; 179 addVersionStatic; 180 181 foreach (d; dFiles.keys.sort) 182 { 183 bufBindStatic.put(""); 184 bufBindStatic.put("//****** " ~ d.baseName ~ " ******//"); 185 bufBindStatic.put(""); 186 bufBindStatic.put(dFiles[d].splitLines); 187 } 188 } 189 190 void bindDynamic() 191 { 192 bufBindDynamic.put(q"EOL 193 module bindbc.blend2d.binddynamic; 194 195 version(BindBlend2D_Static) {} 196 else version = BindBlend2D_Dynamic; 197 198 version(BindBlend2D_Dynamic): 199 200 import core.stdc.stdint; 201 import core.stdc.stdarg; 202 203 import bindbc.loader; 204 import bindbc.blend2d.types; 205 import bindbc.blend2d.bindstatic; 206 207 extern(C) @nogc nothrow alias pblDefaultApproximationOptions = const BLApproximationOptions; 208 __gshared pblDefaultApproximationOptions blDefaultApproximationOptions; 209 210 extern(C) @nogc nothrow alias pblFormatInfo = const(BLFormatInfo)[BL_FORMAT_RESERVED_COUNT]; 211 __gshared pblFormatInfo blFormatInfo; 212 213 extern(C) @nogc nothrow alias pblMatrix2DMapPointDArrayFuncs = BLMapPointDArrayFunc[BL_MATRIX2D_TYPE_COUNT]; 214 __gshared pblMatrix2DMapPointDArrayFuncs blMatrix2DMapPointDArrayFuncs; 215 216 extern(C) @nogc nothrow alias pblNone = BLVariantCore[BL_IMPL_TYPE_COUNT]; 217 __gshared pblNone blNone; 218 219 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); 220 __gshared pblPixelConverterConvert blPixelConverterConvert; 221 222 version(Windows) { 223 extern(C) @nogc nothrow alias pblResultFromWinError = BLResult function(uint e); 224 __gshared pblResultFromWinError blResultFromWinError; 225 } 226 version(Posix) { 227 extern(C) @nogc nothrow alias pblResultFromPosixError = BLResult function(int e); 228 __gshared pblResultFromPosixError blResultFromPosixError; 229 } 230 231 EOL"); 232 233 auto fns = symbols.filter!(a => skipFnList.find(a).empty); 234 auto re_fns = regex(" (" ~ fns.join("|") ~ r")\("); 235 foreach (line; bufBindStatic.data) 236 { 237 const m = line.matchFirst(re_fns); 238 if (m.empty) 239 continue; 240 241 string fn = m[1]; 242 string fn_def = line.replaceFirst(fn, "function")[versionStatic.length .. $]; 243 bufBindDynamic.put("extern(C) @nogc nothrow alias p" ~ fn ~ " = " ~ fn_def); 244 bufBindDynamic.put("__gshared p" ~ fn ~ " " ~ fn ~ ";"); 245 bufBindDynamic.put(""); 246 bufBindSymbol.put(" lib.bindSymbol_stdcall(" ~ fn ~ ", \"" ~ fn ~ "\");"); 247 } 248 249 bufBindDynamic.put(q"EOL 250 private { 251 SharedLib lib; 252 Blend2DSupport loadedVersion; 253 } 254 255 @nogc nothrow: 256 257 void unloadGLFW() 258 { 259 if(lib != invalidHandle) { 260 lib.unload(); 261 } 262 } 263 264 Blend2DSupport loadedBlend2DVersion() @safe 265 { 266 return loadedVersion; 267 } 268 269 bool isBlend2DLoaded() @safe 270 { 271 return lib != invalidHandle; 272 } 273 274 Blend2DSupport loadBlend2D() 275 { 276 version(Windows) { 277 const(char)[][1] libNames = ["blend2d.dll"]; 278 } 279 else version(OSX) { 280 const(char)[][1] libNames = [ 281 "blend2d.dylib" 282 ]; 283 } 284 else version(Posix) { 285 const(char)[][1] libNames = [ 286 "blend2d.so" 287 ]; 288 } 289 else static assert(0, "bindbc-blend2d is not yet supported on this platform."); 290 291 Blend2DSupport ret; 292 foreach(name; libNames) { 293 ret = loadBlend2D(name.ptr); 294 if(ret != Blend2DSupport.noLibrary) break; 295 } 296 return ret; 297 } 298 299 Blend2DSupport loadBlend2D(const(char)* libName) 300 { 301 lib = load(libName); 302 if(lib == invalidHandle) { 303 return Blend2DSupport.noLibrary; 304 } 305 306 auto errCount = errorCount(); 307 loadedVersion = Blend2DSupport.badLibrary; 308 309 lib.bindSymbol(cast(void**)&blDefaultApproximationOptions, "blDefaultApproximationOptions"); 310 lib.bindSymbol(cast(void**)&blFormatInfo, "blFormatInfo"); 311 lib.bindSymbol(cast(void**)&blMatrix2DMapPointDArrayFuncs, "blMatrix2DMapPointDArrayFuncs"); 312 lib.bindSymbol(cast(void**)&blNone, "blNone"); 313 314 lib.bindSymbol_stdcall(blPixelConverterConvert, "blPixelConverterConvert"); 315 316 version(Windows) lib.bindSymbol_stdcall(blResultFromWinError, "blResultFromWinError"); 317 version(Posix) lib.bindSymbol_stdcall(blResultFromPosixError, "blResultFromPosixError"); 318 319 EOL"); 320 321 bufBindDynamic.put(bufBindSymbol.data.join("\n")); 322 323 bufBindDynamic.put(q"EOL 324 325 if(errorCount() != errCount) return Blend2DSupport.badLibrary; 326 else loadedVersion = Blend2DSupport.bl00; 327 328 return loadedVersion; 329 } 330 EOL"); 331 332 } 333 334 void save() 335 { 336 writeText(fileBindStatic, bufBindStatic.data.join("\n")); 337 writeText(fileBindDynamic, bufBindDynamic.data.join("\n")); 338 } 339 }