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