【Golang】ローカル自作パッケージを使おうとすると「package XXX/ZZZZ is not in std」になる

Code

はじまり

リサちゃん
リサちゃん

ちょっと、全然パッケージを使えないんですが・・・

135ml
135ml

新しいプログラミング言語の使い始めあるあるだな。

想定する読者

Go言語を始めたての初心者

まず、何が起きてるんだ

最近まで、Pythonを書いていましたが、処理速度とメモリ節約に関する冒険がしたくなり、Goを使い始めました。

そこで、自分で書いたローカルパッケージをmain.goなどで使おうとした時の話です。

話題のランタイム環境は、Go 1.22.2です。

はい、まずは憎きこのエラー文からです。

main.go:9:3: package mymodule/mypkg is not in std (/usr/local/go/src/mymodule/mypkg)

もしくは、VSCode上で表示されるこのエラー文。

could not import mymodule/mypkg (no required module provides package "mymodule/mypkg")

コイツらを乗り越えて、Goを実行したい!

まずは結論から。

結論としては、以下のディレクトリ構成で以下のようにファイルを設定すれば出来ました。

ディレクトリ構成

mymodule
|--go.mod
|--go.sum
|--main.go
|--mypkg
|  |--tools.go

go.mod

module mymodule

go 1.22.2

require (
	github.com/google/go-github v17.0.0+incompatible
	golang.org/x/oauth2 v0.19.0
)

require github.com/google/go-querystring v1.1.0 // indirect

main.go

package main

import (
    "fmt"

    "mymodule/mypkg"

    "github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

// ...略

func main() {
    fmt.Println(mypkg.Hello())
}

mypkg/tools.go

package my

// ...略

func Hello() string {
    return "Hello, world!"
}

そして、内部パッケージを導入する際には、以下の4点に気を付ければ良いのかなと思いました。

  • 自作パッケージの中のファイル名は何でも良い。・・・mypkgディレクトリ内にあるファイル名は「tools.go」ですし、そのファイルのpackage文に「mypkg」は書いてありません。しかし、プログラムとしては問題なく動きます。ビルドしても動きます。
  • 自作パッケージの中にgo.modは不要である。・・・自作パッケージのディレクトリにgo.modが無ければ、親ディレクトリを見に行きます。モジュール全体で必要な外部パッケージが分からなくなるので、go.modはルートディレクトリだけに置かれるのが良さそうだと思いました。
  • go.mod内のmoduleの値が重要である。・・・ここのモジュール名から、直下のサブディレクトリを参照していきます。
  • main.goimport文にはディレクトリ名を書く。・・・僕の場合はファイル名を書いていて沼りました。

自作パッケージを使用するまでの手順

それでは、細かいところを手順立てていきましょう。

1. パッケージの設定

mypkgディレクトリにあるtools.goファイルでパッケージを定義します。ファイルの先頭に以下のように記述します。

mypkg/tools.go

package my

// ...略

func Hello() string {
    return "Hello, world!"
}

ここで、package文には、「mypkg」ではなく「my」と書きました。外部パッケージとして利用する場合にどうなるかはちょっと分かりませんが、ただ内部で動かす時は問題なく動きます。

2. main.goでのパッケージのインポート

そして、main.goにこんな感じで書きます。

main.go

package main

import (
    "fmt"

    "mymodule/mypkg"

    "github.com/google/go-github/github"
	"golang.org/x/oauth2"
)

// ...略

func main() {
    fmt.Println(mypkg.Hello())
}

そうしたら、必要な外部パッケージのインポートがまだ出来ていないので、go mod initでインストールおよびインポートします。

3. go.modでモジュールを明示する

ここで、go mod initを使うと、このようなメッセージが返ってきますよね。

go: cannot determine module path for source directory /home/username/mymodule (outside GOPATH, module path must be specified)

Example usage:
        'go mod init example.com/m' to initialize a v0 or v1 module
        'go mod init example.com/m/v2' to initialize a v2 module

Run 'go help mod init' for more information.

自分は当初、これはおまじないのようなものだと思っていましたが、ここでgo mod initの引数に渡す文字列がモジュール名になるようです。この部分がとても大事です。

なので、go mod init <モジュール名>を実行して、

shell

go mod init mymodule

すると、こんな感じでメッセージが返ってきます。go.modも作成されます。

go: creating new go.mod: module mymodule
go: to add module requirements and sums:
        go mod tidy

しかし、必要なパッケージがまだインストールされていないので、go mod tidyをして必要なパッケージを全て入れます。

shell

go mod tidy

go mod tidyをすると、go.modmain.goの処理に必要なパッケージが列挙されて、未インストールのものはインストールされます。

go.modの中にはこんな感じで記述されているかと思います。

module mymodule

go 1.22.2

require (
	github.com/google/go-github v17.0.0+incompatible
	golang.org/x/oauth2 v0.19.0
)

require github.com/google/go-querystring v1.1.0 // indirect

ここで、go.modには内部パッケージの情報は記されていないことが分かります。

しかし、go.modが存在している状態では、そのパッケージ全体は「モジュールモード」となり、module文に記述されている文字列がモジュール名になるわけです。(go.modが存在していないと、「GOPATHモード」になるみたいです。ちょっと、そのモードで弄る気力は持ち合わせていない。)

「モジュールモード」では、このモジュール名から内部パッケージを参照していきます。なので、"mymodule/mypkg"mypkg内にあるtools.goを参照できるというわけです。

これで動くようになりました!

まとめ

Go言語でローカルの自作パッケージをインポートする方法を紹介しました。以下にポイントをまとめます。

  • 自作パッケージの中のファイル名とpackage名は、適当に付けても動くことは動く。
  • 自作パッケージの中にgo.modは不要である
  • go.mod内のmoduleの値およびgo mod initの引数が重要である。
  • main.goimport文にはディレクトリ名を書く。

この内部パッケージのインポートの作業でかなり時間を無駄にしたので、同じように無駄にする人が一人でも減れば嬉しいですね・・・!

おしまい

リサちゃん
リサちゃん

いっつもこういう所に詰まるんだよなあ~!!

135ml
135ml

動かせてしまえればコッチのもの。

以上になります!

コメント

タイトルとURLをコピーしました