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
);
84 copyright
= "Copyright (c) 1999-2008 by Digital Mars";
85 written
= "written by Walter Bright";
87 global
.structalign
= 8;
89 memset(¶ms
, 0, sizeof(Param
));
99 buf
.printf("%s", filename
);
104 buf
.printf("(%d)", linnum
);
106 buf
.printf(":%u", linnum
);
109 return (char *)buf
.extractData();
112 Loc::Loc(Module
*mod
, unsigned linnum
)
114 this->linnum
= linnum
;
115 this->filename
= mod
? mod
->srcfile
->toChars() : NULL
;
118 /**************************************
119 * Print error message and exit.
122 void error(Loc loc
, const char *format
, ...)
125 va_start(ap
, format
);
126 verror(loc
, format
, ap
);
130 void verror(Loc loc
, const char *format
, va_list ap
)
134 char *p
= loc
.toChars();
137 fprintf(stdmsg
, "%s: ", p
);
140 fprintf(stdmsg
, "Error: ");
141 vfprintf(stdmsg
, format
, ap
);
142 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/2.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 -version=level compile in version code >= level\n\
225 -version=ident compile in version code identified by ident\n\
226 -w enable warnings\n\
229 " @cmdfile read arguments from cmdfile\n"
236 int main(int argc
, char *argv
[])
243 int status
= EXIT_SUCCESS
;
244 int argcstart
= argc
;
246 // Check for malformed input
247 if (argc
< 1 || !argv
)
250 error("missing or null command line arguments");
253 for (i
= 0; i
< argc
; i
++)
259 #if __DMC__ // DMC unique support for response files
260 if (response_expand(&argc
,&argv
)) // expand response files
261 error("can't open response file");
264 files
.reserve(argc
- 1);
266 // Set default values
267 global
.params
.argv0
= argv
[0];
268 global
.params
.link
= 1;
269 global
.params
.useAssert
= 1;
270 global
.params
.useInvariants
= 1;
271 global
.params
.useIn
= 1;
272 global
.params
.useOut
= 1;
273 global
.params
.useArrayBounds
= 1;
274 global
.params
.useSwitchError
= 1;
275 global
.params
.useInline
= 0;
276 global
.params
.obj
= 1;
277 global
.params
.Dversion
= 2;
278 global
.params
.quiet
= 1;
280 global
.params
.linkswitches
= new Array();
281 global
.params
.libfiles
= new Array();
282 global
.params
.objfiles
= new Array();
283 global
.params
.ddocfiles
= new Array();
286 global
.params
.defaultlibname
= "phobos";
288 global
.params
.defaultlibname
= "phobos2";
290 global
.params
.debuglibname
= global
.params
.defaultlibname
;
292 // Predefine version identifiers
293 VersionCondition::addPredefinedGlobalIdent("DigitalMars");
295 VersionCondition::addPredefinedGlobalIdent("Windows");
296 VersionCondition::addPredefinedGlobalIdent("Win32");
297 global
.params
.isWindows
= 1;
300 VersionCondition::addPredefinedGlobalIdent("linux");
301 global
.params
.isLinux
= 1;
303 VersionCondition::addPredefinedGlobalIdent("X86");
304 VersionCondition::addPredefinedGlobalIdent("LittleEndian");
305 //VersionCondition::addPredefinedGlobalIdent("D_Bits");
306 VersionCondition::addPredefinedGlobalIdent("D_InlineAsm");
307 VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86");
309 VersionCondition::addPredefinedGlobalIdent("D_Version2");
311 VersionCondition::addPredefinedGlobalIdent("all");
314 inifile(argv
[0], "sc.ini");
317 inifile(argv
[0], "dmd.conf");
319 getenv_setargv("DFLAGS", &argc
, &argv
);
322 for (i
= 0; i
< argc
; i
++)
324 printf("argv[%d] = '%s'\n", i
, argv
[i
]);
328 for (i
= 1; i
< argc
; i
++)
333 if (strcmp(p
+ 1, "d") == 0)
334 global
.params
.useDeprecated
= 1;
335 else if (strcmp(p
+ 1, "c") == 0)
336 global
.params
.link
= 0;
337 else if (strcmp(p
+ 1, "cov") == 0)
338 global
.params
.cov
= 1;
339 else if (strcmp(p
+ 1, "fPIC") == 0)
340 global
.params
.pic
= 1;
341 else if (strcmp(p
+ 1, "multiobj") == 0)
342 global
.params
.multiobj
= 1;
343 else if (strcmp(p
+ 1, "g") == 0)
344 global
.params
.symdebug
= 1;
345 else if (strcmp(p
+ 1, "gc") == 0)
346 global
.params
.symdebug
= 2;
347 else if (strcmp(p
+ 1, "gt") == 0)
348 { error("use -profile instead of -gt\n");
349 global
.params
.trace
= 1;
351 else if (strcmp(p
+ 1, "profile") == 0)
352 global
.params
.trace
= 1;
353 else if (strcmp(p
+ 1, "v") == 0)
354 global
.params
.verbose
= 1;
355 else if (strcmp(p
+ 1, "v1") == 0)
358 global
.params
.Dversion
= 1;
360 error("use DMD 1.0 series compilers for -v1 switch");
364 else if (strcmp(p
+ 1, "w") == 0)
365 global
.params
.warnings
= 1;
366 else if (strcmp(p
+ 1, "O") == 0)
367 global
.params
.optimize
= 1;
368 else if (p
[1] == 'o')
373 global
.params
.obj
= 0;
379 global
.params
.objdir
= p
+ 3;
385 global
.params
.objname
= p
+ 3;
391 global
.params
.preservePaths
= 1;
395 error("-o no longer supported, use -of or -od");
402 else if (p
[1] == 'D')
403 { global
.params
.doDocComments
= 1;
409 global
.params
.docdir
= p
+ 3;
414 global
.params
.docname
= p
+ 3;
425 else if (p
[1] == 'H')
426 { global
.params
.doHdrGeneration
= 1;
432 global
.params
.hdrdir
= p
+ 3;
438 global
.params
.hdrname
= p
+ 3;
449 else if (strcmp(p
+ 1, "ignore") == 0)
450 global
.params
.ignoreUnsupportedPragmas
= 1;
451 else if (strcmp(p
+ 1, "inline") == 0)
452 global
.params
.useInline
= 1;
453 else if (strcmp(p
+ 1, "lib") == 0)
454 global
.params
.lib
= 1;
455 else if (strcmp(p
+ 1, "nofloat") == 0)
456 global
.params
.nofloat
= 1;
457 else if (strcmp(p
+ 1, "quiet") == 0)
458 global
.params
.quiet
= 1;
459 else if (strcmp(p
+ 1, "release") == 0)
460 global
.params
.release
= 1;
461 else if (strcmp(p
+ 1, "unittest") == 0)
462 global
.params
.useUnitTests
= 1;
463 else if (p
[1] == 'I')
465 if (!global
.params
.imppath
)
466 global
.params
.imppath
= new Array();
467 global
.params
.imppath
->push(p
+ 2);
469 else if (p
[1] == 'J')
471 if (!global
.params
.fileImppath
)
472 global
.params
.fileImppath
= new Array();
473 global
.params
.fileImppath
->push(p
+ 2);
475 else if (memcmp(p
+ 1, "debug", 5) == 0 && p
[6] != 'l')
487 level
= strtol(p
+ 7, &p
, 10);
488 if (*p
|| errno
|| level
> INT_MAX
)
490 DebugCondition::setGlobalLevel((int)level
);
492 else if (Lexer::isValidIdentifier(p
+ 7))
493 DebugCondition::addGlobalIdent(p
+ 7);
500 global
.params
.debuglevel
= 1;
502 else if (memcmp(p
+ 1, "version", 5) == 0)
506 // -version=identifier
513 level
= strtol(p
+ 9, &p
, 10);
514 if (*p
|| errno
|| level
> INT_MAX
)
516 VersionCondition::setGlobalLevel((int)level
);
518 else if (Lexer::isValidIdentifier(p
+ 9))
519 VersionCondition::addGlobalIdent(p
+ 9);
526 else if (strcmp(p
+ 1, "-b") == 0)
527 global
.params
.debugb
= 1;
528 else if (strcmp(p
+ 1, "-c") == 0)
529 global
.params
.debugc
= 1;
530 else if (strcmp(p
+ 1, "-f") == 0)
531 global
.params
.debugf
= 1;
532 else if (strcmp(p
+ 1, "-help") == 0)
536 else if (strcmp(p
+ 1, "-r") == 0)
537 global
.params
.debugr
= 1;
538 else if (strcmp(p
+ 1, "-x") == 0)
539 global
.params
.debugx
= 1;
540 else if (strcmp(p
+ 1, "-y") == 0)
541 global
.params
.debugy
= 1;
542 else if (p
[1] == 'L')
544 global
.params
.linkswitches
->push(p
+ 2);
546 else if (memcmp(p
+ 1, "defaultlib=", 11) == 0)
548 global
.params
.defaultlibname
= p
+ 1 + 11;
550 else if (memcmp(p
+ 1, "debuglib=", 9) == 0)
552 global
.params
.debuglibname
= p
+ 1 + 9;
554 else if (memcmp(p
+ 1, "man", 3) == 0)
558 browse("http://www.digitalmars.com/d/1.0/dmd-windows.html");
560 browse("http://www.digitalmars.com/d/2.0/dmd-windows.html");
565 browse("http://www.digitalmars.com/d/1.0/dmd-linux.html");
567 browse("http://www.digitalmars.com/d/2.0/dmd-linux.html");
572 else if (strcmp(p
+ 1, "run") == 0)
573 { global
.params
.run
= 1;
574 global
.params
.runargs_length
= ((i
>= argcstart
) ? argc
: argcstart
) - i
- 1;
575 if (global
.params
.runargs_length
)
577 files
.push(argv
[i
+ 1]);
578 global
.params
.runargs
= &argv
[i
+ 2];
579 i
+= global
.params
.runargs_length
;
580 global
.params
.runargs_length
--;
583 { global
.params
.run
= 0;
590 error("unrecognized switch '%s'", argv
[i
]);
594 error("argument expected for switch '%s'", argv
[i
]);
601 char *ext
= FileName::ext(p
);
602 if (ext
&& stricmp(ext
, "exe") == 0)
604 global
.params
.objname
= p
;
620 if (global
.params
.release
)
621 { global
.params
.useInvariants
= 0;
622 global
.params
.useIn
= 0;
623 global
.params
.useOut
= 0;
624 global
.params
.useAssert
= 0;
625 global
.params
.useArrayBounds
= 0;
626 global
.params
.useSwitchError
= 0;
629 if (global
.params
.run
)
630 global
.params
.quiet
= 1;
632 if (global
.params
.useUnitTests
)
633 global
.params
.useAssert
= 1;
635 if (!global
.params
.obj
|| global
.params
.lib
)
636 global
.params
.link
= 0;
638 if (global
.params
.link
)
640 global
.params
.exefile
= global
.params
.objname
;
641 global
.params
.oneobj
= 1;
642 if (global
.params
.objname
)
644 /* Use this to name the one object file with the same
645 * name as the exe file.
647 global
.params
.objname
= FileName::forceExt(global
.params
.objname
, global
.obj_ext
)->toChars();
649 /* If output directory is given, use that path rather than
652 if (global
.params
.objdir
)
653 { char *name
= FileName::name(global
.params
.objname
);
654 global
.params
.objname
= FileName::combine(global
.params
.objdir
, name
);
658 else if (global
.params
.lib
)
660 global
.params
.libname
= global
.params
.objname
;
661 global
.params
.objname
= NULL
;
663 // Haven't investigated handling these options with multiobj
664 if (!global
.params
.cov
&& !global
.params
.trace
)
665 global
.params
.multiobj
= 1;
667 else if (global
.params
.run
)
669 error("flags conflict with -run");
674 if (global
.params
.objname
&& files
.dim
> 1)
676 global
.params
.oneobj
= 1;
677 //error("multiple source files, but only one .obj name");
681 if (global
.params
.cov
)
682 VersionCondition::addPredefinedGlobalIdent("D_Coverage");
684 if (global
.params
.useUnitTests
)
685 VersionCondition::addPredefinedGlobalIdent("unittest");
696 //printf("%d source files\n",files.dim);
698 // Build import search path
699 if (global
.params
.imppath
)
701 for (i
= 0; i
< global
.params
.imppath
->dim
; i
++)
703 char *path
= (char *)global
.params
.imppath
->data
[i
];
704 Array
*a
= FileName::splitPath(path
);
709 global
.path
= new Array();
710 global
.path
->append(a
);
715 // Build string import search path
716 if (global
.params
.fileImppath
)
718 for (i
= 0; i
< global
.params
.fileImppath
->dim
; i
++)
720 char *path
= (char *)global
.params
.fileImppath
->data
[i
];
721 Array
*a
= FileName::splitPath(path
);
725 if (!global
.filePath
)
726 global
.filePath
= new Array();
727 global
.filePath
->append(a
);
734 modules
.reserve(files
.dim
);
736 for (i
= 0; i
< files
.dim
; i
++)
741 p
= (char *) files
.data
[i
];
744 // Convert / to \ so linker will work
745 for (int i
= 0; p
[i
]; i
++)
752 p
= FileName::name(p
); // strip path
753 ext
= FileName::ext(p
);
755 { /* Deduce what to do with a file based on its extension
758 if (strcmp(ext
, global
.obj_ext
) == 0)
760 if (stricmp(ext
, global
.obj_ext
) == 0)
763 global
.params
.objfiles
->push(files
.data
[i
]);
764 libmodules
.push(files
.data
[i
]);
769 if (strcmp(ext
, global
.lib_ext
) == 0)
771 if (stricmp(ext
, global
.lib_ext
) == 0)
774 global
.params
.libfiles
->push(files
.data
[i
]);
775 libmodules
.push(files
.data
[i
]);
779 if (strcmp(ext
, global
.ddoc_ext
) == 0)
781 global
.params
.ddocfiles
->push(files
.data
[i
]);
786 if (stricmp(ext
, "res") == 0)
788 global
.params
.resfile
= (char *)files
.data
[i
];
792 if (stricmp(ext
, "def") == 0)
794 global
.params
.deffile
= (char *)files
.data
[i
];
798 if (stricmp(ext
, "exe") == 0)
800 assert(0); // should have already been handled
804 /* Examine extension to see if it is a valid
805 * D source file extension
807 if (stricmp(ext
, global
.mars_ext
) == 0 ||
808 stricmp(ext
, "dd") == 0 ||
809 stricmp(ext
, "htm") == 0 ||
810 stricmp(ext
, "html") == 0 ||
811 stricmp(ext
, "xhtml") == 0)
813 ext
--; // skip onto '.'
815 name
= (char *)mem
.malloc((ext
- p
) + 1);
816 memcpy(name
, p
, ext
- p
);
817 name
[ext
- p
] = 0; // strip extension
820 strcmp(name
, "..") == 0 ||
821 strcmp(name
, ".") == 0)
824 error("invalid file name '%s'", (char *)files
.data
[i
]);
829 { error("unrecognized file extension %s\n", ext
);
839 /* At this point, name is the D source file name stripped of
840 * its path and extension.
843 Identifier
*id
= new Identifier(name
, 0);
844 m
= new Module((char *) files
.data
[i
], id
, global
.params
.doDocComments
, global
.params
.doHdrGeneration
);
848 { global
.params
.objfiles
->push(m
->objfile
->name
->str
);
857 // Read files, parse them
859 for (i
= 0; i
< modules
.dim
; i
++)
861 m
= (Module
*)modules
.data
[i
];
862 if (global
.params
.verbose
)
863 printf("parse %s\n", m
->toChars());
864 if (!Module::rootModule
)
865 Module::rootModule
= m
;
867 if (!global
.params
.oneobj
|| i
== 0 || m
->isDocFile
)
876 // Remove m from list of modules
880 // Remove m's object file from list of object files
881 for (int j
= 0; j
< global
.params
.objfiles
->dim
; j
++)
883 if (m
->objfile
->name
->str
== global
.params
.objfiles
->data
[j
])
885 global
.params
.objfiles
->remove(j
);
890 if (global
.params
.objfiles
->dim
== 0)
891 global
.params
.link
= 0;
894 if (anydocfiles
&& modules
.dim
&&
895 (global
.params
.oneobj
|| global
.params
.objname
))
897 error("conflicting Ddoc and obj generation options");
903 if (global
.params
.doHdrGeneration
)
905 /* Generate 'header' import files.
906 * Since 'header' import files must be independent of command
907 * line switches and what else is imported, they are generated
908 * before any semantic analysis.
910 for (i
= 0; i
< modules
.dim
; i
++)
912 m
= (Module
*)modules
.data
[i
];
913 if (global
.params
.verbose
)
914 printf("import %s\n", m
->toChars());
922 // Do semantic analysis
923 for (i
= 0; i
< modules
.dim
; i
++)
925 m
= (Module
*)modules
.data
[i
];
926 if (global
.params
.verbose
)
927 printf("semantic %s\n", m
->toChars());
933 // Do pass 2 semantic analysis
934 for (i
= 0; i
< modules
.dim
; i
++)
936 m
= (Module
*)modules
.data
[i
];
937 if (global
.params
.verbose
)
938 printf("semantic2 %s\n", m
->toChars());
944 // Do pass 3 semantic analysis
945 for (i
= 0; i
< modules
.dim
; i
++)
947 m
= (Module
*)modules
.data
[i
];
948 if (global
.params
.verbose
)
949 printf("semantic3 %s\n", m
->toChars());
955 // Scan for functions to inline
956 if (global
.params
.useInline
)
958 /* The problem with useArrayBounds and useAssert is that the
959 * module being linked to may not have generated them, so if
960 * we inline functions from those modules, the symbols for them will
961 * not be found at link time.
963 if (!global
.params
.useArrayBounds
&& !global
.params
.useAssert
)
965 // Do pass 3 semantic analysis on all imported modules,
966 // since otherwise functions in them cannot be inlined
967 for (i
= 0; i
< Module::amodules
.dim
; i
++)
969 m
= (Module
*)Module::amodules
.data
[i
];
970 if (global
.params
.verbose
)
971 printf("semantic3 %s\n", m
->toChars());
978 for (i
= 0; i
< modules
.dim
; i
++)
980 m
= (Module
*)modules
.data
[i
];
981 if (global
.params
.verbose
)
982 printf("inline scan %s\n", m
->toChars());
989 Library
*library
= NULL
;
990 if (global
.params
.lib
)
992 library
= new Library();
993 library
->setFilename(global
.params
.objdir
, global
.params
.libname
);
995 // Add input object and input library files to output library
996 for (int i
= 0; i
< libmodules
.dim
; i
++)
998 char *p
= (char *)libmodules
.data
[i
];
999 library
->addObject(p
, NULL
, 0);
1003 // Generate output files
1004 if (global
.params
.oneobj
)
1006 for (i
= 0; i
< modules
.dim
; i
++)
1008 m
= (Module
*)modules
.data
[i
];
1009 if (global
.params
.verbose
)
1010 printf("code %s\n", m
->toChars());
1012 obj_start(m
->srcfile
->toChars());
1014 if (!global
.errors
&& global
.params
.doDocComments
)
1017 if (!global
.errors
&& modules
.dim
)
1019 obj_end(library
, ((Module
*)modules
.data
[0])->objfile
);
1024 for (i
= 0; i
< modules
.dim
; i
++)
1026 m
= (Module
*)modules
.data
[i
];
1027 if (global
.params
.verbose
)
1028 printf("code %s\n", m
->toChars());
1029 if (global
.params
.obj
)
1030 { obj_start(m
->srcfile
->toChars());
1031 m
->genobjfile(global
.params
.multiobj
);
1032 obj_end(library
, m
->objfile
);
1033 obj_write_deferred(library
);
1037 if (!global
.params
.lib
)
1042 if (global
.params
.doDocComments
)
1048 if (global
.params
.lib
&& !global
.errors
)
1053 __except (__ehfilter(GetExceptionInformation()))
1055 printf("Stack overflow\n");
1063 if (!global
.params
.objfiles
->dim
)
1065 if (global
.params
.link
)
1066 error("no object files to link");
1070 if (global
.params
.link
)
1073 if (global
.params
.run
)
1077 status
= runProgram();
1079 /* Delete .obj files and .exe file
1081 for (i
= 0; i
< modules
.dim
; i
++)
1083 Module
*m
= (Module
*)modules
.data
[i
];
1085 if (global
.params
.oneobj
)
1098 /***********************************
1099 * Parse and append contents of environment variable envvar
1100 * to argc and argv[].
1101 * The string is separated into arguments, processing \ and ".
1104 void getenv_setargv(const char *envvar
, int *pargc
, char** *pargv
)
1111 int wildcard
; // do wildcard expansion
1117 env
= getenv(envvar
);
1121 env
= mem
.strdup(env
); // create our own writable copy
1127 for (int i
= 0; i
< argc
; i
++)
1128 argv
->data
[i
] = (void *)(*pargv
)[i
];
1130 j
= 1; // leave argv[0] alone
1147 argv
->push(env
); // append
1148 //argv->insert(j, env); // insert at position j
1177 //wildcardexpand(); // not implemented
1188 //wildcardexpand(); // not implemented
1204 *pargv
= (char **)argv
->data
;
1209 long __cdecl
__ehfilter(LPEXCEPTION_POINTERS ep
)
1211 //printf("%x\n", ep->ExceptionRecord->ExceptionCode);
1212 if (ep
->ExceptionRecord
->ExceptionCode
== STATUS_STACK_OVERFLOW
)
1215 return EXCEPTION_EXECUTE_HANDLER
;
1218 return EXCEPTION_CONTINUE_SEARCH
;