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