Home » Php » Jenkins CI On Windows – PHP Tools Not Executed At Build

Jenkins CI On Windows – PHP Tools Not Executed At Build

Posted by: admin July 12, 2020 Leave a comment

Questions:

I recently installed Jenkins on my local Windows 7 dev environment.

I configured Jenkins for my local Java and Ant installations and I set up my first project.

I used instruction from http://jenkins-php.org/ to execute certain PHP tools (PHP CodeSniffer, PHP Doc etc.) at build specified in build.xml.

Pear was correctly configured before installation of the PHP tool packages, so all package.bat files (in the Pear directory) of the packages do have the correct PHP bin path. Also, a Windows environment variable PHPBIN is set, pointing to the php bin location – the php bin path is also on the PATH variable.

The Pear path (containing all installation of the PHP tools) is also included in the PATH variable.

When I manually start the build I get error messages that it cannot run certain programs (–> the PHP tools), although they are correctly installed (through Pear) and are executable through a command prompt…

Here’s the error output:

Started by user anonymous
Updating file:///D://SVN/MyProjectRepository/trunk/public_html
At revision 38
[workspace] $ cmd.exe /C '"ant.bat -file build.xml && exit %%ERRORLEVEL%%"'
Buildfile: C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml

clean:
   [delete] Deleting directory C:\Servers\Jenkins\jobs\MyProject\workspace\build\api
   [delete] Deleting directory C:\Servers\Jenkins\jobs\MyProject\workspace\build\code-browser
   [delete] Deleting directory C:\Servers\Jenkins\jobs\MyProject\workspace\build\coverage
   [delete] Deleting directory C:\Servers\Jenkins\jobs\MyProject\workspace\build\logs
   [delete] Deleting directory C:\Servers\Jenkins\jobs\MyProject\workspace\build\pdepend
    [mkdir] Created dir: C:\Servers\Jenkins\jobs\MyProject\workspace\build\api
    [mkdir] Created dir: C:\Servers\Jenkins\jobs\MyProject\workspace\build\code-browser
    [mkdir] Created dir: C:\Servers\Jenkins\jobs\MyProject\workspace\build\coverage
    [mkdir] Created dir: C:\Servers\Jenkins\jobs\MyProject\workspace\build\logs
    [mkdir] Created dir: C:\Servers\Jenkins\jobs\MyProject\workspace\build\pdepend

parallelTasks:

pdepend:

phpcpd:

phpdoc:

phpcs:

phploc:

BUILD FAILED
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:29: 
The following error occurred while executing this line:
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:42: Execute failed: java.io.IOException: Cannot run program "pdepend": CreateProcess error=2, The system cannot find the file specified
The following error occurred while executing this line:
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:62: Execute failed: java.io.IOException: Cannot run program "phpcpd": CreateProcess error=2, The system cannot find the file specified
The following error occurred while executing this line:
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:80: Execute failed: java.io.IOException: Cannot run program "phpcs": CreateProcess error=2, The system cannot find the file specified
The following error occurred while executing this line:
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:93: Execute failed: java.io.IOException: Cannot run program "phpdoc": CreateProcess error=2, The system cannot find the file specified
The following error occurred while executing this line:
C:\Servers\Jenkins\jobs\MyProject\workspace\build.xml:72: Execute failed: java.io.IOException: Cannot run program "phploc": CreateProcess error=2, The system cannot find the file specified

Total time: 0 seconds
Build step 'Invoke Ant' marked build as failure
[CHECKSTYLE] Skipping publisher since build result is FAILURE
[PMD] Skipping publisher since build result is FAILURE
[DRY] Skipping publisher since build result is FAILURE
[htmlpublisher] Archiving HTML reports...
[htmlpublisher] Archiving at PROJECT level C:\Servers\Jenkins\jobs\MyProject\workspace\build/code-browser to C:\Servers\Jenkins\jobs\MyProject\htmlreports\Code_Browser
ERROR: Directory 'C:\Servers\Jenkins\jobs\MyProject\workspace\build/code-browser' exists but failed copying to 'C:\Servers\Jenkins\jobs\MyProject\htmlreports\Code_Browser'.
Publishing Javadoc
[JDepend] JDepend plugin is ready
[JDepend] Couldn't generate JDepend file at 'build/logs/jdepend.xml'java.io.FileNotFoundException: C:\Servers\Jenkins\jobs\MyProject\workspace\build\logs\jdepend.xml (The system cannot find the file specified)
Sending e-mails to: [email protected]
Finished: FAILURE

Can anybody point me in the right direction what the problem is?

How to&Answers:

The short answer is: it is extremely painful to setup the build file on Windows in a way that doesnt break either the execution or the results in one way or the other. This is not a Jenkins issue, nor an Ant issue. It’s the QA tools. If you have any chance to move Jenkins to a *nix, then do so.

