Skip to content

Commit f102704

Browse files
committed
Move frontend utility functions from the DUB packag to dmd.frontend
1 parent 4884f2c commit f102704

4 files changed

Lines changed: 312 additions & 151 deletions

File tree

src/dmd/frontend.d

Lines changed: 245 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,36 +14,28 @@
1414
*/
1515
module 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
*/
2025
void 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+
}

src/dmd/mars.d

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,7 @@ private void setDefaultLibrary()
13111311
* variable and config file) in order to add final flags (such as `X86_64` or
13121312
* the `CRuntime` used).
13131313
*/
1314-
private void addDefaultVersionIdentifiers()
1314+
void addDefaultVersionIdentifiers()
13151315
{
13161316
VersionCondition.addPredefinedGlobalIdent("DigitalMars");
13171317
static if (TARGET_WINDOS)

0 commit comments

Comments
 (0)