2 // Compiler implementation of the D programming language
3 // Copyright (c) 1999-2008 by Digital Mars
5 // written by Walter Bright
6 // http://www.digitalmars.com
7 // License for redistribution is by either the Artistic License
8 // in artistic.txt, or the GNU General Public License in gnu.txt.
9 // See the included readme.txt for details.
20 long __cdecl
__ehfilter(LPEXCEPTION_POINTERS ep
);
40 #include "expression.h"
44 void browse(const char *url
);
45 void getenv_setargv(const char *envvar
, int *pargc
, char** *pargv
);
47 void obj_start(char *srcfile
);
48 void obj_end(Library
*library
, File
*objfile
);
85 copyright
= "Copyright (c) 1999-2008 by Digital Mars";
86 written
= "written by Walter Bright";
88 global
.structalign
= 8;
90 memset(¶ms
, 0, sizeof(Param
));
100 buf
.printf("%s", filename
);
105 buf
.printf("(%d)", linnum
);
107 buf
.printf(":%u", linnum
);
110 return (char *)buf
.extractData();
113 Loc::Loc(Module
*mod
, unsigned linnum
)
115 this->linnum
= linnum
;
116 this->filename
= mod
? mod
->srcfile
->toChars() : NULL
;
119 /**************************************
120 * Print error message and exit.
123 void error(Loc loc
, const char *format
, ...)
126 va_start(ap
, format
);
127 verror(loc
, format
, ap
);
131 void verror(Loc loc
, const char *format
, va_list ap
)
135 char *p
= loc
.toChars();
138 fprintf(stdmsg
, "%s: ", p
);
141 fprintf(stdmsg
, "Error: ");
142 vfprintf(stdmsg
, format
, ap
);
143 fprintf(stdmsg
, "\n");
149 /***************************************
150 * Call this after printing out fatal error messages to clean up and exit
162 /**************************************
163 * Try to stop forgetting to remove the breakpoints from
175 extern void backend_init();
176 extern void backend_term();
180 printf("Digital Mars D Compiler %s\n%s %s\n",
181 global
.version
, global
.copyright
, global
.written
);
183 Documentation: http://www.digitalmars.com/d/1.0/index.html\n\
185 dmd files.d ... { -switch }\n\
187 files.d D source files\n%s\
189 -cov do code coverage analysis\n\
190 -D generate documentation\n\
191 -Dddocdir write documentation file to docdir directory\n\
192 -Dffilename write documentation file to filename\n\
193 -d allow deprecated features\n\
194 -debug compile in debug code\n\
195 -debug=level compile in debug code <= level\n\
196 -debug=ident compile in debug code identified by ident\n\
197 -debuglib=name set symbolic debug library to name\n\
198 -defaultlib=name set default library to name\n\
199 -g add symbolic debug info\n\
200 -gc add symbolic debug info, pretend to be C\n\
201 -H generate 'header' file\n\
202 -Hdhdrdir write 'header' file to hdrdir directory\n\
203 -Hffilename write 'header' file to filename\n\
205 -Ipath where to look for imports\n\
206 -ignore ignore unsupported pragmas\n\
207 -inline do function inlining\n\
208 -Jpath where to look for string imports\n\
209 -Llinkerflag pass linkerflag to link\n\
210 -lib generate library rather than object files\n\
211 -man open web browser on manual page\n\
212 -nofloat do not emit reference to floating point\n\
214 -o- do not write object file\n\
215 -odobjdir write object & library files to directory objdir\n\
216 -offilename name output file to filename\n\
217 -op do not strip paths from source file\n\
218 -profile profile runtime performance of generated code\n\
219 -quiet suppress unnecessary messages\n\
220 -release compile release version\n\
221 -run srcfile args... run resulting program, passing args\n\
222 -unittest compile in unit tests\n\
224 -v1 D language version 1\n\
225 -version=level compile in version code >= level\n\
226 -version=ident compile in version code identified by ident\n\
227 -w enable warnings\n\
230 " @cmdfile read arguments from cmdfile\n"
237 int main(int argc
, char *argv
[])
244 int status
= EXIT_SUCCESS
;
245 int argcstart
= argc
;
247 // Check for malformed input
248 if (argc
< 1 || !argv
)
251 error("missing or null command line arguments");
254 for (i
= 0; i
< argc
; i
++)
260 #if __DMC__ // DMC unique support for response files
261 if (response_expand(&argc
,&argv
)) // expand response files
262 error("can't open response file");
265 files
.reserve(argc
- 1);
267 // Set default values
268 global
.params
.argv0
= argv
[0];
269 global
.params
.link
= 1;
270 global
.params
.useAssert
= 1;
271 global
.params
.useInvariants
= 1;
272 global
.params
.useIn
= 1;
273 global
.params
.useOut
= 1;
274 global
.params
.useArrayBounds
= 1;
275 global
.params
.useSwitchError
= 1;
276 global
.params
.useInline
= 0;
277 global
.params
.obj
= 1;
278 global
.params
.Dversion
= 2;
279 global
.params
.quiet
= 1;
281 global
.params
.linkswitches
= new Array();
282 global
.params
.libfiles
= new Array();
283 global
.params
.objfiles
= new Array();
284 global
.params
.ddocfiles
= new Array();
287 global
.params
.defaultlibname
= "phobos";
289 global
.params
.defaultlibname
= "phobos";
291 global
.params
.debuglibname
= global
.params
.defaultlibname
;
293 // Predefine version identifiers
294 VersionCondition::addPredefinedGlobalIdent("DigitalMars");
296 VersionCondition::addPredefinedGlobalIdent("Windows");
297 VersionCondition::addPredefinedGlobalIdent("Win32");
298 global
.params
.isWindows
= 1;
301 VersionCondition::addPredefinedGlobalIdent("linux");
302 global
.params
.isLinux
= 1;
304 VersionCondition::addPredefinedGlobalIdent("X86");
305 VersionCondition::addPredefinedGlobalIdent("LittleEndian");
306 //VersionCondition::addPredefinedGlobalIdent("D_Bits");
307 VersionCondition::addPredefinedGlobalIdent("D_InlineAsm");
308 VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86");
310 VersionCondition::addPredefinedGlobalIdent("D_Version2");
312 VersionCondition::addPredefinedGlobalIdent("all");
315 inifile(argv
[0], "sc.ini");
318 inifile(argv
[0], "dmd.conf");
320 getenv_setargv("DFLAGS", &argc
, &argv
);
323 for (i
= 0; i
< argc
; i
++)
325 printf("argv[%d] = '%s'\n", i
, argv
[i
]);
329 for (i
= 1; i
< argc
; i
++)
334 if (strcmp(p
+ 1, "d") == 0)
335 global
.params
.useDeprecated
= 1;
336 else if (strcmp(p
+ 1, "c") == 0)
337 global
.params
.link
= 0;
338 else if (strcmp(p
+ 1, "cov") == 0)
339 global
.params
.cov
= 1;
340 else if (strcmp(p
+ 1, "fPIC") == 0)
341 global
.params
.pic
= 1;
342 else if (strcmp(p
+ 1, "multiobj") == 0)
343 global
.params
.multiobj
= 1;
344 else if (strcmp(p
+ 1, "g") == 0)
345 global
.params
.symdebug
= 1;
346 else if (strcmp(p
+ 1, "gc") == 0)
347 global
.params
.symdebug
= 2;
348 else if (strcmp(p
+ 1, "gt") == 0)
349 { error("use -profile instead of -gt\n");
350 global
.params
.trace
= 1;
352 else if (strcmp(p
+ 1, "profile") == 0)
353 global
.params
.trace
= 1;
354 else if (strcmp(p
+ 1, "v") == 0)
355 global
.params
.verbose
= 1;
356 else if (strcmp(p
+ 1, "v1") == 0)
359 global
.params
.Dversion
= 1;
361 error("use DMD 1.0 series compilers for -v1 switch");
365 else if (strcmp(p
+ 1, "w") == 0)
366 global
.params
.warnings
= 1;
367 else if (strcmp(p
+ 1, "O") == 0)
368 global
.params
.optimize
= 1;
369 else if (p
[1] == 'o')
374 global
.params
.obj
= 0;
380 global
.params
.objdir
= p
+ 3;
386 global
.params
.objname
= p
+ 3;
392 global
.params
.preservePaths
= 1;
396 error("-o no longer supported, use -of or -od");
403 else if (p
[1] == 'D')
404 { global
.params
.doDocComments
= 1;
410 global
.params
.docdir
= p
+ 3;
415 global
.params
.docname
= p
+ 3;
426 else if (p
[1] == 'H')
427 { global
.params
.doHdrGeneration
= 1;
433 global
.params
.hdrdir
= p
+ 3;
439 global
.params
.hdrname
= p
+ 3;
450 else if (strcmp(p
+ 1, "ignore") == 0)
451 global
.params
.ignoreUnsupportedPragmas
= 1;
452 else if (strcmp(p
+ 1, "inline") == 0)
453 global
.params
.useInline
= 1;
454 else if (strcmp(p
+ 1, "lib") == 0)
455 global
.params
.lib
= 1;
456 else if (strcmp(p
+ 1, "nofloat") == 0)
457 global
.params
.nofloat
= 1;
458 else if (strcmp(p
+ 1, "quiet") == 0)
459 global
.params
.quiet
= 1;
460 else if (strcmp(p
+ 1, "release") == 0)
461 global
.params
.release
= 1;
462 else if (strcmp(p
+ 1, "unittest") == 0)
463 global
.params
.useUnitTests
= 1;
464 else if (p
[1] == 'I')
466 if (!global
.params
.imppath
)
467 global
.params
.imppath
= new Array();
468 global
.params
.imppath
->push(p
+ 2);
470 else if (p
[1] == 'J')
472 if (!global
.params
.fileImppath
)
473 global
.params
.fileImppath
= new Array();
474 global
.params
.fileImppath
->push(p
+ 2);
476 else if (memcmp(p
+ 1, "debug", 5) == 0 && p
[6] != 'l')
488 level
= strtol(p
+ 7, &p
, 10);
489 if (*p
|| errno
|| level
> INT_MAX
)
491 DebugCondition::setGlobalLevel((int)level
);
493 else if (Lexer::isValidIdentifier(p
+ 7))
494 DebugCondition::addGlobalIdent(p
+ 7);
501 global
.params
.debuglevel
= 1;
503 else if (memcmp(p
+ 1, "version", 5) == 0)
507 // -version=identifier
514 level
= strtol(p
+ 9, &p
, 10);
515 if (*p
|| errno
|| level
> INT_MAX
)
517 VersionCondition::setGlobalLevel((int)level
);
519 else if (Lexer::isValidIdentifier(p
+ 9))
520 VersionCondition::addGlobalIdent(p
+ 9);
527 else if (strcmp(p
+ 1, "-b") == 0)
528 global
.params
.debugb
= 1;
529 else if (strcmp(p
+ 1, "-c") == 0)
530 global
.params
.debugc
= 1;
531 else if (strcmp(p
+ 1, "-f") == 0)
532 global
.params
.debugf
= 1;
533 else if (strcmp(p
+ 1, "-help") == 0)
537 else if (strcmp(p
+ 1, "-r") == 0)
538 global
.params
.debugr
= 1;
539 else if (strcmp(p
+ 1, "-x") == 0)
540 global
.params
.debugx
= 1;
541 else if (strcmp(p
+ 1, "-y") == 0)
542 global
.params
.debugy
= 1;
543 else if (p
[1] == 'L')
545 global
.params
.linkswitches
->push(p
+ 2);
547 else if (memcmp(p
+ 1, "defaultlib=", 11) == 0)
549 global
.params
.defaultlibname
= p
+ 1 + 11;
551 else if (memcmp(p
+ 1, "debuglib=", 9) == 0)
553 global
.params
.debuglibname
= p
+ 1 + 9;
555 else if (memcmp(p
+ 1, "man", 3) == 0)
559 browse("http://www.digitalmars.com/d/1.0/dmd-windows.html");
561 browse("http://www.digitalmars.com/d/2.0/dmd-windows.html");
566 browse("http://www.digitalmars.com/d/1.0/dmd-linux.html");
568 browse("http://www.digitalmars.com/d/2.0/dmd-linux.html");
573 else if (strcmp(p
+ 1, "run") == 0)
574 { global
.params
.run
= 1;
575 global
.params
.runargs_length
= ((i
>= argcstart
) ? argc
: argcstart
) - i
- 1;
576 if (global
.params
.runargs_length
)
578 files
.push(argv
[i
+ 1]);
579 global
.params
.runargs
= &argv
[i
+ 2];
580 i
+= global
.params
.runargs_length
;
581 global
.params
.runargs_length
--;
584 { global
.params
.run
= 0;
591 error("unrecognized switch '%s'", argv
[i
]);
595 error("argument expected for switch '%s'", argv
[i
]);
602 char *ext
= FileName::ext(p
);
603 if (ext
&& stricmp(ext
, "exe") == 0)
605 global
.params
.objname
= p
;
621 if (global
.params
.release
)
622 { global
.params
.useInvariants
= 0;
623 global
.params
.useIn
= 0;
624 global
.params
.useOut
= 0;
625 global
.params
.useAssert
= 0;
626 global
.params
.useArrayBounds
= 0;
627 global
.params
.useSwitchError
= 0;
630 if (global
.params
.run
)
631 global
.params
.quiet
= 1;
633 if (global
.params
.useUnitTests
)
634 global
.params
.useAssert
= 1;
636 if (!global
.params
.obj
|| global
.params
.lib
)
637 global
.params
.link
= 0;
639 if (global
.params
.link
)
641 global
.params
.exefile
= global
.params
.objname
;
642 global
.params
.oneobj
= 1;
643 if (global
.params
.objname
)
645 /* Use this to name the one object file with the same
646 * name as the exe file.
648 global
.params
.objname
= FileName::forceExt(global
.params
.objname
, global
.obj_ext
)->toChars();
650 /* If output directory is given, use that path rather than
653 if (global
.params
.objdir
)
654 { char *name
= FileName::name(global
.params
.objname
);
655 global
.params
.objname
= FileName::combine(global
.params
.objdir
, name
);
659 else if (global
.params
.lib
)
661 global
.params
.libname
= global
.params
.objname
;
662 global
.params
.objname
= NULL
;
664 // Haven't investigated handling these options with multiobj
665 if (!global
.params
.cov
&& !global
.params
.trace
)
666 global
.params
.multiobj
= 1;
668 else if (global
.params
.run
)
670 error("flags conflict with -run");
675 if (global
.params
.objname
&& files
.dim
> 1)
677 global
.params
.oneobj
= 1;
678 //error("multiple source files, but only one .obj name");
682 if (global
.params
.cov
)
683 VersionCondition::addPredefinedGlobalIdent("D_Coverage");
685 if (global
.params
.useUnitTests
)
686 VersionCondition::addPredefinedGlobalIdent("unittest");
697 //printf("%d source files\n",files.dim);
699 // Build import search path
700 if (global
.params
.imppath
)
702 for (i
= 0; i
< global
.params
.imppath
->dim
; i
++)
704 char *path
= (char *)global
.params
.imppath
->data
[i
];
705 Array
*a
= FileName::splitPath(path
);
710 global
.path
= new Array();
711 global
.path
->append(a
);
716 // Build string import search path
717 if (global
.params
.fileImppath
)
719 for (i
= 0; i
< global
.params
.fileImppath
->dim
; i
++)
721 char *path
= (char *)global
.params
.fileImppath
->data
[i
];
722 Array
*a
= FileName::splitPath(path
);
726 if (!global
.filePath
)
727 global
.filePath
= new Array();
728 global
.filePath
->append(a
);
735 modules
.reserve(files
.dim
);
737 for (i
= 0; i
< files
.dim
; i
++)
743 p
= (char *) files
.data
[i
];
746 // Convert / to \ so linker will work
747 for (int i
= 0; p
[i
]; i
++)
754 p
= FileName::name(p
); // strip path
755 ext
= FileName::ext(p
);
757 { /* Deduce what to do with a file based on its extension
760 if (strcmp(ext
, global
.obj_ext
) == 0)
762 if (stricmp(ext
, global
.obj_ext
) == 0)
765 global
.params
.objfiles
->push(files
.data
[i
]);
766 libmodules
.push(files
.data
[i
]);
771 if (strcmp(ext
, global
.lib_ext
) == 0)
773 if (stricmp(ext
, global
.lib_ext
) == 0)
776 global
.params
.libfiles
->push(files
.data
[i
]);
777 libmodules
.push(files
.data
[i
]);
781 if (strcmp(ext
, global
.ddoc_ext
) == 0)
783 global
.params
.ddocfiles
->push(files
.data
[i
]);
788 if (stricmp(ext
, "res") == 0)
790 global
.params
.resfile
= (char *)files
.data
[i
];
794 if (stricmp(ext
, "def") == 0)
796 global
.params
.deffile
= (char *)files
.data
[i
];
800 if (stricmp(ext
, "exe") == 0)
802 assert(0); // should have already been handled
806 /* Examine extension to see if it is a valid
807 * D source file extension
809 isDltFile
= (stricmp(ext
, global
.dlt_ext
) == 0);
810 if (stricmp(ext
, global
.mars_ext
) == 0 ||
811 stricmp(ext
, global
.dlt_ext
) == 0 ||
812 stricmp(ext
, "dd") == 0 ||
813 stricmp(ext
, "htm") == 0 ||
814 stricmp(ext
, "html") == 0 ||
815 stricmp(ext
, "xhtml") == 0)
818 ext
--; // skip onto '.'
820 name
= (char *)mem
.malloc((ext
- p
) + 1);
821 memcpy(name
, p
, ext
- p
);
822 name
[ext
- p
] = 0; // strip extension
825 strcmp(name
, "..") == 0 ||
826 strcmp(name
, ".") == 0)
829 error("invalid file name '%s'", (char *)files
.data
[i
]);
834 { error("unrecognized file extension %s\n", ext
);
844 /* At this point, name is the D source file name stripped of
845 * its path and extension.
848 Identifier
*id
= new Identifier(name
, 0);
849 m
= new Module((char *) files
.data
[i
], id
, global
.params
.doDocComments
, global
.params
.doHdrGeneration
, isDltFile
);
853 { global
.params
.objfiles
->push(m
->objfile
->name
->str
);
862 // Read files, parse them
864 for (i
= 0; i
< modules
.dim
; i
++)
866 m
= (Module
*)modules
.data
[i
];
867 if (global
.params
.verbose
)
868 printf("parse %s\n", m
->toChars());
869 if (!Module::rootModule
)
870 Module::rootModule
= m
;
872 if (!global
.params
.oneobj
|| i
== 0 || m
->isDocFile
)
881 // Remove m from list of modules
885 // Remove m's object file from list of object files
886 for (int j
= 0; j
< global
.params
.objfiles
->dim
; j
++)
888 if (m
->objfile
->name
->str
== global
.params
.objfiles
->data
[j
])
890 global
.params
.objfiles
->remove(j
);
895 if (global
.params
.objfiles
->dim
== 0)
896 global
.params
.link
= 0;
899 if (anydocfiles
&& modules
.dim
&&
900 (global
.params
.oneobj
|| global
.params
.objname
))
902 error("conflicting Ddoc and obj generation options");
908 if (global
.params
.doHdrGeneration
)
910 /* Generate 'header' import files.
911 * Since 'header' import files must be independent of command
912 * line switches and what else is imported, they are generated
913 * before any semantic analysis.
915 for (i
= 0; i
< modules
.dim
; i
++)
917 m
= (Module
*)modules
.data
[i
];
918 if (global
.params
.verbose
)
919 printf("import %s\n", m
->toChars());
927 // Do semantic analysis
928 for (i
= 0; i
< modules
.dim
; i
++)
930 m
= (Module
*)modules
.data
[i
];
931 if (global
.params
.verbose
)
932 printf("semantic %s\n", m
->toChars());
938 // Do pass 2 semantic analysis
939 for (i
= 0; i
< modules
.dim
; i
++)
941 m
= (Module
*)modules
.data
[i
];
942 if (global
.params
.verbose
)
943 printf("semantic2 %s\n", m
->toChars());
949 // Do pass 3 semantic analysis
950 for (i
= 0; i
< modules
.dim
; i
++)
952 m
= (Module
*)modules
.data
[i
];
953 if (global
.params
.verbose
)
954 printf("semantic3 %s\n", m
->toChars());
960 // Scan for functions to inline
961 if (global
.params
.useInline
)
963 /* The problem with useArrayBounds and useAssert is that the
964 * module being linked to may not have generated them, so if
965 * we inline functions from those modules, the symbols for them will
966 * not be found at link time.
968 if (!global
.params
.useArrayBounds
&& !global
.params
.useAssert
)
970 // Do pass 3 semantic analysis on all imported modules,
971 // since otherwise functions in them cannot be inlined
972 for (i
= 0; i
< Module::amodules
.dim
; i
++)
974 m
= (Module
*)Module::amodules
.data
[i
];
975 if (global
.params
.verbose
)
976 printf("semantic3 %s\n", m
->toChars());
983 for (i
= 0; i
< modules
.dim
; i
++)
985 m
= (Module
*)modules
.data
[i
];
986 if (global
.params
.verbose
)
987 printf("inline scan %s\n", m
->toChars());
994 Library
*library
= NULL
;
995 if (global
.params
.lib
)
997 library
= new Library();
998 library
->setFilename(global
.params
.objdir
, global
.params
.libname
);
1000 // Add input object and input library files to output library
1001 for (int i
= 0; i
< libmodules
.dim
; i
++)
1003 char *p
= (char *)libmodules
.data
[i
];
1004 library
->addObject(p
, NULL
, 0);
1008 // Generate output files
1009 if (global
.params
.oneobj
)
1011 for (i
= 0; i
< modules
.dim
; i
++)
1013 m
= (Module
*)modules
.data
[i
];
1014 if (global
.params
.verbose
)
1015 printf("code %s\n", m
->toChars());
1017 obj_start(m
->srcfile
->toChars());
1019 if (!global
.errors
&& global
.params
.doDocComments
)
1022 if (!global
.errors
&& modules
.dim
)
1024 obj_end(library
, ((Module
*)modules
.data
[0])->objfile
);
1029 for (i
= 0; i
< modules
.dim
; i
++)
1031 m
= (Module
*)modules
.data
[i
];
1032 if (global
.params
.verbose
)
1033 printf("code %s\n", m
->toChars());
1034 if (global
.params
.obj
)
1035 { obj_start(m
->srcfile
->toChars());
1036 m
->genobjfile(global
.params
.multiobj
);
1037 obj_end(library
, m
->objfile
);
1038 obj_write_deferred(library
);
1042 if (!global
.params
.lib
)
1047 if (global
.params
.doDocComments
)
1053 if (global
.params
.lib
&& !global
.errors
)
1058 __except (__ehfilter(GetExceptionInformation()))
1060 printf("Stack overflow\n");
1068 if (!global
.params
.objfiles
->dim
)
1070 if (global
.params
.link
)
1071 error("no object files to link");
1075 if (global
.params
.link
)
1078 if (global
.params
.run
)
1082 status
= runProgram();
1084 /* Delete .obj files and .exe file
1086 for (i
= 0; i
< modules
.dim
; i
++)
1088 Module
*m
= (Module
*)modules
.data
[i
];
1090 if (global
.params
.oneobj
)
1103 /***********************************
1104 * Parse and append contents of environment variable envvar
1105 * to argc and argv[].
1106 * The string is separated into arguments, processing \ and ".
1109 void getenv_setargv(const char *envvar
, int *pargc
, char** *pargv
)
1116 int wildcard
; // do wildcard expansion
1122 env
= getenv(envvar
);
1126 env
= mem
.strdup(env
); // create our own writable copy
1132 for (int i
= 0; i
< argc
; i
++)
1133 argv
->data
[i
] = (void *)(*pargv
)[i
];
1135 j
= 1; // leave argv[0] alone
1152 argv
->push(env
); // append
1153 //argv->insert(j, env); // insert at position j
1182 //wildcardexpand(); // not implemented
1193 //wildcardexpand(); // not implemented
1209 *pargv
= (char **)argv
->data
;
1214 long __cdecl
__ehfilter(LPEXCEPTION_POINTERS ep
)
1216 //printf("%x\n", ep->ExceptionRecord->ExceptionCode);
1217 if (ep
->ExceptionRecord
->ExceptionCode
== STATUS_STACK_OVERFLOW
)
1220 return EXCEPTION_EXECUTE_HANDLER
;
1223 return EXCEPTION_CONTINUE_SEARCH
;