name('*') ->ignore_version_control(); pake_remove( $rule, $buildDir ); } } pake_desc( 'Checks out the required external bits required to re-build the website.' ); pake_task( 'checkout_wcv' ); /** * run_checkout_wcv * * @param pakeTask $task * @param array $args * @access public * @return void */ function run_checkout_wcv( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); foreach( $options['wcv.paths'] as $wcvPath ) { if( !is_dir( $wcvPath ) ) { /* --ignore-externals is not supported */ pakeSubversion::checkout( $options['wcv.svn.repository'] . "/${wcvPath}", $wcvPath ); } else { /* --ignore-externals is not supported */ pakeSubversion::update( $wcvPath ); } } /** * since --ignore-externals is not supported in pakeSubversion * that means classes/ezc/* have been updated as well * wo we do not need to update them once again */ } pake_desc( 'Creates the API documentation.' ); pake_task( 'phpdoc' ); /** * Creates the API documentation. * * @param pakeTask $task * @param array $args * @access public * @return void */ function run_phpdoc( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); pake_echo_comment( 'Building PHPDoc for ' . $options['build.name'] ); $command = $options['php.path'] . ' -d "memory_limit=2500M" -d "xdebug.default_enable=0" ' . $options['phpdoc.path'] . ' ' . '--directory ' . escapeshellarg( $options['zeta.base'] ) . ' ' . '--templatebase "phpdoc/" ' . '--target ' . escapeshellarg( $options['build.dir'] . '/phpdoc/' . $options['build.name'] . '/phpdoc' ) . ' ' . '--ignore "autoload/,*autoload.php,tests/,docs/,design/" ' . '--hidden false ' . '--undocumentedelements off ' . '--title "Apache Zeta Components Manual" ' . '--parseprivate off ' . '--defaultpackagename "NoPackageName" ' . '--defaultcategoryname "NoCategoryName" ' . '--output "HTML:Arbit:arbit" ' . '--sourcecode off ' . '--javadocdesc off ' . '--quiet ' . '--pear off'; pake_sh( $command, true ); $PHPDocRoot = $options['build.dir'] . '/phpdoc/' . $options['build.name']; move_html_files( "${PHPDocRoot}/phpdoc" ); // ezcArchiveGnuTar can not write files // and other Tar algorithsm doe not support filenames // longer than 100 chars. shorten_long_filenames( "${PHPDocRoot}/phpdoc" ); $tarball = "${PHPDocRoot}.tar.bz2"; create_bz2_archive( $tarball, $PHPDocRoot ); $targetDir = 'content/documentation/' . $options['build.name']; pake_mkdirs( $targetDir, 0755 ); pake_echo_comment( 'Copying tarball' ); pake_copy( $tarball, $targetDir . '/phpdoc.tar.bz2' ); pake_echo_comment( 'Removing tarball' ); pake_remove_dir( $options['build.dir'] . '/phpdoc/' ); } pake_desc( 'Copies tutorials for a given release to the website directory.' ); pake_task( 'tutorials' ); /** * Copies tutorials to be compatiable with the website * * @param pakeTask $task * @param array $args * @access public * @return void */ function run_tutorials( $task, $args ) { $tutorialRootDir = 'content/documentation'; $options = pakeYaml::loadFile( CONFIG_FILE ); $rule = pakeFinder::type('dir')->name('/docs/') ->ignore_version_control() ->relative() ->maxdepth( 1 ) ->discard( '/tests/' ); $docDirs = $rule->in( $options['zeta.base'] ); $token = $options['zeta.base']; // Windows handles '/' just fine // however the trailing '/' or '\' can come from the config file // so a windows user will most likely write '\' instead of '/' if( substr( $options['zeta.base'], -1 ) != DIRECTORY_SEPARATOR ) { $token = $options['zeta.base'] . '/'; } foreach( $docDirs as $docDir ) { $localPath = str_replace( $token, '', $docDir ); $component = substr( $localPath, 0, strpos( $localPath, '/' ) ); $target = "${tutorialRootDir}/" . $options['build.name'] . "/${component}"; pake_mkdirs( $target, 0755 ); pake_mirror( pakeFinder::type('any')->name('*') ->ignore_version_control(), $options['zeta.base'] . "/${docDir}", $target ); } // MvcTools' .htaccess must be forced pake_copy( $options['zeta.base'] . '/MvcTools/docs/HelloMvc/www/.htaccess', "${tutorialRootDir}/" . $options['build.name'] . "/MvcTools/HelloMvc/www/.htaccess", array( 'override' => true ) ); gen_tutorial_index_file( $tutorialRootDir ); } pake_desc( 'Extract API documentation from archive and copy it to the correct location in the content tree.' ); pake_task( 'extractphpdoc', 'tutorials' ); pake_alias( 'extract-phpdoc', 'extractphpdoc' ); /** * Extracts PHPDoc files in .index.xml * * This task is dependent of the "tutorials" task * * @param pakeTask $task * @param array $args * @access public * @return void */ function run_extractphpdoc( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); $tarballRootDir = 'content/documentation/' . $options['build.name']; $phpDocRootDir = "${tarballRootDir}/phpdoc"; pake_echo_comment( 'Extracting tarball' ); $tarball = ezcArchive::open( "compress.bzip2://${tarballRootDir}/phpdoc.tar.bz2", ezcArchive::TAR_GNU ); $tarball->extract( $tarballRootDir ); $rule = pakeFinder::type('dir')->name( '*' ) ->discard( 'phpdoc','design', 'Framework' ) ->maxdepth( 0 ); $components = $rule->in( $tarballRootDir ); sort( $components ); foreach( $components as $component ) { $componentName = basename( $component ); pake_echo_comment( "Copy API docs for ${component}" ); $rule = pakeFinder::type('any')->name('*'); pake_remove( $rule, "${component}/phpdoc" ); pake_mkdirs( "${component}/phpdoc" ); $phpDocComponentDir = "${component}/phpdoc"; pake_mirror( $rule, "${phpDocRootDir}/${componentName}", $phpDocComponentDir ); $patterns = array( '@="../' . $componentName . '/([a-zA-Z0-9]+\.html(#[a-zA-Z0-9$_]+)?)"@', '@="(?:\.\./)?([a-z]+)/([a-zA-Z0-9]+\.html(#[a-zA-Z0-9$_]+)?)"@i' ); $replacements = array( '="\1"', '="/zetacomponents/documentation/' . $options['build.name'] . '/\1/phpdoc/\2"' ); $xmlContents = ''; $xmlContents .= 'Class treeclasstrees.html'; // pake_replace_regexp is not distributed yet $rule = pakeFinder::type( 'file' )->name( '*.html' )->maxdepth( 0 ); $phpDocFiles = $rule->in( $phpDocComponentDir ); foreach( $phpDocFiles as $phpDocFile ) { pake_echo_action('tokens', $phpDocFile ); file_put_contents( $phpDocFile, preg_replace( $patterns, $replacements, pake_read_file( $phpDocFile ) ) ); $docFilename = basename( $phpDocFile ); // classtress.html is already handled above if( $docFilename != 'classtrees.html' ) { $xmlContents .= ''.$docFilename.''.$docFilename.''; } } write_indexxml( $xmlContents, "${phpDocComponentDir}/.index.xml" ); } pake_remove_dir( $phpDocRootDir ); } pake_desc( 'Build all docs for the release specified by the environment variable zeta.base' ); pake_task( 'docs', 'extractphpdoc' ); function run_docs( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); pake_echo_comment( 'Generating index and overview files' ); $rule = pakeFinder::type( 'dir' ) ->name( '*' ) ->discard( 'phpdoc', 'design' ) ->maxdepth( 0 ); $componentsRootDir = 'content/documentation/' . $options['build.name']; $components = $rule->in( $componentsRootDir ); $overviewFile = "${componentsRootDir}/overview.txt"; pake_copy( 'content/documentation/teaser.txt', $overviewFile, array( 'override' => true ) ); $overviewTxt = pake_read_file( $overviewFile ); $componentLinks = ''; $xmlContents = ''; $xmlContents .= ""; $xmlContents .= 'Overviewoverview.txt'; foreach( $components as $component ) { $componentName = basename( $component ); $xmlContents .= "${componentName}${componentName}/"; // for overview.txt $overviewTxt .= "- ${componentName}__\n"; $componentLinks .= '__ /zetacomponents/documentation/' . $options['build.name'] . "/${componentName}/tutorial.html\n"; } $xmlContents .= ""; pake_write_file( "${componentsRootDir}/.index.xml", $xmlContents, $overwrite = true ); $overviewTxt = $overviewTxt . "\n" . $componentLinks; pake_write_file( $overviewFile, $overviewTxt, $overwrite = true ); } pake_desc( 'Builds the website' ); pake_task( 'website', 'checkout_wcv', 'extractphpdoc' ); /** * Builds the website * * @param pakeTask $task * @param mixed $args * @access public * @return void */ function run_website( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); pake_sh( $options['php.path'] . ' scripts/updateCache.php' ); pake_sh( $options['php.path'] . ' var/scripts/highlight_source.php' ); run_revertempty( $task, $args ); } pake_desc( 'Reverts all files without real changes' ); pake_task( 'revertempty' ); pake_alias( 'revert-empty', 'revertempty' ); /** * Reverts all files without real changes * * @param pakeTask $task * @param mixed $args * @access public * @return void */ function run_revertempty( $task, $args ) { $options = pakeYaml::loadFile( CONFIG_FILE ); $modifiedFiles = array(); $xmlReader = new XMLReader(); $xmlReader->XML( pake_sh( $options['svn.path'] . ' st --xml htdocs' ) ); while( $xmlReader->read() ) { if( $xmlReader->nodeType == XMLReader::ELEMENT && $xmlReader->name == "entry" ) { $xmlReader->moveToNextAttribute(); $modifiedFiles[] = $xmlReader->value; } } foreach( $modifiedFiles as $modifiedFile ) { $diff = explode( "\n", pake_sh( $options['svn.path'] . " diff --diff-cmd '" . $options['diff.path']. "' -x -U0 ${modifiedFile}" ) ); // removes diff header if( count( $diff ) > 4 ) { $diff = array_slice( $diff, 4 ); } $patterns = array( "^@@.*@@$", "", "", "", "^[+-][\s]+by[\s]", "^[+-][\s]+on[\s]" ); foreach( $diff as $lineNumber => $diffLine ) { // kind of equivalent of "grep -v" foreach( $patterns as $pattern ) { if( preg_match( "#${pattern}#", $diffLine, $matches ) ) { unset( $diff[$lineNumber] ); } } } if( count( $diff ) == 0 ) { pake_sh( $options['svn.path'] . " ${modifiedFile}" ); } } } /* ------ "Private" functions ----------- */ /** * Writes an index.xml file * * @param string $xmlContents * @param string $xmlFile * @access public * @return void */ function write_indexxml( $xmlContents, $xmlFile ) { $finalContents = ''; $finalContents .= ''; $finalContents .= $xmlContents; $finalContents .= ''; pake_write_file( $xmlFile, $finalContents, $overwrite = true ); } /** * Generates .index.xml files for tutorials * * @param string $tutorialRootDir * @access public * @return void */ function gen_tutorial_index_file( $tutorialRootDir ) { pake_echo_comment( "Generating index files" ); $rule = pakeFinder::type('dir')->name( '*' ) ->maxdepth( 0 ); $options = pakeYaml::loadFile( CONFIG_FILE ); $components = $rule->in( "${tutorialRootDir}/" . $options['build.name'] ); foreach( $components as $component ) { $xmlContents = ''; if( file_exists( "${component}/tutorial.txt" ) ) { $xmlContents .= 'Tutorialtutorial.txt'; } $xmlContents .= 'APIphpdoc/'; $tutorialRule = pakeFinder::type( 'file' )->name( '*.txt' ) ->relative() ->maxdepth( 0 ) ->discard( 'tutorial.txt' ); $tutorialFiles = $tutorialRule->in( $component ); foreach( $tutorialFiles as $tutorialFile ) { $tutorialName = ucfirst( str_replace( '.txt', '', $tutorialFile) ); $xmlContents .= "${tutorialName}${tutorialFile}"; } $tutorialRule = pakeFinder::type( 'dir' )->name( '*' ) ->relative() ->maxdepth( 0 ) ->discard( 'design', 'phpdoc', 'tutorial', 'functions' ); $tutorialDirs = $tutorialRule->in( $component ); foreach( $tutorialDirs as $tutorialDir ) { $xmlContents .= '' . ucfirst( $tutorialDir ). '' . $tutorialDir . '/'; } write_indexxml( $xmlContents, "${component}/.index.xml" ); } } /** * Defines the path to the config file * * @access public * @return void */ function define_config_file() { $yamlFile = './pake/config.yaml'; if( getenv( 'ZETA_PAKE_FILE' ) and file_exists( getenv( 'ZETA_PAKE_FILE' ) ) ) { $yamlFile = getenv( 'ZETA_PAKE_FILE' ); pake_echo_comment( "Using custom config file ${yamlFile}" ); } define( 'CONFIG_FILE', $yamlFile ); } /** * Activates zeta components * * @access public * @return void */ function setup_zeta_components() { set_include_path( realpath( '../trunk' ) . PATH_SEPARATOR . get_include_path() ); require_once 'Base/src/ezc_bootstrap.php'; spl_autoload_register( 'ezcBase::autoload' ); } /** * Move PHP doc files to their correct location * * @param string $baseDir * @access public * @return void */ function move_html_files( $baseDir ) { $pattern = '/(classtrees|elementindex)_([a-z]+).html/i'; /* There is way too much magic in the Pake file finder ... */ $HTMLFiles = ezcBaseFile::findRecursive( $baseDir, array( $pattern ) ); foreach( $HTMLFiles as $HTMLFile ) { /* it will obviously match */ if( preg_match( $pattern, $HTMLFile , $matches ) ) { list( $unused, $newFile, $component ) = $matches; $destination = "${baseDir}/${component}/${newFile}.html"; pake_echo_comment( "Moving ${HTMLFile} to ${destination}" ); rename( $HTMLFile, $destination ); } } } /** * Shortens PHP doc filenames that are longer than 100 chars * * @param string $baseDir * @access public * @return void */ function shorten_long_filenames( $baseDir ) { $HTMLFiles = ezcBaseFile::findRecursive( $baseDir, array( '/(.*).html/') ); $replacementList = array(); $token = 'fsource_'; // finding long file names foreach( $HTMLFiles as $HTMLFile ) { $baseName = basename( $HTMLFile ); // max length supported by the Ustar algorithm if( strlen( $baseName ) < 100 ) { continue; } // removing "fsource_" should be enough to get an // acceptable length. That way we still keep // a "meaningful" URL for SEO. $newHTMLFile = str_replace( $token, '', $baseName ); pake_rename( $HTMLFile, dirname( $HTMLFile ) . '/' . $newHTMLFile ); if( preg_match( "#^([a-zA-Z]+)_#", $newHTMLFile, $matches ) ) { $component = $matches[1]; $replacementList[$component]['old'][] = $baseName; $replacementList[$component]['new'][] = $newHTMLFile; } } // replacing links foreach( $replacementList as $component => $fileNamesToReplace ) { $componentFiles = ezcBaseFile::findRecursive( $baseDir, array( "/${component}/" ) ); foreach( $componentFiles as $sourceFile ) { file_put_contents( $sourceFile, str_replace( $fileNamesToReplace['old'], $fileNamesToReplace['new'], file_get_contents( $sourceFile ), $count ) ); } } } /** * Creates an .tar.bz2 archive * * @param string $archiveName * @param string $directory * @access public * @return void */ function create_bz2_archive( $archiveName, $directory ) { if( file_exists( $archiveName ) ) { unlink( $archiveName ); } $PHPDocFiles = ezcBaseFile::findRecursive( $directory, array( "/(.*)/" ) ); pake_echo_comment( "Creating ${archiveName}" ); $tarball = ezcArchive::open( "compress.bzip2://${archiveName}", ezcArchive::TAR_USTAR ); foreach( $PHPDocFiles as $PHPDocFile ) { $tarball->append( $PHPDocFile, $directory ); } $tarball->close(); } ?>