add package gbuild

This commit is contained in:
John
2019-12-14 17:01:27 +08:00
parent 951ce46932
commit e33230a88f
14 changed files with 255 additions and 104 deletions

View File

@ -0,0 +1,9 @@
# custom gf build setting.
[compiler]
name = "app"
[compiler.varmap]
name = "GoFrame"
version = "1.10.1"
home-site = "https://goframe.org"

View File

@ -0,0 +1,11 @@
package main
import (
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gbuild"
)
func main() {
g.Dump(gbuild.Info())
g.Dump(gbuild.Map())
}

View File

@ -2,28 +2,12 @@ package main
import (
"fmt"
"github.com/gogf/gf/debug/gdebug"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
"reflect"
"os"
)
// 结构体
type Data struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
data := Data{Name: "abcdefg"}
data1 := Data{}
data2 := Data{}
g.Redis().Do("SET", "goods:id", data)
v, _ := g.Redis().DoVar("GET", "goods:id")
v.Struct(&data1)
gconv.Struct(v, &data2)
fmt.Println(v, data1, data2)
fmt.Println(reflect.TypeOf(v), reflect.TypeOf(data1))
g.Dump(os.Args)
fmt.Println(gdebug.BuildInfo())
}

View File

@ -1,17 +0,0 @@
package main
import (
"fmt"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/util/gconv"
)
func main() {
type User struct {
Scores []int64
}
user := new(User)
err := gconv.Struct(g.Map{"scores": []interface{}{1, 2, 3}}, user)
fmt.Println(err)
fmt.Println(user)
}

View File