In particular, when running the PHP QA Tools, make sure you call them with cmd /c, e.g.

<target name="pdepend">
    <exec executable="cmd">
        <arg line="/c pdepend
            --jdepend-xml='${basedir}/build/logs/jdepend.xml'
            … additional options
            sourcefolder1,sourcefolder2
        " />
    </exec>
</target>

Also make sure you don’t have spaces in any paths because they will lead to issues. You also cannot use the ~ character (like in DOS paths) reliably and the various PHP Tools will each have their own ideas about the Directory Separator and whether they take multiple sourcefolder as comma separated values, etc.

Feel free to report any bugs you encounter with the various tool owners at GitHub, so they get fixed. Also, consider dropping in to #jenkins-php at Freenode IRC.

Find an example of a build file configuration that worked for me:

<?xml version="1.0" encoding="utf-8" ?> 
<project name="foo" default="build" basedir=".">

<target name="clean">
    <!-- Clean up -->
    <delete dir="${basedir}/build" />

    <!-- Create build directories -->
    <mkdir dir="${basedir}/build/api" />
    <mkdir dir="${basedir}/build/code-browser" />
    <mkdir dir="${basedir}/build/coverage" />
    <mkdir dir="${basedir}/build/logs" />
    <mkdir dir="${basedir}/build/pdepend" />
</target>

<!-- Run unit tests and generate junit.xml and clover.xml -->
<target name="phpunit">
    <exec executable="cmd">
        <arg line="/c phpunit '${basedir}/test'" />
    </exec>
</target>

<!-- Run the pdepend, phpmd, phpcpd, phpcs, phpdoc and phploc tasks in parallel 
    using a maximum of 2 threads. -->
<target name="parallelTasks">
    <parallel threadCount="1">
        <sequential>
            <antcall target="pdepend" />
            <antcall target="phpmd" />
        </sequential>
        <antcall target="phpcpd" />
        <antcall target="phpcs" />
        <antcall target="phpdoc" />
        <antcall target="phploc" />
    </parallel>
</target>

<!-- Generate jdepend.xml and software metrics charts -->
<target name="pdepend">
    <exec executable="cmd">
        <arg line="/c pdepend
            --jdepend-xml='${basedir}/build/logs/jdepend.xml'
            --jdepend-chart='${basedir}/build/pdepend/dependencies.svg'
            --summary-xml='${basedir}/build/logs/jdepend-summary.xml'
            --overview-pyramid='${basedir}/build/pdepend/overview-pyramid.svg'
            --ignore='${basedir}\lib\Zend\*'
            application,lib
            " />
    </exec>
</target>

<!-- Generate pmd.xml -->
<target name="phpmd">
    <exec executable="cmd">
        <arg line="/c phpmd application,lib
          xml
          codesize,design,naming,unusedcode
          --reportfile '${basedir}/build/logs/pmd.xml'
          --exclude '${basedir}\lib\Zend\*'
          " />
    </exec>
</target>

<!-- Generate pmd-cpd.xml -->
<target name="phpcpd">
    <exec executable="cmd">
        <arg line="/c phpcpd
            --log-pmd '${basedir}/build/logs/pmd-cpd.xml'
            --exclude '${basedir}/lib/Zend'
            application lib" />
    </exec>
</target>

<!-- Generate phploc.csv -->
<target name="phploc">
    <exec executable="cmd">
        <arg line="/c phploc
            --log-csv '${basedir}/build/logs/phploc.csv'
            --exclude '${basedir}/lib/Zend'
            application lib" />
    </exec>
</target>

<!-- Generate checkstyle.xml -->
<target name="phpcs">
    <exec executable="cmd">
        <arg line="/c phpcs
            --report=checkstyle
            --report-file='${basedir}/build/logs/checkstyle.xml'
            --standard='${basedir}/docs/coding-standard/ruleset.xml'
            --ignore=*\lib\Zend\*
            -p
            application lib" />
    </exec>
</target>

<!-- Generate API documentation -->
<target name="phpdoc">
    <exec executable="cmd">
        <arg line="/c phpdoc
            --directory application lib
            --target    '${basedir}/build/api'
            --ignore    '${basedir}/lib/Zend/*'
           " />
    </exec>
</target>

<target name="phpcb">
    <exec executable="cmd">
        <arg line="/c phpcb
          --log    '${basedir}/build/logs'
          --output '${basedir}/build/code-browser'
          --ignore '${basedir}/lib/Zend'
          " />

    </exec>
</target>

<target name="build" depends="clean,parallelTasks,phpunit,phpcb" />
</project> 

Answer:

I was having the same problem of wanting to run Jenkins builds on Linux and Windows and after looking around for a while, found a related solution that works for both:
http://www.jguru.com/forums/view.jsp?EID=1560053

