2014-09-05 10:33:43 +04:00
|
|
|
|
// Wide 配置相关,所有配置(包括用户配置)都是保存在 wide.json 中.
|
2014-08-18 17:45:43 +04:00
|
|
|
|
package conf
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"io/ioutil"
|
|
|
|
|
"os"
|
|
|
|
|
"os/exec"
|
|
|
|
|
"path/filepath"
|
2014-09-15 09:03:44 +04:00
|
|
|
|
"runtime"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
"strings"
|
2014-09-15 14:03:52 +04:00
|
|
|
|
"time"
|
2014-09-08 07:37:34 +04:00
|
|
|
|
|
2014-09-15 10:24:40 +04:00
|
|
|
|
"github.com/b3log/wide/event"
|
2014-09-08 07:37:34 +04:00
|
|
|
|
_ "github.com/b3log/wide/i18n"
|
|
|
|
|
"github.com/b3log/wide/util"
|
|
|
|
|
"github.com/golang/glog"
|
2014-08-18 17:45:43 +04:00
|
|
|
|
)
|
|
|
|
|
|
2014-10-21 06:47:16 +04:00
|
|
|
|
const (
|
|
|
|
|
PathSeparator = string(os.PathSeparator) // 系统文件路径分隔符
|
|
|
|
|
PathListSeparator = string(os.PathListSeparator) // 系统路径列表分隔符
|
|
|
|
|
|
|
|
|
|
)
|
2014-10-13 10:34:42 +04:00
|
|
|
|
|
2014-09-22 19:13:07 +04:00
|
|
|
|
// 最后一次会话内容结构.
|
|
|
|
|
type LatestSessionContent struct {
|
2014-09-24 07:58:09 +04:00
|
|
|
|
FileTree []string // 文件树展开的路径集
|
|
|
|
|
Files []string // 编辑器打开的文件路径集
|
|
|
|
|
CurrentFile string // 当前编辑器文件路径
|
2014-09-22 19:13:07 +04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用户结构.
|
2014-08-31 14:50:38 +04:00
|
|
|
|
type User struct {
|
2014-09-22 19:13:07 +04:00
|
|
|
|
Name string
|
|
|
|
|
Password string
|
2014-10-21 06:25:45 +04:00
|
|
|
|
Workspace string // 该用户的工作空间 GOPATH 路径
|
2014-10-23 17:43:35 +04:00
|
|
|
|
Locale string
|
2014-10-26 13:08:01 +03:00
|
|
|
|
GoFormat string
|
2014-09-22 19:13:07 +04:00
|
|
|
|
LatestSessionContent *LatestSessionContent
|
2014-08-31 09:31:26 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-22 19:13:07 +04:00
|
|
|
|
// 配置结构.
|
2014-08-23 19:07:24 +04:00
|
|
|
|
type conf struct {
|
2014-10-13 08:22:50 +04:00
|
|
|
|
Server string // 服务地址({IP}:7070)
|
|
|
|
|
StaticServer string // 静态资源服务地址(http://{IP}:7070)
|
|
|
|
|
EditorChannel string // 编辑器通道地址(ws://{IP}:7070)
|
|
|
|
|
OutputChannel string // 输出窗口通道地址(ws://{IP}:7070)
|
|
|
|
|
ShellChannel string // Shell 通道地址(ws://{IP}:7070)
|
|
|
|
|
SessionChannel string // Wide 会话通道地址(ws://{IP}:7070)
|
|
|
|
|
HTTPSessionMaxAge int // HTTP 会话失效时间(秒)
|
|
|
|
|
StaticResourceVersion string // 静态资源版本
|
|
|
|
|
MaxProcs int // 并发执行数
|
|
|
|
|
RuntimeMode string // 运行模式
|
|
|
|
|
Pwd string // 工作目录
|
2014-10-21 06:25:45 +04:00
|
|
|
|
Workspace string // 主工作空间 GOPATH 路径
|
2014-10-23 17:43:35 +04:00
|
|
|
|
Locale string // 默认的区域
|
2014-10-13 08:22:50 +04:00
|
|
|
|
Users []*User // 用户集
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-22 19:13:07 +04:00
|
|
|
|
// 配置.
|
2014-08-23 19:07:24 +04:00
|
|
|
|
var Wide conf
|
2014-09-22 19:13:07 +04:00
|
|
|
|
|
|
|
|
|
// 维护非变化部分的配置.
|
2014-09-25 09:37:59 +04:00
|
|
|
|
//
|
2014-09-22 19:13:07 +04:00
|
|
|
|
// 只有 Users 是会运行时变化的,保存回写文件时要使用这个变量.
|
2014-08-31 14:50:38 +04:00
|
|
|
|
var rawWide conf
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
2014-09-15 14:03:52 +04:00
|
|
|
|
// 定时检查 Wide 运行环境.
|
2014-09-25 09:37:59 +04:00
|
|
|
|
//
|
2014-09-16 17:19:56 +04:00
|
|
|
|
// 如果是特别严重的问题(比如 $GOPATH 不存在)则退出进程,另一些不太严重的问题(比如 gocode 不存在)则放入全局通知队列.
|
2014-09-22 19:13:07 +04:00
|
|
|
|
func FixedTimeCheckEnv() {
|
2014-09-15 14:03:52 +04:00
|
|
|
|
go func() {
|
2014-10-10 10:24:47 +04:00
|
|
|
|
// 7 分钟进行一次检查环境
|
|
|
|
|
for _ = range time.Tick(time.Minute * 7) {
|
2014-09-15 14:03:52 +04:00
|
|
|
|
if "" == os.Getenv("GOPATH") {
|
|
|
|
|
glog.Fatal("Not found $GOPATH")
|
|
|
|
|
os.Exit(-1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if "" == os.Getenv("GOROOT") {
|
|
|
|
|
glog.Fatal("Not found $GOROOT")
|
|
|
|
|
|
|
|
|
|
os.Exit(-1)
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
gocode := Wide.GetExecutableInGOBIN("gocode")
|
2014-09-15 14:03:52 +04:00
|
|
|
|
cmd := exec.Command(gocode, "close")
|
|
|
|
|
_, err := cmd.Output()
|
|
|
|
|
if nil != err {
|
2014-09-15 19:20:01 +04:00
|
|
|
|
event.EventQueue <- event.EvtCodeGocodeNotFound
|
2014-09-15 14:03:52 +04:00
|
|
|
|
glog.Warningf("Not found gocode [%s]", gocode)
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
ide_stub := Wide.GetExecutableInGOBIN("ide_stub")
|
2014-09-15 14:03:52 +04:00
|
|
|
|
cmd = exec.Command(ide_stub, "version")
|
|
|
|
|
_, err = cmd.Output()
|
|
|
|
|
if nil != err {
|
2014-09-15 19:20:01 +04:00
|
|
|
|
event.EventQueue <- event.EvtCodeIDEStubNotFound
|
2014-09-15 14:03:52 +04:00
|
|
|
|
glog.Warningf("Not found ide_stub [%s]", ide_stub)
|
|
|
|
|
}
|
2014-09-22 19:13:07 +04:00
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-10 10:24:47 +04:00
|
|
|
|
// 定时(1 分钟)保存配置.
|
2014-09-25 09:37:59 +04:00
|
|
|
|
//
|
2014-09-23 18:35:05 +04:00
|
|
|
|
// 主要目的是保存用户会话内容,以备下一次用户打开 Wide 时进行会话还原.
|
2014-09-22 19:13:07 +04:00
|
|
|
|
func FixedTimeSave() {
|
|
|
|
|
go func() {
|
2014-10-10 10:24:47 +04:00
|
|
|
|
// 1 分钟进行一次配置保存
|
|
|
|
|
for _ = range time.Tick(time.Minute) {
|
2014-09-22 19:13:07 +04:00
|
|
|
|
Save()
|
2014-09-15 14:03:52 +04:00
|
|
|
|
}
|
|
|
|
|
}()
|
2014-09-15 10:24:40 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 09:37:59 +04:00
|
|
|
|
// 获取 username 指定的用户的工作空间路径,查找不到时返回空字符串.
|
2014-10-21 06:25:45 +04:00
|
|
|
|
func (c *conf) GetUserWorkspace(username string) string {
|
|
|
|
|
for _, user := range c.Users {
|
2014-09-13 12:50:18 +04:00
|
|
|
|
if user.Name == username {
|
2014-10-21 06:25:45 +04:00
|
|
|
|
ret := strings.Replace(user.Workspace, "{pwd}", c.Pwd, 1)
|
2014-09-13 12:50:18 +04:00
|
|
|
|
return filepath.FromSlash(ret)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ""
|
2014-09-05 07:24:53 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-21 06:25:45 +04:00
|
|
|
|
// 获取主工作空间路径.
|
2014-10-28 05:57:16 +03:00
|
|
|
|
//
|
|
|
|
|
// 相比起使用 Wide.Workspace,该函数会做如下处理:
|
|
|
|
|
// 1. 替换里面的 {pwd} 变量为实际的目录路径
|
|
|
|
|
// 2. 把 / 替换为 \\ (Windows)
|
2014-10-21 06:25:45 +04:00
|
|
|
|
func (c *conf) GetWorkspace() string {
|
|
|
|
|
return filepath.FromSlash(strings.Replace(c.Workspace, "{pwd}", c.Pwd, 1))
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 13:08:01 +03:00
|
|
|
|
// 获取 username 指定的用户的 Go 源码格式化工具路径,查找不到时返回 "gofmt".
|
|
|
|
|
func (c *conf) GetGoFmt(username string) string {
|
|
|
|
|
for _, user := range c.Users {
|
|
|
|
|
if user.Name == username {
|
|
|
|
|
switch user.GoFormat {
|
|
|
|
|
case "gofmt":
|
|
|
|
|
return "gofmt"
|
|
|
|
|
case "goimports":
|
|
|
|
|
return c.GetExecutableInGOBIN("goimports")
|
|
|
|
|
default:
|
|
|
|
|
glog.Errorf("Unsupported Go Format tool [%s]", user.GoFormat)
|
|
|
|
|
return "gofmt"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-09-30 12:27:01 +04:00
|
|
|
|
|
2014-10-26 13:08:01 +03:00
|
|
|
|
return "gofmt"
|
2014-09-30 12:27:01 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-28 05:57:16 +03:00
|
|
|
|
// 获取用户的工作空间路径.
|
|
|
|
|
//
|
|
|
|
|
// 相比起使用 User.Workspace,该函数会做如下处理:
|
|
|
|
|
// 1. 替换里面的 {pwd} 变量为实际的目录路径
|
|
|
|
|
// 2. 把 / 替换为 \\ (Windows)
|
|
|
|
|
func (u *User) GetWorkspace() string {
|
|
|
|
|
return filepath.FromSlash(strings.Replace(u.Workspace, "{pwd}", Wide.Pwd, 1))
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-23 18:29:53 +04:00
|
|
|
|
// 获取 username 指定的用户配置.
|
|
|
|
|
func (*conf) GetUser(username string) *User {
|
|
|
|
|
for _, user := range Wide.Users {
|
|
|
|
|
if user.Name == username {
|
|
|
|
|
return user
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
// 获取 GOBIN 中 executable 指定的文件路径.
|
|
|
|
|
//
|
|
|
|
|
// 函数内部会判断操作系统,如果是 Windows 则在 executable 实参后加入 .exe 后缀.
|
|
|
|
|
func (*conf) GetExecutableInGOBIN(executable string) string {
|
|
|
|
|
if util.OS.IsWindows() {
|
|
|
|
|
executable += ".exe"
|
|
|
|
|
}
|
2014-09-15 09:03:44 +04:00
|
|
|
|
|
2014-10-28 08:22:39 +03:00
|
|
|
|
gopaths := filepath.SplitList(os.Getenv("GOPATH"))
|
2014-09-17 10:35:48 +04:00
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
for _, gopath := range gopaths {
|
|
|
|
|
// $GOPATH/bin/$GOOS_$GOARCH/executable
|
|
|
|
|
ret := gopath + PathSeparator + "bin" + PathSeparator +
|
|
|
|
|
os.Getenv("GOOS") + "_" + os.Getenv("GOARCH") + PathSeparator + executable
|
|
|
|
|
if isExist(ret) {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2014-09-17 10:35:48 +04:00
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
// $GOPATH/bin/{runtime.GOOS}_{runtime.GOARCH}/executable
|
|
|
|
|
ret = gopath + PathSeparator + "bin" + PathSeparator +
|
|
|
|
|
runtime.GOOS + "_" + runtime.GOARCH + PathSeparator + executable
|
|
|
|
|
if isExist(ret) {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2014-09-15 10:48:03 +04:00
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
// $GOPATH/bin/executable
|
|
|
|
|
ret = gopath + PathSeparator + "bin" + PathSeparator + executable
|
|
|
|
|
if isExist(ret) {
|
|
|
|
|
return ret
|
|
|
|
|
}
|
2014-09-15 09:03:44 +04:00
|
|
|
|
}
|
2014-09-17 10:35:48 +04:00
|
|
|
|
|
2014-10-26 10:41:34 +03:00
|
|
|
|
// $GOBIN/executable
|
|
|
|
|
return os.Getenv("GOBIN") + PathSeparator + executable
|
2014-09-15 09:03:44 +04:00
|
|
|
|
}
|
|
|
|
|
|
2014-09-15 10:24:40 +04:00
|
|
|
|
// 保存 Wide 配置.
|
2014-08-31 14:50:38 +04:00
|
|
|
|
func Save() bool {
|
2014-09-22 19:13:07 +04:00
|
|
|
|
// 只有 Users 是会运行时变化的,其他属性只能手工维护 wide.json 配置文件
|
2014-08-31 14:50:38 +04:00
|
|
|
|
rawWide.Users = Wide.Users
|
|
|
|
|
|
|
|
|
|
// 原始配置文件内容
|
|
|
|
|
bytes, err := json.MarshalIndent(rawWide, "", " ")
|
|
|
|
|
|
|
|
|
|
if nil != err {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err = ioutil.WriteFile("conf/wide.json", bytes, 0644); nil != err {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-15 10:24:40 +04:00
|
|
|
|
// 加载 Wide 配置.
|
2014-08-31 14:50:38 +04:00
|
|
|
|
func Load() {
|
2014-08-18 17:45:43 +04:00
|
|
|
|
bytes, _ := ioutil.ReadFile("conf/wide.json")
|
|
|
|
|
|
|
|
|
|
err := json.Unmarshal(bytes, &Wide)
|
|
|
|
|
if err != nil {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
os.Exit(-1)
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-31 14:50:38 +04:00
|
|
|
|
// 保存未经变量替换处理的原始配置文件,用于写回时
|
|
|
|
|
json.Unmarshal(bytes, &rawWide)
|
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
ip, err := util.Net.LocalIP()
|
|
|
|
|
if err != nil {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
os.Exit(-1)
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-08 07:37:34 +04:00
|
|
|
|
glog.V(3).Infof("IP [%s]", ip)
|
2014-09-19 20:56:32 +04:00
|
|
|
|
|
2014-08-18 17:45:43 +04:00
|
|
|
|
Wide.Server = strings.Replace(Wide.Server, "{IP}", ip, 1)
|
|
|
|
|
Wide.StaticServer = strings.Replace(Wide.StaticServer, "{IP}", ip, 1)
|
|
|
|
|
Wide.EditorChannel = strings.Replace(Wide.EditorChannel, "{IP}", ip, 1)
|
|
|
|
|
Wide.OutputChannel = strings.Replace(Wide.OutputChannel, "{IP}", ip, 1)
|
|
|
|
|
Wide.ShellChannel = strings.Replace(Wide.ShellChannel, "{IP}", ip, 1)
|
2014-09-19 20:56:32 +04:00
|
|
|
|
Wide.SessionChannel = strings.Replace(Wide.SessionChannel, "{IP}", ip, 1)
|
2014-08-18 17:45:43 +04:00
|
|
|
|
|
2014-08-28 06:51:03 +04:00
|
|
|
|
// 获取当前执行路径
|
2014-08-18 17:45:43 +04:00
|
|
|
|
file, _ := exec.LookPath(os.Args[0])
|
|
|
|
|
pwd, _ := filepath.Abs(file)
|
2014-10-13 10:34:42 +04:00
|
|
|
|
pwd = pwd[:strings.LastIndex(pwd, PathSeparator)]
|
2014-09-13 12:50:18 +04:00
|
|
|
|
Wide.Pwd = pwd
|
2014-09-08 07:37:34 +04:00
|
|
|
|
glog.V(3).Infof("pwd [%s]", pwd)
|
2014-08-28 06:51:03 +04:00
|
|
|
|
|
2014-09-08 07:37:34 +04:00
|
|
|
|
glog.V(3).Info("Conf: \n" + string(bytes))
|
2014-10-28 05:57:16 +03:00
|
|
|
|
|
|
|
|
|
initWorkspaceDirs()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初始化主工作空间、各个用户的工作空间目录.
|
|
|
|
|
//
|
|
|
|
|
// 如果不存在 Workspace 配置所指定的目录路径则创建该目录.
|
|
|
|
|
func initWorkspaceDirs() {
|
2014-10-28 08:22:39 +03:00
|
|
|
|
paths := filepath.SplitList(Wide.GetWorkspace())
|
2014-10-28 05:57:16 +03:00
|
|
|
|
|
|
|
|
|
for _, user := range Wide.Users {
|
2014-10-28 08:22:39 +03:00
|
|
|
|
paths = append(paths, filepath.SplitList(user.GetWorkspace())...)
|
2014-10-28 05:57:16 +03:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, path := range paths {
|
|
|
|
|
createWorkspaceDir(path)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在 path 指定的路径下创建工作空间目录.
|
|
|
|
|
//
|
|
|
|
|
// 1. 根目录:{path}
|
|
|
|
|
// 2. 源码目录:{path}/src
|
|
|
|
|
// 3. 包目录:{path}/pkg
|
|
|
|
|
// 4. 可执行文件目录:{path}/bin
|
|
|
|
|
func createWorkspaceDir(path string) {
|
|
|
|
|
// {path}, workspace root
|
|
|
|
|
createDir(path)
|
|
|
|
|
|
|
|
|
|
// {path}/src
|
|
|
|
|
createDir(path + PathSeparator + "src")
|
|
|
|
|
|
|
|
|
|
// {path}/pkg
|
|
|
|
|
createDir(path + PathSeparator + "pkg")
|
|
|
|
|
|
|
|
|
|
// {path}/bin
|
|
|
|
|
createDir(path + PathSeparator + "bin")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在 path 指定的路径下不存在目录或文件则创建目录.
|
|
|
|
|
func createDir(path string) {
|
|
|
|
|
if !isExist(path) {
|
|
|
|
|
if err := os.MkdirAll(path, 0775); nil != err {
|
|
|
|
|
glog.Error(err)
|
|
|
|
|
|
|
|
|
|
os.Exit(-1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glog.V(7).Infof("Created a directory [%s]", path)
|
|
|
|
|
}
|
2014-08-18 17:45:43 +04:00
|
|
|
|
}
|
2014-09-15 10:48:03 +04:00
|
|
|
|
|
|
|
|
|
// 检查文件或目录是否存在.
|
2014-09-25 09:37:59 +04:00
|
|
|
|
//
|
2014-09-15 10:48:03 +04:00
|
|
|
|
// 如果由 filename 指定的文件或目录存在则返回 true,否则返回 false.
|
|
|
|
|
func isExist(filename string) bool {
|
|
|
|
|
_, err := os.Stat(filename)
|
|
|
|
|
|
|
|
|
|
return err == nil || os.IsExist(err)
|
|
|
|
|
}
|