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 = ["$file$" : base_fname, "$dextool_version$" : ver]; 106 107 auto m = new CppModule; 108 if (custom.length > 0) { 109 auto app = appender!string(); 110 // dfmt off 111 (cast(string) custom) 112 .splitter(newline) 113 .map!(a => a 114 .splitter(' ') 115 .map!((word) { 116 if (auto w = word in kw) return *w; 117 else return word; 118 }) 119 .map!(a => a.dup) 120 .joiner(" ") 121 ) 122 .joiner(newline) 123 .copy(app); 124 // dfmt on 125 126 m.text(app.data); 127 m.sep; 128 } else { 129 m.comment("@file " ~ base_fname)[$.begin = "/// "]; 130 m.comment("@brief Generated by DEXTOOL_VERSION: " ~ ver)[$.begin = "/// "]; 131 } 132 m.comment("DO NOT EDIT THIS FILE, it will be overwritten on update.")[$.begin = "/// "]; 133 134 return m; 135 } 136 137 version (unittest) { 138 import unit_threaded : Values; 139 140 @("The keyword enclosed in $...$ in the custom header is replaced with expected value") 141 @Values([["$file$", "a.h"], ["$dextool_version$", "1.0"]]) 142 unittest { 143 import unit_threaded : shouldEqual, getValue; 144 145 auto filename = FileName("a.h"); 146 auto custom_header = CustomHeader("// " ~ getValue!(string[])[0]); 147 auto version_ = DextoolVersion("1.0"); 148 149 makeHeader(filename, version_, custom_header).render.shouldEqual("// " ~ getValue!( 150 string[])[1] 151 ~ "\n /// DO NOT EDIT THIS FILE, it will be overwritten on update.\n"); 152 } 153 }