Phing obsahuje ve verzi 2.3 tyto Tasky pro práci se Subversion.
- SvnCheckoutTask
- SvnExportTask
- SvnLastRevisionTask
- SvnUpdateTask
To se hodí a také ve svém buildu používám tento postup:
- udělám export HEAD revision (SvnExportTask)
- načtu si číslo HEAD revision (SvnLastRevisionTask)
- potom vygeneruju SVN Log (SvnLogTask)
- vygeneruju API dokumentaci pro build (PhpDocumentorTask)
- všechno zakomprimuji do souboru s dokumentací a se zdrojáky a uložím kam potřebuju (ZipTask)
V postupu je něco co není standardní součástí Phingu i když si myslím, že se to tam objeví. Phing pro práci se SVN používá VersionControl_SVN 0.3.1 alpha. Bohužel neexistuje zatím stable verze této PEAR knihovny což je škoda, protože funguje celkem dobře a má implementováno vše co potřebuji. Když používáte VersionControl_SVN dá se pracovat s několika návratovými typy:
- VERSIONCONTROL_SVN_FETCHMODE_ASSOC,
- VERSIONCONTROL_SVN_FETCHMODE_OBJECT,
- VERSIONCONTROL_SVN_FETCHMODE_XML,
- VERSIONCONTROL_SVN_FETCHMODE_RAW,
- VERSIONCONTROL_SVN_FETCHMODE_ALL,
- VERSIONCONTROL_SVN_FETCHMODE_ARRAY
V Phingu se používá, ale výhradně VERSIONCONTROL_SVN_FETCHMODE_ASSOC což není někdy úplně vhodné. V případě, že máte totiž zapnutý přepínač pro výpis v XML je lepší návratový typ VERSIONCONTROL_SVN_FETCHMODE_XML. Proto jsem musel upravit phing\tasks\ext\svn\SvnBaseTask.php.
Tuto část
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_ASSOC, 'svn_path' => $this->getSvnPath());
jsem nahradil tímto kódem
// Set up runtime options. Will be passed to all
// subclasses.
if ($mode=="log")
{
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_XML, 'svn_path' => $this->getSvnPath());
}
else
{
$options = array('fetchmode' => VERSIONCONTROL_SVN_FETCHMODE_ASSOC, 'svn_path' => $this->getSvnPath());
}
Po této úpravě, která jistě by šla udělat lépe. Jsem se dal do psaní vlastního tasku SvnLogTask.php. Task vrátí log z repozitory v XML formátu. Pokud chceme plain text, tak to prožene XSLT transformací a potom ještě vymaže whitespaces z celého dokumentu.
require_once 'phing/Task.php';
require_once 'phing/tasks/ext/svn/SvnBaseTask.php';
class SvnLogTask extends SvnBaseTask
{
private $name = 'log.xml';
private $xml = true;
private $verbose = false;
/**
* The setter for the attribute "name"
*/
public function setName($str) {
$this->name = $str;
}
/**
* The setter for the attribute "xml"
*/
public function setXML($str) {
$this->xml = $str;
}
/**
* The setter for the attribute "verbose"
*/
public function setVerbose($str) {
$this->verbose = $str;
}
/**
* The main entry point
*
* @throws BuildException
*/
function main()
{
$this->setup('log');
$this->log("Log SVN");
$switches = array('verbose' => $this->verbose);
$output=$this->run(array(), $switches);
$doc = new DOMDocument();
$doc->formatOutput = true;
$doc->loadXML($output);
// output format
if ($this->xml=="true")
{
$doc->save($this->getToDir()."/".$this->name);
}
else
{
// print in plain
$xsl = new DOMDocument;
$xsl->load(dirname(__FILE__).'\LogTxt.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl); // attach the xsl rules
$output=$proc->transformToDoc($doc)->firstChild->wholeText;
$pat[0] = "/^\s+/";
$pat[1] = "/\s{2,}/";
$pat[2] = "/\s+\$/";
$rep[0] = "";
$rep[1] = " ";
$rep[2] = "";
$after=preg_replace($pat, $rep, $output);
$str=str_replace("\\n ","\n", $after);
file_put_contents($this->getToDir()."/".$this->name,$str);
}
}
}
Potom se to dá už použít v build.xml.
<taskdef name="svnlog" classname="phing.tasks.ext.svn.SvnLogTask" />
<svnlog
svnpath="${svnpath}"
repositoryurl="${rep}"
name="CHANGELOG"
xml="true"
verbose="true"
todir="${tmp}\log"/>
Moje vlastní XSLT transformace (phing\tasks\ext\svn\LogTxt.xsl) na plain text není přiliš vhodná pro nějaké systémové řešení. Lepší je parametr nechat zapnutý. Vzít XML a pomocí XsltFilter aplikovat vlastní transformaci.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="log">
CHANGELOG\n
<xsl:apply-templates select='logentry'/>
</xsl:template>
<xsl:template match="logentry">
\n R<xsl:apply-templates select='@revision'/>\n
<xsl:apply-templates select='date' /> - <xsl:apply-templates select='author'/>\n
<xsl:apply-templates select='msg'/>\n
</xsl:template>
</xsl:stylesheet>
Nakonec přidám ještě moje build.propeties a buid.xml jednoho moje projektu pro ilustraci.
# Property files contain key/value pairs # key=value tmp = c:\tmp svnpath = c:\apps\svn\bin\svn.exe rep = file:///rootwww/rep_cvut/akce/trunk wc = c:\rootwww\wc_cvut\akce
Tady v build.propeties si všimněte jen jediného detailu, ale ten vás může stát hodně času. Cesty k svn a k repozitory neobsahují mezery, pokud máte Subversion např. v Program Files může to nadělat více problémů než užitku a chyby se projevují různě a ne zcela systémově. Doporučuji se tomu předem vyhnout, ušetříte si čas a nervy.
<?xml version="1.0" ?>
<project name="akce2" basedir="." default="main">
<!-- Sets the DSTAMP, TSTAMP and TODAY properties -->
<tstamp/>
<!-- Load our configuration -->
<property file="./build.properties" />
<taskdef name="svnlog" classname="phing.tasks.ext.svn.SvnLogTask" />
<property name="package" value="${phing.project.name}" override="true" />
<property name="builddir" value="${tmp}/build/${phing.project.name}" override="true" />
<property name="srcdir" value="${project.basedir}" override="true" />
<target name="svn" description="SVN executes">
<!-- Export HEAD copy do /tmp/ -->
<svnexport
svnpath="${svnpath}"
repositoryurl="${rep}"
force="yes"
todir="${tmp}\export\${phing.project.name}"/>
<!-- Získání čísla HEAD -->
<svnlastrevision
svnpath="${svnpath}"
workingcopy="${wc}"
propertyname="svn.lastrevision"/>
<!-- Vygenerování aktuálního logu -->
<svnlog
svnpath="${svnpath}"
repositoryurl="${rep}"
name="CHANGELOG"
xml="false"
verbose="true"
todir="${tmp}\export\${phing.project.name}"/>
</target>
<target name="phpdoc" description="API Documentation" depends="svn">
<!-- Generování phpdoc dokumentace -->
<phpdoc title="Akce2008 API Documentation"
destdir="${builddir}/apidocs"
sourcecode="yes"
defaultpackagename="Akce2008"
output="HTML:default:default">
<fileset dir="${tmp}/export/${phing.project.name}">
<include name="*/*.php" />
<exclude name="inc/phpmailer/**" />
<exclude name="build/**" />
</fileset>
<projdocfileset dir=".">
<include name="CHANGELOG" />
</projdocfileset>
</phpdoc>
</target>
<!-- Fileset for all files -->
<fileset dir="${tmp}/export/${phing.project.name}" id="allfiles">
<include name="**" />
<exclude name="build.xml" />
<exclude name="build.properties" />
</fileset>
<!-- Main Target -->
<target name="main" description="main target" depends="phpdoc">
<!-- Zdrojové kódy pro příslušnou revizi -->
<zip destfile="${builddir}/${phing.project.name}-R${svn.lastrevision}-${DSTAMP}${TSTAMP}.zip" basedir="${tmp}/export/${phing.project.name}" />
<!-- Vygenerovanou dokumentaci přesunu do ZIP s číslem příslušné revize -->
<zip destfile="${builddir}/${phing.project.name}-apidocs-R${svn.lastrevision}-${DSTAMP}${TSTAMP}.zip" basedir="${builddir}/apidocs" />
<!-- Smazaní temp adresáře -->
<delete dir="${builddir}/apidocs" includeemptydirs="true" verbose="true" failonerror="true" />
</target>
</project>
Vlastní build se pustí pomocí phing z příkazové řádky tam kde máte build.xml uložený. Pokud se nedaří Phing nainstalovat a nakonfigurovat pro chod např. s PHPDoc, tak doporučuji postup ze stránek Phingu.
pear channel-discover pear.phing.info pear install -a phing/phing pear install channel://pear.php.net/VersionControl_SVN-0.3.1
Doufám, že někomu rady budou k užitku, Phing je skvělý produkt i když většina asi dá za přednost Antu pokud nedělají čistě PHP.