; To use this, ; 1) Add to init.el: ; (setq-default chrome-root "/path/to/chrome/src/") ; (add-to-list 'load-path (concat chrome-root "tools/emacs")) ; (require 'trybot) ; 2) Run on trybot output: ; M-x trybot ; ; To hack on this, ; M-x eval-buffer ; M-x trybot-test-win or M-x trybot-test-mac (defvar chrome-root nil "Path to the src/ directory of your Chrome checkout.") (defun get-chrome-root () (or chrome-root default-directory)) ; Hunt down from the top, case correcting each path component as needed. ; Currently does not keep a cache. Returns nil if no matching file can be ; figured out. (defun case-corrected-filename (filename) (save-match-data (let ((path-components (split-string filename "/")) (corrected-path (file-name-as-directory (get-chrome-root)))) (mapc (function (lambda (elt) (if corrected-path (let ((next-component (car (member-ignore-case elt (directory-files corrected-path))))) (setq corrected-path (and next-component (file-name-as-directory (concat corrected-path next-component)))))))) path-components) (if corrected-path (file-relative-name (directory-file-name corrected-path) (get-chrome-root)) nil)))) (defun trybot-fixup-win () "Fix up Windows-specific output." ; Fix Windows paths ("d:\...\src\"). (save-excursion ; This regexp is subtle and rather hard to read. :~( ; Use regexp-builder when making changes to it. (while (re-search-forward (concat ; First part: path leader, either of the form ; e:\...src\ or ..\ "\\(^.:\\\\.*\\\\src\\\\\\|\\.\\.\\\\\\)" ; Second part: path, followed by error message marker. "\\(.*?\\)[(:]") nil t) (replace-match "" nil t nil 1) ; Line now looks like: ; foo\bar\baz.cc error message here ; We want to fixup backslashes in path into forward slashes, ; without modifying the error message - by matching up to the ; first colon above (which will be just beyond the end of the ; filename) we can use the end of the match as a limit. (subst-char-in-region (point) (match-end 0) ?\\ ?/) ; See if we can correct the file name casing. (let ((filename (buffer-substring (match-beginning 2) (match-end 2)))) (if (and (not (file-exists-p filename)) (setq filename (case-corrected-filename filename))) (replace-match filename t t nil 2)))))) (defun trybot-fixup-maclin () "Fix up Mac/Linux output." (save-excursion (while (re-search-forward "^/b/build/[^ ]*/src/" nil t) (replace-match "")))) (defun trybot-fixup (type-hint) "Parse and fixup the contents of the current buffer as trybot output." ; XXX is there something I should so so this stuff doesn't end up on the ; undo stack? ;; Fixup paths. (cd (get-chrome-root)) (goto-char (point-min)) ;; Fix up path references. (cond ((eq type-hint 'win) (trybot-fixup-win)) ((eq type-hint 'mac) (trybot-fixup-maclin)) ((eq type-hint 'linux) (trybot-fixup-maclin)) (t (trybot-fixup-win) (trybot-fixup-maclin))) (compilation-mode)) (defun trybot-get-new-buffer () "Get a new clean buffer for trybot output." ; Use trybot-buffer-name if available; otherwise, "*trybot*". (let ((buffer-name (if (boundp 'trybot-buffer-name) trybot-buffer-name "*trybot*"))) (let ((old (get-buffer buffer-name))) (when old (kill-buffer old))) (get-buffer-create buffer-name))) (defun trybot-fetch (type-hint url) "Fetch a URL and postprocess it as trybot output." (let ((on-fetch-completion (lambda (process state) (switch-to-buffer (process-buffer process)) (when (equal state "finished\n") (trybot-fixup (process-get process 'type-hint))))) (command (concat "curl -s " (shell-quote-argument url) ; Pipe it through the output shortener. (cond ((eq type-hint 'win) (concat " | " (get-chrome-root) "build/sanitize-win-build-log.sh")) ((eq type-hint 'mac) (concat " | " (get-chrome-root) "build/sanitize-mac-build-log.sh")))))) ; Start up the subprocess. (let* ((coding-system-for-read 'utf-8-dos) (buffer (trybot-get-new-buffer)) (process (start-process-shell-command "curl" buffer command))) ; Attach the type hint to the process so we can get it back when ; the process completes. (process-put process 'type-hint type-hint) (set-process-query-on-exit-flag process nil) (set-process-sentinel process on-fetch-completion)))) (defun trybot-test (type-hint filename) "Load the given test data filename and do the trybot parse on it." (let ((trybot-buffer-name "*trybot-test*") (url (concat "file://" (get-chrome-root) "tools/emacs/" filename))) (trybot-fetch type-hint url))) (defun trybot-test-win () "Load the Windows test data and do the trybot parse on it." (interactive) (trybot-test 'win "trybot-windows.txt")) (defun trybot-test-mac () "Load the Mac test data and do the trybot parse on it." (interactive) (trybot-test 'mac "trybot-mac.txt")) (defun trybot-test-linux () "Load the Linux test data and do the trybot parse on it." (interactive) (trybot-test 'linux "trybot-linux.txt")) (defun trybot (url) "Fetch a trybot URL and fix up the output into a compilation-mode buffer." (interactive "sURL to trybot stdout (leave empty to use clipboard): ") ;; Yank URL from clipboard if necessary. (when (= (length url) 0) (with-temp-buffer (clipboard-yank) (setq url (buffer-string)))) ;; Append /text to the URL to get plain text output in the common ;; case of getting a URL to the HTML build log. (when (equal "stdio" (car (last (split-string url "/")))) (setq url (concat url "/text"))) (let ((type-hint (cond ((string-match "/[Ww]in" url) 'win) ((string-match "/mac/" url) 'mac) ; Match /linux, /linux_view, etc. ((string-match "/linux" url) 'linux) (t 'unknown)))) (trybot-fetch type-hint url))) (provide 'trybot)