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