Home | History | Annotate | Download | only in emacs
      1 ;;; android-compile.el --- Compile the Android source tree.
      2 ;;
      3 ;; Copyright (C) 2009 The Android Open Source Project
      4 ;;
      5 ;; Licensed under the Apache License, Version 2.0 (the "License");
      6 ;; you may not use this file except in compliance with the License.
      7 ;; You may obtain a copy of the License at
      8 ;;
      9 ;;      http://www.apache.org/licenses/LICENSE-2.0
     10 ;;
     11 ;; Unless required by applicable law or agreed to in writing, software
     12 ;; distributed under the License is distributed on an "AS IS" BASIS,
     13 ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ;; See the License for the specific language governing permissions and
     15 ;; limitations under the License.
     16 
     17 ;;; Commentary:
     18 ;;
     19 ;; Helper functions to compile Android file within emacs.
     20 ;; This module ignores 'build/envsetup.sh' and any enviroment set by the
     21 ;; 'lunch' shell function.
     22 ;; Instead it relies solely on 'buildspec.mk', remember that when you
     23 ;; switch configuration.
     24 ;;
     25 ;; The only interactive function is 'android-compile'.
     26 ;; In your .emacs load this file (e.g (require 'android-compile)) then:
     27 ;;
     28 ;;   (add-hook 'c++-mode-hook 'android-compile)
     29 ;;   (add-hook 'java-mode-hook 'android-compile)
     30 ;; and/or
     31 ;;   (global-set-key [f9] 'android-compile)
     32 ;;
     33 ;;
     34 ;; TODO: Maybe we could cache the result of the compile function in
     35 ;; buffer local vars.
     36 
     37 ;;; Code:
     38 
     39 (require 'compile)
     40 (require 'android-common)
     41 
     42 ;; No need to be customized.
     43 (defvar android-compile-ignore-re
     44   "\\(^\\(\\sw\\|[/_]\\)+\\(Makefile\\|\\.mk\\):[0-9]+:.*warning\\)\\|\\(^/bin/bash\\)"
     45   "RE to match line to suppress during a compilation.
     46 During the compilation process line matching the above will be
     47 suppressed if `android-compilation-no-buildenv-warning' is non nil.")
     48 
     49 (defun android-makefile-exists-p (directory)
     50   "Return t if an Android makefile exists in DIRECTORY."
     51   ; Test for Android.mk first: more likely.
     52   (or (file-exists-p (concat directory "Android.mk"))
     53       (file-exists-p (concat directory "Makefile"))))
     54 
     55 (defun android-find-makefile (topdir)
     56   "Ascend the current path until an Android makefile is found.
     57 Makefiles are named Android.mk except in the root directory where
     58 the file is named Makefile.
     59 TOPDIR is the root directory of the build.
     60 Return a list with 2 elements (MAKEFILE_PATH IS_ROOT_MAKEFILE).
     61 MAKEFILE_PATH is the relative path of the makefile wrt TOPDIR.
     62 Signal an error if no Makefile was found."
     63   ;; TODO: Could check that topdir is the start of default-directory.
     64   (unless (> (length topdir) 2)
     65     (error "Topdir invalid %s for current dir %s" topdir default-directory))
     66   (let ((default-directory default-directory)
     67         file)
     68     ;; Ascend the path.
     69     (while (and (> (length default-directory) (length topdir))
     70                 (not (android-makefile-exists-p default-directory)))
     71       (setq default-directory
     72             (substring default-directory 0
     73                        (string-match "[^/]+/$" default-directory))))
     74 
     75     (when (not (android-makefile-exists-p default-directory))
     76       (error "Not in a valid android tree"))
     77 
     78     (if (string= default-directory topdir)
     79         (list "Makefile" t)
     80       ;; Remove the root dir at the start of the filename
     81       (setq default-directory (substring default-directory (length topdir) nil))
     82       (setq file (concat default-directory "Android.mk"))
     83       (list file nil))))
     84 
     85 ;; This filter is registered as a `compilation-filter-hook' and is
     86 ;; called when new data has been inserted in the compile buffer. Don't
     87 ;; assume that only one line has been inserted, typically more than
     88 ;; one has changed since the last call due to stdout buffering.
     89 ;;
     90 ;; We store in a buffer local variable `android-compile-context' a
     91 ;; list with 2 elements, the process and point position at the end of
     92 ;; the last invocation. The process is used to detect a new
     93 ;; compilation. The point position is used to limit our search.
     94 ;;
     95 ;; On entry (point) is at the end of the last block inserted.
     96 (defun android-compile-filter ()
     97   "Filter to discard unwanted lines from the compilation buffer.
     98 
     99 This filter is registered as a `compilation-filter-hook' and is
    100 called when new data has been inserted in the compile buffer.
    101 
    102 Has effect only if `android-compilation-no-buildenv-warning' is
    103 not nil."
    104   ;; Currently we are looking only for compilation warnings from the
    105   ;; build env. Move this test lower, near the while loop if we
    106   ;; support more than one category of regexp.
    107   (when android-compilation-no-buildenv-warning
    108 
    109     ;; Check if android-compile-context does not exist or if the
    110     ;; process has changed: new compilation.
    111     (let ((proc (get-buffer-process (current-buffer))))
    112       (unless (and (local-variable-p 'android-compile-context)
    113                    (eq proc (cadr android-compile-context)))
    114         (setq android-compile-context (list (point-min) proc))
    115         (make-local-variable 'android-compile-context)))
    116 
    117     (let ((beg (car android-compile-context))
    118           (end (point)))
    119       (save-excursion
    120         (goto-char beg)
    121         ;; Need to go back at the beginning of the line before we
    122         ;; start the search: because of the buffering, the previous
    123         ;; block inserted may have ended in the middle of the
    124         ;; expression we are trying to match. As result we missed it
    125         ;; last time and we would miss it again if we started just
    126         ;; where we left of. By processing the line from the start we
    127         ;; are catching that case.
    128         (forward-line 0)
    129         (while (search-forward-regexp android-compile-ignore-re end t)
    130           ;; Nuke the line
    131           (let ((bol (point-at-bol)))
    132             (forward-line 1)
    133             (delete-region bol (point)))))
    134       ;; Remember the new end for next time around.
    135       (setcar android-compile-context (point)))))
    136 
    137 (defun android-compile ()
    138   "Elisp equivalent of mm shell function.
    139 Walk up the path until a makefile is found and build it.
    140 You need to have a proper buildspec.mk in your top dir.
    141 
    142 Use `android-compilation-jobs' to control the number of jobs used
    143 in a compilation."
    144   (interactive)
    145   (if (android-project-p)
    146       (let* ((topdir (android-find-build-tree-root))
    147              (makefile (android-find-makefile topdir))
    148              (options
    149               (concat " -j " (number-to-string android-compilation-jobs))))
    150         (unless (file-exists-p (concat topdir "buildspec.mk"))
    151           (error "buildspec.mk missing in %s." topdir))
    152         ;; Add-hook do not re-add if already present. The compile
    153         ;; filter hooks run after the comint cleanup (^M).
    154         (add-hook 'compilation-filter-hook 'android-compile-filter)
    155         (set (make-local-variable 'compile-command)
    156              (if (cadr makefile)
    157                  ;; The root Makefile is not invoked using ONE_SHOT_MAKEFILE.
    158                  (concat "make -C " topdir options) ; Build the whole image.
    159                (concat "ONE_SHOT_MAKEFILE=" (car makefile)
    160                        " make -C " topdir options " files ")))
    161         (if (interactive-p)
    162             (call-interactively 'compile)))))
    163 
    164 (provide 'android-compile)
    165 
    166 ;;; android-compile.el ends here
    167