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