1 /**
2  * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jun 15, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.IncludeHandler;
8 
9 import std.array : Appender;
10 
11 import clang.c.Index;
12 import clang.Cursor;
13 import clang.Util;
14 
15 import dstep.translator.HeaderIndex;
16 import dstep.translator.Options;
17 import dstep.translator.Output;
18 
19 class IncludeHandler
20 {
21     private Options options;
22     private string[string] submodules;
23     private bool[string] includes;
24     private bool[string] imports;
25     private HeaderIndex headerIndex;
26     immutable static string[string] knownIncludes;
27 
28     shared static this ()
29     {
30         knownIncludes = [
31             "complex" : "core.stdc.complex",
32             "config" : "core.stdc.config",
33             "ctype" : "core.stdc.ctype",
34             "errno" : "core.stdc.errno",
35             "fenv" : "core.stdc.fenv",
36             "float" : "core.stdc.float",
37             "inttypes" : "core.stdc.inttypes",
38             "limits" : "core.stdc.limits",
39             "locale" : "core.stdc.locale",
40             "math" : "core.stdc.math",
41             "signal" : "core.stdc.signal",
42             "stdarg" : "core.stdc.stdarg",
43             "stddef" : "core.stdc.stddef",
44             "stdint" : "core.stdc.stdint",
45             "_int8_t" : "core.stdc.stdint",
46             "_int16_t" : "core.stdc.stdint",
47             "_int32_t" : "core.stdc.stdint",
48             "_int64_t" : "core.stdc.stdint",
49             "_uint8_t" : "core.stdc.stdint",
50             "_uint16_t" : "core.stdc.stdint",
51             "_uint32_t" : "core.stdc.stdint",
52             "_uint64_t" : "core.stdc.stdint",
53             "stdio" : "core.stdc.stdio",
54             "_stdio" : "core.stdc.stdio",
55             "corecrt_wstdio" : "core.stdc.stdio",
56             "stdlib" : "core.stdc.stdlib",
57             "string" : "core.stdc.string",
58             "tgmath" : "core.stdc.tgmath",
59             "time" : "core.stdc.time",
60             "_time_t" : "core.stdc.time",
61             "corecrt" : "core.stdc.time",
62             "crtdefs" : "core.stdc.time",
63             "wchar" : "core.stdc.wchar_",
64             "wctype" : "core.stdc.wctype",
65 
66             "dirent" : "core.sys.posix.dirent",
67             "dlfcn" : "core.sys.posix.dlfcn",
68             "fcntl" : "core.sys.posix.fcntl",
69             "netdb" : "core.sys.posix.netdb",
70             "poll" : "core.sys.posix.poll",
71             "pthread" : "core.sys.posix.pthread",
72             "pwd" : "core.sys.posix.pwd",
73             "sched" : "core.sys.posix.sched",
74             "semaphore" : "core.sys.posix.semaphore",
75             "setjmp" : "core.sys.posix.setjmp",
76             "signal" : "core.sys.posix.signal",
77             "termios" : "core.sys.posix.termios",
78             "ucontext" : "core.sys.posix.ucontext",
79             "unistd" : "core.sys.posix.unistd",
80             "utime" : "core.sys.posix.utime",
81 
82             "arpa/inet" : "core.sys.posix.arpa.inet",
83 
84             "net/if" : "core.sys.posix.net.if_",
85 
86             "netinet/in" : "core.sys.posix.netinet.in_",
87             "netinet/tcp" : "core.sys.posix.netinet.tcp",
88 
89             "sys/ipc" : "core.sys.posix.sys.ipc",
90             "sys/mman" : "core.sys.posix.sys.mman",
91             "sys/select" : "core.sys.posix.sys.select",
92             "sys/shm" : "core.sys.posix.sys.shm",
93             "sys/socket" : "core.sys.posix.sys.socket",
94             "sys/stat" : "core.sys.posix.sys.stat",
95             "sys/time" : "core.sys.posix.sys.time",
96             "_time_t" : "core.stdc.time",
97             "sys/types" : "core.sys.posix.sys.types",
98             "sys/_types" : "core.sys.posix.sys.types",
99             "sys/uio" : "core.sys.posix.sys.uio",
100             "sys/un" : "core.sys.posix.sys.un",
101             "sys/utsname" : "core.sys.posix.sys.utsname",
102             "sys/wait" : "core.sys.posix.sys.wait",
103 
104             "windows" : "core.sys.windows.windows"
105         ];
106     }
107 
108     this (HeaderIndex headerIndex, Options options)
109     {
110         import std.format;
111         import std.algorithm : filter;
112 
113         this.headerIndex = headerIndex;
114         this.options = options;
115 
116         if (options.packageName != "")
117         {
118             auto inputFiles = options.inputFiles.filter!(
119                 x => x != options.inputFile);
120 
121             foreach (file; inputFiles)
122             {
123                 auto packageName = options.packageName;
124                 auto normalize = options.normalizeModules;
125                 submodules[file] = fullModuleName(packageName, file, normalize);
126             }
127         }
128     }
129 
130     void addInclude (string include)
131     {
132         import std.path;
133         import std.file;
134         import std.array;
135 
136         auto absolute = include.asAbsNormPath;
137 
138         if (absolute != options.inputFile && !include.empty)
139         {
140             if (exists(absolute) && isFile(absolute))
141                 includes[absolute] = true;
142             else
143                 includes[include] = true;
144         }
145     }
146 
147     void addImport (string imp)
148     {
149         imports[imp] = true;
150     }
151 
152     void addCompatible ()
153     {
154         includes["config.h"] = true;
155     }
156 
157     void toImports (Output output)
158     {
159         import std.algorithm : map;
160         import std.array : array;
161         import std.format : format;
162         import std.algorithm.iteration : filter, map;
163 
164         Set!string standard, package_, unhandled;
165 
166         foreach (entry; includes.byKey)
167         {
168             if (auto i = isKnownInclude(entry))
169                 standard.add(toImport(i));
170             else if (auto i = isPackageSubmodule(entry))
171                 package_.add(toSubmoduleImport(i));
172             else
173                 unhandled.add(format(`/+ #include "%s" +/`, entry));
174         }
175 
176         auto extra = imports.byKey.map!(e => toImport(e)).array;
177 
178         importsBlock(output, standard.keys ~ extra.array);
179         importsBlock(output, package_.keys);
180 
181         if (options.keepUntranslatable)
182             importsBlock(output, unhandled.keys);
183 
184         output.finalize();
185     }
186 
187     bool resolveDependency(in Cursor cursor)
188     {
189         auto module_ = headerIndex.searchKnownModules(cursor);
190 
191         if (module_ !is null)
192         {
193             addImport(module_);
194             return true;
195         }
196 
197         return false;
198     }
199 
200 private:
201 
202     void importsBlock(Output output, string[] imports)
203     {
204         import std.array : empty;
205         import std.algorithm : sort, filter;
206 
207         foreach (entry; imports.sort().filter!(e => !e.empty))
208             output.singleLine(entry);
209 
210         if (!output.empty)
211             output.separator();
212     }
213 
214     string toImport (string str)
215     {
216         return "import " ~ str ~ ";";
217     }
218 
219     string toSubmoduleImport (string str)
220     {
221         if (options.publicSubmodules)
222             return "public import " ~ str ~ ";";
223         else
224             return "import " ~ str ~ ";";
225     }
226 
227     string isKnownInclude (string include)
228     {
229         import std.path : stripExtension, baseName;
230 
231         include = stripExtension(baseName(include));
232 
233         if (auto ptr = include in knownIncludes)
234             return *ptr;
235         else
236             return null;
237     }
238 
239     string isPackageSubmodule (string include)
240     {
241         if (auto ptr = include in submodules)
242             return *ptr;
243         else
244             return null;
245     }
246 }