gorun实现分析

学习golang的时候想写点代码还得到特定的目录结构下,build & run,实在是太罗嗦,好在Programming in Go这本书一开始就提到了把golang当作脚本语言使的工具,但是直到这本书快看完了才受够了这个流程决定使用这两个工具(好强的忍耐力。。。

这篇文章是对gorun代码的分析,项目地址是:https://wiki.ubuntu.com/gorun

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
gorun <source-file> [option...]
```
option是传给source-file的参数

### 处理流程
脚本实现思路是把输入的源文件拷贝到系统TempDir目录下,然后编译执行。大致流程如下:
* 创建工作目录:
rundir = TMPDIR/gorun-hostname-euid[-NUM]/GOOS_GOARCH
* 生成可执行文件的文件名:
runfile = EvalSymlinks(Abs(sourcefile)), Replace("%"->"%%", filepath.Separator->"%")
* 基于ModTime检测源文件相对上次编译是否更新
若不需要重新编译,则touch(runfile) & CleanDir(rundir),请注意touch的原因
* 若需要编译则调用go tool工具编译,这样可以指定产出文件。
如果源文件首行以#!开头,则会生成一个临时的源文件runfile.pid.go并且编译后删除。原因是代码还是用go编译的,#!不符合go的语法,因此源文件除了首行可以以#!开头外,其他地方都要符合go的语法。
* 之后使用系统调用exec执行,如果是因为目标文件不存在导致失败则会有重试。


目前代码里有个小问题:
```go
// CleanDir removes binary files under rundir in case they were not
// accessed for more than CleanFileDelay nanoseconds. A last-cleaned
// marker file is created so that the next verification is only done
// after CleanFileDelay nanoseconds.
func CleanDir(rundir string, now time.Time) error {
cleanedfile := filepath.Join(rundir, "last-cleaned")
cleanLine := now.Add(-CleanFileDelay)
if info, err := os.Stat(cleanedfile); err == nil && info.ModTime().After(cleanLine) {
// It's been cleaned recently.
return nil
}
f, err := os.Create(cleanedfile)
if err != nil {
return err
}
_, err = f.Write([]byte(now.Format(time.RFC3339)))
f.Close()
if err != nil {
return err
}

// Look for expired files.
d, err := os.Open(rundir)
if err != nil {
return err
}
infos, err := d.Readdir(-1)
for _, info := range infos {
atim := sysStat(info).Atim
access := time.Unix(int64(atim.Sec), int64(atim.Nsec))
if access.Before(cleanLine) {
os.Remove(filepath.Join(rundir, info.Name()))
}
}
return nil
}

这里在判断"old compiled files"用的是atime,首先文件系统可以配置不记录atime,这样的话就没法判断bin文件是不是被用过。然后是如果文件已经存在,则Create不会更新atime,这就导致last-cleaned也会被删除,下一次gorun还会调用CleanDir一次。但是都不是大问题,atime本身比较蛋疼需要注意。

还有就是go run可以编译并执行源程序,再用第三方脚本就没必要了。