1 /* 2 * This file is part of gtkD. 3 * 4 * gtkD is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License 6 * as published by the Free Software Foundation; either version 3 7 * of the License, or (at your option) any later version, with 8 * some exceptions, please read the COPYING file. 9 * 10 * gtkD is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public License 16 * along with gtkD; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA 18 */ 19 20 module utils.HTODConvert; 21 22 private import utils.DefReader; 23 private import utils.GtkWrapper; 24 25 private import std.stdio; 26 private import std.file; 27 import std.path; 28 private import std.string; 29 private import std.process; 30 31 static string htod_location = "wrap/utils/htod.exe"; 32 static string wine_command = "wine"; 33 34 // there is no longer a 'bit' type 35 alias bool bit; 36 37 //debug = flow; 38 //debug = processLine; 39 //debug = getDType; 40 //debug = functions; 41 42 public class Ranges 43 { 44 45 private struct Range 46 { 47 int vStart; 48 int vEnd; 49 bool exclude = true; 50 51 public bool contains(int pos) 52 { 53 return (pos>= vStart) && (pos<=vEnd); 54 } 55 56 public bool includes(int pos) 57 { 58 return contains(pos) 59 ? !exclude 60 : exclude 61 ; 62 } 63 64 public bool matches(int start, int end) 65 { 66 return vStart == start 67 && vEnd == end; 68 } 69 } 70 71 private Range*[] ranges; 72 73 public void addRange(int start, int end, bool exclude) 74 { 75 Range* range = new Range; 76 range.vStart = start; 77 range.vEnd = end; 78 range.exclude = exclude; 79 ranges ~= range; 80 } 81 82 /** 83 * Finds the last range that contains the position and returns it's include value 84 * Params: 85 * pos = 86 */ 87 public void include(int pos) 88 { 89 bool incl = true; 90 Range* range; 91 size_t i = ranges.length; 92 while ( i > 0 ) 93 { 94 range = ranges[--i]; 95 if ( range.contains(pos) ) 96 { 97 i=0; 98 incl = !range.exclude; 99 } 100 } 101 //return incl; 102 } 103 104 } 105 106 /** 107 * This is used to post-process dm htod .d file for gtkD 108 * to create the htod .d file: 109 * - copy the original .h file to a working directory 110 * - remove all unecessary definitions 111 * - run `wine ~/dm/bin/htod.exe <file.h>` 112 * - run this on the generated .d file 113 */ 114 public class HTODConvert 115 { 116 string dText; 117 118 DefReader defReader; 119 120 /** the library id on the system (for instance GL for libGL.so) */ 121 string lib; 122 /** the .h (or .h pos-processed) to convert to .d */ 123 string preFile; 124 /** the package */ 125 string pack; 126 string bindDir; 127 string outputRoot; 128 string inputRoot; 129 /** convert to dynamic loading */ 130 bool dynLoad; 131 /** mark when the header was already added to the text */ 132 bool dynLoadAlreadyOpen; 133 /** mar when the module and license where already added to the text */ 134 bool headerAlreadyAdded; 135 string[] functions; 136 string[] comment; 137 138 139 this(string htodFilename) 140 { 141 defReader = new DefReader(htodFilename); 142 clearValues(); 143 process(); 144 } 145 this(string htodFilename, string _outputRoot, string _inputRoot) 146 { 147 inputRoot = _inputRoot; 148 outputRoot = _outputRoot; 149 this(htodFilename); 150 } 151 void process() 152 { 153 debug(flow)(writefln("HTODConvert.process 1")); 154 while ( defReader.next().length > 0 ) 155 { 156 switch ( defReader.getKey() ) 157 { 158 case "prefile": 159 debug(flow)(writefln("HTODConvert.process case prefile")); 160 preFile = defReader.getValue(); 161 break; 162 case "bindDir": 163 bindDir = defReader.getValue(); 164 break; 165 case "pack": 166 debug(flow)(writefln("HTODConvert.process case pack")); 167 pack = defReader.getValue(); 168 break; 169 170 case "lib": 171 debug(flow)(writefln("HTODConvert.process case libe")); 172 lib = defReader.getValue(); 173 break; 174 175 case "file": 176 debug(flow)(writefln("HTODConvert.process case file 1")); 177 if ( preFile.length > 0 ) 178 { 179 debug(flow)(writefln("HTODConvert.process case file 2")); 180 processPreFile(preFile, defReader.getValue()); 181 debug(flow)(writefln("HTODConvert.process case file 3")); 182 } 183 debug(flow)(writefln("HTODConvert.process case file 4")); 184 preFile.length = 0; 185 debug(flow)(writefln("HTODConvert.process case file 5")); 186 processFile(defReader.getValue()); 187 debug(flow)(writefln("HTODConvert.process case file 6 ")); 188 break; 189 190 case "dynload": 191 debug(flow)(writefln("HTODConvert.process case dynload")); 192 dynLoadAlreadyOpen = false; 193 headerAlreadyAdded = false; 194 dynLoad = true; 195 break; 196 197 case "outfile": 198 debug(flow)(writefln("HTODConvert.process case outfile")); 199 writeFile(defReader.getValue()); 200 clearValues(); 201 break; 202 203 default: 204 debug(flow)(writefln("HTODConvert.process case default")); 205 writefln("Unknown key/value = %s/%s", defReader.getKey(), defReader.getValue()); 206 break; 207 208 } 209 } 210 } 211 212 void processPreFile(string preFileName, string fileName) 213 { 214 //debug(flow) 215 writefln("HTODConvert.processPreFile files: %s > %s", preFileName, fileName); 216 string[] args; 217 args ~= htod_location; 218 args ~= htod_location; 219 args ~= preFileName; 220 args ~= std.path.buildPath(inputRoot,fileName); 221 try 222 { 223 std.process.execvp(wine_command, args); 224 } 225 catch (Throwable e) 226 { 227 // ignore - it always fail - most of the time produces the file 228 } 229 } 230 231 void processFile(string fileName) 232 { 233 debug(flow)(writefln("HTODConvert.processFile")); 234 string hText = cast(string)std.file.read(fileName); 235 string[] hLines = std..string.splitLines(hText); 236 string line; 237 238 int i = 0; 239 while ( i < hLines.length ) 240 { 241 line = hLines[i++]; 242 if ( std..string.indexOf(line,'(')>=0 ) 243 { 244 int openBrace = 1; 245 if ( std..string.indexOf(line,')')>=0 ) --openBrace; 246 while ( openBrace > 0 247 && i<hLines.length 248 ) 249 { 250 string l = hLines[i++]; 251 if ( std..string.indexOf(l,')') >=0) --openBrace; 252 line ~= " " ~ std..string.strip(l); 253 } 254 } 255 addLine(line); 256 } 257 258 if ( dynLoad ) 259 { 260 closeDynLoad(); 261 } 262 } 263 264 void addLine(string line) 265 { 266 if ( !startsWith(line, "//C") 267 && line !="alias extern GLAPI;" 268 && line !="alias GLAPIENTRY APIENTRY;" 269 ) 270 { 271 if ( line == "alias sbyte GLbyte;" ) 272 { 273 line = "alias byte GLbyte;"; 274 } 275 if ( line == "module wrap/cHeaders/GL/gl;" ) 276 { 277 line = "module "~bindDir~".gl;"; 278 } 279 280 if ( line == "module wrap/cHeaders/GL/glu;" ) 281 { 282 line = "module "~bindDir~".glu;"; 283 } 284 285 if ( line == "import std.c.gl;" ) 286 { 287 line = "import "~bindDir~".gl;"; 288 } 289 290 if ( startsWith(line, "/* Converted to D from" ) ) 291 { 292 line = GtkWrapper.license; 293 } 294 295 if ( dynLoad ) 296 { 297 addLineDynLoad(line); 298 } 299 else 300 { 301 dText ~= line~"\n"; 302 } 303 304 } 305 } 306 307 /** 308 * If the line is a function definition stores the line to include on the extern(C) 309 * and the symbols. 310 * for now a function is a line that ends with ");" 311 * Params: 312 * dText = 313 * line = 314 */ 315 void addLineDynLoad(string line) 316 { 317 //debug(flow)(writefln("HTODConvert.addLineDynLoad")); 318 319 if ( headerAlreadyAdded 320 && !dynLoadAlreadyOpen 321 ) 322 { 323 openDynLoad(); 324 } 325 326 if ( endsWith(line, ");") ) 327 { 328 int i=0; 329 while ( i<comment.length ) 330 { 331 functions ~= comment[i++]; 332 } 333 comment.length = 0; 334 functions ~= line; 335 debug(functions)writefln("HTODConvert.addLineDynLoad function[%s] = %s", 336 functions.length, line); 337 } 338 else if ( startsWith(line, "/*") 339 || startsWith(line, " *") 340 || startsWith(line, " */") 341 || line.length == 0 342 ) 343 { 344 comment ~= line; 345 } 346 else 347 { 348 if ( startsWith(line, "module") ) 349 { 350 headerAlreadyAdded = true; 351 } 352 int i=0; 353 while ( i<comment.length ) 354 { 355 dText ~= comment[i++]~"\n"; 356 } 357 comment.length = 0; 358 dText ~= line~"\n"; 359 } 360 } 361 362 void openDynLoad() 363 { 364 debug(flow)(writefln("HTODConvert.openDynLoad")); 365 dynLoadAlreadyOpen = true; 366 dText ~= 367 "\n" 368 "\n" 369 "private import std.stdio;\n" 370 "private import "~bindDir~"."~pack~"types;\n" 371 "private import gtkc.Loader;\n" 372 "private import gtkc.paths;\n" 373 "\n" 374 ; 375 if ( lib == "GL" 376 || lib == "GLU" ) 377 { 378 return; 379 } 380 dText ~= 381 "private Linker "~lib~"_Linker;\n" 382 "\n" 383 "static this()\n" 384 "{\n" 385 " "~lib~"_Linker = new Linker(libPath ~ importLibs[LIBRARY."~std..string.toUpper(lib)~"] );\n" 386 " "~lib~"_Linker.link("~lib~"Links);\n" 387 " debug writefln(\"* Finished static this(): "~lib~"\");\n" 388 "}\n" 389 "\n" 390 "static ~this()\n" 391 "{\n" 392 " delete "~lib~"_Linker;\n" 393 " debug writefln(\"* Finished static ~this(): "~lib~"\");\n" 394 "}\n\n" 395 ; 396 397 398 } 399 400 void closeDynLoad() 401 { 402 debug(flow)(writefln("HTODConvert.closeDynLoad")); 403 dText ~= "\n\nextern(C)\n{\n"; 404 foreach ( string line ; functions ) 405 { 406 dText ~= "\t" ~ line ~"\n"; 407 } 408 dText ~= "} // extern(C)\n"; 409 410 dText ~= "\n\nSymbol[] "~lib~"Links = \n"; 411 dText ~= "[\n"; 412 413 foreach ( string line ; functions ) 414 { 415 if ( startsWith(line, "/*") 416 || startsWith(line, " *") 417 || startsWith(line, " */") 418 || line.length == 0 419 ) 420 { 421 dText ~= "\t"~line~"\n"; 422 } 423 else 424 { 425 sizediff_t end = std..string.indexOf(line,'('); 426 427 if ( end > 0 ) 428 { 429 sizediff_t start = end; 430 while ( start > 0 && line[start] > ' ' ) 431 { 432 --start; 433 } 434 string functionName = line[start+1..end]; 435 if ( functionName.length>1) 436 { 437 dText ~= "\t{ \"" 438 ~functionName 439 ~"\", cast(void**)& " 440 ~functionName 441 ~"},\n" 442 ; 443 } 444 } 445 } 446 447 } 448 449 dText ~= "];\n\n"; 450 } 451 452 void clearValues() 453 { 454 debug(flow)(writefln("HTODConvert.clearValues")); 455 dText.length = 0; 456 dynLoad = false; 457 lib.length = 0; 458 preFile.length = 0; 459 pack.length = 0; 460 bool dynLoadAlreadyOpen = false; 461 bool headerAlreadyAdded = false; 462 functions.length = 0; 463 comment.length = 0; 464 465 } 466 467 void writeFile(string fileName) 468 { 469 debug(flow)(writefln("HTODConvert.writeFile")); 470 debug(flow)(writefln("HTODConvert.writeFile fileName = %s", fileName)); 471 std.file.write(std.path.buildPath(outputRoot,fileName), dText); 472 } 473 474 public static bit startsWith(string str, string prefix) 475 { 476 return str.length >= prefix.length 477 && str[0..prefix.length] == prefix; 478 } 479 480 public static bit startsWith(string str, char prefix) 481 { 482 return str.length > 0 483 && str[0] == prefix; 484 } 485 486 public static bit endsWith(string str, string prefix) 487 { 488 return str.length >= prefix.length 489 && str[str.length-prefix.length..str.length] == prefix; 490 } 491 492 public static bit endsWith(string str, char suffix) 493 { 494 return str.length >= 1 495 && str[str.length-1] == suffix; 496 } 497 498 } 499 500