@ -233,15 +233,7 @@ func (v *Var) Map(tags ...string) map[string]interface{} {
// MapStrStr converts <v> to map[string]string.
func (v *Var) MapStrStr(tags ...string) map[string]string {
m := v.Map(tags...)
if len(m) > 0 {
vMap := make(map[string]string)
for k, v := range m {
vMap[k] = gconv.String(v)
}
return vMap
}
return nil
return gconv.MapStrStr(v.Val(), tags...)
}
// MapStrVar converts <v> to map[string]*Var.
@ -264,15 +256,7 @@ func (v *Var) MapDeep(tags ...string) map[string]interface{} {
// MapDeep converts <v> to map[string]string recursively.
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
m := v.MapDeep(tags...)
if len(m) > 0 {
vMap := make(map[string]string)
for k, v := range m {
vMap[k] = gconv.String(v)
}
return vMap
}
return nil
return gconv.MapStrStrDeep(v.Val(), tags...)
}
// MapStrVarDeep converts <v> to map[string]*Var recursively.

View File

@ -10,7 +10,6 @@ package gdebug
import (
"bytes"
"fmt"
"github.com/gogf/gf"
"path/filepath"
"reflect"
"runtime"
@ -29,9 +28,6 @@ const (
)
var (
buildTime = "" // Binary time string, which is injected from "go build -ldflags '-X importpath.name=value'".
buildGoVersion = "" // Binary go version, which is injected from "go build -ldflags '-X importpath.name=value'".
buildGitCommit = "" // Binary git commit, which is injected from "go build -ldflags '-X importpath.name=value'".
goRootForFilter = runtime.GOROOT() // goRootForFilter is used for stack filtering purpose.
binaryVersion = "" // The version of current running binary(uint64 hex).
binaryVersionMd5 = "" // The version of current running binary(MD5).
@ -43,18 +39,6 @@ func init() {
}
}
// BuildInfo returns the built information of the binary.
// Note that it should be used with gf-cli tool: gf build,
// which injects necessary information into the binary.
func BuildInfo() map[string]string {
return map[string]string{
"gf": gf.VERSION,
"go": buildGoVersion,
"git": buildGitCommit,
"time": buildTime,
}
}
// BinVersion returns the version of current running binary.
// It uses ghash.BKDRHash+BASE36 algorithm to calculate the unique version of the binary.
func BinVersion() string {

View File

@ -78,7 +78,7 @@ func (j *Json) GetVars(pattern string, def ...interface{}) []*gvar.Var {
return gvar.New(j.Get(pattern, def...)).Vars()
}
// GetMap gets the value by specified <pattern>,
// GetMap retrieves the value by specified <pattern>,
// and converts it to map[string]interface{}.
func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{} {
result := j.Get(pattern, def...)
@ -88,6 +88,16 @@ func (j *Json) GetMap(pattern string, def ...interface{}) map[string]interface{}
return nil
}
// GetMapStrStr retrieves the value by specified <pattern>,
// and converts it to map[string]string.
func (j *Json) GetMapStrStr(pattern string, def ...interface{}) map[string]string {
result := j.Get(pattern, def...)
if result != nil {
return gconv.MapStrStr(result)
}
return nil
}
// GetJson gets the value by specified <pattern>,
// and converts it to a un-concurrent-safe Json object.
func (j *Json) GetJson(pattern string, def ...interface{}) *Json {

72
os/gbuild/gbuild.go Normal file
View File

@ -0,0 +1,72 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package gbuild manages the build-in variables from "gf build".
package gbuild
import (
"encoding/json"
"github.com/gogf/gf"
"github.com/gogf/gf/container/gvar"
"github.com/gogf/gf/encoding/gbase64"
"github.com/gogf/gf/internal/intlog"
"github.com/gogf/gf/util/gconv"
"runtime"
)
var (
builtInVarStr = "" // Raw variable base64 string.
builtInVarMap = map[string]interface{}{} // Binary custom variable map decoded.
)
func init() {
if builtInVarStr != "" {
err := json.Unmarshal(gbase64.MustDecodeString(builtInVarStr), &builtInVarMap)
if err != nil {
intlog.Error(err)
}
builtInVarMap["gfVersion"] = gf.VERSION
builtInVarMap["goVersion"] = runtime.Version()
}
}
// Info returns the basic built information of the binary as map.
// Note that it should be used with gf-cli tool "gf build",
// which injects necessary information into the binary.
func Info() map[string]string {
return map[string]string{
"gf": GetString("gfVersion"),
"go": GetString("goVersion"),
"git": GetString("builtGit"),
"time": GetString("builtTime"),
}
}
// Get retrieves and returns the build-in binary variable with given name.
func Get(name string, def ...interface{}) interface{} {
if v, ok := builtInVarMap[name]; ok {
return v
}
if len(def) > 0 {
return def[0]
}
return nil
}
// Get retrieves and returns the build-in binary variable of given name as *gvar.Var.
func GetVar(name string, def ...interface{}) *gvar.Var {
return gvar.New(Get(name, def...))
}
// GetString retrieves and returns the build-in binary variable of given name as string.
func GetString(name string, def ...interface{}) string {
return gconv.String(Get(name, def...))
}
// Map returns the custom build-in variable map.
func Map() map[string]interface{} {
return builtInVarMap
}

View File

@ -44,6 +44,13 @@ func (c *Config) GetMap(pattern string, def ...interface{}) map[string]interface
return nil
}
func (c *Config) GetMapStrStr(pattern string, def ...interface{}) map[string]string {
if j := c.getJson(); j != nil {
return j.GetMapStrStr(pattern, def...)
}
return nil
}
func (c *Config) GetArray(pattern string, def ...interface{}) []interface{} {
if j := c.getJson(); j != nil {
return j.GetArray(pattern, def...)

View File

@ -13,7 +13,6 @@ import (
"io"
"os"
"runtime"
"strings"
"time"
"github.com/gogf/gf/os/gfile"
@ -36,7 +35,7 @@ func Pid() int {
return processPid
}
// 获取父进程ID(gproc父进程如果当前进程本身就是父进程那么返回自身的pid不存在时则使用系统父进程)
// PPid returns the custom parent pid if exists, or else it returns the system parent pid.
func PPid() int {
if !IsChild() {
return Pid()
@ -48,18 +47,22 @@ func PPid() int {
return PPidOS()
}
// 获取父进程ID(系统父进程)
// PPidOS returns the system parent pid of current process.
// Note that the difference between PPidOS and PPid function is that the PPidOS returns
// the system ppid, but the PPid functions may return the custom pid by gproc if the custom
// ppid exists.
func PPidOS() int {
return os.Getppid()
}
// 判断当前进程是否为gproc创建的子进程
// IsChild checks and returns whether current process is a child process.
// A child process is forked by another gproc process.
func IsChild() bool {
ppidValue := os.Getenv(gPROC_ENV_KEY_PPID_KEY)
return ppidValue != "" && ppidValue != "0"
}
// 设置gproc父进程ID当ppid为0时表示该进程为gproc主进程否则为gproc子进程
// SetPPid sets custom parent pid for current process.
func SetPPid(ppid int) error {
if ppid > 0 {
return os.Setenv(gPROC_ENV_KEY_PPID_KEY, gconv.String(ppid))
@ -68,50 +71,84 @@ func SetPPid(ppid int) error {
}
}
// 进程开始执行时间
// StartTime returns the start time of current process.
func StartTime() time.Time {
return processStartTime
}
// 进程已经运行的时间(毫秒)
func Uptime() int {
return int(time.Now().UnixNano()/1e6 - processStartTime.UnixNano()/1e6)
// Uptime returns the duration which current process has been running
func Uptime() time.Duration {
return time.Now().Sub(processStartTime)
}
// 阻塞执行shell指令并给定输入输出对象
// Shell executes command <cmd> synchronizingly with given input pipe <in> and output pipe <out>.
// The command <cmd> reads the input parameters from input pipe <in>, and writes its output automatically
// to output pipe <out>.
func Shell(cmd string, out io.Writer, in io.Reader) error {
p := NewProcess(getShell(), []string{getShellOption(), cmd})
p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...))
p.Stdin = in
p.Stdout = out
return p.Run()
}
// 阻塞执行shell指令并输出结果当终端(如果需要异步请使用goroutine)
// ShellRun executes given command <cmd> synchronizingly and outputs the command result to the stdout.
func ShellRun(cmd string) error {
p := NewProcess(getShell(), []string{getShellOption(), cmd})
p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...))
return p.Run()
}
// 阻塞执行shell指令并返回输出结果(如果需要异步请使用goroutine)
// ShellExec executes given command <cmd> synchronizingly and returns the command result.
func ShellExec(cmd string, environment ...[]string) (string, error) {
buf := bytes.NewBuffer(nil)
p := NewProcess(getShell(), []string{getShellOption(), cmd}, environment...)
p := NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...)
p.Stdout = buf
err := p.Run()
return buf.String(), err
}
// 检测环境变量中是否已经存在指定键名
func checkEnvKey(env []string, key string) bool {
for _, v := range env {
if len(v) >= len(key) && strings.EqualFold(v[0:len(key)], key) {
return true
// parseCommand parses command <cmd> into slice arguments.
//
// Note that it just parses the <cmd> for "cmd.exe" binary in windows, but it is not necessary
// parsing the <cmd> for other systems using "bash"/"sh" binary.
func parseCommand(cmd string) (args []string) {
if runtime.GOOS != "windows" {
return []string{cmd}
}
// Just for "cmd.exe" in windows.
var argStr string
var firstChar, prevChar, lastChar1, lastChar2 byte
array := gstr.SplitAndTrim(cmd, " ")
for _, v := range array {
if len(argStr) > 0 {
argStr += " "
}
firstChar = v[0]
lastChar1 = v[len(v)-1]
lastChar2 = 0
if len(v) > 1 {
lastChar2 = v[len(v)-2]
}
if prevChar == 0 && (firstChar == '"' || firstChar == '\'') {
// It should remove the first quote char.
argStr += v[1:]
prevChar = firstChar
} else if prevChar != 0 && lastChar2 != '\\' && lastChar1 == prevChar {
// It should remove the last quote char.
argStr += v[:len(v)-1]
args = append(args, argStr)
argStr = ""
prevChar = 0
} else if len(argStr) > 0 {
argStr += v
} else {
args = append(args, v)
}
}
return false
return
}
// 获取当前系统下的shell路径
// getShell returns the shell command depending on current working operation system.
// It returns "cmd.exe" for windows, and "bash" or "sh" for others.
func getShell() string {
switch runtime.GOOS {
case "windows":
@ -125,7 +162,8 @@ func getShell() string {
}
}
// 获取当前系统默认shell执行指令的option参数
// getShellOption returns the shell option depending on current working operation system.
// It returns "/c" for windows, and "-c" for others.
func getShellOption() string {
switch runtime.GOOS {
case "windows":
@ -135,7 +173,7 @@ func getShellOption() string {
}
}
// 从环境变量PATH中搜索可执行文件
// SearchBinary searches the binary <file> in current working folder and PATH environment.
func SearchBinary(file string) string {
// Check if it's absolute path of exists at current working directory.
if gfile.Exists(file) {

View File

@ -14,14 +14,14 @@ import (
"strings"
)
// 子进程
// Process is the struct for a single process.
type Process struct {
exec.Cmd
Manager *Manager // 所属进程管理器
PPid int // 自定义关联的父进程ID
Manager *Manager
PPid int
}
// 创建一个进程(不执行)
// NewProcess creates and returns a new Process.
func NewProcess(path string, args []string, environment ...[]string) *Process {
var env []string
if len(environment) > 0 {
@ -57,10 +57,11 @@ func NewProcess(path string, args []string, environment ...[]string) *Process {
// NewProcessCmd creates and returns a process with given command and optional environment variable array.
func NewProcessCmd(cmd string, environment ...[]string) *Process {
return NewProcess(getShell(), []string{getShellOption(), cmd}, environment...)
return NewProcess(getShell(), append([]string{getShellOption()}, parseCommand(cmd)...), environment...)
}
// 开始执行(非阻塞)
// Start starts executing the process in non-blocking way.
// It returns the pid if success, or else it returns an error.
func (p *Process) Start() (int, error) {
if p.Process != nil {
return p.Pid(), nil
@ -76,7 +77,7 @@ func (p *Process) Start() (int, error) {
}
}
// 运行进程(阻塞等待执行完毕)
// Run executes the process in blocking way.
func (p *Process) Run() error {
if _, err := p.Start(); err == nil {
return p.Wait()
@ -93,7 +94,7 @@ func (p *Process) Pid() int {
return 0
}
// 向进程发送消息
// Send send custom data to the process.
func (p *Process) Send(data []byte) error {
if p.Process != nil {
return Send(p.Process.Pid, data)

View File

@ -0,0 +1,26 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gproc
import (
"github.com/gogf/gf/test/gtest"
"testing"
)
func Test_parseCommand(t *testing.T) {
gtest.Case(t, func() {
commandMap := map[string]interface{}{
`cmd`: []string{`cmd`},
`cmd /c`: []string{`cmd`, `/c`},
`cmd /c go build`: []string{`cmd`, `/c`, `go`, `build`},
`cmd /c go build -ldflags "-X 'a=123' -X 'b=456'" test.go`: []string{`cmd`, `/c`, `go`, `build`, `-ldflags`, `"-X 'a=123' -X 'b=456'"`, `test.go`},
}
for k, v := range commandMap {
gtest.Assert(parseCommand(k), v)
}
})
}

View File

@ -196,6 +196,14 @@ func (t *Time) Truncate(d time.Duration) *Time {
return t
}
// Sub returns the duration t-u. If the result exceeds the maximum (or minimum)
// value that can be stored in a Duration, the maximum (or minimum) duration
// will be returned.
// To compute t-d for a duration d, use t.Add(-d).
func (t *Time) Sub(u *Time) time.Duration {
return t.Time.Sub(u.Time)
}
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
func (t *Time) MarshalJSON() ([]byte, error) {
return []byte(`"` + t.String() + `"`), nil

View File

@ -190,6 +190,40 @@ func MapDeep(value interface{}, tags ...string) map[string]interface{} {
return data
}
// MapStrStr converts <value> to map[string]string.
// Note that there might be data copy for this map type converting.
func MapStrStr(value interface{}, tags ...string) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
}
m := Map(value, tags...)
if len(m) > 0 {
vMap := make(map[string]string)
for k, v := range m {
vMap[k] = String(v)
}
return vMap
}
return nil
}
// MapStrStrDeep converts <value> to map[string]string recursively.
// Note that there might be data copy for this map type converting.
func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
if r, ok := value.(map[string]string); ok {
return r
}
m := MapDeep(value, tags...)
if len(m) > 0 {
vMap := make(map[string]string)
for k, v := range m {
vMap[k] = String(v)
}
return vMap
}
return nil
}
// MapToMap converts map type variable <params> to another map type variable <pointer> using reflect.
// The elements of <pointer> should be type of *map.
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {