Mercurial > projects > ldc
comparison dmd2/link.c.nolink @ 758:f04dde6e882c
Added initial D2 support, D2 frontend and changes to codegen to make things compile.
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Tue, 11 Nov 2008 01:38:48 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
757:2c730d530c98 | 758:f04dde6e882c |
---|---|
1 | |
2 // Copyright (c) 1999-2008 by Digital Mars | |
3 // All Rights Reserved | |
4 // written by Walter Bright | |
5 // http://www.digitalmars.com | |
6 // License for redistribution is by either the Artistic License | |
7 // in artistic.txt, or the GNU General Public License in gnu.txt. | |
8 // See the included readme.txt for details. | |
9 | |
10 | |
11 #include <stdio.h> | |
12 #include <ctype.h> | |
13 #include <assert.h> | |
14 #include <stdarg.h> | |
15 #include <string.h> | |
16 #include <stdlib.h> | |
17 | |
18 #if _WIN32 | |
19 #include <process.h> | |
20 #endif | |
21 | |
22 #if linux | |
23 #include <sys/types.h> | |
24 #include <sys/wait.h> | |
25 #include <unistd.h> | |
26 #endif | |
27 | |
28 #include "root.h" | |
29 | |
30 #include "mars.h" | |
31 | |
32 #include "mem.h" | |
33 | |
34 int executecmd(char *cmd, char *args, int useenv); | |
35 int executearg0(char *cmd, char *args); | |
36 | |
37 /**************************************** | |
38 * Write filename to cmdbuf, quoting if necessary. | |
39 */ | |
40 | |
41 void writeFilename(OutBuffer *buf, char *filename, size_t len) | |
42 { | |
43 /* Loop and see if we need to quote | |
44 */ | |
45 for (size_t i = 0; i < len; i++) | |
46 { char c = filename[i]; | |
47 | |
48 if (isalnum(c) || c == '_') | |
49 continue; | |
50 | |
51 /* Need to quote | |
52 */ | |
53 buf->writeByte('"'); | |
54 buf->write(filename, len); | |
55 buf->writeByte('"'); | |
56 return; | |
57 } | |
58 | |
59 /* No quoting necessary | |
60 */ | |
61 buf->write(filename, len); | |
62 } | |
63 | |
64 void writeFilename(OutBuffer *buf, char *filename) | |
65 { | |
66 writeFilename(buf, filename, strlen(filename)); | |
67 } | |
68 | |
69 /***************************** | |
70 * Run the linker. Return status of execution. | |
71 */ | |
72 | |
73 int runLINK() | |
74 { | |
75 #if _WIN32 | |
76 char *p; | |
77 int i; | |
78 int status; | |
79 OutBuffer cmdbuf; | |
80 | |
81 global.params.libfiles->push((void *) "user32"); | |
82 global.params.libfiles->push((void *) "kernel32"); | |
83 | |
84 for (i = 0; i < global.params.objfiles->dim; i++) | |
85 { | |
86 if (i) | |
87 cmdbuf.writeByte('+'); | |
88 p = (char *)global.params.objfiles->data[i]; | |
89 char *ext = FileName::ext(p); | |
90 if (ext) | |
91 // Write name sans extension | |
92 writeFilename(&cmdbuf, p, ext - p - 1); | |
93 else | |
94 writeFilename(&cmdbuf, p); | |
95 } | |
96 cmdbuf.writeByte(','); | |
97 if (global.params.exefile) | |
98 writeFilename(&cmdbuf, global.params.exefile); | |
99 else | |
100 { /* Generate exe file name from first obj name. | |
101 * No need to add it to cmdbuf because the linker will default to it. | |
102 */ | |
103 char *n = (char *)global.params.objfiles->data[0]; | |
104 n = FileName::name(n); | |
105 FileName *fn = FileName::forceExt(n, "exe"); | |
106 global.params.exefile = fn->toChars(); | |
107 } | |
108 | |
109 // Make sure path to exe file exists | |
110 { char *p = FileName::path(global.params.exefile); | |
111 FileName::ensurePathExists(p); | |
112 mem.free(p); | |
113 } | |
114 | |
115 cmdbuf.writeByte(','); | |
116 if (global.params.run) | |
117 cmdbuf.writestring("nul"); | |
118 // if (mapfile) | |
119 // cmdbuf.writestring(output); | |
120 cmdbuf.writeByte(','); | |
121 | |
122 for (i = 0; i < global.params.libfiles->dim; i++) | |
123 { | |
124 if (i) | |
125 cmdbuf.writeByte('+'); | |
126 writeFilename(&cmdbuf, (char *) global.params.libfiles->data[i]); | |
127 } | |
128 | |
129 if (global.params.deffile) | |
130 { | |
131 cmdbuf.writeByte(','); | |
132 writeFilename(&cmdbuf, global.params.deffile); | |
133 } | |
134 | |
135 /* Eliminate unnecessary trailing commas */ | |
136 while (1) | |
137 { i = cmdbuf.offset; | |
138 if (!i || cmdbuf.data[i - 1] != ',') | |
139 break; | |
140 cmdbuf.offset--; | |
141 } | |
142 | |
143 if (global.params.resfile) | |
144 { | |
145 cmdbuf.writestring("/RC:"); | |
146 writeFilename(&cmdbuf, global.params.resfile); | |
147 } | |
148 | |
149 #if 0 | |
150 if (mapfile) | |
151 cmdbuf.writestring("/m"); | |
152 if (debuginfo) | |
153 cmdbuf.writestring("/li"); | |
154 if (codeview) | |
155 { | |
156 cmdbuf.writestring("/co"); | |
157 if (codeview3) | |
158 cmdbuf.writestring(":3"); | |
159 } | |
160 #else | |
161 if (global.params.symdebug) | |
162 cmdbuf.writestring("/co"); | |
163 #endif | |
164 | |
165 cmdbuf.writestring("/noi"); | |
166 for (i = 0; i < global.params.linkswitches->dim; i++) | |
167 { | |
168 cmdbuf.writestring((char *) global.params.linkswitches->data[i]); | |
169 } | |
170 cmdbuf.writeByte(';'); | |
171 | |
172 p = cmdbuf.toChars(); | |
173 | |
174 FileName *lnkfilename = NULL; | |
175 size_t plen = strlen(p); | |
176 if (plen > 7000) | |
177 { | |
178 lnkfilename = FileName::forceExt(global.params.exefile, "lnk"); | |
179 File flnk(lnkfilename); | |
180 flnk.setbuffer(p, plen); | |
181 flnk.ref = 1; | |
182 if (flnk.write()) | |
183 error("error writing file %s", lnkfilename); | |
184 if (lnkfilename->len() < plen) | |
185 sprintf(p, "@%s", lnkfilename->toChars()); | |
186 } | |
187 | |
188 char *linkcmd = getenv("LINKCMD"); | |
189 if (!linkcmd) | |
190 linkcmd = "link"; | |
191 status = executecmd(linkcmd, p, 1); | |
192 if (lnkfilename) | |
193 { | |
194 remove(lnkfilename->toChars()); | |
195 delete lnkfilename; | |
196 } | |
197 return status; | |
198 #elif linux | |
199 pid_t childpid; | |
200 int i; | |
201 int status; | |
202 | |
203 // Build argv[] | |
204 Array argv; | |
205 | |
206 const char *cc = getenv("CC"); | |
207 if (!cc) | |
208 cc = "gcc"; | |
209 argv.push((void *)cc); | |
210 argv.insert(1, global.params.objfiles); | |
211 | |
212 // None of that a.out stuff. Use explicit exe file name, or | |
213 // generate one from name of first source file. | |
214 argv.push((void *)"-o"); | |
215 if (global.params.exefile) | |
216 { | |
217 argv.push(global.params.exefile); | |
218 } | |
219 else | |
220 { // Generate exe file name from first obj name | |
221 char *n = (char *)global.params.objfiles->data[0]; | |
222 char *e; | |
223 char *ex; | |
224 | |
225 n = FileName::name(n); | |
226 e = FileName::ext(n); | |
227 if (e) | |
228 { | |
229 e--; // back up over '.' | |
230 ex = (char *)mem.malloc(e - n + 1); | |
231 memcpy(ex, n, e - n); | |
232 ex[e - n] = 0; | |
233 } | |
234 else | |
235 ex = (char *)"a.out"; // no extension, so give up | |
236 argv.push(ex); | |
237 global.params.exefile = ex; | |
238 } | |
239 | |
240 // Make sure path to exe file exists | |
241 { char *p = FileName::path(global.params.exefile); | |
242 FileName::ensurePathExists(p); | |
243 mem.free(p); | |
244 } | |
245 | |
246 argv.insert(argv.dim, global.params.libfiles); | |
247 | |
248 if (global.params.symdebug) | |
249 argv.push((void *)"-g"); | |
250 | |
251 argv.push((void *)"-m32"); | |
252 | |
253 if (0 && global.params.exefile) | |
254 { | |
255 /* This switch enables what is known as 'smart linking' | |
256 * in the Windows world, where unreferenced sections | |
257 * are removed from the executable. It eliminates unreferenced | |
258 * functions, essentially making a 'library' out of a module. | |
259 * Although it is documented to work with ld version 2.13, | |
260 * in practice it does not, but just seems to be ignored. | |
261 * Thomas Kuehne has verified that it works with ld 2.16.1. | |
262 * BUG: disabled because it causes exception handling to fail | |
263 */ | |
264 argv.push((void *)"-Xlinker"); | |
265 argv.push((void *)"--gc-sections"); | |
266 } | |
267 | |
268 for (i = 0; i < global.params.linkswitches->dim; i++) | |
269 { char *p = (char *)global.params.linkswitches->data[i]; | |
270 if (!p || !p[0] || !(p[0] == '-' && p[1] == 'l')) | |
271 // Don't need -Xlinker if switch starts with -l | |
272 argv.push((void *)"-Xlinker"); | |
273 argv.push((void *) p); | |
274 } | |
275 | |
276 /* Standard libraries must go after user specified libraries | |
277 * passed with -l. | |
278 */ | |
279 const char *libname = (global.params.symdebug) | |
280 ? global.params.debuglibname | |
281 : global.params.defaultlibname; | |
282 char *buf = (char *)malloc(2 + strlen(libname) + 1); | |
283 strcpy(buf, "-l"); | |
284 strcpy(buf + 2, libname); | |
285 argv.push((void *)buf); // turns into /usr/lib/libphobos2.a | |
286 | |
287 argv.push((void *)"-ldruntime"); | |
288 argv.push((void *)"-lpthread"); | |
289 argv.push((void *)"-lm"); | |
290 | |
291 if (!global.params.quiet || global.params.verbose) | |
292 { | |
293 // Print it | |
294 for (i = 0; i < argv.dim; i++) | |
295 printf("%s ", (char *)argv.data[i]); | |
296 printf("\n"); | |
297 fflush(stdout); | |
298 } | |
299 | |
300 argv.push(NULL); | |
301 childpid = fork(); | |
302 if (childpid == 0) | |
303 { | |
304 execvp((char *)argv.data[0], (char **)argv.data); | |
305 perror((char *)argv.data[0]); // failed to execute | |
306 return -1; | |
307 } | |
308 | |
309 waitpid(childpid, &status, 0); | |
310 | |
311 status=WEXITSTATUS(status); | |
312 if (status) | |
313 printf("--- errorlevel %d\n", status); | |
314 return status; | |
315 #else | |
316 printf ("Linking is not yet supported for this version of DMD.\n"); | |
317 return -1; | |
318 #endif | |
319 } | |
320 | |
321 /********************************** | |
322 * Delete generated EXE file. | |
323 */ | |
324 | |
325 void deleteExeFile() | |
326 { | |
327 if (global.params.exefile) | |
328 { | |
329 //printf("deleteExeFile() %s\n", global.params.exefile); | |
330 remove(global.params.exefile); | |
331 } | |
332 } | |
333 | |
334 /****************************** | |
335 * Execute a rule. Return the status. | |
336 * cmd program to run | |
337 * args arguments to cmd, as a string | |
338 * useenv if cmd knows about _CMDLINE environment variable | |
339 */ | |
340 | |
341 #if _WIN32 | |
342 int executecmd(char *cmd, char *args, int useenv) | |
343 { | |
344 int status; | |
345 char *buff; | |
346 size_t len; | |
347 | |
348 if (!global.params.quiet || global.params.verbose) | |
349 { | |
350 printf("%s %s\n", cmd, args); | |
351 fflush(stdout); | |
352 } | |
353 | |
354 if ((len = strlen(args)) > 255) | |
355 { char *q; | |
356 static char envname[] = "@_CMDLINE"; | |
357 | |
358 envname[0] = '@'; | |
359 switch (useenv) | |
360 { case 0: goto L1; | |
361 case 2: envname[0] = '%'; break; | |
362 } | |
363 q = (char *) alloca(sizeof(envname) + len + 1); | |
364 sprintf(q,"%s=%s", envname + 1, args); | |
365 status = putenv(q); | |
366 if (status == 0) | |
367 args = envname; | |
368 else | |
369 { | |
370 L1: | |
371 error("command line length of %d is too long",len); | |
372 } | |
373 } | |
374 | |
375 status = executearg0(cmd,args); | |
376 #if _WIN32 | |
377 if (status == -1) | |
378 status = spawnlp(0,cmd,cmd,args,NULL); | |
379 #endif | |
380 // if (global.params.verbose) | |
381 // printf("\n"); | |
382 if (status) | |
383 { | |
384 if (status == -1) | |
385 printf("Can't run '%s', check PATH\n", cmd); | |
386 else | |
387 printf("--- errorlevel %d\n", status); | |
388 } | |
389 return status; | |
390 } | |
391 #endif | |
392 | |
393 /************************************** | |
394 * Attempt to find command to execute by first looking in the directory | |
395 * where DMD was run from. | |
396 * Returns: | |
397 * -1 did not find command there | |
398 * !=-1 exit status from command | |
399 */ | |
400 | |
401 #if _WIN32 | |
402 int executearg0(char *cmd, char *args) | |
403 { | |
404 char *file; | |
405 char *argv0 = global.params.argv0; | |
406 | |
407 //printf("argv0='%s', cmd='%s', args='%s'\n",argv0,cmd,args); | |
408 | |
409 // If cmd is fully qualified, we don't do this | |
410 if (FileName::absolute(cmd)) | |
411 return -1; | |
412 | |
413 file = FileName::replaceName(argv0, cmd); | |
414 | |
415 //printf("spawning '%s'\n",file); | |
416 #if _WIN32 | |
417 return spawnl(0,file,file,args,NULL); | |
418 #elif linux | |
419 char *full; | |
420 int cmdl = strlen(cmd); | |
421 | |
422 full = (char*) mem.malloc(cmdl + strlen(args) + 2); | |
423 if (full == NULL) | |
424 return 1; | |
425 strcpy(full, cmd); | |
426 full [cmdl] = ' '; | |
427 strcpy(full + cmdl + 1, args); | |
428 | |
429 int result = system(full); | |
430 | |
431 mem.free(full); | |
432 return result; | |
433 #else | |
434 assert(0); | |
435 #endif | |
436 } | |
437 #endif | |
438 | |
439 /*************************************** | |
440 * Run the compiled program. | |
441 * Return exit status. | |
442 */ | |
443 | |
444 int runProgram() | |
445 { | |
446 //printf("runProgram()\n"); | |
447 if (global.params.verbose) | |
448 { | |
449 printf("%s", global.params.exefile); | |
450 for (size_t i = 0; i < global.params.runargs_length; i++) | |
451 printf(" %s", (char *)global.params.runargs[i]); | |
452 printf("\n"); | |
453 } | |
454 | |
455 // Build argv[] | |
456 Array argv; | |
457 | |
458 argv.push((void *)global.params.exefile); | |
459 for (size_t i = 0; i < global.params.runargs_length; i++) | |
460 { char *a = global.params.runargs[i]; | |
461 | |
462 #if _WIN32 | |
463 // BUG: what about " appearing in the string? | |
464 if (strchr(a, ' ')) | |
465 { char *b = (char *)mem.malloc(3 + strlen(a)); | |
466 sprintf(b, "\"%s\"", a); | |
467 a = b; | |
468 } | |
469 #endif | |
470 argv.push((void *)a); | |
471 } | |
472 argv.push(NULL); | |
473 | |
474 #if _WIN32 | |
475 char *ex = FileName::name(global.params.exefile); | |
476 if (ex == global.params.exefile) | |
477 ex = FileName::combine(".", ex); | |
478 else | |
479 ex = global.params.exefile; | |
480 return spawnv(0,ex,(char **)argv.data); | |
481 #elif linux | |
482 pid_t childpid; | |
483 int status; | |
484 | |
485 childpid = fork(); | |
486 if (childpid == 0) | |
487 { | |
488 const char *fn = (const char *)argv.data[0]; | |
489 if (!FileName::absolute(fn)) | |
490 { // Make it "./fn" | |
491 fn = FileName::combine(".", fn); | |
492 } | |
493 execv(fn, (char **)argv.data); | |
494 perror(fn); // failed to execute | |
495 return -1; | |
496 } | |
497 | |
498 waitpid(childpid, &status, 0); | |
499 | |
500 status = WEXITSTATUS(status); | |
501 return status; | |
502 #else | |
503 assert(0); | |
504 #endif | |
505 } |