2019/11/18

[golang]githubでforkしてrenameした場合

golangで、よくgithub.comからパッケージを取得して使っている印象がある。
githubといえば、fork。
forkすると、パッケージまでのルートが変わる。
そして、githubではリポジトリの名前を変更することもできる。

その両方をやったとき、元のソースファイルをどう変更するのが良いのだろうか?


例題は、これにしよう。

hiro99ma blog: [golang]久々のgolang (3) - 他のpackage
https://hiro99ma.blogspot.com/2019/11/golanggolang-3-package.html

パッケージでこれを使っている。

https://github.com/hashicorp/logutils

これを、自分のところにforkして、かつリポジトリの名前を変えてみた。

https://github.com/hirokuma/logutilsutils

 

一番簡単なのは、一括置換だろう。
「元からそのパッケージを見ていました」ということになれば、なんの問題も無い。
しかし、その書き換えが大量になるのは苦痛だ。

かといって、symbolic linkなんかでごまかすのも、いかがなものかと考えてしまう。
なんか、卑怯な気がする。

理想は、importだけは書き換え、実装の方にはリポジトリ名の変更を感じさせない、というところか。


エイリアス、というものがあるらしい。

https://qiita.com/taji-taji/items/5a4f17bcf5b819954cc1#%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E6%93%8D%E4%BD%9C

 

import分を書き換えた。

import "github.com/hashicorp/logutils"

↓↓

import logutils "github.com/hirokuma/logutilsutils"

go getかな?

$ go get
go: finding github.com/hirokuma/logutilsutils v1.0.0
go: downloading github.com/hirokuma/logutilsutils v1.0.0
go: extracting github.com/hirokuma/logutilsutils v1.0.0
go: local/helloworld/hel imports
        github.com/hirokuma/logutilsutils: github.com/hirokuma/logutilsutils@v1.0.0: parsing go.mod:
        module declares its path as: github.com/hashicorp/logutils
                 but was required as: github.com/hirokuma/logutilsutils

うーん、logutilsutils/go.modがhashicorpのままだからだろうか?
しかし、書き換えてもダメだ。

いや、ダメとか云々の前に、どこからhirokuma/logutilsutilsをダウンロードしているのだろうか?
v1.0.0が付いているのでgithubからtagを削除したのに、それでもまだpkg/mod/には@v1.0.0が付いているのだ。

 

$GOPATH/pkg/以下は全部削除しているし、$GOPATH以下にhashicorp関係のファイルがないところも確認した。
わからん、何を調べたらいいのか分からん。。。

 

苦し紛れに、local/helloworld/helにあるgo.modを削除するとうまくいった。
うまくいったというか、src/github.com/hirokuma/logutilsutilsを置いていたので、それを使うようになったらしい。
go.modには特に何も書いていなかったのだが、ファイルがあるだけで挙動が変わるのか。
GO111MODULE=onとかしてないから、そういうのも原因かもしれん。

2019/11/17

[golang]__func__的なものを出力したい

golangの練習で、いろいろ関数を書いている。
関数を呼び出していると何を実行しているのかわかりにくくなってきたので、logを出そうかと考えた。
が、別に関数の名前だけ表示すればいいや、とC/C++のプリプロセッサみたいなものを使って出力させようとした。

 

が、golangにはプリプロセッサのようなものは無いらしい。まあ、わからんでもない。プリプロセッサは強力すぎて訳がわからないことまでできてしまいがちだからなぁ。

スタックトレース的なものはあるので、あちこち参考にしながら私も書いてみた。

01: package dbg
02: 
03: import (
04: 	"fmt"
05: 	"runtime"
06: 	"strings"
07: )
08: 
09: //Trace trace log
10: func Trace() {
11: 	print("", "")
12: }
13: 
14: //Head function header
15: func Head() {
16: 	banner := strings.Repeat("*", 30)
17: 	print("\n\n"+banner+"\n", "\n"+banner)
18: }
19: 
20: func print(head string, tail string) {
21: 	upc, fname, fline, _ := runtime.Caller(2)
22: 	fmt.Printf("%s[%s:%d]%s()%s\n", head, fname, fline, runtime.FuncForPC(upc).Name(), tail)
23: }
  

