Function: byte-compile-file

byte-compile-file is an autoloaded, interactive and byte-compiled function defined in bytecomp.el.gz.

Signature

(byte-compile-file FILENAME)

Documentation

Compile a file of Lisp code named FILENAME into a file of byte code.

The output file's name is generated by passing FILENAME to the function byte-compile-dest-file(var)/byte-compile-dest-file(fun) (which see). The value is non-nil if there were no errors, nil if errors. If the file sets the file variable no-byte-compile, it is not compiled, any existing output file is removed, and the return value is no-byte-compile.

See also emacs-lisp-byte-compile-and-load.

View in manual

Probably introduced at or before Emacs version 15.

Key Bindings

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/bytecomp.el.gz
;;;###autoload
(defun byte-compile-file (filename &optional load)
  "Compile a file of Lisp code named FILENAME into a file of byte code.
The output file's name is generated by passing FILENAME to the
function `byte-compile-dest-file' (which see).
The value is non-nil if there were no errors, nil if errors.
If the file sets the file variable `no-byte-compile', it is not
compiled, any existing output file is removed, and the return
value is `no-byte-compile'.

See also `emacs-lisp-byte-compile-and-load'."
  (declare (advertised-calling-convention (filename) "28.1"))
  (interactive
   (let ((file buffer-file-name)
	 (file-dir nil))
     (and file
	  (derived-mode-p 'emacs-lisp-mode)
	  (setq file-dir (file-name-directory file)))
     (list (read-file-name (if current-prefix-arg
			       "Byte compile and load file: "
			     "Byte compile file: ")
			   file-dir buffer-file-name nil)
	   current-prefix-arg)))
  ;; Expand now so we get the current buffer's defaults
  (setq filename (expand-file-name filename))

  ;; If we're compiling a file that's in a buffer and is modified, offer
  ;; to save it first.
  (or noninteractive
      (let ((b (get-file-buffer (expand-file-name filename))))
	(if (and b (buffer-modified-p b)
		 (y-or-n-p (format "Save buffer %s first? " (buffer-name b))))
	    (with-current-buffer b (save-buffer)))))

  ;; Force logging of the file name for each file compiled.
  (setq byte-compile-last-logged-file nil)
  (let ((byte-compile-current-file filename)
        (byte-compile-current-group nil)
	(set-auto-coding-for-load t)
        (byte-compile--seen-defvars nil)
        (byte-compile--known-dynamic-vars
         (byte-compile--load-dynvars (getenv "EMACS_DYNVARS_FILE")))
	target-file input-buffer output-buffer
	byte-compile-dest-file byte-compiler-error-flag)
    (setq target-file (byte-compile-dest-file filename))
    (setq byte-compile-dest-file target-file)
    (with-current-buffer
	;; It would be cleaner to use a temp buffer, but if there was
	;; an error, we leave this buffer around for diagnostics.
	;; Its name is documented in the lispref.
	(setq input-buffer (get-buffer-create
			    (concat " *Compiler Input*"
				    (if (zerop byte-compile-level) ""
				      (format "-%s" byte-compile-level)))))
      (erase-buffer)
      (setq buffer-file-coding-system nil)
      ;; Always compile an Emacs Lisp file as multibyte
      ;; unless the file itself forces unibyte with -*-coding: raw-text;-*-
      (set-buffer-multibyte t)
      (insert-file-contents filename)
      ;; Mimic the way after-insert-file-set-coding can make the
      ;; buffer unibyte when visiting this file.
      (when (or (eq last-coding-system-used 'no-conversion)
		(eq (coding-system-type last-coding-system-used) 5))
	;; For coding systems no-conversion and raw-text...,
	;; edit the buffer as unibyte.
	(set-buffer-multibyte nil))
      ;; Run hooks including the uncompression hook.
      ;; If they change the file name, then change it for the output also.
      (let ((buffer-file-name filename)
            (dmm (default-value 'major-mode))
            ;; Ignore unsafe local variables.
            ;; We only care about a few of them for our purposes.
            (enable-local-variables :safe)
            (enable-local-eval nil))
        (unwind-protect
            (progn
              (setq-default major-mode 'emacs-lisp-mode)
              ;; Arg of t means don't alter enable-local-variables.
              (delay-mode-hooks (normal-mode t)))
          (setq-default major-mode dmm))
        ;; There may be a file local variable setting (bug#10419).
        (setq buffer-read-only nil
              filename buffer-file-name))
      ;; Don't inherit lexical-binding from caller (bug#12938).
      (unless (or (local-variable-p 'lexical-binding)
                  bytecomp--inhibit-lexical-cookie-warning)
        (let ((byte-compile-current-buffer (current-buffer)))
          (displaying-byte-compile-warnings
           (byte-compile-warn-x
            (position-symbol 'a (point-min))
            "file has no `lexical-binding' directive on its first line")))
        (setq-local lexical-binding nil))
      ;; Set the default directory, in case an eval-when-compile uses it.
      (setq default-directory (file-name-directory filename)))
    ;; Check if the file's local variables explicitly specify not to
    ;; compile this file.
    (if (with-current-buffer input-buffer no-byte-compile)
	(progn
	  ;; (message "%s not compiled because of `no-byte-compile: %s'"
	  ;;       (byte-compile-abbreviate-file filename)
	  ;;       (with-current-buffer input-buffer no-byte-compile))
	  (when (and target-file (file-exists-p target-file))
	    (message "%s deleted because of `no-byte-compile: %s'"
		     (byte-compile-abbreviate-file target-file)
		     (buffer-local-value 'no-byte-compile input-buffer))
	    (condition-case nil (delete-file target-file) (error nil)))
	  ;; We successfully didn't compile this file.
	  'no-byte-compile)
      (when byte-compile-verbose
	(message "Compiling %s..." filename))
      ;; It is important that input-buffer not be current at this call,
      ;; so that the value of point set in input-buffer
      ;; within byte-compile-from-buffer lingers in that buffer.
      (setq output-buffer
	    (save-current-buffer
	      (let ((byte-compile-level (1+ byte-compile-level)))
                (byte-compile-from-buffer input-buffer))))
      (if byte-compiler-error-flag
	  nil
	(when byte-compile-verbose
	  (message "Compiling %s...done" filename))
	(kill-buffer input-buffer)
	(with-current-buffer output-buffer
          (when (and target-file
                     (or (not byte-native-compiling)
                         (and byte-native-compiling byte+native-compile)))
	    (goto-char (point-max))
	    (insert "\n")			; aaah, unix.
	    (cond
	     ((and (file-writable-p target-file)
		   ;; We attempt to create a temporary file in the
		   ;; target directory, so the target directory must be
		   ;; writable.
		   (file-writable-p
		    (file-name-directory
		     ;; Need to expand in case TARGET-FILE doesn't
		     ;; include a directory (Bug#45287).
		     (expand-file-name target-file))))
              (if byte-native-compiling
                  ;; Defer elc production.
                  (setf byte-to-native-output-buffer-file
                        (cons (current-buffer) target-file))
                (byte-write-target-file (current-buffer) target-file))
	      (or noninteractive
		  byte-native-compiling
		  (message "Wrote %s" target-file)))
             ((file-writable-p target-file)
              ;; In case the target directory isn't writable (see e.g. Bug#44631),
              ;; try writing to the output file directly.  We must disable any
              ;; code conversion here.
              (let ((coding-system-for-write 'no-conversion))
                (with-file-modes (logand (default-file-modes) #o666)
                  (write-region (point-min) (point-max) target-file nil 1)))
              (or noninteractive (message "Wrote %s" target-file)))
	     (t
	      ;; This is just to give a better error message than write-region
	      (let ((exists (file-exists-p target-file)))
                (signal (if exists 'file-error 'file-missing)
                        (list "Opening output file"
			      (if exists
				  "Cannot overwrite file"
                                "Directory not writable or nonexistent")
			      target-file))))))
          (unless byte-native-compiling
	    (kill-buffer (current-buffer))))
	(if (and byte-compile-generate-call-tree
		 (or (eq t byte-compile-generate-call-tree)
		     (y-or-n-p (format "Report call tree for %s? "
                                       filename))))
	    (save-excursion
	      (display-call-tree filename)))
        (let ((gen-dynvars (getenv "EMACS_GENERATE_DYNVARS")))
          (when (and gen-dynvars (not (equal gen-dynvars ""))
                     byte-compile--seen-defvars)
            (let ((dynvar-file (concat target-file ".dynvars"))
                  (print-symbols-bare t))
              (message "Generating %s" dynvar-file)
              (with-temp-buffer
                (dolist (var (delete-dups byte-compile--seen-defvars))
                  (insert (format "%S\n" (cons var filename))))
                (write-region (point-min) (point-max) dynvar-file)))))
	(if load
            (load target-file))
	t))))