マルチファイルパッケージのバイトコンパイル:「関数が定義されているかどうかは不明です」


7

(ばかげた)パッケージに次のファイルがあるとします。

ファイルtest1.el

;;; test1.el ---                                   

;;; Code:

(defvar test-var1)

(defun test-fun1 (test)
  nil)

(require 'test2 "./test2.el)

(provide 'test1)
;;; test1.el ends here

ファイルtest2.el

;;; test2.el ---  

;;; Code:

(defun test-fun2 ()
  (let ((test test-var1))
    (test-fun1 test)))

(provide 'test2)
;;; test2.el ends here

それなら私は走ります:

emacs -batch -f batch-byte-compile *.el

次の結果が得られます。

Compiling .../test1.el...
Wrote .../test1.elc
Compiling .../test2.el...

In test-fun2:
test2.el:9:15:Warning: reference to free variable `test-var1'

In end of data:
test2.el:14:1:Warning: the function `test-fun1' is not known to be defined.
Wrote .../test2.elc

私はこれらの警告が表示される理由を理解し、それらは単なる警告であることを理解しています。ただし、この種の警告をすべて無視することで、関数名のタイプミスを見逃しやすくなります。

(require 'test2)行を追加test2.elすることで修正できると思いました。ただし、この場合は次のようになります。

Compiling .../test1.el...

In toplevel form:
test1.el:10:1:Error: Recursive `require' for feature `test2'
Compiling .../test2.el...

In toplevel form:
test2.el:5:1:Error: Recursive `require' for feature `test1'

これは不可解です。というのは、require再帰的なロードを回避することが目的であると私は思ったからです。私はそれrequireloadコンパイル時のように動作していると思います。

これらの警告を取り除くための良い(そして安全な)方法は何ですか?

マニュアルは回避策を提供しますが(私はそれを以下より良い回答として投稿します)、最終的には、解決策をかなり自動化したいと思います(必要なすべての関数と変数をリストする必要はありません)すべてのファイル)。

理想的なソリューションは、emacsに組み込まれるか、Caskで提供されます。それが存在しない場合は、もちろん利用可能なものを利用します。

回答:


8

必要について

require再帰的なロードを回避するためのものではなく、反復的なロードを回避するためのものです。したがって、ここでは問題を解決しません。

問題について

(私の意見では)これに取り組む正しい方法は、相互依存を避けることです。

test1あなたの例のファイルは要求する理由がありませんtest2。それが実際のパッケージに当てはまらない場合でも、ファイル間でコードを委任する方法を再設計できます。一般に、ファイル間の相互依存を回避することは可能です。

回避策

  1. 相互依存関係を回避できない場合、マニュアルに は解決策が記載されています。必要な関数/変数ごとに、次のような行を追加する必要があります。

    (declare-function test-fun1 "./test1.el")
    (defvar test-var1)
    
  2. 別のオプションはrequire、ファイルを条件付きでのみにすることです。このようなものをファイルに追加します1

    (defvar test1-is-loading t)
    (unless (and (boundp 'test2-is-loading)
                 test2-is-loading)
      (require 'test2))
    

    そして、このような何かをファイルする2

    (defvar test2-is-loading t)
    (unless (and (boundp 'test1-is-loading)
                 test1-is-loading)
      (require 'test1))
    

現実的な状況は、メインファイルがいくつかのもの(モードマップ、実行可能パス、カスタマイズグループなどの変数)を宣言し、パッケージに一連の機能を提供する他のファイルを必要とし、それらをすべて(少なくとも-mode関数)。この例はおもちゃの例であり、それを使用しtest-fun2ても問題はまったく変わりません。
T. Verron 2014年

1
@ T.Verronはい、私はあなたの例が完全に正確ではないという仮定に基づいて作業し、回避策を提供しました。それでも、私はパッケージをよりよく設計できるという私の発言を支持しています。現実的な例では、すべてをまとめるファイルが変数とグループを定義するファイルでもある必要がある理由はありません。これらの定義(などtest-variables)を含む追加のファイルを作成します。このファイルは他の定義を必要とする必要はありません。
Malabarba 14年

最初の回答で述べたように、最初の回避策は問題なく機能しますが、かなり退屈です。2つ目は有望に聞こえますが、少し複雑になります。ステートメントはでラップeval-when-compileする必要があり、パッケージは変数をnilファイルの最後に設定する必要があります(すべてのファイルが単一セッション)。また、繰り返しの読み込みよりも正確に再帰的な読み込みを回避する方が複雑な理由を示す利点もあります。
T. Verron、2014年

そして、リファクタリングの提案をありがとう、これは確かにうまくいくでしょう。
T. Verron 2014年

4

あなたの例は奇妙です:

  • あなたはrequireでTEST2 終わりのに対し、TEST1のrequire「常に」ファイルの先頭になければなりません。
  • test1はtest2関数を呼び出さないため、動作するためにtest2は必要ありません(したがってrequire、不必要に動作します)require

IOW、あなたはあなたrequireの後ろを持っています。


実際の例でtest1は、もちろんで定義された関数を使用しtest2、パッケージはtest1(オートロードを介して)のみロードされます。代わりに、この答えはコメントではありませんか?私の例の選択が不十分であり、それ以外の点では質問への回答が提供されないことを指摘しているだけです。
T. Verron、2014年

@ T.Verronこの回答は、あなたが提供した特定のケースに答えたものです。それが不十分に選ばれた例であったという事実は彼の責任ではありません。;-)
マラバルバ2014年

どちらの回答もこのリファクタリングポイントに対応しているので、質問ではこれが有効なポイントであると想定する必要があります。あなたには、一般的な問題に対処するという利点に加えて、私の特定のケースに対するコメント内のポインタがありました。これは本当に実際のユースケースを持つ誰にとっても役立つ可能性がありますか?
T. Verron 2014年

そして、もっと短期的な見方をすれば、コメントとして投稿されていれば、質問を編集して(少し)より現実的にすることができたでしょう。ただし、回答として投稿されているため、別の質問にすることなく質問を編集することはできません(以前の回答は適用されなくなるため、違います)。イモ、この答えは正確にコメントの領域に含まれます:質問者からの説明(この場合、最小限の例が本当に問題を代表していることの確認)を求めます。
T. Verron、2014年

3

マニュアルでは、追加示唆declare-functionしてdefvar行。

結果のtest2ファイルは次のとおりです。

;;; test2.el ---  

;;; Code:

(declare-function test-fun1 "./test1.el")
(defvar test-var1)

(defun test-fun2 ()
  (let ((test test-var1))
    (test-fun1 test)))

(provide 'test2)
;;; test2.el ends here

ただし、これは、「親」ファイルで定義されているすべての関数とすべての変数に対して行う必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.