This commit is contained in:
parent
eea2ae469d
commit
931e573f52
|
@ -1,4 +1,4 @@
|
|||
FROM golang:latest
|
||||
FROM golang:cross
|
||||
MAINTAINER Liang Ding <dl88250@gmail.com>
|
||||
|
||||
ADD . /wide/gogogo/src/github.com/b3log/wide
|
||||
|
|
|
@ -63,8 +63,18 @@ func CreateZipHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
path := args["path"].(string)
|
||||
var name string
|
||||
|
||||
base := filepath.Base(path)
|
||||
|
||||
if nil != args["name"] {
|
||||
name = args["name"].(string)
|
||||
} else {
|
||||
name = base
|
||||
}
|
||||
|
||||
dir := filepath.Dir(path)
|
||||
|
||||
if !util.File.IsExist(path) {
|
||||
data["succ"] = false
|
||||
data["msg"] = "Can't find file [" + path + "]"
|
||||
|
@ -72,7 +82,8 @@ func CreateZipHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
zipFile, err := util.Zip.Create(path + ".zip")
|
||||
zipPath := filepath.Join(dir, name)
|
||||
zipFile, err := util.Zip.Create(zipPath + ".zip")
|
||||
if nil != err {
|
||||
logger.Error(err)
|
||||
data["succ"] = false
|
||||
|
@ -86,4 +97,6 @@ func CreateZipHandler(w http.ResponseWriter, r *http.Request) {
|
|||
} else {
|
||||
zipFile.AddEntry(base, path)
|
||||
}
|
||||
|
||||
data["path"] = zipPath
|
||||
}
|
||||
|
|
7
main.go
7
main.go
|
@ -87,6 +87,8 @@ func init() {
|
|||
if *confStat {
|
||||
session.FixedTimeReport()
|
||||
}
|
||||
|
||||
logger.Debug("cross-compilation ", util.Go.GetCrossPlatforms())
|
||||
}
|
||||
|
||||
// Main.
|
||||
|
@ -125,6 +127,9 @@ func main() {
|
|||
http.HandleFunc(conf.Wide.Context+"/go/install", handlerWrapper(output.GoInstallHandler))
|
||||
http.HandleFunc(conf.Wide.Context+"/output/ws", handlerWrapper(output.WSHandler))
|
||||
|
||||
// cross compilation
|
||||
http.HandleFunc(conf.Wide.Context+"/cross", handlerWrapper(output.CrossCompilationHandler))
|
||||
|
||||
// file tree
|
||||
http.HandleFunc(conf.Wide.Context+"/files", handlerWrapper(file.GetFilesHandler))
|
||||
http.HandleFunc(conf.Wide.Context+"/file/refresh", handlerWrapper(file.RefreshDirectoryHandler))
|
||||
|
@ -237,7 +242,7 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
|||
model := map[string]interface{}{"conf": conf.Wide, "i18n": i18n.GetAll(locale), "locale": locale,
|
||||
"session": wideSession, "latestSessionContent": user.LatestSessionContent,
|
||||
"pathSeparator": conf.PathSeparator, "codeMirrorVer": conf.CodeMirrorVer,
|
||||
"user": user, "editorThemes": conf.GetEditorThemes()}
|
||||
"user": user, "editorThemes": conf.GetEditorThemes(), "crossPlatforms": util.Go.GetCrossPlatforms()}
|
||||
|
||||
logger.Debugf("User [%s] has [%d] sessions", username, len(wideSessions))
|
||||
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
// Copyright (c) 2014-2015, b3log.org
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package output
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/b3log/wide/conf"
|
||||
"github.com/b3log/wide/i18n"
|
||||
"github.com/b3log/wide/session"
|
||||
"github.com/b3log/wide/util"
|
||||
)
|
||||
|
||||
// CrossCompilationHandler handles request of cross compilation.
|
||||
func CrossCompilationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
data := map[string]interface{}{"succ": true}
|
||||
defer util.RetJSON(w, r, data)
|
||||
|
||||
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
|
||||
if httpSession.IsNew {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
username := httpSession.Values["username"].(string)
|
||||
locale := conf.GetUser(username).Locale
|
||||
|
||||
var args map[string]interface{}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
|
||||
logger.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
sid := args["sid"].(string)
|
||||
filePath := args["path"].(string)
|
||||
|
||||
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
|
||||
http.Error(w, "Forbidden", http.StatusForbidden)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
platform := args["platform"].(string)
|
||||
goos := strings.Split(platform, "_")[0]
|
||||
goarch := strings.Split(platform, "_")[1]
|
||||
|
||||
curDir := filepath.Dir(filePath)
|
||||
|
||||
suffix := ""
|
||||
if "windows" == goos {
|
||||
suffix = ".exe"
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", "build")
|
||||
cmd.Dir = curDir
|
||||
|
||||
setCmdEnv(cmd, username)
|
||||
|
||||
for i, env := range cmd.Env {
|
||||
if strings.HasPrefix(env, "GOOS=") {
|
||||
cmd.Env[i] = "GOOS=" + goos
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(env, "GOARCH=") {
|
||||
cmd.Env[i] = "GOARCH=" + goarch
|
||||
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
executable := filepath.Base(curDir) + suffix
|
||||
executable = filepath.Join(curDir, executable)
|
||||
name := filepath.Base(curDir) + "-" + goos + "-" + goarch
|
||||
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if nil != err {
|
||||
logger.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if nil != err {
|
||||
logger.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !data["succ"].(bool) {
|
||||
return
|
||||
}
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// display "START [go build]" in front-end browser
|
||||
|
||||
channelRet["output"] = "<span class='start-build'>" + i18n.Get(locale, "start-build").(string) + "</span>\n"
|
||||
channelRet["cmd"] = "start-build"
|
||||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
reader := bufio.NewReader(io.MultiReader(stdout, stderr))
|
||||
|
||||
if err := cmd.Start(); nil != err {
|
||||
logger.Error(err)
|
||||
data["succ"] = false
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
go func(runningId int) {
|
||||
defer util.Recover()
|
||||
defer cmd.Wait()
|
||||
|
||||
// logger.Debugf("User [%s, %s] is building [id=%d, dir=%s]", username, sid, runningId, curDir)
|
||||
|
||||
// read all
|
||||
buf, _ := ioutil.ReadAll(reader)
|
||||
|
||||
channelRet := map[string]interface{}{}
|
||||
channelRet["cmd"] = "cross-build"
|
||||
channelRet["executable"] = executable
|
||||
channelRet["name"] = name
|
||||
|
||||
if 0 == len(buf) { // build success
|
||||
channelRet["output"] = "<span class='build-succ'>" + i18n.Get(locale, "build-succ").(string) + "</span>\n"
|
||||
} else { // build error
|
||||
// build gutter lint
|
||||
|
||||
errOut := string(buf)
|
||||
lines := strings.Split(errOut, "\n")
|
||||
|
||||
// path process
|
||||
var errOutWithPath string
|
||||
for _, line := range lines {
|
||||
errOutWithPath += parsePath(curDir, line) + "\n"
|
||||
}
|
||||
|
||||
channelRet["output"] = "<span class='build-error'>" + i18n.Get(locale, "build-error").(string) + "</span>\n" +
|
||||
"<span class='stderr'>" + errOutWithPath + "</span>"
|
||||
|
||||
// lint process
|
||||
|
||||
if lines[0][0] == '#' {
|
||||
lines = lines[1:] // skip the first line
|
||||
}
|
||||
|
||||
lints := []*Lint{}
|
||||
|
||||
for _, line := range lines {
|
||||
if len(line) < 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
if line[0] == '\t' {
|
||||
// append to the last lint
|
||||
last := len(lints)
|
||||
msg := lints[last-1].Msg
|
||||
msg += line
|
||||
|
||||
lints[last-1].Msg = msg
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
file := line[:strings.Index(line, ":")]
|
||||
left := line[strings.Index(line, ":")+1:]
|
||||
index := strings.Index(left, ":")
|
||||
lineNo := 0
|
||||
msg := left
|
||||
if index >= 0 {
|
||||
lineNo, err = strconv.Atoi(left[:index])
|
||||
|
||||
if nil != err {
|
||||
continue
|
||||
}
|
||||
|
||||
msg = left[index+2:]
|
||||
}
|
||||
|
||||
lint := &Lint{
|
||||
File: filepath.Join(curDir, file),
|
||||
LineNo: lineNo - 1,
|
||||
Severity: lintSeverityError,
|
||||
Msg: msg,
|
||||
}
|
||||
|
||||
lints = append(lints, lint)
|
||||
}
|
||||
|
||||
channelRet["lints"] = lints
|
||||
}
|
||||
|
||||
if nil != session.OutputWS[sid] {
|
||||
// logger.Debugf("User [%s, %s] 's build [id=%d, dir=%s] has done", username, sid, runningId, curDir)
|
||||
|
||||
wsChannel := session.OutputWS[sid]
|
||||
err := wsChannel.WriteJSON(&channelRet)
|
||||
if nil != err {
|
||||
logger.Warn(err)
|
||||
}
|
||||
|
||||
wsChannel.Refresh()
|
||||
}
|
||||
|
||||
}(rand.Int())
|
||||
}
|
|
@ -171,6 +171,26 @@ var tree = {
|
|||
window.open(config.context + '/file/zip?path=' + wide.curNode.path + ".zip");
|
||||
}
|
||||
},
|
||||
crossCompile: function (platform) {
|
||||
var request = newWideRequest();
|
||||
request.path = wide.curNode.path;
|
||||
request.platform = platform;
|
||||
|
||||
$.ajax({
|
||||
async: false,
|
||||
type: 'POST',
|
||||
url: config.context + '/cross',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogAlert").dialog("open", data.msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
decompress: function () {
|
||||
var request = newWideRequest();
|
||||
request.path = wide.curNode.path;
|
||||
|
@ -278,12 +298,18 @@ var tree = {
|
|||
$fileRMenu.find(".remove").addClass("disabled");
|
||||
}
|
||||
|
||||
if (wide.curNode.path.indexOf("zip", wide.curNode.path.length - "zip".length) === -1) { // !path.endsWith("zip")
|
||||
if (-1 === wide.curNode.path.indexOf("zip", wide.curNode.path.length - "zip".length)) { // !path.endsWith("zip")
|
||||
$fileRMenu.find(".decompress").hide();
|
||||
} else {
|
||||
$fileRMenu.find(".decompress").show();
|
||||
}
|
||||
|
||||
if (-1 === wide.curNode.path.indexOf("go", wide.curNode.path.length - "go".length)) { // !path.endsWith("go")
|
||||
$fileRMenu.find(".linux64").hide();
|
||||
} else {
|
||||
$fileRMenu.find(".linux64").show();
|
||||
}
|
||||
|
||||
var top = event.clientY - 10;
|
||||
if ($fileRMenu.height() + top > $('.content').height()) {
|
||||
top = top - $fileRMenu.height() - 25;
|
||||
|
|
|
@ -86,8 +86,8 @@ var wide = {
|
|||
|
||||
$("#dialogAlert").dialog({
|
||||
"modal": true,
|
||||
"height": 36,
|
||||
"width": 260,
|
||||
"height": 40,
|
||||
"width": 350,
|
||||
"title": config.label.tip,
|
||||
"hiddenOk": true,
|
||||
"cancelText": config.label.confirm,
|
||||
|
@ -483,6 +483,7 @@ var wide = {
|
|||
|
||||
break;
|
||||
case 'build':
|
||||
case 'cross-build':
|
||||
bottomGroup.fillOutput($('.bottom-window-group .output > div').html() + data.output);
|
||||
|
||||
if (data.lints) { // has build error
|
||||
|
@ -506,6 +507,30 @@ var wide = {
|
|||
var editor = editors.getEditorByPath(path);
|
||||
CodeMirror.signal(editor, "change", editor);
|
||||
}
|
||||
} else {
|
||||
if ('cross-build' === data.cmd) {
|
||||
var request = newWideRequest(),
|
||||
isSucc = false;
|
||||
request.path = data.executable;
|
||||
request.name = data.name;
|
||||
|
||||
$.ajax({
|
||||
async: false,
|
||||
type: 'POST',
|
||||
url: config.context + '/file/zip/new',
|
||||
data: JSON.stringify(request),
|
||||
dataType: "json",
|
||||
success: function (data) {
|
||||
if (!data.succ) {
|
||||
$("#dialogAlert").dialog("open", data.msg);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
window.open(config.context + '/file/zip?path=' + data.path + ".zip");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
21
util/go.go
21
util/go.go
|
@ -33,6 +33,27 @@ type mygo struct{}
|
|||
// Go utilities.
|
||||
var Go = mygo{}
|
||||
|
||||
func (*mygo) GetCrossPlatforms() []string {
|
||||
ret := []string{}
|
||||
|
||||
toolDir := runtime.GOROOT() + "/pkg/tool"
|
||||
f, _ := os.Open(toolDir)
|
||||
names, _ := f.Readdirnames(-1)
|
||||
f.Close()
|
||||
|
||||
for _, name := range names {
|
||||
subDir, _ := os.Open(toolDir + "/" + name)
|
||||
tools, _ := subDir.Readdirnames(10)
|
||||
subDir.Close()
|
||||
|
||||
if len(tools) > 5 {
|
||||
ret = append(ret, name)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// GetAPIPath gets the Go source code path.
|
||||
//
|
||||
// 1. before Go 1.4: $GOROOT/src/pkg
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<title>{{.i18n.wide}} - {{.i18n.wide_title}}</title>
|
||||
|
||||
<meta name="keywords" content="Wide, Golang, IDE, Team, Cloud, B3log"/>
|
||||
<meta name="description" content="A Web-based IDE for Teams using Golang, do your development anytime, anywhere."/>
|
||||
<meta name="description" content="A Web-based IDE for Teams using Golang, do your development anytime, anywrhere."/>
|
||||
<meta name="author" content="B3log">
|
||||
<meta property="og:description" content="A Web-based IDE for Teams using Golang, do your development anytime, anywhere."/>
|
||||
|
||||
|
@ -457,6 +457,12 @@
|
|||
<span class="space"></span> {{.i18n.rename}}
|
||||
</li>
|
||||
<li class="hr"></li>
|
||||
{{range .crossPlatforms}}
|
||||
<li class="{{.}}" onclick="tree.crossCompile('');">
|
||||
<span class="space"></span> {{.}}
|
||||
</li>
|
||||
{{end}}
|
||||
<li class="hr"></li>
|
||||
<li class="export" onclick="tree.export(this);">
|
||||
<span class="ico-export font-ico"></span> {{.i18n.export}}
|
||||
</li>
|
||||
|
|
Loading…
Reference in New Issue