mkdist.php 15 KB

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