Use a conditional script suffix to append to each of the executable scripts:

<condition property="script-suffix" value=".bat" else="">
  <os family="windows" />
</condition>

Then your script calls looks like this:

<exec executable="phpmd${script-suffix}">
...
</exec>

for each PHP tool executable. It’s not as pretty, but it works for me.

So my build.xml looks like this. Note that I have changed some of the php-template behavior to match my specific project (deletion, config file locations, etc), which isn’t necessary for the norm.

<?xml version="1.0" encoding="UTF-8"?>
<project name="Project" default="build" basedir=".">
 <!-- ... defined properties ... -->

 <condition property="script-suffix" value=".bat" else="">
   <os family="windows" />
 </condition>

 <target name="clean"
         description="Clean up and create artifact directories">
  <delete>
    <fileset dir="${basedir}/build/api" />
    <fileset dir="${basedir}/build/coverage"/>
    <fileset dir="${basedir}/build/logs"/>
    <fileset dir="${basedir}/build/pdepend"/>
  </delete>
  <delete dir="${basedir}/build/code-browser"/>
  <mkdir dir="${basedir}/build/code-browser"/>
 </target>

 <target name="phpunit"
         description="Run unit tests using PHPUnit and generates junit.xml and clover.xml">
  <exec executable="phpunit${script-suffix}" failonerror="true">
   <arg value="-c" />
   <arg path="${basedir}/build/phpunit.xml" />
   <arg value="-d" />
   <arg value="memory_limit=1024M" />
   <arg path="${source}" />
  </exec>
 </target>

 <target name="parallelTasks"
         description="Run the pdepend, phpmd, phpcpd, phpcs, phpdoc and phploc tasks in parallel using a maximum of 2 threads.">
  <parallel threadCount="2">
   <sequential>
    <antcall target="pdepend"/>
    <antcall target="phpmd"/>
   </sequential>
   <antcall target="phpcpd"/>
   <antcall target="phpcs"/>
   <antcall target="phpdoc"/>
   <antcall target="phploc"/>
  </parallel>
 </target>

 <target name="pdepend"
         description="Generate jdepend.xml and software metrics charts using PHP_Depend">
  <exec executable="pdepend${script-suffix}">
   <arg value="--jdepend-xml=${basedir}/build/logs/jdepend.xml" />
   <arg value="--jdepend-chart=${basedir}/build/pdepend/dependencies.svg" />
   <arg value="--overview-pyramid=${basedir}/build/pdepend/overview-pyramid.svg" />
   <arg path="${source}" />
  </exec>
 </target>

 <target name="phpmd"
         description="Generate pmd.xml using PHPMD">
  <exec executable="phpmd${script-suffix}">
   <arg path="${source}" />
   <arg value="xml" />
   <arg value="${basedir}/build/phpmd.xml" />
   <arg value="--reportfile" />
   <arg value="${basedir}/build/logs/pmd.xml" />
  </exec>
 </target>

 <target name="phpcpd"
         description="Generate pmd-cpd.xml using PHPCPD">
  <exec executable="phpcpd${script-suffix}">
   <arg value="--log-pmd" />
   <arg value="${basedir}/build/logs/pmd-cpd.xml" />
   <arg path="${source}" />
  </exec>
 </target>

 <target name="phploc"
         description="Generate phploc.csv">
  <exec executable="phploc${script-suffix}">
   <arg value="--log-csv" />
   <arg value="${basedir}/build/logs/phploc.csv" />
   <arg path="${source}" />
  </exec>
 </target>

 <target name="phpcs"
         description="Generate checkstyle.xml using PHP_CodeSniffer">
  <exec executable="phpcs${script-suffix}">
   <arg value="--report=checkstyle" />
   <arg value="--report-file=${basedir}/build/logs/checkstyle.xml" />
   <arg value="--standard=${basedir}/build/phpcs.xml" />
   <arg path="${source}" />
  </exec>
 </target>

 <target name="phpdoc"
         description="Generate API documentation using PHPDocumentor">
  <exec executable="phpdoc${script-suffix}">
   <arg value="-c" />
   <arg path="${basedir}/build/phpdoc.ini" />
  </exec>
 </target>

 <target name="phpcb"
         description="Aggregate tool output with PHP_CodeBrowser">
  <exec executable="phpcb${script-suffix}">
   <arg value="--log" />
   <arg path="${basedir}/build/logs" />
   <arg value="--source" />
   <arg path="${source}" />
   <arg value="--output" />
   <arg path="${basedir}/build/code-browser" />
  </exec>
 </target>

 <target name="build" depends="clean,parallelTasks,phpunit,phpcb"/>

 <!-- ... other targets ... -->
</project>