1 module pbcompiler; 2 // compiler for .proto protocol buffer definition files that generates D code 3 // 4 import dprotobuf.pbroot; 5 import dprotobuf.pbextension; 6 import dprotobuf.pbmessage; 7 import dprotobuf.pbgeneral; 8 import dprotobuf.generator.d1lang; 9 import dprotobuf.generator.dlang; 10 11 import std.conv; 12 import std.file; 13 import std.path; 14 import std.stdio; 15 import std.string; 16 17 version(D_Version2) { 18 import std.getopt; 19 import std.algorithm; 20 import std.range; 21 } else 22 import dprotobuf.d1support; 23 24 // our tree of roots to play with, so that we can apply multiple extensions to a given document 25 PBRoot[string] docroots; 26 27 enum Language { 28 D1, 29 D2, 30 } 31 32 int main(string[] args) { 33 version(D_Version2) { 34 Language lang = Language.D2; 35 getopt(args, config.passThrough, 36 "lang", &lang 37 ); 38 } else 39 Language lang = Language.D1; 40 41 // rip off the first arg, because that's the name of the program 42 args = args[1..$]; 43 44 if (!args.length) throw new Exception("No proto files supplied on the command line!"); 45 46 foreach (arg;args) { 47 readRoot(arg); 48 } 49 applyExtensions(); 50 writeRoots(lang); 51 return 0; 52 } 53 54 // returns package name 55 string readRoot(string filename) { 56 string contents = cast(string)read(filename); 57 auto root = PBRoot(contents); 58 string fname = root.Package; 59 if (!fname.length) { 60 if (filename.length>6 && filename[$-6..$].icmp(".proto") == 0) { 61 fname = filename[0..$-6]; 62 } else { 63 fname = filename; 64 } 65 } 66 root.Package = fname; 67 foreach(ref imp;root.imports) { 68 imp = readRoot(imp); 69 } 70 // store this for later use under its package name 71 docroots[root.Package] = root; 72 return root.Package; 73 } 74 75 // we run through the whole list looking for extensions and applying them 76 void applyExtensions() { 77 foreach(root;docroots) { 78 // make sure something can only extend what it has access to (including where it was defined) 79 writefln("Probing root %s for extensions",root.Package); 80 PBExtension[]extlist = getExtensions(root); 81 foreach(ext;extlist) { 82 // check the current node first (just in case) 83 if (root.Package.applyExtension(ext)) continue; 84 foreach(imp;root.imports) { 85 // break out of the import loop to jump to the next extension 86 if (imp.applyExtension(ext)) break; 87 } 88 } 89 } 90 } 91 92 // attempt to apply an individual extension to a node identifier 93 // returns 1 if applied 94 int applyExtension(string imp,PBExtension ext) { 95 writefln("Attempting to apply extension %s",ext.name); 96 string tmp = ext.name; 97 bool impflag = false; 98 tmp.skipOver(imp); 99 // now look for a message that matches the section within the current import 100 PBMessage*dst = imp.findMessage(tmp); 101 if (dst is null) { 102 writefln("Found no destination to apply extension to %s in import \"%s\"",ext.name,imp); 103 if (impflag) throw new Exception("Found an import path match \""~imp~"\", but unable to apply extension \""~ext.name~"\'"); 104 return 0; 105 } 106 // we have something we might want to apply it to! this is exciting! 107 // make sure it's within the allowed extensions 108 *dst = insertExtension(*dst, ext); 109 110 // it looks like we have a match! 111 writefln("Applying extensions to %s",dst.name); 112 return 1; 113 } 114 115 // this function digs through a given root to see if it has the message described by the dotstring 116 PBMessage*findMessage(string impstr,string message) { 117 PBRoot root = docroots[impstr]; 118 return searchMessages(root, ParserData(message)); 119 } 120 121 // this is where all files are written, no real processing is done here 122 void writeRoots(Language lang) { 123 foreach(root;docroots) { 124 string tmp; 125 tmp ~= addComments(root.comments).finalize(); 126 tmp ~= "module "~root.Package~";\n"; 127 // write out imports 128 foreach(imp;root.imports) { 129 tmp ~= "import "~imp~";\n"; 130 } 131 switch(lang) { 132 case Language.D1: 133 tmp ~= langD1(root); 134 break; 135 case Language.D2: 136 tmp ~= langD(root); 137 break; 138 default: 139 assert(false); 140 } 141 string fname = root.Package.tr(".","/")~".d"; 142 version(D_Version2) string dname = fname.dirName(); 143 else string dname = fname.getDirName(); 144 // check to see if we need to create the directory 145 if (dname.length && !dname.exists()) { 146 dname.mkdirRecurse(); 147 } 148 std.file.write(fname,tmp); 149 } 150 } 151 152 void mkdirRecurse(in string pathname) 153 { 154 version(D_Version2) string left = dirName(pathname); 155 else string left = getDirName(pathname); 156 exists(left) || mkdirRecurse(left); 157 mkdir(pathname); 158 }