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