Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 17399] ファイルの概要

このコミットは、EmacsのGoモード(go-mode.el)において、goコマンドのパスをユーザーがカスタマイズできるようにする変更を導入しています。これにより、複数のGo開発環境を持つユーザーが、それぞれの環境に合わせたgoツールを柔軟に利用できるようになります。

コミット

commit 264b73b3f24b56ac1db5c65e9cfaa267d0a41f48
Author: Alan Donovan <adonovan@google.com>
Date:   Tue Aug 27 09:47:58 2013 -0400

    misc/emacs: allow users to customize path of 'go' tool.
    
    Some users have multiple Go development trees and invoke the
    'go' tool via a wrapper that sets GOROOT and GOPATH based on
    the current directory.  Such users should customize go-command
    to point to the wrapper script.
    
    R=dominik.honnef
    CC=golang-dev
    https://golang.org/cl/13233043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/264b73b3f24b56ac1db5c65e9cfaa267d0a41f48

元コミット内容

misc/emacs: allow users to customize path of 'go' tool.

このコミットは、EmacsのGoモードにおいて、goツールのパスをユーザーがカスタマイズできるようにするものです。一部のユーザーは複数のGo開発ツリーを持っており、現在のディレクトリに基づいてGOROOTGOPATHを設定するラッパースクリプトを介してgoツールを呼び出しています。このようなユーザーは、go-command変数をラッパースクリプトを指すようにカスタマイズすることができます。

変更の背景

Go言語の開発環境では、GOROOT(Goのインストールディレクトリ)とGOPATH(Goのワークスペースディレクトリ)という2つの重要な環境変数があります。これらはGoのツールチェインがソースコードやパッケージを見つけるために不可欠です。

一般的な開発環境では、これらの変数はシステム全体で設定されるか、単一のプロジェクトに対して設定されます。しかし、大規模な開発組織や、複数の異なるバージョンのGoや異なるプロジェクト構造を扱う開発者にとっては、これらの環境変数をプロジェクトごとに切り替える必要が生じます。

この問題に対処するため、一部のユーザーは、現在の作業ディレクトリに基づいてGOROOTGOPATHを動的に設定する「ラッパースクリプト」を使用しています。例えば、特定のプロジェクトディレクトリに入ると、そのプロジェクト専用のGoバージョンや依存関係が自動的に設定されるようなスクリプトです。

EmacsのGoモードは、Go開発を支援するための様々な機能を提供しますが、内部でgoコマンドを直接呼び出していました。このため、ラッパースクリプトを使用しているユーザーは、Emacsが常にシステムデフォルトのgoコマンドを呼び出してしまい、期待するGo環境で動作しないという問題に直面していました。

このコミットは、このようなユーザーのニーズに応えるため、EmacsのGoモードが使用するgoコマンドのパスをユーザーが任意に設定できるようにすることで、より柔軟な開発環境の構築を可能にすることを目的としています。

前提知識の解説

Emacs Lisp (Elisp)

Emacs Lispは、Emacsエディタの拡張言語です。Emacsのほぼ全ての機能はEmacs Lispで書かれており、ユーザーはEmacs Lispを使ってEmacsをカスタマイズしたり、新しい機能を追加したりできます。

  • defcustom: Emacs Lispでユーザーがカスタマイズ可能な変数を定義するためのマクロです。defcustomで定義された変数は、Emacsのカスタマイズインターフェース(M-x customize)を通じて簡単に設定できます。
    • :type: 変数の型を指定します(例: 'string, 'hook)。
    • :group: カスタマイズインターフェースでの表示グループを指定します。
    • ドキュメンテーション文字列: 変数の目的や使い方を説明します。
  • concat: 複数の文字列を結合して新しい文字列を生成する関数です。
  • shell-command-to-string: シェルコマンドを実行し、その標準出力を文字列として返す関数です。Emacs Lispから外部コマンドを実行する際によく使われます。
  • split-string: 文字列を指定した区切り文字で分割し、文字列のリストを返す関数です。
  • let*: 複数のローカル変数を順番に定義し、それらの変数を使って式を評価するための特殊形式です。前の変数の値を使って次の変数を定義できる点がletと異なります。

