1<?php
2/**
3 * Extension generator class
4 *
5 * PHP versions 5
6 *
7 * LICENSE: This source file is subject to version 3.0 of the PHP license
8 * that is available through the world-wide-web at the following URI:
9 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10 * the PHP License and are unable to obtain it through the web, please
11 * send a note to license@php.net so we can mail you a copy immediately.
12 *
13 * @category   Tools and Utilities
14 * @package    CodeGen
15 * @author     Hartmut Holzgraefe <hartmut@php.net>
16 * @copyright  2005-2008 Hartmut Holzgraefe
17 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
18 * @version    CVS: $Id: Extension.php,v 1.31 2007/04/16 09:17:49 hholzgra Exp $
19 * @link       http://pear.php.net/package/CodeGen
20 */
21
22/**
23 * includes
24 *
25 */
26require_once "CodeGen/Maintainer.php";
27require_once "CodeGen/License.php";
28require_once "CodeGen/Release.php";
29require_once "CodeGen/Tools/Platform.php";
30require_once "CodeGen/Tools/FileReplacer.php";
31require_once "CodeGen/Tools/Outbuf.php";
32require_once "CodeGen/Tools/Code.php";
33require_once "CodeGen/Dependency/Lib.php";
34require_once "CodeGen/Dependency/Header.php";
35
36/**
37 * Extension generator class
38 *
39 * @category   Tools and Utilities
40 * @package    CodeGen
41 * @author     Hartmut Holzgraefe <hartmut@php.net>
42 * @copyright  2005-2008 Hartmut Holzgraefe
43 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
44 * @version    Release: @package_version@
45 * @link       http://pear.php.net/package/CodeGen
46 */
47abstract class CodeGen_Extension
48{
49    /**
50    * Current version number
51    *
52    * @return string
53    */
54    abstract public function version();
55
56    /**
57    * Copyright message
58    *
59    * @return string
60    */
61    abstract public function copyright();
62
63    /**
64     * The extensions basename (C naming rules apply)
65     *
66     * @var string
67     */
68    protected $name = "unknown";
69
70
71    /**
72     * The extensions descriptive name
73     *
74     * @var string
75     */
76    protected $summary = "The unknown extension";
77
78    /**
79     * extension description
80     *
81     * @var    string
82     * @access private
83     */
84    protected $description = "";
85
86    /**
87     * The license for this extension
88     *
89     * @var object
90     */
91    protected $license  = null;
92
93    /**
94     * The release info for this extension
95     *
96     * @var object
97     */
98    protected $release  = null;
99
100    /**
101     * The implementation language
102     *
103     * Currently we support "c" and "cpp"
104     *
105     * @var string
106     */
107    protected $language  = "c";
108
109    /**
110     * The target platform for this extension
111     *
112     * Possible values are "unix", "win" and "all"
113     *
114     * @var string
115     */
116    protected $platform = null;
117
118
119    /**
120     * The authors contributing to this extension
121     *
122     * @var array
123     */
124    protected $authors = array();
125
126
127    /**
128     * Name prefix for functions etc.
129     *
130     * @var string
131     */
132    protected $prefix = "";
133
134
135    /**
136     * Release changelog
137     *
138     * @access private
139     * @var     string
140     */
141    protected $changelog = "";
142
143
144    /**
145     * Basedir for all created files
146     *
147     * @access protected
148     * @var    string
149     */
150    public $dirpath = ".";
151
152
153    /**
154     * External libraries
155     *
156     * @var    array
157     * @access private
158     */
159    protected $libs = array();
160
161    /**
162     * External header files
163     *
164     * @var    array
165     * @access private
166     */
167    protected $headers = array();
168
169    /**
170     * Code snippets
171     *
172     * @var array
173     */
174    protected $code = array();
175
176    /**
177     * The package files created by this extension
178     *
179     * @var array
180     */
181    protected $packageFiles = array();
182
183    /**
184     * Version requested by input if any
185     *
186     * @var string
187     */
188    protected $version = "";
189
190
191    /**
192     * Up front #defines
193     *
194     * @var array
195     */
196    protected $defines = array();
197
198
199    /**
200     * Makefile fragments
201     *
202     * @var    array
203     * @access private
204     */
205    protected $makefragments = array();
206
207
208    /**
209     * config.m4 fragments
210     *
211     * @var    array
212     * @access private
213     */
214    protected $configfragments = array("top"=>array(), "bottom"=>array());
215
216
217    /**
218     * acinclude fragments
219     *
220     * @var    array
221     * @access private
222     */
223    protected $acfragments = array("top"=>array(), "bottom"=>array());
224
225
226    /**
227     * CodeGen_Tool_Code instance for internal use
228     *
229     * @var object
230     */
231    public $codegen;
232
233    // {{{ constructor
234
235    /**
236     * The constructor
237     *
238     * @access public
239     */
240    function __construct()
241    {
242        setlocale(LC_ALL, "C"); // ASCII only
243
244        if ($this->release == null) {
245            $this->release = new CodeGen_Release;
246        }
247        if ($this->platform == null) {
248            $this->platform = new CodeGen_Tools_Platform("all");
249        }
250
251        $this->codegen = new CodeGen_Tools_Code;
252    }
253
254    // }}}
255    /**
256     * Set method for changelog
257     *
258     * @access public
259     * @param  string changelog
260     * @return bool   true on success
261     */
262    function setChangelog($changelog)
263    {
264        $this->changelog = $changelog;
265
266        return true;
267    }
268
269    /**
270     * changelog getter
271     *
272     * @access public
273     * @return string
274     */
275    function getChangelog()
276    {
277        return $this->changelog;
278    }
279
280    /**
281     * Set extension base name
282     *
283     * @access public
284     * @param  string  name
285     */
286    function setName($name)
287    {
288        if (!preg_match('|^[a-z_]\w*$|i', $name)) {
289            return PEAR::raiseError("'$name' is not a valid extension name");
290        }
291
292        $this->name = $name;
293        return true;
294    }
295
296    /**
297     * Get extension base name
298     *
299     * @return string
300     */
301    function getName()
302    {
303        return $this->name;
304    }
305
306    /**
307     * Set extension summary text
308     *
309     * @access public
310     * @param  string  short summary
311     */
312    function setSummary($text)
313    {
314        $this->summary = $text;
315        return true;
316    }
317
318    /**
319     * Set extension documentation text
320     *
321     * @access public
322     * @param  string  long description
323     */
324    function setDescription($text)
325    {
326        $this->description = $text;
327        return true;
328    }
329
330    /**
331     * Set the programming language to produce code for
332     *
333     * @access public
334     * @param  string  programming language name
335     */
336    function setLanguage($lang)
337    {
338        switch (strtolower($lang)) {
339        case "c":
340            $this->language = "c";
341            $this->codegen->setLanguage("c");
342            return true;
343        case "cpp":
344        case "cxx":
345        case "c++":
346            $this->language = "cpp";
347            $this->codegen->setLanguage("cpp");
348            return true;
349        default:
350            break;
351        }
352
353        return PEAR::raiseError("'$lang' is not a supported implementation language");
354    }
355
356    /**
357     * Get programming language
358     *
359     * @return string
360     */
361    function getLanguage()
362    {
363        return $this->language;
364    }
365
366    /**
367     * Set target platform for generated code
368     *
369     * @access public
370     * @param  string  platform name
371     */
372    function setPlatform($type)
373    {
374        $this->platform = new CodeGen_Tools_Platform($type);
375        if (PEAR::isError($this->platform)) {
376            return $this->platform;
377        }
378
379        return true;
380    }
381
382    /**
383     * Add an author or maintainer to the extension
384     *
385     * @access public
386     * @param  object   a maintainer object
387     */
388    function addAuthor($author)
389    {
390        if (!is_a($author, "CodeGen_Maintainer")) {
391            return PEAR::raiseError("argument is not CodeGen_Maintainer");
392        }
393
394        $this->authors[$author->getUser()] = $author;
395
396        return true;
397    }
398
399
400    /**
401     * Get Extension Maintainers
402     *
403     * @access public
404     * @param  array   Array of maintainer objects
405     */
406    function getAuthors()
407    {
408        return $this->authors;
409    }
410
411
412    /**
413     * Set release info
414     *
415     * @access public
416     * @var    object
417     */
418    function setRelease($release)
419    {
420        $this->release = $release;
421
422        return true;
423    }
424
425    /**
426     * Get release info
427     *
428     * @access public
429     * @return object
430     */
431    function getRelease()
432    {
433        return $this->release;
434    }
435
436
437
438    /**
439     * Set license
440     *
441     * @access public
442     * @param  object
443     */
444    function setLicense($license)
445    {
446        $this->license = $license;
447
448        return true;
449    }
450
451    /**
452     * Get license
453     *
454     * @access public
455     * @return object
456     */
457    function getLicense()
458    {
459        return $this->license;
460    }
461
462
463
464
465    /**
466     * Set extension name prefix (for functions etc.)
467     *
468     * @access public
469     * @param  string  name
470     */
471    function setPrefix($prefix)
472    {
473        if (! CodeGen_Element::isName($prefix)) {
474            return PEAR::raiseError("'$name' is not a valid name prefix");
475        }
476
477        $this->prefix = $prefix;
478        return true;
479    }
480
481    /**
482     * Get extension name prefix
483     *
484     * @return string
485     */
486    function getPrefix()
487    {
488        return $this->prefix;
489    }
490
491    /**
492     * Add verbatim code snippet to extension
493     *
494     * @access public
495     * @param  string  which file to put the code to
496     * @param  string  where in the file the code should be put
497     * @param  string  the actual code
498     */
499    function addCode($role, $position, $code)
500    {
501        if (!in_array($role, array("header", "code"))) {
502            return PEAR::raiseError("'$role' is not a valid custom code role");
503        }
504        if (!in_array($position, array("top", "bottom"))) {
505            return PEAR::raiseError("'$position' is not a valid custom code position");
506        }
507        $this->code[$role][$position][] = $code;
508    }
509
510
511    /**
512     * Add toplevel library dependancy
513     *
514     * @var  string  library basename
515     */
516    function addLib(CodeGen_Dependency_Lib $lib)
517    {
518        $name = $lib->getName();
519
520        if (isset($this->libs[$name])) {
521            return PEAR::raiseError("library '{$name}' added twice");
522        }
523
524        $this->libs[$name] = $lib;
525
526        return true;
527    }
528
529    /**
530     * Add toplevel header file dependancy
531     *
532     * @var  string  header filename
533	 * @var  bool    check for duplicates?
534     */
535    function addHeader(CodeGen_Dependency_Header $header, $catchDuplicates = true)
536    {
537        $name = $header->getName();
538
539        if (isset($this->headers[$name])) {
540			if ($catchDuplicates) {
541				return PEAR::raiseError("header '{$name}' added twice");
542			} else {
543				return true;
544			}
545		}
546
547        $this->headers[$name] = $header;
548
549        // TODO $this->addConfigFragment($header->configm4());
550
551        return true;
552    }
553
554    /**
555    * Describe next steps after successfull extension creation
556    *
557    * @access private
558    */
559    function successMsg()
560    {
561        $relpath = str_replace(getcwd(), '.', $this->dirpath);
562
563        $msg = "\nYour extension has been created in directory $relpath.\n";
564        $msg.= "See $relpath/README and/or $relpath/INSTALL for further instructions.\n";
565
566        return $msg;
567    }
568
569    /**
570     * Get requested version
571     *
572     * @return  string
573     */
574    function getVersion()
575    {
576        return $this->version;
577    }
578
579    /**
580     * Set requested version
581     *
582     * @param  string
583     */
584    function setVersion($version)
585    {
586        if (!preg_match('/^\d+\.\d+\.\d+(dev|alpha|beta|gamma|rc|pl)?\d*$/', $version)) {
587            return PEAR::raiseError("'$version' is not a valid version number");
588        }
589
590        if (version_compare($version, $this->version(), ">")) {
591            return PEAR::raiseError("This is ".get_class($this)." ".$this->version().", input file requires at least version $version ");
592        }
593
594        $this->version = $version;
595        return true;
596    }
597
598    /**
599     * Check requested version
600     *
601     * @param  string version
602     * @return bool
603     */
604    function haveVersion($version)
605    {
606        return version_compare(empty($this->version) ? $this->version() : $this->version, $version) >= 0;
607    }
608
609    /**
610     * Add a package file by type and path
611     *
612     * @access  public
613     * @param   string  type
614     * @param   string  path
615     * @param   string  optional target dir
616     * @returns bool    success state
617     */
618    function addPackageFile($type, $path, $dir = "")
619    {
620        $targetpath = basename($path);
621        if ($dir) {
622            if ($dir{0} == "/") {
623                return PEAR::raiseError("only relative pathes are allowed as target dir");
624            }
625            $targetpath = $dir."/".$targetpath;
626        }
627
628        if (isset($this->packageFiles[$type][$targetpath])) {
629            return PEAR::raiseError("duplicate distribution file name '$targetpath'");
630        }
631
632        $this->packageFiles[$type][$targetpath] = $path;
633        return true;
634    }
635
636    /**
637     * Add a source file to be copied to the extension dir
638     *
639     * @access public
640     * @param  string path
641     * @param  string optional target dir
642     */
643    function addSourceFile($name, $dir="")
644    {
645        // TODO catch errors returned from addPackageFile
646
647        $filename = realpath($name);
648
649        if (!is_file($filename)) {
650            return PEAR::raiseError("'$name' is not a valid file");
651        }
652
653        if (!is_readable($filename)) {
654            return PEAR::raiseError("'$name' is not readable");
655        }
656
657        $pathinfo = pathinfo($filename);
658        $ext      = $pathinfo["extension"];
659
660        switch ($ext) {
661        case 'c':
662            $this->addConfigFragment("AC_PROG_CC");
663            $this->addPackageFile('code', $filename);
664            break;
665        case 'cpp':
666        case 'cxx':
667        case 'c++':
668            $this->addConfigFragment("AC_PROG_CXX");
669            $this->addConfigFragment("AC_LANG([C++])");
670            $this->addPackageFile('code', $filename);
671            break;
672        case 'l':
673        case 'flex':
674            $this->addConfigFragment("AM_PROG_LEX");
675            $this->addPackageFile('code', $filename);
676            break;
677        case 'y':
678        case 'bison':
679            $this->addConfigFragment("AM_PROG_YACC");
680            $this->addPackageFile('code', $filename);
681            break;
682        default:
683            break;
684        }
685
686        return $this->addPackageFile('copy', $filename, $dir);
687    }
688
689    /**
690     * Add up front define
691     *
692     * @access public
693     * @param  string  #define name
694     * @param  string  value
695     * @param  string  comment
696     */
697    function addDefine($name, $value, $comment) {
698         if (! CodeGen_Element::isName($name)) {
699            return PEAR::raiseError("'$name' is not a valid define name");
700         }
701
702         // TODO check for invalid comment characters
703
704         $this->defines[] = array("name" => $name, "value" => $value, "comment" => $comment);
705
706         return true;
707    }
708
709    /**
710     * Add makefile fragment
711     *
712     * @access public
713     * @param  string
714     */
715    function addMakeFragment($text)
716    {
717        $this->makefragments[] = $text;
718        return true;
719    }
720
721
722    /**
723     * Add config.m4 fragment
724     *
725     * @access public
726     * @param  string
727     */
728    function addConfigFragment($text, $position="top")
729    {
730        if (!in_array($position, array("top", "bottom"))) {
731            return PEAR::raiseError("'$position' is not a valid config snippet position");
732        }
733        $this->configfragments[$position][] = $text;
734        return true;
735    }
736
737
738    /**
739     * Add acinclude.m4 fragment
740     *
741     * @access public
742     * @param  string
743     */
744    function addAcIncludeFragment($text, $position="top")
745    {
746        if (!in_array($position, array("top", "bottom"))) {
747            return PEAR::raiseError("'$position' is not a valid config snippet position");
748        }
749        $this->acfragments[$position][] = $text;
750        return true;
751    }
752
753
754    /**
755     * Write .cvsignore entries
756     *
757     * @access public
758     * @param  string  directory to write to
759     */
760    function writeDotCvsignore()
761    {
762        $file = new CodeGen_Tools_Outbuf($this->dirpath."/.cvsignore");
763
764        // unix specific entries
765        if ($this->platform->test("unix")) {
766            echo
767"*.lo
768*.la
769.deps
770.libs
771Makefile
772Makefile.fragments
773Makefile.global
774Makefile.objects
775acinclude.m4
776aclocal.m4
777autom4te.cache
778build
779config.cache
780config.guess
781config.h
782config.h.in
783config.log
784config.nice
785config.status
786config.sub
787configure
788configure.in
789conftest
790conftest.c
791include
792install-sh
793libtool
794ltmain.sh
795missing
796mkinstalldirs
797modules
798scan_makefile_in.awk
799";
800        }
801
802        // windows specific entries
803        if ($this->platform->test("windows")) {
804            echo
805"*.dsw
806*.plg
807*.opt
808*.ncb
809Release
810Release_inline
811Debug
812Release_TS
813Release_TSDbg
814Release_TS_inline
815Debug_TS
816";
817        }
818
819        // "pear package" creates .tgz
820        echo "{$this->name}*.tgz\n";
821
822        return $file->write();
823    }
824
825    /**
826     * Generate Editor settings block for C source files
827     *
828     * @access public
829     * @return string Editor settings comment block
830     */
831    function cCodeEditorSettings()
832    {
833            return '
834/*
835 * Local variables:
836 * tab-width: 4
837 * c-basic-offset: 4
838 * End:
839 * vim600: noet sw=4 ts=4 fdm=marker
840 * vim<600: noet sw=4 ts=4
841 */
842';
843    }
844
845    /**
846     * Generate Editor settings block for documentation files
847     *
848     * @access public
849     * @param  int    Directory nesting depth of target file (default: 3)
850     * @return string Editor settings comment block
851     */
852    static function docEditorSettings($level=3)
853    {
854        return "";
855    }
856
857    /**
858     * Run extra commands on generated source
859     *
860     * @param  string  token identifying what to run
861     */
862    function runExtra($what)
863    {
864        return PEAR::raiseError("don't know how to run '$what'");
865    }
866}
867
868
869
870?>
871