ext_skel.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. #!/usr/bin/env php
  2. <?php
  3. /*
  4. +----------------------------------------------------------------------+
  5. | PHP Version 7 |
  6. +----------------------------------------------------------------------+
  7. | Copyright (c) 1997-2018 The PHP Group |
  8. +----------------------------------------------------------------------+
  9. | This source file is subject to version 3.01 of the PHP license, |
  10. | that is bundled with this package in the file LICENSE, and is |
  11. | available through the world-wide-web at the following url: |
  12. | http://www.php.net/license/3_01.txt |
  13. | If you did not receive a copy of the PHP license and are unable to |
  14. | obtain it through the world-wide-web, please send a note to |
  15. | license@php.net so we can mail you a copy immediately. |
  16. +----------------------------------------------------------------------+
  17. | Authors: Kalle Sommer Nielsen <kalle@php.net> |
  18. +----------------------------------------------------------------------+
  19. */
  20. /* $Id$ */
  21. /* {{{ error
  22. */
  23. function error($message) {
  24. printf('Error: %s%s', $message, PHP_EOL);
  25. exit;
  26. }
  27. /* }}} */
  28. /* {{{ print_help
  29. */
  30. function print_help() {
  31. printf('php ext_skel.php --ext <name> [--experimental] [--author <name>]%s', PHP_EOL);
  32. printf(' [--dir <path>] [--std] [--onlyunix]%s', PHP_EOL);
  33. printf(' [--onlywindows] [--help]%1$s%1$s', PHP_EOL);
  34. printf(' --ext <name> The name of the extension defined as <name>%s', PHP_EOL);
  35. printf(' --experimental Passed if this extension is experimental, this creates%s', PHP_EOL);
  36. printf(' the EXPERIMENTAL file in the root of the extension%s', PHP_EOL);
  37. printf(' --author <name> Your name, this is used if --std is passed and%s', PHP_EOL);
  38. printf(' for the CREDITS file%s', PHP_EOL);
  39. printf(' --dir <path> Path to the directory for where extension should be%s', PHP_EOL);
  40. printf(' created. Defaults to the directory of where this script%s', PHP_EOL);
  41. printf(' lives%s', PHP_EOL);
  42. printf(' --std If passed, the standard header and vim rules footer used%s', PHP_EOL);
  43. printf(' in extensions that is included in the core, will be used%s', PHP_EOL);
  44. printf(' --onlyunix Only generate configure scripts for Unix%s', PHP_EOL);
  45. printf(' --onlywindows Only generate configure scripts for Windows%s', PHP_EOL);
  46. printf(' --help This help%s', PHP_EOL);
  47. exit;
  48. }
  49. /* }}} */
  50. /* {{{ task
  51. */
  52. function task($label, $callback) {
  53. printf('%s... ', $label);
  54. $callback();
  55. printf('done%s', PHP_EOL);
  56. }
  57. /* }}} */
  58. /* {{{ print_success
  59. */
  60. function print_success() {
  61. global $options;
  62. if (PHP_OS_FAMILY != 'Windows') {
  63. $file_prefix = './';
  64. $make_prefix = '';
  65. } else {
  66. $file_prefix = '';
  67. $make_prefix = 'n';
  68. }
  69. printf('%1$sSuccess. The extension is now ready to be compiled into PHP. To do so, use the%s', PHP_EOL);
  70. printf('following steps:%1$s%1$s', PHP_EOL);
  71. printf('cd /path/to/php-src%s', PHP_EOL);
  72. printf('%sbuildconf%s', $file_prefix, PHP_EOL);
  73. printf('%sconfigure --enable-%s%s', $file_prefix, $options['ext'], PHP_EOL);
  74. printf('%smake%2$s%2$s', $make_prefix, PHP_EOL);
  75. printf('Don\'t forget to run tests once the compilation is done:%s', PHP_EOL);
  76. printf('%smake test TESTS=ext/%s/tests%3$s%3$s', $make_prefix, $options['ext'], PHP_EOL);
  77. printf('Thank you for using PHP!%s', PHP_EOL);
  78. }
  79. /* }}} */
  80. /* {{{ process_args
  81. */
  82. function process_args($argv, $argc) {
  83. $options = [
  84. 'unix' => true,
  85. 'windows' => true,
  86. 'ext' => '',
  87. 'dir' => __DIR__ . DIRECTORY_SEPARATOR,
  88. 'skel' => __DIR__ . DIRECTORY_SEPARATOR . 'skeleton' . DIRECTORY_SEPARATOR,
  89. 'author' => false,
  90. 'experimental' => false,
  91. 'std' => false
  92. ];
  93. for($i = 1; $i < $argc; ++$i)
  94. {
  95. $val = $argv[$i];
  96. if($val{0} != '-' || $val{1} != '-')
  97. {
  98. continue;
  99. }
  100. switch($opt = strtolower(substr($val, 2)))
  101. {
  102. case 'help': {
  103. print_help();
  104. }
  105. case 'onlyunix': {
  106. $options['windows'] = false;
  107. }
  108. break;
  109. case 'onlywindows': {
  110. $options['unix'] = false;
  111. }
  112. break;
  113. case 'experimental': {
  114. $options['experimental'] = true;
  115. }
  116. break;
  117. case 'std': {
  118. $options['std'] = true;
  119. }
  120. break;
  121. case 'ext':
  122. case 'dir':
  123. case 'author': {
  124. if (!isset($argv[$i + 1]) || ($argv[$i + 1]{0} == '-' && $argv[$i + 1]{1} == '-')) {
  125. error('Argument "' . $val . '" expects a value, none passed');
  126. } else if ($opt == 'dir' && empty($argv[$i + 1])) {
  127. continue 2;
  128. }
  129. $options[$opt] = ($opt == 'dir' ? realpath($argv[$i + 1]) . DIRECTORY_SEPARATOR : $argv[$i + 1]);
  130. }
  131. break;
  132. default: {
  133. error('Unsupported argument "' . $val . '" passed');
  134. }
  135. }
  136. }
  137. if (empty($options['ext'])) {
  138. error('No extension name passed, use "--ext <name>"');
  139. } else if (!$options['unix'] && !$options['windows']) {
  140. error('Cannot pass both --onlyunix and --onlywindows');
  141. } else if (!is_dir($options['skel'])) {
  142. error('The skeleton directory was not found');
  143. }
  144. $options['ext'] = str_replace(['\\', '/'], '', strtolower($options['ext']));
  145. return $options;
  146. }
  147. /* }}} */
  148. /* {{{ process_source_tags
  149. */
  150. function process_source_tags($file, $short_name) {
  151. global $options;
  152. $source = file_get_contents($file);
  153. if ($source === false) {
  154. error('Unable to open file for reading: ' . $short_name);
  155. }
  156. $source = str_replace('%EXTNAME%', $options['ext'], $source);
  157. $source = str_replace('%EXTNAMECAPS%', strtoupper($options['ext']), $source);
  158. if (strpos($short_name, '.c') !== false || strpos($short_name, '.h') !== false) {
  159. static $header, $footer;
  160. if (!$header) {
  161. if ($options['std']) {
  162. $year = date('Y');
  163. $author_len = strlen($options['author']);
  164. $credits = $options['author'] . ($author_len && $author_len <= 60 ? str_repeat(' ', 60 - $author_len) : '');
  165. $header = <<<"HEADER"
  166. /*
  167. +----------------------------------------------------------------------+
  168. | PHP Version 7 |
  169. +----------------------------------------------------------------------+
  170. | Copyright (c) 1997-$year The PHP Group |
  171. +----------------------------------------------------------------------+
  172. | This source file is subject to version 3.01 of the PHP license, |
  173. | that is bundled with this package in the file LICENSE, and is |
  174. | available through the world-wide-web at the following url: |
  175. | http://www.php.net/license/3_01.txt |
  176. | If you did not receive a copy of the PHP license and are unable to |
  177. | obtain it through the world-wide-web, please send a note to |
  178. | license@php.net so we can mail you a copy immediately. |
  179. +----------------------------------------------------------------------+
  180. | Author: $credits |
  181. +----------------------------------------------------------------------+
  182. */
  183. HEADER;
  184. $footer = <<<'FOOTER'
  185. /*
  186. * Local variables:
  187. * tab-width: 4
  188. * c-basic-offset: 4
  189. * End:
  190. */
  191. FOOTER;
  192. } else {
  193. if ($options['author']) {
  194. $header = sprintf('/* %s extension for PHP (c) %d %s */', $options['ext'], date('Y'), $options['author']);
  195. } else {
  196. $header = sprintf('/* %s extension for PHP */', $options['ext']);
  197. }
  198. $footer = '';
  199. }
  200. }
  201. $source = str_replace(['%HEADER%', '%FOOTER%'], [$header, $footer], $source);
  202. }
  203. if (!file_put_contents($file, $source)) {
  204. error('Unable to save contents to file: ' . $short_name);
  205. }
  206. }
  207. /* }}} */
  208. /* {{{ copy_config_scripts
  209. */
  210. function copy_config_scripts() {
  211. global $options;
  212. $files = [];
  213. if ($options['unix']) {
  214. $files[] = 'config.m4';
  215. }
  216. if ($options['windows']) {
  217. $files[] = 'config.w32';
  218. }
  219. $files[] = '.gitignore';
  220. foreach($files as $config_script) {
  221. $new_config_script = $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $config_script;
  222. if (!copy($options['skel'] . $config_script . '.in', $new_config_script)) {
  223. error('Unable to copy config script: ' . $config_script);
  224. }
  225. process_source_tags($new_config_script, $config_script);
  226. }
  227. }
  228. /* }}} */
  229. /* {{{ copy_sources
  230. */
  231. function copy_sources() {
  232. global $options;
  233. $files = [
  234. 'skeleton.c' => $options['ext'] . '.c',
  235. 'php_skeleton.h' => 'php_' . $options['ext'] . '.h'
  236. ];
  237. foreach ($files as $src_file => $dst_file) {
  238. if (!copy($options['skel'] . $src_file, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file)) {
  239. error('Unable to copy source file: ' . $src_file);
  240. }
  241. process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $dst_file, $dst_file);
  242. }
  243. }
  244. /* }}} */
  245. /* {{{ copy_tests
  246. */
  247. function copy_tests() {
  248. global $options;
  249. $test_files = glob($options['skel'] . 'tests/*', GLOB_MARK);
  250. if (!$test_files) {
  251. return;
  252. }
  253. foreach ($test_files as $test) {
  254. if (is_dir($test)) {
  255. continue;
  256. }
  257. $new_test = str_replace([$options['skel'], '/'], ['', DIRECTORY_SEPARATOR], $test);
  258. if (!copy($test, $options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test)) {
  259. error('Unable to copy file: ' . $new_test);
  260. }
  261. process_source_tags($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . $new_test, $new_test);
  262. }
  263. }
  264. /* }}} */
  265. if (PHP_SAPI != 'cli') {
  266. error('This script is only suited for CLI');
  267. }
  268. if ($argc < 1) {
  269. print_help();
  270. exit;
  271. }
  272. $options = process_args($argv, $argc);
  273. if (!$options['dir'] || !is_dir($options['dir'])) {
  274. error('The selected output directory does not exist');
  275. } else if (is_dir($options['dir'] . $options['ext'])) {
  276. error('There is already a folder named "' . $options['ext'] . '" in the output directory');
  277. } else if (!mkdir($options['dir'] . $options['ext'])) {
  278. error('Unable to create extension directory in the output directory');
  279. } else if (!mkdir($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'tests')) {
  280. error('Unable to create the tests directory');
  281. }
  282. if ($options['experimental']) {
  283. print('Creating EXPERIMENTAL... ');
  284. if (file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'EXPERIMENTAL', '') === false) {
  285. error('Unable to create the EXPERIMENTAL file');
  286. }
  287. printf('done%s', PHP_EOL);
  288. }
  289. if (!empty($options['author'])) {
  290. print('Creating CREDITS... ');
  291. if (!file_put_contents($options['dir'] . $options['ext'] . DIRECTORY_SEPARATOR . 'CREDITS', $options['ext'] . PHP_EOL . $options['author'])) {
  292. error('Unable to create the CREDITS file');
  293. }
  294. printf('done%s', PHP_EOL);
  295. }
  296. date_default_timezone_set('UTC');
  297. task('Copying config scripts', 'copy_config_scripts');
  298. task('Copying sources', 'copy_sources');
  299. task('Copying tests', 'copy_tests');
  300. print_success();