Mercurial > projects > ddmd
comparison dmd/Util.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | 7427ded8caf7 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:10317f0c89a5 |
---|---|
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 } |