【问题标题】:How to set the default-directory of compilation in Emacs?如何在 Emacs 中设置编译的默认目录?
【发布时间】:2012-01-27 17:58:16
【问题描述】:

我在 Emacs 下编写 OCaml,工作文件夹中有一个 makefile,还有几个包含 .ml 文件的子文件夹。如果我启动M-x compilemakemakefile 的缓冲区上工作正常,但在.ml 文件的缓冲区上不起作用,它会给我一个错误:

-*- mode: compilation; default-directory: "..." -*-
Compilation started at Fri Jan 27 18:51:35

make -k
make: *** No targets specified and no makefile found.  Stop.

Compilation exited abnormally with code 2 at Fri Jan 27 18:51:35

这是可以理解的,因为默认目录是不包含makefile 的子文件夹。有谁知道如何将makefile的文件夹设置为默认编译目录?

【问题讨论】:

    标签: emacs compilation ocaml


    【解决方案1】:

    您可以使用正确的参数调用make

    make -C .. -k
    

    其中.. 是您的Makefile 的路径

    【讨论】:

    • 哈,一直在寻找这个问题,而你的答案是事后看来最明显的一个,我更喜欢它而不是 define-your-own -custom-function-with-a-hook-and-change-it-when-you-need-a-different-path 答案在上面。谢谢!
    • 但是,构建工具将打印错误消息,其路径相对于它们正在运行的目录,如果与 *compilation* 缓冲区的目录不匹配,请单击 file:line在 Emacs 中不起作用?
    • 啊哈! Emacs 解析make: Entering directory '..' 行(但仅在英语语言环境中),因此单击文件:行链接仍然有效!如果不使用 make,您可以手动打印它们:编译命令:echo "make: Entering directory '$DIR'"; cd $DIR; ...
    • 这应该是公认的答案,因为它更方便且实际工作,与上述问题相反。
    【解决方案2】:

    您可以通过编写一个(临时)设置default-directory 并调用compile 的函数在emacs 中控制它。

    (defun compile-in-parent-directory ()
      (interactive)
      (let ((default-directory
              (if (string= (file-name-extension buffer-file-name) "ml")
                  (concat default-directory "..")
                default-directory))))
      (call-interactively #'compile))
    

    使用compile-in-parent-directory 时,所有ml 文件都将在它们所在的父目录中编译。当然,如果它们嵌套得更深,您可以更改逻辑以反映这一点。事实上,有一个version on the EmacsWiki 会搜索父目录,直到找到一个makefile。我在写完这个答案后发现了这个,否则我只会把你指向那里。 叹息。我的方法的好处是它不特定于make,因此您可以对其他命令使用相同的“技巧”。

    如果您确切地知道您想要的命令是什么,您也可以将 compile 调用更改为非交互式。如果它绑定到适当模式挂钩中的键,这将特别有效。

    【讨论】:

    • 感谢您的回答...我已将您的代码添加到.emacs,但在M-x 之后找不到compile-in-parent-directory
    • 我已将 EmacsWiki 上的代码放到 .emacs,按 F5 会出现错误:Symbol's function definition is void: loop
    • 要使函数可用于与 M-x 进行交互调用,您必须将 (interactive) 作为第一个语句添加到函数中。
    • 对不起,我已经修好了。您需要为 EmacsWiki 上的代码添加一个 (require 'cl) 以获取 loop 的定义。
    • 您还需要在 compile 前面插入一个 ' --- 它必须是 (call-interactively 'compile)。我会编辑它,但堆栈溢出需要至少 6 个字符进行编辑。
    【解决方案3】:

    我使用这样的脚本,它允许我从任何子目录运行 make(假设您处于类似 posix 的环境中)。只需将此脚本作为“sub_make.sh”之类的内容放在您的 PATH 中,并以与调用 make 相同的方式调用它:

    #!/bin/bash
    
    # search for project base
    INIT_DIR=`pwd`
    while [ "$PWD" != "/" ] ; do
      if [ -e "makefile" ] ; then
        break
      fi
    
      cd ..
    done
    
    if [ ! -e "makefile" ] ; then
      echo "Couldn't find 'makefile'!"
      exit 1
    fi
    
    # indicate where we are now
    echo "cd "`pwd`
    echo make "$@"
    
    # now run make for real
    exec make "$@"
    

    【讨论】:

    • 您可以按如下方式修改:if [ -e "makefile" ] || [ -e "Makefile" ] || [ -e "GNUmakefile" ] ; then 以允许其他标准 makefile 名称
    【解决方案4】:

    这就是我的一些配置中的内容:)

    (defun* get-closest-pathname (&optional (max-level 3) (file "Makefile"))
      (let* ((root (expand-file-name "/"))
             (level 0)
             (dir (loop
                   for d = default-directory then (expand-file-name ".." d)
                     do (setq level (+ level 1))
                   if (file-exists-p (expand-file-name file d))
                     return d
                   if (> level max-level)
                     return nil
                   if (equal d root)
                     return nil)))
        (if dir
            (expand-file-name file dir)
          nil)))
    
    (add-hook 'c-mode-hook
              (lambda ()
                (unless (file-exists-p "Makefile")
                  (set (make-local-variable 'compile-command)
                       (let ((file (file-name-nondirectory buffer-file-name))
                             (mkfile (get-closest-pathname)))
                         (if mkfile
                             (progn (format "cd %s; make -f %s"
                                (file-name-directory mkfile) mkfile))
                           (format "%s -c -o %s.o %s %s %s"
                                   (or (getenv "CC") "gcc")
                                   (file-name-sans-extension file)
                                   (or (getenv "CPPFLAGS") "-DDEBUG=9")
                                   (or (getenv "CFLAGS") "-ansi -pedantic -Wall -g")
                                   file)))))))
    

    【讨论】:

    • 感谢您的回答...我已将您的代码添加到.emacs,但在启动emacs后,它给了我一个警告Warning (initialization): An error occurred while loading '~/.emacs': Symbol's function definition is void: defun*
    • hm... 尝试在此函数之前添加 (require 'cl)。
    • 感谢 Bad_ptr。我使用您的代码有一个命令,该命令可以在任何模式下在主目录中执行 make 命令,而不仅仅是 C,如果缓冲区处于目录模式下也是如此。如果它没有找到 Makefile 的主目录,我希望它不做任何事情。不幸的是,我的函数仍然执行 make 无论如何。你能建议吗? (global-set-key [f1] (lambda () (interactive) (compile (let ((mkfile (get-closest-pathname)))) (if mkfile (format "cd %s; make" (file-name-directory (get-closest-pathname)))) ))))
    • @Ammari 试试这个:(global-set-key [f1] (lambda () (interactive) (let ((mkfile (get-closest-pathname))) (if mkfile (compile (format "cd %s; make " (file-name-directory mkfile)))))))
    • @Ammari 好的。我编辑了get-closest-pathname 功能代码。试试这个版本。
    【解决方案5】:

    Matthias Puech 在项目根目录中有一个solution 涉及.dir-local 文件:

    ((nil . ((eval . (setq default-directory 
                      (locate-dominating-file buffer-file-name
                       ".dir-locals.el")
    )))))
    

    大概也可以使用类似:(shell-command-to-string "git rev-parse --show-toplevel") 作为最里面的位。

    【讨论】:

    • No no no :/ 这会破坏一切,包括find-file
    【解决方案6】:

    不是一个完全通用的解决方案 w.r.t makefile 位置,而是在此处添加它以供后代使用,因为它解决了我的特定用例。

    如果您使用projectile 并且您的makefile 始终位于项目目录的根目录中,那么您可以使用projectile-compile-project

    (在我的情况下,我想对我的项目进行 lint,因此调用 (compile "flake8") 只会从当前缓冲区的目录向下剥落,而我真正想要的是整个项目的 linting。projectile-compile-project 实现了这一点。)

    【讨论】:

      猜你喜欢
      • 2012-12-07
      • 2014-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多