使用direnv来独立项目环境

概述

最近Web Assembly(简称wasm)特性合并到了go主分支上,想尝试一下,但又不想直接把本机的go直接升级成devel版本,于是找到了direnv来独立一个go环境来测试wasm

direnvshell的环境切换器。它知道如何挂钩bashzshtcshfishelvish,以根据当前目录加载或卸载环境变量。这允许项目特定的环境变量而不会混淆~/.profile文件。

在每次提示之前,direnv检查当前目录和父目录中是否存在.envrc文件。如果该文件存在(并且已被allow),则将其加载到子shell中,然后所有导出的变量将被direnv捕获,然后提供给当前shell

安装

通过源码安装需要go版本大于1.9

1
2
3
go get github.com/direnv/direnv
cd $GOPATH/src/github.com/direnv/direnv
make install

或使用常见的包管理软件安装,如在mac上使用brew

1
brew install direnv

使用

配置shell

zsh~/.zshrc文件的末尾添加以下行:

1
eval "$(direnv hook zsh)"

其他类似,根据文档添加相应的配置。

配置目标目录

在目标文件夹中,创建一个.envrc文件并在其中添加一些export指令。请注意,.envrc文件的内容必须是有效的bash语法,无论您使用的是何种shell。这是因为direnv总是用bash执行.envrc,这样direnv可以在shell中工作。如果尝试使用某些在bash中不起作用的语法(如zsh的嵌套扩展),会有问题。

编辑完.envrc会看到direnv警告direnv: error .envrc is blocked.。这是避免自动加载新文件的安全机制,需要手动执行direnv allow批准记载并看到direnv加载新环境。请注意,direnv edit .是一个快捷方式,可以在$EDITOR中打开文件,并在文件被修改自动allow该文件。

进入对应目录,环境自动加载,一旦你从目录中移出,它会自动卸载。如果回到它,它再次被加载。直接进入子目录也会加载。

简单测试下:

1
2
3
cd /path/to/project
echo "echo 'test direnv'" >> .envrc
direnv allow

那么再你再进入这个目录时,就会输出test direnv

stdlib

手动export变量有点麻烦,direnv提供了些实用的函数可以方便的编写.envrc,输入direnv stdlib或在stdlib.sh查看。也可以通过在~/.config/direnv/direnvrc~/.direnvrc中创建一个bash文件来创建自己的扩展函数。创建一个独立的go环境只要在想要的目录下添加.envrc

1
layout go

加载分层的.envrc

假设你有以下结构:

1
2
3
4
5
.
└── a
├── .envrc
└── b
└── .envrc

进入子目录b时只有b下的.envrc会生效,可以在文件里添加source_env ..source_up来同时加载对应目录或更高级目录的.envrc

运用

使用源码安装特定的go版本,该版本的go就编译好在/your/path/go/bin

1
2
3
4
5
6
7
8
9
cd /your/path/
git clone https://go.googlesource.com/go
cd go
# 添加有需要特性的remote,切换到到对应的版本(分支),非必要
# git remote add neelance https://github.com/neelance/go
# git fetch --all
# git checkout wasm-wip # 该分支当前已经合并到主分支上
cd src
./all.bash

然后创建新的GOPATH目录

1
2
3
4
5
.
├── .envrc
├── bin
├── pkg
└── src

并编辑.envrc文件

1
2
3
4
PATH_add /your/path/go/bin
export GOROOT="/your/path/go"

layout go

go version查看下版本,可以看到是devel版本

1
go version devel +b080abf656 Tue Jun 26 01:57:01 2018 +0000 darwin/amd64

src创建项目目录并创建main.go

1
2
3
4
5
package main

func main() {
println("Hello World, from wasm+Go")
}

然后可以拷贝源码里的misc/wasm下文件到同目录,只需要构建一个名为test.wasm的文件,我们就可以重用wasm_exec.htmlwasm_exec.js

1
GOARCH=wasm GOOS=js go build -o test.wasm main.go

开启个静态服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"flag"
"log"
"net/http"
)

func main() {
addr := flag.String("addr", ":8000", "server address:port")
flag.Parse()
srv := http.FileServer(http.Dir("."))
log.Printf("listening on %q...", *addr)
log.Fatal(http.ListenAndServe(*addr, srv))
}

并访问wasm_exec.html,点击按钮可以看到console输出了Hello World, from wasm+Go