├── ChangeLog ├── Makefile ├── README.md ├── dmd-script └── dmd-script.1 /ChangeLog: -------------------------------------------------------------------------------- 1 | 2013-03-23 Iain Buclaw 2 | 3 | * Create gdmd project. 4 | 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # -*- mode: makefile -*- 2 | 3 | # gdmd -- dmd-like wrapper for gdc. 4 | # Copyright (C) 2011, 2012 Free Software Foundation, Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GCC; see the file COPYING3. If not see 18 | # . 19 | 20 | 21 | DESTDIR = 22 | target_prefix = 23 | program_suffix = 24 | prefix = /usr/local 25 | bindir = $(prefix)/bin 26 | man1dir = $(prefix)/share/man/man1 27 | 28 | src = dmd-script 29 | man = dmd-script.1 30 | 31 | all: 32 | 33 | 34 | install: $(DESTDIR)$(bindir)/$(target_prefix)gdmd$(program_suffix) \ 35 | $(DESTDIR)$(man1dir)/$(target_prefix)gdmd$(program_suffix).1 36 | 37 | 38 | $(DESTDIR)$(bindir)/$(target_prefix)gdmd$(program_suffix): $(src) 39 | -rm -f $@ 40 | -install $< $@ 41 | 42 | 43 | $(DESTDIR)$(man1dir)/$(target_prefix)gdmd$(program_suffix).1: $(man) 44 | -rm -f $@ 45 | -install -m 644 $< $@ 46 | 47 | 48 | uninstall: 49 | -rm -f $(DESTDIR)$(bindir)/$(target_prefix)gdmd$(program_suffix) 50 | -rm -f $(DESTDIR)$(man1dir)/$(target_prefix)gdmd$(program_suffix).1 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | GDMD 2 | ==== 3 | 4 | A DMD-like wrapper for GDC. -------------------------------------------------------------------------------- /dmd-script: -------------------------------------------------------------------------------- 1 | #! /usr/bin/perl -w 2 | 3 | # GDC -- D front-end for GCC 4 | # Copyright (C) 2011, 2012 Free Software Foundation, Inc. 5 | # 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GCC; see the file COPYING3. If not see 18 | # . 19 | 20 | # This is a wrapper script for gdc that emulates the dmd command. 21 | # -f and -m options are passed to gdc. Extra options are: 22 | # 23 | # -vdmd Print commands executed by this wrapper script 24 | # -q,[,,,...] Pass the comma-separated arguments to gdc 25 | 26 | 27 | use strict; 28 | use warnings; 29 | use Cwd qw(abs_path); 30 | use FindBin qw($Bin); 31 | use File::Basename; 32 | use File::Spec; 33 | use File::Path; 34 | use File::Temp qw(tempdir); 35 | 36 | my $output_directory; 37 | my $output_parents; 38 | my $output_file; 39 | my $header_directory; 40 | my $header_file; 41 | my $documentation_directory; 42 | my $documentation_file; 43 | my $debug = 0; 44 | my $link = 1; 45 | my $header = 0; 46 | my $documentation = 0; 47 | my $json = 0; 48 | my $json_file; 49 | my $map = 0; 50 | my $map_file; 51 | my $run = 0; 52 | my $verbose = 0; 53 | my $show_commands = 0; 54 | my $print_version = 0; 55 | my $seen_all_sources_flag = 0; 56 | my $first_input_file; 57 | my $combine = 0; # Compile multiple sources into a single object file 58 | my $lib = 0; 59 | my $tmpdir; 60 | my %tmpdir_objs; 61 | my $stdin = 0; 62 | 63 | my @sources; 64 | my @objects; 65 | my @dobjects; 66 | 67 | my @out; 68 | my @link_out; 69 | my @run_args; 70 | 71 | # Use the gdc executable in the same directory as this script and account 72 | # for the target prefix. 73 | basename($0) =~ m/^(.*-)?g?dmd(-.*)?$/; 74 | my $target_prefix = $1?$1:""; 75 | my $gdc_dir = abs_path(dirname($0)); 76 | my $gdc = File::Spec->catfile( $gdc_dir, $target_prefix . "gdc" . ($2?$2:"")); 77 | 78 | sub osHasEXE() { 79 | return $^O =~ m/MS(DOS|Win32)|os2/i; # taken from File::Basename 80 | } 81 | 82 | sub targetHasEXE() { 83 | my $target = `$gdc -dumpmachine`; 84 | return $target =~ m/mingw/ || $target =~ m/cygwin/; 85 | } 86 | 87 | sub pathSep() { 88 | return ";" if $^O =~ m/MS(DOS|Win32)/i; 89 | return "," if $^O =~ m/MacOS/i; 90 | return ":"; 91 | } 92 | 93 | sub expandHome($) { 94 | my ($path) = (@_); 95 | if ( $^O !~ m/MS(DOS|Win32)|MacOS/i ) { 96 | $path =~ s/^~/$ENV{HOME}/; 97 | } 98 | return $path; 99 | } 100 | 101 | sub printUsage() { 102 | print < limit the number of error messages (0 means unlimited) 173 | -verrors=context (enabled by default) show diagnostic messages with context 174 | -verrors=spec show errors from speculative compiles such as __traits(compiles,...) 175 | --version print compiler version and exit 176 | -version=level compile in version code <= level 177 | -version=ident compile in version code identified by ident 178 | -vtemplates list statistics on template instantiations 179 | -vtls list all variables going into thread local storage 180 | -w enable warnings 181 | -wi enable informational warnings 182 | -X generate JSON file 183 | -Xffilename write JSON file to filename 184 | EOF 185 | ; 186 | } 187 | 188 | sub errorExit(@) { 189 | print STDERR "gdmd: ", @_, "\n" if @_; 190 | exit 1; 191 | } 192 | use subs qw(errorExit); 193 | 194 | sub readINI { 195 | # look for dmd.conf in the following sequence of directories: 196 | # - current working directory 197 | # - directory specified by the HOME environment variable 198 | # - directory gdmd resides in 199 | # - /etc directory 200 | my @confpaths = ("./", "$ENV{HOME}/", "$Bin/", "/etc/"); 201 | my $dmdconfpath = ""; 202 | my $dmdconf = ""; 203 | 204 | foreach my $confpath (@confpaths) { 205 | if (-e $confpath."dmd.conf") { 206 | $dmdconfpath = $confpath; 207 | $dmdconf = $confpath."dmd.conf"; 208 | last; 209 | } 210 | } 211 | 212 | if (-e $dmdconf) { 213 | open(DMDCONF, "<$dmdconf"); 214 | my $envsection = 0; 215 | 216 | while() { 217 | # Ignore all lines up to [Environment] section 218 | if ($_ =~ /^\s*\[\s*Environment\s*\]\s*$/) { 219 | $envsection = 1; 220 | next; 221 | } 222 | next if (!$envsection); 223 | 224 | # Ignore comments 225 | next if ($_ =~ /^\s*;/); 226 | # Ignore empty lines 227 | next if ($_ =~ /^\s*$/); 228 | 229 | # Check correct syntax 230 | $_ =~ /^\s*(\S+?)\s*=\s*(.*)\s*$/; 231 | if ($&) { 232 | my $VAR = $1; 233 | my $VAL = $2; 234 | # The special name %@P% is replaced with the path to dmd.conf 235 | $VAL =~ s/%\@P%/$dmdconfpath/g; 236 | # Names enclosed by %% are searched for in the existing environment and inserted 237 | while ($VAL =~ /%(\S+?)%/) { 238 | my $envp = $1; 239 | if ($ENV{$envp}) { 240 | $VAL =~ s/%$envp%/$ENV{$envp}/g; 241 | } else { 242 | $VAL =~ s/%$envp%//g; 243 | } 244 | } 245 | $ENV{$VAR} = "$VAL"; 246 | } else { 247 | errorExit "syntax error at line $. in file $dmdconf"; 248 | } 249 | } 250 | close DMDCONF; 251 | } 252 | } 253 | 254 | my $gcc_version = `$gdc -dumpversion`; 255 | my $gcc_maj; 256 | my $gcc_min; 257 | 258 | chomp $gcc_version; 259 | if ($gcc_version =~ m/^\d+$/) { 260 | ($gcc_maj, $gcc_min) = ($gcc_version, 0); 261 | } else { 262 | ($gcc_maj, $gcc_min) = ($gcc_version =~ m/^(\d+)\.(\d+)/); 263 | } 264 | 265 | #my $target_machine = `$gdc -dumpmachine`; 266 | #chomp $target_machine; 267 | 268 | sub addSourceFile($) { 269 | my ($arg) = @_; 270 | $first_input_file = $arg if ! $first_input_file; 271 | push @sources, $arg; 272 | } 273 | 274 | sub argCheck($$) { 275 | my ($name,$arg) = @_; 276 | errorExit "argument expected for switch '$name'" unless defined $arg; 277 | } 278 | 279 | sub determineARexe() { 280 | my $name = $target_prefix . 'ar'; 281 | $name .= '.exe' if (osHasEXE()); 282 | 283 | # Prefer the 'ar' in the same directory as gdc even if there is no 284 | # target prefix. 285 | my $path = File::Spec->catfile( $gdc_dir, $name ); 286 | return $path if -x $path; 287 | 288 | if ( length $target_prefix ) { 289 | foreach my $dir (split pathSep, $ENV{PATH}) { 290 | $path = File::Spec->catfile( $dir, $name ); 291 | return $name if -x $path; # Could return $path, but this looks better 292 | } 293 | errorExit "Could not find archiver command '$name'."; 294 | } else { 295 | return "ar"; 296 | } 297 | } 298 | 299 | sub determineARcommand() { 300 | my @exe = determineARexe(); 301 | return (@exe, 'cru'); 302 | } 303 | 304 | sub browse($) { 305 | my ($url) = @_; 306 | my @cmd; 307 | 308 | if ($^O =~ m/MSWin32/i) { 309 | @cmd = qw(cmd /c start); 310 | } elsif ($^O =~ m/darwin/i && 311 | -x '/usr/bin/open') { # MacOS X vs. just Darwin 312 | @cmd = 'open'; 313 | } elsif ($ENV{KDE_FULL_SESSION} eq 'true') { 314 | @cmd = qw(kfmclient exec); 315 | } elsif ($ENV{GNOME_DESKTOP_SESSION_ID} ne '') { 316 | @cmd = 'gnome-open'; 317 | } else { 318 | errorExit "Sorry, I do not know how to start your browser.\nManual URL: $url" 319 | } 320 | push @cmd, $url; 321 | system @cmd; 322 | print "Opening documentation page."; 323 | exit 0; 324 | } 325 | 326 | # Load dmd.conf before before parsing arguments. 327 | readINI(); 328 | 329 | if ($ENV{DFLAGS}) { 330 | push @ARGV, split /\s+/, $ENV{DFLAGS}; 331 | } 332 | 333 | my $arg_i = 0; 334 | while ( $arg_i < scalar(@ARGV) ) { 335 | my $arg = $ARGV[$arg_i++]; 336 | 337 | if ( $arg =~ m/^-allinst$/ ) { 338 | push @out, '-fall-instantiations' 339 | } elsif ( $arg eq '-arch' ) { 340 | push @out, '-arch', $ARGV[$arg_i++]; 341 | } elsif ( $arg =~ m/^-betterC$/ ) { 342 | push @out, '-fno-druntime' 343 | } elsif ( $arg =~ m/^-vcolumns$/ ) { 344 | # ignored 345 | } elsif ( $arg =~ m/^-boundscheck=(.*)$/ ) { 346 | push @out, "-fbounds-check=$1" 347 | } elsif ($arg =~ m/^-c$/ ) { 348 | $link = 0; 349 | } elsif ( $arg eq '-cov' ) { 350 | push @out, '-fprofile-arcs', '-ftest-coverage'; 351 | } elsif ( $arg =~ m/^-D$/ ) { 352 | $documentation = 1; 353 | } elsif ( $arg =~ m/^-Dd(.*)$/ ) { 354 | $documentation = 1; 355 | $documentation_directory = $1; 356 | } elsif ( $arg =~ m/^-Df(.*)$/ ) { 357 | $documentation = 1; 358 | $documentation_file = $1; 359 | } elsif ( $arg =~ m/^-d$/ ) { 360 | push @out, '-Wno-deprecated'; 361 | } elsif ( $arg =~ m/^-de$/ ) { 362 | push @out, '-Wdeprecated', '-Werror'; 363 | } elsif ( $arg =~ m/^-dw$/ ) { 364 | push @out, '-Wdeprecated'; 365 | } elsif ( $arg =~ m/^-debug(?:=(.*))?$/ ) { 366 | push @out, (defined($1) ? "-fdebug=$1" : '-fdebug'); 367 | } elsif ( $arg =~ m/^-debuglib=(.*)$/ ) { 368 | push @link_out, '-debuglib', $1; 369 | } elsif ( $arg =~ m/^-debug.*$/ ) { 370 | # Passing this to gdc only gives warnings; exit with an error here 371 | errorExit "unrecognized switch '$arg'"; 372 | } elsif ( $arg =~ m/^-defaultlib=(.*)$/ ) { 373 | push @link_out, '-defaultlib', $1; 374 | } elsif ( $arg =~ m/^-deps=(.*)$/ ) { 375 | push @out, (defined($1) ? "-fdeps=$1" : '-fdeps'); 376 | } elsif ( $arg =~ m/^-extern-std=(.*)$/ ) { 377 | push @out, "-fextern-std=$1"; 378 | } elsif ( $arg =~ m/^-g$/ ) { 379 | $debug = 1; 380 | push @out, '-g'; 381 | } elsif ( $arg =~ m/^-gs$/ ) { 382 | push @out, '-fno-omit-frame-pointer'; 383 | } elsif ( $arg =~ m/^-gt$/ ) { 384 | errorExit "use -profile instead of -gt"; 385 | push @out, '-pg'; 386 | } elsif ( $arg =~ m/^-gx$/ ) { 387 | push @out, '-fstack-protector'; 388 | } elsif ( $arg =~ m/^-H$/ ) { 389 | $header = 1; 390 | } elsif ( $arg =~ m/^-Hd(.*)$/ ) { 391 | $header = 1; 392 | $header_directory = $1; 393 | } elsif ( $arg =~ m/^-Hf(.*)$/ ) { 394 | $header = 1; 395 | $header_file = $1; 396 | } elsif ( $arg =~ m/^-HC(?:=(.*))?$/ ) { 397 | if ( ! $1 ) { 398 | print "GDC doesn't support outputing a C++ header to stdout\n"; 399 | print "Please use the -HCf=filename form\n"; 400 | errorExit "plain -HC is unsupported"; 401 | } 402 | if ( $1 =~ m/^\?|h|help$/ ) { 403 | print "Possible value of -HC are silent and verbose\n"; 404 | exit 0; 405 | } 406 | if ( $1 eq 'verbose' ) { 407 | push @out, '-fdump-c++-spec-verbose'; 408 | } elsif ( $1 eq 'silent' ) { 409 | # ignore 410 | } else { 411 | errorExit "Unknown argument passed to -HC '$1'"; 412 | } 413 | } elsif ( $arg =~ m/^-HCf=(.*)$/ ) { 414 | push @out, "-fdump-c++-spec=$1"; 415 | } elsif ( $arg eq '--help' || $arg eq '-h' ) { 416 | printUsage; 417 | exit 0; 418 | } elsif ($arg eq '-framework' ) { 419 | push @link_out, '-framework', $ARGV[$arg_i++]; 420 | } elsif ( $arg eq '-i' ) { 421 | push @out, '-finclude-imports'; 422 | } elsif ( $arg eq '-ignore' ) { 423 | push @out, '-fignore-unknown-pragmas'; 424 | } elsif ( $arg =~ m/^-inline$/ ) { 425 | push @out, '-finline-functions'; 426 | } elsif ( $arg =~ m/^-I(.*)$/ ) { 427 | foreach my $i (split pathSep, $1) { 428 | push @out, '-I', expandHome $i; 429 | } 430 | } elsif ( $arg =~ m/^-J(.*)$/ ) { 431 | foreach my $i (split pathSep, $1) { 432 | push @out, '-J', expandHome $i; 433 | } 434 | } elsif ( $arg =~ m/^-L(.*)$/ ) { 435 | push @link_out, '-Wl,' . $1; 436 | } elsif ( $arg eq '-lib' ) { 437 | $lib = 1; 438 | $link = 0; 439 | $tmpdir = tempdir(CLEANUP => 1); 440 | } elsif ( $arg =~ m/^-O$/ ) { 441 | push @out, '-O2'; 442 | } elsif ( $arg =~ m/^-o-$/ ) { 443 | push @out, '-fsyntax-only'; 444 | $link = 0; 445 | } elsif ( $arg =~ m/^-od(.*)$/ ) { 446 | $output_directory = $1; 447 | } elsif ( $arg =~ m/^-of(.*)$/ ) { 448 | $output_file = $1; 449 | } elsif ( $arg =~ m/^-op$/ ) { 450 | $output_parents = 1; 451 | } elsif ( $arg =~ m/^-pipe$/ ) { 452 | push @out, '-pipe'; 453 | } elsif ( $arg =~ m/^-preview=(.*)$/ ) { 454 | # gdc identifiers are all lowercase, with dmd some have mixedcase 455 | my $gdc_id = lc($1); 456 | push @out, "-fpreview=$gdc_id"; 457 | } elsif ( $arg =~ m/^-profile$/ ) { 458 | # there is more to profiling than this ... -finstrument-functions? 459 | push @out, '-pg'; 460 | } elsif ( $arg =~ m/^-release$/ ) { 461 | push @out, '-frelease'; 462 | } elsif ( $arg =~ m/^-revert=(.*)$/ ) { 463 | # gdc identifiers are all lowercase, with dmd some have mixedcase 464 | my $gdc_id = lc($1); 465 | push @out, "-frevert=$gdc_id"; 466 | } elsif ( $arg eq '-run' ) { 467 | $run = 1; 468 | 469 | $arg = $ARGV[$arg_i++]; 470 | argCheck '-run', $arg; 471 | 472 | addSourceFile $arg; 473 | 474 | push @run_args, @ARGV[$arg_i..$#ARGV]; 475 | last; 476 | } elsif ( $arg =~ m/^-shared$/ ) { 477 | push @out, '-shared'; 478 | } elsif ( $arg =~ m/^-transition=(.*)$/ ) { 479 | # gdc identifiers are all lowercase, with dmd some have mixedcase 480 | my $gdc_id = lc($1); 481 | push @out, "-ftransition=$gdc_id"; 482 | } elsif ( $arg =~ m/^-noboundscheck$/ ) { 483 | push @out, '-fno-bounds-check'; 484 | } elsif ( $arg =~ m/^-unittest$/ ) { 485 | push @out, '-funittest'; 486 | } elsif ( $arg =~ m/^-v$/ ) { 487 | $verbose = 1; 488 | if ($gcc_maj >= 8) { 489 | push @out, '--verbose'; 490 | } else { 491 | push @out, '-fd-verbose'; 492 | } 493 | } elsif ( $arg =~ m/^-vtls$/ ) { 494 | if ($gcc_maj >= 8) { 495 | push @out, '-ftransition=tls'; 496 | } else { 497 | push @out, '-fd-vtls'; 498 | } 499 | } elsif ( $arg =~ m/^-vtemplates(?:=(.*))?$/ ) { 500 | if ( $1 && $1 ne 'list-instances' ) { 501 | errorExit "-vtemplates only supports list-instances, not '$1'" 502 | } 503 | push @out, '-ftransition=templates'; 504 | } elsif ( $arg =~ m/^-v1$/ ) { 505 | push @out, '-fd-version=1'; 506 | } elsif ( $arg =~ m/^-verrors$/ ) { 507 | errorExit "switch -verrors expects a value"; 508 | } elsif ( $arg =~ m/^-verrors=(\d*)$/ ) { 509 | push @out, "-fmax-errors=" . ( $1 || 0 ); 510 | } elsif ( $arg =~ m/^-verrors=(.+)$/ ) { 511 | if ( $1 eq "context" ) { 512 | # ignored 513 | } elsif ( $1 eq "spec" ) { 514 | push @out, '-Wspeculative'; 515 | } else { 516 | errorExit "unrecongnized value passed to -verrors '$1'"; 517 | } 518 | } elsif ( $arg =~ m/^--version$/ ) { 519 | $print_version = 1; 520 | } elsif ( $arg =~ m/^-version=(.*)$/ ) { 521 | push @out, "-fversion=$1"; 522 | } elsif ( $arg =~ m/^-version.*$/ ) { 523 | errorExit "unrecognized switch '$arg'"; 524 | } elsif ( $arg =~ m/^-vdmd$/ ) { 525 | $show_commands = 1; 526 | } elsif ( $arg =~ m/^-w$/ ) { 527 | push @out, "-Werror"; 528 | } elsif ( $arg =~ m/^-wi$/ ) { 529 | push @out, "-Wall"; 530 | } elsif ( $arg =~ m/^-wo$/ ) { 531 | # ignored 532 | } elsif ( $arg =~ m/^-quiet$/ ) { 533 | # ignored 534 | } elsif ( $arg =~ m/^-q,(.*)$/ ) { 535 | push @out, split(qr/,/, $1); 536 | } elsif ( $arg =~ m/^-X$/ ) { 537 | $json = 1; 538 | } elsif ( $arg =~ m/^-Xf(.*)$/ ) { 539 | $json = 1; 540 | $json_file = $1; 541 | } elsif ( $arg eq '-fall-sources' ) { 542 | $seen_all_sources_flag = 1; 543 | } elsif ( $arg =~ m/^-f.+/ ) { 544 | # Pass -fxxx options 545 | push @out, $arg; 546 | } elsif ($arg eq '-main') { 547 | push @out, '-fmain'; 548 | } elsif ($arg eq '-man') { 549 | browse("http://www.gdcproject.org/wiki/UserDocumentation"); 550 | exit 0; 551 | } elsif ( $arg =~ m/^-map$/ ) { 552 | $map = 1; 553 | if ($ARGV[$arg_i] =~ m/.map$/ ) { 554 | $map_file = $ARGV[$arg_i++]; 555 | } 556 | } elsif ( $arg =~ m/^-mixin=(.*)$/ ) { 557 | push @out, "-fsave-mixins=$1"; 558 | } elsif ( $arg =~ m/^-m.+/ ) { 559 | # Pass -mxxx options 560 | push @out, $arg; 561 | } elsif ( $arg =~ m/^-.+$/ ) { 562 | errorExit "unrecognized switch '$arg'"; 563 | } elsif ( $arg =~ m/^\@(.+)$/i ) { 564 | # Append response file to end of ARGV. 565 | open(my $rsp_fh, "<", $1) or die("Can't read response file: $!"); 566 | my $rsp = do { local $/; <$rsp_fh> }; 567 | close($rsp_fh); 568 | 569 | while (length $rsp) { 570 | if ($rsp =~ m/^"(([^\\"]|\\.)*(\\\\)*)"\s*/ ) { 571 | $arg = $1; 572 | $rsp = $'; 573 | $arg =~ s/\\"/"/g; 574 | $arg =~ s/(\\+)\1$/$1/g; 575 | } else { 576 | $rsp =~ m/^(\S*)\s*/; 577 | $arg = $1; 578 | $rsp = $'; 579 | } 580 | push @ARGV, $arg; 581 | } 582 | } elsif ( $arg =~ m/^.+\.d$/i || 583 | $arg =~ m/^.+\.dd$/i || 584 | $arg =~ m/^.+\.di$/i) { 585 | addSourceFile $arg; 586 | } elsif ( $arg =~ m/^.+\.ddoc/i ) { 587 | push @out, "-fdoc-inc=$arg"; 588 | } elsif ( $arg eq '-' ) { 589 | push @out, "-x"; 590 | push @out, 'd'; 591 | push @out, '-'; 592 | $stdin = 1; 593 | } elsif ( $arg !~ m/\./ ) { 594 | addSourceFile $arg . ".d"; 595 | } elsif ( $arg =~ m/^(.+)(\.exe)$/i ) { 596 | $first_input_file = $arg if ! $first_input_file; 597 | $output_file = $1; 598 | if ( targetHasEXE() ) { 599 | $output_file .= $2; 600 | } 601 | } else { 602 | push @objects, $arg 603 | } 604 | } 605 | 606 | if ($link || (! $lib && $output_file && scalar(@sources) > 1)) { 607 | $combine = 1; 608 | } 609 | 610 | if ( $run && ! $link ) { 611 | errorExit "flags conflict with -run"; 612 | } 613 | 614 | if ( $stdin && ! $output_file && $output_directory ) { 615 | $output_file = "$output_directory/a.out" 616 | } 617 | 618 | if ( $output_file ) { 619 | my $dir = dirname( $output_file ); 620 | eval { mkpath( $dir ) }; 621 | if ($@) { 622 | errorExit "could not create $dir: $@"; 623 | } 624 | } 625 | 626 | if ( ($link || $lib) && ! $output_file && $first_input_file ) { 627 | $output_file = fileparse( $first_input_file, qr/\..*$/ ); 628 | if ( $link && targetHasEXE() ) { 629 | $output_file .= '.exe'; 630 | } elsif ( $lib ) { 631 | $output_file .= '.a'; 632 | } 633 | } 634 | 635 | if ( $print_version ) { 636 | my @cmd = ($gdc, '--version', @out); 637 | my $result = system(@cmd); 638 | errorExit if $result & 0xff; # Give up if can't exec or gdc exited with a signal 639 | exit 0; 640 | } elsif (! scalar(@sources) && ! $stdin && ! (($link || $lib) && scalar(@objects))) { 641 | my @cmd = ($gdc, '--version', @out); 642 | my $result = system(@cmd); 643 | errorExit if $result & 0xff; # Give up if can't exec or gdc exited with a signal 644 | printUsage; 645 | exit 1; 646 | } 647 | 648 | my $ok = 1; 649 | 650 | foreach my $srcf_i (@sources) { 651 | # Step 1: Determine the object file path 652 | my $outf; 653 | my $hdrd; 654 | my $docd; 655 | my $srcf = $srcf_i; # To avoid modifying elements of @sources 656 | my @outbits; 657 | my @hdrbits; 658 | my @docbits; 659 | 660 | if ( $lib ) { 661 | # Generate a unique name in the temporary directory. The -op argument 662 | # is ignored in this case and there could very well be duplicate base 663 | # names. 664 | my $base = basename( $srcf, '.d' ); 665 | my $i = 1; 666 | $outf = $base . '.o'; 667 | while ( defined $tmpdir_objs{$outf} ) { 668 | $outf = $base . '-' . $i++ . '.o'; 669 | } 670 | $tmpdir_objs{$outf} = 1; 671 | 672 | $outf = File::Spec->catfile( $tmpdir, $outf ); 673 | } elsif ( ! ($link || $lib) && $output_file ) { 674 | $outf = $output_file; 675 | } else { 676 | if ( $output_directory ) { 677 | push @outbits, $output_directory; 678 | } 679 | if ( $output_parents ) { 680 | push @outbits, dirname( $srcf ); 681 | } 682 | 683 | if ( scalar( @outbits )) { 684 | my $dir = File::Spec->catfile( @outbits ); 685 | eval { mkpath($dir) }; 686 | if ($@) { 687 | errorExit "could not create $dir: $@"; 688 | } 689 | } 690 | 691 | # Note: There is currently no ($combine && $lib) case to check 692 | if ( $combine && $link) { 693 | push @outbits, basename( $output_file, '.exe' ) . '.o'; 694 | } else { 695 | push @outbits, basename( $srcf, '.d' ) . '.o'; 696 | } 697 | $outf = File::Spec->catfile( @outbits ); 698 | if ( $combine && $link && $outf eq $output_file) { 699 | $outf .= '.o'; 700 | } 701 | } 702 | 703 | if ($header) { 704 | if ( $header_directory ) { 705 | push @hdrbits, $header_directory; 706 | } 707 | if ( $output_parents ) { 708 | push @hdrbits, dirname( $srcf ); 709 | } 710 | 711 | if ( scalar( @hdrbits )) { 712 | $hdrd = File::Spec->catfile( @hdrbits ); 713 | eval { mkpath($hdrd) }; 714 | if ($@) { 715 | errorExit "could not create $hdrd: $@"; 716 | } 717 | } 718 | } 719 | 720 | if ($documentation) { 721 | if ( $documentation_directory ) { 722 | push @docbits, $documentation_directory; 723 | } 724 | if ( $output_parents ) { 725 | push @docbits, dirname( $srcf ); 726 | } 727 | 728 | if ( scalar( @docbits )) { 729 | $docd = File::Spec->catfile( @docbits ); 730 | eval { mkpath($docd) }; 731 | if ($@) { 732 | errorExit "could not create $docd: $@"; 733 | } 734 | } 735 | } 736 | 737 | if ($json) { 738 | if (! $json_file) { 739 | $json_file = substr($first_input_file, 0, length($first_input_file)-2) . ".json"; 740 | } 741 | if ($gcc_maj >= 8) { 742 | push @out, '-Xf' . $json_file; 743 | } else { 744 | push @out, '-fXf=' . $json_file; 745 | } 746 | } 747 | 748 | if ($map) { 749 | if (! $map_file) { 750 | $map_file = substr($first_input_file, 0, length($first_input_file)-2) . ".map"; 751 | } 752 | # Check for Mac (Untested) 753 | if ($^O =~ m/darwin/i) { 754 | push @link_out, '-Wl,-map=' . $map_file; 755 | } else { 756 | push @link_out, '-Wl,-Map=' . $map_file; 757 | } 758 | } 759 | 760 | push @dobjects, $outf; 761 | 762 | my @source_args; 763 | if ( $combine ) { 764 | if ($gcc_maj == 4 && $gcc_min <= 5) { 765 | push @source_args, "-combine"; 766 | } 767 | push @source_args, @sources; 768 | } elsif ( $seen_all_sources_flag ) { 769 | @source_args = (@sources, "-fonly=$srcf"); 770 | } else { 771 | @source_args = $srcf; 772 | } 773 | 774 | my @interface; 775 | if ( $header ) { 776 | push @interface, '-fintfc'; 777 | push @interface, "-fintfc-dir=$hdrd" if $hdrd; 778 | push @interface, "-fintfc-file=$header_file" if $header_file; 779 | } 780 | 781 | my @documentation; 782 | if ( $documentation ) { 783 | push @documentation, '-fdoc'; 784 | push @documentation, "-fdoc-dir=$docd" if $docd; 785 | push @documentation, "-fdoc-file=$documentation_file" if $documentation_file; 786 | } 787 | 788 | # Step 2: Run the compiler driver 789 | my @cmd = ($gdc, @out, '-c', @source_args, '-o', $outf, @interface, @documentation); 790 | if ( $show_commands ) { 791 | print join(' ', @cmd), "\n"; 792 | } 793 | my $result = system(@cmd); 794 | errorExit if $result & 0xff; # Give up if can't exec or gdc exited with a signal 795 | $ok = $ok && $result == 0; 796 | 797 | last if $combine; 798 | } 799 | 800 | if ($ok && ($link || $stdin)) { 801 | my @cmd = ($gdc, @out, @dobjects, @objects, @link_out); 802 | if ( $output_file ) { 803 | push @cmd, '-o', $output_file; 804 | } 805 | if ( $show_commands ) { 806 | print join(' ', @cmd), "\n"; 807 | } 808 | $ok = $ok && system(@cmd) == 0; 809 | } elsif ($ok && $lib) { 810 | my @ar_cmd = determineARcommand(); 811 | my $outf = $output_file; 812 | if ( $output_directory ) { 813 | $outf = File::Spec->catfile($output_directory, $output_file); 814 | } 815 | my @cmd = (@ar_cmd, $outf, @dobjects, @objects); 816 | if ( $show_commands ) { 817 | print join(' ', @cmd), "\n"; 818 | } 819 | $ok = $ok && system(@cmd) == 0; 820 | } 821 | 822 | if ($ok && $run) { 823 | my @cmd = (abs_path($output_file), @run_args); 824 | if ($verbose) { 825 | print join(' ', @cmd), "\n"; 826 | } 827 | my $result = system @cmd; 828 | unlink ($output_file, @dobjects); 829 | if ($result == -1) { 830 | print STDERR "$output_file: $!\n"; 831 | exit 127; 832 | } elsif ($result & 127) { 833 | exit 128 + ($result & 127); 834 | } else { 835 | exit $result >> 8; 836 | } 837 | } 838 | 839 | exit ($ok ? 0 : 1); 840 | -------------------------------------------------------------------------------- /dmd-script.1: -------------------------------------------------------------------------------- 1 | .TH gdmd 1 2 | .SH NAME 3 | gdmd - wrapper script for gdc that emulates the dmd command 4 | .SH SYNOPSIS 5 | .B gdmd 6 | files.d 7 | ... 8 | .I { -switch } 9 | .SH OPTIONS 10 | .IP files.d 11 | D source files 12 | .IP files.di 13 | D interface files 14 | .IP files.o 15 | Object files to link in 16 | .IP files.a 17 | Library files to link in 18 | .IP -allinst 19 | generate code for all template instantiations 20 | .IP "-arch ..." 21 | pass -arch option to gdc 22 | .IP -boundscheck=[on|safeonly|off] 23 | bounds checks on, in @safe only, or off 24 | .IP -betterC 25 | omit generating some runtime information and helper functions 26 | .IP -c 27 | compile only, do not link 28 | .IP -cov 29 | do code coverage analysis 30 | .IP -D 31 | generate documentation 32 | .IP -Dddocdir 33 | write documentation file to docdir directory 34 | .IP -Dffilename 35 | write documentation file to filename 36 | .IP -d 37 | allow deprecated features 38 | .IP -debug 39 | compile in debug code 40 | .IP -debug=level 41 | compile in debug code <= level 42 | .IP -debug=ident 43 | compile in debug code identified by ident 44 | .IP -debuglib=lib 45 | debug library to use instead of phobos 46 | .IP -defaultlib=lib 47 | default library to use instead of phobos 48 | .IP -deps=filename 49 | write module dependencies to filename 50 | .IP -extern-std=standard 51 | Sets the C++ name mangling compatiblity. Check -fextern-std in gdc(1) 52 | .IP -f... 53 | pass an -f... option to gdc 54 | .IP -fall-sources 55 | for every source file, semantically process each file preceding i 56 | .IP "-framework ..." 57 | pass a -framework ... option to gdc 58 | .IP -g 59 | add symbolic debug info 60 | .IP -gs 61 | always emit stack frame 62 | .IP -H 63 | generate 'header' file 64 | .IP -Hdhdrdir 65 | write 'header' file to hdrdir directory 66 | .IP -Hffilename 67 | write 'header' file to filename 68 | .IP -HC=value 69 | Control the verbosity of C++ header generation. Possible values are: 70 | .RS 71 | .IP \(bu 2 72 | silent 73 | .IP \(bu 74 | verbose 75 | 76 | Add comments for ignored declarations in the generated C++ header. 77 | .RE 78 | .IP -HCf=filename 79 | write C++ 'header' file to filename 80 | .IP -Ipath 81 | where to look for imports 82 | 83 | .I path 84 | is a ; separated list of paths. Multiple -I's can be used, and the paths are searched in the same order. 85 | 86 | .IP -i 87 | Include imported modules in the compilation, as if they were given on the command line. When this option is enabled, all imported modules are compiled except those that are part of libphobos. 88 | .IP -ignore 89 | ignore unsupported pragmas 90 | .IP -inline 91 | do function inlining 92 | .IP -Jpath 93 | where to look for string imports 94 | .IP -Llinkerflag 95 | pass linkerflag to linker 96 | .IP -lib 97 | generate library 98 | .IP -m... 99 | pass an -m... option to gdc 100 | .IP -man 101 | open web browser on manual page 102 | .IP -map 103 | generate linker .map file 104 | .IP -mixin=filename 105 | expand and save mixins to file specified by filename 106 | .IP -noboundscheck 107 | turns off array bounds checking for all functions 108 | .IP -O 109 | optimize 110 | .IP -o- 111 | do not write object file 112 | .IP -odobjdir 113 | write object files to directory objdir 114 | .IP -offilename 115 | name output file to filename 116 | .IP -op 117 | do not strip paths from source file 118 | .IP -pipe 119 | use pipes instead of intermediate files 120 | .IP -preview=id 121 | enable an upcoming language change identified by id. Check -fpreview in gdc(1) 122 | .IP -profile 123 | profile runtime performance 124 | .IP -revert=id 125 | revert language change identified by id. Check -frevert in gdc(1) 126 | .IP -quiet 127 | suppress unnecessary error messages 128 | .IP -q,[,,,...] 129 | Pass the comma-separated arguments to gdc 130 | .IP -release 131 | compile release version 132 | .IP -run 133 | run resulting program, passing args 134 | .IP -shared 135 | generate shared library (DLL) 136 | .IP -transition=id 137 | report additional information about D language changes identified by id. Check -ftransition in gdc(1) 138 | .IP -unittest 139 | compile in unit tests 140 | .IP -v 141 | verbose 142 | .IP -vdmd 143 | Print commands executed by this wrapper script 144 | .IP --version 145 | print compiler version and exit 146 | .IP -h|--help 147 | Print the usage information and exit 148 | .IP -version=level 149 | compile in version code <= level 150 | .IP -version=ident 151 | compile in version code identified by ident 152 | .IP -vtemplates 153 | list statistics on template instantiations 154 | .IP -vtls 155 | list all variables going into thread local storage 156 | .IP -w 157 | enable warnings 158 | .IP -wi 159 | enable informational warnings 160 | compile in version code identified by ident 161 | .IP -X 162 | generate JSON file 163 | .IP -Xffilename 164 | write JSON to filename 165 | 166 | .SH SEE ALSO 167 | .BR gdc(1) 168 | 169 | .SH AUTHOR 170 | Copyright 171 | 172 | (C) 2007 David Friedman 173 | 174 | Maintained by: 175 | 176 | (C) 2011 Iain Buclaw 177 | --------------------------------------------------------------------------------