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