PageRenderTime 67ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/d/dmd-script

https://bitbucket.org/tbone/gdc-fixes
Perl | 616 lines | 517 code | 54 blank | 45 comment | 105 complexity | 1a54e8d8efeedf1f4e0a79eb44547eeb MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0
  1. #! /usr/bin/perl -w
  2. # GDC -- D front-end for GCC
  3. # Copyright (C) 2004 David Friedman
  4. #
  5. # Modified by
  6. # Michael Parrott, (C) 2010
  7. # Iain Buclaw, (C) 2010
  8. #
  9. # This program is free software; you can redistribute it and/or modify
  10. # it under the terms of the GNU General Public License as published by
  11. # the Free Software Foundation; either version 2 of the License, or
  12. # (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU General Public License
  20. # along with this program; if not, write to the Free Software
  21. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22. # This is a wrapper script for gdc that emulates the dmd command.
  23. # -f and -m options are passed to gdc. Extra options are:
  24. #
  25. # -vdmd Print commands executed by this wrapper script
  26. # -q,<arg1>[,<arg2>,<arg3>,...] Pass the comma-separated arguments to gdc
  27. use strict;
  28. use Cwd qw(abs_path);
  29. use File::Basename;
  30. use File::Spec;
  31. use File::Path;
  32. use File::Temp qw(tempdir);
  33. my $output_directory;
  34. my $output_parents;
  35. my $output_file;
  36. my $header_directory;
  37. my $header_parents;
  38. my $header_file;
  39. my $documentation_directory;
  40. my $documentation_file;
  41. my $default_lib;
  42. my $debug_lib;
  43. my $debug = 0;
  44. my $link = 1;
  45. my $header = 0;
  46. my $documentation = 0;
  47. my $run = 0;
  48. my $verbose = 0;
  49. my $show_commands = 0;
  50. my $seen_all_sources_flag = 0;
  51. my $first_input_file;
  52. my $combine; # Compile multiple sources into a single object file
  53. my $lib = 0;
  54. my $tmpdir;
  55. my %tmpdir_objs;
  56. my @sources;
  57. my @objects;
  58. my @dobjects;
  59. my @out;
  60. my @link_out;
  61. my @run_args;
  62. # Use the gdc executable in the same directory as this script and account
  63. # for the target prefix.
  64. basename($0) =~ m/^(.*-)?g?dmd(-.*)?$/;
  65. my $target_prefix = $1?$1:"";
  66. my $gdc_dir = abs_path(dirname($0));
  67. my $gdc = File::Spec->catfile( $gdc_dir, $target_prefix . "gdc" . ($2?$2:""));
  68. sub osHasEXE() {
  69. return $^O =~ m/MS(DOS|Win32)|os2/i; # taken from File::Basename
  70. }
  71. sub targetHasEXE() {
  72. my $target = `$gdc -dumpmachine`;
  73. return $target =~ m/mingw/ || $target =~ m/cygwin/;
  74. }
  75. sub pathSep() {
  76. return ";" if $^O =~ m/MS(DOS|Win32)/i;
  77. return "," if $^O =~ m/MacOS/i;
  78. return ":";
  79. }
  80. sub expandHome($) {
  81. my ($path) = (@_);
  82. if ( $^O !~ m/MS(DOS|Win32)|MacOS/i ) {
  83. $path =~ s/^~/$ENV{HOME}/;
  84. }
  85. return $path;
  86. }
  87. sub printUsage() {
  88. print <<EOF
  89. Documentation: http://www.digitalmars.com/d/1.0/index.html
  90. http://dgcc.sourceforge.net/
  91. http://bitbucket.org/goshawk/gdc/wiki/Home
  92. Usage:
  93. gdmd files.d ... { -switch }
  94. files.d D source files
  95. -arch ... pass an -arch ... option to gdc
  96. -c do not link
  97. -cov do code coverage analysis
  98. -D generate documentation
  99. -Dddocdir write documentation file to docdir directory
  100. -Dffilename write documentation file to filename
  101. -d allow deprecated features
  102. -debug compile in debug code
  103. -debug=level compile in debug code <= level
  104. -debug=ident compile in debug code identified by ident
  105. -debuglib=lib debug library to use instead of phobos
  106. -defaultlib=lib default library to use instead of phobos
  107. -deps=filename write module dependencies to filename
  108. -f... pass an -f... option to gdc
  109. -fall-sources for every source file, semantically process each file preceding it
  110. -framework ... pass a -framework ... option to gdc
  111. -g add symbolic debug info
  112. -gc add symbolic debug info, pretend to be C
  113. -H generate 'header' file
  114. -Hdhdrdir write 'header' file to hdrdir directory
  115. -Hffilename write 'header' file to filename
  116. --help print help
  117. -Ipath where to look for imports
  118. -ignore ignore unsupported pragmas
  119. -inline do function inlining
  120. -Jpath where to look for string imports
  121. -Llinkerflag pass linkerflag to link
  122. -lib generate library rather than object files
  123. -m... pass an -m... option to gdc
  124. -man open web browser on manual page
  125. -map generate linker .map file
  126. -noboundscheck turns off array bounds checking for all functions
  127. -nofloat do not emit reference to floating point
  128. -O optimize
  129. -o- do not write object file
  130. -odobjdir write object files to directory objdir
  131. -offilename name output file to filename
  132. -op do not strip paths from source file
  133. -pipe use pipes rather than intermediate files
  134. -profile profile runtime performance of generated code
  135. -quiet suppress unnecessary messages
  136. -q,arg1,... pass arg1, arg2, etc. to to gdc
  137. -release compile release version
  138. -run srcfile args... run resulting program, passing args
  139. -unittest compile in unit tests
  140. -v verbose
  141. -v1 D language version 1
  142. -vdmd print commands run by this script
  143. -version=level compile in version code >= level
  144. -version=ident compile in version code identified by ident
  145. -vtls list all variables going into thread local storage
  146. -w enable warnings
  147. -wi enable informational warnings
  148. -X generate JSON file
  149. -Xffilename write JSON file to filename
  150. EOF
  151. ;
  152. }
  153. sub errorExit(@) {
  154. print STDERR "gdmd: ", @_, "\n" if @_;
  155. exit 1;
  156. }
  157. use subs qw(errorExit);
  158. my $gcc_version = `$gdc -dumpversion`;
  159. chomp $gcc_version;
  160. $gcc_version =~ m/^(\d+)\.(\d+)/;
  161. my ($gcc_maj, $gcc_min) = ( $1, $2 );
  162. my $target_machine = `$gdc -dumpmachine`;
  163. chomp $target_machine;
  164. sub addSourceFile($) {
  165. my ($arg) = @_;
  166. $first_input_file = $arg if ! $first_input_file;
  167. push @sources, $arg;
  168. }
  169. sub argCheck($$) {
  170. my ($name,$arg) = @_;
  171. errorExit "argument expected for switch '$name'" unless defined $arg;
  172. }
  173. sub determineARexe() {
  174. my $name = $target_prefix . 'ar';
  175. $name .= '.exe' if (osHasEXE());
  176. # Prefer the 'ar' in the same directory as gdc even if there is no
  177. # target prefix.
  178. my $path = File::Spec->catfile( $gdc_dir, $name );
  179. return $path if -x $path;
  180. if ( length $target_prefix ) {
  181. foreach my $dir (split pathSep, $ENV{PATH}) {
  182. $path = File::Spec->catfile( $path, $name );
  183. return $name if -x $path; # Could return $path, but this looks better
  184. }
  185. errorExit "Could not find archiver command '$name'.";
  186. } else {
  187. return "ar";
  188. }
  189. }
  190. sub determineARcommand() {
  191. my @exe = determineARexe();
  192. return (@exe, 'cru');
  193. }
  194. sub browse($) {
  195. my ($url) = @_;
  196. my @cmd;
  197. if ($^O =~ m/MSWin32/i) {
  198. @cmd = qw(cmd /c start);
  199. } elsif ($^O =~ m/darwin/i &&
  200. -x '/usr/bin/open') { # MacOS X vs. just Darwin
  201. @cmd = 'open';
  202. } elsif ($ENV{KDE_FULL_SESSION} eq 'true') {
  203. @cmd = qw(kfmclient exec);
  204. } elsif ($ENV{GNOME_DESKTOP_SESSION_ID} ne '') {
  205. @cmd = 'gnome-open';
  206. } else {
  207. errorExit "Sorry, I do not know how to start your browser.\nManual URL: $url"
  208. }
  209. push @cmd, $url;
  210. system @cmd;
  211. print "Opening documentation page.";
  212. exit 0;
  213. }
  214. my $arg_i = 0;
  215. while ( $arg_i < scalar(@ARGV) ) {
  216. my $arg = $ARGV[$arg_i++];
  217. if ($arg eq '-arch' ) {
  218. push @out, '-arch', $ARGV[$arg_i++];
  219. } elsif ($arg =~ m/^-c$/ ) {
  220. $link = 0;
  221. } elsif ( $arg eq '-cov' ) {
  222. push @out, '-fprofile-arcs', '-ftest-coverage';
  223. } elsif ( $arg =~ m/^-D$/ ) {
  224. $documentation = 1;
  225. } elsif ( $arg =~ m/^-Dd(.*)$/ ) {
  226. $documentation = 1;
  227. $documentation_directory = $1;
  228. } elsif ( $arg =~ m/^-Df(.*)$/ ) {
  229. $documentation = 1;
  230. $documentation_file = $1;
  231. } elsif ( $arg =~ m/^-d$/ ) {
  232. push @out, '-fdeprecated';
  233. } elsif ( $arg =~ m/^-debug(?:=(.*))?$/ ) {
  234. push @out, (defined($1) ? "-fdebug=$1" : '-fdebug');
  235. } elsif ( $arg =~ m/^-debuglib=(.*)$/ ) {
  236. push @link_out, '-debuglib', $1;
  237. } elsif ( $arg =~ m/^-debug.*$/ ) {
  238. # Passing this to gdc only gives warnings; exit with an error here
  239. errorExit "unrecognized switch '$arg'";
  240. } elsif ( $arg =~ m/^-defaultlib=(.*)$/ ) {
  241. push @link_out, '-defaultlib', $1;
  242. } elsif ( $arg =~ m/^-deps=(.*)$/ ) {
  243. push @out, (defined($1) ? "-fdeps=$1" : '-fdeps');
  244. } elsif ( $arg =~ m/^-g$/ ) {
  245. $debug = 1;
  246. push @out, '-g';
  247. } elsif ( $arg =~ m/^-gc$/ ) {
  248. $debug = 1;
  249. push @out, '-fdebug-c';
  250. } elsif ( $arg =~ m/^-gt$/ ) {
  251. errorExit "use -profile instead of -gt";
  252. push @out, '-pg';
  253. } elsif ( $arg =~ m/^-H$/ ) {
  254. $header = 1;
  255. } elsif ( $arg =~ m/^-Hd(.*)$/ ) {
  256. $header = 1;
  257. $header_directory = $1;
  258. } elsif ( $arg =~ m/^-Hf(.*)$/ ) {
  259. $header = 1;
  260. $header_file = $1;
  261. } elsif ( $arg eq '--help' ) {
  262. printUsage;
  263. exit 0;
  264. } elsif ($arg eq '-framework' ) {
  265. push @link_out, '-framework', $ARGV[$arg_i++];
  266. } elsif ( $arg eq '-ignore' ) {
  267. push @out, '-fignore-unknown-pragmas';
  268. } elsif ( $arg =~ m/^-inline$/ ) {
  269. push @out, '-finline-functions';
  270. } elsif ( $arg =~ m/^-I(.*)$/ ) {
  271. foreach my $i (split pathSep, $1) {
  272. push @out, '-I', expandHome $i;
  273. }
  274. } elsif ( $arg =~ m/^-J(.*)$/ ) {
  275. foreach my $i (split pathSep, $1) {
  276. push @out, '-J', expandHome $i;
  277. }
  278. } elsif ( $arg =~ m/^-L(.*)$/ ) {
  279. push @link_out, '-Wl,' . $1;
  280. } elsif ( $arg eq '-lib' ) {
  281. $lib = 1;
  282. $link = 0;
  283. $tmpdir = tempdir(CLEANUP => 1);
  284. } elsif ( $arg =~ m/^-O$/ ) {
  285. push @out, '-O3', '-fomit-frame-pointer';
  286. if( ! grep(/^-inline$/,@ARGV) ) {
  287. push @out, '-fno-inline-functions';
  288. }
  289. if ( $gcc_maj < 4) {
  290. push @out, '-frename-registers';
  291. }
  292. if ( $gcc_maj > 3 || ( $gcc_maj == 3 && $gcc_min >= 4 ) ) {
  293. push @out, '-fweb';
  294. }
  295. } elsif ( $arg =~ m/^-o-$/ ) {
  296. push @out, '-fsyntax-only';
  297. $link = 0;
  298. } elsif ( $arg =~ m/^-od(.*)$/ ) {
  299. $output_directory = $1;
  300. } elsif ( $arg =~ m/^-of(.*)$/ ) {
  301. $output_file = $1;
  302. } elsif ( $arg =~ m/^-op$/ ) {
  303. $output_parents = 1;
  304. } elsif ( $arg =~ m/^-nofloat$/ ) {
  305. # do nothing
  306. } elsif ( $arg =~ m/^-pipe$/ ) {
  307. push @out, '-pipe';
  308. } elsif ( $arg =~ m/^-profile$/ ) {
  309. # there is more to profiling than this ... -finstrument-functions?
  310. push @out, '-pg';
  311. } elsif ( $arg =~ m/^-release$/ ) {
  312. push @out, '-frelease';
  313. } elsif ( $arg eq '-run' ) {
  314. $run = 1;
  315. $arg = $ARGV[$arg_i++];
  316. argCheck '-run', $arg;
  317. addSourceFile $arg;
  318. push @run_args, @ARGV[$arg_i..$#ARGV];
  319. last;
  320. } elsif ( $arg =~ m/^-noboundscheck$/ ) {
  321. push @out, '-fnobounds-check';
  322. } elsif ( $arg =~ m/^-unittest$/ ) {
  323. push @out, '-funittest';
  324. } elsif ( $arg =~ m/^-v$/ ) {
  325. $verbose = 1;
  326. push @out, '-fd-verbose';
  327. } elsif ( $arg =~ m/^-vtls$/ ) {
  328. push @out, '-fd-vtls';
  329. } elsif ( $arg =~ m/^-v1$/ ) {
  330. push @out, '-fd-version=1';
  331. } elsif ( $arg =~ m/^-version=(.*)$/ ) {
  332. push @out, "-fversion=$1";
  333. } elsif ( $arg =~ m/^-version.*$/ ) {
  334. errorExit "unrecognized switch '$arg'";
  335. } elsif ( $arg =~ m/^-vdmd$/ ) {
  336. $show_commands = 1;
  337. } elsif ( $arg =~ m/^-w$/ ) {
  338. push @out, "-Werror";
  339. } elsif ( $arg =~ m/^-wi$/ ) {
  340. push @out, "-Wall";
  341. } elsif ( $arg =~ m/^-quiet$/ ) {
  342. # ignored
  343. } elsif ( $arg =~ m/^-q,(.*)$/ ) {
  344. push @out, split(qr/,/, $1);
  345. } elsif ( $arg =~ m/^-X$/ ) {
  346. push @out, '-fXf=' . substr($first_input_file, 0, length($first_input_file)-2) . ".json";;
  347. } elsif ( $arg =~ m/^-Xf(.*)$/ ) {
  348. push @out, '-fXf=' . $1
  349. } elsif ( $arg eq '-fall-sources' ) {
  350. $seen_all_sources_flag = 1;
  351. } elsif ( $arg =~ m/^-f.+/ ) {
  352. # Pass -fxxx options
  353. push @out, $arg;
  354. } elsif ($arg eq '-man') {
  355. browse("http://bitbucket.org/goshawk/gdc/wiki/UserDocumentation");
  356. exit 0;
  357. } elsif ( $arg =~ m/^-map$/ ) {
  358. # Check for Mac (Untested)
  359. if ($^O =~ m/darwin/i)
  360. {
  361. push @link_out, '-Wl,-map=' . substr($first_input_file, 0, length($first_input_file)-2) . ".map";
  362. }
  363. else
  364. {
  365. push @link_out, '-Wl,-Map=' . substr($first_input_file, 0, length($first_input_file)-2) . ".map";
  366. }
  367. } elsif ( $arg =~ m/^-m.+/ ) {
  368. # Pass -mxxx options
  369. push @out, $arg;
  370. } elsif ( $arg =~ m/^-.+$/ ) {
  371. errorExit "unrecognized switch '$arg'";
  372. } elsif ( $arg =~ m/^.+\.d$/i ||
  373. $arg =~ m/^.+\.dd$/i ||
  374. $arg =~ m/^.+\.di$/i ||
  375. $arg =~ m/^.+\.htm$/i ||
  376. $arg =~ m/^.+\.html$/i ||
  377. $arg =~ m/^.+\.xhtml$/i) {
  378. addSourceFile $arg;
  379. } elsif ( $arg =~ m/^.+\.ddoc/i ) {
  380. push @out, "-fdoc-inc=$arg";
  381. } elsif ( $arg !~ m/\./ ) {
  382. addSourceFile $arg . ".d";
  383. } elsif ( $arg =~ m/^(.+)(\.exe)$/i ) {
  384. $first_input_file = $arg if ! $first_input_file;
  385. $output_file = $1;
  386. if ( targetHasEXE() ) {
  387. $output_file .= $2;
  388. }
  389. } else {
  390. push @objects, $arg
  391. }
  392. }
  393. # Slightly different from dmd... allows -of to specify
  394. # the name of the executable.
  395. $combine =
  396. (! $link && ! $lib && scalar(@sources) > 1 && $output_file ) ||
  397. ($link && scalar(@sources) > 1); # > 0 ? does DMD now do the same for 1 vs many sources?
  398. if ( $run && ! $link ) {
  399. errorExit "flags conflict with -run";
  400. }
  401. if ( ($link || $lib) && ! $output_file && $first_input_file ) {
  402. $output_file = fileparse( $first_input_file, qr/\..*$/ );
  403. if ( $link && targetHasEXE() ) {
  404. $output_file .= '.exe';
  405. } elsif ( $lib ) {
  406. $output_file .= '.a';
  407. }
  408. }
  409. if (! scalar(@sources) && ! ($link && scalar(@objects))) {
  410. my @cmd = ($gdc, '--version', @out);
  411. my $result = system(@cmd);
  412. errorExit if $result & 0xff; # Give up if can't exec or gdc exited with a signal
  413. printUsage;
  414. exit 1;
  415. }
  416. my $ok = 1;
  417. foreach my $srcf_i (@sources) {
  418. # Step 1: Determine the object file path
  419. my $outf;
  420. my $hdrd;
  421. my $docd;
  422. my $srcf = $srcf_i; # To avoid modifying elements of @sources
  423. my @outbits;
  424. my @hdrbits;
  425. my @docbits;
  426. if ( $lib ) {
  427. # Generate a unique name in the temporary directory. The -op argument
  428. # is ignored in this case and there could very well be duplicate base
  429. # names.
  430. my $base = basename( $srcf, '.d' );
  431. my $i = 1;
  432. $outf = $base . '.o';
  433. while ( defined $tmpdir_objs{$outf} ) {
  434. $outf = $base . '-' . $i++ . '.o';
  435. }
  436. $tmpdir_objs{$outf} = 1;
  437. $outf = File::Spec->catfile( $tmpdir, $outf );
  438. } elsif ( ! ($link || $lib) && $output_file ) {
  439. $outf = $output_file;
  440. } else {
  441. if ( $output_directory ) {
  442. push @outbits, $output_directory;
  443. }
  444. if ( $output_parents ) {
  445. push @outbits, dirname( $srcf );
  446. }
  447. if ( scalar( @outbits )) {
  448. my $dir = File::Spec->catfile( @outbits );
  449. eval { mkpath($dir) };
  450. if ($@) {
  451. errorExit "could not create $dir: $@";
  452. }
  453. }
  454. # Note: There is currently no ($combine && $lib) case to check
  455. if ( $combine && $link) {
  456. push @outbits, basename( $output_file, '.exe' ) . '.o';
  457. } else {
  458. push @outbits, basename( $srcf, '.d' ) . '.o';
  459. }
  460. $outf = File::Spec->catfile( @outbits );
  461. if ( $combine && $link && $outf eq $output_file) {
  462. $outf .= '.o';
  463. }
  464. }
  465. if ($header) {
  466. if ( $header_directory ) {
  467. push @hdrbits, $header_directory;
  468. }
  469. if ( $output_parents ) {
  470. push @hdrbits, dirname( $srcf );
  471. }
  472. if ( scalar( @hdrbits )) {
  473. $hdrd = File::Spec->catfile( @hdrbits );
  474. eval { mkpath($hdrd) };
  475. if ($@) {
  476. errorExit "could not create $hdrd: $@";
  477. }
  478. }
  479. }
  480. if ($documentation) {
  481. if ( $documentation_directory ) {
  482. push @docbits, $documentation_directory;
  483. }
  484. if ( $output_parents ) {
  485. push @docbits, dirname( $srcf );
  486. }
  487. if ( scalar( @docbits )) {
  488. $docd = File::Spec->catfile( @docbits );
  489. eval { mkpath($docd) };
  490. if ($@) {
  491. errorExit "could not create $docd: $@";
  492. }
  493. }
  494. }
  495. push @dobjects, $outf;
  496. my @source_args;
  497. if ( $combine ) {
  498. if ($gcc_maj >= 4) {
  499. push @source_args, "-combine";
  500. }
  501. push @source_args, @sources;
  502. } elsif ( $seen_all_sources_flag ) {
  503. @source_args = (@sources, "-fonly=$srcf");
  504. } else {
  505. @source_args = $srcf;
  506. }
  507. my @interface;
  508. if ( $header ) {
  509. push @interface, '-fintfc';
  510. push @interface, "-fintfc-dir=$hdrd" if $hdrd;
  511. push @interface, "-fintfc-file=$header_file" if $header_file;
  512. }
  513. my @documentation;
  514. if ( $documentation ) {
  515. push @documentation, '-fdoc';
  516. push @documentation, "-fdoc-dir=$docd" if $docd;
  517. push @documentation, "-fdoc-file=$documentation_file" if $documentation_file;
  518. }
  519. # Step 2: Run the compiler driver
  520. my @cmd = ($gdc, @out, '-c', @source_args, '-o', $outf, @interface, @documentation);
  521. if ( $show_commands ) {
  522. print join(' ', @cmd), "\n";
  523. }
  524. my $result = system(@cmd);
  525. errorExit if $result & 0xff; # Give up if can't exec or gdc exited with a signal
  526. $ok = $ok && $result == 0;
  527. last if $combine;
  528. }
  529. if ($ok && $link) {
  530. my @cmd = ($gdc, @out, @dobjects, @objects, @link_out);
  531. if ( $output_file ) {
  532. push @cmd, '-o', $output_file;
  533. }
  534. if ( $show_commands ) {
  535. print join(' ', @cmd), "\n";
  536. }
  537. $ok = $ok && system(@cmd) == 0;
  538. } elsif ($ok && $lib) {
  539. my @ar_cmd = determineARcommand();
  540. my @cmd = (@ar_cmd, $output_file, @dobjects, @objects);
  541. if ( $show_commands ) {
  542. print join(' ', @cmd), "\n";
  543. }
  544. $ok = $ok && system(@cmd) == 0;
  545. }
  546. if ($ok && $run) {
  547. my @cmd = (abs_path($output_file), @run_args);
  548. if ($verbose) {
  549. print join(' ', @cmd), "\n";
  550. }
  551. my $result = system @cmd;
  552. unlink ($output_file, @dobjects);
  553. if ($result == -1) {
  554. print STDERR "$output_file: $!\n";
  555. exit 127;
  556. } elsif ($result & 127) {
  557. exit 128 + ($result & 127);
  558. } else {
  559. exit $result >> 8;
  560. }
  561. }
  562. exit ($ok ? 0 : 1);