関数の頭で呼んだときは派手派手にしたかったのでHead()とTrace()にわけた。
まあ、Traceっていっても名前を出すだけなんだけどね。

オーバーライドのようなものは無いみたいで、引数を構造体にしてデフォルト引数を実現するような技が出てきた。今回は使ってないがそういうのもあるということで。

あと、文字列なのでポインタ型で渡した方がいいのかと思ったが、string型はどうなのだろうか?

01: package dbg
02: 
03: import (
04: 	"fmt"
05: 	"runtime"
06: 	"strings"
07: )
08: 
09: var banner = strings.Repeat("*", 30)
10: var head = "\n\n" + banner + "\n"
11: var tail = "\n" + banner
12: 
13: //Trace trace log
14: func Trace() {
15: 	empty := ""
16: 	print(&empty, &empty)
17: }
18: 
19: //Head function header
20: func Head() {
21: 	print(&head, &tail)
22: }
23: 
24: func print(head *string, tail *string) {
25: 	upc, fname, fline, _ := runtime.Caller(2)
26: 	fmt.Printf("%s[%s:%d]%s()%s\n", *head, fname, fline, runtime.FuncForPC(upc).Name(), *tail)
27: }
  
emptyをconstにしようとしたけど失敗。よくわからん。。
スタックに場所を確保する程度の重荷だろうから、もうこのままでいいや。

[golang]久々のgolang (4) - 実行ファイル名

こういう構成でビルドした。
main()はmain.goにある。

image

$ cd $GOPATH/src/main
$ go mod init
$ go build
$ ls
ex.go  go.mod  go.sum  main  main.go

実行ファイル名がmainなのはいただけないな、とディレクトリ名を変更した。

$ rm main
$ cd ..
$ mv main gogoex

image

$ go build
$ ls
ex.go  go.mod  go.sum  main  main.go

あれ、mainのままになっているのはなぜだ?
実行ファイル名はmain()を含んでいるファイルのディレクトリ名になると思っていたのだが。

 

go.modの1行目を見ると、こうなっていた。

module main

これを、変更した。

module gogoex

そしてビルド。

$ rm main
$ go build
$ ls
ex.go  gogoex  go.mod  go.sum  main.go

ディレクトリ名よりもgo.modの方が強いのか!
というよりも、実行ファイル名の決定にgo.modが使われるのか。

 

Go Modulesの説明が長いので、理由を探す気力が湧かなかった。
go getなどで更新されるわけでもないので、わざとそうしているのだろうか?
やらかしそうな気がするので、注意しておこう。

ただ、単にモジュール名として使われるだけなら便利かもしれん。
ディレクトリはmain()が入っているのでmain/にしたい、とか。


と、単純に考えてディレクトリ構成を変更していたのだが、ビルドができなくなった。
直下にはgoファイルを置かず、中でディレクトリを作って分けたいのだが。。。

image

~/Golang/go/src/example$ go build
can't load package: package example: no Go files in /home/xxx/Golang/go/src/example

まあ、ないよね。

 

