1414 */
1515module dmd.frontend ;
1616
17- /**
18- Initializes the DMD compiler
17+ import dmd.dmodule : Module;
18+ import std.range.primitives : isInputRange, ElementType;
19+ import std.traits : isNarrowString;
20+
21+ version (Windows ) private enum sep = " ;" , exe = " .exe" ;
22+ version (Posix ) private enum sep = " :" , exe = " " ;
23+
24+ /*
25+ Initializes the global variables of the DMD compiler.
26+ This needs to be done $(I before) calling any function.
1927*/
2028void initDMD ()
2129{
30+ import dmd.builtin : builtin_init;
2231 import dmd.dmodule : Module;
32+ import dmd.expression : Expression;
2333 import dmd.globals : global;
2434 import dmd.id : Id;
35+ import dmd.mars : addDefaultVersionIdentifiers;
2536 import dmd.mtype : Type;
26- import dmd.target : Target;
27- import dmd.expression : Expression;
2837 import dmd.objc : Objc;
29- import dmd.builtin : builtin_init ;
38+ import dmd.target : Target ;
3039
31- global._init;
32-
33- version (linux )
34- global.params.isLinux = 1 ;
35- else version (OSX )
36- global.params.isOSX = 1 ;
37- else version (FreeBSD )
38- global.params.isFreeBSD = 1 ;
39- else version (Windows )
40- global.params.isWindows = 1 ;
41- else version (Solaris )
42- global.params.isSolaris = 1 ;
43- else version (OpenBSD )
44- global.params.isOpenBSD = 1 ;
45- else
46- static assert (0 , " OS not supported yet." );
40+ global._init();
41+ addDefaultVersionIdentifiers();
4742
4843 Type._init();
4944 Id.initialize();
@@ -54,3 +49,242 @@ void initDMD()
5449 builtin_init();
5550}
5651
52+ /**
53+ Add import path to the `global.path`.
54+ Params:
55+ path = import to add
56+ */
57+ void addImport (string path)
58+ {
59+ import dmd.globals : global;
60+ import dmd.arraytypes : Strings;
61+ import std.string : toStringz;
62+
63+ if (global.path is null )
64+ global.path = new Strings();
65+
66+ global.path.push(path.toStringz);
67+ }
68+
69+ /**
70+ Searches for a `dmd.conf`.
71+
72+ Params:
73+ dmdFilePath = path to the current DMD executable
74+
75+ Returns: full path to the found `dmd.conf`, `null` otherwise.
76+ */
77+ string findDMDConfig (string dmdFilePath)
78+ {
79+ import dmd.dinifile : findConfFile;
80+ import std.string : fromStringz, toStringz;
81+
82+ auto f = findConfFile(dmdFilePath.toStringz, " dmd.conf" );
83+ if (f is null )
84+ return null ;
85+
86+ return f.fromStringz.idup;
87+ }
88+
89+ /**
90+ Searches for a `ldc2.conf`.
91+
92+ Params:
93+ ldcFilePath = path to the current LDC executable
94+
95+ Returns: full path to the found `ldc2.conf`, `null` otherwise.
96+ */
97+ string findLDCConfig (string ldcFilePath)
98+ {
99+ import std.file : getcwd;
100+ import std.path : buildPath, dirName;
101+ import std.algorithm.iteration : filter;
102+ import std.file : exists;
103+
104+ auto execDir = ldcFilePath.dirName;
105+
106+ immutable ldcConfig = " ldc2.conf" ;
107+ // https://wiki.dlang.org/Using_LDC
108+ auto ldcConfigs = [
109+ getcwd.buildPath(ldcConfig),
110+ execDir.buildPath(ldcConfig),
111+ execDir.dirName.buildPath(" etc" , ldcConfig),
112+ " ~/.ldc" .buildPath(ldcConfig),
113+ execDir.buildPath(" etc" , ldcConfig),
114+ execDir.buildPath(" etc" , " ldc" , ldcConfig),
115+ " /etc" .buildPath(ldcConfig),
116+ " /etc/ldc" .buildPath(ldcConfig),
117+ ].filter! exists;
118+ if (ldcConfigs.empty)
119+ return null ;
120+
121+ return ldcConfigs.front;
122+ }
123+
124+ /**
125+ Detect the currently active compiler.
126+ Returns: full path to the executable of the found compiler, `null` otherwise.
127+ */
128+ string determineDefaultCompiler ()
129+ {
130+ import std.algorithm.iteration : filter, joiner, map, splitter;
131+ import std.file : exists;
132+ import std.path : buildPath;
133+ import std.process : environment;
134+ import std.range : front, empty, transposed;
135+ // adapted from DUB: https://github.com/dlang/dub/blob/350a0315c38fab9d3d0c4c9d30ff6bb90efb54d6/source/dub/dub.d#L1183
136+
137+ auto compilers = [" dmd" , " gdc" , " gdmd" , " ldc2" , " ldmd2" ];
138+
139+ // Search the user's PATH for the compiler binary
140+ if (" DMD" in environment)
141+ compilers = environment.get (" DMD" ) ~ compilers;
142+ auto paths = environment.get (" PATH" , " " ).splitter(sep);
143+ auto res = compilers.map! (c => paths.map! (p => p.buildPath(c~ exe))).joiner.filter! exists;
144+ return ! res.empty ? res.front : null ;
145+ }
146+
147+ /**
148+ Parses a `dmd.conf` or `ldc2.conf` config file and returns defined import paths.
149+
150+ Params:
151+ iniFile = iniFile to parse imports from
152+ execDir = directory of the compiler binary
153+
154+ Returns: forward range of import paths found in `iniFile`
155+ */
156+ auto parseImportPathsFromConfig (string iniFile, string execDir)
157+ {
158+ import std.algorithm , std.range , std.regex ;
159+ import std.stdio : File ;
160+ import std.path : buildNormalizedPath;
161+
162+ alias expandConfigVariables = a => a.drop(2 ) // -I
163+ // "set" common config variables
164+ .replace(" %@P%" , execDir)
165+ .replace(" %%ldcbinarypath%%" , execDir);
166+
167+ // search for all -I imports in this file
168+ alias searchForImports = l => l.matchAll(` -I[^ "]+` .regex).joiner.map! expandConfigVariables;
169+
170+ return File (iniFile, " r" )
171+ .byLineCopy
172+ .map! searchForImports
173+ .joiner
174+ // remove duplicated imports paths
175+ .array
176+ .sort
177+ .uniq
178+ .map! buildNormalizedPath;
179+ }
180+
181+ /**
182+ Finds a `dmd.conf` and parses it for import paths.
183+ This depends on the `$DMD` environment variable.
184+ If `$DMD` is set to `ldmd`, it will try to detect and parse a `ldc2.conf` instead.
185+
186+ Returns:
187+ A forward range of normalized import paths.
188+
189+ See_Also: $(LREF determineDefaultCompiler), $(LREF parseImportPathsFromConfig)
190+ */
191+ auto findImportPaths ()
192+ {
193+ import std.algorithm.searching : endsWith;
194+ import std.file : exists;
195+ import std.path : dirName;
196+
197+ string execFilePath = determineDefaultCompiler();
198+ assert (execFilePath ! is null , " No D compiler found. `Use parseImportsFromConfig` manually." );
199+
200+ immutable execDir = execFilePath.dirName;
201+
202+ string iniFile;
203+ if (execFilePath.endsWith(" ldc" ~ exe, " ldc2" ~ exe, " ldmd" ~ exe, " ldmd2" ~ exe))
204+ iniFile = findLDCConfig(execFilePath);
205+ else
206+ iniFile = findDMDConfig(execFilePath);
207+
208+ assert (iniFile ! is null && iniFile.exists, " No valid config found." );
209+ return iniFile.parseImportPathsFromConfig(execDir);
210+ }
211+
212+ /**
213+ Parse a module from a string.
214+
215+ Params:
216+ fileName = file to parse
217+ code = text to use instead of opening the file
218+
219+ Returns: the parsed module object
220+ */
221+ Module parseModule (string fileName, string code = null )
222+ {
223+ import dmd.astcodegen : ASTCodegen;
224+ import dmd.globals : Loc;
225+ import dmd.parse : Parser;
226+ import dmd.statement : Identifier;
227+ import dmd.tokens : TOKeof;
228+ import std.string : toStringz;
229+
230+ auto parse (Module m, string code)
231+ {
232+ scope p = new Parser! ASTCodegen(m, code, false );
233+ p.nextToken; // skip the initial token
234+ auto members = p.parseModule;
235+ assert (! p.errors, " Parsing error occurred." );
236+ assert (p.token.value == TOKeof, " Didn't reach the end token. Did an error occur?" );
237+ return members;
238+ }
239+
240+ Identifier id = Identifier.idPool(fileName);
241+ auto m = new Module(fileName.toStringz, id, 0 , 0 );
242+ if (code ! is null )
243+ m.members = parse(m, code);
244+ else
245+ {
246+ m.read(Loc());
247+ m.parse();
248+ }
249+
250+ return m;
251+ }
252+
253+ /**
254+ Run full semantic analysis on a module.
255+ */
256+ void fullSemantic (Module m)
257+ {
258+ import dmd.dsymbolsem : dsymbolSemantic;
259+ import dmd.semantic2 : semantic2;
260+ import dmd.semantic3 : semantic3;
261+
262+ m.importedFrom = m;
263+ m.importAll(null );
264+ m.dsymbolSemantic(null );
265+ m.semantic2(null );
266+ m.semantic3(null );
267+ }
268+
269+ /**
270+ Pretty print a module.
271+
272+ Returns:
273+ Pretty printed module as string.
274+ */
275+ string prettyPrint (Module m)
276+ {
277+ import dmd.root.outbuffer: OutBuffer ;
278+ import dmd.hdrgen : HdrGenState, PrettyPrintVisitor;
279+
280+ OutBuffer buf = { doindent: 1 };
281+ HdrGenState hgs = { fullDump: 1 };
282+ scope PrettyPrintVisitor ppv = new PrettyPrintVisitor(&buf, &hgs);
283+ m.accept(ppv);
284+
285+ import std.string : replace, fromStringz;
286+ import std.exception : assumeUnique;
287+
288+ auto generated = buf.extractData.fromStringz.replace(" \t " , " " );
289+ return generated.assumeUnique;
290+ }
0 commit comments