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+ /*
22+ Initializes the global variables of the DMD compiler.
23+ This needs to be done $(I before) calling any function.
1924*/
2025void initDMD ()
2126{
27+ import dmd.builtin : builtin_init;
2228 import dmd.dmodule : Module;
29+ import dmd.expression : Expression;
2330 import dmd.globals : global;
2431 import dmd.id : Id;
32+ import dmd.mars : addDefaultVersionIdentifiers;
2533 import dmd.mtype : Type;
26- import dmd.target : Target;
27- import dmd.expression : Expression;
2834 import dmd.objc : Objc;
29- import dmd.builtin : builtin_init ;
35+ import dmd.target : Target ;
3036
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." );
37+ global._init();
38+ addDefaultVersionIdentifiers();
4739
4840 Type._init();
4941 Id.initialize();
@@ -54,3 +46,235 @@ void initDMD()
5446 builtin_init();
5547}
5648
49+ /**
50+ Add import path to the `global.path`.
51+ Params:
52+ path = import to add
53+ */
54+ void addImport (string path)
55+ {
56+ import dmd.globals : global;
57+ import dmd.arraytypes : Strings;
58+ import std.string : toStringz;
59+
60+ if (global.path is null )
61+ global.path = new Strings();
62+
63+ global.path.push(path.toStringz);
64+ }
65+
66+ /**
67+ Searches for a `dmd.conf`.
68+
69+ Params:
70+ dmdFilePath = path to the current DMD executable
71+
72+ Returns: full path to the found `dmd.conf`, `null` otherwise.
73+ */
74+ string findDMDConfig (string dmdFilePath)
75+ {
76+ import dmd.dinifile : findConfFile;
77+ import std.string : fromStringz, toStringz;
78+
79+ auto f = findConfFile(dmdFilePath.toStringz, " dmd.conf" );
80+ if (f is null )
81+ return null ;
82+
83+ return f.fromStringz.idup;
84+ }
85+
86+ /**
87+ Searches for a `ldc2.conf`.
88+
89+ Params:
90+ ldcFilePath = path to the current LDC executable
91+
92+ Returns: full path to the found `ldc2.conf`, `null` otherwise.
93+ */
94+ string findLDCConfig (string ldcFilePath)
95+ {
96+ import std.file : getcwd;
97+ import std.path : buildPath, dirName;
98+ import std.algorithm.iteration : filter;
99+ import std.file : exists;
100+
101+ auto execDir = ldcFilePath.dirName;
102+
103+ immutable ldcConfig = " ldc2.conf" ;
104+ // https://wiki.dlang.org/Using_LDC
105+ auto ldcConfigs = [
106+ getcwd.buildPath(ldcConfig),
107+ execDir.buildPath(ldcConfig),
108+ execDir.dirName.buildPath(" etc" , ldcConfig),
109+ " ~/.ldc" .buildPath(ldcConfig),
110+ execDir.buildPath(" etc" , ldcConfig),
111+ execDir.buildPath(" etc" , " ldc" , ldcConfig),
112+ " /etc" .buildPath(ldcConfig),
113+ " /etc/ldc" .buildPath(ldcConfig),
114+ ].filter! exists;
115+ if (ldcConfigs.empty)
116+ return null ;
117+
118+ return ldcConfigs.front;
119+ }
120+
121+ /**
122+ Detect the currently active compiler.
123+ Returns: full path to the executable of the found compiler, `null` otherwise.
124+ */
125+ // TODO: use a more fault-proof detection
126+ string findCompiler ()
127+ {
128+ import std.process : env = environment, execute;
129+
130+ auto dmdEnv = env.get (" DMD" , " dmd" );
131+ auto whichDMD = execute([" which" , dmdEnv]);
132+ if (whichDMD.status != 0 )
133+ return null ;
134+
135+ return whichDMD.output;
136+ }
137+
138+ /**
139+ Parses a `dmd.conf` or `ldc2.conf` config file and returns defined import paths.
140+
141+ Params:
142+ iniFile = iniFile to parse imports from
143+ execDir = directory of the compiler binary
144+
145+ Returns: forward range of import paths found in `iniFile`
146+ */
147+ auto parseImportPathsFromConfig (string iniFile, string execDir)
148+ {
149+ import std.algorithm , std.range , std.regex ;
150+ import std.stdio : File ;
151+ import std.path : buildNormalizedPath;
152+
153+ alias expandConfigVariables = a => a.drop(2 ) // -I
154+ // "set" common config variables
155+ .replace(" %@P%" , execDir)
156+ .replace(" %%ldcbinarypath%%" , execDir);
157+
158+ // search for all -I imports in this file
159+ alias searchForImports = l => l.matchAll(` -I[^ "]+` .regex).joiner.map! expandConfigVariables;
160+
161+ return File (iniFile, " r" )
162+ .byLineCopy
163+ .map! searchForImports
164+ .joiner
165+ // remove duplicated imports paths
166+ .array
167+ .sort
168+ .uniq
169+ .map! buildNormalizedPath;
170+ }
171+
172+ /**
173+ Finds a dmd.conf and parses it for import paths.
174+ This depends on the `$DMD` environment variable.
175+ If it's set to `ldmd`, it will try to detect and parse an `ldc2.conf` instead.
176+
177+ Returns:
178+ A forward range of normalized import paths.
179+ */
180+ auto findImportPaths ()
181+ {
182+ import std.algorithm.searching : canFind;
183+ import std.file : exists;
184+ import std.path : dirName;
185+
186+ string execFilePath = findCompiler;
187+ assert (execFilePath ! is null , " No D compiler found." );
188+
189+ immutable execDir = execFilePath.dirName;
190+
191+ string iniFile;
192+ if (execFilePath.canFind(" ldmd" ) || execFilePath.canFind(" ldc" ))
193+ iniFile = findLDCConfig(execFilePath);
194+ else
195+ iniFile = findDMDConfig(execFilePath);
196+
197+ assert (iniFile ! is null , " No valid config found." );
198+ assert (iniFile.exists, " No valid config found." );
199+ return iniFile.parseImportPathsFromConfig(execDir);
200+ }
201+
202+ /**
203+ Parse a module from a string.
204+
205+ Params:
206+ fileName = file to parse
207+ code = text to use instead of opening the file
208+
209+ Returns: the parsed module object
210+ */
211+ Module parseModule (string fileName, string code = null )
212+ {
213+ import dmd.astcodegen : ASTCodegen;
214+ import dmd.globals : Loc;
215+ import dmd.parse : Parser;
216+ import dmd.statement : Identifier;
217+ import dmd.tokens : TOKeof;
218+ import std.string : toStringz;
219+
220+ auto parse (Module m, string code)
221+ {
222+ scope p = new Parser! ASTCodegen(m, code, false );
223+ p.nextToken; // skip the initial token
224+ auto members = p.parseModule;
225+ assert (! p.errors, " Parsing error occurred." );
226+ assert (p.token.value == TOKeof, " Didn't reach the end token. Did an error occur?" );
227+ return members;
228+ }
229+
230+ Identifier id = Identifier.idPool(fileName);
231+ auto m = new Module(fileName.toStringz, id, 0 , 0 );
232+ if (code ! is null )
233+ m.members = parse(m, code);
234+ else
235+ {
236+ m.read(Loc());
237+ m.parse();
238+ }
239+
240+ return m;
241+ }
242+
243+ /**
244+ Run full semantic analysis on a module.
245+ */
246+ void fullSemantic (Module m)
247+ {
248+ import dmd.dsymbolsem : dsymbolSemantic;
249+ import dmd.semantic2 : semantic2;
250+ import dmd.semantic3 : semantic3;
251+
252+ m.importedFrom = m;
253+ m.importAll(null );
254+ m.dsymbolSemantic(null );
255+ m.semantic2(null );
256+ m.semantic3(null );
257+ }
258+
259+ /**
260+ Pretty print a module.
261+
262+ Returns:
263+ Pretty printed module as string.
264+ */
265+ string prettyPrint (Module m)
266+ {
267+ import dmd.root.outbuffer: OutBuffer ;
268+ import dmd.hdrgen : HdrGenState, PrettyPrintVisitor;
269+
270+ OutBuffer buf = { doindent: 1 };
271+ HdrGenState hgs = { fullDump: 1 };
272+ scope PrettyPrintVisitor ppv = new PrettyPrintVisitor(&buf, &hgs);
273+ m.accept(ppv);
274+
275+ import std.string : replace, fromStringz;
276+ import std.exception : assumeUnique;
277+
278+ auto generated = buf.extractData.fromStringz.replace(" \t " , " " );
279+ return generated.assumeUnique;
280+ }
0 commit comments