$ go build ./*

これだとエラーは出ずに終わるのだが、実行ファイルが生成されていないようだ。

 

$ go build ./main/main.go
$ ./main/command-line-arguments
I am masao.

なぜか"command-line-arguments"という実行ファイル名ができる。
そして実行できてしまう。

$ go build -o mmm ./main/main.go
$ ls
ex  main  mmm

gccと同じように-oは使えるようだ。

もしや、go.modの"module"はここで使われるものなのでは?

$ go mod init
$ vi go.mod
(適当にmoduleの名前を変更)
$ go build ./main/main.go
build command-line-arguments: cannot load example/ex: malformed module path "example/ex": missing dot in first path element

moduleの名前を変更しないと、main/command-line-argumentsができるが、変更するとエラーになる。

 

どこかで...という形式を使うのを見たことがある。

$ go install -v ./...
$ ls $GOPATH/bin
main

もしかしたらmain/go.modを置くといいのでは?とやってみたのだが、変わらん。
そもそも、./...とか関係あるのかどうかわからんし。

 

んー、適当じゃダメだな。


go build

https://golang.org/cmd/go/#hdr-Compile_packages_and_dependencies

-oはいいとして、ビルド対象の指定方法を知りたいのだ。
[packages]と書いてあるが、.goファイルのリストでもよいらしい。
-vするとコンパイルしたパッケージの名前が表示されるらしい。

$ go build -v ./main/
go build: build output "main" already exists and is a directory

なるほど、ディレクトリと同じ名前になるから生成できなかったというわけか。
golang-standards/project-layoutというものを見てみると、cmd/の中にアプリ名のディレクトリを作るというのが定番らしい。逆らう理由もないのでそうしてみよう。

image

$ go build -v cmd/gogoex/main.go
$ ls
cmd  ex  main

むう、まだディレクトリ名になってくれないのか・・・。

When compiling a single main package, build writes the resulting executable to an output file named after the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe') or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe').

実行ファイル名は、goのファイル名からとるときとディレクトリ名から取るときがあるのか。

$ go build -v ./cmd/gogoex
example/cmd/gogoex
$ ls
cmd  ex  gogoex

なるほど、これでいいんだ。
最初の./を省略するとフルのパッケージ名として解釈しようとするので、相対パスの要領で"cmd/gogoex"とするとエラーになる。
ここなら、example/cmd/gogoex、と指定すれば良い。

 

go install

https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies

go buildは実行ファイルをその場に置いておくが、go buildは$GOPATH/bin/に移動させるようだ。

 

go clean

https://golang.org/cmd/go/#hdr-Remove_object_files_and_cached_files

cleanがよくわからん。
前回などは"go build"としてビルドしていて、それは"go clean"で実行ファイルが削除できていた。
でも、今回のようにパッケージ名を指定すると、どこのファイルも消してくれたように見えない。
"from package source directories"とあるから、$GOPATH/pkg/の方を消すのかな?
この程度のサンプルだとよくわからんな。


まあ、実行ファイル名関係についてはこのくらいでよかろう。
Makefileを使う場合もあるらしいし、また調べることになるだろう。

[golang]久々のgolang (3) - 他のpackage

前回golangのpackageのことを調べていたが、github.comなどのpackageについて調べ忘れていた。

 

例題として何かgit関係のリポジトリから取ってこよう。

https://godoc.org/github.com/hashicorp/logutils

ログ出力だったら、難しくないんじゃなかろうか。

local/helloworld/hel/main.go

01: package main
02: 
03: import "log"
04: import "os"
05: import "local/helloworld/hel/makkey"
06: import "github.com/hashicorp/logutils"
07: 
08: func main() {
09: 	filter := &logutils.LevelFilter{
10: 		Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
11: 		MinLevel: logutils.LogLevel("WARN"),
12: 		Writer:   os.Stderr,
13: 	}
14: 	log.SetOutput(filter)
15: 	log.Print("[WARN]Hello, World!\n")
16: 	makkey.Callme(filter)
17: }
  

local/helloworld/hel/makkey/masao.go

01: package makkey
02: 
03: import "log"
04: import "github.com/hashicorp/logutils"
05: 
06: //Callme masao
07: func Callme(filter *logutils.LevelFilter) {
08: 	log.Print("[ERROR]I'm masao!\n")
09: }
  

 

$ go get -d -u github.com/hashicorp/logutils
$ go run local/helloworld/hel
2019/11/16 23:28:51 [WARN]Hello, World!
2019/11/16 23:28:51 [ERROR]I'm masao!

を見ながら、なんとなく動かせた。


go getでライブラリをダウンロードしたりインストールしたりするのを見ていたので使ってみたのだが、そもそもどうなんだろうか?
他にもglideやらdepやらでインストールすることがあったのだが、そもそもどれが何なのだろうか?

 

  • glideは非推奨になって、depを使ってほしいらしい(glide v0.13.0)
  • depはgolang v1.9以上らしい
  • go getは依存関係は解決してくれるが、パッケージ管理とは異なりそうな気がする
  • vgoという名前も出てくるが、Go Modulesの前身?

 

わけが分からんぞ!

たぶん、golang v1.13以降で、リポジトリにgo.modがあるなら、GO111MODULE=onなどしなくてもGo Modulesの動作をするんだろうと思う。
では、Go Modulesの動作とは何かというと、おそらく$GOPATH/pkg/mod/の下に依存するソースファイルをダウンロードすることだ(ビルドもするのだろう)。「go getがGOPATH modeとは異なる動作をする」といってるので、そうなのじゃなかろうか。

 

私が見たGo Modulesの使い方は、こんなのだった。

$ GO111MODULE=on go install xxx

これでやってるせいか知らないが、pkg/mod/の中に入ってるものはアクセス権があってsudoしないと削除できなかった。

 

仕事でやろうとしているリポジトリはgo.modがあったし、今はgolang v1.13なのでgo getしてみたものの、pkg/mod/にはダウンロードせず、src/github.com/などにだらだらダウンロードし始めてしまった(途中でプロセスを殺したので、最後までは知らない)。

$ GO111MODULE=on go get -u xxx

とすると、pkg/mod/の下にダウンロードしているようだ。src/の下は何も増えていない。

 

「golang v1.13でもGO111MODULE=autoがデフォルトなのはそのままで、ただし$GOPATH/src/の中にgo.modがあればmodule-aware modeになる」らしい。

module-aware modeというのが、私が思うGo Modulesっぽい動作なのだろう。そしてGO111MODULE=onすると最初からGo Modulesっぽい動作をするので、$GOPATH/src/にはダウンロードされないんだろう。

なので、golang v1.13でGO111MODULEは未設定のまま、メインのソースファイルを編集したいが依存するパッケージはそのまま使いたいのなら、本体はgit cloneなどで$GOPATH/src/以下に取得し、その中でgo installすればいいはずだ。go.modがあるなら$GOPATH/pkg/mod/以下に取得するし、無かったら$GOPATH/src/に取得するだろう。

 

私がめんどくさいと思っているのは、$GOPATH/src以下に取得するときなのだ。github.comだったとして、github.com/aaaa/bbbbというリポジトリを取得したいのだったら、

$ mkdir -p $GOPATH/src/github.com/aaaa
$ cd $GOPATH/src/github.com/aaaa
$ git clone https://github.com/aaaa/bbbb

とかしないといかんじゃないか。git cloneで引数を付けてディレクトリを指定してもいいけど、打ち込む文字数はそんなに変わらんし。それがgo getだとディレクトリの作成までやってくれるので使いたかったのだ。


src/local/helloworld/helにはgo.modがないので、go installとかgo getとかすると$GOPATH/src/以下にgithub.com/hashicorpのリポジトリをダウンロードしてくる。

これをsrc/に起きたくなかったら、go.modを書くのが良いだろう。
手動で書かなくても済みそうな気がするが、自動生成はできるのだろうか?

https://github.com/golang/go/wiki/Modules

できそうだ。

$ cd $GOPATH/src/local/helloworld/hel
$ go mod init local/helloworld/hel
$ go build

go mod initでgo.modができて、go buildすると更新されてgithub.com/hashicorp/logutilsがバージョン付きで追加されていた。logutilsもsrc/の下ではなく、pkg/mod/の下に取得されていた。
local/helloworld/hel/makkeyの方は書かれていなかったので、パスで見分けているのかもしれん。

2019/11/16

[golang]久々のgolang (2) - package

前回に引き続き、golangの勉強。versionは1.13.4。


まずはpackageというかライブラリというか、そういうやつ。

たぶん、種類としてはgo言語標準、ローカルで実装したもの、他から取得したもの、くらいになるんじゃなかろうか。

 

標準ライブラリ

https://golang.org/pkg/#stdlib

これが標準のものだろう。
下の方にOther packagesがあるが、これはメインのgoには入ってないがプロジェクトの一部ではあるらしい。"net"はstandardにもotherにもあるが、otherはgolang.org/x/net(import "goglang.org/x/net/bpf"など)とのこと。

 

ローカルの実装

JavaやC#のイメージだと、機能単位でpackageを分けて、あわよくば他でも使えるようにするだろう。
ライブラリと同じようなイメージかもしれん。

 

他から取得したもの

よく見るのは、これだ。githubなどで公開されているものを使うのだ。
git getとかで取得できていた気がする。

 

 

標準のやつは"fmt"で試せたことにして、まずはローカルの実装だろう。

image

こんな感じで、masao.goというファイルを作った。

local/helloworld/hel/main.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	fmt.Printf("Hello, World!\n")
07: 	//masao.Callme()
08: 	Callme()
09: }
  

 

local/helloworld/hel/masao.go

01: package main
02: 
03: import "fmt"
04: 
05: //Callme masao
06: func Callme() {
07: 	fmt.Printf("I'm masao!\n")
08: }
  

これは、masaoをmainパッケージに置いたので、動いた。
Cなどと違って、別にincludeみたいなことをしなくて良いのだな。

 

では、masao.goを別パッケージにしたらどうなるかと、masaoというパッケージに変更した。

$ go install local/helloworld/hel
can't load package: package local/helloworld/hel: found packages main (main.go) and masao (masao.go) in /home/xxx/Golang/go/src/local/helloworld/hel

見つからないとかなんとかいう以前に、パッケージにmainとmasaoがあるからダメ、みたいなことを言われた。
Javaだとディレクトリ構成やファイル名がパッケージと関連していたが、golangはどうなんだろうか。

https://golang.org/doc/effective_go.html#package-names
Another convention is that the package name is the base name of its source directory;

ファイル名はいいとして、パッケージ名とディレクトリ構成は関連するのか。
ならば、パッケージ名はmakkeyにしてみよう。

image

local/helloworld/hel/main.go

01: package main
02: 
03: import "fmt"
04: import "local/helloworld/hel/makkey"
05: 
06: func main() {
07: 	fmt.Printf("Hello, World!\n")
08: 	makkey.Callme()
09: }
  

 

local/helloworld/hel/makkey/masao.go

01: package makkey
02: 
03: import "fmt"
04: 
05: //Callme masao
06: func Callme() {
07: 	fmt.Printf("I'm masao!\n")
08: }
  

 

これはコンパイルできたし、実行できた。

$ go install local/helloworld/hel
$ hel
Hello, World!
I'm masao!

 

ファイル名は影響しないようだ。
とはいえ、golangは構造体を使ってclassっぽいものを使えたと思うので、それに合わせることになるだろう。

[golang]久々のgolang (1)

いろいろあって、golangをやらねばならなくなりそうだ。
Pythonはスクリプト言語を覚えたいというモチベーションがあったのだけど、golangは仕事で使えればいいや、という程度だ。

まあ、仕事で使える程度、ってのはかなり高い要求なんだけどね。。。


まず、インストールから。Ubuntu18.04でやる。

これを書いている時点のstableバージョン(なのかな?)は、1.13.4。

https://github.com/golang/go/wiki/Ubuntu

PPAのを使って、apt installした。

$ go version
go version go1.13.4 linux/amd64

よかろう。


以前はGOPATHとかGOROOTとかの環境変数を設定していた気がする。
言語はやってなくてもgolangで作られたアプリはインストールしていたのだが、GOPATHしか設定した記憶が無い。
GOROOTは使わなくなったのだろうか?

GolangのGOPATHやGOROOTについて
https://tech.librastudio.co.jp/entry/index.php/2018/02/20/post-1792/

ほう、そうか。
GOROOTはインストール場所だったのか。aptでも有効だったのだろうか?まあ、もう遅いんだけど。

そして、デフォルトでGOPATH未設定の場合でもパスが自動で決められるらしい。
ただまあ、たくさんライブラリを使うようなアプリだったりすると、$GOPATH/srcの中がゴチャゴチャしてくるし、$GOPATH/binをPATHに追加してしまってよいものか悩むシーンも出てくる。
なので、あれこれ切り分けたいとか、golangあまり使わないのでそのときだけ設定したいとかあるだろうから、私はGOPATHは指定するようにしておこう。
pythonのvenvなんかを使い始めてわかったのだけど、GOPATHも環境を分ける方法の1つと考えて良いだろう。ストレージに余裕がある場合は、なるべく分けるようにしよう。

 

そういうわけで、今回は$HOME/Golangを作って、その中でやる。
こんなのをsourceコマンドで読み込んでおけばいいや。

01: #!/bin/bash
02: 
03: export GOPATH=`pwd`/go
04: export PATH=$PATH:$GOPATH/bin
  

では、helloworldしてみる。

$ cd Golang
$ source config.sh  #↑のスクリプト
$ mkdir -p go/src/hello
$ cd go/src/hello
$ vi main.go

================================================

package main

import "fmt"

func main() {
    fmt.Printf("Hello, World!\n")
}

================================================

$ go build
$ ./hello
Hello, World!
$

あれ、goって、$GOPATHの下にbin, pkg, srcができて、実行ファイルはbinにコピーされるんじゃないの?

Go言語の作業ディレクトリをドキュメントに従って作り直してみた
https://qiita.com/ttsuzo/items/45eb4fc269986df9122c

ああ、推奨構成みたいなものがあるのね。そしてgo installするとうまいことやってくれるようだ。

$ go install
$ ls ../../bin
hello

ほう。そしてどうでもいいことだが、go buildしてカレントディレクトリに作られていたhelloは削除されていた。
go installの引数は、カレントディレクトリかsrc/からのパスのようだ。

$ cd ..
$ pwd
/home/xxx/Golang/go/src
$ mkdir -p local/helloworld
$ mv hello local/helloworld/hel
$ go install local/helloworld/hel
$ ls ../bin
hel  hello

実行ファイル名はディレクトリ名がそのままになるのか。そして、名前が変わったとしても前回installしたものはそのまま残る、と。追跡はしていないようだ。

$ rm ../bin/*
$ go install local/helloworld/hel
$ go clean local/helloworld/hel
$ ls ../bin
hel

実行ファイルがカレントディレクトリにあったときはcleanすると削除してくれたのだが、binにあるとだめなのか。
helpで"Clean removes object files from package source directories."と書いているから、srcだけなのだろう。

$ go run local/helloworld/hel
Hello, World!

こうすると、バイナリファイルが残っていないけど実行ができている。
main.goがカレントディレクトリなら引数が不要かと思ったが、それはダメだった。


そろそろ、vscodeを使う。
src/で開くべきかgo/で開くべきか悩ましいが、まずはgoにしておこう。

それで気付いたのだが、Goのインデントはハードタブなのだろうか?
C言語でvscodeを使っているときはスペースだったと思うので、言語によって切り替えられているようだ。
https://golang.org/doc/を検索しても出てこないのだが、ネットではハードタブを使うような記述が出てくる。
スペースに置き換えて gofmt -w main.go するとハードタブになるから、少なくともコマンドとしてはハードタブがデフォルトのようだ。

 

vscodeにGoのextensionを入れたのだが、ツールのインストールを要求するのでOKOKとやってたところ、$HOME/Golang/goのbinやpkgにいろいろインストールし始めた。。。
せっかく仮想環境の一環だ、なんて思ってたのに台無しじゃないか。。。
go.toolsGopathにパスを書くと、そこをvscode用の$GOPATHとして使ってくれそうだ。

 

デバッガは、launch.jsonをデフォルトのまま使うことができた。
"${fileDirname}"になっているので、main.goを開いているならそのままでいいのだろう。しかし腰を据えてやるような状態だとmain.goなんかいちいち開いていられないだろう。
たぶんだが、main.goがあるディレクトリまでのフルパスを書けばよさそうだ。フルパスというのが、/home/から書かないとダメみたい。${workspaceFolder}から書き始めるのはよさそうだ。
これは、私が.bashrcなんかにGOPATHを書いてないとか、binにPATHを通してないとか、まだmain.goしかファイルがないとか、いろいろわかってないことが多いので保留にしておこう。

 

goとは関係ないが、vscodeのexplorerで"OPEN EDITORS"を表示したくない場合は

"explorer.openEditors.visible": 0

を追加すると良いそうだ。
あれがあると、ファイルを開くたびにツリーが下に下がっていくので、毎回隠していたのよねぇ。


デバッグできそうな環境ができたので、次からは言語の本体をやっていこう。
基本的なところだけわかれば、あとは仕事しながら覚えてもいいだろう。