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