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