Fix ArrayIterator interface implementation; remove usage of arraysize(), structsize...
[plcapi.git] / pakefile.php
1 <?php
2 /**
3  * Makefile for phpxmlrpc library.
4  * To be used with the Pake tool: https://github.com/indeyets/pake/wiki
5  *
6  * @copyright (c) 2015 G. Giunta
7  *
8  * @todo !important allow user to specify location of docbook xslt instead of the one installed via composer
9  */
10
11 namespace PhpXmlRpc {
12
13 class Builder
14 {
15     protected static $buildDir = 'build';
16     protected static $libVersion;
17     protected static $tools = array(
18         'asciidoctor' => 'asciidoctor',
19         'fop' => 'fop',
20         'php' => 'php',
21         'zip' => 'zip',
22     );
23     protected static $options = array(
24         'repo' => 'https://github.com/gggeek/phpxmlrpc',
25         'branch' => 'php53'
26     );
27
28     public static function libVersion()
29     {
30         if (self::$libVersion == null)
31             throw new \Exception('Missing library version argument');
32         return self::$libVersion;
33     }
34
35     public static function buildDir()
36     {
37         return self::$buildDir;
38     }
39
40     public static function workspaceDir()
41     {
42         return self::buildDir().'/workspace';
43     }
44
45     /// most likely things will break if this one is moved outside of BuildDir
46     public static function distDir()
47     {
48         return self::buildDir().'/xmlrpc-'.self::libVersion();
49     }
50
51     /// these will be generated in BuildDir
52     public static function distFiles()
53     {
54         return array(
55             'xmlrpc-'.self::libVersion().'.tar.gz',
56             'xmlrpc-'.self::libVersion().'.zip',
57         );
58     }
59
60     public static function getOpts($args=array(), $cliOpts=array())
61     {
62         if (count($args) > 0)
63         //    throw new \Exception('Missing library version argument');
64             self::$libVersion = $args[0];
65
66         foreach (self::$tools as $name => $binary) {
67             if (isset($cliOpts[$name])) {
68                 self::$tools[$name] = $cliOpts[$name];
69             }
70         }
71
72         foreach (self::$options as $name => $value) {
73             if (isset($cliOpts[$name])) {
74                 self::$options[$name] = $cliOpts[$name];
75             }
76         }
77
78         //pake_echo('---'.self::$libVersion.'---');
79     }
80
81     public static function tool($name)
82     {
83         return self::$tools[$name];
84     }
85
86     public static function option($name)
87     {
88         return self::$options[$name];
89     }
90
91     /**
92      * @param string $inFile
93      * @param string $xssFile
94      * @param string $outFileOrDir
95      * @throws \Exception
96      */
97     public static function applyXslt($inFile, $xssFile, $outFileOrDir)
98     {
99
100         if (!file_exists($inFile)) {
101             throw new \Exception("File $inFile cannot be found");
102         }
103         if (!file_exists($xssFile)) {
104             throw new \Exception("File $xssFile cannot be found");
105         }
106
107         // Load the XML source
108         $xml = new \DOMDocument();
109         $xml->load($inFile);
110         $xsl = new \DOMDocument();
111         $xsl->load($xssFile);
112
113         // Configure the transformer
114         $processor = new \XSLTProcessor();
115         if (version_compare(PHP_VERSION, '5.4', "<")) {
116             if (defined('XSL_SECPREF_WRITE_FILE')) {
117                 ini_set("xsl.security_prefs", XSL_SECPREF_CREATE_DIRECTORY | XSL_SECPREF_WRITE_FILE);
118             }
119         } else {
120             // the php online docs only mention setSecurityPrefs, but somehow some installs have setSecurityPreferences...
121             if (method_exists('XSLTProcessor', 'setSecurityPrefs')) {
122                 $processor->setSecurityPrefs(XSL_SECPREF_CREATE_DIRECTORY | XSL_SECPREF_WRITE_FILE);
123             } else {
124                 $processor->setSecurityPreferences(XSL_SECPREF_CREATE_DIRECTORY | XSL_SECPREF_WRITE_FILE);
125             }
126         }
127         $processor->importStyleSheet($xsl); // attach the xsl rules
128
129         if (is_dir($outFileOrDir)) {
130             if (!$processor->setParameter('', 'base.dir', realpath($outFileOrDir))) {
131                 echo "setting param base.dir KO\n";
132             }
133         }
134
135         $out = $processor->transformToXML($xml);
136
137         if (!is_dir($outFileOrDir)) {
138             file_put_contents($outFileOrDir, $out);
139         }
140     }
141
142     public static function highlightPhpInHtml($content)
143     {
144         $startTag = '<pre class="programlisting">';
145         $endTag = '</pre>';
146
147         //$content = file_get_contents($inFile);
148         $last = 0;
149         $out = '';
150         while (($start = strpos($content, $startTag, $last)) !== false) {
151             $end = strpos($content, $endTag, $start);
152             $code = substr($content, $start + strlen($startTag), $end - $start - strlen($startTag));
153             if ($code[strlen($code) - 1] == "\n") {
154                 $code = substr($code, 0, -1);
155             }
156
157             $code = str_replace(array('&gt;', '&lt;'), array('>', '<'), $code);
158             $code = highlight_string('<?php ' . $code, true);
159             $code = str_replace('<span style="color: #0000BB">&lt;?php&nbsp;<br />', '<span style="color: #0000BB">', $code);
160
161             $out = $out . substr($content, $last, $start + strlen($startTag) - $last) . $code . $endTag;
162             $last = $end + strlen($endTag);
163         }
164         $out .= substr($content, $last, strlen($content));
165
166         return $out;
167     }
168 }
169
170 }
171
172 namespace {
173
174 use PhpXmlRpc\Builder;
175
176 function run_default($task=null, $args=array(), $cliOpts=array())
177 {
178     echo "Syntax: pake {\$pake-options} \$task \$lib-version [\$git-tag] {\$task-options}\n";
179     echo "\n";
180     echo "  Run 'pake help' to list all pake options\n";
181     echo "  Run 'pake -T' to list available tasks\n";
182     echo "  Run 'pake -P' to list all available tasks (including hidden ones) and their dependencies\n";
183     echo "\n";
184     echo "  Task options:\n";
185     echo "      --repo=REPO      URL of the source repository to clone. Defaults to the github repo.\n";
186     echo "      --branch=BRANCH  The git branch to build from.\n";
187     echo "      --asciidoctor=ASCIIDOCTOR Location of the asciidoctor command-line tool\n";
188     echo "      --fop=FOP        Location of the apache fop command-line tool\n";
189     echo "      --php=PHP        Location of the php command-line interpreter\n";
190     echo "      --zip=ZIP        Location of the zip tool\n";
191 }
192
193 function run_getopts($task=null, $args=array(), $cliOpts=array())
194 {
195     Builder::getOpts($args, $cliOpts);
196 }
197
198 /**
199  * Downloads source code in the build workspace directory, optionally checking out the given branch/tag
200  */
201 function run_init($task=null, $args=array(), $cliOpts=array())
202 {
203     // download the current version into the workspace
204     $targetDir = Builder::workspaceDir();
205
206     // check if workspace exists and is not already set to the correct repo
207     if (is_dir($targetDir) && pakeGit::isRepository($targetDir)) {
208         $repo = new pakeGit($targetDir);
209         $remotes = $repo->remotes();
210         if (trim($remotes['origin']['fetch']) != Builder::option('repo')) {
211             throw new Exception("Directory '$targetDir' exists and is not linked to correct git repo");
212         }
213
214         /// @todo should we not just fetch instead?
215         $repo->pull();
216     } else {
217         pake_mkdirs(dirname($targetDir));
218         $repo = pakeGit::clone_repository(Builder::option('repo'), Builder::workspaceDir());
219     }
220
221     $repo->checkout(Builder::option('branch'));
222 }
223
224 /**
225  * Runs all the build steps.
226  *
227  * (does nothing by itself, as all the steps are managed via task dependencies)
228  */
229 function run_build($task=null, $args=array(), $cliOpts=array())
230 {
231 }
232
233 function run_clean_doc()
234 {
235     pake_remove_dir(Builder::workspaceDir().'/doc/api');
236     $finder = pakeFinder::type('file')->name('*.html');
237     pake_remove($finder, Builder::workspaceDir().'/doc/manual');
238     $finder = pakeFinder::type('file')->name('*.xml');
239     pake_remove($finder, Builder::workspaceDir().'/doc/manual');
240 }
241
242 /**
243  * Generates documentation in all formats
244  */
245 function run_doc($task=null, $args=array(), $cliOpts=array())
246 {
247     $docDir = Builder::workspaceDir().'/doc';
248
249     // API docs
250
251     // from phpdoc comments using phpdocumentor
252     $cmd = Builder::tool('php');
253     pake_sh("$cmd vendor/phpdocumentor/phpdocumentor/bin/phpdoc run -d ".Builder::workspaceDir().'/src'." -t ".Builder::workspaceDir().'/doc/api --title PHP-XMLRPC');
254
255     // User Manual
256
257     // html (single file) from asciidoc
258     $cmd = Builder::tool('asciidoctor');
259     pake_sh("$cmd -d book $docDir/manual/phpxmlrpc_manual.adoc");
260
261     // then docbook from asciidoc
262     /// @todo create phpxmlrpc_manual.xml with the good version number
263     /// @todo create phpxmlrpc_manual.xml with the date set to the one of last commit (or today?)
264     pake_sh("$cmd -d book  -b docbook $docDir/manual/phpxmlrpc_manual.adoc");
265
266     # Other tools for docbook...
267     #
268     # jade cmd yet to be rebuilt, starting from xml file and putting output in ./out dir, e.g.
269     #   jade -t xml -d custom.dsl xmlrpc_php.xml
270     #
271     # convertdoc command for xmlmind xxe editor
272     #   convertdoc docb.toHTML xmlrpc_php.xml -u out
273     #
274     # saxon + xerces xml parser + saxon extensions + xslthl: adds a little syntax highligting
275     # (bold and italics only, no color) for php source examples...
276     #   java \
277     #   -classpath c:\programmi\saxon\saxon.jar\;c:\programmi\saxon\xslthl.jar\;c:\programmi\xerces\xercesImpl.jar\;C:\htdocs\xmlrpc_cvs\docbook-xsl\extensions\saxon65.jar \
278     #   -Djavax.xml.parsers.DocumentBuilderFactory=org.apache.xerces.jaxp.DocumentBuilderFactoryImpl \
279     #   -Djavax.xml.parsers.SAXParserFactory=org.apache.xerces.jaxp.SAXParserFactoryImpl \
280     #   -Dxslthl.config=file:///c:/htdocs/xmlrpc_cvs/docbook-xsl/highlighting/xslthl-config.xml \
281     #   com.icl.saxon.StyleSheet -o xmlrpc_php.fo.xml xmlrpc_php.xml custom.fo.xsl use.extensions=1
282
283     // HTML (multiple files) from docbook - discontinued, as we use the nicer-looking html gotten from asciidoc
284     /*Builder::applyXslt($docDir.'/manual/phpxmlrpc_manual.xml', $docDir.'/build/custom.xsl', $docDir.'/manual');
285     // post process html files to highlight php code samples
286     foreach(pakeFinder::type('file')->name('*.html')->in($docDir.'/manual') as $file)
287     {
288         file_put_contents($file, Builder::highlightPhpInHtml(file_get_contents($file)));
289     }*/
290
291     // PDF file from docbook
292
293     // convert to fo and then to pdf using apache fop
294     Builder::applyXslt($docDir.'/manual/phpxmlrpc_manual.xml', $docDir.'/build/custom.fo.xsl', $docDir.'/manual/phpxmlrpc_manual.fo.xml');
295     $cmd = Builder::tool('fop');
296     pake_sh("$cmd $docDir/manual/phpxmlrpc_manual.fo.xml $docDir/manual/phpxmlrpc_manual.pdf");
297
298     // cleanup
299     unlink($docDir.'/manual/phpxmlrpc_manual.xml');
300     unlink($docDir.'/manual/phpxmlrpc_manual.fo.xml');
301 }
302
303 function run_clean_dist()
304 {
305     pake_remove_dir(Builder::distDir());
306     $finder = pakeFinder::type('file')->name(Builder::distFiles());
307     pake_remove($finder, Builder::buildDir());
308 }
309
310 /**
311  * Creates the tarballs for a release
312  */
313 function run_dist($task=null, $args=array(), $cliOpts=array())
314 {
315     // copy workspace dir into dist dir, without git
316     pake_mkdirs(Builder::distDir());
317     $finder = pakeFinder::type('any')->ignore_version_control();
318     pake_mirror($finder, realpath(Builder::workspaceDir()), realpath(Builder::distDir()));
319
320     // remove unwanted files from dist dir
321
322     // also: do we still need to run dos2unix?
323
324     // create tarballs
325     $cwd = getcwd();
326     chdir(dirname(Builder::distDir()));
327     foreach(Builder::distFiles() as $distFile) {
328         // php can not really create good zip files via phar: they are not compressed!
329         if (substr($distFile, -4) == '.zip') {
330             $cmd = Builder::tool('zip');
331             $extra = '-9 -r';
332             pake_sh("$cmd $distFile $extra ".basename(Builder::distDir()));
333         }
334         else {
335             $finder = pakeFinder::type('any')->pattern(basename(Builder::distDir()).'/**');
336             // see https://bugs.php.net/bug.php?id=58852
337             $pharFile = str_replace(Builder::libVersion(), '_LIBVERSION_', $distFile);
338             pakeArchive::createArchive($finder, '.', $pharFile);
339             rename($pharFile, $distFile);
340         }
341     }
342     chdir($cwd);
343 }
344
345 function run_clean_workspace($task=null, $args=array(), $cliOpts=array())
346 {
347     pake_remove_dir(Builder::workspaceDir());
348 }
349
350 /**
351  * Cleans up the whole build directory
352  * @todo 'make clean' usually just removes the results of the build, distclean removes all but sources
353  */
354 function run_clean($task=null, $args=array(), $cliOpts=array())
355 {
356     pake_remove_dir(Builder::buildDir());
357 }
358
359 // helper task: display help text
360 pake_task( 'default' );
361 // internal task: parse cli options
362 pake_task('getopts');
363 pake_task('init', 'getopts');
364 pake_task('doc', 'getopts', 'init', 'clean-doc');
365 pake_task('build', 'getopts', 'init', 'doc');
366 pake_task('dist', 'getopts', 'init', 'build', 'clean-dist');
367 pake_task('clean-doc', 'getopts');
368 pake_task('clean-dist', 'getopts');
369 pake_task('clean-workspace', 'getopts');
370 pake_task('clean', 'getopts');
371
372 }