xref: /PHP-5.4/win32/build/mkdist.php (revision 5efda71e)
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	"$GLOBALS[build_dir]\\deplister.exe"	=>	"deplister.exe",
253);
254
255foreach ($general_files as $src => $dest) {
256	copy($src, $dist_dir . '/' . $dest);
257}
258
259/* include a snapshot identifier */
260$branch = "HEAD"; // TODO - determine this from SVN branche name
261$fp = fopen("$dist_dir/snapshot.txt", "w");
262$now = date("r");
263$version = phpversion();
264fwrite($fp, <<<EOT
265This snapshot was automatically generated on
266$now
267
268Version: $version
269Branch: $branch
270Build: $build_dir
271
272EOT
273);
274/* list build-in extensions */
275$exts = get_loaded_extensions();
276fprintf($fp, "\r\nBuilt-in Extensions\r\n");
277fwrite($fp, "===========================\r\n");
278foreach ($exts as $ext) {
279	fprintf($fp, "%s\r\n", $ext);
280}
281fwrite($fp, "\r\n\r\n");
282
283/* list dependencies */
284fprintf($fp, "Dependency information:\r\n");
285foreach ($per_module_deps as $modulename => $deps) {
286	if (in_array($modulename, $pecl_targets))
287		continue;
288
289	fprintf($fp, "Module: %s\r\n", $modulename);
290	fwrite($fp, "===========================\r\n");
291	foreach ($deps as $dll) {
292		fprintf($fp, "\t%s\r\n", basename($dll));
293	}
294	fwrite($fp, "\r\n");
295}
296fclose($fp);
297
298/* Now add those dependencies */
299foreach ($extra_dll_deps as $dll) {
300	if (!file_exists($dll)) {
301		/* try template dir */
302		$tdll = $snapshot_template . "/dlls/" . basename($dll);
303		if (!file_exists($tdll)) {
304			$tdll = $php_build_dir . '/bin/' . basename($dll);
305			if (!file_exists($tdll)) {
306				echo "WARNING: distro depends on $dll, but could not find it on your system\n";
307				continue;
308			}
309		}
310		$dll = $tdll;
311	}
312	copy($dll, "$dist_dir/" . basename($dll));
313}
314
315/* TODO:
316add sanity check and test if all required DLLs are present, per version
317This version works at least for 3.6, 3.8 and 4.0 (5.3-vc6, 5.3-vc9 and HEAD).
318Add ADD_DLLS to add extra DLLs like dynamic dependencies for standard
319deps. For example, libenchant.dll loads libenchant_myspell.dll or
320libenchant_ispell.dll
321*/
322$ICU_DLLS = $php_build_dir . '/bin/icu*.dll';
323foreach (glob($ICU_DLLS) as $filename) {
324	copy($filename, "$dist_dir/" . basename($filename));
325}
326$ENCHANT_DLLS = array(
327	'glib-2.dll',
328	'gmodule-2.dll',
329	'libenchant_myspell.dll',
330	'libenchant_ispell.dll',
331);
332foreach ($ENCHANT_DLLS as $filename) {
333	copy($php_build_dir . '/bin/' . $filename, "$dist_dir/" . basename($filename));
334}
335
336/* and those for pecl */
337foreach ($pecl_dll_deps as $dll) {
338	if (in_array($dll, $extra_dll_deps)) {
339		/* already in main distro */
340		continue;
341	}
342	if (!file_exists($dll)) {
343		/* try template dir */
344		$tdll = $snapshot_template . "/dlls/" . basename($dll);
345		if (!file_exists($tdll)) {
346			echo "WARNING: distro depends on $dll, but could not find it on your system\n";
347			continue;
348		}
349		$dll = $tdll;
350	}
351	copy($dll, "$pecl_dir/" . basename($dll));
352}
353
354function copy_dir($source, $dest)
355{
356	if (!is_dir($dest)) {
357		if (!mkdir($dest)) {
358			return false;
359		}
360	}
361
362	$d = opendir($source);
363	while (($f = readdir($d)) !== false) {
364		if ($f == '.' || $f == '..' || $f == '.svn') {
365			continue;
366		}
367		$fs = $source . '/' . $f;
368		$fd = $dest . '/' . $f;
369		if (is_dir($fs)) {
370			copy_dir($fs, $fd);
371		} else {
372			copy($fs, $fd);
373		}
374	}
375	closedir($d);
376}
377
378
379
380function copy_test_dir($directory, $dest)
381{
382	if(substr($directory,-1) == '/') {
383		$directory = substr($directory,0,-1);
384	}
385
386	if ($directory == 'tests' || $directory == 'examples') {
387		if (!is_dir($dest . '/tests')) {
388			mkdir($dest . '/tests', 0775, true);
389		}
390		copy_dir($directory, $dest . '/tests/');
391
392		return false;
393	}
394
395	if(!file_exists($directory) || !is_dir($directory)) {
396		echo "failed... $directory\n";
397		return FALSE;
398	}
399
400	$directory_list = opendir($directory);
401
402	while (FALSE !== ($file = readdir($directory_list))) {
403		$full_path = $directory . '/' . $file;
404		if($file != '.' && $file != '..' && $file != '.svn' && is_dir($full_path)) {
405			if ($file == 'tests' || $file == 'examples') {
406				if (!is_dir($dest . '/' . $full_path)) {
407					mkdir($dest . '/' . $full_path , 0775, true);
408				}
409				copy_dir($full_path, $dest . '/' . $full_path . '/');
410				continue;
411			} else {
412				copy_test_dir($full_path, $dest);
413			}
414		}
415	}
416
417	closedir($directory_list);
418}
419
420function make_phar_dot_phar($dist_dir)
421{
422	if (!extension_loaded('phar')) {
423		return;
424	}
425
426	$path_to_phar = realpath(__DIR__ . '/../../ext/phar');
427
428	echo "Generating pharcommand.phar\n";
429	$phar = new Phar($dist_dir . '/pharcommand.phar', 0, 'pharcommand');
430
431	foreach (new DirectoryIterator($path_to_phar . '/phar') as $file) {
432		if ($file->isDir() || $file == 'phar.php') {
433			continue;
434		}
435
436		echo 'adding ', $file, "\n";
437		$phar[(string) $file] = file_get_contents($path_to_phar.  '/phar/' . $file);
438	}
439
440	$phar->setSignatureAlgorithm(Phar::SHA1);
441	$stub = file($path_to_phar . '/phar/phar.php');
442
443	unset($stub[0]); // remove hashbang
444	$phar->setStub(implode('', $stub));
445
446	echo "Creating phar.phar.bat\n";
447	file_put_contents($dist_dir . '/phar.phar.bat', "%~dp0php.exe %~dp0pharcommand.phar %*\r\n");
448}
449
450if (!is_dir($test_dir)) {
451	mkdir($test_dir);
452}
453
454$dirs = array(
455	'ext',
456	'Sapi',
457	'Zend',
458	'tests'
459);
460foreach ($dirs as $dir) {
461	copy_test_dir($dir, $test_dir);
462}
463copy('run-tests.php', $test_dir . '/run-test.php');
464
465/* change this next line to true to use good-old
466 * hand-assembled go-pear-bundle from the snapshot template */
467$use_pear_template = true;
468
469if (!$use_pear_template) {
470	/* Let's do a PEAR-less pear setup */
471	mkdir("$dist_dir/PEAR");
472	mkdir("$dist_dir/PEAR/go-pear-bundle");
473
474	/* grab the bootstrap script */
475	echo "Downloading go-pear\n";
476	copy("http://pear.php.net/go-pear", "$dist_dir/PEAR/go-pear.php");
477
478	/* import the package list -- sets $packages variable */
479	include "pear/go-pear-list.php";
480
481	/* download the packages into the destination */
482	echo "Fetching packages\n";
483
484	foreach ($packages as $name => $version) {
485		$filename = "$name-$version.tgz";
486		$destfilename = "$dist_dir/PEAR/go-pear-bundle/$filename";
487		if (file_exists($destfilename))
488			continue;
489		$url = "http://pear.php.net/get/$filename";
490		echo "Downloading $name from $url\n";
491		flush();
492		copy($url, $destfilename);
493	}
494
495	echo "Download complete.  Extracting bootstrap files\n";
496
497	/* Now, we want PEAR.php, Getopt.php (Console_Getopt) and Tar.php (Archive_Tar)
498	 * broken out of the tarballs */
499	extract_file_from_tarball('PEAR', 'PEAR.php', "$dist_dir/PEAR/go-pear-bundle");
500	extract_file_from_tarball('Archive_Tar', 'Archive/Tar.php', "$dist_dir/PEAR/go-pear-bundle");
501	extract_file_from_tarball('Console_Getopt', 'Console/Getopt.php', "$dist_dir/PEAR/go-pear-bundle");
502}
503
504/* add extras from the template dir */
505if (file_exists($snapshot_template)) {
506	$items = glob("$snapshot_template/*");
507	print_r($items);
508
509	foreach ($items as $item) {
510		$bi = basename($item);
511		if (is_dir($item)) {
512			if ($bi == 'dlls' || $bi == 'symbols') {
513				continue;
514			} else if ($bi == 'PEAR') {
515				if ($use_pear_template) {
516					/* copy to top level */
517					copy_dir($item, "$dist_dir/$bi");
518				}
519			} else {
520				/* copy that dir into extras */
521				copy_dir($item, "$dist_dir/extras/$bi");
522			}
523		} else {
524			if ($bi == 'go-pear.bat') {
525				/* copy to top level */
526				copy($item, "$dist_dir/$bi");
527			} else {
528				/* copy to extras */
529				copy($item, "$dist_dir/extras/$bi");
530			}
531		}
532	}
533
534	/* copy c++ runtime */
535	$items = glob("$snapshot_template/dlls/*.CRT");
536
537	foreach ($items as $item) {
538		$bi = basename($item);
539		if (is_dir($item)) {
540			copy_dir($item, "$dist_dir/$bi");
541			copy_dir($item, "$dist_dir/ext/$bi");
542		}
543	}
544} else {
545	echo "WARNING: you don't have a snapshot template, your dist will not be complete\n";
546}
547
548make_phar_dot_phar($dist_dir);
549?>
550