0
|
1 module dmd.Util;
|
|
2
|
|
3 import dmd.Loc;
|
|
4 import dmd.Library;
|
|
5 import dmd.File;
|
|
6 import dmd.String;
|
|
7 import dmd.OutBuffer;
|
|
8 import dmd.FileName;
|
|
9 import dmd.Global;
|
|
10 import dmd.PREC;
|
|
11 import dmd.TOK;
|
|
12
|
|
13 import std.process : getenv;
|
|
14 import std.c.string;
|
|
15 import std.stdio : writef, writefln, write;
|
|
16 import std.c.process : spawnl, spawnlp;
|
|
17 import core.stdc.stdlib;
|
|
18 import core.stdc.ctype;
|
|
19 import core.stdc.stdarg;
|
|
20 import core.stdc.stdio;
|
|
21
|
|
22 extern(C) int putenv(char*);
|
|
23
|
|
24 //version = LOG;
|
|
25
|
|
26 version (Windows) {
|
|
27 } else {
|
|
28 import core.sys.posix.stdlib : putenv;
|
|
29 }
|
|
30
|
|
31 enum MAX_PATH = 256; ///
|
|
32
|
|
33 version (Windows) {
|
|
34 import core.sys.windows.windows : GetModuleFileNameA;
|
|
35 }
|
|
36
|
|
37 string fromStringz(const(char)* s)
|
|
38 {
|
|
39 return s[0..strlen(s)].idup;
|
|
40 }
|
|
41
|
|
42 void warning(T...)(string format, T t)
|
|
43 {
|
|
44 assert(false);
|
|
45 }
|
|
46
|
|
47 void warning(T...)(Loc loc, string format, T t)
|
|
48 {
|
|
49 assert(false);
|
|
50 }
|
|
51
|
|
52 void error(T...)(Loc loc, string format, T t)
|
|
53 {
|
|
54 if (!global.gag)
|
|
55 {
|
|
56 string p = loc.toChars();
|
|
57
|
|
58 if (p.length != 0)
|
|
59 writef("%s: ", p);
|
|
60
|
|
61 write("Error: ");
|
|
62 writefln(format, t);
|
|
63
|
|
64 //halt();
|
|
65 }
|
|
66 global.errors++;
|
|
67 }
|
|
68
|
|
69 char* strupr(char* s)
|
|
70 {
|
|
71 char* t = s;
|
|
72
|
|
73 while (*s)
|
|
74 {
|
|
75 *s = cast(char)toupper(*s);
|
|
76 s++;
|
|
77 }
|
|
78
|
|
79 return t;
|
|
80 }
|
|
81
|
|
82 char[] skipspace(char[] p)
|
|
83 {
|
|
84 foreach (i, c; p) {
|
|
85 if (!isspace(c)) {
|
|
86 return p[i..$];
|
|
87 }
|
|
88 }
|
|
89
|
|
90 return null;
|
|
91 }
|
|
92
|
|
93 char* skipspace(char* p)
|
|
94 {
|
|
95 while (isspace(*p))
|
|
96 p++;
|
|
97
|
|
98 return p;
|
|
99 }
|
|
100
|
|
101 void inifile(string argv0, string inifile)
|
|
102 {
|
|
103 char *path; // need path for @P macro
|
|
104 string filename;
|
|
105 int i;
|
|
106 int k;
|
|
107 int envsection = 0;
|
|
108
|
|
109 version (LOG) {
|
|
110 writef("inifile(argv0 = '%s', inifile = '%s')\n", argv0, inifile);
|
|
111 }
|
|
112 if (FileName.absolute(inifile)) {
|
|
113 filename = inifile;
|
|
114 } else {
|
|
115 /* Look for inifile in the following sequence of places:
|
|
116 * o current directory
|
|
117 * o home directory
|
|
118 * o directory off of argv0
|
|
119 * o /etc/
|
|
120 */
|
|
121 if (FileName.exists(inifile)) {
|
|
122 filename = inifile;
|
|
123 } else {
|
|
124 filename = FileName.combine(getenv("HOME"), inifile);
|
|
125 if (!FileName.exists(filename)) {
|
|
126 version (_WIN32) { // This fix by Tim Matthews
|
|
127 char resolved_name_b[MAX_PATH + 1];
|
|
128 auto resolved_name = resolved_name_b[].idup;
|
|
129 if (GetModuleFileNameA(null, resolved_name_b.ptr, MAX_PATH + 1) && FileName.exists(resolved_name))
|
|
130 {
|
|
131 filename = FileName.replaceName(resolved_name, inifile);
|
|
132 if (FileName.exists(filename)) {
|
|
133 goto Ldone;
|
|
134 }
|
|
135 }
|
|
136 }
|
|
137 filename = FileName.replaceName(argv0, inifile);
|
|
138 if (!FileName.exists(filename)) {
|
|
139 version (XXX) { /// linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
|
|
140 version (XXX) { /// __GLIBC__ || __APPLE__ || __FreeBSD__ || __sun&&__SVR4 // This fix by Thomas Kuehne
|
|
141 /* argv0 might be a symbolic link,
|
|
142 * so try again looking past it to the real path
|
|
143 */
|
|
144 version (XXX) {/// #if __APPLE__ || __FreeBSD__ || __sun&&__SVR4
|
|
145 char resolved_name[PATH_MAX + 1];
|
|
146 char* real_argv0 = realpath(argv0, resolved_name);
|
|
147 } else {
|
|
148 char* real_argv0 = realpath(argv0, null);
|
|
149 }
|
|
150 //printf("argv0 = %s, real_argv0 = %p\n", argv0, real_argv0);
|
|
151 if (real_argv0) {
|
|
152 filename = FileName.replaceName(real_argv0, inifile);
|
|
153 version (linux) {
|
|
154 free(real_argv0);
|
|
155 }
|
|
156 if (FileName.exists(filename)) {
|
|
157 goto Ldone;
|
|
158 }
|
|
159 }
|
|
160 } else {
|
|
161 static assert (false, "use of glibc non-standard extension realpath(char*, null)");
|
|
162 }
|
|
163 if (true) {
|
|
164 // Search PATH for argv0
|
|
165 const(char)* p = getenv("PATH");
|
|
166 version (LOG) {
|
|
167 writef("\tPATH='%s'\n", p);
|
|
168 }
|
|
169 Array paths = FileName.splitPath(p);
|
|
170 filename = FileName.searchPath(paths, argv0, 0);
|
|
171 if (!filename) {
|
|
172 goto Letc; // argv0 not found on path
|
|
173 }
|
|
174
|
|
175 filename = FileName.replaceName(filename, inifile);
|
|
176 if (FileName.exists(filename)) {
|
|
177 goto Ldone;
|
|
178 }
|
|
179 }
|
|
180 }
|
|
181 // Search /etc/ for inifile
|
|
182 Letc:
|
|
183 filename = FileName.combine("/etc/", inifile);
|
|
184
|
|
185 Ldone:
|
|
186 ;
|
|
187 }
|
|
188 }
|
|
189 }
|
|
190 }
|
|
191
|
|
192 path = cast(char*)toStringz(FileName.path(filename));
|
|
193
|
|
194 version (LOG) {
|
|
195 writef("\tpath = '%s', filename = '%s'\n", fromStringz(path), filename);
|
|
196 }
|
|
197
|
|
198 scope File file = new File(filename);
|
|
199
|
|
200 if (file.read()) {
|
|
201 return; // error reading file
|
|
202 }
|
|
203
|
|
204 scope OutBuffer buf = new OutBuffer();
|
|
205
|
|
206 // Parse into lines
|
|
207 int eof = 0;
|
|
208 for (i = 0; i < file.len && !eof; i++)
|
|
209 {
|
|
210 int linestart = i;
|
|
211
|
|
212 for (; i < file.len; i++)
|
|
213 {
|
|
214 switch (file.buffer[i])
|
|
215 {
|
|
216 case '\r':
|
|
217 break;
|
|
218
|
|
219 case '\n':
|
|
220 // Skip if it was preceded by '\r'
|
|
221 if (i && file.buffer[i - 1] == '\r')
|
|
222 goto Lskip;
|
|
223 break;
|
|
224
|
|
225 case 0:
|
|
226 case 0x1A:
|
|
227 eof = 1;
|
|
228 break;
|
|
229
|
|
230 default:
|
|
231 continue;
|
|
232 }
|
|
233 break;
|
|
234 }
|
|
235
|
|
236 // The line is file.buffer[linestart..i]
|
|
237 char *line;
|
|
238 int len;
|
|
239 char *p;
|
|
240 char* pn;
|
|
241
|
|
242 line = cast(char*)&file.buffer[linestart];
|
|
243 len = i - linestart;
|
|
244
|
|
245 buf.reset();
|
|
246
|
|
247 // First, expand the macros.
|
|
248 // Macros are bracketed by % characters.
|
|
249
|
|
250 for (k = 0; k < len; k++)
|
|
251 {
|
|
252 if (line[k] == '%')
|
|
253 {
|
|
254 int j;
|
|
255
|
|
256 for (j = k + 1; j < len; j++)
|
|
257 {
|
|
258 if (line[j] == '%')
|
|
259 {
|
|
260 if (j - k == 3 && memicmp(&line[k + 1], "@P", 2) == 0)
|
|
261 {
|
|
262 // %@P% is special meaning the path to the .ini file
|
|
263 p = path;
|
|
264 if (!*p)
|
|
265 p = cast(char*)".";
|
|
266 }
|
|
267 else
|
|
268 {
|
|
269 int l = j - k;
|
|
270 char tmp[10]; // big enough most of the time
|
|
271
|
|
272 if (l <= tmp.sizeof)
|
|
273 p = tmp.ptr;
|
|
274 else
|
|
275 p = cast(char*)alloca(l);
|
|
276 l--;
|
|
277 memcpy(p, &line[k + 1], l);
|
|
278 p[l] = 0;
|
|
279 strupr(p);
|
|
280 p = core.stdc.stdlib.getenv(p);
|
|
281 if (!p)
|
|
282 p = cast(char*)"";
|
|
283 }
|
|
284 buf.writestring(p[0..strlen(p)]); ///
|
|
285 k = j;
|
|
286 goto L1;
|
|
287 }
|
|
288 }
|
|
289 }
|
|
290 buf.writeByte(line[k]);
|
|
291 L1:
|
|
292 ;
|
|
293 }
|
|
294
|
|
295 // Remove trailing spaces
|
|
296 while (buf.offset && isspace(buf.data[buf.offset - 1]))
|
|
297 buf.offset--;
|
|
298
|
|
299 char[] pp = buf.getString();
|
|
300
|
|
301 // The expanded line is in p.
|
|
302 // Now parse it for meaning.
|
|
303
|
|
304 pp = skipspace(pp);
|
|
305 if (pp.length != 0) {
|
|
306 switch (pp[0])
|
|
307 {
|
|
308 case ';': // comment
|
|
309 break;
|
|
310
|
|
311 case '[': // look for [Environment]
|
|
312 pp = skipspace(pp[1..$]);
|
|
313 for (pn = pp.ptr; isalnum(*pn); pn++) {
|
|
314 ;
|
|
315 }
|
|
316
|
|
317 if (pn - pp.ptr == 11 &&
|
|
318 memicmp(pp.ptr, "Environment", 11) == 0 &&
|
|
319 *skipspace(pn) == ']'
|
|
320 )
|
|
321 envsection = 1;
|
|
322 else
|
|
323 envsection = 0;
|
|
324 break;
|
|
325
|
|
326 default:
|
|
327 if (envsection)
|
|
328 {
|
|
329 pn = pp.ptr;
|
|
330
|
|
331 // Convert name to upper case;
|
|
332 // remove spaces bracketing =
|
|
333 auto p2 = pn;
|
|
334 for ( ; *p2; p2++)
|
|
335 { if (islower(*p2))
|
|
336 *p2 &= ~0x20;
|
|
337 else if (isspace(*p))
|
|
338 memmove(p2, p2 + 1, strlen(p2));
|
|
339 else if (*p2 == '=')
|
|
340 {
|
|
341 p2++;
|
|
342 while (isspace(*p2))
|
|
343 memmove(p2, p2 + 1, strlen(p2));
|
|
344 break;
|
|
345 }
|
|
346 }
|
|
347
|
|
348 //putenv(pn);
|
|
349 putenv(cast(char*)toStringz(pp));
|
|
350
|
|
351 version (LOG) {
|
|
352 writef("\tputenv('%s')\n", pn[0..strlen(pn)]);
|
|
353 //printf("getenv(\"TEST\") = '%s'\n",getenv("TEST"));
|
|
354 }
|
|
355 }
|
|
356 break;
|
|
357 }
|
|
358 }
|
|
359
|
|
360 Lskip:
|
|
361 ;
|
|
362 }
|
|
363 }
|
|
364
|
|
365 ///int response_expand(int *pargc, char ***pargv);
|
|
366 void browse(const(char)* url)
|
|
367 {
|
|
368 assert(false);
|
|
369 }
|
|
370
|
|
371 string[] getenv_setargv(string envvar, string[] args)
|
|
372 {
|
|
373 char *p;
|
|
374
|
|
375 string[] argv = args.dup;
|
|
376 int argc = args.length;
|
|
377
|
|
378 int wildcard; // do wildcard expansion
|
|
379 int instring;
|
|
380 int slash;
|
|
381 char c;
|
|
382 int j;
|
|
383
|
|
384 string ienv = getenv(envvar);
|
|
385 if (ienv is null)
|
|
386 return args;
|
|
387
|
|
388 char[] env = ienv.dup; // create our own writable copy
|
|
389
|
|
390 j = 1; // leave argv[0] alone
|
|
391 char* e = env.ptr;
|
|
392 while (1)
|
|
393 {
|
|
394 wildcard = 1;
|
|
395 switch (*e)
|
|
396 {
|
|
397 case ' ':
|
|
398 case '\t':
|
|
399 e++;
|
|
400 break;
|
|
401
|
|
402 case 0:
|
|
403 goto Ldone;
|
|
404
|
|
405 case '"':
|
|
406 wildcard = 0;
|
|
407 default:
|
|
408 argv ~= assumeUnique(e[0..strlen(e)]); // append
|
|
409 //argv.insert(j, env); // insert at position j
|
|
410 j++;
|
|
411 argc++;
|
|
412 p = e;
|
|
413 slash = 0;
|
|
414 instring = 0;
|
|
415 c = 0;
|
|
416
|
|
417 char* ecopy = e;
|
|
418
|
|
419 while (1)
|
|
420 {
|
|
421 c = *e++;
|
|
422 switch (c)
|
|
423 {
|
|
424 case '"':
|
|
425 p -= (slash >> 1);
|
|
426 if (slash & 1)
|
|
427 {
|
|
428 p--;
|
|
429 goto Laddc;
|
|
430 }
|
|
431 instring ^= 1;
|
|
432 slash = 0;
|
|
433 continue;
|
|
434
|
|
435 case '\\':
|
|
436 slash++;
|
|
437 *p++ = c;
|
|
438 continue;
|
|
439
|
|
440 case ' ':
|
|
441 case '\t':
|
|
442 if (instring)
|
|
443 goto Laddc;
|
|
444 case 0:
|
|
445 *p = 0;
|
|
446 if (argv.length != 0) {
|
|
447 argv[$-1].length = p - argv[$-1].ptr;
|
|
448 }
|
|
449 //if (wildcard)
|
|
450 //wildcardexpand(); // not implemented
|
|
451 if (c == 0) goto Ldone;
|
|
452 break;
|
|
453
|
|
454 default:
|
|
455 Laddc:
|
|
456 slash = 0;
|
|
457 *p++ = c;
|
|
458 continue;
|
|
459 }
|
|
460 break;
|
|
461 }
|
|
462 }
|
|
463 }
|
|
464
|
|
465 Ldone:
|
|
466 return argv;
|
|
467 }
|
|
468
|
|
469 void error(T...)(string format, T t)
|
|
470 {
|
|
471 writefln(format, t);
|
|
472 exit(EXIT_FAILURE);
|
|
473 }
|
|
474
|
|
475 void usage()
|
|
476 {
|
|
477 writef("Digital Mars D Compiler %s\n%s %s\n", global.version_, global.copyright, global.written);
|
|
478 writef(
|
|
479 "Documentation: http://www.digitalmars.com/d/2.0/index.html\n"
|
|
480 "Usage:\n"
|
|
481 " dmd files.d ... { -switch }\n"
|
|
482 "\n"
|
|
483 " files.d D source files\n"
|
|
484 " @cmdfile read arguments from cmdfile\n"
|
|
485 " -c do not link\n"
|
|
486 " -cov do code coverage analysis\n"
|
|
487 " -D generate documentation\n"
|
|
488 " -Dddocdir write documentation file to docdir directory\n"
|
|
489 " -Dffilename write documentation file to filename\n"
|
|
490 " -d allow deprecated features\n"
|
|
491 " -debug compile in debug code\n"
|
|
492 " -debug=level compile in debug code <= level\n"
|
|
493 " -debug=ident compile in debug code identified by ident\n"
|
|
494 " -debuglib=name set symbolic debug library to name\n"
|
|
495 " -defaultlib=name set default library to name\n"
|
|
496 " -deps=filename write module dependencies to filename\n"
|
|
497 " -g add symbolic debug info\n"
|
|
498 " -gc add symbolic debug info, pretend to be C\n"
|
|
499 " -H generate 'header' file\n"
|
|
500 " -Hdhdrdir write 'header' file to hdrdir directory\n"
|
|
501 " -Hffilename write 'header' file to filename\n"
|
|
502 " --help print help\n"
|
|
503 " -Ipath where to look for imports\n"
|
|
504 " -ignore ignore unsupported pragmas\n"
|
|
505 " -inline do function inlining\n"
|
|
506 " -Jpath where to look for string imports\n"
|
|
507 " -Llinkerflag pass linkerflag to link\n"
|
|
508 " -lib generate library rather than object files\n"
|
|
509 " -man open web browser on manual page\n"
|
|
510 " -nofloat do not emit reference to floating point\n"
|
|
511 " -O optimize\n"
|
|
512 " -o- do not write object file\n"
|
|
513 " -odobjdir write object & library files to directory objdir\n"
|
|
514 " -offilename name output file to filename\n"
|
|
515 " -op do not strip paths from source file\n"
|
|
516 " -profile profile runtime performance of generated code\n"
|
|
517 " -quiet suppress unnecessary messages\n"
|
|
518 " -release compile release version\n"
|
|
519 " -run srcfile args... run resulting program, passing args\n"
|
|
520 " -safe safe memory model\n"
|
|
521 " -unittest compile in unit tests\n"
|
|
522 " -v verbose\n"
|
|
523 " -version=level compile in version code >= level\n"
|
|
524 " -version=ident compile in version code identified by ident\n"
|
|
525 " -vtls list all variables going into thread local storage\n"
|
|
526 " -w enable warnings\n"
|
|
527 );
|
|
528 }
|
|
529
|
|
530 void fatal()
|
|
531 {
|
|
532 static if (false) {
|
|
533 halt();
|
|
534 } else {
|
|
535 exit(EXIT_FAILURE);
|
|
536 }
|
|
537 }
|
|
538
|
|
539 void halt()
|
|
540 {
|
|
541 assert(false);
|
|
542 }
|
|
543
|
|
544 void initPrecedence()
|
|
545 {
|
|
546 precedence[TOK.TOKdotvar] = PREC.PREC_primary;
|
|
547 precedence[TOK.TOKimport] = PREC.PREC_primary;
|
|
548 precedence[TOK.TOKidentifier] = PREC.PREC_primary;
|
|
549 precedence[TOK.TOKthis] = PREC.PREC_primary;
|
|
550 precedence[TOK.TOKsuper] = PREC.PREC_primary;
|
|
551 precedence[TOK.TOKint64] = PREC.PREC_primary;
|
|
552 precedence[TOK.TOKfloat64] = PREC.PREC_primary;
|
|
553 precedence[TOK.TOKnull] = PREC.PREC_primary;
|
|
554 precedence[TOK.TOKstring] = PREC.PREC_primary;
|
|
555 precedence[TOK.TOKarrayliteral] = PREC.PREC_primary;
|
|
556 precedence[TOK.TOKtypeid] = PREC.PREC_primary;
|
|
557 precedence[TOK.TOKis] = PREC.PREC_primary;
|
|
558 precedence[TOK.TOKassert] = PREC.PREC_primary;
|
|
559 precedence[TOK.TOKfunction] = PREC.PREC_primary;
|
|
560 precedence[TOK.TOKvar] = PREC.PREC_primary;
|
|
561 version (DMDV2) {
|
|
562 precedence[TOK.TOKdefault] = PREC.PREC_primary;
|
|
563 }
|
|
564
|
|
565 // post
|
|
566 precedence[TOK.TOKdotti] = PREC.PREC_primary;
|
|
567 precedence[TOK.TOKdot] = PREC.PREC_primary;
|
|
568 // precedence[TOK.TOKarrow] = PREC.PREC_primary;
|
|
569 precedence[TOK.TOKplusplus] = PREC.PREC_primary;
|
|
570 precedence[TOK.TOKminusminus] = PREC.PREC_primary;
|
|
571 precedence[TOK.TOKcall] = PREC.PREC_primary;
|
|
572 precedence[TOK.TOKslice] = PREC.PREC_primary;
|
|
573 precedence[TOK.TOKarray] = PREC.PREC_primary;
|
|
574
|
|
575 precedence[TOK.TOKaddress] = PREC.PREC_unary;
|
|
576 precedence[TOK.TOKstar] = PREC.PREC_unary;
|
|
577 precedence[TOK.TOKneg] = PREC.PREC_unary;
|
|
578 precedence[TOK.TOKuadd] = PREC.PREC_unary;
|
|
579 precedence[TOK.TOKnot] = PREC.PREC_unary;
|
|
580 precedence[TOK.TOKtobool] = PREC.PREC_add;
|
|
581 precedence[TOK.TOKtilde] = PREC.PREC_unary;
|
|
582 precedence[TOK.TOKdelete] = PREC.PREC_unary;
|
|
583 precedence[TOK.TOKnew] = PREC.PREC_unary;
|
|
584 precedence[TOK.TOKcast] = PREC.PREC_unary;
|
|
585
|
|
586 precedence[TOK.TOKmul] = PREC.PREC_mul;
|
|
587 precedence[TOK.TOKdiv] = PREC.PREC_mul;
|
|
588 precedence[TOK.TOKmod] = PREC.PREC_mul;
|
|
589
|
|
590 precedence[TOK.TOKadd] = PREC.PREC_add;
|
|
591 precedence[TOK.TOKmin] = PREC.PREC_add;
|
|
592 precedence[TOK.TOKcat] = PREC.PREC_add;
|
|
593
|
|
594 precedence[TOK.TOKshl] = PREC.PREC_shift;
|
|
595 precedence[TOK.TOKshr] = PREC.PREC_shift;
|
|
596 precedence[TOK.TOKushr] = PREC.PREC_shift;
|
|
597
|
|
598 precedence[TOK.TOKlt] = PREC.PREC_rel;
|
|
599 precedence[TOK.TOKle] = PREC.PREC_rel;
|
|
600 precedence[TOK.TOKgt] = PREC.PREC_rel;
|
|
601 precedence[TOK.TOKge] = PREC.PREC_rel;
|
|
602 precedence[TOK.TOKunord] = PREC.PREC_rel;
|
|
603 precedence[TOK.TOKlg] = PREC.PREC_rel;
|
|
604 precedence[TOK.TOKleg] = PREC.PREC_rel;
|
|
605 precedence[TOK.TOKule] = PREC.PREC_rel;
|
|
606 precedence[TOK.TOKul] = PREC.PREC_rel;
|
|
607 precedence[TOK.TOKuge] = PREC.PREC_rel;
|
|
608 precedence[TOK.TOKug] = PREC.PREC_rel;
|
|
609 precedence[TOK.TOKue] = PREC.PREC_rel;
|
|
610 precedence[TOK.TOKin] = PREC.PREC_rel;
|
|
611
|
|
612 static if (false) {
|
|
613 precedence[TOK.TOKequal] = PREC.PREC_equal;
|
|
614 precedence[TOK.TOKnotequal] = PREC.PREC_equal;
|
|
615 precedence[TOK.TOKidentity] = PREC.PREC_equal;
|
|
616 precedence[TOK.TOKnotidentity] = PREC.PREC_equal;
|
|
617 } else {
|
|
618 /* Note that we changed precedence, so that < and != have the same
|
|
619 * precedence. This change is in the parser, too.
|
|
620 */
|
|
621 precedence[TOK.TOKequal] = PREC.PREC_rel;
|
|
622 precedence[TOK.TOKnotequal] = PREC.PREC_rel;
|
|
623 precedence[TOK.TOKidentity] = PREC.PREC_rel;
|
|
624 precedence[TOK.TOKnotidentity] = PREC.PREC_rel;
|
|
625 }
|
|
626
|
|
627 precedence[TOK.TOKand] = PREC.PREC_and;
|
|
628
|
|
629 precedence[TOK.TOKxor] = PREC.PREC_xor;
|
|
630
|
|
631 precedence[TOK.TOKor] = PREC.PREC_or;
|
|
632
|
|
633 precedence[TOK.TOKandand] = PREC.PREC_andand;
|
|
634
|
|
635 precedence[TOK.TOKoror] = PREC.PREC_oror;
|
|
636
|
|
637 precedence[TOK.TOKquestion] = PREC.PREC_cond;
|
|
638
|
|
639 precedence[TOK.TOKassign] = PREC.PREC_assign;
|
|
640 precedence[TOK.TOKconstruct] = PREC.PREC_assign;
|
|
641 precedence[TOK.TOKblit] = PREC.PREC_assign;
|
|
642 precedence[TOK.TOKaddass] = PREC.PREC_assign;
|
|
643 precedence[TOK.TOKminass] = PREC.PREC_assign;
|
|
644 precedence[TOK.TOKcatass] = PREC.PREC_assign;
|
|
645 precedence[TOK.TOKmulass] = PREC.PREC_assign;
|
|
646 precedence[TOK.TOKdivass] = PREC.PREC_assign;
|
|
647 precedence[TOK.TOKmodass] = PREC.PREC_assign;
|
|
648 precedence[TOK.TOKshlass] = PREC.PREC_assign;
|
|
649 precedence[TOK.TOKshrass] = PREC.PREC_assign;
|
|
650 precedence[TOK.TOKushrass] = PREC.PREC_assign;
|
|
651 precedence[TOK.TOKandass] = PREC.PREC_assign;
|
|
652 precedence[TOK.TOKorass] = PREC.PREC_assign;
|
|
653 precedence[TOK.TOKxorass] = PREC.PREC_assign;
|
|
654
|
|
655 precedence[TOK.TOKcomma] = PREC.PREC_expr;
|
|
656 }
|
|
657
|
|
658 int runLINK()
|
|
659 {
|
|
660 version (_WIN32) {
|
|
661 string p;
|
|
662 int i;
|
|
663 int status;
|
|
664 scope OutBuffer cmdbuf = new OutBuffer();
|
|
665
|
|
666 global.params.libfiles.push(cast(void*)new String("user32"));
|
|
667 global.params.libfiles.push(cast(void*)new String("kernel32"));
|
|
668
|
|
669 for (i = 0; i < global.params.objfiles.dim; i++)
|
|
670 {
|
|
671 if (i)
|
|
672 cmdbuf.writeByte('+');
|
|
673 p = (cast(String)global.params.objfiles.data[i]).str;
|
|
674 string ext = FileName.ext(p);
|
|
675 if (ext)
|
|
676 // Write name sans extension
|
|
677 writeFilename(cmdbuf, p[0..p.length - ext.length - 1]);
|
|
678 else
|
|
679 writeFilename(cmdbuf, p);
|
|
680 }
|
|
681 cmdbuf.writeByte(',');
|
|
682 if (global.params.exefile)
|
|
683 writeFilename(cmdbuf, global.params.exefile);
|
|
684 else
|
|
685 {
|
|
686 /* Generate exe file name from first obj name.
|
|
687 * No need to add it to cmdbuf because the linker will default to it.
|
|
688 */
|
|
689 string n = (cast(String)global.params.objfiles.data[0]).str;
|
|
690 n = FileName.name(n);
|
|
691 FileName fn = FileName.forceExt(n, "exe");
|
|
692 global.params.exefile = fn.toChars();
|
|
693 }
|
|
694
|
|
695 // Make sure path to exe file exists
|
|
696 {
|
|
697 string pp = FileName.path(global.params.exefile);
|
|
698 FileName.ensurePathExists(pp);
|
|
699 }
|
|
700
|
|
701 cmdbuf.writeByte(',');
|
|
702 if (global.params.run)
|
|
703 cmdbuf.writestring("nul");
|
|
704
|
|
705 // if (mapfile)
|
|
706 // cmdbuf.writestring(output);
|
|
707 cmdbuf.writeByte(',');
|
|
708
|
|
709 for (i = 0; i < global.params.libfiles.dim; i++)
|
|
710 {
|
|
711 if (i)
|
|
712 cmdbuf.writeByte('+');
|
|
713 writeFilename(cmdbuf, (cast(String)global.params.libfiles.data[i]).str);
|
|
714 }
|
|
715
|
|
716 if (global.params.deffile)
|
|
717 {
|
|
718 cmdbuf.writeByte(',');
|
|
719 writeFilename(cmdbuf, global.params.deffile);
|
|
720 }
|
|
721
|
|
722 /* Eliminate unnecessary trailing commas */
|
|
723 while (1)
|
|
724 {
|
|
725 i = cmdbuf.offset;
|
|
726 if (!i || cmdbuf.data[i - 1] != ',')
|
|
727 break;
|
|
728 cmdbuf.offset--;
|
|
729 }
|
|
730
|
|
731 if (global.params.resfile)
|
|
732 {
|
|
733 cmdbuf.writestring("/RC:");
|
|
734 writeFilename(cmdbuf, global.params.resfile);
|
|
735 }
|
|
736
|
|
737 static if (false) {
|
|
738 if (mapfile)
|
|
739 cmdbuf.writestring("/m");
|
|
740 if (debuginfo)
|
|
741 cmdbuf.writestring("/li");
|
|
742 if (codeview)
|
|
743 {
|
|
744 cmdbuf.writestring("/co");
|
|
745 if (codeview3)
|
|
746 cmdbuf.writestring(":3");
|
|
747 }
|
|
748 } else {
|
|
749 if (global.params.symdebug)
|
|
750 cmdbuf.writestring("/co");
|
|
751 }
|
|
752
|
|
753 cmdbuf.writestring("/noi");
|
|
754 for (i = 0; i < global.params.linkswitches.dim; i++)
|
|
755 {
|
|
756 cmdbuf.writestring((cast(String)global.params.linkswitches.data[i]).str);
|
|
757 }
|
|
758 cmdbuf.writeByte(';');
|
|
759
|
|
760 p = cmdbuf.toChars();
|
|
761
|
|
762 FileName lnkfilename = null;
|
|
763 size_t plen = p.length;
|
|
764 if (plen > 7000)
|
|
765 {
|
|
766 lnkfilename = FileName.forceExt(global.params.exefile, "lnk");
|
|
767 scope File flnk = new File(lnkfilename);
|
|
768 flnk.setbuffer(cast(void*)p.ptr, plen);
|
|
769 flnk.ref_ = 1;
|
|
770 if (flnk.write())
|
|
771 error("error writing file %s", lnkfilename);
|
|
772 if (lnkfilename.len() < plen)
|
|
773 p = std.string.format("@%s", lnkfilename.toChars());
|
|
774 }
|
|
775
|
|
776 string linkcmd = getenv("LINKCMD");
|
|
777 if (!linkcmd)
|
|
778 linkcmd = "link";
|
|
779
|
|
780 status = executecmd(linkcmd, p, 1);
|
|
781 if (lnkfilename)
|
|
782 {
|
|
783 remove(toStringz(lnkfilename.toChars()));
|
|
784 ///delete lnkfilename;
|
|
785 }
|
|
786 return status;
|
|
787 } else if (XXX) {/// linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
|
|
788 assert(false);
|
|
789 /+
|
|
790 pid_t childpid;
|
|
791 int i;
|
|
792 int status;
|
|
793
|
|
794 // Build argv[]
|
|
795 Array argv;
|
|
796
|
|
797 const char *cc = getenv("CC");
|
|
798 if (!cc)
|
|
799 cc = "gcc";
|
|
800 argv.push((void *)cc);
|
|
801 argv.insert(1, global.params.objfiles);
|
|
802
|
|
803 // None of that a.out stuff. Use explicit exe file name, or
|
|
804 // generate one from name of first source file.
|
|
805 argv.push((void *)"-o");
|
|
806 if (global.params.exefile)
|
|
807 {
|
|
808 argv.push(global.params.exefile);
|
|
809 }
|
|
810 else
|
|
811 { // Generate exe file name from first obj name
|
|
812 char *n = (char *)global.params.objfiles.data[0];
|
|
813 char *e;
|
|
814 char *ex;
|
|
815
|
|
816 n = FileName.name(n);
|
|
817 e = FileName.ext(n);
|
|
818 if (e)
|
|
819 {
|
|
820 e--; // back up over '.'
|
|
821 ex = (char *)mem.malloc(e - n + 1);
|
|
822 memcpy(ex, n, e - n);
|
|
823 ex[e - n] = 0;
|
|
824 }
|
|
825 else
|
|
826 ex = (char *)"a.out"; // no extension, so give up
|
|
827 argv.push(ex);
|
|
828 global.params.exefile = ex;
|
|
829 }
|
|
830
|
|
831 // Make sure path to exe file exists
|
|
832 { char *p = FileName.path(global.params.exefile);
|
|
833 FileName.ensurePathExists(p);
|
|
834 mem.free(p);
|
|
835 }
|
|
836
|
|
837 if (global.params.symdebug)
|
|
838 argv.push((void *)"-g");
|
|
839
|
|
840 if (global.params.isX86_64)
|
|
841 argv.push((void *)"-m64");
|
|
842 else
|
|
843 argv.push((void *)"-m32");
|
|
844
|
|
845 if (0 && global.params.exefile)
|
|
846 {
|
|
847 /* This switch enables what is known as 'smart linking'
|
|
848 * in the Windows world, where unreferenced sections
|
|
849 * are removed from the executable. It eliminates unreferenced
|
|
850 * functions, essentially making a 'library' out of a module.
|
|
851 * Although it is documented to work with ld version 2.13,
|
|
852 * in practice it does not, but just seems to be ignored.
|
|
853 * Thomas Kuehne has verified that it works with ld 2.16.1.
|
|
854 * BUG: disabled because it causes exception handling to fail
|
|
855 */
|
|
856 argv.push((void *)"-Xlinker");
|
|
857 argv.push((void *)"--gc-sections");
|
|
858 }
|
|
859
|
|
860 for (i = 0; i < global.params.linkswitches.dim; i++)
|
|
861 { char *p = (char *)global.params.linkswitches.data[i];
|
|
862 if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l'))
|
|
863 // Don't need -Xlinker if switch starts with -l
|
|
864 argv.push((void *)"-Xlinker");
|
|
865 argv.push((void *) p);
|
|
866 }
|
|
867
|
|
868 /* Add each library, prefixing it with "-l".
|
|
869 * The order of libraries passed is:
|
|
870 * 1. any libraries passed with -L command line switch
|
|
871 * 2. libraries specified on the command line
|
|
872 * 3. libraries specified by pragma(lib), which were appended
|
|
873 * to global.params.libfiles.
|
|
874 * 4. standard libraries.
|
|
875 */
|
|
876 for (i = 0; i < global.params.libfiles.dim; i++)
|
|
877 { char *p = (char *)global.params.libfiles.data[i];
|
|
878 size_t plen = strlen(p);
|
|
879 if (plen > 2 && p[plen - 2] == '.' && p[plen -1] == 'a')
|
|
880 argv.push((void *)p);
|
|
881 else
|
|
882 {
|
|
883 char *s = (char *)mem.malloc(plen + 3);
|
|
884 s[0] = '-';
|
|
885 s[1] = 'l';
|
|
886 memcpy(s + 2, p, plen + 1);
|
|
887 argv.push((void *)s);
|
|
888 }
|
|
889 }
|
|
890
|
|
891 /* Standard libraries must go after user specified libraries
|
|
892 * passed with -l.
|
|
893 */
|
|
894 const char *libname = (global.params.symdebug)
|
|
895 ? global.params.debuglibname
|
|
896 : global.params.defaultlibname;
|
|
897 char *buf = (char *)malloc(2 + strlen(libname) + 1);
|
|
898 strcpy(buf, "-l");
|
|
899 strcpy(buf + 2, libname);
|
|
900 argv.push((void *)buf); // turns into /usr/lib/libphobos2.a
|
|
901
|
|
902 // argv.push((void *)"-ldruntime");
|
|
903 argv.push((void *)"-lpthread");
|
|
904 argv.push((void *)"-lm");
|
|
905
|
|
906 if (!global.params.quiet || global.params.verbose)
|
|
907 {
|
|
908 // Print it
|
|
909 for (i = 0; i < argv.dim; i++)
|
|
910 printf("%s ", (char *)argv.data[i]);
|
|
911 printf("\n");
|
|
912 fflush(stdout);
|
|
913 }
|
|
914
|
|
915 argv.push(null);
|
|
916 childpid = fork();
|
|
917 if (childpid == 0)
|
|
918 {
|
|
919 execvp((char *)argv.data[0], (char **)argv.data);
|
|
920 perror((char *)argv.data[0]); // failed to execute
|
|
921 return -1;
|
|
922 }
|
|
923
|
|
924 waitpid(childpid, &status, 0);
|
|
925
|
|
926 status=WEXITSTATUS(status);
|
|
927 if (status)
|
|
928 printf("--- errorlevel %d\n", status);
|
|
929 return status;
|
|
930 +/
|
|
931 } else {
|
|
932 writef ("Linking is not yet supported for this version of DMD.\n");
|
|
933 return -1;
|
|
934 }
|
|
935 }
|
|
936
|
|
937 int runProgram()
|
|
938 {
|
|
939 assert(false);
|
|
940 }
|
|
941
|
|
942 void deleteExeFile()
|
|
943 {
|
|
944 assert(false);
|
|
945 }
|
|
946
|
|
947 /****************************************
|
|
948 * Write filename to cmdbuf, quoting if necessary.
|
|
949 */
|
|
950
|
|
951 void writeFilename(OutBuffer buf, string filename)
|
|
952 {
|
|
953 auto len = filename.length;
|
|
954 /* Loop and see if we need to quote
|
|
955 */
|
|
956 for (size_t i = 0; i < len; i++)
|
|
957 {
|
|
958 char c = filename[i];
|
|
959
|
|
960 if (isalnum(c) || c == '_')
|
|
961 continue;
|
|
962
|
|
963 /* Need to quote
|
|
964 */
|
|
965 buf.writeByte('"');
|
|
966 buf.writestring(filename);
|
|
967 buf.writeByte('"');
|
|
968 return;
|
|
969 }
|
|
970
|
|
971 /* No quoting necessary
|
|
972 */
|
|
973 buf.writestring(filename);
|
|
974 }
|
|
975
|
|
976 /******************************
|
|
977 * Execute a rule. Return the status.
|
|
978 * cmd program to run
|
|
979 * args arguments to cmd, as a string
|
|
980 * useenv if cmd knows about _CMDLINE environment variable
|
|
981 */
|
|
982
|
|
983 version (_WIN32) {
|
|
984 int executecmd(string cmd, string args, int useenv)
|
|
985 {
|
|
986 int status;
|
|
987 size_t len = args.length;
|
|
988
|
|
989 if (!global.params.quiet || global.params.verbose)
|
|
990 {
|
|
991 printf("%s %s\n", cmd, args);
|
|
992 fflush(stdout);
|
|
993 }
|
|
994
|
|
995 if (len > 255)
|
|
996 {
|
|
997 char* q;
|
|
998 static char[9] envname = "@_CMDLINE";
|
|
999
|
|
1000 envname[0] = '@';
|
|
1001 switch (useenv)
|
|
1002 {
|
|
1003 case 0: goto L1;
|
|
1004 case 2: envname[0] = '%'; break;
|
|
1005 default: break; ///
|
|
1006 }
|
|
1007 q = cast(char*) alloca(envname.sizeof + len + 1);
|
|
1008 sprintf(q, "%s=%s", envname.ptr + 1, args);
|
|
1009 status = putenv(q);
|
|
1010 if (status == 0)
|
|
1011 args = envname[].idup;
|
|
1012 else
|
|
1013 {
|
|
1014 L1:
|
|
1015 error("command line length of %d is too long",len);
|
|
1016 }
|
|
1017 }
|
|
1018
|
|
1019 status = executearg0(cmd, args);
|
|
1020 version (_WIN32) {
|
|
1021 if (status == -1) {
|
|
1022 auto cmdZ = toStringz(cmd);
|
|
1023 auto argsZ = toStringz(args);
|
|
1024 status = spawnlp(0, cmdZ, cmdZ, argsZ, null);
|
|
1025 }
|
|
1026 }
|
|
1027 // if (global.params.verbose)
|
|
1028 // printf("\n");
|
|
1029 if (status)
|
|
1030 {
|
|
1031 if (status == -1)
|
|
1032 printf("Can't run '%s', check PATH\n", cmd);
|
|
1033 else
|
|
1034 printf("--- errorlevel %d\n", status);
|
|
1035 }
|
|
1036 return status;
|
|
1037 }
|
|
1038 }
|
|
1039
|
|
1040 /**************************************
|
|
1041 * Attempt to find command to execute by first looking in the directory
|
|
1042 * where DMD was run from.
|
|
1043 * Returns:
|
|
1044 * -1 did not find command there
|
|
1045 * !=-1 exit status from command
|
|
1046 */
|
|
1047
|
|
1048 version (_WIN32) {
|
|
1049 int executearg0(string cmd, string args)
|
|
1050 {
|
|
1051 string file;
|
|
1052 string argv0 = global.params.argv0;
|
|
1053
|
|
1054 //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args);
|
|
1055
|
|
1056 // If cmd is fully qualified, we don't do this
|
|
1057 if (FileName.absolute(cmd))
|
|
1058 return -1;
|
|
1059
|
|
1060 file = FileName.replaceName(argv0, cmd);
|
|
1061
|
|
1062 //printf("spawning '%s'\n",file);
|
|
1063 version (_WIN32) {
|
|
1064 auto fileZ = toStringz(file);
|
|
1065 auto argsZ = toStringz(args);
|
|
1066 return spawnl(0, fileZ, fileZ, argsZ, null);
|
|
1067 } else version (XXX) { ///#elif linux || __APPLE__ || __FreeBSD__ || __sun&&__SVR4
|
|
1068 assert(false);
|
|
1069 /+
|
|
1070 char *full;
|
|
1071 int cmdl = strlen(cmd);
|
|
1072
|
|
1073 full = (char*) mem.malloc(cmdl + strlen(args) + 2);
|
|
1074 if (full == null)
|
|
1075 return 1;
|
|
1076 strcpy(full, cmd);
|
|
1077 full [cmdl] = ' ';
|
|
1078 strcpy(full + cmdl + 1, args);
|
|
1079
|
|
1080 int result = system(full);
|
|
1081
|
|
1082 mem.free(full);
|
|
1083 return result;
|
|
1084 +/
|
|
1085 } else {
|
|
1086 static assert(false);
|
|
1087 }
|
|
1088 }
|
|
1089 } |