PatchTask.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. /**
  3. * Patches a file by applying a 'diff' file to it
  4. *
  5. * Requires "patch" to be on the execution path.
  6. *
  7. * Based on Apache Ant PatchTask:
  8. *
  9. * Licensed to the Apache Software Foundation (ASF) under one or more
  10. * contributor license agreements. See the NOTICE file distributed with
  11. * this work for additional information regarding copyright ownership.
  12. * The ASF licenses this file to You under the Apache License, Version 2.0
  13. * (the "License"); you may not use this file except in compliance with
  14. * the License. You may obtain a copy of the License at
  15. *
  16. * http://www.apache.org/licenses/LICENSE-2.0
  17. *
  18. * Unless required by applicable law or agreed to in writing, software
  19. * distributed under the License is distributed on an "AS IS" BASIS,
  20. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  21. * See the License for the specific language governing permissions and
  22. * limitations under the License.
  23. *
  24. * @author Mikhail Krasilnikov <mk@3wstyle>
  25. * @version 0.01
  26. * @package phing.tasks.ext
  27. */
  28. require_once 'phing/Task.php';
  29. /**
  30. * Patches a file by applying a 'diff' file to it
  31. *
  32. * Requires "patch" to be on the execution path.
  33. *
  34. * @package phing.tasks.ext
  35. */
  36. class PatchTask extends Task
  37. {
  38. /**
  39. * Base command to be executed
  40. * @var string
  41. */
  42. const CMD = 'patch --batch ';
  43. /**
  44. * File to be patched
  45. * @var string
  46. */
  47. private $originalFile;
  48. /**
  49. * Patch file
  50. *
  51. * @var string
  52. */
  53. private $patchFile;
  54. /**
  55. * Value for a "-p" option
  56. * @var int
  57. */
  58. private $strip;
  59. /**
  60. * Command line arguments for patch binary
  61. * @var array
  62. */
  63. private $cmdArgs = array();
  64. /**
  65. * Halt on error return value from patch invocation.
  66. * @var bool
  67. */
  68. private $haltOnFailure = false;
  69. /**
  70. * The file containing the diff output
  71. *
  72. * Required.
  73. *
  74. * @param string $file File containing the diff output
  75. * @return void
  76. * @throws BuildException if $file not exists
  77. */
  78. public function setPatchFile($file)
  79. {
  80. if (!is_file($file))
  81. throw new BuildException(sprintf('Patchfile %s doesn\'t exist', $file));
  82. $this->patchFile = $file;
  83. }
  84. /**
  85. * The file to patch
  86. *
  87. * Optional if it can be inferred from the diff file.
  88. *
  89. * @param string $file File to patch
  90. * @return void
  91. */
  92. public function setOriginalFile($file)
  93. {
  94. $this->originalFile = $file;
  95. }
  96. /**
  97. * The name of a file to send the output to, instead of patching
  98. * the file(s) in place
  99. *
  100. * Optional.
  101. *
  102. * @param string $file File to send the output to
  103. * @return void
  104. */
  105. public function setDestFile($file)
  106. {
  107. if ($file !== null)
  108. $this->cmdArgs []= "--output=$file";
  109. }
  110. /**
  111. * Flag to create backups
  112. *
  113. * Optional, default - false
  114. *
  115. * @param bool $backups If true create backups
  116. * @return void
  117. */
  118. public function setBackups($backups)
  119. {
  120. if ($backups)
  121. $this->cmdArgs []= '--backup';
  122. }
  123. /**
  124. * Flag to ignore whitespace differences;
  125. *
  126. * Default - false
  127. *
  128. * @param bool $ignore If true ignore whitespace differences
  129. * @return void
  130. */
  131. public function setIgnoreWhiteSpace($ignore)
  132. {
  133. if ($ignore)
  134. $this->cmdArgs []= '--ignore-whitespace';
  135. }
  136. /**
  137. * Strip the smallest prefix containing <i>num</i> leading slashes
  138. * from filenames.
  139. *
  140. * patch's <i>--strip</i> option.
  141. *
  142. * @param int $num number of lines to strip
  143. * @return void
  144. * @throws BuildException if num is < 0, or other errors
  145. */
  146. public function setStrip($num)
  147. {
  148. if ($num < 0)
  149. throw new BuildException('strip has to be >= 0');
  150. $this->strip = $num;
  151. }
  152. /**
  153. * Work silently unless an error occurs
  154. *
  155. * Optional, default - false
  156. * @param bool $flag If true suppress set the -s option on the patch command
  157. * @return void
  158. */
  159. public function setQuiet($flag)
  160. {
  161. if ($flag)
  162. $this->cmdArgs []= '--silent';
  163. }
  164. /**
  165. * Assume patch was created with old and new files swapped
  166. *
  167. * Optional, default - false
  168. *
  169. * @param bool $flag If true set the -R option on the patch command
  170. * @return void
  171. */
  172. public function setReverse($flag)
  173. {
  174. if ($flag)
  175. $this->cmdArgs []= '--reverse';
  176. }
  177. /**
  178. * The directory to run the patch command in
  179. *
  180. * Defaults to the project's base directory.
  181. *
  182. * @param string $directory Directory to run the patch command in
  183. * @return void
  184. */
  185. public function setDir($directory)
  186. {
  187. $this->cmdArgs []= "--directory=$directory";
  188. }
  189. /**
  190. * Ignore patches that seem to be reversed or already applied
  191. *
  192. * @param bool $flag If true set the -N (--forward) option
  193. * @return void
  194. */
  195. public function setForward($flag)
  196. {
  197. if ($flag)
  198. $this->cmdArgs []= "--forward";
  199. }
  200. /**
  201. * Set the maximum fuzz factor
  202. *
  203. * Defaults to 0
  204. *
  205. * @param string $value Value of a fuzz factor
  206. * @return void
  207. */
  208. public function setFuzz($value)
  209. {
  210. $this->cmdArgs []= "--fuzz=$value";
  211. }
  212. /**
  213. * If true, stop the build process if the patch command
  214. * exits with an error status.
  215. *
  216. * The default is "false"
  217. *
  218. * @param bool $value "true" if it should halt, otherwise "false"
  219. * @return void
  220. */
  221. public function setHaltOnFailure($value)
  222. {
  223. $this->haltOnFailure = $value;
  224. }
  225. /**
  226. * Main task method
  227. *
  228. * @return void
  229. * @throws BuildException when it all goes a bit pear shaped
  230. */
  231. public function main()
  232. {
  233. if ($this->patchFile == null)
  234. throw new BuildException('patchfile argument is required');
  235. // Define patch file
  236. $this->cmdArgs []= '-i ' . $this->patchFile;
  237. // Define strip factor
  238. if ($this->strip != null)
  239. $this->cmdArgs []= '--strip=' . $this->strip;
  240. // Define original file if specified
  241. if ($this->originalFile != null)
  242. $this->cmdArgs []= $this->originalFile;
  243. $cmd = self::CMD . implode(' ', $this->cmdArgs);
  244. $this->log('Applying patch: ' . $this->patchFile);
  245. exec($cmd, $output, $exitCode);
  246. foreach ($output as $line)
  247. $this->log($line, Project::MSG_VERBOSE);
  248. if ($exitCode != 0 && $this->haltOnFailure)
  249. throw new BuildException( "Task exited with code $exitCode" );
  250. }
  251. }