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