2019/12/07

[golang]あきらめてパッチにした

GitHubにあったgolangアプリのリポジトリをforkして改造しようとしていた話の続き。

アプリの本体だけならまだよかったのだが、アプリが使っている別リポジトリのライブラリも改造内容に関わっていて、そっちも変更が必要だ。
go.modで置き換えようとしていたのだが、その置き換えだけのためにライブラリをforkして・・・ということをやっていると、もう私の精神力が持たなかった。

 

というわけで、こうした。

  • 置き換えたいリポジトリたちは、go getではなくgit cloneで$GOPATH/srcの方にcloneする
  • cloneしたファイルを編集する
  • リポジトリごとにgit diff > xxx.patchなどとしてパッチファイルを取得する
  • 差分を取得してpatchにしたり、cloneしてpatchをあてるためのスクリプトを作る
  • 自分のリポジトリを作って、そのスクリプトやパッチだけを管理する

 

cloneするときに、forkしたものをオリジナルと同じディレクトリにするなりシンボリックリンクにするなりでもできそうだけど、自分で間違えてしまいそうな気がするのでね。

 

なんか、なんかよい方法があるんじゃないかと思うのだが、探しきれてないのよねぇ。
こちらの、オリジナルをgo getしてupstreamにforkした方を載せる、というのがgolangでのやり方なのかな。

github でホストされている go アプリを fork して PR する時には、まず go get から始めよう - Qiita
https://qiita.com/t-suwa/items/d384facb79866dc16cad

stackoverflowにも出てた。

Using forked package import in Go - Stack Overflow
https://stackoverflow.com/questions/14323872/using-forked-package-import-in-go

 

パッチでやるときの心配は、操作を間違えて消してしまうんじゃないだろうか、というものだ。
それに、一人でやっているからいいけど、複数人でやるとパッチファイルのマージなんかが起きたときに目も当てられないことになってしまいそうだ。

 

ああ、そうだ、その心配があったな。。。
やっぱりupstream方式にした方が無難か。

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の方は書かれていなかったので、パスで見分けているのかもしれん。