1 /** 2 Date: 2015-2017, Joakim Brännström 3 License: MPL-2, Mozilla Public License 2.0 4 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 5 */ 6 module cpptooling.generator.includes; 7 8 import dsrcgen.cpp : CppModule; 9 import dextool.type : DextoolVersion, Path; 10 import cpptooling.type : CustomHeader; 11 12 @safe: 13 14 /** Include headers as if they are C code. 15 * 16 * Wrapped in extern "C" to ensure C binding of the includes. 17 */ 18 void generateWrapIncludeInExternC(ControllerT, ParamT)(ControllerT ctrl, ParamT params, 19 CppModule hdr) { 20 import std.path : baseName; 21 22 if (ctrl.doIncludeOfPreIncludes) { 23 hdr.include(params.getFiles.pre_incl.baseName); 24 } 25 26 auto extern_c = hdr.suite("extern \"C\""); 27 extern_c.suppressIndent(1); 28 29 foreach (incl; params.getIncludes) { 30 extern_c.include(cast(string) incl); 31 } 32 33 if (ctrl.doIncludeOfPostIncludes) { 34 hdr.include(params.getFiles.post_incl.baseName); 35 } 36 37 hdr.sep(2); 38 } 39 40 /** Normal, unmodified include directives. 41 * 42 * Compared to generateC there are no special wrapping extern "C" wrapping. 43 */ 44 void generateIncludes(IncludesT)(IncludesT incls, CppModule hdr) { 45 foreach (incl; incls) { 46 hdr.include(cast(string) incl); 47 } 48 49 hdr.sep(2); 50 } 51 52 string convToIncludeGuard(FileT)(FileT fname) { 53 import std..string : translate; 54 import std.path : baseName; 55 56 // dfmt off 57 dchar[dchar] table = [ 58 '.' : '_', 59 '-' : '_', 60 '/' : '_']; 61 // dfmt on 62 63 return translate((cast(string) fname).baseName, table); 64 } 65 66 auto generatePreInclude(FileT)(FileT fname) { 67 import dsrcgen.cpp : CppHModule; 68 69 auto o = CppHModule(convToIncludeGuard(fname)); 70 auto c = new CppModule; 71 c.stmt("#undef __cplusplus")[$.end = ""]; 72 o.content.append(c); 73 74 return o; 75 } 76 77 auto generatePostInclude(FileT)(FileT fname) { 78 import dsrcgen.cpp : CppHModule; 79 80 auto o = CppHModule(convToIncludeGuard(fname)); 81 auto c = new CppModule; 82 c.define("__cplusplus"); 83 o.content.append(c); 84 85 return o; 86 } 87 88 /** Create a header. 89 * 90 * The users custom header will be parsed for the magic keywords. 91 * $file$ = replaced by the filename. 92 * 93 * Params: 94 * fname = destination filename 95 * ver = version of dextool 96 * custom = header appended last, intened for user customisation 97 */ 98 auto makeHeader(Path fname, DextoolVersion ver, CustomHeader custom = CustomHeader("")) { 99 import std.algorithm : splitter, map, joiner, copy; 100 import std.array : appender; 101 import std.ascii : newline; 102 import std.path : baseName; 103 import dsrcgen.cpp : CppModule; 104 105 auto base_fname = fname.baseName; 106 immutable string[string] kw = [ 107 "$file$" : base_fname, "$dextool_version$" : ver.get 108 ]; 109 110 auto m = new CppModule; 111 if (custom.length > 0) { 112 auto app = appender!string(); 113 // dfmt off 114 (cast(string) custom) 115 .splitter(newline) 116 .map!(a => a 117 .splitter(' ') 118 .map!((word) { 119 if (auto w = word in kw) return *w; 120 else return word; 121 }) 122 .map!(a => a.dup) 123 .joiner(" ") 124 ) 125 .joiner(newline) 126 .copy(app); 127 // dfmt on 128 129 m.text(app.data); 130 m.sep; 131 } else { 132 m.comment("@file " ~ base_fname)[$.begin = "/// "]; 133 m.comment("@brief Generated by DEXTOOL_VERSION: " ~ ver.get)[$.begin = "/// "]; 134 } 135 m.comment("DO NOT EDIT THIS FILE, it will be overwritten on update.")[$.begin = "/// "]; 136 137 return m; 138 } 139 140 version (unittest) { 141 @("The keyword enclosed in $...$ in the custom header is replaced with expected value") 142 unittest { 143 foreach (getValue; [["$file$", "a.h"], ["$dextool_version$", "1.0"]]) { 144 import unit_threaded : shouldEqual; 145 146 auto filename = Path("a.h"); 147 auto custom_header = CustomHeader("// " ~ getValue[0]); 148 auto version_ = DextoolVersion("1.0"); 149 150 makeHeader(filename, version_, custom_header).render.shouldEqual( 151 "// " ~ getValue[1] 152 ~ "\n /// DO NOT EDIT THIS FILE, it will be overwritten on update.\n"); 153 } 154 } 155 }