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();
}
?>