xref: /PHP-5.3/win32/build/mkdist.php (revision 81ba6b14)
1<?php # $Id$
2/* piece together a windows binary distro */
3
4$build_dir = $argv[1];
5$php_build_dir = $argv[2];
6$phpdll = $argv[3];
7$sapi_targets = explode(" ", $argv[4]);
8$ext_targets = explode(" ", $argv[5]);
9$pecl_targets = explode(" ", $argv[6]);
10$snapshot_template = $argv[7];
11
12$is_debug = preg_match("/^debug/i", $build_dir);
13
14echo "Making dist for $build_dir\n";
15
16$dist_dir = $build_dir . "/php-" . phpversion();
17$test_dir = $build_dir . "/php-test-pack-" . phpversion();
18$pecl_dir = $build_dir . "/pecl-" . phpversion();
19
20@mkdir($dist_dir);
21@mkdir("$dist_dir/ext");
22@mkdir("$dist_dir/dev");
23@mkdir("$dist_dir/extras");
24@mkdir($pecl_dir);
25
26/* figure out additional DLL's that are required */
27$extra_dll_deps = array();
28$per_module_deps = array();
29$pecl_dll_deps = array();
30
31function get_depends($module)
32{
33	static $no_dist = array(
34		/* windows system dlls that should not be bundled */
35		'advapi32.dll', 'comdlg32.dll', 'crypt32.dll', 'gdi32.dll', 'kernel32.dll', 'ntdll.dll',
36		'odbc32.dll', 'ole32.dll', 'oleaut32.dll', 'rpcrt4.dll',
37		'shell32.dll', 'shlwapi.dll', 'user32.dll', 'ws2_32.dll', 'ws2help.dll',
38		'comctl32.dll', 'winmm.dll', 'wsock32.dll', 'winspool.drv', 'msasn1.dll',
39		'secur32.dll', 'netapi32.dll',
40
41		/* apache */
42		'apachecore.dll',
43
44		/* apache 2 */
45		'libhttpd.dll', 'libapr.dll', 'libaprutil.dll','libapr-1.dll', 'libaprutil-1.dll',
46
47		/* pi3web */
48		'piapi.dll', 'pi3api.dll',
49
50		/* nsapi */
51		'ns-httpd30.dll', 'ns-httpd35.dll', 'ns-httpd36.dll', 'ns-httpd40.dll',
52
53		/* oracle */
54		'oci.dll', 'ociw32.dll',
55
56		/* sybase */
57		'libcs.dll', 'libct.dll',
58
59		/* firebird */
60		'fbclient.dll',
61
62		/* visual C++; mscvrt.dll is present on everyones system,
63		 * but the debug version (msvcrtd.dll) and those from visual studio.net
64		 * (msvcrt7x.dll) are not */
65		'msvcrt.dll',
66		'msvcr90.dll',
67		'wldap32.dll'
68		);
69	global $build_dir, $extra_dll_deps, $ext_targets, $sapi_targets, $pecl_targets, $phpdll, $per_module_deps, $pecl_dll_deps;
70
71	$bd = strtolower(realpath($build_dir));
72
73	$is_pecl = in_array($module, $pecl_targets);
74
75	$cmd = "$GLOBALS[build_dir]\\deplister.exe \"$module\" \"$GLOBALS[build_dir]\"";
76	$proc = proc_open($cmd,
77			array(1 => array("pipe", "w")),
78			$pipes);
79
80	$n = 0;
81	while (($line = fgetcsv($pipes[1]))) {
82		$n++;
83
84		$dep = strtolower($line[0]);
85		$depbase = basename($dep);
86		/* ignore stuff in our build dir, but only if it is
87	     * one of our targets */
88		if (((in_array($depbase, $sapi_targets) ||
89			   	in_array($depbase, $ext_targets) || in_array($depbase, $pecl_targets)) ||
90				$depbase == $phpdll) && file_exists($GLOBALS['build_dir'] . "/$depbase")) {
91			continue;
92		}
93		/* ignore some well-known system dlls */
94		if (in_array(basename($dep), $no_dist)) {
95			continue;
96		}
97
98		if ($is_pecl) {
99			if (!in_array($dep, $pecl_dll_deps)) {
100				$pecl_dll_deps[] = $dep;
101			}
102		} else {
103			if (!in_array($dep, $extra_dll_deps)) {
104				$extra_dll_deps[] = $dep;
105			}
106		}
107
108		$per_module_deps[basename($module)][] = $dep;
109	}
110	fclose($pipes[1]);
111	proc_close($proc);
112//echo "Module $module [$n lines]\n";
113}
114
115function copy_file_list($source_dir, $dest_dir, $list)
116{
117	global $is_debug, $dist_dir;
118
119	foreach ($list as $item) {
120		if (empty($item)) {
121			continue;
122		} elseif (!is_file($source_dir . DIRECTORY_SEPARATOR . $item)) {
123			echo "WARNING: $item not found\n";
124			continue;
125		}
126
127		echo "Copying $item from $source_dir to $dest_dir\n";
128		copy($source_dir . DIRECTORY_SEPARATOR . $item, $dest_dir . DIRECTORY_SEPARATOR . $item);
129		if ($is_debug) {
130			$itemdb = preg_replace("/\.(exe|dll|lib)$/i", ".pdb", $item);
131			if (file_exists("$source_dir/$itemdb")) {
132				copy("$source_dir/$itemdb", "$dist_dir/dev/$itemdb");
133			}
134		}
135		if (preg_match("/\.(exe|dll)$/i", $item)) {
136			get_depends($source_dir . '/' . $item);
137		}
138	}
139}
140
141function copy_text_file($source, $dest)
142{
143	$text = file_get_contents($source);
144	$text = preg_replace("/(\r\n?)|\n/", "\r\n", $text);
145	$fp = fopen($dest, "w");
146	fwrite($fp, $text);
147	fclose($fp);
148}
149
150/* very light-weight function to extract a single named file from
151 * a gzipped tarball.  This makes assumptions about the files
152 * based on the PEAR info set in $packages. */
153function extract_file_from_tarball($pkg, $filename, $dest_dir) /* {{{ */
154{
155	global $packages;
156
157	$name = $pkg . '-' . $packages[$pkg];
158	$tarball = $dest_dir . "/" . $name . '.tgz';
159	$filename = $name . '/' . $filename;
160	$destfilename = $dest_dir . "/" . basename($filename);
161
162	$fp = gzopen($tarball, 'rb');
163
164	$done = false;
165	do {
166		/* read the header */
167		$hdr_data = gzread($fp, 512);
168	   	if (strlen($hdr_data) == 0)
169			break;
170		$checksum = 0;
171		for ($i = 0; $i < 148; $i++)
172			$checksum += ord($hdr_data{$i});
173		for ($i = 148; $i < 156; $i++)
174			$checksum += 32;
175		for ($i = 156; $i < 512; $i++)
176			$checksum += ord($hdr_data{$i});
177
178		$hdr = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $hdr_data);
179
180		$hdr['checksum'] = octdec(trim($hdr['checksum']));
181
182		if ($hdr['checksum'] != $checksum) {
183			echo "Checksum for $tarball $hdr[filename] is invalid\n";
184			print_r($hdr);
185			return;
186		}
187
188		$hdr['size'] = octdec(trim($hdr['size']));
189		echo "File: $hdr[filename] $hdr[size]\n";
190
191		if ($filename == $hdr['filename']) {
192			echo "Found the file we want\n";
193			$dest = fopen($destfilename, 'wb');
194			$x = stream_copy_to_stream($fp, $dest, $hdr['size']);
195			fclose($dest);
196			echo "Wrote $x bytes into $destfilename\n";
197			break;
198		}
199
200		/* skip body of the file */
201		$size = 512 * ceil((int)$hdr['size'] / 512);
202		echo "Skipping $size bytes\n";
203		gzseek($fp, gztell($fp) + $size);
204
205	} while (!$done);
206
207} /* }}} */
208
209
210/* the core dll */
211copy("$build_dir/php.exe", "$dist_dir/php.exe");
212copy("$build_dir/$phpdll", "$dist_dir/$phpdll");
213
214/* and the .lib goes into dev */
215$phplib = str_replace(".dll", ".lib", $phpdll);
216copy("$build_dir/$phplib", "$dist_dir/dev/$phplib");
217/* debug builds; copy the symbols too */
218if ($is_debug) {
219	$phppdb = str_replace(".dll", ".pdb", $phpdll);
220	copy("$build_dir/$phppdb", "$dist_dir/dev/$phppdb");
221}
222/* copy the sapi */
223copy_file_list($build_dir, "$dist_dir", $sapi_targets);
224
225/* copy the extensions */
226copy_file_list($build_dir, "$dist_dir/ext", $ext_targets);
227
228/* pecl sapi and extensions */
229if(sizeof($pecl_targets)) {
230	copy_file_list($build_dir, $pecl_dir, $pecl_targets);
231}
232
233/* populate reading material */
234$text_files = array(
235	"LICENSE" => "license.txt",
236	"NEWS" => "news.txt",
237	"README.REDIST.BINS" => "readme-redist-bins.txt",
238	"php.ini-development" => "php.ini-development",
239	"php.ini-production" => "php.ini-production",
240	"win32/install.txt" => "install.txt",
241	"win32/pws-php5cgi.reg" => "pws-php5cgi.reg",
242	"win32/pws-php5isapi.reg" => "pws-php5isapi.reg",
243);
244
245foreach ($text_files as $src => $dest) {
246	copy_text_file($src, $dist_dir . '/' . $dest);
247}
248
249/* general other files */
250$general_files = array(
251	"php.gif"			=>	"php.gif",
252);
253
254foreach ($general_files as $src => $dest) {
255	copy($src, $dist_dir . '/' . $dest);
256}
257
258/* include a snapshot identifier */
259$branch = "HEAD"; // TODO - determine this from SVN branche name
260$fp = fopen("$dist_dir/snapshot.txt", "w");
261$now = date("r");
262$version = phpversion();
263fwrite($fp, <<<EOT
264This snapshot was automatically generated on
265$now
266
267Version: $version
268Branch: $branch
269Build: $build_dir
270
271EOT
272);
273/* list build-in extensions */
274$exts = get_loaded_extensions();
275fprintf($fp, "\r\nBuilt-in Extensions\r\n");
276fwrite($fp, "===========================\r\n");
277foreach ($exts as $ext) {
278	fprintf($fp, "%s\r\n", $ext);
279}
280fwrite($fp, "\r\n\r\n");
281
282/* list dependencies */
283fprintf($fp, "Dependency information:\r\n");
284foreach ($per_module_deps as $modulename => $deps) {
285	if (in_array($modulename, $pecl_targets))
286		continue;
287
288	fprintf($fp, "Module: %s\r\n", $modulename);
289	fwrite($fp, "===========================\r\n");
290	foreach ($deps as $dll) {
291		fprintf($fp, "\t%s\r\n", basename($dll));
292	}
293	fwrite($fp, "\r\n");
294}
295fclose($fp);
296
297/* Now add those dependencies */
298foreach ($extra_dll_deps as $dll) {
299	if (!file_exists($dll)) {
300		/* try template dir */
301		$tdll = $snapshot_template . "/dlls/" . basename($dll);
302		if (!file_exists($tdll)) {
303			$tdll = $php_build_dir . '/bin/' . basename($dll);
304			if (!file_exists($tdll)) {
305				echo "WARNING: distro depends on $dll, but could not find it on your system\n";
306				continue;
307			}
308		}
309		$dll = $tdll;
310	}
311	copy($dll, "$dist_dir/" . basename($dll));
312}
313
314/* TODO:
315add sanity check and test if all required DLLs are present, per version
316This version works at least for 3.6, 3.8 and 4.0 (5.3-vc6, 5.3-vc9 and HEAD).
317Add ADD_DLLS to add extra DLLs like dynamic dependencies for standard
318deps. For example, libenchant.dll loads libenchant_myspell.dll or
319libenchant_ispell.dll
320*/
321$ICU_DLLS = $php_build_dir . '/bin/icu*.dll';
322foreach (glob($ICU_DLLS) as $filename) {
323	copy($filename, "$dist_dir/" . basename($filename));
324}
325$ENCHANT_DLLS = array(
326	'glib-2.dll',
327	'gmodule-2.dll',
328	'libenchant_myspell.dll',
329	'libenchant_ispell.dll',
330);
331foreach ($ENCHANT_DLLS as $filename) {
332	copy($php_build_dir . '/bin/' . $filename, "$dist_dir/" . basename($filename));
333}
334
335/* and those for pecl */
336foreach ($pecl_dll_deps as $dll) {
337	if (in_array($dll, $extra_dll_deps)) {
338		/* already in main distro */
339		continue;
340	}
341	if (!file_exists($dll)) {
342		/* try template dir */
343		$tdll = $snapshot_template . "/dlls/" . basename($dll);
344		if (!file_exists($tdll)) {
345			echo "WARNING: distro depends on $dll, but could not find it on your system\n";
346			continue;
347		}
348		$dll = $tdll;
349	}
350	copy($dll, "$pecl_dir/" . basename($dll));
351}
352
353function copy_dir($source, $dest)
354{
355	if (!is_dir($dest)) {
356		if (!mkdir($dest)) {
357			return false;
358		}
359	}
360
361	$d = opendir($source);
362	while (($f = readdir($d)) !== false) {
363		if ($f == '.' || $f == '..' || $f == '.svn') {
364			continue;
365		}
366		$fs = $source . '/' . $f;
367		$fd = $dest . '/' . $f;
368		if (is_dir($fs)) {
369			copy_dir($fs, $fd);
370		} else {
371			copy($fs, $fd);
372		}
373	}
374	closedir($d);
375}
376
377
378
379function copy_test_dir($directory, $dest)
380{
381	if(substr($directory,-1) == '/') {
382		$directory = substr($directory,0,-1);
383	}
384
385	if ($directory == 'tests' || $directory == 'examples') {
386		if (!is_dir($dest . '/tests')) {
387			mkdir($dest . '/tests', 0775, true);
388		}
389		copy_dir($directory, $dest . '/tests/');
390
391		return false;
392	}
393
394	if(!file_exists($directory) || !is_dir($directory)) {
395		echo "failed... $directory\n";
396		return FALSE;
397	}
398
399	$directory_list = opendir($directory);
400
401	while (FALSE !== ($file = readdir($directory_list))) {
402		$full_path = $directory . '/' . $file;
403		if($file != '.' && $file != '..' && $file != '.svn' && is_dir($full_path)) {
404			if ($file == 'tests' || $file == 'examples') {
405				if (!is_dir($dest . '/' . $full_path)) {
406					mkdir($dest . '/' . $full_path , 0775, true);
407				}
408				copy_dir($full_path, $dest . '/' . $full_path . '/');
409				continue;
410			} else {
411				copy_test_dir($full_path, $dest);
412			}
413		}
414	}
415
416	closedir($directory_list);
417}
418
419function make_phar_dot_phar($dist_dir)
420{
421	if (!extension_loaded('phar')) {
422		return;
423	}
424
425	$path_to_phar = realpath(__DIR__ . '/../../ext/phar');
426
427	echo "Generating pharcommand.phar\n";
428	$phar = new Phar($dist_dir . '/pharcommand.phar', 0, 'pharcommand');
429
430	foreach (new DirectoryIterator($path_to_phar . '/phar') as $file) {
431		if ($file->isDir() || $file == 'phar.php') {
432			continue;
433		}
434
435		echo 'adding ', $file, "\n";
436		$phar[(string) $file] = file_get_contents($path_to_phar.  '/phar/' . $file);
437	}
438
439	$phar->setSignatureAlgorithm(Phar::SHA1);
440	$stub = file($path_to_phar . '/phar/phar.php');
441
442	unset($stub[0]); // remove hashbang
443	$phar->setStub(implode('', $stub));
444
445	echo "Creating phar.phar.bat\n";
446	file_put_contents($dist_dir . '/phar.phar.bat', "%~dp0php.exe %~dp0pharcommand.phar %*\r\n");
447}
448
449if (!is_dir($test_dir)) {
450	mkdir($test_dir);
451}
452
453$dirs = array(
454	'ext',
455	'Sapi',
456	'Zend',
457	'tests'
458);
459foreach ($dirs as $dir) {
460	copy_test_dir($dir, $test_dir);
461}
462copy('run-tests.php', $test_dir . '/run-test.php');
463
464/* change this next line to true to use good-old
465 * hand-assembled go-pear-bundle from the snapshot template */
466$use_pear_template = true;
467
468if (!$use_pear_template) {
469	/* Let's do a PEAR-less pear setup */
470	mkdir("$dist_dir/PEAR");
471	mkdir("$dist_dir/PEAR/go-pear-bundle");
472
473	/* grab the bootstrap script */
474	echo "Downloading go-pear\n";
475	copy("http://pear.php.net/go-pear", "$dist_dir/PEAR/go-pear.php");
476
477	/* import the package list -- sets $packages variable */
478	include "pear/go-pear-list.php";
479
480	/* download the packages into the destination */
481	echo "Fetching packages\n";
482
483	foreach ($packages as $name => $version) {
484		$filename = "$name-$version.tgz";
485		$destfilename = "$dist_dir/PEAR/go-pear-bundle/$filename";
486		if (file_exists($destfilename))
487			continue;
488		$url = "http://pear.php.net/get/$filename";
489		echo "Downloading $name from $url\n";
490		flush();
491		copy($url, $destfilename);
492	}
493
494	echo "Download complete.  Extracting bootstrap files\n";
495
496	/* Now, we want PEAR.php, Getopt.php (Console_Getopt) and Tar.php (Archive_Tar)
497	 * broken out of the tarballs */
498	extract_file_from_tarball('PEAR', 'PEAR.php', "$dist_dir/PEAR/go-pear-bundle");
499	extract_file_from_tarball('Archive_Tar', 'Archive/Tar.php', "$dist_dir/PEAR/go-pear-bundle");
500	extract_file_from_tarball('Console_Getopt', 'Console/Getopt.php', "$dist_dir/PEAR/go-pear-bundle");
501}
502
503/* add extras from the template dir */
504if (file_exists($snapshot_template)) {
505	$items = glob("$snapshot_template/*");
506	print_r($items);
507
508	foreach ($items as $item) {
509		$bi = basename($item);
510		if (is_dir($item)) {
511			if ($bi == 'dlls' || $bi == 'symbols') {
512				continue;
513			} else if ($bi == 'PEAR') {
514				if ($use_pear_template) {
515					/* copy to top level */
516					copy_dir($item, "$dist_dir/$bi");
517				}
518			} else {
519				/* copy that dir into extras */
520				copy_dir($item, "$dist_dir/extras/$bi");
521			}
522		} else {
523			if ($bi == 'go-pear.bat') {
524				/* copy to top level */
525				copy($item, "$dist_dir/$bi");
526			} else {
527				/* copy to extras */
528				copy($item, "$dist_dir/extras/$bi");
529			}
530		}
531	}
532
533	/* copy c++ runtime */
534	$items = glob("$snapshot_template/dlls/*.CRT");
535
536	foreach ($items as $item) {
537		$bi = basename($item);
538		if (is_dir($item)) {
539			copy_dir($item, "$dist_dir/$bi");
540			copy_dir($item, "$dist_dir/ext/$bi");
541		}
542	}
543} else {
544	echo "WARNING: you don't have a snapshot template, your dist will not be complete\n";
545}
546
547make_phar_dot_phar($dist_dir);
548?>
549