1# $Id: Makefile.PL,v 1.48 2012/10/26 19:11:54 mpeppler Exp $
2
3use ExtUtils::MakeMaker;
4
5require 5.004;
6
7use strict;
8
9# If either of these aren't available on your system then you need to
10# get them!
11use DBI;
12use DBI::DBD;
13use Config;
14use Getopt::Long;
15
16use vars qw($SYBASE $inc_string $lib_string $LINKTYPE $written_pwd_file
17  $newlibnames $libdir);
18
19$LINKTYPE         = 'dynamic';
20$written_pwd_file = 'PWD';
21$libdir           = 'lib';
22
23my $file;
24my $chained;
25my $threaded_libs;
26my $accept_test_defaults;
27GetOptions(
28	'--file'                 => \$file,
29	'--chained:s'            => \$chained,
30	'--threaded_libs:s'      => \$threaded_libs,
31	'--accept_test_defaults' => \$accept_test_defaults
32);
33
34select(STDOUT);
35$| = 1;
36
37configure();
38
39configPwd();
40
41my $lddlflags = $Config{lddlflags};
42$lddlflags = "-L$SYBASE/$libdir $lddlflags" unless $^O eq 'VMS';
43my $ldflags = $Config{ldflags};
44$ldflags = "-L$SYBASE/$libdir $ldflags" unless $^O eq 'VMS';
45
46WriteMakefile(
47	'NAME'         => 'DBD::Sybase',
48	LIBS           => [$lib_string],
49	INC            => $inc_string,
50	clean          => { FILES => "Sybase.xsi $written_pwd_file" },
51	OBJECT         => '$(O_FILES)',
52	'VERSION_FROM' => 'Sybase.pm',
53	'LDDLFLAGS'    => $lddlflags,
54
55	#	      'LDFLAGS'      => $ldflags,
56	LINKTYPE => $LINKTYPE,
57	(
58		$^O eq 'VMS'
59		? ( MAN3PODS => { 'Sybase.pm' => 'blib/man3/DBD_Sybase.3' } )
60		: ( MAN3PODS => { 'Sybase.pm' => 'blib/man3/DBD::Sybase.3' } )
61	),
62	(
63		$] >= 5.005
64		? (
65			ABSTRACT => 'DBI driver for Sybase datasources',
66			AUTHOR   => 'Michael Peppler (mpeppler@peppler.org)'
67		  )
68		: ()
69	),
70	(
71		     $] >= 5.005
72		  && $^O eq 'MSWin32'
73		  && $Config{archname} =~ /-object\b/i ? ( CAPI => 'TRUE' ) : ()
74	),
75	'dist' => { 'TARFLAGS' => 'cvf', 'COMPRESS' => 'gzip' },
76	( $^O eq 'VMS' ? ( PM => 'Sybase.pm' ) : () )
77);
78
79sub MY::postamble {
80	return dbd_postamble();
81}
82
83sub configure {
84	my %attr;
85	my ( $key, $val );
86
87	my $sybase_dir = $ENV{SYBASE};
88
89	if ( !$sybase_dir ) {
90
91		# PR 517 - getpwnam() isn't portable.
92		eval q{
93	    $sybase_dir = (getpwnam('sybase'))[7];
94	};
95	}
96
97	open( IN, "CONFIG" ) || die "Can't open CONFIG: $!";
98	while (<IN>) {
99		chomp;
100		next if /^\s*\#/;
101		next if /^\s*$/;
102
103		( $key, $val ) = split( /\s*=\s*/, $_ );
104		$key =~ s/\s//g;
105		$val =~ s/\s*$//;
106
107		$attr{$key} = $val;
108	}
109
110	if ( -d $sybase_dir ) {
111		$SYBASE = $sybase_dir;
112	}
113	else {
114		if ( $attr{SYBASE} && -d $attr{SYBASE} ) {
115			$SYBASE = $attr{SYBASE};
116		}
117	}
118
119	if ( !$SYBASE || $SYBASE =~ /^\s*$/ ) {
120		die
121"Please set SYBASE in CONFIG, or set the \$SYBASE environment variable";
122	}
123
124	$SYBASE = VMS::Filespec::unixify($SYBASE) if $^O eq 'VMS';
125	if ( $^O eq 'darwin' ) {
126		my $osxdir = 'Applications/Sybase/System';
127		$SYBASE = $osxdir if -d $SYBASE && -d $osxdir;
128	}
129
130	# System 12.0 has a different directory structure...
131	if ( defined( $ENV{SYBASE_OCS} ) ) {
132		# if $SYBASE_OCS is an absolute path, then use that
133		if ($ENV{SYBASE_OCS} =~ m!^/!) {
134			$SYBASE = $ENV{SYBASE_OCS};
135		} else {
136		  $SYBASE .= "/$ENV{SYBASE_OCS}";
137		}
138	}
139
140	my $libfound = 0;
141	my $libsub   = '';
142  my @libdir = ( 'lib', 'lib64' );
143  if ($^O =~ /win/i) {
144    @libdir = ( 'dll' );
145  }
146	foreach $libdir ( @libdir ) {
147		if ( -d "$SYBASE/$libdir" ) {
148			if ( checkLib($SYBASE) ) {
149				$libfound = 1;
150				$libsub   = $libdir;
151			}
152		}
153	}
154
155	die "Can't find any Sybase libraries in $SYBASE/lib or $SYBASE/lib64"
156	  unless $libfound;
157
158	my $inc_found = 0;
159	if ( -d "$SYBASE/include" && -f "$SYBASE/include/cspublic.h" ) {
160		++$inc_found;
161		$inc_string = "-I$SYBASE/include";
162	}
163
164	# In some freetds installations the include files have been moved
165	# into /usr/include/freetds.
166	if ( -d "$SYBASE/include/freetds" && "$SYBASE/include/freetds/cspublic.h" )
167	{
168		++$inc_found;
169		$inc_string = "-I$SYBASE/include/freetds";
170	}
171	die "Can't find the Client Library include files under $SYBASE"
172	  unless ($inc_found);
173
174	my $version = getLibVersion("$SYBASE/$libsub");
175
176	#    if(!$version || $version lt '12') {
177	#print "FreeTDS or older Client Library. Enabling CS-Lib Callbacks\n";
178	#$inc_string .= " -DUSE_CSLIB_CB=1";
179	#    }
180
181	checkChainedAutoCommit();
182
183	#    print "OS = $^O\n";
184
185	if ( $^O eq 'MSWin32' ) {
186    if ($version ge '15') {
187      $lib_string =
188  "-L$SYBASE/lib -llibsybct.lib -llibsybcs.lib -llibsybtcl.lib -llibsybcomn.lib -llibsybintl.lib -llibsybblk.lib $attr{EXTRA_LIBS} -lm";
189    }
190    else {
191      $lib_string =
192  "-L$SYBASE/lib -llibct.lib -llibcs.lib -llibtcl.lib -llibcomn.lib -llibintl.lib -llibblk.lib $attr{EXTRA_LIBS} -lm";
193    }
194	}
195	elsif ( $^O eq 'VMS' ) {
196		$lib_string =
197"-L$SYBASE/lib -llibct.olb -llibcs.olb -llibtcl.olb -llibcomn.olb -llibintl.olb -llibblk.olb $attr{EXTRA_LIBS}";
198	}
199	elsif ( $^O =~ /cygwin/ ) {
200		$lib_string = "-L$SYBASE/lib -lct -lcs -lblk";
201		$inc_string .= " -D_MSC_VER=800";
202	}
203	else {
204
205		# Supplied by Erick Calder. I'm not sure why libsybsrv is needed...
206		$attr{EXTRA_LIBS} .= " -lsybsrv" if $^O eq 'darwin';
207
208		my $extra = getExtraLibs( $SYBASE, $attr{EXTRA_LIBS}, $version );
209		if ($file) {
210			$lib_string =
211"-L$SYBASE/$libdir -lct -lcs -ltcl -lcomn -lintl -lblk $attr{EXTRA_LIBS} -ldl -lm";
212		}
213		else {
214			$lib_string =
215"-L$SYBASE/$libdir -lct -lcs -ltcl -lcomn -lintl -lblk $extra -ldl -lm";
216		}
217		if ($newlibnames) {
218			foreach (qw(ct cs tcl comn intl blk)) {
219				$lib_string =~ s/-l$_/-lsyb$_/;
220			}
221		}
222		elsif ( $^O =~ /linux|freebsd/i ) {
223			$lib_string =~ s/-ltcl/-lsybtcl/;
224		}
225
226		my %libname;
227
228		%libname = loadSybLibs("$SYBASE/$libdir");
229
230		my $libtype = '';
231
232		# Logic to replace normal libs with _r (re-entrant) libs if
233		# usethreads is defined provided by W. Phillip Moore (wpm@ms.com)
234		# I have no idea if this works on Win32 systems (probably not!)
235		if ( $Config{usethreads} ) {
236			print "Running in threaded mode - looking for _r libraries...\n";
237
238			if ( checkForThreadedLibs() ) {
239				my $found = 0;
240				foreach ( split( /\s+/, $lib_string ) ) {
241					next unless /^-l(\S+)/;
242					my $oldname = $1;
243					my $newname = $1 . "_r";
244					next unless exists $libname{$newname};
245					print "Found -l$newname for -l$oldname\n";
246					++$found;
247					$lib_string =~ s/-l$oldname\b/-l$newname/;
248				}
249				if ( !$found ) {
250					print "No thread-safe Sybase libraries found\n";
251					$inc_string .= ' -DNO_THREADS ';
252				}
253				else {
254					$libtype .= '_r';
255				}
256			}
257			else {
258				print "OK - I'll use the normal libs\n\n";
259			}
260		}
261
262		# If we are building for a 64 bit platform that also supports 32 bit
263		# (i.e. Solaris 8, HP-UX11, etc) then we need to make some adjustments
264		if ( $Config{use64bitall} ) {
265
266			# Tru64/DEC OSF does NOT use the SYB_LP64 define
267			# as it is ALWAYS in 64 bit mode.
268			$inc_string .= ' -DSYB_LP64' unless $^O eq 'dec_osf';
269
270			print "Running in 64bit mode - looking for '64' libraries...\n";
271
272			my $found = 0;
273			foreach ( split( /\s+/, $lib_string ) ) {
274				next unless /^-l(\S+)/;
275				my $oldname = $1;
276				my $newname = $1 . '64';
277				next unless exists $libname{$newname};
278				print "Found -l$newname for -l$oldname\n";
279				$lib_string =~ s/-l$oldname\b/-l$newname/;
280				++$found;
281			}
282			if ($found) {
283				$libtype .= '64';
284			}
285		}
286
287		# Is the blk library available?
288		#my @k = keys(%libname);
289		#print "@k\n";
290		#print "libtype = $libtype\n";
291		if ( my @l = grep( /(syb)?blk$libtype/, keys(%libname) ) ) {
292			print "BLK api available - found: @l\n";
293		}
294		else {
295			print "BLK api NOT available.\n";
296			$inc_string .= ' -DNO_BLK=1';
297		}
298	}
299
300	my $config_sitearch  = $Config{sitearchexp};
301	my $attr_dbi_include = $attr{DBI_INCLUDE};
302	if ( $^O eq 'VMS' ) {
303		$config_sitearch  = VMS::Filespec::unixify($config_sitearch);
304		$attr_dbi_include = VMS::Filespec::unixify($attr_dbi_include);
305	}
306	my @try = ( @INC, $Config{sitearchexp} );
307	unshift @try, $attr{DBI_INCLUDE} if $attr{DBI_INCLUDE};
308	my $dbidir;
309	for my $trydir (@try) {
310		if ( -e "$trydir/auto/DBI/DBIXS.h" ) {
311			$dbidir = "$trydir/auto/DBI";
312			last;
313		}
314	}
315	die "Can't find the DBI include files. Please set DBI_INCLUDE in CONFIG"
316	  if !$dbidir;
317	$inc_string .= " -I$dbidir";
318	if ( $attr{LINKTYPE} ) {
319		$LINKTYPE = $attr{LINKTYPE};
320	}
321}
322
323sub loadSybLibs {
324	my $dir = shift;
325
326	my %libname = ();
327
328	opendir( SYBLIB, $dir )
329	  or die "Unable to opendir $dir: $!\n";
330	foreach ( readdir(SYBLIB) ) {
331		next unless -f "$dir/$_";
332		next unless /^lib(\S+)\.(so|a|sl)/;
333		$libname{$1} = 1;
334	}
335
336	closedir(SYBLIB);
337
338	return %libname;
339}
340
341sub getLibVersion {
342	my $lib = shift;
343
344	opendir( DIR, $lib );
345
346	# reverse to pick up libsybct before libct...
347	my @files = reverse( grep( /lib(syb)?ct(64)?\./, readdir(DIR) ) );
348	closedir(DIR);
349	my $file;
350	foreach (@files) {
351		$file = "$lib/$_";
352		last if -e $file;
353	}
354
355	open( IN, $file ) || die "Can't open $file: $!";
356	binmode(IN);
357	my $version;
358	while (<IN>) {
359		if (/Sybase Client-Library\/([^\/]+)\//) {
360			$version = $1;
361			last;
362		}
363	}
364	close(IN);
365
366	if ( !$version ) {
367		print "Unknown Client Library version - assuming FreeTDS.\n";
368	}
369	else {
370		print "Sybase OpenClient $version found.\n";
371	}
372
373	return $version;
374}
375
376sub getExtraLibs {
377	my $dir     = shift;
378	my $cfg     = shift;
379	my $version = shift;
380
381	opendir( DIR, "$dir/$libdir" ) || die "Can't access $dir/$libdir: $!";
382	my %files =
383	  map { $_ =~ s/lib([^\.]+)\..*/$1/; $_ => 1 }
384	  grep( /lib/ && -f "$dir/$libdir/$_", readdir(DIR) );
385	closedir(DIR);
386
387	my %x = map { $_ => 1 } split( ' ', $cfg );
388	my $dlext = $Config{dlext} || 'so';
389	foreach my $f ( keys(%x) ) {
390		my $file = $f;
391		$file =~ s/-l//;
392		next if ( $file =~ /^-/ );
393		delete( $x{$f} ) unless exists( $files{$file} );
394	}
395
396	foreach my $f (qw(insck tli sdna dnet_stub tds skrb gss)) {
397		$x{"-l$f"} = 1 if exists $files{$f} && -f "$dir/$libdir/lib$f.$dlext";
398	}
399	if ( $version gt '11' ) {
400		delete( $x{-linsck} );
401		delete( $x{-ltli} );
402	}
403
404	#    if($version ge '12.5.1') {
405	#	delete($x{-lskrb});
406	#    }
407
408	join( ' ', keys(%x) );
409}
410
411sub checkLib {
412	my $dir = shift;
413
414	opendir( DIR, "$dir/$libdir" ) || die "Can't access $dir/$libdir: $!";
415	my @files = grep( /libct|libsybct/i, readdir(DIR) );
416	closedir(DIR);
417	if ( grep( /libsybct/, @files ) ) {
418		$newlibnames = 1;
419	}
420	else {
421		$newlibnames = 0;
422	}
423
424	scalar(@files);
425}
426
427sub configPwd {
428	open( IN, "PWD.factory" ) || die "Can't open PWD.factory: $!";
429	my %pwd;
430	while (<IN>) {
431		chomp;
432		next if (/^\s*\#/);
433		next if (/^\s*$/);
434		my ( $key, $val ) = split( /=/, $_ );
435		$pwd{$key} = $val || "undef";
436	}
437	close(IN);
438
439	if ($accept_test_defaults) {
440		$pwd{SRV} = $pwd{SRV};
441		$pwd{UID} = $pwd{UID};
442		$pwd{PWD} = $pwd{PWD};
443		$pwd{DB}  = $pwd{DB};
444	}
445	else {
446		print
447"The DBD::Sybase module need access to a Sybase server to run the tests.\n";
448		print "To clear an entry please enter 'undef'\n";
449		print "Sybase server to use (default: $pwd{SRV}): ";
450		$pwd{SRV} = getAns(0) || $pwd{SRV};
451		print "User ID to log in to Sybase (default: $pwd{UID}): ";
452		$pwd{UID} = getAns(0) || $pwd{UID};
453		print "Password (default: $pwd{PWD}): ";
454		$pwd{PWD} = getAns(1) || $pwd{PWD};
455		print "Sybase database to use on $pwd{SRV} (default: $pwd{DB}): ";
456		$pwd{DB} = getAns(0) || $pwd{DB};
457	}
458
459	warn
460"\n* Writing login information, including password, to file $written_pwd_file.\n\n";
461
462	# Create the file non-readable by anyone else.
463	my $old_umask;
464	unless ( $^O =~ /MSWin32/ ) {
465		$old_umask = umask(077);
466		warn "cannot umask(): $!" unless defined($old_umask);
467	}
468	open( OUT, ">$written_pwd_file" ) || die "Can't open $written_pwd_file: $!";
469	unless ( $^O =~ /MSWin32/ ) {
470		umask($old_umask) != 077 && warn "strange return from umask()";
471	}
472	print OUT <<EOF;
473# This file contains optional login id, passwd and server info for the test
474# programs:
475# You probably don't want to have it lying around after you've made
476# sure that everything works OK.
477
478EOF
479	foreach ( keys %pwd ) {
480		$pwd{$_} = '' if $pwd{$_} eq 'undef';
481		print OUT "$_=$pwd{$_}\n";
482	}
483	close(OUT);
484}
485
486sub checkChainedAutoCommit {
487	print <<EOF;
488
489By default DBD::Sybase 1.05 and later use the 'CHAINED' mode (where available)
490when 'AutoCommit' is turned off. Versions 1.04 and older instead managed
491the transactions explicitly with a 'BEGIN TRAN' before the first DML
492statement. Using the 'CHAINED' mode is preferable as it is the way that
493Sybase implements AutoCommit handling for both its ODBC and JDBC drivers.
494
495EOF
496	print "Use 'CHAINED' mode by default (Y/N) [Y]: ";
497	my $ans;
498	if ( defined($chained) ) {
499		$ans = $chained;
500	}
501	else {
502		$ans = getAns(0);
503	}
504	if ( $ans and $ans !~ /^y/i ) {
505		$inc_string .= " -DNO_CHAINED_TRAN=1";
506	}
507	print "\n";
508}
509
510sub checkForThreadedLibs {
511	my $ret = 1;
512	if ( $] >= 5.008 ) {
513		$ret = 0;
514
515		print <<EOF;
516
517***NOTE***
518There is an incompatibility between perl (5.8.x or later) built in threaded 
519mode and Sybase's threaded libraries, which means that signals delivered to 
520the perl process result in a segment violation.
521
522I suggest building DBD::Sybase with the normal libraries in this case to get
523reasonable behavior for signal handling.
524
525EOF
526		print "Use the threaded (lib..._r) libraries [N]: ";
527		my $ans;
528
529		if ( defined($threaded_libs) ) {
530			$ans = $threaded_libs;
531		}
532		else {
533			$ans = getAns(0);
534		}
535		if ( $ans and $ans =~ /^y/i ) {
536			$ret = 1;
537		}
538		print "\n";
539	}
540
541	return $ret;
542}
543
544sub getAns {
545	my $flag = shift;
546
547	if ( $flag && -t ) {
548		eval {
549			require Term::ReadKey;
550			Term::ReadKey::ReadMode('noecho');
551		};
552	}
553	my $ans = <STDIN>;
554	if ( $flag && -t ) {
555		eval { Term::ReadKey::ReadMode('normal'); };
556		print "\n";    # because newline from user wasn't echo'd
557	}
558	$ans =~ s/^\s+//;
559	$ans =~ s/\s+$//;
560
561	return $ans;
562}
563