PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/external-programs/tracked-compile.el

https://github.com/hillwithsmallfields/JCGS-emacs
Emacs Lisp | 741 lines | 540 code | 119 blank | 82 comment | 26 complexity | 43ad3c78b5a844763b8129c6ccc05846 MD5 | raw file
Possible License(s): GPL-2.0
  1. ;;; tracked-compile.el --- run compilations with tracking
  2. ;; Copyright (C) 2011, 2012, 2013 John Sturdy
  3. ;; Author: John Sturdy <john.sturdy@citrix.com>
  4. ;; Keywords: convenience
  5. ;; This program is free software; you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;; Experimental package for doing code experiments, in the XenClient (BB/OE) environment
  17. ;;; Code:
  18. (require 'annotation)
  19. ;;;;;;;;;;;;;;;
  20. ;; Recording ;;
  21. ;;;;;;;;;;;;;;;
  22. (defvar tracking-org-file work-log-file
  23. "The tracking org file for this buffer.
  24. May be a central one for everything, or you can use separate files for
  25. different projects.")
  26. (defvar tracked-compile-latest-build nil
  27. "The name of the latest build.")
  28. (add-to-list 'auto-mode-alist (cons (file-name-nondirectory tracking-org-file) 'tracked-compile-mode))
  29. ;;;;;;;;;;;;;;;;;;;;;;;
  30. ;; Date-based filing ;;
  31. ;;;;;;;;;;;;;;;;;;;;;;;
  32. (defun tracking-open-date (date)
  33. "Ensure there is an open tracking record for DATE."
  34. (interactive
  35. (list
  36. (read-from-minibuffer "Date (YYYY_MM_DD): "
  37. (format-time-string "%Y_%m_%d"))))
  38. (find-file tracking-org-file)
  39. ;; we must be in something based on org-mode for some org-mode
  40. ;; functions we use to work; we mustn't call the mode setup
  41. ;; function each time, because it kills all local variables
  42. (if (fboundp 'tracked-compile-mode)
  43. (unless (eq major-mode 'tracked-compile-mode)
  44. (tracked-compile-mode))
  45. (unless (eq major-mode 'org-mode)
  46. (org-mode)))
  47. (jcgs/org-open-hierarchical-date date))
  48. (defun tracking-record (date name &optional command motive changes)
  49. "Record that on DATE we did a build which we call NAME.
  50. Optional COMMAND says what `compile-command' is being used.
  51. Optional MOTIVE is entered by the user.
  52. Optional CHANGES says what the changes are."
  53. (if (null tracking-org-file)
  54. (message "No tracking org file for this buffer")
  55. (let ((bbd bb-bb-directory) ; take local copy of buffer-local
  56. ; variable as we will change buffer
  57. (origin-file (or (buffer-file-name) default-directory))
  58. (project-description (annotation-project-description)))
  59. (save-excursion
  60. (tracking-open-date date)
  61. (when command
  62. (setq compile-command command))
  63. (goto-char (point-max))
  64. (insert "**** Build " name "\n ")
  65. (when project-description
  66. (insert "In " project-description "\n "))
  67. (when command
  68. (message "Setting recorded command to %S" command)
  69. (org-set-property "compile-command" command)
  70. (org-set-property "compile-file" origin-file))
  71. (when (stringp motive)
  72. (org-set-property "motive" motive))
  73. (when changes
  74. (org-set-property "changes" changes))))))
  75. (defun tracked-compile (&optional motive)
  76. "Run a compilation, with tracking in the file named in `tracking-org-file'.
  77. Optional argument MOTIVE says what the compilation is meant to acheive."
  78. (interactive "sReason for compilation: ")
  79. (let* ((tracking-date (format-time-string "%Y_%m_%d"))
  80. (tracking-name (concat tracking-date (format-time-string "_%H_%M_%S"))))
  81. (setq tracked-compile-latest-build tracking-name)
  82. (let ((bb-dir (ancestor-directory-containing default-directory "bb"))
  83. (git-dir (ancestor-directory-containing default-directory "git")))
  84. (let ((changes (funcall tracked-compile-unrecorded-changes-function
  85. git-dir)))
  86. (tracking-record tracking-date
  87. tracking-name
  88. compile-command
  89. motive
  90. changes))
  91. (message "tracked-compile from buffer %s, bb-dir %s, git-dir %s"
  92. (current-buffer) bb-dir git-dir)
  93. (when tracked-compile-ensure-initialized-snapshot-system-function
  94. (funcall tracked-compile-ensure-initialized-snapshot-system-function
  95. git-dir))
  96. (funcall tracked-compile-snapshot-function
  97. git-dir
  98. tracking-name)
  99. (let* ((t-o-f tracking-org-file)
  100. (compilation-buffer (compile (eval compile-command))))
  101. (save-excursion
  102. (find-file t-o-f)
  103. (mapcar (lambda (flag)
  104. (org-toggle-tag flag t))
  105. (tracked-compile-get-flags nil git-dir)))
  106. (set-buffer compilation-buffer)
  107. (setq tracking-org-file t-o-f) ; set local to compilation buffer
  108. (add-hook 'compilation-finish-functions
  109. 'tracked-compile-compilation-finish-function t t)))))
  110. ;;;;;;;;;;;;;;;;;;;
  111. ;; tracked tests ;;
  112. ;;;;;;;;;;;;;;;;;;;
  113. (defun strings-matching-pattern-in-file (pattern number file)
  114. "Return all strings matching PATTERN capture NUMBER in the contents of FILE."
  115. (let ((was-visiting (find-buffer-visiting file))
  116. (results nil))
  117. (save-excursion
  118. (save-window-excursion
  119. (find-file file)
  120. (save-excursion
  121. (goto-char (point-min))
  122. (while (re-search-forward pattern (point-max) t)
  123. (push (match-string-no-properties number) results))
  124. results)))))
  125. (defvar latest-tested-laptop nil
  126. "The latest laptop to be used by `tracked-test'.")
  127. (defun tracking-format-current-date ()
  128. "Return the current date, as a string."
  129. (format-time-string "%Y_%m_%d"))
  130. (defun tracked-test (&optional laptop)
  131. "Prepare to record a test result, with tracking by date and file versions.
  132. The record is made in the file named in `tracking-org-file'.
  133. Optional argument LAPTOP says which laptop you're running the test on."
  134. (interactive
  135. (list (prompt-for-laptop "Run test on laptop: ")))
  136. (when laptop
  137. (setq latest-tested-laptop laptop))
  138. (shell-command (format "ssh root@%s \"echo -n > /var/log/messages; reboot -f\"" laptop))
  139. (let* ((tracking-date (tracking-format-current-date))
  140. (tracking-name (concat tracking-date (format-time-string "_%H_%M_%S"))))
  141. (tracking-open-date tracking-date)
  142. (goto-char (point-max))
  143. (insert "**** Test " tracking-name "\n ")
  144. (let ((raw-ls (shell-command-to-string
  145. (format "ssh root@%s ls -l /usr/lib/xen/bin/qemu-dm"
  146. (getenv "LAPTOPNAME")))))
  147. (when (string-match
  148. "^[-rwx.]+.+?\\([0-9]\\{7,9\\} [a-z]\\{3\\} +[0-9]+ [0-9]+:[0-9]+\\) /usr"
  149. raw-ls)
  150. (org-set-property "qemu-file"
  151. (substring raw-ls (match-beginning 1) (match-end 1)))))
  152. (laptop-state-record laptop)
  153. (let* ((files-named (strings-matching-pattern-in-file
  154. " \\([-a-z0-9:_]+/[-a-z0-9/.:_]+\\)" 1
  155. (format "/home/xc_tftpboot/pxe/%s/pxelinux.cfg"
  156. (getenv "LAPTOPNAME")))))
  157. (dolist (file files-named)
  158. (let* ((basename (file-name-sans-extension
  159. (file-name-nondirectory file)))
  160. (fullname (file-truename
  161. (expand-file-name file
  162. "/home/xc_tftpboot/pxe")))
  163. (original (string-match "original" fullname))
  164. (attributes (file-attributes fullname)))
  165. (org-set-property basename (format "%d %s%s"
  166. (nth 7 attributes)
  167. (format-time-string
  168. "%b %d %H:%M" (nth 5 attributes))
  169. (if original
  170. " (original)"
  171. " (replacement)"))))))))
  172. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  173. ;; tracked shell commands ;;
  174. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  175. (defvar tracked-recent-shell-commands nil
  176. "Recent shell commands.
  177. Used while selecting a shell command to log.")
  178. (defun tracked-read-recent-shell-command (prompt)
  179. "Select a recent shell command from the history.
  180. Argument PROMPT is passed to `read-from-minibuffer'."
  181. (setq tracked-recent-shell-commands (ring-elements comint-input-ring))
  182. (when nil
  183. (mapcar (function
  184. (lambda (str)
  185. (set-text-properties 0 (length str) nil str)
  186. str))
  187. tracked-recent-shell-commands)
  188. (message "recent-commands are now %S" tracked-recent-shell-commands))
  189. (read-from-minibuffer prompt
  190. (car tracked-recent-shell-commands)
  191. nil ; keymap
  192. nil ; read
  193. 'tracked-recent-shell-commands
  194. ))
  195. (defun tracked-recent-shell-command (command)
  196. "Record a recent shell COMMAND in your work log.
  197. For use from the comint (shell) buffer."
  198. (interactive
  199. (list
  200. (tracked-read-recent-shell-command "Record shell command: ")))
  201. (save-window-excursion
  202. (save-excursion
  203. (tracking-open-date (tracking-format-current-date))
  204. (goto-char (point-max))
  205. (insert "\n $ " command "\n\n"))))
  206. (require 'shell) ; for shell-mode-map
  207. (define-key shell-mode-map (kbd "C-<return>") 'tracked-recent-shell-command)
  208. ;;;;;;;;;;;;;;;;;;
  209. ;; laptop state ;;
  210. ;;;;;;;;;;;;;;;;;;
  211. (defvar laptop-states nil
  212. "Alist of laptop names to their states.
  213. The states are alists of component names to component states.")
  214. (defun laptop-states-initialize ()
  215. "Initialize `laptop-states' from the tracking file."
  216. (save-excursion
  217. (goto-char (point-max))
  218. (while (re-search-backward ":laptop-\\([-a-z]+\\)--\\([^:]+\\): \\(.+\\)" (point-min) t)
  219. (let ((laptop (match-string-no-properties 1))
  220. (prop (match-string-no-properties 2)))
  221. ;; keep only the one nearest the end of each file, for each
  222. ;; laptop-property combination:
  223. (unless (laptop-state-get laptop prop)
  224. (laptop-state-put laptop
  225. prop
  226. (match-string-no-properties 3)))))))
  227. (defun laptop-state-get (laptop &optional component)
  228. "For LAPTOP, get state COMPONENT."
  229. (let ((state (cdr (assoc laptop laptop-states))))
  230. (if (null component)
  231. state
  232. (and state
  233. (cdr (assoc component state))))))
  234. (defun laptop-state-put (laptop component value)
  235. "For LAPTOP, set state COMPONENT to VALUE."
  236. (let ((state-holder (assoc laptop laptop-states)))
  237. (unless state-holder
  238. (setq state-holder (cons laptop nil)
  239. laptop-states (cons state-holder laptop-states)))
  240. (let ((component-holder (assoc component (cdr state-holder))))
  241. (if component-holder
  242. (rplacd component-holder value)
  243. (rplacd state-holder
  244. (cons (cons component value)
  245. (cdr state-holder)))))))
  246. (defun laptop-state-put-alist (laptop pairs)
  247. "Into the state of LAPTOP write data from PAIRS."
  248. (dolist (pair pairs)
  249. (message "putting %S %S into state for %S" (car pair) (cdr pair) laptop)
  250. (laptop-state-put laptop (car pair) (cdr pair))))
  251. (defun laptop-state-record (laptop)
  252. "Write the state of LAPTOP into the log file, as properties."
  253. (goto-char (point-max))
  254. (let ((state (laptop-state-get laptop)))
  255. (dolist (prop state)
  256. (org-set-property (format "laptop-%s--%s" laptop (car prop))
  257. (cdr prop)))))
  258. ;;;;;;;;;;;;;;;;;;;;;;
  259. ;; tracked commands ;;
  260. ;;;;;;;;;;;;;;;;;;;;;;
  261. (defvar tracked-commands nil
  262. "Alist of commands that can be done through `tracked-command'.
  263. Each element is a cons of:
  264. the command name
  265. a function to prompt for the arguments.")
  266. (defun tracked-command (command &rest args)
  267. "Execute COMMAND with ARGS, noting it in the file in `tracking-org-file'.
  268. Commands are defined in `tracked-commands'."
  269. (interactive
  270. (let* ((command (completing-read "Command: "
  271. tracked-commands))
  272. (description (assoc command tracked-commands))
  273. (arg-reader (third description))
  274. (args (if arg-reader
  275. (funcall arg-reader command)
  276. nil)))
  277. (cons command args)))
  278. (let* ((description (assoc command tracked-commands))
  279. (directory (second description))
  280. (command-file (if directory
  281. (expand-file-name command
  282. (substitute-in-file-name directory))
  283. command))
  284. (command-string (mapconcat
  285. 'identity
  286. (cons command-file args)
  287. " "))
  288. (result-string (shell-command-to-string command-string))
  289. (state-updater (fourth (assoc command tracked-commands))))
  290. (message "Result is %s" result-string)
  291. (tracking-open-date (format-time-string "%Y_%m_%d"))
  292. (goto-char (point-max))
  293. (insert "**** Command " command-string "\n ")
  294. (if (> (length result-string) 0)
  295. (progn
  296. (insert "Result:\n")
  297. (let ((result-start (point)))
  298. (insert result-string)
  299. (indent-rigidly result-start (point) 5))
  300. (insert "\n"))
  301. (message "No output from command %s" command-string))
  302. (message "state-updater is %S" state-updater)
  303. (when state-updater
  304. (apply state-updater args))))
  305. (defun define-tracked-command (name &optional directory arg-reader state-updater)
  306. "Define a tracked command with NAME ARG-READER STATE-UPDATER.
  307. Optional argument DIRECTORY is where the command is defined."
  308. (let ((holder (assoc name tracked-commands))
  309. (definition (list directory arg-reader state-updater)))
  310. (if holder
  311. (rplacd holder definition)
  312. (setq tracked-commands
  313. (cons (cons name definition)
  314. tracked-commands)))))
  315. ;;;;;;;;;;;;;;;;;;;
  316. ;; Build results ;;
  317. ;;;;;;;;;;;;;;;;;;;
  318. (defvar tracked-compile-messages-to-tags-alist
  319. '(("exited abnormally" . "build_failed")
  320. ;; todo: find more of these
  321. ("finished" . "built"))
  322. "Alist matching compilation end messages to tags.")
  323. (defun tracked-compile-messages-to-tags (message)
  324. "Find a tag for MESSAGE."
  325. (catch 'found
  326. (dolist (pattern-pair tracked-compile-messages-to-tags-alist)
  327. (when (string-match (car pattern-pair) message)
  328. (throw 'found (cdr pattern-pair))))))
  329. (defun tracked-compile-compilation-finish-function (buffer message)
  330. "Function to run when compilation finishes.
  331. Arguments BUFFER and MESSAGE are for the compilation finish function calling protocol."
  332. (let ((tag (tracked-compile-messages-to-tags message)))
  333. (when tag
  334. (set-buffer buffer)
  335. (when tracking-org-file
  336. ;; todo: I'd like to grab the error messages here, and put them in the journal
  337. (save-excursion
  338. (find-file-other-window tracking-org-file)
  339. ;; (message "Setting overlay, tracked-compile-mode-current-build-overlay is %S" tracked-compile-mode-current-build-overlay)
  340. (save-excursion
  341. (goto-char (point-max))
  342. (org-back-to-heading)
  343. (org-toggle-tag tag t)
  344. (tracked-compile-setup-overlay)))))))
  345. ;;;;;;;;;;;;;;;;;;;;;;;;;;
  346. ;; Flags in source code ;;
  347. ;;;;;;;;;;;;;;;;;;;;;;;;;;
  348. (defvar tracked-compile-flagged-files nil
  349. "A list of the files containing flags.")
  350. (make-variable-buffer-local 'tracked-compile-flagged-files)
  351. (defun tracked-compile-get-flagged-files (&optional force git-directory)
  352. "Return a list of files with flags.
  353. A cached value is used if available unless optional FORCE is non-nil.
  354. Optional GIT-DIRECTORY says where to look, to save time on calculating it."
  355. (interactive (list t nil))
  356. (if (and (not force)
  357. (consp tracked-compile-flagged-files)
  358. (file-exists-p (car tracked-compile-flagged-files)))
  359. tracked-compile-flagged-files
  360. (unless (stringp git-directory)
  361. (setq git-directory (ancestor-directory-containing default-directory "git")))
  362. (setq tracked-compile-flagged-files
  363. (mapcar (lambda (name) (expand-file-name name git-directory))
  364. (nreverse
  365. (cdr
  366. (nreverse
  367. (split-string
  368. (shell-command-to-string
  369. (format "cd %s; find git -name \"*.c\" -exec grep -l \"flag:\" {} \\;"
  370. git-directory))
  371. "\n"))))))))
  372. (defun tracked-compile-get-flags (&optional force git-directory)
  373. "Return a list of flagged #if directives that are on.
  374. A cached value for the files to look in is used if available unless
  375. optional FORCE is non-nil.
  376. Optional GIT-DIRECTORY says where to look, to save time on calculating it."
  377. (unless (or force
  378. (and (stringp git-directory)
  379. (file-directory-p git-directory)))
  380. (setq git-directory (ancestor-directory-containing default-directory "git")))
  381. (message "Getting flags for %S, flagged files are %S" git-directory (tracked-compile-get-flagged-files force git-directory))
  382. (nreverse
  383. (cdr
  384. (nreverse
  385. (mapcar (function (lambda (raw)
  386. (if (string-match "flag:\\s-*\\([a-z_]+\\)" raw)
  387. (match-string 1 raw)
  388. raw)))
  389. (split-string
  390. (let ((command (format "cd %s; grep \"flag:\" %s | grep \"#if 1\""
  391. git-directory
  392. (mapconcat 'identity (tracked-compile-get-flagged-files force git-directory) " "))))
  393. (message "Getting flags using command: %s" command)
  394. (shell-command-to-string command))
  395. "\n"))))))
  396. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  397. ;; Winding to specified state ;;
  398. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  399. (defun tracked-compile-get-applied-patches (&optional git-directory)
  400. "Return a list of the currently applied patches.
  401. Optional GIT-DIRECTORY says where to look, to save time on calculating it."
  402. (unless (stringp git-directory)
  403. (setq git-directory (ancestor-directory-containing default-directory "git")))
  404. (split-string (shell-command-to-string (format "cd %s/git; guilt applied" git-directory)) "\n"))
  405. (defun tracked-compile-patch-applied-p (patch-name &optional git-directory)
  406. "Return whether PATCH-NAME is currently applied.
  407. Optional GIT-DIRECTORY says where to look, to save time on calculating it."
  408. (member patch-name (tracked-compile-get-applied-patches git-directory)))
  409. (defun tracked-compile-build-around-point ()
  410. "Return the build name for the text around point."
  411. (save-excursion
  412. (let* ((raw-name (org-get-heading t))
  413. (name (substring raw-name 6)))
  414. (set-text-properties 0 (length name) nil name)
  415. name)))
  416. (defun tracked-compile-versions-list ()
  417. "Return a list of the versions described in this buffer.
  418. Each one is consed with nil, to make it suitable for completion."
  419. (let ((result nil))
  420. (save-excursion
  421. (goto-char (point-min))
  422. (while (re-search-forward "^\\*\\*\\*\\* Build \\([^ ]+\\)" (point-max) t)
  423. (push (match-string-no-properties 1)
  424. result)))
  425. result))
  426. (defun revert-buffers-visiting-files-below (directory)
  427. "Revert any buffers in files within DIRECTORY and its subdirectories."
  428. (setq directory (concat "^" (regexp-quote (file-truename directory))))
  429. (save-excursion
  430. (dolist (buffer (buffer-list))
  431. (let ((filename (buffer-file-name buffer)))
  432. (when filename
  433. (setq filename (file-truename filename))
  434. (when (string-match directory filename)
  435. (set-buffer buffer)
  436. (revert-buffer)))))))
  437. (defun tracked-compile-rollback-to-version (version)
  438. "Go back to, and build, VERSION."
  439. (interactive
  440. '(list (completing-read "Rollback to version: "
  441. (tracked-compile-versions-list)
  442. nil
  443. t)))
  444. (goto-char (point-min))
  445. (if (re-search-forward (format "^\\*\\*\\*\\* Build %s" version) (point-max) t)
  446. (let ((git-directory (ancestor-directory-containing
  447. (file-name-directory (org-entry-get (point) "compile-file"))
  448. "git"))
  449. (recorded-compile-command (org-entry-get (point) "compile-command")))
  450. (tracked-compile-setup-overlay)
  451. (funcall tracked-compile-rollback-function git-directory version)
  452. (revert-buffers-visiting-files-below git-directory)
  453. (message "Compiling using retrieved command %s" recorded-compile-command)
  454. (compile recorded-compile-command))
  455. (error "Could not find version %s") version))
  456. (defun tracked-compile-rollback-to-version-around-point ()
  457. "Go back to, and build, the version around point."
  458. (interactive)
  459. (tracked-compile-rollback-to-version
  460. (tracked-compile-build-around-point)))
  461. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  462. ;; Overlay to indicate latest build ;;
  463. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  464. (defvar tracked-compile-mode-current-build-overlay nil
  465. "Overlay to indicate the current build.")
  466. (make-variable-buffer-local 'tracked-compile-mode-current-build-overlay)
  467. (defun tracked-compile-setup-overlay ()
  468. "Mark the current subtree in an outlined document.
  469. This puts point at the start of the current subtree, and mark at the end."
  470. (interactive)
  471. (if (outline-on-heading-p)
  472. ;; we are already looking at a heading
  473. (beginning-of-line)
  474. ;; else go back to previous heading
  475. (outline-previous-visible-heading 1))
  476. (let* ((beg (point))
  477. (end (progn (outline-end-of-subtree)
  478. (point))))
  479. (message "setting overlay, tracked-compile-mode-current-build-overlay is %S in %S" tracked-compile-mode-current-build-overlay (current-buffer))
  480. (if (overlayp tracked-compile-mode-current-build-overlay)
  481. (move-overlay tracked-compile-mode-current-build-overlay
  482. beg end)
  483. (setq tracked-compile-mode-current-build-overlay
  484. (make-overlay beg end))
  485. (overlay-put tracked-compile-mode-current-build-overlay
  486. 'face
  487. '(background-color . "yellow"))
  488. (when nil
  489. (overlay-put tracked-compile-mode-current-build-overlay
  490. 'display
  491. '(left-fringe vertical-stripes))))
  492. (message "set overlay, tracked-compile-mode-current-build-overlay is %S in %S" tracked-compile-mode-current-build-overlay (current-buffer))))
  493. ;; (define-fringe-bitmap 'vertical-stripes [ 52428 52428 52428 52428 52428 52428 52428 52428 52428 ])
  494. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  495. ;; Functions to call in back-ends ;;
  496. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  497. (defvar tracked-compile-ensure-initialized-snapshot-system-function nil)
  498. (defvar tracked-compile-unrecorded-changes-function nil)
  499. (defvar tracked-compile-snapshot-function nil)
  500. (defvar tracked-compile-rollback-function nil)
  501. (defvar tracked-compile-list-snapshots-function nil)
  502. ;;;;;;;;;;;;;;;;;;;;;;
  503. ;; Back ends: darcs ;;
  504. ;;;;;;;;;;;;;;;;;;;;;;
  505. (defun tracked-compile-ensure-initialized-snapshot-system-function-darcs (git-dir)
  506. "Initialize compile version tracking for GIT-DIR using darcs."
  507. (unless (file-directory-p (expand-file-name "_darcs" git-dir))
  508. (message "Setting up darcs for %s" git-dir)
  509. (setenv "EMAIL" user-mail-address)
  510. (shell-command
  511. (format
  512. "cd %s; darcs initialize; find . -name \"*.c\" -o -name \"*.h\" | xargs darcs add ; darcs record --all -m \"Initial adding\""
  513. git-dir))))
  514. (defun tracked-compile-unrecorded-changes-function-darcs (git-dir)
  515. "Return the unrecorded changes for GIT-DIR."
  516. (shell-command-to-string (format "cd %s/git; darcs whatsnew --unified" git-dir)))
  517. (defun tracked-compile-snapshot-function-darcs (git-dir tracking-name)
  518. "Take a snapshot for GIT-DIR called TRACKING-NAME using darcs."
  519. (let ((command (format "cd %s/git; darcs record --all -m \"%s-c\"; darcs tag -m \"%s\"" git-dir tracking-name tracking-name)))
  520. (message "Taking snapshot by doing: %s" command)
  521. (shell-command command))
  522. (when t
  523. (message "Changes list is now %S" (tracked-compile-list-snapshots-function-darcs git-dir))))
  524. (defun tracked-compile-rollback-function-darcs (git-dir snapshot-name)
  525. "Rollback GIT-DIR to SNAPSHOT-NAME using darcs."
  526. (let ((command (format
  527. ;; todo: I get "Shall I rollback this patch? (1/7) [ynWvplxdaqjk], or ? for help: darcs: promptCharFancy: unexpected end of input"
  528. ;; the "--all" means don't prompt for whether to do each one
  529. "cd %s; darcs rollback --all --from-patch \"%s\" -m \"Rollback to %s\""
  530. git-dir snapshot-name snapshot-name)))
  531. (message "Doing rollback using command: %s" command)
  532. (shell-command command)))
  533. (defun tracked-compile-list-snapshots-function-darcs (git-dir)
  534. "Get the snapshots list for GIT-DIR using darcs."
  535. (split-string
  536. (shell-command-to-string (format "cd %s; darcs show tags" git-dir))
  537. "\n" t))
  538. ;;;;;;;;;;;;;;;;;;;;;;
  539. ;; Back ends: guilt ;;
  540. ;;;;;;;;;;;;;;;;;;;;;;
  541. (defun tracked-compile-snapshot-function-guilt (git-dir tracking-name)
  542. "Take a snapshot of GIT-DIR with TRACKING-NAME using guilt."
  543. (let ((command (format "cd %s/git; guilt new -f %s" git-dir tracking-name)))
  544. (message "Taking snapshot by doing: %s" command)
  545. (shell-command command)))
  546. (defun tracked-compile-rollback-function-guilt (git-dir snapshot-name)
  547. "Rollback GIT-DIR to SNAPSHOT-NAME, using guilt."
  548. (let ((guilt-command
  549. (if (tracked-compile-patch-applied-p snapshot-name git-directory)
  550. (format "cd %s/git; guilt pop %s; guilt push;" git-directory snapshot-name)
  551. (format "cd %s/git; guilt push %s" git-directory snapshot-name)
  552. )))
  553. (message "Doing %s" guilt-command)
  554. (shell-command guilt-command)))
  555. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  556. ;; Setup for specific back-ends ;;
  557. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  558. (defun tracked-compile-use-darcs ()
  559. "Set the tracked compilation system up to use darcs for tracking."
  560. (interactive)
  561. (setq tracked-compile-snapshot-function 'tracked-compile-snapshot-function-darcs
  562. tracked-compile-unrecorded-changes-function 'tracked-compile-unrecorded-changes-function-darcs
  563. tracked-compile-ensure-initialized-snapshot-system-function 'tracked-compile-ensure-initialized-snapshot-system-function-darcs
  564. tracked-compile-rollback-function 'tracked-compile-rollback-function-darcs
  565. tracked-compile-list-snapshots-function 'tracked-compile-list-snapshots-function-darcs))
  566. (defun tracked-compile-use-guilt ()
  567. "Set the tracked compilation system up to use git and guilt for tracking."
  568. (interactive)
  569. (setq tracked-compile-snapshot-function 'tracked-compile-snapshot-function-guilt
  570. tracked-compile-rollback-function 'tracked-compile-rollback-function-guilt))
  571. ;;;;;;;;;;;;;;;;
  572. ;; Major mode ;;
  573. ;;;;;;;;;;;;;;;;
  574. (defun narrowest-margin (text-lines)
  575. "Return the narrowest margin width in TEXT-LINES.
  576. TEXT-LINES may be either a string or a list of strings."
  577. (when (stringp text-lines)
  578. (setq text-lines (split-string text-lines "\n")))
  579. (apply 'min (mapcar (function
  580. (lambda (line)
  581. (or (string-match "\\S-" line)
  582. 0)))
  583. text-lines)))
  584. (defun tracked-compile-yank (&optional arg)
  585. "Like `yank', but if at the left margin, indent to suit this mode.
  586. Optional argument ARG is passed on to `yank'."
  587. (interactive "*P")
  588. ;; if off the margin but only whitespace is on the line, get rid of
  589. ;; the whitespace first
  590. (let ((initial-column (current-column)))
  591. (when (save-excursion
  592. (beginning-of-line)
  593. (looking-at "^\\s-*$"))
  594. (delete-region (line-beginning-position) (line-end-position)))
  595. (let ((at-margin (bolp)))
  596. (yank arg)
  597. ;; if it was a multi-line insert at the margin, adjust the
  598. ;; indentation
  599. (when (and at-margin
  600. (string-match "\n" (current-kill 0 t)))
  601. (put-text-property (region-beginning) (region-end) 'blockquote t)
  602. (indent-rigidly (region-beginning)
  603. (region-end)
  604. (- 8 (narrowest-margin (current-kill 0 t))))
  605. (unless (bolp)
  606. (insert "\n")))
  607. (indent-to initial-column))))
  608. (defvar tracked-compile-mode-map
  609. (let ((map (make-sparse-keymap)))
  610. (define-key map [?\C-c ?c] 'tracked-compile-build-around-point)
  611. (define-key map [?\C-c ?d] 'tracking-open-date)
  612. (define-key map [?\C-y] 'tracked-compile-yank)
  613. map)
  614. "Map for `tracked-compile-mode'.")
  615. (define-derived-mode tracked-compile-mode org-mode "Tracked-compilations"
  616. "Major mode for tracking compiled versions."
  617. (laptop-states-initialize))
  618. (provide 'tracked-compile)
  619. ;; (tracked-compile-use-git)
  620. (tracked-compile-use-darcs)
  621. (find-file tracking-org-file)
  622. ;;; tracked-compile.el ends here