Go言語の環境変数 (GOROOT, GOPATH)

  • GOROOT: GoのSDKがインストールされているディレクトリを指します。Goのコンパイラ、標準ライブラリ、ツールなどが含まれています。
  • GOPATH: Goのワークスペースディレクトリを指します。Goのソースコード、コンパイルされたバイナリ、ダウンロードされたサードパーティ製パッケージなどが格納されます。Go 1.11以降のGo Modulesの導入により、GOPATHの重要性は以前ほどではなくなりましたが、依然として一部のツールやレガシーなプロジェクトでは利用されます。

ラッパースクリプト

特定のコマンドの実行を仲介し、追加の処理(環境変数の設定、ログの出力など)を行うスクリプトのことです。このコミットの文脈では、goコマンドを呼び出す前にGOROOTGOPATHを適切に設定するシェルスクリプトなどを指します。

技術的詳細

このコミットの主要な変更点は、EmacsのGoモードがgoコマンドを呼び出す際に、ハードコードされた"go"という文字列ではなく、新しく導入されたgo-commandというカスタマイズ可能な変数を使用するようにした点です。

  1. go-command変数の導入: defcustomマクロを使用して、go-commandという新しい変数が定義されました。この変数は文字列型で、デフォルト値は"go"です。ユーザーはEmacsのカスタマイズ機能を通じて、この変数の値を任意の実行可能ファイル(例えば、goコマンドのラッパースクリプトのパス)に変更できます。

    (defcustom go-command "go"
      "The 'go' command.  Some users have multiple Go development
    trees and invoke the 'go' tool via a wrapper that sets GOROOT and
    GOPATH based on the current directory.  Such users should
    customize this variable to point to the wrapper script."
      :type 'string
      :group 'go)
    

    この定義により、EmacsユーザーはM-x customize-group RET go RETと入力することで、Goモードに関連する設定項目の中からGo Commandを見つけ、その値を変更できるようになります。

  2. go-root-and-paths関数の変更: この関数は、go env GOROOT GOPATHコマンドを実行して、現在のGo環境のGOROOTGOPATHの情報を取得します。変更前は"go env GOROOT GOPATH"という文字列を直接shell-command-to-stringに渡していましたが、変更後はgo-command変数の値と" env GOROOT GOPATH"concat関数で結合した文字列を渡すようになりました。

    ;; 変更前
    (shell-command-to-string "go env GOROOT GOPATH")
    ;; 変更後
    (shell-command-to-string (concat go-command " env GOROOT GOPATH"))
    

    これにより、go-commandがラッパースクリプトを指している場合、そのラッパースクリプトが設定する環境変数がEmacsのGoモードに反映されるようになります。

  3. ビルド/テストコマンドの変更: Goモードが内部でgo buildgo test -cコマンドを実行する箇所も同様に変更されました。ファイルがテストファイル(_test.goで終わる)の場合はgo test -cを、それ以外の場合はgo build -o /dev/nullを実行して構文チェックなどを行いますが、ここでもgoコマンドの部分がgo-command変数に置き換えられました。

    ;; 変更前
    (shell-command-to-string
     (if (string-match "_test\\.go$" buffer-file-truename)
         "go test -c"
       "go build -o /dev/null"))
    ;; 変更後
    (shell-command-to-string
     (concat go-command
             (if (string-match "_test\\.go$" buffer-file-truename)
                 " test -c"
               " build -o /dev/null")))
    

    ここでもconcat関数が使用され、go-commandの値に続けて" test -c"または" build -o /dev/null"が結合されます。これにより、EmacsのGoモードが実行するビルドやテストのコマンドも、ユーザーが指定したgoコマンドのパスを通じて実行されるようになります。

これらの変更により、EmacsのGoモードは、ユーザーが設定したカスタムのgoコマンド(ラッパースクリプトなど)を尊重し、複数のGo開発環境をよりシームレスに切り替えることができるようになりました。

コアとなるコードの変更箇所

misc/emacs/go-mode.elファイルにおいて、以下の変更が行われました。

  1. go-command変数の追加: ファイルの冒頭付近にdefcustomを用いてgo-command変数が追加されました。

    --- a/misc/emacs/go-mode.el
    +++ b/misc/emacs/go-mode.el
    @@ -150,6 +150,14 @@
       :type 'hook
       :group 'go)
     
    +(defcustom go-command "go"
    +  "The 'go' command.  Some users have multiple Go development
    +trees and invoke the 'go' tool via a wrapper that sets GOROOT and
    +GOPATH based on the current directory.  Such users should
    +customize this variable to point to the wrapper script."
    +  :type 'string
    +  :group 'go)
    +
     (defface go-coverage-untracked
       '((t (:foreground "#505050")))\
       "Coverage color of untracked code."
    
  2. go-root-and-paths関数内のgoコマンド呼び出しの変更: go envコマンドを呼び出す部分が、ハードコードされた"go"からgo-command変数を使用するように変更されました。

    --- a/misc/emacs/go-mode.el
    +++ b/misc/emacs/go-mode.el
    @@ -844,7 +852,8 @@ uncommented, otherwise a new import will be added."
               ('none (insert "\nimport (\n\t" line "\n)\n"))))))))
     
     (defun go-root-and-paths ()
    -  (let* ((output (split-string (shell-command-to-string "go env GOROOT GOPATH") "\n"))
    +  (let* ((output (split-string (shell-command-to-string (concat go-command " env GOROOT GOPATH"))
    +                               "\n"))
              (root (car output))
              (paths (split-string (cadr output) ":"))))
         (append (list root) paths)))
    
  3. ビルド/テストコマンド呼び出しの変更: go buildまたはgo test -cコマンドを呼び出す部分が、同様にgo-command変数を使用するように変更されました。

    --- a/misc/emacs/go-mode.el
    +++ b/misc/emacs/go-mode.el
    @@ -903,9 +912,10 @@ If IGNORE-CASE is non-nil, the comparison is case-insensitive."
                               (if (string= (file-truename (match-string 1 line)) (file-truename buffer-file-name))
                                   (string-to-number (match-string 2 line))))))
                         (split-string (shell-command-to-string
    -                                   (if (string-match "_test\\.go$" buffer-file-truename)
    -                                       "go test -c"
    -                                     "go build -o /dev/null")) "\n")))))\
    +                                   (concat go-command
    +                                           (if (string-match "_test\\.go$" buffer-file-truename)
    +                                               " test -c"
    +                                             " build -o /dev/null"))) "\n")))))\
     
     (defun go-remove-unused-imports (arg)
       "Removes all unused imports. If ARG is non-nil, unused imports
    

コアとなるコードの解説

このコミットの核心は、Emacs Lispのdefcustomconcat関数を効果的に利用して、外部コマンドの呼び出しを柔軟にした点にあります。

  • defcustom go-command "go" ...: これは、Emacsのカスタマイズシステムに統合されたユーザー設定可能な変数go-commandを定義しています。デフォルト値は"go"であり、これはシステムパスから通常のgo実行ファイルを見つけることを意味します。しかし、ユーザーはEmacsのカスタマイズインターフェースを通じて、この値を例えば/usr/local/bin/my-go-wrapperのようなカスタムスクリプトのパスに変更できます。これにより、EmacsのGoモードがGoツールを呼び出す際に、常にこの指定されたパスを使用するようになります。

  • (concat go-command " env GOROOT GOPATH"): この行は、go-command変数の値(例: "go"または"/path/to/my-go-wrapper")と、それに続く引数文字列(例: " env GOROOT GOPATH")を結合して、シェルで実行される完全なコマンド文字列を生成します。例えば、go-command"/usr/local/bin/my-go-wrapper"に設定されている場合、この式は"/usr/local/bin/my-go-wrapper env GOROOT GOPATH"という文字列を生成し、これがshell-command-to-stringに渡されて実行されます。

  • (concat go-command (if ...)): 同様に、ビルドやテストのコマンドを生成する際にもconcatが使用されます。if式によって、現在のバッファのファイル名がテストファイルであるかどうかに応じて、" test -c"または" build -o /dev/null"という文字列が選択されます。この選択された文字列がgo-commandと結合され、最終的なコマンド文字列が形成されます。

これらの変更により、EmacsのGoモードは、Goツールチェインとの連携において、ユーザーの多様な環境設定(特に複数のGoバージョンやカスタムのGOROOT/GOPATH管理)に柔軟に対応できるようになりました。これは、EmacsをGo開発の主要なIDEとして使用しているユーザーにとって、非常に重要な改善と言えます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Emacs Lispの公式マニュアル
  • GitHubのgolang/goリポジトリのコミット履歴
  • Go言語の環境変数に関する一般的な知識
  • Emacsのカスタマイズに関する一般的な知識