This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Upgrade to ExtUtils::CBuilder 0.24
[perl5.git] / lib / ExtUtils / CBuilder / Platform / Windows.pm
... / ...
CommitLineData
1package ExtUtils::CBuilder::Platform::Windows;
2
3use strict;
4use warnings;
5
6use File::Basename;
7use File::Spec;
8
9use ExtUtils::CBuilder::Base;
10use IO::File;
11
12use vars qw($VERSION @ISA);
13$VERSION = '0.24';
14@ISA = qw(ExtUtils::CBuilder::Base);
15
16sub new {
17 my $class = shift;
18 my $self = $class->SUPER::new(@_);
19 my $cf = $self->{config};
20
21 # Inherit from an appropriate compiler driver class
22 unshift @ISA, "ExtUtils::CBuilder::Platform::Windows::" . $self->_compiler_type;
23
24 return $self;
25}
26
27sub _compiler_type {
28 my $self = shift;
29 my $cc = $self->{config}{cc};
30
31 return ( $cc =~ /cl(\.exe)?$/ ? 'MSVC'
32 : $cc =~ /bcc32(\.exe)?$/ ? 'BCC'
33 : 'GCC');
34}
35
36sub split_like_shell {
37 # Since Windows will pass the whole command string (not an argument
38 # array) to the target program and make the program parse it itself,
39 # we don't actually need to do any processing here.
40 (my $self, local $_) = @_;
41
42 return @$_ if defined() && UNIVERSAL::isa($_, 'ARRAY');
43 return unless defined() && length();
44 return ($_);
45}
46
47sub do_system {
48 # See above
49 my $self = shift;
50 my $cmd = join(" ",
51 grep length,
52 map {$a=$_;$a=~s/\t/ /g;$a=~s/^\s+|\s+$//;$a}
53 grep defined, @_);
54 return $self->SUPER::do_system($cmd);
55}
56
57sub arg_defines {
58 my ($self, %args) = @_;
59 s/"/\\"/g foreach values %args;
60 return map qq{"-D$_=$args{$_}"}, keys %args;
61}
62
63sub compile {
64 my ($self, %args) = @_;
65 my $cf = $self->{config};
66
67 die "Missing 'source' argument to compile()" unless defined $args{source};
68
69 my ($basename, $srcdir) =
70 ( File::Basename::fileparse($args{source}, '\.[^.]+$') )[0,1];
71
72 $srcdir ||= File::Spec->curdir();
73
74 my @defines = $self->arg_defines( %{ $args{defines} || {} } );
75
76 my %spec = (
77 srcdir => $srcdir,
78 builddir => $srcdir,
79 basename => $basename,
80 source => $args{source},
81 output => File::Spec->catfile($srcdir, $basename) . $cf->{obj_ext},
82 cc => $cf->{cc},
83 cflags => [
84 $self->split_like_shell($cf->{ccflags}),
85 $self->split_like_shell($cf->{cccdlflags}),
86 $self->split_like_shell($args{extra_compiler_flags}),
87 ],
88 optimize => [ $self->split_like_shell($cf->{optimize}) ],
89 defines => \@defines,
90 includes => [ @{$args{include_dirs} || []} ],
91 perlinc => [
92 $self->perl_inc(),
93 $self->split_like_shell($cf->{incpath}),
94 ],
95 use_scripts => 1, # XXX provide user option to change this???
96 );
97
98 $self->normalize_filespecs(
99 \$spec{source},
100 \$spec{output},
101 $spec{includes},
102 $spec{perlinc},
103 );
104
105 my @cmds = $self->format_compiler_cmd(%spec);
106 while ( my $cmd = shift @cmds ) {
107 $self->do_system( @$cmd )
108 or die "error building $cf->{dlext} file from '$args{source}'";
109 }
110
111 (my $out = $spec{output}) =~ tr/'"//d;
112 return $out;
113}
114
115sub need_prelink { 1 }
116
117sub link {
118 my ($self, %args) = @_;
119 my $cf = $self->{config};
120
121 my @objects = ( ref $args{objects} eq 'ARRAY' ? @{$args{objects}} : $args{objects} );
122 my $to = join '', (File::Spec->splitpath($objects[0]))[0,1];
123 $to ||= File::Spec->curdir();
124
125 (my $file_base = $args{module_name}) =~ s/.*:://;
126 my $output = $args{lib_file} ||
127 File::Spec->catfile($to, "$file_base.$cf->{dlext}");
128
129 # if running in perl source tree, look for libs there, not installed
130 my $lddlflags = $cf->{lddlflags};
131 my $perl_src = $self->perl_src();
132 $lddlflags =~ s/\Q$cf->{archlibexp}\E[\\\/]CORE/$perl_src/ if $perl_src;
133
134 my %spec = (
135 srcdir => $to,
136 builddir => $to,
137 startup => [ ],
138 objects => \@objects,
139 libs => [ ],
140 output => $output,
141 ld => $cf->{ld},
142 libperl => $cf->{libperl},
143 perllibs => [ $self->split_like_shell($cf->{perllibs}) ],
144 libpath => [ $self->split_like_shell($cf->{libpth}) ],
145 lddlflags => [ $self->split_like_shell($lddlflags) ],
146 other_ldflags => [ $self->split_like_shell($args{extra_linker_flags} || '') ],
147 use_scripts => 1, # XXX provide user option to change this???
148 );
149
150 unless ( $spec{basename} ) {
151 ($spec{basename} = $args{module_name}) =~ s/.*:://;
152 }
153
154 $spec{srcdir} = File::Spec->canonpath( $spec{srcdir} );
155 $spec{builddir} = File::Spec->canonpath( $spec{builddir} );
156
157 $spec{output} ||= File::Spec->catfile( $spec{builddir},
158 $spec{basename} . '.'.$cf->{dlext} );
159 $spec{manifest} ||= File::Spec->catfile( $spec{builddir},
160 $spec{basename} . '.'.$cf->{dlext}.'.manifest');
161 $spec{implib} ||= File::Spec->catfile( $spec{builddir},
162 $spec{basename} . $cf->{lib_ext} );
163 $spec{explib} ||= File::Spec->catfile( $spec{builddir},
164 $spec{basename} . '.exp' );
165 if ($cf->{cc} eq 'cl') {
166 $spec{dbg_file} ||= File::Spec->catfile( $spec{builddir},
167 $spec{basename} . '.pdb' );
168 }
169 elsif ($cf->{cc} eq 'bcc32') {
170 $spec{dbg_file} ||= File::Spec->catfile( $spec{builddir},
171 $spec{basename} . '.tds' );
172 }
173 $spec{def_file} ||= File::Spec->catfile( $spec{srcdir} ,
174 $spec{basename} . '.def' );
175 $spec{base_file} ||= File::Spec->catfile( $spec{srcdir} ,
176 $spec{basename} . '.base' );
177
178 $self->add_to_cleanup(
179 grep defined,
180 @{[ @spec{qw(manifest implib explib dbg_file def_file base_file map_file)} ]}
181 );
182
183 foreach my $opt ( qw(output manifest implib explib dbg_file def_file map_file base_file) ) {
184 $self->normalize_filespecs( \$spec{$opt} );
185 }
186
187 foreach my $opt ( qw(libpath startup objects) ) {
188 $self->normalize_filespecs( $spec{$opt} );
189 }
190
191 (my $def_base = $spec{def_file}) =~ tr/'"//d;
192 $def_base =~ s/\.def$//;
193 $self->prelink( dl_name => $args{module_name},
194 dl_file => $def_base,
195 dl_base => $spec{basename} );
196
197 my @cmds = $self->format_linker_cmd(%spec);
198 while ( my $cmd = shift @cmds ) {
199 $self->do_system( @$cmd );
200 }
201
202 $spec{output} =~ tr/'"//d;
203 return wantarray
204 ? grep defined, @spec{qw[output manifest implib explib dbg_file def_file map_file base_file]}
205 : $spec{output};
206}
207
208# canonize & quote paths
209sub normalize_filespecs {
210 my ($self, @specs) = @_;
211 foreach my $spec ( grep defined, @specs ) {
212 if ( ref $spec eq 'ARRAY') {
213 $self->normalize_filespecs( map {\$_} grep defined, @$spec )
214 } elsif ( ref $spec eq 'SCALAR' ) {
215 $$spec =~ tr/"//d if $$spec;
216 next unless $$spec;
217 $$spec = '"' . File::Spec->canonpath($$spec) . '"';
218 } elsif ( ref $spec eq '' ) {
219 $spec = '"' . File::Spec->canonpath($spec) . '"';
220 } else {
221 die "Don't know how to normalize " . (ref $spec || $spec) . "\n";
222 }
223 }
224}
225
226# directory of perl's include files
227sub perl_inc {
228 my $self = shift;
229
230 my $perl_src = $self->perl_src();
231
232 if ($perl_src) {
233 File::Spec->catdir($perl_src, "lib", "CORE");
234 } else {
235 File::Spec->catdir($self->{config}{archlibexp},"CORE");
236 }
237}
238
2391;
240
241########################################################################
242
243=begin comment
244
245The packages below implement functions for generating properly
246formatted commandlines for the compiler being used. Each package
247defines two primary functions 'format_linker_cmd()' &
248'format_compiler_cmd()' that accepts a list of named arguments (a
249hash) and returns a list of formatted options suitable for invoking the
250compiler. By default, if the compiler supports scripting of its
251operation then a script file is built containing the options while
252those options are removed from the commandline, and a reference to the
253script is pushed onto the commandline in their place. Scripting the
254compiler in this way helps to avoid the problems associated with long
255commandlines under some shells.
256
257=end comment
258
259=cut
260
261########################################################################
262package ExtUtils::CBuilder::Platform::Windows::MSVC;
263
264sub format_compiler_cmd {
265 my ($self, %spec) = @_;
266
267 foreach my $path ( @{ $spec{includes} || [] },
268 @{ $spec{perlinc} || [] } ) {
269 $path = '-I' . $path;
270 }
271
272 %spec = $self->write_compiler_script(%spec)
273 if $spec{use_scripts};
274
275 return [ grep {defined && length} (
276 $spec{cc},'-nologo','-c',
277 @{$spec{includes}} ,
278 @{$spec{cflags}} ,
279 @{$spec{optimize}} ,
280 @{$spec{defines}} ,
281 @{$spec{perlinc}} ,
282 "-Fo$spec{output}" ,
283 $spec{source} ,
284 ) ];
285}
286
287sub write_compiler_script {
288 my ($self, %spec) = @_;
289
290 my $script = File::Spec->catfile( $spec{srcdir},
291 $spec{basename} . '.ccs' );
292
293 $self->add_to_cleanup($script);
294 print "Generating script '$script'\n" if !$self->{quiet};
295
296 my $SCRIPT = IO::File->new( ">$script" )
297 or die( "Could not create script '$script': $!" );
298
299 print $SCRIPT join( "\n",
300 map { ref $_ ? @{$_} : $_ }
301 grep defined,
302 delete(
303 @spec{ qw(includes cflags optimize defines perlinc) } )
304 );
305
306 push @{$spec{includes}}, '@"' . $script . '"';
307
308 return %spec;
309}
310
311sub format_linker_cmd {
312 my ($self, %spec) = @_;
313 my $cf = $self->{config};
314
315 foreach my $path ( @{$spec{libpath}} ) {
316 $path = "-libpath:$path";
317 }
318
319 my $output = $spec{output};
320
321 $spec{def_file} &&= '-def:' . $spec{def_file};
322 $spec{output} &&= '-out:' . $spec{output};
323 $spec{manifest} &&= '-manifest ' . $spec{manifest};
324 $spec{implib} &&= '-implib:' . $spec{implib};
325 $spec{map_file} &&= '-map:' . $spec{map_file};
326
327 %spec = $self->write_linker_script(%spec)
328 if $spec{use_scripts};
329
330 my @cmds; # Stores the series of commands needed to build the module.
331
332 push @cmds, [ grep {defined && length} (
333 $spec{ld} ,
334 @{$spec{lddlflags}} ,
335 @{$spec{libpath}} ,
336 @{$spec{other_ldflags}} ,
337 @{$spec{startup}} ,
338 @{$spec{objects}} ,
339 $spec{map_file} ,
340 $spec{libperl} ,
341 @{$spec{perllibs}} ,
342 $spec{def_file} ,
343 $spec{implib} ,
344 $spec{output} ,
345 ) ];
346
347 # Embed the manifest file for VC 2005 (aka VC 8) or higher, but not for the 64-bit Platform SDK compiler
348 if ($cf->{ivsize} == 4 && $cf->{cc} eq 'cl' and $cf->{ccversion} =~ /^(\d+)/ and $1 >= 14) {
349 push @cmds, [
350 'mt', '-nologo', $spec{manifest}, '-outputresource:' . "$output;2"
351 ];
352 }
353
354 return @cmds;
355}
356
357sub write_linker_script {
358 my ($self, %spec) = @_;
359
360 my $script = File::Spec->catfile( $spec{srcdir},
361 $spec{basename} . '.lds' );
362
363 $self->add_to_cleanup($script);
364
365 print "Generating script '$script'\n" if !$self->{quiet};
366
367 my $SCRIPT = IO::File->new( ">$script" )
368 or die( "Could not create script '$script': $!" );
369
370 print $SCRIPT join( "\n",
371 map { ref $_ ? @{$_} : $_ }
372 grep defined,
373 delete(
374 @spec{ qw(lddlflags libpath other_ldflags
375 startup objects libperl perllibs
376 def_file implib map_file) } )
377 );
378
379 push @{$spec{lddlflags}}, '@"' . $script . '"';
380
381 return %spec;
382}
383
3841;
385
386########################################################################
387package ExtUtils::CBuilder::Platform::Windows::BCC;
388
389sub format_compiler_cmd {
390 my ($self, %spec) = @_;
391
392 foreach my $path ( @{ $spec{includes} || [] },
393 @{ $spec{perlinc} || [] } ) {
394 $path = '-I' . $path;
395 }
396
397 %spec = $self->write_compiler_script(%spec)
398 if $spec{use_scripts};
399
400 return [ grep {defined && length} (
401 $spec{cc}, '-c' ,
402 @{$spec{includes}} ,
403 @{$spec{cflags}} ,
404 @{$spec{optimize}} ,
405 @{$spec{defines}} ,
406 @{$spec{perlinc}} ,
407 "-o$spec{output}" ,
408 $spec{source} ,
409 ) ];
410}
411
412sub write_compiler_script {
413 my ($self, %spec) = @_;
414
415 my $script = File::Spec->catfile( $spec{srcdir},
416 $spec{basename} . '.ccs' );
417
418 $self->add_to_cleanup($script);
419
420 print "Generating script '$script'\n" if !$self->{quiet};
421
422 my $SCRIPT = IO::File->new( ">$script" )
423 or die( "Could not create script '$script': $!" );
424
425 # XXX Borland "response files" seem to be unable to accept macro
426 # definitions containing quoted strings. Escaping strings with
427 # backslash doesn't work, and any level of quotes are stripped. The
428 # result is is a floating point number in the source file where a
429 # string is expected. So we leave the macros on the command line.
430 print $SCRIPT join( "\n",
431 map { ref $_ ? @{$_} : $_ }
432 grep defined,
433 delete(
434 @spec{ qw(includes cflags optimize perlinc) } )
435 );
436
437 push @{$spec{includes}}, '@"' . $script . '"';
438
439 return %spec;
440}
441
442sub format_linker_cmd {
443 my ($self, %spec) = @_;
444
445 foreach my $path ( @{$spec{libpath}} ) {
446 $path = "-L$path";
447 }
448
449 push( @{$spec{startup}}, 'c0d32.obj' )
450 unless ( $spec{starup} && @{$spec{startup}} );
451
452 %spec = $self->write_linker_script(%spec)
453 if $spec{use_scripts};
454
455 return [ grep {defined && length} (
456 $spec{ld} ,
457 @{$spec{lddlflags}} ,
458 @{$spec{libpath}} ,
459 @{$spec{other_ldflags}} ,
460 @{$spec{startup}} ,
461 @{$spec{objects}} , ',',
462 $spec{output} , ',',
463 $spec{map_file} , ',',
464 $spec{libperl} ,
465 @{$spec{perllibs}} , ',',
466 $spec{def_file}
467 ) ];
468}
469
470sub write_linker_script {
471 my ($self, %spec) = @_;
472
473 # To work around Borlands "unique" commandline syntax,
474 # two scripts are used:
475
476 my $ld_script = File::Spec->catfile( $spec{srcdir},
477 $spec{basename} . '.lds' );
478 my $ld_libs = File::Spec->catfile( $spec{srcdir},
479 $spec{basename} . '.lbs' );
480
481 $self->add_to_cleanup($ld_script, $ld_libs);
482
483 print "Generating scripts '$ld_script' and '$ld_libs'.\n" if !$self->{quiet};
484
485 # Script 1: contains options & names of object files.
486 my $LD_SCRIPT = IO::File->new( ">$ld_script" )
487 or die( "Could not create linker script '$ld_script': $!" );
488
489 print $LD_SCRIPT join( " +\n",
490 map { @{$_} }
491 grep defined,
492 delete(
493 @spec{ qw(lddlflags libpath other_ldflags startup objects) } )
494 );
495
496 # Script 2: contains name of libs to link against.
497 my $LD_LIBS = IO::File->new( ">$ld_libs" )
498 or die( "Could not create linker script '$ld_libs': $!" );
499
500 print $LD_LIBS join( " +\n",
501 (delete $spec{libperl} || ''),
502 @{delete $spec{perllibs} || []},
503 );
504
505 push @{$spec{lddlflags}}, '@"' . $ld_script . '"';
506 push @{$spec{perllibs}}, '@"' . $ld_libs . '"';
507
508 return %spec;
509}
510
5111;
512
513########################################################################
514package ExtUtils::CBuilder::Platform::Windows::GCC;
515
516sub format_compiler_cmd {
517 my ($self, %spec) = @_;
518
519 foreach my $path ( @{ $spec{includes} || [] },
520 @{ $spec{perlinc} || [] } ) {
521 $path = '-I' . $path;
522 }
523
524 # split off any -arguments included in cc
525 my @cc = split / (?=-)/, $spec{cc};
526
527 return [ grep {defined && length} (
528 @cc, '-c' ,
529 @{$spec{includes}} ,
530 @{$spec{cflags}} ,
531 @{$spec{optimize}} ,
532 @{$spec{defines}} ,
533 @{$spec{perlinc}} ,
534 '-o', $spec{output} ,
535 $spec{source} ,
536 ) ];
537}
538
539sub format_linker_cmd {
540 my ($self, %spec) = @_;
541
542 # The Config.pm variable 'libperl' is hardcoded to the full name
543 # of the perl import library (i.e. 'libperl56.a'). GCC will not
544 # find it unless the 'lib' prefix & the extension are stripped.
545 $spec{libperl} =~ s/^(?:lib)?([^.]+).*$/-l$1/;
546
547 unshift( @{$spec{other_ldflags}}, '-nostartfiles' )
548 if ( $spec{startup} && @{$spec{startup}} );
549
550 # From ExtUtils::MM_Win32:
551 #
552 ## one thing for GCC/Mingw32:
553 ## we try to overcome non-relocateable-DLL problems by generating
554 ## a (hopefully unique) image-base from the dll's name
555 ## -- BKS, 10-19-1999
556 File::Basename::basename( $spec{output} ) =~ /(....)(.{0,4})/;
557 $spec{image_base} = sprintf( "0x%x0000", unpack('n', $1 ^ $2) );
558
559 %spec = $self->write_linker_script(%spec)
560 if $spec{use_scripts};
561
562 foreach my $path ( @{$spec{libpath}} ) {
563 $path = "-L$path";
564 }
565
566 my @cmds; # Stores the series of commands needed to build the module.
567
568 push @cmds, [
569 'dlltool', '--def' , $spec{def_file},
570 '--output-exp' , $spec{explib}
571 ];
572
573 # split off any -arguments included in ld
574 my @ld = split / (?=-)/, $spec{ld};
575
576 push @cmds, [ grep {defined && length} (
577 @ld ,
578 '-o', $spec{output} ,
579 "-Wl,--base-file,$spec{base_file}" ,
580 "-Wl,--image-base,$spec{image_base}" ,
581 @{$spec{lddlflags}} ,
582 @{$spec{libpath}} ,
583 @{$spec{startup}} ,
584 @{$spec{objects}} ,
585 @{$spec{other_ldflags}} ,
586 $spec{libperl} ,
587 @{$spec{perllibs}} ,
588 $spec{explib} ,
589 $spec{map_file} ? ('-Map', $spec{map_file}) : ''
590 ) ];
591
592 push @cmds, [
593 'dlltool', '--def' , $spec{def_file},
594 '--output-exp' , $spec{explib},
595 '--base-file' , $spec{base_file}
596 ];
597
598 push @cmds, [ grep {defined && length} (
599 @ld ,
600 '-o', $spec{output} ,
601 "-Wl,--image-base,$spec{image_base}" ,
602 @{$spec{lddlflags}} ,
603 @{$spec{libpath}} ,
604 @{$spec{startup}} ,
605 @{$spec{objects}} ,
606 @{$spec{other_ldflags}} ,
607 $spec{libperl} ,
608 @{$spec{perllibs}} ,
609 $spec{explib} ,
610 $spec{map_file} ? ('-Map', $spec{map_file}) : ''
611 ) ];
612
613 return @cmds;
614}
615
616sub write_linker_script {
617 my ($self, %spec) = @_;
618
619 my $script = File::Spec->catfile( $spec{srcdir},
620 $spec{basename} . '.lds' );
621
622 $self->add_to_cleanup($script);
623
624 print "Generating script '$script'\n" if !$self->{quiet};
625
626 my $SCRIPT = IO::File->new( ">$script" )
627 or die( "Could not create script '$script': $!" );
628
629 print $SCRIPT ( 'SEARCH_DIR(' . $_ . ")\n" )
630 for @{delete $spec{libpath} || []};
631
632 # gcc takes only one startup file, so the first object in startup is
633 # specified as the startup file and any others are shifted into the
634 # beginning of the list of objects.
635 if ( $spec{startup} && @{$spec{startup}} ) {
636 print $SCRIPT 'STARTUP(' . shift( @{$spec{startup}} ) . ")\n";
637 unshift @{$spec{objects}},
638 @{delete $spec{startup} || []};
639 }
640
641 print $SCRIPT 'INPUT(' . join( ',',
642 @{delete $spec{objects} || []}
643 ) . ")\n";
644
645 print $SCRIPT 'INPUT(' . join( ' ',
646 (delete $spec{libperl} || ''),
647 @{delete $spec{perllibs} || []},
648 ) . ")\n";
649
650 push @{$spec{other_ldflags}}, '"' . $script . '"';
651
652 return %spec;
653}
654
6551;
656
657__END__
658
659=head1 NAME
660
661ExtUtils::CBuilder::Platform::Windows - Builder class for Windows platforms
662
663=head1 DESCRIPTION
664
665This module implements the Windows-specific parts of ExtUtils::CBuilder.
666Most of the Windows-specific stuff has to do with compiling and
667linking C code. Currently we support the 3 compilers perl itself
668supports: MSVC, BCC, and GCC.
669
670This module inherits from C<ExtUtils::CBuilder::Base>, so any functionality
671not implemented here will be implemented there. The interfaces are
672defined by the L<ExtUtils::CBuilder> documentation.
673
674=head1 AUTHOR
675
676Ken Williams <ken@mathforum.org>
677
678Most of the code here was written by Randy W. Sims <RandyS@ThePierianSpring.org>.
679
680=head1 SEE ALSO
681
682perl(1), ExtUtils::CBuilder(3), ExtUtils::MakeMaker(3)
683
684=cut