Merge branch 'develop' into fix-required-tag

This commit is contained in:
John 2021-01-28 19:48:23 +08:00 committed by GitHub
commit d0bc4f76fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
481 changed files with 3119 additions and 173294 deletions

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ _testmain.go
bee
*.exe~
.goxc.local.json
vendor

View File

@ -11,4 +11,4 @@
}
},
"ConfigVersion": "0.9"
}
}

View File

@ -1,20 +1,21 @@
language: go
go:
- 1.12.5
- 1.14.6
install:
- export PATH=$PATH:$HOME/gopath/bin
- go get -u github.com/opennota/check/cmd/structcheck
- go get -u honnef.co/go/tools/cmd/gosimple
- go get -u honnef.co/go/tools/cmd/staticcheck
- go get -u honnef.co/go/tools/cmd/unused
- go get -u github.com/mdempsky/unconvert
- go get -u github.com/gordonklaus/ineffassign
script:
- pwd
- cd $(dirname `dirname $(pwd)`)/beego/bee
- export GO111MODULE="on"
- go mod download
- find . ! \( -path './vendor' -prune \) -type f -name '*.go' -print0 | xargs -0 gofmt -l -s
- go vet $(go list ./... | grep -v /vendor/)
- structcheck $(go list ./... | grep -v /vendor/)
- gosimple -ignore "$(cat gosimple.ignore)" $(go list ./... | grep -v /vendor/)
- staticcheck -ignore "$(cat staticcheck.ignore)" $(go list ./... | grep -v /vendor/)
- unused $(go list ./... | grep -v /vendor/)
- unconvert $(go list ./... | grep -v /vendor/)
- go list ./... | grep -v /vendor/ | grep -v /pkg/mod/
- go vet $(go list ./... | grep -v /vendor/ | grep -v /pkg/mod/ )
- structcheck $(go list ./... | grep -v /vendor/ | grep -v /pkg/mod/ )
- staticcheck $(go list ./... | grep -v /vendor/ | grep -v /pkg/mod/ )
- unconvert $(go list ./... | grep -v /vendor/ | grep -v /pkg/mod/ )
- ineffassign .

View File

@ -188,4 +188,4 @@ third-party archives.
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.
limitations under the License.

View File

@ -24,6 +24,6 @@ clean:
publish:
mkdir -p bin/$(VERSION)
cd bin/$(VERSION)
xgo -v -x --targets="windows/*,darwin/*,linux/386,linux/amd64,linux/arm-5,linux/arm64" -out bee_$(VERSION) github.com/beego/bee
xgo -v -x --targets="windows/*,darwin/*,linux/386,linux/amd64,linux/arm-5,linux/arm64" -out bee_$(VERSION) github.com/beego/bee/v2
cd ..
ghr -u beego -r bee $(VERSION) $(VERSION)
ghr -u beego -r bee $(VERSION) $(VERSION)

250
README.md
View File

@ -9,14 +9,14 @@ Bee is a command-line tool facilitating development of Beego-based application.
## Requirements
- Go version >= 1.3.
- Go version >= 1.13
## Installation
To install `bee` use the `go get` command:
```bash
go get github.com/beego/bee
go get github.com/beego/bee/v2
```
Then you can add `bee` binary to PATH environment variable in your `~/.bashrc` or `~/.bash_profile` file:
@ -28,7 +28,7 @@ export PATH=$PATH:<your_main_gopath>/bin
> If you already have `bee` installed, updating `bee` is simple:
```bash
go get -u github.com/beego/bee
go get -u github.com/beego/bee/v2
```
## Basic commands
@ -41,6 +41,7 @@ Bee provides a variety of commands which can be helpful at various stages of dev
api Creates a Beego API application
bale Transforms non-Go files to Go source files
fix Fixes your application by making it compatible with newer versions of Beego
pro Source code generator
dlv Start a debugging session using Delve
dockerize Generates a Dockerfile for your Beego application
generate Source code generator
@ -49,7 +50,8 @@ Bee provides a variety of commands which can be helpful at various stages of dev
pack Compresses a Beego application into a single file
rs Run customized scripts
run Run the application by starting a local development server
server serving static content over HTTP on port
update Update Bee
```
### bee version
@ -63,17 +65,17 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
\____/ \___| \___| v2.0.0
├── Beego : 1.7.2
├── GoVersion : go1.7.4
├── GOOS : linux
├── Beego : 2.0.0
├── GoVersion : go1.14.1
├── GOOS : darwin
├── GOARCH : amd64
├── NumCPU : 2
├── NumCPU : 4
├── GOPATH : /home/beeuser/.go
├── GOROOT : /usr/lib/go
├── GOROOT : /usr/local/Cellar/go/1.14.1/libexec
├── Compiler : gc
└── Date : Monday, 26 Dec 2016
└── Published : 2020-09-13
```
You can also change the output format using `-o` flag:
@ -81,15 +83,16 @@ You can also change the output format using `-o` flag:
```bash
$ bee version -o json
{
"GoVersion": "go1.7.4",
"GOOS": "linux",
"GoVersion": "go1.14.1",
"GOOS": "darwin",
"GOARCH": "amd64",
"NumCPU": 2,
"NumCPU": 4,
"GOPATH": "/home/beeuser/.go",
"GOROOT": "/usr/lib/go",
"GOROOT": "/usr/local/Cellar/go/1.14.1/libexec",
"Compiler": "gc",
"BeeVersion": "1.6.2",
"BeegoVersion": "1.7.2"
"BeeVersion": "2.0.0",
"BeegoVersion": "2.0.0",
"Published": "2020-09-13"
}
```
@ -101,31 +104,27 @@ To create a new Beego web application:
```bash
$ bee new my-web-app
______
| ___ \
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:28:11 INFO ▶ 0001 Creating application...
create /home/beeuser/.go/src/github.com/user/my-web-app/
create /home/beeuser/.go/src/github.com/user/my-web-app/conf/
create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/
create /home/beeuser/.go/src/github.com/user/my-web-app/models/
create /home/beeuser/.go/src/github.com/user/my-web-app/routers/
create /home/beeuser/.go/src/github.com/user/my-web-app/tests/
create /home/beeuser/.go/src/github.com/user/my-web-app/static/
create /home/beeuser/.go/src/github.com/user/my-web-app/static/js/
create /home/beeuser/.go/src/github.com/user/my-web-app/static/css/
create /home/beeuser/.go/src/github.com/user/my-web-app/static/img/
create /home/beeuser/.go/src/github.com/user/my-web-app/views/
create /home/beeuser/.go/src/github.com/user/my-web-app/conf/app.conf
create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/default.go
create /home/beeuser/.go/src/github.com/user/my-web-app/views/index.tpl
create /home/beeuser/.go/src/github.com/user/my-web-app/routers/router.go
create /home/beeuser/.go/src/github.com/user/my-web-app/tests/default_test.go
create /home/beeuser/.go/src/github.com/user/my-web-app/main.go
2016/12/26 22:28:11 SUCCESS ▶ 0002 New application successfully created!
2020/09/14 22:28:51 INFO ▶ 0001 generate new project support go modules.
2020/09/14 22:28:51 INFO ▶ 0002 Creating application...
create /Users/beeuser/learn/my-web-app/go.mod
create /Users/beeuser/learn/my-web-app/
create /Users/beeuser/learn/my-web-app/conf/
create /Users/beeuser/learn/my-web-app/controllers/
create /Users/beeuser/learn/my-web-app/models/
create /Users/beeuser/learn/my-web-app/routers/
create /Users/beeuser/learn/my-web-app/tests/
create /Users/beeuser/learn/my-web-app/static/
create /Users/beeuser/learn/my-web-app/static/js/
create /Users/beeuser/learn/my-web-app/static/css/
create /Users/beeuser/learn/my-web-app/static/img/
create /Users/beeuser/learn/my-web-app/views/
create /Users/beeuser/learn/my-web-app/conf/app.conf
create /Users/beeuser/learn/my-web-app/controllers/default.go
create /Users/beeuser/learn/my-web-app/views/index.tpl
create /Users/beeuser/learn/my-web-app/routers/router.go
create /Users/beeuser/learn/my-web-app/tests/default_test.go
create /Users/beeuser/learn/my-web-app/main.go
2020/09/14 22:28:51 SUCCESS ▶ 0003 New application successfully created!
```
For more information on the usage, run `bee help new`.
@ -138,12 +137,6 @@ To run the application we just created, you can navigate to the application fold
$ cd my-web-app && bee run
```
Or from anywhere in your machine:
```
$ bee run github.com/user/my-web-app
```
For more information on the usage, run `bee help run`.
### bee pack
@ -157,7 +150,7 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
\____/ \___| \___| v2.0.0
2016/12/26 22:29:29 INFO ▶ 0001 Packaging application on '/home/beeuser/.go/src/github.com/user/my-web-app'...
2016/12/26 22:29:29 INFO ▶ 0002 Building application...
2016/12/26 22:29:29 INFO ▶ 0003 Using: GOOS=linux GOARCH=amd64
@ -213,23 +206,25 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:30:12 INFO ▶ 0001 Creating API...
create /home/beeuser/.go/src/github.com/user/my-api
create /home/beeuser/.go/src/github.com/user/my-api/conf
create /home/beeuser/.go/src/github.com/user/my-api/controllers
create /home/beeuser/.go/src/github.com/user/my-api/tests
create /home/beeuser/.go/src/github.com/user/my-api/conf/app.conf
create /home/beeuser/.go/src/github.com/user/my-api/models
create /home/beeuser/.go/src/github.com/user/my-api/routers/
create /home/beeuser/.go/src/github.com/user/my-api/controllers/object.go
create /home/beeuser/.go/src/github.com/user/my-api/controllers/user.go
create /home/beeuser/.go/src/github.com/user/my-api/tests/default_test.go
create /home/beeuser/.go/src/github.com/user/my-api/routers/router.go
create /home/beeuser/.go/src/github.com/user/my-api/models/object.go
create /home/beeuser/.go/src/github.com/user/my-api/models/user.go
create /home/beeuser/.go/src/github.com/user/my-api/main.go
2016/12/26 22:30:12 SUCCESS ▶ 0002 New API successfully created!
\____/ \___| \___| v2.0.0
2020/09/14 22:35:11 INFO ▶ 0001 generate api project support go modules.
2020/09/14 22:35:11 INFO ▶ 0002 Creating API...
create /Users/beeuser/code/learn/my-api/go.mod
create /Users/beeuser/code/learn/my-api
create /Users/beeuser/code/learn/my-api/conf
create /Users/beeuser/code/learn/my-api/controllers
create /Users/beeuser/code/learn/my-api/tests
create /Users/beeuser/code/learn/my-api/conf/app.conf
create /Users/beeuser/code/learn/my-api/models
create /Users/beeuser/code/learn/my-api/routers/
create /Users/beeuser/code/learn/my-api/controllers/object.go
create /Users/beeuser/code/learn/my-api/controllers/user.go
create /Users/beeuser/code/learn/my-api/tests/default_test.go
create /Users/beeuser/code/learn/my-api/routers/router.go
create /Users/beeuser/code/learn/my-api/models/object.go
create /Users/beeuser/code/learn/my-api/models/user.go
create /Users/beeuser/code/learn/my-api/main.go
2020/09/14 22:35:11 SUCCESS ▶ 0003 New API successfully created!
```
For more information on the usage, run `bee help api`.
@ -245,26 +240,18 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:30:58 INFO ▶ 0001 Creating application...
create /home/beeuser/.go/src/github.com/user/my-rpc-app/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/conf/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/controllers/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/models/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/routers/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/tests/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/js/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/css/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/static/img/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/views/
create /home/beeuser/.go/src/github.com/user/my-rpc-app/conf/app.conf
create /home/beeuser/.go/src/github.com/user/my-rpc-app/controllers/default.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/views/index.tpl
create /home/beeuser/.go/src/github.com/user/my-rpc-app/routers/router.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/tests/default_test.go
create /home/beeuser/.go/src/github.com/user/my-rpc-app/main.go
2016/12/26 22:30:58 SUCCESS ▶ 0002 New application successfully created!
\____/ \___| \___| v2.0.0
2020/09/14 22:36:39 INFO ▶ 0001 generate api project support go modules.
2020/09/14 22:36:39 INFO ▶ 0002 Creating Hprose application...
create /Users/beeuser/code/learn/my-rpc-app/go.mod
create /Users/beeuser/code/learn/my-rpc-app
create /Users/beeuser/code/learn/my-rpc-app/conf
create /Users/beeuser/code/learn/my-rpc-app/conf/app.conf
create /Users/beeuser/code/learn/my-rpc-app/models
create /Users/beeuser/code/learn/my-rpc-app/models/object.go
create /Users/beeuser/code/learn/my-rpc-app/models/user.go
create /Users/beeuser/code/learn/my-rpc-app/main.go
2020/09/14 22:36:39 SUCCESS ▶ 0003 New Hprose application successfully created!
```
For more information on the usage, run `bee help hprose`.
@ -280,9 +267,8 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:32:41 INFO ▶ 0001 Loading configuration from 'bee.json'...
2016/12/26 22:32:41 SUCCESS ▶ 0002 Baled resources successfully!
\____/ \___| \___| v2.0.0
2020/09/14 22:37:56 SUCCESS ▶ 0001 Baled resources successfully!
```
For more information on the usage, run `bee help bale`.
@ -306,11 +292,11 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:33:58 INFO ▶ 0001 Using 'Hello' as controller name
2016/12/26 22:33:58 INFO ▶ 0002 Using 'controllers' as package name
create /home/beeuser/.go/src/github.com/user/my-web-app/controllers/hello.go
2016/12/26 22:33:58 SUCCESS ▶ 0003 Controller successfully generated!
\____/ \___| \___| v2.0.0
2020/09/14 22:38:44 INFO ▶ 0001 Using 'Hello' as controller name
2020/09/14 22:38:44 INFO ▶ 0002 Using 'controllers' as package name
create /Users/beeuser/code/learn/my-api/controllers/hello.go
2020/09/14 22:38:44 SUCCESS ▶ 0003 Controller successfully generated!
```
For more information on the usage, run `bee help generate`.
@ -328,9 +314,9 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.6.2
2016/12/26 22:34:54 INFO ▶ 0001 Generating Dockerfile...
2016/12/26 22:34:54 SUCCESS ▶ 0002 Dockerfile generated.
\____/ \___| \___| v2.0.0
2020/09/14 22:40:12 INFO ▶ 0001 Generating Dockerfile...
2020/09/14 22:40:12 SUCCESS ▶ 0002 Dockerfile generated.
```
For more information on the usage, run `bee help dockerize`.
@ -345,8 +331,8 @@ ______
| |_/ / ___ ___
| ___ \ / _ \ / _ \
| |_/ /| __/| __/
\____/ \___| \___| v1.8.0
2017/03/22 11:17:05 INFO ▶ 0001 Starting Delve Debugger...
\____/ \___| \___| v2.0.0
2020/09/14 22:40:12 INFO ▶ 0001 Starting Delve Debugger...
Type 'help' for list of commands.
(dlv) break main.main
Breakpoint 1 set at 0x40100f for main.main() ./main.go:8
@ -355,7 +341,7 @@ Breakpoint 1 set at 0x40100f for main.main() ./main.go:8
> main.main() ./main.go:8 (hits goroutine(1):1 total:1) (PC: 0x40100f)
3: import (
4: _ "github.com/user/myapp/routers"
5: "github.com/astaxie/beego"
5: beego "github.com/beego/beego/v2/server/web"
6: )
7:
=> 8: func main() {
@ -366,6 +352,68 @@ Breakpoint 1 set at 0x40100f for main.main() ./main.go:8
For more information on the usage, run `bee help dlv`.
### bee pro
#### bee pro toml
To create a beegopro.toml file
```bash
$ bee pro toml
2020/09/14 22:51:18 SUCCESS ▶ 0001 Successfully created file beegopro.toml
2020/09/14 22:51:18 SUCCESS ▶ 0002 Toml successfully generated!
```
#### bee pro gen
Source code generator by beegopro.toml
```bash
$ bee pro gen
2020/09/14 23:01:13 INFO ▶ 0001 Create /Users/beeuser/.beego/beego-pro Success!
2020/09/14 23:01:13 INFO ▶ 0002 git pull /Users/beeuser/.beego/beego-pro
2020/09/14 23:01:15 INFO ▶ 0003 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0004 Using 'example' as package name from controllers
2020/09/14 23:01:15 INFO ▶ 0005 create file '/Users/beeuser/code/learn/my-web-app/controllers/bee_default_controller.go' from controllers
2020/09/14 23:01:15 INFO ▶ 0006 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0007 Using 'example' as package name from controllers
2020/09/14 23:01:15 INFO ▶ 0008 create file '/Users/beeuser/code/learn/my-web-app/controllers/example.go' from controllers
2020/09/14 23:01:15 INFO ▶ 0009 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0010 Using 'example' as package name from models
2020/09/14 23:01:15 INFO ▶ 0011 create file '/Users/beeuser/code/learn/my-web-app/models/bee_default_model.go' from models
2020/09/14 23:01:15 INFO ▶ 0012 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0013 Using 'example' as package name from models
2020/09/14 23:01:15 INFO ▶ 0014 create file '/Users/beeuser/code/learn/my-web-app/models/example.go' from models
2020/09/14 23:01:15 INFO ▶ 0015 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0016 Using 'example' as package name from routers
2020/09/14 23:01:15 INFO ▶ 0017 create file '/Users/beeuser/code/learn/my-web-app/routers/example.go' from routers
2020/09/14 23:01:15 INFO ▶ 0018 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0019 Using 'example' as package name from example
2020/09/14 23:01:15 INFO ▶ 0020 create file '/Users/beeuser/code/learn/my-web-app/ant/src/pages/example/list.tsx' from example
2020/09/14 23:01:15 INFO ▶ 0021 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0022 Using 'example' as package name from example
2020/09/14 23:01:15 INFO ▶ 0023 create file '/Users/beeuser/code/learn/my-web-app/ant/src/pages/example/formconfig.tsx' from example
2020/09/14 23:01:15 INFO ▶ 0024 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0025 Using 'example' as package name from example
2020/09/14 23:01:15 INFO ▶ 0026 create file '/Users/beeuser/code/learn/my-web-app/ant/src/pages/example/create.tsx' from example
2020/09/14 23:01:15 INFO ▶ 0027 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0028 Using 'example' as package name from example
2020/09/14 23:01:15 INFO ▶ 0029 create file '/Users/beeuser/code/learn/my-web-app/ant/src/pages/example/update.tsx' from example
2020/09/14 23:01:15 INFO ▶ 0030 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0031 Using 'example' as package name from example
2020/09/14 23:01:15 INFO ▶ 0032 create file '/Users/beeuser/code/learn/my-web-app/ant/src/pages/example/info.tsx' from example
2020/09/14 23:01:15 INFO ▶ 0033 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0034 Using 'example' as package name from sql
2020/09/14 23:01:15 INFO ▶ 0035 create file '/Users/beeuser/code/learn/my-web-app/sql/example_up.sql' from sql
2020/09/14 23:01:15 INFO ▶ 0036 2020/09/14 23:01:15 INFO ▶ 0001 db exec info ./sql/example_up.sql
2020/09/14 23:01:15 SUCCESS ▶ 0002 Migration successfully generated!
2020/09/14 23:01:15 INFO ▶ 0037 Using 'example' as name
2020/09/14 23:01:15 INFO ▶ 0038 Using 'example' as package name from sql
2020/09/14 23:01:15 INFO ▶ 0039 create file '/Users/beeuser/code/learn/my-web-app/sql/example_down.sql' from sql
2020/09/14 23:01:15 SUCCESS ▶ 0040 Gen successfully generated!
```
####
## Shortcuts
Because you'll likely type these generator commands over and over, it makes sense to create aliases:
@ -520,7 +568,7 @@ Rebasing will update your branch with the most recent code and make your changes
## Licence
```text
Copyright 2016 bee authors
Copyright 2020 bee authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -15,4 +15,4 @@
"driver": "mysql"
},
"enable_reload": false
}
}

View File

@ -16,22 +16,25 @@
package cmd
import (
"github.com/beego/bee/cmd/commands"
_ "github.com/beego/bee/cmd/commands/api"
_ "github.com/beego/bee/cmd/commands/bale"
_ "github.com/beego/bee/cmd/commands/beefix"
_ "github.com/beego/bee/cmd/commands/dlv"
_ "github.com/beego/bee/cmd/commands/dockerize"
_ "github.com/beego/bee/cmd/commands/generate"
_ "github.com/beego/bee/cmd/commands/hprose"
_ "github.com/beego/bee/cmd/commands/migrate"
_ "github.com/beego/bee/cmd/commands/new"
_ "github.com/beego/bee/cmd/commands/pack"
_ "github.com/beego/bee/cmd/commands/rs"
_ "github.com/beego/bee/cmd/commands/run"
_ "github.com/beego/bee/cmd/commands/server"
_ "github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
_ "github.com/beego/bee/v2/cmd/commands/api"
_ "github.com/beego/bee/v2/cmd/commands/bale"
_ "github.com/beego/bee/v2/cmd/commands/beefix"
_ "github.com/beego/bee/v2/cmd/commands/beegopro"
_ "github.com/beego/bee/v2/cmd/commands/dev"
_ "github.com/beego/bee/v2/cmd/commands/dlv"
_ "github.com/beego/bee/v2/cmd/commands/dockerize"
_ "github.com/beego/bee/v2/cmd/commands/generate"
_ "github.com/beego/bee/v2/cmd/commands/hprose"
_ "github.com/beego/bee/v2/cmd/commands/migrate"
_ "github.com/beego/bee/v2/cmd/commands/new"
_ "github.com/beego/bee/v2/cmd/commands/pack"
_ "github.com/beego/bee/v2/cmd/commands/rs"
_ "github.com/beego/bee/v2/cmd/commands/run"
_ "github.com/beego/bee/v2/cmd/commands/server"
_ "github.com/beego/bee/v2/cmd/commands/update"
_ "github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/utils"
)
func IfGenerateDocs(name string, args []string) bool {
@ -48,6 +51,8 @@ func IfGenerateDocs(name string, args []string) bool {
var usageTemplate = `Bee is a Fast and Flexible tool for managing your Beego Web Application.
You are using bee for beego v2.x. If you are working on beego v1.x, please downgrade version to bee v1.12.0
{{"USAGE" | headline}}
{{"bee command [arguments]" | bold}}
@ -85,6 +90,7 @@ func Usage() {
func Help(args []string) {
if len(args) == 0 {
Usage()
return
}
if len(args) != 1 {
utils.PrintErrorAndExit("Too many arguments", ErrorTemplate)

View File

@ -20,11 +20,13 @@ import (
path "path/filepath"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/generate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/generate"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdApiapp = &commands.Command{
@ -33,9 +35,10 @@ var CmdApiapp = &commands.Command{
Short: "Creates a Beego API application",
Long: `
The command 'api' creates a Beego API application.
now default supoort generate a go modules project.
{{"Example:"|bold}}
$ bee api [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
$ bee api [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-gopath=false] [-beego=v1.12.3]
If 'conn' argument is empty, the command will generate an example API application. Otherwise the command
will connect to your database and generate models based on the existing tables.
@ -43,6 +46,7 @@ var CmdApiapp = &commands.Command{
The command 'api' creates a folder named [appname] with the following structure:
main.go
go.mod
{{"conf"|foldername}}
app.conf
{{"controllers"|foldername}}
@ -72,7 +76,7 @@ var apiMaingo = `package main
import (
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func main() {
@ -89,8 +93,8 @@ var apiMainconngo = `package main
import (
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/client/orm"
{{.DriverPkg}}
)
@ -103,6 +107,14 @@ func main() {
beego.Run()
}
`
var goMod = `
module %s
go %s
require github.com/beego/beego/v2 %s
require github.com/smartystreets/goconvey v1.6.4
`
var apirouter = `// @APIVersion 1.0.0
@ -117,7 +129,7 @@ package routers
import (
"{{.Appname}}/controllers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func init() {
@ -286,7 +298,7 @@ import (
"{{.Appname}}/models"
"encoding/json"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about object
@ -379,7 +391,7 @@ import (
"{{.Appname}}/models"
"encoding/json"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
// Operations about Users
@ -504,7 +516,8 @@ import (
"path/filepath"
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/core/logs"
. "github.com/smartystreets/goconvey/convey"
)
@ -520,7 +533,7 @@ func TestGet(t *testing.T) {
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
beego.Trace("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String())
logs.Info("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String())
Convey("Subject: Test Station Endpoint\n", t, func() {
Convey("Status Code Should Be 200", func() {
@ -533,11 +546,15 @@ func TestGet(t *testing.T) {
}
`
var gopath utils.DocValue
var beegoVersion utils.DocValue
func init() {
CmdApiapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.")
CmdApiapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
CmdApiapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.")
CmdApiapp.Flag.Var(&gopath, "gopath", "Support go path,default false")
CmdApiapp.Flag.Var(&beegoVersion, "beego", "set beego version,only take effect by go mod")
commands.AvailableCommands = append(commands.AvailableCommands, CmdApiapp)
}
@ -548,14 +565,39 @@ func createAPI(cmd *commands.Command, args []string) int {
beeLogger.Log.Fatal("Argument [appname] is missing")
}
if len(args) > 1 {
if len(args) >= 2 {
err := cmd.Flag.Parse(args[1:])
if err != nil {
beeLogger.Log.Error(err.Error())
beeLogger.Log.Fatal("Parse args err " + err.Error())
}
}
var appPath string
var packPath string
var err error
if gopath == `true` {
beeLogger.Log.Info("Generate api project support GOPATH")
version.ShowShortVersionBanner()
appPath, packPath, err = utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
}
} else {
beeLogger.Log.Info("Generate api project support go modules.")
appPath = path.Join(utils.GetBeeWorkPath(), args[0])
packPath = args[0]
if beegoVersion.String() == `` {
beegoVersion.Set(utils.BEEGO_VERSION)
}
}
if utils.IsExist(appPath) {
beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), appPath)
beeLogger.Log.Warn(colors.Bold("Do you want to overwrite it? [Yes|No] "))
if !utils.AskForConfirmation() {
os.Exit(2)
}
}
appPath, packPath, err := utils.CheckEnv(args[0])
appName := path.Base(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
@ -567,6 +609,10 @@ func createAPI(cmd *commands.Command, args []string) int {
beeLogger.Log.Info("Creating API...")
os.MkdirAll(appPath, 0755)
if gopath != `true` { //generate first for calc model name
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "go.mod"), "\x1b[0m")
utils.WriteToFile(path.Join(appPath, "go.mod"), fmt.Sprintf(goMod, packPath, utils.GetGoVersionSkipMinor(), beegoVersion.String()))
}
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", appPath, "\x1b[0m")
os.Mkdir(path.Join(appPath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf"), "\x1b[0m")

View File

@ -24,11 +24,11 @@ import (
"runtime"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/config"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdBale = &commands.Command{

View File

@ -1,232 +1,48 @@
package beefix
import (
"fmt"
"go/parser"
"go/token"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdFix = &commands.Command{
UsageLine: "fix",
Short: "Fixes your application by making it compatible with newer versions of Beego",
Long: `As of {{"Beego 1.6"|bold}}, there are some backward compatibility issues.
Long: `
The command 'fix' will try to solve those issues by upgrading your code base
to be compatible with Beego version 1.6+.
to be compatible with Beego old version
-s source version
-t target version
example: bee fix -s 1 -t 2 means that upgrade Beego version from v1.x to v2.x
`,
}
var (
source, target utils.DocValue
)
func init() {
CmdFix.Run = runFix
CmdFix.PreRun = func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() }
CmdFix.Flag.Var(&source, "s", "source version")
CmdFix.Flag.Var(&target, "t", "target version")
commands.AvailableCommands = append(commands.AvailableCommands, CmdFix)
}
func runFix(cmd *commands.Command, args []string) int {
output := cmd.Out()
beeLogger.Log.Info("Upgrading the application...")
dir, err := os.Getwd()
if err != nil {
beeLogger.Log.Fatalf("Error while getting the current working directory: %s", err)
t := target.String()
if t == "" || t == "1.6" {
return fixTo16(cmd, args)
} else if strings.HasPrefix(t, "2") {
// upgrade to v2
return fix1To2()
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
if strings.HasPrefix(info.Name(), ".") {
return filepath.SkipDir
}
return nil
}
if err != nil {
return err
}
if strings.HasSuffix(info.Name(), ".exe") {
return nil
}
err = fixFile(path)
fmt.Fprintf(output, colors.GreenBold("\tfix\t")+"%s\n", path)
if err != nil {
beeLogger.Log.Errorf("Could not fix file: %s", err)
}
return err
})
beeLogger.Log.Success("Upgrade Done!")
beeLogger.Log.Info("The target is compatible version, do nothing")
return 0
}
var rules = []string{
"beego.AppName", "beego.BConfig.AppName",
"beego.RunMode", "beego.BConfig.RunMode",
"beego.RecoverPanic", "beego.BConfig.RecoverPanic",
"beego.RouterCaseSensitive", "beego.BConfig.RouterCaseSensitive",
"beego.BeegoServerName", "beego.BConfig.ServerName",
"beego.EnableGzip", "beego.BConfig.EnableGzip",
"beego.ErrorsShow", "beego.BConfig.EnableErrorsShow",
"beego.CopyRequestBody", "beego.BConfig.CopyRequestBody",
"beego.MaxMemory", "beego.BConfig.MaxMemory",
"beego.Graceful", "beego.BConfig.Listen.Graceful",
"beego.HttpAddr", "beego.BConfig.Listen.HTTPAddr",
"beego.HttpPort", "beego.BConfig.Listen.HTTPPort",
"beego.ListenTCP4", "beego.BConfig.Listen.ListenTCP4",
"beego.EnableHttpListen", "beego.BConfig.Listen.EnableHTTP",
"beego.EnableHttpTLS", "beego.BConfig.Listen.EnableHTTPS",
"beego.HttpsAddr", "beego.BConfig.Listen.HTTPSAddr",
"beego.HttpsPort", "beego.BConfig.Listen.HTTPSPort",
"beego.HttpCertFile", "beego.BConfig.Listen.HTTPSCertFile",
"beego.HttpKeyFile", "beego.BConfig.Listen.HTTPSKeyFile",
"beego.EnableAdmin", "beego.BConfig.Listen.EnableAdmin",
"beego.AdminHttpAddr", "beego.BConfig.Listen.AdminAddr",
"beego.AdminHttpPort", "beego.BConfig.Listen.AdminPort",
"beego.UseFcgi", "beego.BConfig.Listen.EnableFcgi",
"beego.HttpServerTimeOut", "beego.BConfig.Listen.ServerTimeOut",
"beego.AutoRender", "beego.BConfig.WebConfig.AutoRender",
"beego.ViewsPath", "beego.BConfig.WebConfig.ViewsPath",
"beego.StaticDir", "beego.BConfig.WebConfig.StaticDir",
"beego.StaticExtensionsToGzip", "beego.BConfig.WebConfig.StaticExtensionsToGzip",
"beego.DirectoryIndex", "beego.BConfig.WebConfig.DirectoryIndex",
"beego.FlashName", "beego.BConfig.WebConfig.FlashName",
"beego.FlashSeperator", "beego.BConfig.WebConfig.FlashSeparator",
"beego.EnableDocs", "beego.BConfig.WebConfig.EnableDocs",
"beego.XSRFKEY", "beego.BConfig.WebConfig.XSRFKey",
"beego.EnableXSRF", "beego.BConfig.WebConfig.EnableXSRF",
"beego.XSRFExpire", "beego.BConfig.WebConfig.XSRFExpire",
"beego.TemplateLeft", "beego.BConfig.WebConfig.TemplateLeft",
"beego.TemplateRight", "beego.BConfig.WebConfig.TemplateRight",
"beego.SessionOn", "beego.BConfig.WebConfig.Session.SessionOn",
"beego.SessionProvider", "beego.BConfig.WebConfig.Session.SessionProvider",
"beego.SessionName", "beego.BConfig.WebConfig.Session.SessionName",
"beego.SessionGCMaxLifetime", "beego.BConfig.WebConfig.Session.SessionGCMaxLifetime",
"beego.SessionSavePath", "beego.BConfig.WebConfig.Session.SessionProviderConfig",
"beego.SessionCookieLifeTime", "beego.BConfig.WebConfig.Session.SessionCookieLifeTime",
"beego.SessionAutoSetCookie", "beego.BConfig.WebConfig.Session.SessionAutoSetCookie",
"beego.SessionDomain", "beego.BConfig.WebConfig.Session.SessionDomain",
"Ctx.Input.CopyBody(", "Ctx.Input.CopyBody(beego.BConfig.MaxMemory",
".UrlFor(", ".URLFor(",
".ServeJson(", ".ServeJSON(",
".ServeXml(", ".ServeXML(",
".ServeJsonp(", ".ServeJSONP(",
".XsrfToken(", ".XSRFToken(",
".CheckXsrfCookie(", ".CheckXSRFCookie(",
".XsrfFormHtml(", ".XSRFFormHTML(",
"beego.UrlFor(", "beego.URLFor(",
"beego.GlobalDocApi", "beego.GlobalDocAPI",
"beego.Errorhandler", "beego.ErrorHandler",
"Output.Jsonp(", "Output.JSONP(",
"Output.Json(", "Output.JSON(",
"Output.Xml(", "Output.XML(",
"Input.Uri()", "Input.URI()",
"Input.Url()", "Input.URL()",
"Input.AcceptsHtml()", "Input.AcceptsHTML()",
"Input.AcceptsXml()", "Input.AcceptsXML()",
"Input.AcceptsJson()", "Input.AcceptsJSON()",
"Ctx.XsrfToken()", "Ctx.XSRFToken()",
"Ctx.CheckXsrfCookie()", "Ctx.CheckXSRFCookie()",
"session.SessionStore", "session.Store",
".TplNames", ".TplName",
"swagger.ApiRef", "swagger.APIRef",
"swagger.ApiDeclaration", "swagger.APIDeclaration",
"swagger.Api", "swagger.API",
"swagger.ApiRef", "swagger.APIRef",
"swagger.Infomation", "swagger.Information",
"toolbox.UrlMap", "toolbox.URLMap",
"logs.LoggerInterface", "logs.Logger",
"Input.Request", "Input.Context.Request",
"Input.Params)", "Input.Params())",
"httplib.BeegoHttpSettings", "httplib.BeegoHTTPSettings",
"httplib.BeegoHttpRequest", "httplib.BeegoHTTPRequest",
".TlsClientConfig", ".TLSClientConfig",
".JsonBody", ".JSONBody",
".ToJson", ".ToJSON",
".ToXml", ".ToXML",
"beego.Html2str", "beego.HTML2str",
"beego.AssetsCss", "beego.AssetsCSS",
"orm.DR_Sqlite", "orm.DRSqlite",
"orm.DR_Postgres", "orm.DRPostgres",
"orm.DR_MySQL", "orm.DRMySQL",
"orm.DR_Oracle", "orm.DROracle",
"orm.Col_Add", "orm.ColAdd",
"orm.Col_Minus", "orm.ColMinus",
"orm.Col_Multiply", "orm.ColMultiply",
"orm.Col_Except", "orm.ColExcept",
"GenerateOperatorSql", "GenerateOperatorSQL",
"OperatorSql", "OperatorSQL",
"orm.Debug_Queries", "orm.DebugQueries",
"orm.COMMA_SPACE", "orm.CommaSpace",
".SendOut()", ".DoRequest()",
"validation.ValidationError", "validation.Error",
}
func fixFile(file string) error {
rp := strings.NewReplacer(rules...)
content, err := ioutil.ReadFile(file)
if err != nil {
return err
}
fixed := rp.Replace(string(content))
// Forword the RequestBody from the replace
// "Input.Request", "Input.Context.Request",
fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1)
// Regexp replace
pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`)
fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")")
// Fix the cache object Put method
pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`)
if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") {
fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)")
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
if err != nil {
panic(err)
}
// Print the imports from the file's AST.
hasTimepkg := false
for _, s := range f.Imports {
if s.Path.Value == `"time"` {
hasTimepkg = true
break
}
}
if !hasTimepkg {
fixed = strings.Replace(fixed, "import (", "import (\n\t\"time\"", 1)
}
}
// Replace the v.Apis in docs.go
if strings.Contains(file, "docs.go") {
fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1)
}
// Replace the config file
if strings.HasSuffix(file, ".conf") {
fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1)
fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1)
fixed = strings.Replace(fixed, "EnableHttpListen", "HTTPEnable", -1)
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
fixed = strings.Replace(fixed, "BeegoServerName", "ServerName", -1)
fixed = strings.Replace(fixed, "AdminHttpAddr", "AdminAddr", -1)
fixed = strings.Replace(fixed, "AdminHttpPort", "AdminPort", -1)
fixed = strings.Replace(fixed, "HttpServerTimeOut", "ServerTimeOut", -1)
}
err = os.Truncate(file, 0)
if err != nil {
return err
}
return ioutil.WriteFile(file, []byte(fixed), 0666)
}

View File

@ -0,0 +1,61 @@
// Copyright 2020
//
// 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 beefix
import (
"os"
"os/exec"
beeLogger "github.com/beego/bee/v2/logger"
)
func fix1To2() int {
beeLogger.Log.Info("Upgrading the application...")
cmdStr := `go get -u github.com/beego/beego/v2@develop`
err := runShell(cmdStr)
if err != nil {
beeLogger.Log.Error(err.Error())
beeLogger.Log.Error(`fetch v2.0.1 failed. Please try to run: export GO111MODULE=on
and if your network is not stable, please try to use proxy, for example: export GOPROXY=https://goproxy.cn;'
`)
return 1
}
cmdStr = `find ./ -name '*.go' -type f -exec sed -i '' -e 's/github.com\/astaxie\/beego/github.com\/beego\/beego\/v2\/adapter/g' {} \;`
err = runShell(cmdStr)
if err != nil {
beeLogger.Log.Error(err.Error())
return 1
}
cmdStr = `find ./ -name '*.go' -type f -exec sed -i '' -e 's/"github.com\/beego\/beego\/v2\/adapter"/beego "github.com\/beego\/beego\/v2\/adapter"/g' {} \;`
err = runShell(cmdStr)
if err != nil {
beeLogger.Log.Error(err.Error())
return 1
}
return 0
}
func runShell(cmdStr string) error {
c := exec.Command("sh", "-c", cmdStr)
c.Stdout = os.Stdout
err := c.Run()
if err != nil {
beeLogger.Log.Errorf("execute command [%s] failed: %s", cmdStr, err.Error())
return err
}
return nil
}

View File

@ -0,0 +1,230 @@
// Copyright 2020
//
// 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 beefix
import (
"fmt"
"go/parser"
"go/token"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/beego/bee/v2/cmd/commands"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
)
// fixTo16 upgrade beego to 1.6
func fixTo16(cmd *commands.Command, args []string) int {
output := cmd.Out()
beeLogger.Log.Info("Upgrading the application...")
dir, err := os.Getwd()
if err != nil {
beeLogger.Log.Fatalf("Error while getting the current working directory: %s", err)
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
if strings.HasPrefix(info.Name(), ".") {
return filepath.SkipDir
}
return nil
}
if err != nil {
return err
}
if strings.HasSuffix(info.Name(), ".exe") {
return nil
}
err = fixFile(path)
fmt.Fprintf(output, colors.GreenBold("\tfix\t")+"%s\n", path)
if err != nil {
beeLogger.Log.Errorf("Could not fix file: %s", err)
}
return err
})
beeLogger.Log.Success("Upgrade Done!")
return 0
}
var rules = []string{
"beego.AppName", "beego.BConfig.AppName",
"beego.RunMode", "beego.BConfig.RunMode",
"beego.RecoverPanic", "beego.BConfig.RecoverPanic",
"beego.RouterCaseSensitive", "beego.BConfig.RouterCaseSensitive",
"beego.BeegoServerName", "beego.BConfig.ServerName",
"beego.EnableGzip", "beego.BConfig.EnableGzip",
"beego.ErrorsShow", "beego.BConfig.EnableErrorsShow",
"beego.CopyRequestBody", "beego.BConfig.CopyRequestBody",
"beego.MaxMemory", "beego.BConfig.MaxMemory",
"beego.Graceful", "beego.BConfig.Listen.Graceful",
"beego.HttpAddr", "beego.BConfig.Listen.HTTPAddr",
"beego.HttpPort", "beego.BConfig.Listen.HTTPPort",
"beego.ListenTCP4", "beego.BConfig.Listen.ListenTCP4",
"beego.EnableHttpListen", "beego.BConfig.Listen.EnableHTTP",
"beego.EnableHttpTLS", "beego.BConfig.Listen.EnableHTTPS",
"beego.HttpsAddr", "beego.BConfig.Listen.HTTPSAddr",
"beego.HttpsPort", "beego.BConfig.Listen.HTTPSPort",
"beego.HttpCertFile", "beego.BConfig.Listen.HTTPSCertFile",
"beego.HttpKeyFile", "beego.BConfig.Listen.HTTPSKeyFile",
"beego.EnableAdmin", "beego.BConfig.Listen.EnableAdmin",
"beego.AdminHttpAddr", "beego.BConfig.Listen.AdminAddr",
"beego.AdminHttpPort", "beego.BConfig.Listen.AdminPort",
"beego.UseFcgi", "beego.BConfig.Listen.EnableFcgi",
"beego.HttpServerTimeOut", "beego.BConfig.Listen.ServerTimeOut",
"beego.AutoRender", "beego.BConfig.WebConfig.AutoRender",
"beego.ViewsPath", "beego.BConfig.WebConfig.ViewsPath",
"beego.StaticDir", "beego.BConfig.WebConfig.StaticDir",
"beego.StaticExtensionsToGzip", "beego.BConfig.WebConfig.StaticExtensionsToGzip",
"beego.DirectoryIndex", "beego.BConfig.WebConfig.DirectoryIndex",
"beego.FlashName", "beego.BConfig.WebConfig.FlashName",
"beego.FlashSeperator", "beego.BConfig.WebConfig.FlashSeparator",
"beego.EnableDocs", "beego.BConfig.WebConfig.EnableDocs",
"beego.XSRFKEY", "beego.BConfig.WebConfig.XSRFKey",
"beego.EnableXSRF", "beego.BConfig.WebConfig.EnableXSRF",
"beego.XSRFExpire", "beego.BConfig.WebConfig.XSRFExpire",
"beego.TemplateLeft", "beego.BConfig.WebConfig.TemplateLeft",
"beego.TemplateRight", "beego.BConfig.WebConfig.TemplateRight",
"beego.SessionOn", "beego.BConfig.WebConfig.Session.SessionOn",
"beego.SessionProvider", "beego.BConfig.WebConfig.Session.SessionProvider",
"beego.SessionName", "beego.BConfig.WebConfig.Session.SessionName",
"beego.SessionGCMaxLifetime", "beego.BConfig.WebConfig.Session.SessionGCMaxLifetime",
"beego.SessionSavePath", "beego.BConfig.WebConfig.Session.SessionProviderConfig",
"beego.SessionCookieLifeTime", "beego.BConfig.WebConfig.Session.SessionCookieLifeTime",
"beego.SessionAutoSetCookie", "beego.BConfig.WebConfig.Session.SessionAutoSetCookie",
"beego.SessionDomain", "beego.BConfig.WebConfig.Session.SessionDomain",
"Ctx.Input.CopyBody(", "Ctx.Input.CopyBody(beego.BConfig.MaxMemory",
".UrlFor(", ".URLFor(",
".ServeJson(", ".ServeJSON(",
".ServeXml(", ".ServeXML(",
".ServeJsonp(", ".ServeJSONP(",
".XsrfToken(", ".XSRFToken(",
".CheckXsrfCookie(", ".CheckXSRFCookie(",
".XsrfFormHtml(", ".XSRFFormHTML(",
"beego.UrlFor(", "beego.URLFor(",
"beego.GlobalDocApi", "beego.GlobalDocAPI",
"beego.Errorhandler", "beego.ErrorHandler",
"Output.Jsonp(", "Output.JSONP(",
"Output.Json(", "Output.JSON(",
"Output.Xml(", "Output.XML(",
"Input.Uri()", "Input.URI()",
"Input.Url()", "Input.URL()",
"Input.AcceptsHtml()", "Input.AcceptsHTML()",
"Input.AcceptsXml()", "Input.AcceptsXML()",
"Input.AcceptsJson()", "Input.AcceptsJSON()",
"Ctx.XsrfToken()", "Ctx.XSRFToken()",
"Ctx.CheckXsrfCookie()", "Ctx.CheckXSRFCookie()",
"session.SessionStore", "session.Store",
".TplNames", ".TplName",
"swagger.ApiRef", "swagger.APIRef",
"swagger.ApiDeclaration", "swagger.APIDeclaration",
"swagger.Api", "swagger.API",
"swagger.ApiRef", "swagger.APIRef",
"swagger.Infomation", "swagger.Information",
"toolbox.UrlMap", "toolbox.URLMap",
"logs.LoggerInterface", "logs.Logger",
"Input.Request", "Input.Context.Request",
"Input.Params)", "Input.Params())",
"httplib.BeegoHttpSettings", "httplib.BeegoHTTPSettings",
"httplib.BeegoHttpRequest", "httplib.BeegoHTTPRequest",
".TlsClientConfig", ".TLSClientConfig",
".JsonBody", ".JSONBody",
".ToJson", ".ToJSON",
".ToXml", ".ToXML",
"beego.Html2str", "beego.HTML2str",
"beego.AssetsCss", "beego.AssetsCSS",
"orm.DR_Sqlite", "orm.DRSqlite",
"orm.DR_Postgres", "orm.DRPostgres",
"orm.DR_MySQL", "orm.DRMySQL",
"orm.DR_Oracle", "orm.DROracle",
"orm.Col_Add", "orm.ColAdd",
"orm.Col_Minus", "orm.ColMinus",
"orm.Col_Multiply", "orm.ColMultiply",
"orm.Col_Except", "orm.ColExcept",
"GenerateOperatorSql", "GenerateOperatorSQL",
"OperatorSql", "OperatorSQL",
"orm.Debug_Queries", "orm.DebugQueries",
"orm.COMMA_SPACE", "orm.CommaSpace",
".SendOut()", ".DoRequest()",
"validation.ValidationError", "validation.Error",
}
func fixFile(file string) error {
rp := strings.NewReplacer(rules...)
content, err := ioutil.ReadFile(file)
if err != nil {
return err
}
fixed := rp.Replace(string(content))
// Forword the RequestBody from the replace
// "Input.Request", "Input.Context.Request",
fixed = strings.Replace(fixed, "Input.Context.RequestBody", "Input.RequestBody", -1)
// Regexp replace
pareg := regexp.MustCompile(`(Input.Params\[")(.*)("])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Param(\"$2\")")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])(\s)(=)(\s)(.*)`)
fixed = pareg.ReplaceAllString(fixed, "Input.SetData(\"$2\", $7)")
pareg = regexp.MustCompile(`(Input.Data\[\")(.*)(\"\])`)
fixed = pareg.ReplaceAllString(fixed, "Input.Data(\"$2\")")
// Fix the cache object Put method
pareg = regexp.MustCompile(`(\.Put\(\")(.*)(\",)(\s)(.*)(,\s*)([^\*.]*)(\))`)
if pareg.MatchString(fixed) && strings.HasSuffix(file, ".go") {
fixed = pareg.ReplaceAllString(fixed, ".Put(\"$2\", $5, $7*time.Second)")
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, file, nil, parser.ImportsOnly)
if err != nil {
panic(err)
}
// Print the imports from the file's AST.
hasTimepkg := false
for _, s := range f.Imports {
if s.Path.Value == `"time"` {
hasTimepkg = true
break
}
}
if !hasTimepkg {
fixed = strings.Replace(fixed, "import (", "import (\n\t\"time\"", 1)
}
}
// Replace the v.Apis in docs.go
if strings.Contains(file, "docs.go") {
fixed = strings.Replace(fixed, "v.Apis", "v.APIs", -1)
}
// Replace the config file
if strings.HasSuffix(file, ".conf") {
fixed = strings.Replace(fixed, "HttpCertFile", "HTTPSCertFile", -1)
fixed = strings.Replace(fixed, "HttpKeyFile", "HTTPSKeyFile", -1)
fixed = strings.Replace(fixed, "EnableHttpListen", "HTTPEnable", -1)
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
fixed = strings.Replace(fixed, "EnableHttpTLS", "EnableHTTPS", -1)
fixed = strings.Replace(fixed, "BeegoServerName", "ServerName", -1)
fixed = strings.Replace(fixed, "AdminHttpAddr", "AdminAddr", -1)
fixed = strings.Replace(fixed, "AdminHttpPort", "AdminPort", -1)
fixed = strings.Replace(fixed, "HttpServerTimeOut", "ServerTimeOut", -1)
}
err = os.Truncate(file, 0)
if err != nil {
return err
}
return ioutil.WriteFile(file, []byte(fixed), 0666)
}

View File

@ -0,0 +1,63 @@
// Copyright 2013 bee authors
//
// 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 beegopro
import (
"strings"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/internal/app/module/beegopro"
"github.com/beego/bee/v2/logger"
)
var CmdBeegoPro = &commands.Command{
UsageLine: "pro [command]",
Short: "Source code generator",
Long: ``,
Run: BeegoPro,
}
func init() {
CmdBeegoPro.Flag.Var(&beegopro.SQL, "sql", "sql file path")
CmdBeegoPro.Flag.Var(&beegopro.SQLMode, "sqlmode", "sql mode")
CmdBeegoPro.Flag.Var(&beegopro.SQLModePath, "sqlpath", "sql mode path")
CmdBeegoPro.Flag.Var(&beegopro.GitRemotePath, "url", "git remote path")
commands.AvailableCommands = append(commands.AvailableCommands, CmdBeegoPro)
}
func BeegoPro(cmd *commands.Command, args []string) int {
if len(args) < 1 {
beeLogger.Log.Fatal("Command is missing")
}
if len(args) >= 2 {
cmd.Flag.Parse(args[1:])
}
gcmd := args[0]
switch gcmd {
case "gen":
beegopro.DefaultBeegoPro.Run()
case "toml":
beegopro.DefaultBeegoPro.InitToml()
case "config":
beegopro.DefaultBeegoPro.GenConfig()
case "migration":
beegopro.DefaultBeegoPro.Migration(args)
default:
beeLogger.Log.Fatal("Command is missing")
}
beeLogger.Log.Successf("%s successfully generated!", strings.Title(gcmd))
return 0
}

View File

@ -6,8 +6,8 @@ import (
"os"
"strings"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
// Command is the unit of execution
@ -65,6 +65,7 @@ func (c *Command) Out() io.Writer {
if c.output != nil {
return *c.output
}
return colors.NewColorWriter(os.Stderr)
}

56
cmd/commands/dev/cmd.go Normal file
View File

@ -0,0 +1,56 @@
// Copyright 2020
//
// 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 dev
import (
"github.com/beego/bee/v2/cmd/commands"
beeLogger "github.com/beego/bee/v2/logger"
)
var CmdDev = &commands.Command{
CustomFlags: true,
UsageLine: "dev [command]",
Short: "Commands which used to help to develop beego and bee",
Long: `
Commands that help developer develop, build and test beego.
- githook Prepare githooks
`,
Run: Run,
}
func init() {
commands.AvailableCommands = append(commands.AvailableCommands, CmdDev)
}
func Run(cmd *commands.Command, args []string) int {
if len(args) < 1 {
beeLogger.Log.Fatal("Command is missing")
}
if len(args) >= 2 {
cmd.Flag.Parse(args[1:])
}
gcmd := args[0]
switch gcmd {
case "githook":
initGitHook()
default:
beeLogger.Log.Fatal("Unknown command")
}
return 0
}

View File

@ -0,0 +1,47 @@
// Copyright 2020
//
// 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 dev
import (
"os"
beeLogger "github.com/beego/bee/v2/logger"
)
var preCommit = `
goimports -w -format-only ./ \
ineffassign . \
staticcheck -show-ignored -checks "-ST1017,-U1000,-ST1005,-S1034,-S1012,-SA4006,-SA6005,-SA1019,-SA1024" ./ \
`
// for now, we simply override pre-commit file
func initGitHook() {
// pcf => pre-commit file
pcfPath := "./.git/hooks/pre-commit"
pcf, err := os.OpenFile(pcfPath, os.O_RDWR|os.O_CREATE, 0777)
if err != nil {
beeLogger.Log.Errorf("try to create or open file failed: %s, cause: %s", pcfPath, err.Error())
return
}
defer pcf.Close()
_, err = pcf.Write(([]byte)(preCommit))
if err != nil {
beeLogger.Log.Errorf("could not init githooks: %s", err.Error())
} else {
beeLogger.Log.Successf("The githooks has been added, the content is:\n %s ", preCommit)
}
}

View File

@ -24,15 +24,16 @@ import (
"strings"
"time"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/derekparker/delve/service"
"github.com/derekparker/delve/service/rpc2"
"github.com/derekparker/delve/service/rpccommon"
"github.com/derekparker/delve/terminal"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
"github.com/fsnotify/fsnotify"
"github.com/go-delve/delve/pkg/terminal"
"github.com/go-delve/delve/service"
"github.com/go-delve/delve/service/debugger"
"github.com/go-delve/delve/service/rpc2"
"github.com/go-delve/delve/service/rpccommon"
)
var cmdDlv = &commands.Command{
@ -43,7 +44,7 @@ var cmdDlv = &commands.Command{
To debug your application using Delve, use: {{"$ bee dlv" | bold}}
For more information on Delve: https://github.com/derekparker/delve
For more information on Delve: https://github.com/go-delve/delve
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
Run: runDlv,
@ -148,11 +149,14 @@ func startDelveDebugger(addr string, ch chan int) int {
server := rpccommon.NewServer(&service.Config{
Listener: listener,
AcceptMulti: true,
AttachPid: 0,
APIVersion: 2,
WorkingDir: ".",
ProcessArgs: []string{abs},
}, false)
Debugger: debugger.Config{
AttachPid: 0,
WorkingDir: ".",
Backend: "default",
},
})
if err := server.Run(); err != nil {
beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
}
@ -163,7 +167,7 @@ func startDelveDebugger(addr string, ch chan int) int {
go func() {
for {
if val := <-ch; val == 0 {
if _, err := client.Restart(); err != nil {
if _, err := client.Restart(true); err != nil {
utils.Notify("Error while restarting the client: "+err.Error(), "bee")
} else {
if verbose {
@ -182,7 +186,7 @@ func startDelveDebugger(addr string, ch chan int) int {
}
// Stop and kill the debugger server once user quits the REPL
if err := server.Stop(true); err != nil {
if err := server.Stop(); err != nil {
beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
}
return status

View File

@ -22,10 +22,10 @@ import (
"strings"
"text/template"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
const dockerBuildTemplate = `FROM {{.BaseImage}}

View File

@ -17,13 +17,13 @@ import (
"os"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
"github.com/beego/bee/generate"
"github.com/beego/bee/generate/swaggergen"
"github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/config"
"github.com/beego/bee/v2/generate"
"github.com/beego/bee/v2/generate/swaggergen"
"github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdGenerate = &commands.Command{
@ -81,15 +81,6 @@ func GenerateCode(cmd *commands.Command, args []string) int {
beeLogger.Log.Fatal("Command is missing")
}
gps := utils.GetGOPATHs()
if len(gps) == 0 {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
gopath := gps[0]
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
gcmd := args[0]
switch gcmd {
case "scaffold":

View File

@ -1,18 +1,19 @@
package hprose
import (
"os"
"fmt"
"os"
"path"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/api"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/generate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/api"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/generate"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdHproseapp = &commands.Command{
@ -24,7 +25,7 @@ var CmdHproseapp = &commands.Command{
{{"To scaffold out your application, use:"|bold}}
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"]
$ bee hprose [appname] [-tables=""] [-driver=mysql] [-conn="root:@tcp(127.0.0.1:3306)/test"] [-gopath=false] [-beego=v1.12.3]
If 'conn' is empty, the command will generate a sample application. Otherwise the command
will connect to your database and generate models based on the existing tables.
@ -32,6 +33,7 @@ var CmdHproseapp = &commands.Command{
The command 'hprose' creates a folder named [appname] with the following structure:
main.go
go.mod
{{"conf"|foldername}}
app.conf
{{"models"|foldername}}
@ -42,34 +44,77 @@ var CmdHproseapp = &commands.Command{
Run: createhprose,
}
var goMod = `
module %s
go %s
require github.com/beego/beego/v2 %s
require github.com/smartystreets/goconvey v1.6.4
`
var gopath utils.DocValue
var beegoVersion utils.DocValue
func init() {
CmdHproseapp.Flag.Var(&generate.Tables, "tables", "List of table names separated by a comma.")
CmdHproseapp.Flag.Var(&generate.SQLDriver, "driver", "Database driver. Either mysql, postgres or sqlite.")
CmdHproseapp.Flag.Var(&generate.SQLConn, "conn", "Connection string used by the driver to connect to a database instance.")
CmdHproseapp.Flag.Var(&gopath, "gopath", "Support go path,default false")
CmdHproseapp.Flag.Var(&beegoVersion, "beego", "set beego version,only take effect by go mod")
commands.AvailableCommands = append(commands.AvailableCommands, CmdHproseapp)
}
func createhprose(cmd *commands.Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
if len(args) == 0 {
beeLogger.Log.Fatal("Argument [appname] is missing")
}
curpath, _ := os.Getwd()
if len(args) > 1 {
cmd.Flag.Parse(args[1:])
if len(args) >= 2 {
err := cmd.Flag.Parse(args[1:])
if err != nil {
beeLogger.Log.Fatal("Parse args err " + err.Error())
}
}
apppath, packpath, err := utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
var apppath string
var packpath string
var err error
if gopath == `true` {
beeLogger.Log.Info("Generate api project support GOPATH")
version.ShowShortVersionBanner()
apppath, packpath, err = utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
}
} else {
beeLogger.Log.Info("Generate api project support go modules.")
apppath = path.Join(utils.GetBeeWorkPath(), args[0])
packpath = args[0]
if beegoVersion.String() == `` {
beegoVersion.Set(utils.BEEGO_VERSION)
}
}
if utils.IsExist(apppath) {
beeLogger.Log.Errorf(colors.Bold("Application '%s' already exists"), apppath)
beeLogger.Log.Warn(colors.Bold("Do you want to overwrite it? [Yes|No] "))
if !utils.AskForConfirmation() {
os.Exit(2)
}
}
if generate.SQLDriver == "" {
generate.SQLDriver = "mysql"
}
beeLogger.Log.Info("Creating Hprose application...")
os.MkdirAll(apppath, 0755)
if gopath != `true` { //generate first for calc model name
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "go.mod"), "\x1b[0m")
utils.WriteToFile(path.Join(apppath, "go.mod"), fmt.Sprintf(goMod, packpath, utils.GetGoVersionSkipMinor(), beegoVersion.String()))
}
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", apppath, "\x1b[0m")
os.Mkdir(path.Join(apppath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(apppath, "conf"), "\x1b[0m")

View File

@ -23,12 +23,12 @@ import (
"strings"
"time"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/config"
"github.com/beego/bee/v2/utils"
beeLogger "github.com/beego/bee/logger"
beeLogger "github.com/beego/bee/v2/logger"
)
var CmdMigrate = &commands.Command{
@ -71,15 +71,6 @@ func init() {
func RunMigration(cmd *commands.Command, args []string) int {
currpath, _ := os.Getwd()
gps := utils.GetGOPATHs()
if len(gps) == 0 {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
gopath := gps[0]
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
// Getting command line arguments
if len(args) != 0 {
cmd.Flag.Parse(args[1:])
@ -207,8 +198,8 @@ func checkForSchemaUpdateTable(db *sql.DB, driver string) {
beeLogger.Log.Fatalf("Column migration.name type mismatch: TYPE: %s, NULL: %s", typeStr, nullStr)
}
} else if fieldStr == "created_at" {
if typeStr != "timestamp" || defaultStr != "CURRENT_TIMESTAMP" {
beeLogger.Log.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP")
if typeStr != "timestamp" || (!strings.EqualFold(defaultStr, "CURRENT_TIMESTAMP") && !strings.EqualFold(defaultStr, "CURRENT_TIMESTAMP()")) {
beeLogger.Log.Hint("Expecting TYPE: timestamp, DEFAULT: CURRENT_TIMESTAMP || CURRENT_TIMESTAMP()")
beeLogger.Log.Fatalf("Column migration.timestamp type mismatch: TYPE: %s, DEFAULT: %s", typeStr, defaultStr)
}
}
@ -375,8 +366,8 @@ const (
import(
"os"
"github.com/astaxie/beego/orm"
"github.com/astaxie/beego/migration"
"github.com/beego/beego/v2/client/orm"
"github.com/beego/beego/v2/client/orm/migration"
_ "{{DriverRepo}}"
)

View File

@ -20,22 +20,26 @@ import (
path "path/filepath"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
var gopath utils.DocValue
var beegoVersion utils.DocValue
var CmdNew = &commands.Command{
UsageLine: "new [appname]",
UsageLine: "new [appname] [-gopath=false] [-beego=v1.12.3]",
Short: "Creates a Beego application",
Long: `
Creates a Beego application for the given app name in the current directory.
The command 'new' creates a folder named [appname] and generates the following structure:
now default supoort generate a go modules project
The command 'new' creates a folder named [appname] [-gopath=false] [-beego=v1.12.3] and generates the following structure:
main.go
go.mod
{{"conf"|foldername}}
app.conf
{{"controllers"|foldername}}
@ -53,7 +57,7 @@ Creates a Beego application for the given app name in the current directory.
index.tpl
`,
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
PreRun: nil,
Run: CreateApp,
}
@ -66,7 +70,7 @@ var maingo = `package main
import (
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func main() {
@ -78,14 +82,20 @@ var router = `package routers
import (
"{{.Appname}}/controllers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func init() {
beego.Router("/", &controllers.MainController{})
}
`
var goMod = `module %s
go %s
require github.com/beego/beego/v2 %s
require github.com/smartystreets/goconvey v1.6.4
`
var test = `package test
import (
@ -94,9 +104,12 @@ import (
"testing"
"runtime"
"path/filepath"
"github.com/beego/beego/v2/core/logs"
_ "{{.Appname}}/routers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
. "github.com/smartystreets/goconvey/convey"
)
@ -113,7 +126,7 @@ func TestBeego(t *testing.T) {
w := httptest.NewRecorder()
beego.BeeApp.Handlers.ServeHTTP(w, r)
beego.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String())
logs.Trace("testing", "TestBeego", "Code[%d]\n%s", w.Code, w.Body.String())
Convey("Subject: Test Station Endpoint\n", t, func() {
Convey("Status Code Should Be 200", func() {
@ -130,7 +143,7 @@ func TestBeego(t *testing.T) {
var controllers = `package controllers
import (
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
type MainController struct {
@ -245,18 +258,40 @@ var reloadJsClient = `function b(a){var c=new WebSocket(a);c.onclose=function(){
`
func init() {
CmdNew.Flag.Var(&gopath, "gopath", "Support go path,default false")
CmdNew.Flag.Var(&beegoVersion, "beego", "set beego version,only take effect by go mod")
commands.AvailableCommands = append(commands.AvailableCommands, CmdNew)
}
func CreateApp(cmd *commands.Command, args []string) int {
output := cmd.Out()
if len(args) != 1 {
if len(args) == 0 {
beeLogger.Log.Fatal("Argument [appname] is missing")
}
appPath, packPath, err := utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
if len(args) >= 2 {
err := cmd.Flag.Parse(args[1:])
if err != nil {
beeLogger.Log.Fatal("Parse args err " + err.Error())
}
}
var appPath string
var packPath string
var err error
if gopath == `true` {
beeLogger.Log.Info("Generate new project support GOPATH")
version.ShowShortVersionBanner()
appPath, packPath, err = utils.CheckEnv(args[0])
if err != nil {
beeLogger.Log.Fatalf("%s", err)
}
} else {
beeLogger.Log.Info("Generate new project support go modules.")
appPath = path.Join(utils.GetBeeWorkPath(), args[0])
packPath = args[0]
if beegoVersion.String() == `` {
beegoVersion.Set(utils.BEEGO_VERSION)
}
}
if utils.IsExist(appPath) {
@ -269,7 +304,16 @@ func CreateApp(cmd *commands.Command, args []string) int {
beeLogger.Log.Info("Creating application...")
// If it is the current directory, select the current folder name to package path
if packPath == "." {
packPath = path.Base(appPath)
}
os.MkdirAll(appPath, 0755)
if gopath != `true` {
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "go.mod"), "\x1b[0m")
utils.WriteToFile(path.Join(appPath, "go.mod"), fmt.Sprintf(goMod, packPath, utils.GetGoVersionSkipMinor(), beegoVersion.String()))
}
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", appPath+string(path.Separator), "\x1b[0m")
os.Mkdir(path.Join(appPath, "conf"), 0755)
fmt.Fprintf(output, "\t%s%screate%s\t %s%s\n", "\x1b[32m", "\x1b[1m", "\x1b[21m", path.Join(appPath, "conf")+string(path.Separator), "\x1b[0m")

View File

@ -18,10 +18,10 @@ import (
"syscall"
"time"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdPack = &commands.Command{
@ -40,6 +40,7 @@ var CmdPack = &commands.Command{
var (
appPath string
appName string
excludeP string
excludeS string
outputP string
@ -57,6 +58,7 @@ func init() {
fs := flag.NewFlagSet("pack", flag.ContinueOnError)
fs.StringVar(&appPath, "p", "", "Set the application path. Defaults to the current path.")
fs.BoolVar(&build, "b", true, "Tell the command to do a build for the current platform. Defaults to true.")
fs.StringVar(&appName, "a", "", "Set the application name. Defaults to the dir name.")
fs.StringVar(&buildArgs, "ba", "", "Specify additional args for Go build.")
fs.Var(&buildEnvs, "be", "Specify additional env variables for Go build. e.g. GOARCH=arm.")
fs.StringVar(&outputP, "o", "", "Set the compressed file output path. Defaults to the current path.")
@ -445,7 +447,9 @@ func packApp(cmd *commands.Command, args []string) int {
beeLogger.Log.Infof("Packaging application on '%s'...", thePath)
appName := path.Base(thePath)
if len(appName) == 0 {
appName = path.Base(thePath)
}
goos := runtime.GOOS
if v, found := syscall.Getenv("GOOS"); found {
@ -470,7 +474,7 @@ func packApp(cmd *commands.Command, args []string) int {
}()
if build {
beeLogger.Log.Info("Building application...")
beeLogger.Log.Infof("Building application (%v)...", appName)
var envs []string
for _, env := range buildEnvs {
parts := strings.SplitN(env, "=", 2)
@ -553,7 +557,8 @@ func packApp(cmd *commands.Command, args []string) int {
exs = append(exs, p)
}
}
exs = append(exs, `go.mod`)
exs = append(exs, `go.sum`)
var exr []*regexp.Regexp
for _, r := range excludeR {
if len(r) > 0 {

View File

@ -24,12 +24,12 @@ import (
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
"github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/config"
"github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
var cmdRs = &commands.Command{

View File

@ -7,7 +7,7 @@ import (
"os"
"strings"
beeLogger "github.com/beego/bee/logger"
beeLogger "github.com/beego/bee/v2/logger"
)
var (

View File

@ -18,7 +18,7 @@ import (
"net/http"
"time"
beeLogger "github.com/beego/bee/logger"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/gorilla/websocket"
)

View File

@ -20,11 +20,11 @@ import (
"runtime"
"strings"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/config"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdRun = &commands.Command{
@ -46,6 +46,8 @@ var (
excludedPaths utils.StrFlags
// Pass through to -tags arg of "go build"
buildTags string
// Pass through to -ldflags arg of "go build"
buildLDFlags string
// Application path
currpath string
// Application name
@ -72,6 +74,7 @@ func init() {
CmdRun.Flag.Var(&excludedPaths, "e", "List of paths to exclude.")
CmdRun.Flag.BoolVar(&vendorWatch, "vendor", false, "Enable watch vendor folder.")
CmdRun.Flag.StringVar(&buildTags, "tags", "", "Set the build tags. See: https://golang.org/pkg/go/build/")
CmdRun.Flag.StringVar(&buildLDFlags, "ldflags", "", "Set the build ldflags. See: https://golang.org/pkg/go/build/")
CmdRun.Flag.StringVar(&runmode, "runmode", "", "Set the Beego run mode.")
CmdRun.Flag.StringVar(&runargs, "runargs", "", "Extra args to run application")
CmdRun.Flag.Var(&extraPackages, "ex", "List of extra package to watch.")

View File

@ -24,10 +24,10 @@ import (
"sync"
"time"
"github.com/beego/bee/config"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/config"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
"github.com/fsnotify/fsnotify"
)
@ -39,11 +39,11 @@ var (
watchExts = config.Conf.WatchExts
watchExtsStatic = config.Conf.WatchExtsStatic
ignoredFilesRegExps = []string{
`.#(\w+).go`,
`.(\w+).go.swp`,
`(\w+).go~`,
`(\w+).tmp`,
`commentsRouter_controllers.go`,
`.#(\w+).go$`,
`.(\w+).go.swp$`,
`(\w+).go~$`,
`(\w+).tmp$`,
`commentsRouter_controllers.go$`,
}
)
@ -158,6 +158,9 @@ func AutoBuild(files []string, isgenerate bool) {
if buildTags != "" {
args = append(args, "-tags", buildTags)
}
if buildLDFlags != "" {
args = append(args, "-ldflags", buildLDFlags)
}
args = append(args, files...)
bcmd := exec.Command(cmdName, args...)

View File

@ -17,13 +17,13 @@ package apiapp
import (
"net/http"
beeLogger "github.com/beego/bee/logger"
beeLogger "github.com/beego/bee/v2/logger"
"os"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/cmd/commands/version"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/cmd/commands/version"
"github.com/beego/bee/v2/utils"
)
var CmdServer = &commands.Command{

View File

@ -0,0 +1,41 @@
package update
import (
"flag"
"os"
"os/exec"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/config"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var CmdUpdate = &commands.Command{
UsageLine: "update",
Short: "Update Bee",
Long: `
Automatic run command "go get -u github.com/beego/bee/v2" for selfupdate
`,
Run: updateBee,
}
func init() {
fs := flag.NewFlagSet("update", flag.ContinueOnError)
CmdUpdate.Flag = *fs
commands.AvailableCommands = append(commands.AvailableCommands, CmdUpdate)
}
func updateBee(cmd *commands.Command, args []string) int {
beeLogger.Log.Info("Updating")
beePath := config.GitRemotePath
cmdUp := exec.Command("go", "get", "-u", beePath)
cmdUp.Stdout = os.Stdout
cmdUp.Stderr = os.Stderr
if err := cmdUp.Run(); err != nil {
beeLogger.Log.Warnf("Run cmd err:%s", err)
}
// update the Time when updateBee every time
utils.UpdateLastPublishedTime()
return 0
}

View File

@ -9,7 +9,8 @@ import (
"time"
beeLogger "github.com/beego/bee/logger"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
// RuntimeInfo holds information about the current runtime.
@ -23,6 +24,7 @@ type RuntimeInfo struct {
Compiler string
BeeVersion string
BeegoVersion string
Published string
}
// InitBanner loads the banner and prints it to output
@ -60,6 +62,7 @@ func show(out io.Writer, content string) {
runtime.Compiler,
version,
GetBeegoVersion(),
utils.GetLastPublishedTime(),
})
if err != nil {
beeLogger.Log.Error(err.Error())

View File

@ -14,11 +14,13 @@ import (
"runtime"
"strings"
"github.com/beego/bee/cmd/commands"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"gopkg.in/yaml.v2"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/config"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
const verboseVersionBanner string = `%s%s______
@ -36,7 +38,7 @@ const verboseVersionBanner string = `%s%s______
GOPATH : {{ .GOPATH }}
GOROOT : {{ .GOROOT }}
Compiler : {{ .Compiler }}
Date : {{ Now "Monday, 2 Jan 2006" }}%s
Published : {{ .Published }}%s
`
const shortVersionBanner = `______
@ -57,7 +59,7 @@ Prints the current Bee, Beego and Go version alongside the platform information.
}
var outputFormat string
const version = "1.10.0"
const version = config.Version
func init() {
fs := flag.NewFlagSet("version", flag.ContinueOnError)
@ -82,6 +84,7 @@ func versionCmd(cmd *commands.Command, args []string) int {
runtime.Compiler,
version,
GetBeegoVersion(),
utils.GetLastPublishedTime(),
}
switch outputFormat {
case "json":
@ -124,11 +127,11 @@ func GetBeegoVersion() string {
}
wgopath := utils.GetGOPATHs()
if len(wgopath) == 0 {
beeLogger.Log.Error("You need to set GOPATH environment variable")
beeLogger.Log.Error("GOPATH environment is empty,may be you use `go module`")
return ""
}
for _, wg := range wgopath {
wg, _ = path.EvalSymlinks(path.Join(wg, "src", "github.com", "astaxie", "beego"))
wg, _ = path.EvalSymlinks(path.Join(wg, "src", "github.com", "beego", "beego"))
filename := path.Join(wg, "beego.go")
_, err := os.Stat(filename)
if err != nil {
@ -159,7 +162,8 @@ func GetBeegoVersion() string {
}
}
return "Beego is not installed. Please do consider installing it first: https://github.com/astaxie/beego"
return "Beego is not installed. Please do consider installing it first: https://github.com/beego/beego/v2. " +
"If you are using go mod, and you don't install the beego under $GOPATH/src/github.com/beego, just ignore this."
}
func GetGoVersion() string {

View File

@ -19,12 +19,18 @@ import (
"os"
"path/filepath"
beeLogger "github.com/beego/bee/logger"
"gopkg.in/yaml.v2"
beeLogger "github.com/beego/bee/v2/logger"
)
const confVer = 0
const (
Version = "2.0.2"
GitRemotePath = "github.com/beego/bee/v2"
)
var Conf = struct {
Version int
WatchExts []string `json:"watch_ext" yaml:"watch_ext"`

View File

@ -14,7 +14,7 @@
package generate
import "github.com/beego/bee/utils"
import "github.com/beego/bee/v2/utils"
var SQLDriver utils.DocValue
var SQLConn utils.DocValue

View File

@ -15,17 +15,19 @@
package generate
import (
"bufio"
"database/sql"
"fmt"
"io"
"os"
"path"
"path/filepath"
"regexp"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
@ -923,9 +925,9 @@ func extractColSize(colType string) string {
}
func extractIntSignness(colType string) string {
regex := regexp.MustCompile(`(int|smallint|mediumint|bigint)\([0-9]+\)(.*)`)
regex := regexp.MustCompile(`(int|smallint|mediumint|bigint).*`)
signRegex := regex.FindStringSubmatch(colType)
return strings.Trim(signRegex[2], " ")
return strings.Trim(signRegex[1], " ")
}
func extractDecimal(colType string) (digits string, decimals string) {
@ -948,11 +950,40 @@ func getFileName(tbName string) (filename string) {
func getPackagePath(curpath string) (packpath string) {
gopath := os.Getenv("GOPATH")
if gopath == "" {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
info := "GOPATH environment variable is not set or empty"
gomodpath := filepath.Join(curpath, `go.mod`)
re, err := regexp.Compile(`^module\s+(.+)$`)
if err != nil {
beeLogger.Log.Error(info)
beeLogger.Log.Fatalf("try `go.mod` generate regexp error:%s", err)
return ""
}
fd, err := os.Open(gomodpath)
if err != nil {
beeLogger.Log.Error(info)
beeLogger.Log.Fatalf("try `go.mod` Error while reading 'go.mod',%s", gomodpath)
}
reader := bufio.NewReader(fd)
for {
byteLine, _, er := reader.ReadLine()
if er != nil && er != io.EOF {
return ""
}
if er == io.EOF {
break
}
line := string(byteLine)
s := re.FindStringSubmatch(line)
if len(s) >= 2 {
return s[1]
}
}
beeLogger.Log.Error(info)
beeLogger.Log.Fatalf("try `go.mod` Error while parse 'go.mod',%s", gomodpath)
} else {
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
}
beeLogger.Log.Debugf("GOPATH: %s", utils.FILE(), utils.LINE(), gopath)
appsrcpath := ""
haspath := false
wgopath := filepath.SplitList(gopath)
@ -992,7 +1023,7 @@ import (
"reflect"
"strings"
{{timePkg}}
"github.com/astaxie/beego/orm"
"github.com/beego/beego/v2/client/orm"
)
{{modelStruct}}
@ -1141,7 +1172,7 @@ import (
"strconv"
"strings"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
// {{ctrlName}}Controller operations for {{ctrlName}}
@ -1316,7 +1347,7 @@ package routers
import (
"{{pkgPath}}/controllers"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func init() {

View File

@ -20,9 +20,9 @@ import (
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
func GenerateController(cname, currpath string) {
@ -78,7 +78,7 @@ func GenerateController(cname, currpath string) {
var controllerTpl = `package {{packageName}}
import (
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
// {{controllerName}}Controller operations for {{controllerName}}
@ -166,7 +166,7 @@ import (
"strconv"
"strings"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
// {{controllerName}}Controller operations for {{controllerName}}

View File

@ -24,9 +24,9 @@ import (
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
_ "github.com/go-sql-driver/mysql"
_ "github.com/lib/pq"
)
@ -47,7 +47,7 @@ import (
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
beego "github.com/beego/beego/v2/server/web"
)
func logInvokeHandler(
@ -90,8 +90,8 @@ import (
"{{.Appname}}/models"
"github.com/hprose/hprose-golang/rpc"
"github.com/astaxie/beego"
"github.com/astaxie/beego/orm"
beego "github.com/beego/beego/v2/server/web"
"github.com/beego/beego/v2/client/orm"
{{.DriverPkg}}
)
@ -419,7 +419,7 @@ import (
"reflect"
"strings"
{{timePkg}}
"github.com/astaxie/beego/orm"
"github.com/beego/beego/v2/client/orm"
)
{{modelStruct}}

View File

@ -21,9 +21,9 @@ import (
"strings"
"time"
"github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
const (
@ -238,7 +238,7 @@ func GenerateMigration(mname, upsql, downsql, curpath string) {
const (
MigrationHeader = `package main
import (
"github.com/astaxie/beego/migration"
"github.com/beego/beego/v2/client/orm/migration"
)
// DO NOT MODIFY

View File

@ -21,9 +21,9 @@ import (
"path"
"strings"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
func GenerateModel(mname, fields, currpath string) {
@ -145,7 +145,7 @@ import (
"reflect"
"strings"
{{timePkg}}
"github.com/astaxie/beego/orm"
"github.com/beego/beego/v2/client/orm"
)
{{modelStruct}}

View File

@ -3,9 +3,9 @@ package generate
import (
"strings"
"github.com/beego/bee/cmd/commands/migrate"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/utils"
"github.com/beego/bee/v2/cmd/commands/migrate"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
func GenerateScaffold(sname, fields, currpath, driver, conn string) {

View File

@ -19,9 +19,9 @@ import (
"os"
"path"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
"github.com/beego/bee/v2/utils"
)
// recipe

View File

@ -19,6 +19,7 @@ import (
"errors"
"fmt"
"go/ast"
"go/build"
"go/parser"
"go/token"
"os"
@ -34,10 +35,9 @@ import (
yaml "gopkg.in/yaml.v2"
"github.com/astaxie/beego/swagger"
"github.com/astaxie/beego/utils"
beeLogger "github.com/beego/bee/logger"
bu "github.com/beego/bee/utils"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/beego/v2/core/utils"
"github.com/beego/beego/v2/server/web/swagger"
)
const (
@ -102,8 +102,8 @@ func init() {
astPkgs = make([]*ast.Package, 0)
}
// ParsePackagesFromDir parses packages from a given directory
func ParsePackagesFromDir(dirpath string) {
// parsePackagesFromDir parses packages from a given directory
func parsePackagesFromDir(dirpath string) {
c := make(chan error)
go func() {
@ -157,8 +157,14 @@ func parsePackageFromDir(path string) error {
// GenerateDocs generates documentations for a given path.
func GenerateDocs(curpath string) {
fset := token.NewFileSet()
pkgspath := curpath
workspace := os.Getenv("BeeWorkspace")
if workspace != "" {
pkgspath = workspace
}
parsePackagesFromDir(pkgspath)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, filepath.Join(curpath, "routers", "router.go"), nil, parser.ParseComments)
if err != nil {
beeLogger.Log.Fatalf("Error while parsing router.go: %s", err)
@ -263,7 +269,7 @@ func GenerateDocs(curpath string) {
if im.Name != nil {
localName = im.Name.Name
}
analyseControllerPkg(path.Join(curpath, "vendor"), localName, im.Path.Value)
analyseControllerPkg(localName, im.Path.Value)
}
for _, d := range f.Decls {
switch specDecl := d.(type) {
@ -418,12 +424,12 @@ func analyseNSInclude(baseurl string, ce *ast.CallExpr) string {
return cname
}
func analyseControllerPkg(vendorPath, localName, pkgpath string) {
func analyseControllerPkg(localName, pkgpath string) {
pkgpath = strings.Trim(pkgpath, "\"")
if isSystemPackage(pkgpath) {
return
}
if pkgpath == "github.com/astaxie/beego" {
if pkgpath == "github.com/beego/beego/v2/server/web" {
return
}
if localName != "" {
@ -432,32 +438,19 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) {
pps := strings.Split(pkgpath, "/")
importlist[pps[len(pps)-1]] = pkgpath
}
gopaths := bu.GetGOPATHs()
if len(gopaths) == 0 {
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
pkgRealpath := ""
wg, _ := filepath.EvalSymlinks(filepath.Join(vendorPath, pkgpath))
if utils.FileExists(wg) {
pkgRealpath = wg
} else {
wgopath := gopaths
for _, wg := range wgopath {
wg, _ = filepath.EvalSymlinks(filepath.Join(wg, "src", pkgpath))
if utils.FileExists(wg) {
pkgRealpath = wg
break
}
}
pkg, err := build.Default.Import(pkgpath, ".", build.FindOnly)
if err != nil {
beeLogger.Log.Fatalf("Package %s cannot be imported: %v", pkgpath, err)
}
pkgRealpath := pkg.Dir
if pkgRealpath != "" {
if _, ok := pkgCache[pkgpath]; ok {
return
}
pkgCache[pkgpath] = struct{}{}
} else {
beeLogger.Log.Fatalf("Package '%s' does not exist in the GOPATH or vendor path", pkgpath)
beeLogger.Log.Fatalf("Package '%s' does not have source directory", pkgpath)
}
fileSet := token.NewFileSet()
@ -468,6 +461,7 @@ func analyseControllerPkg(vendorPath, localName, pkgpath string) {
if err != nil {
beeLogger.Log.Fatalf("Error while parsing dir at '%s': %s", pkgpath, err)
}
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for _, d := range fl.Decls {
@ -730,7 +724,7 @@ func parserComments(f *ast.FuncDecl, controllerName, pkgpath string) error {
}
}
}
routerPath = urlReplace(routerPath)
if routerPath != "" {
//Go over function parameters which were not mapped and create swagger params for them
for name, typ := range funcParamMap {
@ -802,7 +796,7 @@ func setParamType(para *swagger.Parameter, typ string, pkgpath, controllerName s
paraFormat = typeFormat[1]
if para.In == "body" {
para.Schema = &swagger.Schema{
Type: paraType,
Type: paraType,
Format: paraFormat,
}
}
@ -1209,7 +1203,7 @@ func parseStruct(st *ast.StructType, k string, m *swagger.Schema, realTypes *[]s
for _, pkg := range astPkgs {
for _, fl := range pkg.Files {
for nameOfObj, obj := range fl.Scope.Objects {
if obj.Name == fmt.Sprint(field.Type) {
if pkg.Name+"."+obj.Name == realType {
parseObject(obj, nameOfObj, nm, realTypes, astPkgs, pkg.Name)
}
}

20
go.mod Normal file
View File

@ -0,0 +1,20 @@
module github.com/beego/bee/v2
go 1.13
require (
github.com/beego/beego/v2 v2.0.1
github.com/davecgh/go-spew v1.1.1
github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915
github.com/fsnotify/fsnotify v1.4.9
github.com/go-delve/delve v1.5.0
github.com/go-sql-driver/mysql v1.5.0
github.com/gorilla/websocket v1.4.2
github.com/lib/pq v1.7.0
github.com/pelletier/go-toml v1.8.1
github.com/smartwalle/pongo2render v1.0.1
github.com/spf13/viper v1.7.0
gopkg.in/yaml.v2 v2.3.0
)
//replace github.com/beego/beego/v2 => ../beego

View File

@ -1,3 +1,3 @@
github.com/beego/bee/cmd/commands/run/*.go:S1024
github.com/beego/bee/cmd/commands/dlv/*.go:S1024
github.com/beego/bee/utils/*.go:S1026
github.com/beego/bee/v2/cmd/commands/run/*.go:S1024
github.com/beego/bee/v2/cmd/commands/dlv/*.go:S1024
github.com/beego/bee/v2/utils/*.go:S1026

View File

@ -0,0 +1,23 @@
package beegopro
import (
"io/ioutil"
"github.com/beego/bee/v2/internal/pkg/utils"
beeLogger "github.com/beego/bee/v2/logger"
)
var CompareExcept = []string{"@BeeGenerateTime"}
func (c *Container) GenConfig() {
if utils.IsExist(c.BeegoProFile) {
beeLogger.Log.Fatalf("beego pro toml exist")
return
}
err := ioutil.WriteFile("beegopro.toml", []byte(BeegoToml), 0644)
if err != nil {
beeLogger.Log.Fatalf("write beego pro toml err: %s", err)
return
}
}

View File

@ -0,0 +1,18 @@
package beegopro
const BeegoToml = `
dsn = "root:123456@tcp(127.0.0.1:3306)/beego"
driver = "mysql"
proType = "default"
enableModule = []
apiPrefix = "/"
gitRemotePath = "https://github.com/beego-dev/beego-pro.git"
format = true
sourceGen = "text"
gitPull = true
[models.user]
name = ["uid"]
orm = ["auto"]
comment = ["Uid"]
`

View File

@ -0,0 +1,254 @@
package beegopro
import (
"fmt"
"io/ioutil"
"net/url"
"strings"
"sync"
"time"
"github.com/beego/bee/v2/internal/pkg/git"
"github.com/beego/bee/v2/internal/pkg/system"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
"github.com/davecgh/go-spew/spew"
"github.com/pelletier/go-toml"
"github.com/spf13/viper"
)
var GitRemotePath utils.DocValue
const MDateFormat = "20060102_150405"
var DefaultBeegoPro = &Container{
BeegoProFile: system.CurrentDir + "/beegopro.toml",
TimestampFile: system.CurrentDir + "/.beegopro.timestamp",
GoModFile: system.CurrentDir + "/go.mod",
UserOption: UserOption{
Debug: false,
ContextDebug: false,
Dsn: "",
Driver: "mysql",
ProType: "default",
ApiPrefix: "/api",
EnableModule: nil,
Models: make(map[string]TextModel),
GitRemotePath: "",
//GitRemotePath: "https://github.com/beego/beego-pro.git",
Branch: "master",
//GitLocalPath: system.BeegoHome + "/beego-pro",
EnableFormat: true,
SourceGen: "text",
EnableGitPull: true,
Path: map[string]string{
"beego": ".",
},
EnableGomod: true,
RefreshGitTime: 24 * 3600,
Extend: nil,
},
GenerateTime: time.Now().Format(MDateFormat),
GenerateTimeUnix: time.Now().Unix(),
TmplOption: TmplOption{},
CurPath: system.CurrentDir,
EnableModules: make(map[string]interface{}), // get the user configuration, get the enable module result
FunctionOnce: make(map[string]sync.Once), // get the tmpl configuration, get the function once result
}
func (c *Container) Run() {
// init git refresh cache time
c.initTimestamp()
c.initUserOption()
c.initTemplateOption()
c.initParser()
c.initRender()
c.flushTimestamp()
}
func (c *Container) initUserOption() {
if !utils.IsExist(c.BeegoProFile) {
beeLogger.Log.Fatalf("beego pro config is not exist, beego json path: %s", c.BeegoProFile)
return
}
viper.SetConfigFile(c.BeegoProFile)
err := viper.ReadInConfig()
if err != nil {
beeLogger.Log.Fatalf("read beego pro config content, err: %s", err.Error())
return
}
err = viper.Unmarshal(&c.UserOption)
if err != nil {
beeLogger.Log.Fatalf("beego pro config unmarshal error, err: %s", err.Error())
return
}
if c.UserOption.Debug {
viper.Debug()
}
if c.UserOption.EnableGomod {
if !utils.IsExist(c.GoModFile) {
beeLogger.Log.Fatalf("go mod not exist, please create go mod file")
return
}
}
for _, value := range c.UserOption.EnableModule {
c.EnableModules[value] = struct{}{}
}
if len(c.EnableModules) == 0 {
c.EnableModules["*"] = struct{}{}
}
if c.UserOption.Debug {
fmt.Println("c.modules", c.EnableModules)
}
}
func (c *Container) initTemplateOption() {
c.GetLocalPath()
if c.UserOption.EnableGitPull && (c.GenerateTimeUnix-c.Timestamp.GitCacheLastRefresh > c.UserOption.RefreshGitTime) {
err := git.CloneORPullRepo(c.UserOption.GitRemotePath, c.UserOption.GitLocalPath)
if err != nil {
beeLogger.Log.Fatalf("beego pro git clone or pull repo error, err: %s", err)
return
}
c.Timestamp.GitCacheLastRefresh = c.GenerateTimeUnix
}
tree, err := toml.LoadFile(c.UserOption.GitLocalPath + "/" + c.UserOption.ProType + "/bee.toml")
if err != nil {
beeLogger.Log.Fatalf("beego tmpl exec error, err: %s", err)
return
}
err = tree.Unmarshal(&c.TmplOption)
if err != nil {
beeLogger.Log.Fatalf("beego tmpl parse error, err: %s", err)
return
}
if c.UserOption.Debug {
spew.Dump("tmpl", c.TmplOption)
}
for _, value := range c.TmplOption.Descriptor {
if value.Once {
c.FunctionOnce[value.SrcName] = sync.Once{}
}
}
}
func (c *Container) initParser() {
driver, flag := ParserDriver[c.UserOption.SourceGen]
if !flag {
beeLogger.Log.Fatalf("parse driver not exit, source gen %s", c.UserOption.SourceGen)
}
driver.RegisterOption(c.UserOption, c.TmplOption)
c.Parser = driver
}
func (c *Container) initRender() {
for _, desc := range c.TmplOption.Descriptor {
_, allFlag := c.EnableModules["*"]
_, moduleFlag := c.EnableModules[desc.Module]
if !allFlag && !moduleFlag {
continue
}
models := c.Parser.GetRenderInfos(desc)
// model table name, model table schema
for _, m := range models {
// some render exec once
syncOnce, flag := c.FunctionOnce[desc.SrcName]
if flag {
syncOnce.Do(func() {
c.renderModel(m)
})
continue
}
c.renderModel(m)
}
}
}
func (c *Container) renderModel(m RenderInfo) {
// todo optimize
m.GenerateTime = c.GenerateTime
render := NewRender(m)
render.Exec(m.Descriptor.SrcName)
if render.Descriptor.IsExistScript() {
err := render.Descriptor.ExecScript(c.CurPath)
if err != nil {
beeLogger.Log.Fatalf("beego exec shell error, err: %s", err)
}
}
}
func (c *Container) initTimestamp() {
if utils.IsExist(c.TimestampFile) {
tree, err := toml.LoadFile(c.TimestampFile)
if err != nil {
beeLogger.Log.Fatalf("beego timestamp tmpl exec error, err: %s", err)
return
}
err = tree.Unmarshal(&c.Timestamp)
if err != nil {
beeLogger.Log.Fatalf("beego timestamp tmpl parse error, err: %s", err)
return
}
}
c.Timestamp.Generate = c.GenerateTimeUnix
}
func (c *Container) flushTimestamp() {
tomlByte, err := toml.Marshal(c.Timestamp)
if err != nil {
beeLogger.Log.Fatalf("marshal timestamp tmpl parse error, err: %s", err)
}
err = ioutil.WriteFile(c.TimestampFile, tomlByte, 0644)
if err != nil {
beeLogger.Log.Fatalf("flush timestamp tmpl parse error, err: %s", err)
}
}
func (c *Container) InitToml() {
if exist := utils.IsExist(c.BeegoProFile); exist {
beeLogger.Log.Fatalf("file beegopro.toml already exists")
}
sourceFile := c.UserOption.GitLocalPath + "/beegopro.toml"
input, err := ioutil.ReadFile(sourceFile)
if err != nil {
beeLogger.Log.Fatalf("read beegopro.toml file err, %s", err.Error())
return
}
err = ioutil.WriteFile(c.BeegoProFile, input, 0644)
if err != nil {
beeLogger.Log.Fatalf("create beegopro.toml file err, %s", err.Error())
return
}
beeLogger.Log.Success("Successfully created file beegopro.toml")
}
//form https://github.com/beego/beego-pro.git
//get beego/beego-pro
func (c *Container) GetLocalPath() {
if c.UserOption.GitLocalPath != "" {
return
}
if GitRemotePath != "" {
c.UserOption.GitRemotePath = GitRemotePath.String()
}
if c.UserOption.GitRemotePath == "" {
c.UserOption.GitRemotePath = "https://github.com/beego/beego-pro.git"
}
parse, err := url.Parse(c.UserOption.GitRemotePath)
if err != nil {
beeLogger.Log.Fatalf("git GitRemotePath err, %s", err.Error())
return
}
s := parse.Path
s = strings.TrimRight(s, ".git")
c.UserOption.GitLocalPath = system.BeegoHome + s
}

View File

@ -0,0 +1,80 @@
package beegopro
import (
"database/sql"
"io/ioutil"
"path/filepath"
"strings"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
)
var SQL utils.DocValue
var SQLMode utils.DocValue
var SQLModePath utils.DocValue
var (
SQLModeUp = "up"
SQLModeDown = "down"
)
func (c *Container) Migration(args []string) {
c.initUserOption()
db, err := sql.Open(c.UserOption.Driver, c.UserOption.Dsn)
if err != nil {
beeLogger.Log.Fatalf("Could not connect to '%s' database using '%s': %s", c.UserOption.Driver, c.UserOption.Dsn, err)
return
}
defer db.Close()
switch SQLMode.String() {
case SQLModeUp:
doByMode(db, "up.sql")
case SQLModeDown:
doByMode(db, "down.sql")
default:
doBySqlFile(db)
}
}
func doBySqlFile(db *sql.DB) {
fileName := SQL.String()
if !utils.IsExist(fileName) {
beeLogger.Log.Fatalf("sql mode path not exist, path %s", SQL.String())
}
doDb(db, fileName)
}
func doByMode(db *sql.DB, suffix string) {
pathName := SQLModePath.String()
if !utils.IsExist(pathName) {
beeLogger.Log.Fatalf("sql mode path not exist, path %s", SQLModePath.String())
}
rd, err := ioutil.ReadDir(pathName)
if err != nil {
beeLogger.Log.Fatalf("read dir err, path %s, err %s", pathName, err)
}
for _, fi := range rd {
if !fi.IsDir() {
if !strings.HasSuffix(fi.Name(), suffix) {
continue
}
doDb(db, filepath.Join(pathName, fi.Name()))
}
}
}
func doDb(db *sql.DB, filePath string) {
absFile, _ := filepath.Abs(filePath)
content, err := ioutil.ReadFile(filePath)
if err != nil {
beeLogger.Log.Errorf("read file err %s, abs file %s", err, absFile)
}
_, err = db.Exec(string(content))
if err != nil {
beeLogger.Log.Errorf("db exec err %s", err)
}
beeLogger.Log.Infof("db exec info %s", filePath)
}

View File

@ -0,0 +1,13 @@
package beegopro
type Parser interface {
RegisterOption(userOption UserOption, tmplOption TmplOption)
Parse(descriptor Descriptor)
GetRenderInfos(descriptor Descriptor) (output []RenderInfo)
Unregister()
}
var ParserDriver = map[string]Parser{
"text": &TextParser{},
"mysql": &MysqlParser{},
}

View File

@ -0,0 +1,88 @@
package beegopro
import (
"database/sql"
"fmt"
"github.com/beego/bee/v2/internal/pkg/utils"
beeLogger "github.com/beego/bee/v2/logger"
)
type MysqlParser struct {
userOption UserOption
tmplOption TmplOption
}
func (m *MysqlParser) RegisterOption(userOption UserOption, tmplOption TmplOption) {
m.userOption = userOption
m.tmplOption = tmplOption
}
func (*MysqlParser) Parse(descriptor Descriptor) {
}
func (m *MysqlParser) GetRenderInfos(descriptor Descriptor) (output []RenderInfo) {
tableSchemas, err := m.getTableSchemas()
if err != nil {
beeLogger.Log.Fatalf("get table schemas err %s", err)
}
models := tableSchemas.ToTableMap()
output = make([]RenderInfo, 0)
// model table name, model table schema
for modelName, content := range models {
output = append(output, RenderInfo{
Module: descriptor.Module,
ModelName: modelName,
Content: content,
Option: m.userOption,
Descriptor: descriptor,
TmplPath: m.tmplOption.RenderPath,
})
}
return
}
func (t *MysqlParser) Unregister() {
}
func (m *MysqlParser) getTableSchemas() (resp TableSchemas, err error) {
dsn, err := utils.ParseDSN(m.userOption.Dsn)
if err != nil {
beeLogger.Log.Fatalf("parse dsn err %s", err)
return
}
conn, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/information_schema", dsn.User, dsn.Passwd, dsn.Addr))
if err != nil {
beeLogger.Log.Fatalf("Could not connect to mysql database using '%s': %s", m.userOption.Dsn, err)
return
}
defer conn.Close()
q := `SELECT TABLE_NAME, COLUMN_NAME, IS_NULLABLE, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH,
NUMERIC_PRECISION, NUMERIC_SCALE,COLUMN_TYPE,COLUMN_KEY,COLUMN_COMMENT
FROM COLUMNS WHERE TABLE_SCHEMA = ? ORDER BY TABLE_NAME, ORDINAL_POSITION`
rows, err := conn.Query(q, dsn.DBName)
if err != nil {
return nil, err
}
columns := make(TableSchemas, 0)
for rows.Next() {
cs := TableSchema{}
err := rows.Scan(&cs.TableName, &cs.ColumnName, &cs.IsNullable, &cs.DataType,
&cs.CharacterMaximumLength, &cs.NumericPrecision, &cs.NumericScale,
&cs.ColumnType, &cs.ColumnKey, &cs.Comment)
if err != nil {
return nil, err
}
columns = append(columns, cs)
}
if err := rows.Err(); err != nil {
return nil, err
}
return columns, nil
}

View File

@ -0,0 +1,35 @@
package beegopro
type TextParser struct {
userOption UserOption
tmplOption TmplOption
}
func (t *TextParser) RegisterOption(userOption UserOption, tmplOption TmplOption) {
t.userOption = userOption
t.tmplOption = tmplOption
}
func (*TextParser) Parse(descriptor Descriptor) {
}
func (t *TextParser) GetRenderInfos(descriptor Descriptor) (output []RenderInfo) {
output = make([]RenderInfo, 0)
// model table name, model table schema
for modelName, content := range t.userOption.Models {
output = append(output, RenderInfo{
Module: descriptor.Module,
ModelName: modelName,
Content: content.ToModelInfos(),
Option: t.userOption,
Descriptor: descriptor,
TmplPath: t.tmplOption.RenderPath,
})
}
return
}
func (t *TextParser) Unregister() {
}

View File

@ -0,0 +1,59 @@
package beegopro
import (
"strings"
"unicode/utf8"
"github.com/beego/bee/v2/utils"
"github.com/flosch/pongo2"
)
func init() {
_ = pongo2.RegisterFilter("lowerFirst", pongo2LowerFirst)
_ = pongo2.RegisterFilter("upperFirst", pongo2UpperFirst)
_ = pongo2.RegisterFilter("snakeString", pongo2SnakeString)
_ = pongo2.RegisterFilter("camelString", pongo2CamelString)
}
func pongo2LowerFirst(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
if in.Len() <= 0 {
return pongo2.AsValue(""), nil
}
t := in.String()
r, size := utf8.DecodeRuneInString(t)
return pongo2.AsValue(strings.ToLower(string(r)) + t[size:]), nil
}
func pongo2UpperFirst(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
if in.Len() <= 0 {
return pongo2.AsValue(""), nil
}
t := in.String()
return pongo2.AsValue(strings.Replace(t, string(t[0]), strings.ToUpper(string(t[0])), 1)), nil
}
// snake string, XxYy to xx_yy
func pongo2SnakeString(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
if in.Len() <= 0 {
return pongo2.AsValue(""), nil
}
t := in.String()
return pongo2.AsValue(utils.SnakeString(t)), nil
}
// snake string, XxYy to xx_yy
func pongo2CamelString(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) {
if in.Len() <= 0 {
return pongo2.AsValue(""), nil
}
t := in.String()
return pongo2.AsValue(utils.CamelString(t)), nil
}
//func upperFirst(str string) string {
// return strings.Replace(str, string(str[0]), strings.ToUpper(string(str[0])), 1)
//}
func lowerFirst(str string) string {
return strings.Replace(str, string(str[0]), strings.ToLower(string(str[0])), 1)
}

View File

@ -0,0 +1,153 @@
package beegopro
import (
"go/format"
"io/ioutil"
"os"
"path"
"path/filepath"
"github.com/davecgh/go-spew/spew"
"github.com/flosch/pongo2"
"github.com/smartwalle/pongo2render"
"github.com/beego/bee/v2/internal/pkg/system"
beeLogger "github.com/beego/bee/v2/logger"
)
// render
type RenderFile struct {
*pongo2render.Render
Context pongo2.Context
GenerateTime string
Option UserOption
ModelName string
PackageName string
FlushFile string
PkgPath string
TmplPath string
Descriptor Descriptor
}
func NewRender(m RenderInfo) *RenderFile {
var (
pathCtx pongo2.Context
newDescriptor Descriptor
)
// parse descriptor, get flush file path, beego path, etc...
newDescriptor, pathCtx = m.Descriptor.Parse(m.ModelName, m.Option.Path)
obj := &RenderFile{
Context: make(pongo2.Context),
Option: m.Option,
ModelName: m.ModelName,
GenerateTime: m.GenerateTime,
Descriptor: newDescriptor,
}
obj.FlushFile = newDescriptor.DstPath
// new render
obj.Render = pongo2render.NewRender(path.Join(obj.Option.GitLocalPath, obj.Option.ProType, m.TmplPath))
filePath := path.Dir(obj.FlushFile)
err := createPath(filePath)
if err != nil {
beeLogger.Log.Fatalf("Could not create the controllers directory: %s", err)
}
// get go package path
obj.PkgPath = getPackagePath()
relativePath, err := filepath.Rel(system.CurrentDir, obj.FlushFile)
if err != nil {
beeLogger.Log.Fatalf("Could not get the relative path: %s", err)
}
modelSchemas := m.Content.ToModelSchemas()
camelPrimaryKey := modelSchemas.GetPrimaryKey()
importMaps := make(map[string]struct{})
if modelSchemas.IsExistTime() {
importMaps["time"] = struct{}{}
}
obj.PackageName = filepath.Base(filepath.Dir(relativePath))
beeLogger.Log.Infof("Using '%s' as name", obj.ModelName)
beeLogger.Log.Infof("Using '%s' as package name from %s", obj.ModelName, obj.PackageName)
// package
obj.SetContext("packageName", obj.PackageName)
obj.SetContext("packageImports", importMaps)
// todo optimize
// todo Set the beego directory, should recalculate the package
if pathCtx["pathRelBeego"] == "." {
obj.SetContext("packagePath", obj.PkgPath)
} else {
obj.SetContext("packagePath", obj.PkgPath+"/"+pathCtx["pathRelBeego"].(string))
}
obj.SetContext("packageMod", obj.PkgPath)
obj.SetContext("modelSchemas", modelSchemas)
obj.SetContext("modelPrimaryKey", camelPrimaryKey)
for key, value := range pathCtx {
obj.SetContext(key, value)
}
obj.SetContext("apiPrefix", obj.Option.ApiPrefix)
obj.SetContext("generateTime", obj.GenerateTime)
if obj.Option.ContextDebug {
spew.Dump(obj.Context)
}
return obj
}
func (r *RenderFile) SetContext(key string, value interface{}) {
r.Context[key] = value
}
func (r *RenderFile) Exec(name string) {
var (
buf string
err error
)
buf, err = r.Render.Template(name).Execute(r.Context)
if err != nil {
beeLogger.Log.Fatalf("Could not create the %s render tmpl: %s", name, err)
return
}
_, err = os.Stat(r.Descriptor.DstPath)
var orgContent []byte
if err == nil {
if org, err := os.OpenFile(r.Descriptor.DstPath, os.O_RDONLY, 0666); err == nil {
orgContent, _ = ioutil.ReadAll(org)
org.Close()
} else {
beeLogger.Log.Infof("file err %s", err)
}
}
// Replace or create when content changes
output := []byte(buf)
ext := filepath.Ext(r.FlushFile)
if r.Option.EnableFormat && ext == ".go" {
// format code
var bts []byte
bts, err = format.Source([]byte(buf))
if err != nil {
beeLogger.Log.Warnf("format buf error %s", err.Error())
}
output = bts
}
if FileContentChange(orgContent, output, GetSeg(ext)) {
err = r.write(r.FlushFile, output)
if err != nil {
beeLogger.Log.Fatalf("Could not create file: %s", err)
return
}
beeLogger.Log.Infof("create file '%s' from %s", r.FlushFile, r.PackageName)
}
}

View File

@ -0,0 +1,142 @@
package beegopro
import (
"fmt"
"path/filepath"
"strings"
"sync"
"github.com/beego/bee/v2/internal/pkg/command"
"github.com/beego/bee/v2/internal/pkg/system"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/utils"
"github.com/flosch/pongo2"
"github.com/smartwalle/pongo2render"
)
// store all data
type Container struct {
BeegoProFile string // beego pro toml
TimestampFile string // store ts file
GoModFile string // go mod file
UserOption UserOption // user option
TmplOption TmplOption // tmpl option
CurPath string // user current path
EnableModules map[string]interface{} // beego pro provider a collection of module
FunctionOnce map[string]sync.Once // exec function once
Timestamp Timestamp
GenerateTime string
GenerateTimeUnix int64
Parser Parser
}
// user option
type UserOption struct {
Debug bool `json:"debug"`
ContextDebug bool `json:"contextDebug"`
Dsn string `json:"dsn"`
Driver string `json:"driver"`
ProType string `json:"proType"`
ApiPrefix string `json:"apiPrefix"`
EnableModule []string `json:"enableModule"`
Models map[string]TextModel `json:"models"`
GitRemotePath string `json:"gitRemotePath"`
Branch string `json:"branch"`
GitLocalPath string `json:"gitLocalPath"`
EnableFormat bool `json:"enableFormat"`
SourceGen string `json:"sourceGen"`
EnableGitPull bool `json:"enbaleGitPull"`
Path map[string]string `json:"path"`
EnableGomod bool `json:"enableGomod"`
RefreshGitTime int64 `json:"refreshGitTime"`
Extend map[string]string `json:"extend"` // extend user data
}
// tmpl option
type TmplOption struct {
RenderPath string `toml:"renderPath"`
Descriptor []Descriptor
}
type Descriptor struct {
Module string `toml:"module"`
SrcName string `toml:"srcName"`
DstPath string `toml:"dstPath"`
Once bool `toml:"once"`
Script string `toml:"script"`
}
func (descriptor Descriptor) Parse(modelName string, paths map[string]string) (newDescriptor Descriptor, ctx pongo2.Context) {
var (
err error
relativeDstPath string
absFile string
relPath string
)
newDescriptor = descriptor
render := pongo2render.NewRender("")
ctx = make(pongo2.Context)
for key, value := range paths {
absFile, err = filepath.Abs(value)
if err != nil {
beeLogger.Log.Fatalf("absolute path error %s from key %s and value %s", err, key, value)
}
relPath, err = filepath.Rel(system.CurrentDir, absFile)
if err != nil {
beeLogger.Log.Fatalf("Could not get the relative path: %s", err)
}
// user input path
ctx["path"+utils.CamelCase(key)] = value
// relativePath
ctx["pathRel"+utils.CamelCase(key)] = relPath
}
ctx["modelName"] = lowerFirst(utils.CamelString(modelName))
relativeDstPath, err = render.TemplateFromString(descriptor.DstPath).Execute(ctx)
if err != nil {
beeLogger.Log.Fatalf("beego tmpl exec error, err: %s", err)
return
}
newDescriptor.DstPath, err = filepath.Abs(relativeDstPath)
if err != nil {
beeLogger.Log.Fatalf("absolute path error %s from flush file %s", err, relativeDstPath)
}
newDescriptor.Script, err = render.TemplateFromString(descriptor.Script).Execute(ctx)
if err != nil {
beeLogger.Log.Fatalf("parse script %s, error %s", descriptor.Script, err)
}
return
}
func (descriptor Descriptor) IsExistScript() bool {
return descriptor.Script != ""
}
func (d Descriptor) ExecScript(path string) (err error) {
arr := strings.Split(d.Script, " ")
if len(arr) == 0 {
return
}
stdout, stderr, err := command.ExecCmdDir(path, arr[0], arr[1:]...)
if err != nil {
return concatenateError(err, stderr)
}
beeLogger.Log.Info(stdout)
return nil
}
type Timestamp struct {
GitCacheLastRefresh int64 `toml:"gitCacheLastRefresh"`
Generate int64 `toml:"generate"`
}
func concatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%v: %s", err, stderr)
}

View File

@ -0,0 +1,98 @@
package beegopro
import (
"strings"
"github.com/beego/bee/v2/utils"
)
// parse get the model info
type ModelInfo struct {
Name string `json:"name"` // mysql name
InputType string `json:"inputType"` // user input type
MysqlType string `json:"mysqlType"` // mysql type
GoType string `json:"goType"` // go type
Orm string `json:"orm"` // orm tag
Comment string `json:"comment"` // mysql comment
Extend string `json:"extend"` // user extend info
}
func (m ModelInfo) GetColumnKey() (columnKey string) {
if m.InputType == "auto" || m.Orm == "pk" {
columnKey = "PRI"
}
return
}
func (m ModelInfo) IsPrimaryKey() (flag bool) {
if m.Orm == "auto" || m.Orm == "pk" || strings.ToLower(m.Name) == "id" {
flag = true
}
return
}
type ModelInfos []ModelInfo
// to render model schemas
func (modelInfos ModelInfos) ToModelSchemas() (output ModelSchemas) {
output = make(ModelSchemas, 0)
for i, value := range modelInfos {
if i == 0 && !value.IsPrimaryKey() {
inputType, goType, mysqlType, ormTag := getModelType("auto")
output = append(output, &ModelSchema{
Name: "id",
InputType: inputType,
ColumnKey: "PRI",
Comment: "ID",
MysqlType: mysqlType,
GoType: goType,
OrmTag: ormTag,
Extend: value.Extend,
})
}
modelSchema := &ModelSchema{
Name: value.Name,
InputType: value.InputType,
ColumnKey: value.GetColumnKey(),
MysqlType: value.MysqlType,
Comment: value.Comment,
GoType: value.GoType,
OrmTag: value.Orm,
}
output = append(output, modelSchema)
}
return
}
type ModelSchema struct {
Name string // column name
InputType string // user input type
MysqlType string // mysql type
ColumnKey string // PRI
Comment string // comment
GoType string // go type
OrmTag string // orm tag
Extend string
}
type ModelSchemas []*ModelSchema
func (m ModelSchemas) IsExistTime() bool {
for _, value := range m {
if value.InputType == "datetime" {
return true
}
}
return false
}
func (m ModelSchemas) GetPrimaryKey() string {
camelPrimaryKey := ""
for _, value := range m {
if value.ColumnKey == "PRI" {
camelPrimaryKey = utils.CamelString(value.Name)
}
}
return camelPrimaryKey
}

View File

@ -0,0 +1,75 @@
package beegopro
import (
"database/sql"
"errors"
"github.com/beego/bee/v2/logger"
)
type TableSchema struct {
TableName string
ColumnName string
IsNullable string
DataType string
CharacterMaximumLength sql.NullInt64
NumericPrecision sql.NullInt64
NumericScale sql.NullInt64
ColumnType string
ColumnKey string
Comment string
}
type TableSchemas []TableSchema
func (tableSchemas TableSchemas) ToTableMap() (resp map[string]ModelInfos) {
resp = make(map[string]ModelInfos)
for _, value := range tableSchemas {
if _, ok := resp[value.TableName]; !ok {
resp[value.TableName] = make(ModelInfos, 0)
}
modelInfos := resp[value.TableName]
inputType, goType, err := value.ToGoType()
if err != nil {
beeLogger.Log.Fatalf("parse go type err %s", err)
return
}
modelInfo := ModelInfo{
Name: value.ColumnName,
InputType: inputType,
GoType: goType,
Comment: value.Comment,
}
if value.ColumnKey == "PRI" {
modelInfo.Orm = "pk"
}
resp[value.TableName] = append(modelInfos, modelInfo)
}
return
}
// GetGoDataType maps an SQL data type to Golang data type
func (col TableSchema) ToGoType() (inputType string, goType string, err error) {
switch col.DataType {
case "char", "varchar", "enum", "set", "text", "longtext", "mediumtext", "tinytext":
goType = "string"
case "blob", "mediumblob", "longblob", "varbinary", "binary":
goType = "[]byte"
case "date", "time", "datetime", "timestamp":
goType, inputType = "time.Time", "dateTime"
case "tinyint", "smallint", "int", "mediumint":
goType = "int"
case "bit", "bigint":
goType = "int64"
case "float", "decimal", "double":
goType = "float64"
}
if goType == "" {
err = errors.New("No compatible datatype (" + col.DataType + ", CamelName: " + col.ColumnName + ") found")
}
return
}

View File

@ -0,0 +1,11 @@
package beegopro
type RenderInfo struct {
Module string
ModelName string
Option UserOption
Content ModelInfos
Descriptor Descriptor
TmplPath string
GenerateTime string
}

View File

@ -0,0 +1,50 @@
package beegopro
import (
beeLogger "github.com/beego/bee/v2/logger"
)
type TextModel struct {
Names []string
Orms []string
Comments []string
Extends []string
}
func (content TextModel) ToModelInfos() (output []ModelInfo) {
namesLen := len(content.Names)
ormsLen := len(content.Orms)
commentsLen := len(content.Comments)
if namesLen != ormsLen && namesLen != commentsLen {
beeLogger.Log.Fatalf("length error, namesLen is %d, ormsLen is %d, commentsLen is %d", namesLen, ormsLen, commentsLen)
}
extendLen := len(content.Extends)
if extendLen != 0 && extendLen != namesLen {
beeLogger.Log.Fatalf("extend length error, namesLen is %d, extendsLen is %d", namesLen, extendLen)
}
output = make([]ModelInfo, 0)
for i, name := range content.Names {
comment := content.Comments[i]
if comment == "" {
comment = name
}
inputType, goType, mysqlType, ormTag := getModelType(content.Orms[i])
m := ModelInfo{
Name: name,
InputType: inputType,
GoType: goType,
Orm: ormTag,
Comment: comment,
MysqlType: mysqlType,
Extend: "",
}
// extend value
if extendLen != 0 {
m.Extend = content.Extends[i]
}
output = append(output, m)
}
return
}

View File

@ -0,0 +1,215 @@
package beegopro
import (
"crypto/md5"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"time"
"github.com/beego/bee/v2/internal/pkg/utils"
beeLogger "github.com/beego/bee/v2/logger"
)
// write to file
func (c *RenderFile) write(filename string, buf []byte) (err error) {
if utils.IsExist(filename) && !isNeedOverwrite(filename) {
return
}
filePath := filepath.Dir(filename)
err = createPath(filePath)
if err != nil {
err = errors.New("write create path " + err.Error())
return
}
filePathBak := filePath + "/bak"
err = createPath(filePathBak)
if err != nil {
err = errors.New("write create path bak " + err.Error())
return
}
name := path.Base(filename)
if utils.IsExist(filename) {
bakName := fmt.Sprintf("%s/%s.%s.bak", filePathBak, filepath.Base(name), time.Now().Format("2006.01.02.15.04.05"))
beeLogger.Log.Infof("bak file '%s'", bakName)
if err := os.Rename(filename, bakName); err != nil {
err = errors.New("file is bak error, path is " + bakName)
return err
}
}
file, err := os.Create(filename)
defer func() {
err = file.Close()
if err != nil {
beeLogger.Log.Fatalf("file close error, err %s", err)
}
}()
if err != nil {
err = errors.New("write create file " + err.Error())
return
}
err = ioutil.WriteFile(filename, buf, 0644)
if err != nil {
err = errors.New("write write file " + err.Error())
return
}
return
}
func isNeedOverwrite(fileName string) (flag bool) {
seg := GetSeg(filepath.Ext(fileName))
f, err := os.Open(fileName)
if err != nil {
return
}
defer f.Close()
overwrite := ""
var contentByte []byte
contentByte, err = ioutil.ReadAll(f)
if err != nil {
return
}
for _, s := range strings.Split(string(contentByte), "\n") {
s = strings.TrimSpace(strings.TrimPrefix(s, seg))
if strings.HasPrefix(s, "@BeeOverwrite") {
overwrite = strings.TrimSpace(s[len("@BeeOverwrite"):])
}
}
if strings.ToLower(overwrite) == "yes" {
flag = true
return
}
return
}
// createPath 调用os.MkdirAll递归创建文件夹
func createPath(filePath string) error {
if !utils.IsExist(filePath) {
err := os.MkdirAll(filePath, os.ModePerm)
return err
}
return nil
}
func getPackagePath() (packagePath string) {
f, err := os.Open("go.mod")
if err != nil {
return
}
defer f.Close()
var contentByte []byte
contentByte, err = ioutil.ReadAll(f)
if err != nil {
return
}
for _, s := range strings.Split(string(contentByte), "\n") {
packagePath = strings.TrimSpace(strings.TrimPrefix(s, "module"))
return
}
return
}
func getModelType(orm string) (inputType, goType, mysqlType, tag string) {
kv := strings.SplitN(orm, ",", 2)
inputType = kv[0]
switch inputType {
case "string":
goType = "string"
tag = "size(255)"
// todo use orm data
mysqlType = "varchar(255) NOT NULL"
case "text":
goType = "string"
tag = "type(longtext)"
mysqlType = "longtext NOT NULL"
case "auto":
goType = "int"
tag = "auto"
mysqlType = "int(11) NOT NULL AUTO_INCREMENT"
case "pk":
goType = "int"
tag = "pk"
mysqlType = "int(11) NOT NULL"
case "datetime":
goType = "time.Time"
tag = "type(datetime)"
mysqlType = "datetime NOT NULL"
case "int", "int8", "int16", "int32", "int64":
fallthrough
case "uint", "uint8", "uint16", "uint32", "uint64":
goType = inputType
tag = ""
mysqlType = "int(11) DEFAULT NULL"
case "bool":
goType = inputType
tag = ""
mysqlType = "int(11) DEFAULT NULL"
case "float32", "float64":
goType = inputType
tag = ""
mysqlType = "float NOT NULL"
case "float":
goType = "float64"
tag = ""
mysqlType = "float NOT NULL"
default:
beeLogger.Log.Fatalf("not support type: %s", inputType)
}
// user set orm tag
if len(kv) == 2 {
tag = kv[1]
}
return
}
func FileContentChange(org, new []byte, seg string) bool {
if len(org) == 0 {
return true
}
orgContent := GetFilterContent(string(org), seg)
newContent := GetFilterContent(string(new), seg)
orgMd5 := md5.Sum([]byte(orgContent))
newMd5 := md5.Sum([]byte(newContent))
if orgMd5 != newMd5 {
return true
}
beeLogger.Log.Infof("File has no change in the content")
return false
}
func GetFilterContent(content string, seg string) string {
res := ""
for _, s := range strings.Split(content, "\n") {
s = strings.TrimSpace(strings.TrimPrefix(s, seg))
var have bool
for _, except := range CompareExcept {
if strings.HasPrefix(s, except) {
have = true
}
}
if !have {
res += s
}
}
return res
}
func GetSeg(ext string) string {
switch ext {
case ".sql":
return "--"
default:
return "//"
}
}

View File

@ -0,0 +1,65 @@
package command
import (
"bytes"
"fmt"
"os/exec"
"strconv"
"strings"
)
// ExecCmdDirBytes executes system command in given directory
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdDirBytes(dir, cmdName string, args ...string) ([]byte, []byte, error) {
bufOut := new(bytes.Buffer)
bufErr := new(bytes.Buffer)
cmd := exec.Command(cmdName, args...)
cmd.Dir = dir
cmd.Stdout = bufOut
cmd.Stderr = bufErr
err := cmd.Run()
return bufOut.Bytes(), bufErr.Bytes(), err
}
// ExecCmdBytes executes system command
// and return stdout, stderr in bytes type, along with possible error.
func ExecCmdBytes(cmdName string, args ...string) ([]byte, []byte, error) {
return ExecCmdDirBytes("", cmdName, args...)
}
// ExecCmdDir executes system command in given directory
// and return stdout, stderr in string type, along with possible error.
func ExecCmdDir(dir, cmdName string, args ...string) (string, string, error) {
bufOut, bufErr, err := ExecCmdDirBytes(dir, cmdName, args...)
return string(bufOut), string(bufErr), err
}
// ExecCmd executes system command
// and return stdout, stderr in string type, along with possible error.
func ExecCmd(cmdName string, args ...string) (string, string, error) {
return ExecCmdDir("", cmdName, args...)
}
// 版本对比 v1比v2大返回1小于返回-1等于返回0
func VerCompare(ver1, ver2 string) int {
ver1 = strings.TrimLeft(ver1, "ver") // 清除v,e,r
ver2 = strings.TrimLeft(ver2, "ver") // 清除v,e,r
p1 := strings.Split(ver1, ".")
p2 := strings.Split(ver2, ".")
ver1 = ""
for _, v := range p1 {
iv, _ := strconv.Atoi(v)
ver1 = fmt.Sprintf("%s%04d", ver1, iv)
}
ver2 = ""
for _, v := range p2 {
iv, _ := strconv.Atoi(v)
ver2 = fmt.Sprintf("%s%04d", ver2, iv)
}
return strings.Compare(ver1, ver2)
}

View File

@ -0,0 +1,209 @@
package git
import (
"errors"
"fmt"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/beego/bee/v2/internal/pkg/command"
"github.com/beego/bee/v2/internal/pkg/utils"
beeLogger "github.com/beego/bee/v2/logger"
)
// git tag
func GetTags(repoPath string, limit int) ([]string, error) {
repo, err := OpenRepository(repoPath)
if err != nil {
return nil, err
}
err = repo.Pull()
if err != nil {
return nil, err
}
list, err := repo.GetTags()
if err != nil {
return nil, err
}
if len(list) > limit {
list = list[0:limit]
}
return list, nil
}
// clone repo
func CloneRepo(url string, dst string) (err error) {
if utils.IsExist(dst) {
return errors.New("dst is not empty, dst is " + dst)
}
if !utils.Mkdir(dst) {
err = errors.New("make dir error, dst is " + dst)
return
}
beeLogger.Log.Info("start git clone from " + url + ", to dst at " + dst)
_, stderr, err := command.ExecCmd("git", "clone", url, dst)
if err != nil {
beeLogger.Log.Error("error git clone from " + url + ", to dst at " + dst)
return concatenateError(err, stderr)
}
return nil
}
// CloneORPullRepo
func CloneORPullRepo(url string, dst string) error {
if !utils.IsDir(dst) {
return CloneRepo(url, dst)
} else {
utils.Mkdir(dst)
repo, err := OpenRepository(dst)
if err != nil {
return err
}
return repo.Pull()
}
}
//
func CloneRepoBranch(branch string, url string, dst string) error {
_, stderr, err := command.ExecCmd("git", "clone", "-b", branch, url, dst)
if err != nil {
return concatenateError(err, stderr)
}
return nil
}
// SortTag ...
type SortTag struct {
data []string
}
// Len ...
func (t *SortTag) Len() int {
return len(t.data)
}
// Swap ...
func (t *SortTag) Swap(i, j int) {
t.data[i], t.data[j] = t.data[j], t.data[i]
}
// Less ...
func (t *SortTag) Less(i, j int) bool {
return command.VerCompare(t.data[i], t.data[j]) == 1
}
// Sort ...
func (t *SortTag) Sort() []string {
sort.Sort(t)
return t.data
}
// Repository ...
type Repository struct {
Path string
}
// OpenRepository ...
func OpenRepository(repoPath string) (*Repository, error) {
repoPath, err := filepath.Abs(repoPath)
if err != nil {
return nil, err
} else if !utils.IsDir(repoPath) {
return nil, errors.New("no such file or directory")
}
return &Repository{Path: repoPath}, nil
}
// 拉取代码
func (repo *Repository) Pull() error {
beeLogger.Log.Info("git pull " + repo.Path)
_, stderr, err := command.ExecCmdDir(repo.Path, "git", "pull")
if err != nil {
return concatenateError(err, stderr)
}
return nil
}
// 获取tag列表
func (repo *Repository) GetTags() ([]string, error) {
stdout, stderr, err := command.ExecCmdDir(repo.Path, "git", "tag", "-l")
if err != nil {
return nil, concatenateError(err, stderr)
}
tags := strings.Split(stdout, "\n")
tags = tags[:len(tags)-1]
so := &SortTag{data: tags}
return so.Sort(), nil
}
// 获取两个版本之间的修改日志
func (repo *Repository) GetChangeLogs(startVer, endVer string) ([]string, error) {
// git log --pretty=format:"%cd %cn: %s" --date=iso v1.8.0...v1.9.0
stdout, stderr, err := command.ExecCmdDir(repo.Path, "git", "log", "--pretty=format:%cd %cn: %s", "--date=iso", startVer+"..."+endVer)
if err != nil {
return nil, concatenateError(err, stderr)
}
logs := strings.Split(stdout, "\n")
return logs, nil
}
// 获取两个版本之间的差异文件列表
func (repo *Repository) GetChangeFiles(startVer, endVer string, onlyFile bool) ([]string, error) {
// git diff --name-status -b v1.8.0 v1.9.0
param := "--name-status"
if onlyFile {
param = "--name-only"
}
stdout, stderr, err := command.ExecCmdDir(repo.Path, "git", "diff", param, "-b", startVer, endVer)
if err != nil {
return nil, concatenateError(err, stderr)
}
lines := strings.Split(stdout, "\n")
return lines[:len(lines)-1], nil
}
// 获取两个版本间的新增或修改的文件数量
func (repo *Repository) GetDiffFileCount(startVer, endVer string) (int, error) {
cmd := "git diff --name-status -b " + startVer + " " + endVer + " |grep -v ^D |wc -l"
stdout, stderr, err := command.ExecCmdDir(repo.Path, "/bin/bash", "-c", cmd)
if err != nil {
return 0, concatenateError(err, stderr)
}
count, _ := strconv.Atoi(strings.TrimSpace(stdout))
return count, nil
}
// 导出版本到tar包
func (repo *Repository) Export(startVer, endVer string, filename string) error {
// git archive --format=tar.gz $endVer $(git diff --name-status -b $beginVer $endVer |grep -v ^D |grep -v Upgrade/ |awk '{print $2}') -o $tmpFile
cmd := ""
if startVer == "" {
cmd = "git archive --format=tar " + endVer + " | gzip > " + filename
} else {
cmd = "git archive --format=tar " + endVer + " $(dgit diff --name-status -b " + startVer + " " + endVer + "|grep -v ^D |awk '{print $2}') | gzip > " + filename
}
_, stderr, err := command.ExecCmdDir(repo.Path, "/bin/bash", "-c", cmd)
if err != nil {
return concatenateError(err, stderr)
}
return nil
}
func concatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%v: %s", err, stderr)
}

View File

@ -0,0 +1,22 @@
package system
import (
"os"
"os/user"
"path/filepath"
)
// Bee System Params ...
var (
Usr, _ = user.Current()
BeegoHome = filepath.Join(Usr.HomeDir, "/.beego")
CurrentDir = getCurrentDirectory()
GoPath = os.Getenv("GOPATH")
)
func getCurrentDirectory() string {
if dir, err := os.Getwd(); err == nil {
return dir
}
return ""
}

113
internal/pkg/utils/dsn.go Normal file
View File

@ -0,0 +1,113 @@
package utils
import (
"errors"
"net/url"
"strings"
)
// DSN ...
type DSN struct {
User string // Username
Passwd string // Password (requires User)
Net string // Network type
Addr string // Network address (requires Net)
DBName string // Database name
Params map[string]string // Connection parameters
}
var (
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
)
// ParseDSN parses the DSN string to a Config
func ParseDSN(dsn string) (cfg *DSN, err error) {
// New config with some default values
cfg = new(DSN)
// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
// Find the last '/' (since the password or the net addr might contain a '/')
foundSlash := false
for i := len(dsn) - 1; i >= 0; i-- {
if dsn[i] == '/' {
foundSlash = true
var j, k int
// left part is empty if i <= 0
if i > 0 {
// [username[:password]@][protocol[(address)]]
// Find the last '@' in dsn[:i]
for j = i; j >= 0; j-- {
if dsn[j] == '@' {
// username[:password]
// Find the first ':' in dsn[:j]
for k = 0; k < j; k++ {
if dsn[k] == ':' {
cfg.Passwd = dsn[k+1 : j]
break
}
}
cfg.User = dsn[:k]
break
}
}
// [protocol[(address)]]
// Find the first '(' in dsn[j+1:i]
for k = j + 1; k < i; k++ {
if dsn[k] == '(' {
// dsn[i-1] must be == ')' if an address is specified
if dsn[i-1] != ')' {
if strings.ContainsRune(dsn[k+1:i], ')') {
return nil, errInvalidDSNUnescaped
}
return nil, errInvalidDSNAddr
}
cfg.Addr = dsn[k+1 : i-1]
break
}
}
cfg.Net = dsn[j+1 : k]
}
// dbname[?param1=value1&...&paramN=valueN]
// Find the first '?' in dsn[i+1:]
for j = i + 1; j < len(dsn); j++ {
if dsn[j] == '?' {
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
return
}
break
}
}
cfg.DBName = dsn[i+1 : j]
break
}
}
if !foundSlash && len(dsn) > 0 {
return nil, errInvalidDSNNoSlash
}
return
}
func parseDSNParams(cfg *DSN, params string) (err error) {
for _, v := range strings.Split(params, "&") {
param := strings.SplitN(v, "=", 2)
if len(param) != 2 {
continue
}
// lazy init
if cfg.Params == nil {
cfg.Params = make(map[string]string)
}
value := param[1]
if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
return
}
}
return
}

View File

@ -0,0 +1,38 @@
package utils
import (
"os"
beeLogger "github.com/beego/bee/v2/logger"
)
// Mkdir ...
func Mkdir(dir string) bool {
if dir == "" {
beeLogger.Log.Fatalf("The directory is empty")
return false
}
err := os.MkdirAll(dir, 0755)
if err != nil {
beeLogger.Log.Fatalf("Could not create the directory: %s", err)
return false
}
beeLogger.Log.Infof("Create %s Success!", dir)
return true
}
// IsDir ...
func IsDir(dir string) bool {
f, e := os.Stat(dir)
if e != nil {
return false
}
return f.IsDir()
}
// IsExist returns whether a file or directory exists.
func IsExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}

View File

@ -24,7 +24,7 @@ import (
"text/template"
"time"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/v2/logger/colors"
)
var errInvalidLogLevel = errors.New("logger: invalid log level")

24
main.go
View File

@ -18,22 +18,14 @@ import (
"log"
"os"
"github.com/beego/bee/cmd"
"github.com/beego/bee/cmd/commands"
"github.com/beego/bee/config"
"github.com/beego/bee/generate/swaggergen"
"github.com/beego/bee/utils"
)
var (
workspace = os.Getenv("BeeWorkspace")
"github.com/beego/bee/v2/cmd"
"github.com/beego/bee/v2/cmd/commands"
"github.com/beego/bee/v2/config"
"github.com/beego/bee/v2/utils"
)
func main() {
currentpath, _ := os.Getwd()
if workspace != "" {
currentpath = workspace
}
utils.NoticeUpdateBee()
flag.Usage = cmd.Usage
flag.Parse()
log.SetFlags(0)
@ -66,12 +58,6 @@ func main() {
}
config.LoadConfig()
// Check if current directory is inside the GOPATH,
// if so parse the packages inside it.
if utils.IsInGOPATH(currentpath) && cmd.IfGenerateDocs(c.Name(), args) {
swaggergen.ParsePackagesFromDir(currentpath)
}
os.Exit(c.Run(c, args))
return
}

7
scripts/quickstart.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
export GO111MODULE=on
go get github.com/beego/bee/v2@v2.0.2
bee new hello
cd hello
bee run

View File

@ -1 +1 @@
github.com/beego/bee/generate/swaggergen/*.go:SA1024
github.com/beego/bee/v2/generate/swaggergen/*.go:SA1024

View File

@ -21,7 +21,7 @@ import (
"runtime"
"github.com/beego/bee/config"
"github.com/beego/bee/v2/config"
)
const appName = "Beego"

View File

@ -16,23 +16,50 @@ package utils
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
"text/template"
"time"
"unicode"
beeLogger "github.com/beego/bee/logger"
"github.com/beego/bee/logger/colors"
"github.com/beego/bee/v2/config"
"github.com/beego/bee/v2/internal/pkg/system"
beeLogger "github.com/beego/bee/v2/logger"
"github.com/beego/bee/v2/logger/colors"
)
type tagName struct {
Name string `json:"name"`
}
type Repos struct {
UpdatedAt time.Time `json:"updated_at"`
PushedAt time.Time `json:"pushed_at"`
}
type Releases struct {
PublishedAt time.Time `json:"published_at"`
TagName string `json:"tag_name"`
}
func GetBeeWorkPath() string {
curpath, err := os.Getwd()
if err != nil {
panic(err)
}
return curpath
}
// Go is a basic promise implementation: it wraps calls a function in a goroutine
// and returns a channel which will later return the function's return value.
func Go(f func() error) chan error {
@ -71,7 +98,7 @@ func IsInGOPATH(thePath string) bool {
// IsBeegoProject checks whether the current path is a Beego application or not
func IsBeegoProject(thePath string) bool {
mainFiles := []string{}
hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/astaxie/beego".*?\).*func main()`)
hasBeegoRegex := regexp.MustCompile(`(?s)package main.*?import.*?\(.*?github.com/beego/beego/v2".*?\).*func main()`)
c := make(chan error)
// Walk the application path tree to look for main files.
// Main files must satisfy the 'hasBeegoRegex' regular expression.
@ -305,6 +332,7 @@ func Tmpl(text string, data interface{}) {
func CheckEnv(appname string) (apppath, packpath string, err error) {
gps := GetGOPATHs()
if len(gps) == 0 {
beeLogger.Log.Error("if you want new a go module project,please add param `-gopath=false`.")
beeLogger.Log.Fatal("GOPATH environment variable is not set or empty")
}
currpath, _ := os.Getwd()
@ -438,3 +466,187 @@ func defaultGOPATH() string {
}
return ""
}
func GetGoVersionSkipMinor() string {
strArray := strings.Split(runtime.Version()[2:], `.`)
return strArray[0] + `.` + strArray[1]
}
func IsGOMODULE() bool {
if combinedOutput, e := exec.Command(`go`, `env`).CombinedOutput(); e != nil {
beeLogger.Log.Errorf("i cann't find go.")
} else {
regex := regexp.MustCompile(`GOMOD="?(.+go.mod)"?`)
stringSubmatch := regex.FindStringSubmatch(string(combinedOutput))
return len(stringSubmatch) == 2
}
return false
}
func NoticeUpdateBee() {
cmd := exec.Command("go", "version")
cmd.Output()
if cmd.Process == nil || cmd.Process.Pid <= 0 {
beeLogger.Log.Warn("There is no go environment")
return
}
beeHome := system.BeegoHome
if !IsExist(beeHome) {
if err := os.MkdirAll(beeHome, 0755); err != nil {
beeLogger.Log.Fatalf("Could not create the directory: %s", err)
return
}
}
fp := beeHome + "/.noticeUpdateBee"
timeNow := time.Now().Unix()
var timeOld int64
if !IsExist(fp) {
f, err := os.Create(fp)
if err != nil {
beeLogger.Log.Warnf("Create noticeUpdateBee file err: %s", err)
return
}
defer f.Close()
}
oldContent, err := ioutil.ReadFile(fp)
if err != nil {
beeLogger.Log.Warnf("Read noticeUpdateBee file err: %s", err)
return
}
timeOld, _ = strconv.ParseInt(string(oldContent), 10, 64)
if timeNow-timeOld < 24*60*60 {
return
}
w, err := os.OpenFile(fp, os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
beeLogger.Log.Warnf("Open noticeUpdateBee file err: %s", err)
return
}
defer w.Close()
timeNowStr := strconv.FormatInt(timeNow, 10)
if _, err := w.WriteString(timeNowStr); err != nil {
beeLogger.Log.Warnf("Update noticeUpdateBee file err: %s", err)
return
}
beeLogger.Log.Info("Getting bee latest version...")
versionLast := BeeLastVersion()
versionNow := config.Version
if versionLast == "" {
beeLogger.Log.Warn("Get latest version err")
return
}
if versionNow != versionLast {
beeLogger.Log.Warnf("Update available %s ==> %s", versionNow, versionLast)
beeLogger.Log.Warn("Run `bee update` to update")
}
beeLogger.Log.Info("Your bee are up to date")
}
func BeeLastVersion() (version string) {
var url = "https://api.github.com/repos/beego/bee/tags"
resp, err := http.Get(url)
if err != nil {
beeLogger.Log.Warnf("Get bee tags from github error: %s", err)
return
}
defer resp.Body.Close()
bodyContent, _ := ioutil.ReadAll(resp.Body)
var tags []tagName
if err = json.Unmarshal(bodyContent, &tags); err != nil {
beeLogger.Log.Warnf("Unmarshal tags body error: %s", err)
return
}
if len(tags) < 1 {
beeLogger.Log.Warn("There is no tags")
return
}
last := tags[0]
re, _ := regexp.Compile(`[0-9.]+`)
versionList := re.FindStringSubmatch(last.Name)
if len(versionList) > 0 {
return versionList[0]
}
beeLogger.Log.Warn("There is no tags")
return
}
// get info of bee repos
func BeeReposInfo() (repos Repos) {
var url = "https://api.github.com/repos/beego/bee"
resp, err := http.Get(url)
if err != nil {
beeLogger.Log.Warnf("Get bee repos from github error: %s", err)
return
}
defer resp.Body.Close()
bodyContent, _ := ioutil.ReadAll(resp.Body)
if err = json.Unmarshal(bodyContent, &repos); err != nil {
beeLogger.Log.Warnf("Unmarshal repos body error: %s", err)
return
}
return
}
// get info of bee releases
func BeeReleasesInfo() (repos []Releases) {
var url = "https://api.github.com/repos/beego/bee/releases"
resp, err := http.Get(url)
if err != nil {
beeLogger.Log.Warnf("Get bee releases from github error: %s", err)
return
}
defer resp.Body.Close()
bodyContent, _ := ioutil.ReadAll(resp.Body)
if err = json.Unmarshal(bodyContent, &repos); err != nil {
beeLogger.Log.Warnf("Unmarshal releases body error: %s", err)
return
}
return
}
//TODO merge UpdateLastPublishedTime and NoticeUpdateBee
func UpdateLastPublishedTime() {
info := BeeReleasesInfo()
if len(info) == 0 {
beeLogger.Log.Warn("Has no releases")
return
}
createdAt := info[0].PublishedAt.Format("2006-01-02")
beeHome := system.BeegoHome
if !IsExist(beeHome) {
if err := os.MkdirAll(beeHome, 0755); err != nil {
beeLogger.Log.Fatalf("Could not create the directory: %s", err)
return
}
}
fp := beeHome + "/.lastPublishedAt"
w, err := os.OpenFile(fp, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0644)
if err != nil {
beeLogger.Log.Warnf("Open .lastPublishedAt file err: %s", err)
return
}
defer w.Close()
if _, err := w.WriteString(createdAt); err != nil {
beeLogger.Log.Warnf("Update .lastPublishedAt file err: %s", err)
return
}
}
func GetLastPublishedTime() string {
fp := system.BeegoHome + "/.lastPublishedAt"
if !IsExist(fp) {
UpdateLastPublishedTime()
}
w, err := os.OpenFile(fp, os.O_RDONLY, 0644)
if err != nil {
beeLogger.Log.Warnf("Open .lastPublishedAt file err: %s", err)
return "unknown"
}
t := make([]byte, 1024)
read, err := w.Read(t)
if err != nil {
beeLogger.Log.Warnf("read .lastPublishedAt file err: %s", err)
return "unknown"
}
return string(t[:read])
}

View File

@ -1,10 +1,10 @@
// Copyright 2014 beego Author. All Rights Reserved.
// Copyright 2020
//
// 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
// 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,
@ -14,12 +14,4 @@
package utils
import (
"reflect"
"runtime"
)
// GetFuncName get function name
func GetFuncName(i interface{}) string {
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}
const BEEGO_VERSION = "v2.0.1"

View File

@ -1,174 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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.
//
// Swagger™ is a project used to describe and document RESTful APIs.
//
// The Swagger specification defines a set of files required to describe such an API. These files can then be used by the Swagger-UI project to display the API and Swagger-Codegen to generate clients in various languages. Additional utilities can also take advantage of the resulting files, such as testing tools.
// Now in version 2.0, Swagger is more enabling than ever. And it's 100% open source software.
// Package swagger struct definition
package swagger
// Swagger list the resource
type Swagger struct {
SwaggerVersion string `json:"swagger,omitempty" yaml:"swagger,omitempty"`
Infos Information `json:"info" yaml:"info"`
Host string `json:"host,omitempty" yaml:"host,omitempty"`
BasePath string `json:"basePath,omitempty" yaml:"basePath,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Paths map[string]*Item `json:"paths" yaml:"paths"`
Definitions map[string]Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"`
SecurityDefinitions map[string]Security `json:"securityDefinitions,omitempty" yaml:"securityDefinitions,omitempty"`
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
Tags []Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
}
// Information Provides metadata about the API. The metadata can be used by the clients if needed.
type Information struct {
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Version string `json:"version,omitempty" yaml:"version,omitempty"`
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
Contact Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
License *License `json:"license,omitempty" yaml:"license,omitempty"`
}
// Contact information for the exposed API.
type Contact struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
EMail string `json:"email,omitempty" yaml:"email,omitempty"`
}
// License information for the exposed API.
type License struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
}
// Item Describes the operations available on a single path.
type Item struct {
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
}
// Operation Describes a single API operation on a path.
type Operation struct {
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
OperationID string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Consumes []string `json:"consumes,omitempty" yaml:"consumes,omitempty"`
Produces []string `json:"produces,omitempty" yaml:"produces,omitempty"`
Schemes []string `json:"schemes,omitempty" yaml:"schemes,omitempty"`
Parameters []Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Responses map[string]Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Security []map[string][]string `json:"security,omitempty" yaml:"security,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
}
// Parameter Describes a single operation parameter.
type Parameter struct {
In string `json:"in,omitempty" yaml:"in,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Items *ParameterItems `json:"items,omitempty" yaml:"items,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
}
// ParameterItems A limited subset of JSON-Schema's items object. It is used by parameter definitions that are not located in "body".
// http://swagger.io/specification/#itemsObject
type ParameterItems struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Items []*ParameterItems `json:"items,omitempty" yaml:"items,omitempty"` //Required if type is "array". Describes the type of items in the array.
CollectionFormat string `json:"collectionFormat,omitempty" yaml:"collectionFormat,omitempty"`
Default string `json:"default,omitempty" yaml:"default,omitempty"`
}
// Schema Object allows the definition of input and output data types.
type Schema struct {
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
Enum []interface{} `json:"enum,omitempty" yaml:"enum,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
}
// Propertie are taken from the JSON Schema definition but their definitions were adjusted to the Swagger Specification
type Propertie struct {
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default interface{} `json:"default,omitempty" yaml:"default,omitempty"`
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Example interface{} `json:"example,omitempty" yaml:"example,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"`
Properties map[string]Propertie `json:"properties,omitempty" yaml:"properties,omitempty"`
Items *Propertie `json:"items,omitempty" yaml:"items,omitempty"`
AdditionalProperties *Propertie `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
}
// Response as they are returned from executing this operation.
type Response struct {
Description string `json:"description" yaml:"description"`
Schema *Schema `json:"schema,omitempty" yaml:"schema,omitempty"`
Ref string `json:"$ref,omitempty" yaml:"$ref,omitempty"`
}
// Security Allows the definition of a security scheme that can be used by the operations
type Security struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"` // Valid values are "basic", "apiKey" or "oauth2".
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Name string `json:"name,omitempty" yaml:"name,omitempty"`
In string `json:"in,omitempty" yaml:"in,omitempty"` // Valid values are "query" or "header".
Flow string `json:"flow,omitempty" yaml:"flow,omitempty"` // Valid values are "implicit", "password", "application" or "accessCode".
AuthorizationURL string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
TokenURL string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` // The available scopes for the OAuth2 security scheme.
}
// Tag Allows adding meta data to a single tag that is used by the Operation Object
type Tag struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
}
// ExternalDocs include Additional external documentation
type ExternalDocs struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
}

View File

@ -1,478 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"bytes"
"fmt"
"log"
"reflect"
"runtime"
)
var (
dunno = []byte("???")
centerDot = []byte("·")
dot = []byte(".")
)
type pointerInfo struct {
prev *pointerInfo
n int
addr uintptr
pos int
used []int
}
// Display print the data in console
func Display(data ...interface{}) {
display(true, data...)
}
// GetDisplayString return data print string
func GetDisplayString(data ...interface{}) string {
return display(false, data...)
}
func display(displayed bool, data ...interface{}) string {
var pc, file, line, ok = runtime.Caller(2)
if !ok {
return ""
}
var buf = new(bytes.Buffer)
fmt.Fprintf(buf, "[Debug] at %s() [%s:%d]\n", function(pc), file, line)
fmt.Fprintf(buf, "\n[Variables]\n")
for i := 0; i < len(data); i += 2 {
var output = fomateinfo(len(data[i].(string))+3, data[i+1])
fmt.Fprintf(buf, "%s = %s", data[i], output)
}
if displayed {
log.Print(buf)
}
return buf.String()
}
// return data dump and format bytes
func fomateinfo(headlen int, data ...interface{}) []byte {
var buf = new(bytes.Buffer)
if len(data) > 1 {
fmt.Fprint(buf, " ")
fmt.Fprint(buf, "[")
fmt.Fprintln(buf)
}
for k, v := range data {
var buf2 = new(bytes.Buffer)
var pointers *pointerInfo
var interfaces = make([]reflect.Value, 0, 10)
printKeyValue(buf2, reflect.ValueOf(v), &pointers, &interfaces, nil, true, " ", 1)
if k < len(data)-1 {
fmt.Fprint(buf2, ", ")
}
fmt.Fprintln(buf2)
buf.Write(buf2.Bytes())
}
if len(data) > 1 {
fmt.Fprintln(buf)
fmt.Fprint(buf, " ")
fmt.Fprint(buf, "]")
}
return buf.Bytes()
}
// check data is golang basic type
func isSimpleType(val reflect.Value, kind reflect.Kind, pointers **pointerInfo, interfaces *[]reflect.Value) bool {
switch kind {
case reflect.Bool:
return true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return true
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
return true
case reflect.Float32, reflect.Float64:
return true
case reflect.Complex64, reflect.Complex128:
return true
case reflect.String:
return true
case reflect.Chan:
return true
case reflect.Invalid:
return true
case reflect.Interface:
for _, in := range *interfaces {
if reflect.DeepEqual(in, val) {
return true
}
}
return false
case reflect.UnsafePointer:
if val.IsNil() {
return true
}
var elem = val.Elem()
if isSimpleType(elem, elem.Kind(), pointers, interfaces) {
return true
}
var addr = val.Elem().UnsafeAddr()
for p := *pointers; p != nil; p = p.prev {
if addr == p.addr {
return true
}
}
return false
}
return false
}
// dump value
func printKeyValue(buf *bytes.Buffer, val reflect.Value, pointers **pointerInfo, interfaces *[]reflect.Value, structFilter func(string, string) bool, formatOutput bool, indent string, level int) {
var t = val.Kind()
switch t {
case reflect.Bool:
fmt.Fprint(buf, val.Bool())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
fmt.Fprint(buf, val.Int())
case reflect.Uint8, reflect.Uint16, reflect.Uint, reflect.Uint32, reflect.Uint64:
fmt.Fprint(buf, val.Uint())
case reflect.Float32, reflect.Float64:
fmt.Fprint(buf, val.Float())
case reflect.Complex64, reflect.Complex128:
fmt.Fprint(buf, val.Complex())
case reflect.UnsafePointer:
fmt.Fprintf(buf, "unsafe.Pointer(0x%X)", val.Pointer())
case reflect.Ptr:
if val.IsNil() {
fmt.Fprint(buf, "nil")
return
}
var addr = val.Elem().UnsafeAddr()
for p := *pointers; p != nil; p = p.prev {
if addr == p.addr {
p.used = append(p.used, buf.Len())
fmt.Fprintf(buf, "0x%X", addr)
return
}
}
*pointers = &pointerInfo{
prev: *pointers,
addr: addr,
pos: buf.Len(),
used: make([]int, 0),
}
fmt.Fprint(buf, "&")
printKeyValue(buf, val.Elem(), pointers, interfaces, structFilter, formatOutput, indent, level)
case reflect.String:
fmt.Fprint(buf, "\"", val.String(), "\"")
case reflect.Interface:
var value = val.Elem()
if !value.IsValid() {
fmt.Fprint(buf, "nil")
} else {
for _, in := range *interfaces {
if reflect.DeepEqual(in, val) {
fmt.Fprint(buf, "repeat")
return
}
}
*interfaces = append(*interfaces, val)
printKeyValue(buf, value, pointers, interfaces, structFilter, formatOutput, indent, level+1)
}
case reflect.Struct:
var t = val.Type()
fmt.Fprint(buf, t)
fmt.Fprint(buf, "{")
for i := 0; i < val.NumField(); i++ {
if formatOutput {
fmt.Fprintln(buf)
} else {
fmt.Fprint(buf, " ")
}
var name = t.Field(i).Name
if formatOutput {
for ind := 0; ind < level; ind++ {
fmt.Fprint(buf, indent)
}
}
fmt.Fprint(buf, name)
fmt.Fprint(buf, ": ")
if structFilter != nil && structFilter(t.String(), name) {
fmt.Fprint(buf, "ignore")
} else {
printKeyValue(buf, val.Field(i), pointers, interfaces, structFilter, formatOutput, indent, level+1)
}
fmt.Fprint(buf, ",")
}
if formatOutput {
fmt.Fprintln(buf)
for ind := 0; ind < level-1; ind++ {
fmt.Fprint(buf, indent)
}
} else {
fmt.Fprint(buf, " ")
}
fmt.Fprint(buf, "}")
case reflect.Array, reflect.Slice:
fmt.Fprint(buf, val.Type())
fmt.Fprint(buf, "{")
var allSimple = true
for i := 0; i < val.Len(); i++ {
var elem = val.Index(i)
var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
if !isSimple {
allSimple = false
}
if formatOutput && !isSimple {
fmt.Fprintln(buf)
} else {
fmt.Fprint(buf, " ")
}
if formatOutput && !isSimple {
for ind := 0; ind < level; ind++ {
fmt.Fprint(buf, indent)
}
}
printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
if i != val.Len()-1 || !allSimple {
fmt.Fprint(buf, ",")
}
}
if formatOutput && !allSimple {
fmt.Fprintln(buf)
for ind := 0; ind < level-1; ind++ {
fmt.Fprint(buf, indent)
}
} else {
fmt.Fprint(buf, " ")
}
fmt.Fprint(buf, "}")
case reflect.Map:
var t = val.Type()
var keys = val.MapKeys()
fmt.Fprint(buf, t)
fmt.Fprint(buf, "{")
var allSimple = true
for i := 0; i < len(keys); i++ {
var elem = val.MapIndex(keys[i])
var isSimple = isSimpleType(elem, elem.Kind(), pointers, interfaces)
if !isSimple {
allSimple = false
}
if formatOutput && !isSimple {
fmt.Fprintln(buf)
} else {
fmt.Fprint(buf, " ")
}
if formatOutput && !isSimple {
for ind := 0; ind <= level; ind++ {
fmt.Fprint(buf, indent)
}
}
printKeyValue(buf, keys[i], pointers, interfaces, structFilter, formatOutput, indent, level+1)
fmt.Fprint(buf, ": ")
printKeyValue(buf, elem, pointers, interfaces, structFilter, formatOutput, indent, level+1)
if i != val.Len()-1 || !allSimple {
fmt.Fprint(buf, ",")
}
}
if formatOutput && !allSimple {
fmt.Fprintln(buf)
for ind := 0; ind < level-1; ind++ {
fmt.Fprint(buf, indent)
}
} else {
fmt.Fprint(buf, " ")
}
fmt.Fprint(buf, "}")
case reflect.Chan:
fmt.Fprint(buf, val.Type())
case reflect.Invalid:
fmt.Fprint(buf, "invalid")
default:
fmt.Fprint(buf, "unknow")
}
}
// PrintPointerInfo dump pointer value
func PrintPointerInfo(buf *bytes.Buffer, headlen int, pointers *pointerInfo) {
var anyused = false
var pointerNum = 0
for p := pointers; p != nil; p = p.prev {
if len(p.used) > 0 {
anyused = true
}
pointerNum++
p.n = pointerNum
}
if anyused {
var pointerBufs = make([][]rune, pointerNum+1)
for i := 0; i < len(pointerBufs); i++ {
var pointerBuf = make([]rune, buf.Len()+headlen)
for j := 0; j < len(pointerBuf); j++ {
pointerBuf[j] = ' '
}
pointerBufs[i] = pointerBuf
}
for pn := 0; pn <= pointerNum; pn++ {
for p := pointers; p != nil; p = p.prev {
if len(p.used) > 0 && p.n >= pn {
if pn == p.n {
pointerBufs[pn][p.pos+headlen] = '└'
var maxpos = 0
for i, pos := range p.used {
if i < len(p.used)-1 {
pointerBufs[pn][pos+headlen] = '┴'
} else {
pointerBufs[pn][pos+headlen] = '┘'
}
maxpos = pos
}
for i := 0; i < maxpos-p.pos-1; i++ {
if pointerBufs[pn][i+p.pos+headlen+1] == ' ' {
pointerBufs[pn][i+p.pos+headlen+1] = '─'
}
}
} else {
pointerBufs[pn][p.pos+headlen] = '│'
for _, pos := range p.used {
if pointerBufs[pn][pos+headlen] == ' ' {
pointerBufs[pn][pos+headlen] = '│'
} else {
pointerBufs[pn][pos+headlen] = '┼'
}
}
}
}
}
buf.WriteString(string(pointerBufs[pn]) + "\n")
}
}
}
// Stack get stack bytes
func Stack(skip int, indent string) []byte {
var buf = new(bytes.Buffer)
for i := skip; ; i++ {
var pc, file, line, ok = runtime.Caller(i)
if !ok {
break
}
buf.WriteString(indent)
fmt.Fprintf(buf, "at %s() [%s:%d]\n", function(pc), file, line)
}
return buf.Bytes()
}
// return the name of the function containing the PC if possible,
func function(pc uintptr) []byte {
fn := runtime.FuncForPC(pc)
if fn == nil {
return dunno
}
name := []byte(fn.Name())
// The name includes the path name to the package, which is unnecessary
// since the file name is already included. Plus, it has center dots.
// That is, we see
// runtime/debug.*T·ptrmethod
// and want
// *T.ptrmethod
if period := bytes.Index(name, dot); period >= 0 {
name = name[period+1:]
}
name = bytes.Replace(name, centerDot, dot, -1)
return name
}

View File

@ -1,101 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"bufio"
"errors"
"io"
"os"
"path/filepath"
"regexp"
)
// SelfPath gets compiled executable file absolute path
func SelfPath() string {
path, _ := filepath.Abs(os.Args[0])
return path
}
// SelfDir gets compiled executable file directory
func SelfDir() string {
return filepath.Dir(SelfPath())
}
// FileExists reports whether the named file or directory exists.
func FileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// SearchFile Search a file in paths.
// this is often used in search config file in /etc ~/
func SearchFile(filename string, paths ...string) (fullpath string, err error) {
for _, path := range paths {
if fullpath = filepath.Join(path, filename); FileExists(fullpath) {
return
}
}
err = errors.New(fullpath + " not found in paths")
return
}
// GrepFile like command grep -E
// for example: GrepFile(`^hello`, "hello.txt")
// \n is striped while read
func GrepFile(patten string, filename string) (lines []string, err error) {
re, err := regexp.Compile(patten)
if err != nil {
return
}
fd, err := os.Open(filename)
if err != nil {
return
}
lines = make([]string, 0)
reader := bufio.NewReader(fd)
prefix := ""
isLongLine := false
for {
byteLine, isPrefix, er := reader.ReadLine()
if er != nil && er != io.EOF {
return nil, er
}
if er == io.EOF {
break
}
line := string(byteLine)
if isPrefix {
prefix += line
continue
} else {
isLongLine = true
}
line = prefix + line
if isLongLine {
prefix = ""
}
if re.MatchString(line) {
lines = append(lines, line)
}
}
return lines, nil
}

View File

@ -1,423 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"mime"
"mime/multipart"
"net/mail"
"net/smtp"
"net/textproto"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
)
const (
maxLineLength = 76
upperhex = "0123456789ABCDEF"
)
// Email is the type used for email messages
type Email struct {
Auth smtp.Auth
Identity string `json:"identity"`
Username string `json:"username"`
Password string `json:"password"`
Host string `json:"host"`
Port int `json:"port"`
From string `json:"from"`
To []string
Bcc []string
Cc []string
Subject string
Text string // Plaintext message (optional)
HTML string // Html message (optional)
Headers textproto.MIMEHeader
Attachments []*Attachment
ReadReceipt []string
}
// Attachment is a struct representing an email attachment.
// Based on the mime/multipart.FileHeader struct, Attachment contains the name, MIMEHeader, and content of the attachment in question
type Attachment struct {
Filename string
Header textproto.MIMEHeader
Content []byte
}
// NewEMail create new Email struct with config json.
// config json is followed from Email struct fields.
func NewEMail(config string) *Email {
e := new(Email)
e.Headers = textproto.MIMEHeader{}
err := json.Unmarshal([]byte(config), e)
if err != nil {
return nil
}
return e
}
// Bytes Make all send information to byte
func (e *Email) Bytes() ([]byte, error) {
buff := &bytes.Buffer{}
w := multipart.NewWriter(buff)
// Set the appropriate headers (overwriting any conflicts)
// Leave out Bcc (only included in envelope headers)
e.Headers.Set("To", strings.Join(e.To, ","))
if e.Cc != nil {
e.Headers.Set("Cc", strings.Join(e.Cc, ","))
}
e.Headers.Set("From", e.From)
e.Headers.Set("Subject", e.Subject)
if len(e.ReadReceipt) != 0 {
e.Headers.Set("Disposition-Notification-To", strings.Join(e.ReadReceipt, ","))
}
e.Headers.Set("MIME-Version", "1.0")
// Write the envelope headers (including any custom headers)
if err := headerToBytes(buff, e.Headers); err != nil {
return nil, fmt.Errorf("Failed to render message headers: %s", err)
}
e.Headers.Set("Content-Type", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))
fmt.Fprintf(buff, "%s:", "Content-Type")
fmt.Fprintf(buff, " %s\r\n", fmt.Sprintf("multipart/mixed;\r\n boundary=%s\r\n", w.Boundary()))
// Start the multipart/mixed part
fmt.Fprintf(buff, "--%s\r\n", w.Boundary())
header := textproto.MIMEHeader{}
// Check to see if there is a Text or HTML field
if e.Text != "" || e.HTML != "" {
subWriter := multipart.NewWriter(buff)
// Create the multipart alternative part
header.Set("Content-Type", fmt.Sprintf("multipart/alternative;\r\n boundary=%s\r\n", subWriter.Boundary()))
// Write the header
if err := headerToBytes(buff, header); err != nil {
return nil, fmt.Errorf("Failed to render multipart message headers: %s", err)
}
// Create the body sections
if e.Text != "" {
header.Set("Content-Type", fmt.Sprintf("text/plain; charset=UTF-8"))
header.Set("Content-Transfer-Encoding", "quoted-printable")
if _, err := subWriter.CreatePart(header); err != nil {
return nil, err
}
// Write the text
if err := quotePrintEncode(buff, e.Text); err != nil {
return nil, err
}
}
if e.HTML != "" {
header.Set("Content-Type", fmt.Sprintf("text/html; charset=UTF-8"))
header.Set("Content-Transfer-Encoding", "quoted-printable")
if _, err := subWriter.CreatePart(header); err != nil {
return nil, err
}
// Write the text
if err := quotePrintEncode(buff, e.HTML); err != nil {
return nil, err
}
}
if err := subWriter.Close(); err != nil {
return nil, err
}
}
// Create attachment part, if necessary
for _, a := range e.Attachments {
ap, err := w.CreatePart(a.Header)
if err != nil {
return nil, err
}
// Write the base64Wrapped content to the part
base64Wrap(ap, a.Content)
}
if err := w.Close(); err != nil {
return nil, err
}
return buff.Bytes(), nil
}
// AttachFile Add attach file to the send mail
func (e *Email) AttachFile(args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
err = errors.New("Must specify a file name and number of parameters can not exceed at least two")
return
}
filename := args[0]
id := ""
if len(args) > 1 {
id = args[1]
}
f, err := os.Open(filename)
if err != nil {
return
}
ct := mime.TypeByExtension(filepath.Ext(filename))
basename := path.Base(filename)
return e.Attach(f, basename, ct, id)
}
// Attach is used to attach content from an io.Reader to the email.
// Parameters include an io.Reader, the desired filename for the attachment, and the Content-Type.
func (e *Email) Attach(r io.Reader, filename string, args ...string) (a *Attachment, err error) {
if len(args) < 1 && len(args) > 2 {
err = errors.New("Must specify the file type and number of parameters can not exceed at least two")
return
}
c := args[0] //Content-Type
id := ""
if len(args) > 1 {
id = args[1] //Content-ID
}
var buffer bytes.Buffer
if _, err = io.Copy(&buffer, r); err != nil {
return
}
at := &Attachment{
Filename: filename,
Header: textproto.MIMEHeader{},
Content: buffer.Bytes(),
}
// Get the Content-Type to be used in the MIMEHeader
if c != "" {
at.Header.Set("Content-Type", c)
} else {
// If the Content-Type is blank, set the Content-Type to "application/octet-stream"
at.Header.Set("Content-Type", "application/octet-stream")
}
if id != "" {
at.Header.Set("Content-Disposition", fmt.Sprintf("inline;\r\n filename=\"%s\"", filename))
at.Header.Set("Content-ID", fmt.Sprintf("<%s>", id))
} else {
at.Header.Set("Content-Disposition", fmt.Sprintf("attachment;\r\n filename=\"%s\"", filename))
}
at.Header.Set("Content-Transfer-Encoding", "base64")
e.Attachments = append(e.Attachments, at)
return at, nil
}
// Send will send out the mail
func (e *Email) Send() error {
if e.Auth == nil {
e.Auth = smtp.PlainAuth(e.Identity, e.Username, e.Password, e.Host)
}
// Merge the To, Cc, and Bcc fields
to := make([]string, 0, len(e.To)+len(e.Cc)+len(e.Bcc))
to = append(append(append(to, e.To...), e.Cc...), e.Bcc...)
// Check to make sure there is at least one recipient and one "From" address
if len(to) == 0 {
return errors.New("Must specify at least one To address")
}
// Use the username if no From is provided
if len(e.From) == 0 {
e.From = e.Username
}
from, err := mail.ParseAddress(e.From)
if err != nil {
return err
}
// use mail's RFC 2047 to encode any string
e.Subject = qEncode("utf-8", e.Subject)
raw, err := e.Bytes()
if err != nil {
return err
}
return smtp.SendMail(e.Host+":"+strconv.Itoa(e.Port), e.Auth, from.Address, to, raw)
}
// quotePrintEncode writes the quoted-printable text to the IO Writer (according to RFC 2045)
func quotePrintEncode(w io.Writer, s string) error {
var buf [3]byte
mc := 0
for i := 0; i < len(s); i++ {
c := s[i]
// We're assuming Unix style text formats as input (LF line break), and
// quoted-printble uses CRLF line breaks. (Literal CRs will become
// "=0D", but probably shouldn't be there to begin with!)
if c == '\n' {
io.WriteString(w, "\r\n")
mc = 0
continue
}
var nextOut []byte
if isPrintable(c) {
nextOut = append(buf[:0], c)
} else {
nextOut = buf[:]
qpEscape(nextOut, c)
}
// Add a soft line break if the next (encoded) byte would push this line
// to or past the limit.
if mc+len(nextOut) >= maxLineLength {
if _, err := io.WriteString(w, "=\r\n"); err != nil {
return err
}
mc = 0
}
if _, err := w.Write(nextOut); err != nil {
return err
}
mc += len(nextOut)
}
// No trailing end-of-line?? Soft line break, then. TODO: is this sane?
if mc > 0 {
io.WriteString(w, "=\r\n")
}
return nil
}
// isPrintable returns true if the rune given is "printable" according to RFC 2045, false otherwise
func isPrintable(c byte) bool {
return (c >= '!' && c <= '<') || (c >= '>' && c <= '~') || (c == ' ' || c == '\n' || c == '\t')
}
// qpEscape is a helper function for quotePrintEncode which escapes a
// non-printable byte. Expects len(dest) == 3.
func qpEscape(dest []byte, c byte) {
const nums = "0123456789ABCDEF"
dest[0] = '='
dest[1] = nums[(c&0xf0)>>4]
dest[2] = nums[(c & 0xf)]
}
// headerToBytes enumerates the key and values in the header, and writes the results to the IO Writer
func headerToBytes(w io.Writer, t textproto.MIMEHeader) error {
for k, v := range t {
// Write the header key
_, err := fmt.Fprintf(w, "%s:", k)
if err != nil {
return err
}
// Write each value in the header
for _, c := range v {
_, err := fmt.Fprintf(w, " %s\r\n", c)
if err != nil {
return err
}
}
}
return nil
}
// base64Wrap encodes the attachment content, and wraps it according to RFC 2045 standards (every 76 chars)
// The output is then written to the specified io.Writer
func base64Wrap(w io.Writer, b []byte) {
// 57 raw bytes per 76-byte base64 line.
const maxRaw = 57
// Buffer for each line, including trailing CRLF.
var buffer [maxLineLength + len("\r\n")]byte
copy(buffer[maxLineLength:], "\r\n")
// Process raw chunks until there's no longer enough to fill a line.
for len(b) >= maxRaw {
base64.StdEncoding.Encode(buffer[:], b[:maxRaw])
w.Write(buffer[:])
b = b[maxRaw:]
}
// Handle the last chunk of bytes.
if len(b) > 0 {
out := buffer[:base64.StdEncoding.EncodedLen(len(b))]
base64.StdEncoding.Encode(out, b)
out = append(out, "\r\n"...)
w.Write(out)
}
}
// Encode returns the encoded-word form of s. If s is ASCII without special
// characters, it is returned unchanged. The provided charset is the IANA
// charset name of s. It is case insensitive.
// RFC 2047 encoded-word
func qEncode(charset, s string) string {
if !needsEncoding(s) {
return s
}
return encodeWord(charset, s)
}
func needsEncoding(s string) bool {
for _, b := range s {
if (b < ' ' || b > '~') && b != '\t' {
return true
}
}
return false
}
// encodeWord encodes a string into an encoded-word.
func encodeWord(charset, s string) string {
buf := getBuffer()
buf.WriteString("=?")
buf.WriteString(charset)
buf.WriteByte('?')
buf.WriteByte('q')
buf.WriteByte('?')
enc := make([]byte, 3)
for i := 0; i < len(s); i++ {
b := s[i]
switch {
case b == ' ':
buf.WriteByte('_')
case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_':
buf.WriteByte(b)
default:
enc[0] = '='
enc[1] = upperhex[b>>4]
enc[2] = upperhex[b&0x0f]
buf.Write(enc)
}
}
buf.WriteString("?=")
es := buf.String()
putBuffer(buf)
return es
}
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func getBuffer() *bytes.Buffer {
return bufPool.Get().(*bytes.Buffer)
}
func putBuffer(buf *bytes.Buffer) {
if buf.Len() > 1024 {
return
}
buf.Reset()
bufPool.Put(buf)
}

View File

@ -1,44 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"crypto/rand"
r "math/rand"
"time"
)
var alphaNum = []byte(`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`)
// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
if len(alphabets) == 0 {
alphabets = alphaNum
}
var bytes = make([]byte, n)
var randBy bool
if num, err := rand.Read(bytes); num != n || err != nil {
r.Seed(time.Now().UnixNano())
randBy = true
}
for i, b := range bytes {
if randBy {
bytes[i] = alphabets[r.Intn(len(alphabets))]
} else {
bytes[i] = alphabets[b%byte(len(alphabets))]
}
}
return bytes
}

View File

@ -1,91 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"sync"
)
// BeeMap is a map with lock
type BeeMap struct {
lock *sync.RWMutex
bm map[interface{}]interface{}
}
// NewBeeMap return new safemap
func NewBeeMap() *BeeMap {
return &BeeMap{
lock: new(sync.RWMutex),
bm: make(map[interface{}]interface{}),
}
}
// Get from maps return the k's value
func (m *BeeMap) Get(k interface{}) interface{} {
m.lock.RLock()
defer m.lock.RUnlock()
if val, ok := m.bm[k]; ok {
return val
}
return nil
}
// Set Maps the given key and value. Returns false
// if the key is already in the map and changes nothing.
func (m *BeeMap) Set(k interface{}, v interface{}) bool {
m.lock.Lock()
defer m.lock.Unlock()
if val, ok := m.bm[k]; !ok {
m.bm[k] = v
} else if val != v {
m.bm[k] = v
} else {
return false
}
return true
}
// Check Returns true if k is exist in the map.
func (m *BeeMap) Check(k interface{}) bool {
m.lock.RLock()
defer m.lock.RUnlock()
_, ok := m.bm[k]
return ok
}
// Delete the given key and value.
func (m *BeeMap) Delete(k interface{}) {
m.lock.Lock()
defer m.lock.Unlock()
delete(m.bm, k)
}
// Items returns all items in safemap.
func (m *BeeMap) Items() map[interface{}]interface{} {
m.lock.RLock()
defer m.lock.RUnlock()
r := make(map[interface{}]interface{})
for k, v := range m.bm {
r[k] = v
}
return r
}
// Count returns the number of items within the map.
func (m *BeeMap) Count() int {
m.lock.RLock()
defer m.lock.RUnlock()
return len(m.bm)
}

View File

@ -1,170 +0,0 @@
// Copyright 2014 beego Author. All Rights Reserved.
//
// 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 utils
import (
"math/rand"
"time"
)
type reducetype func(interface{}) interface{}
type filtertype func(interface{}) bool
// InSlice checks given string in string slice or not.
func InSlice(v string, sl []string) bool {
for _, vv := range sl {
if vv == v {
return true
}
}
return false
}
// InSliceIface checks given interface in interface slice.
func InSliceIface(v interface{}, sl []interface{}) bool {
for _, vv := range sl {
if vv == v {
return true
}
}
return false
}
// SliceRandList generate an int slice from min to max.
func SliceRandList(min, max int) []int {
if max < min {
min, max = max, min
}
length := max - min + 1
t0 := time.Now()
rand.Seed(int64(t0.Nanosecond()))
list := rand.Perm(length)
for index := range list {
list[index] += min
}
return list
}
// SliceMerge merges interface slices to one slice.
func SliceMerge(slice1, slice2 []interface{}) (c []interface{}) {
c = append(slice1, slice2...)
return
}
// SliceReduce generates a new slice after parsing every value by reduce function
func SliceReduce(slice []interface{}, a reducetype) (dslice []interface{}) {
for _, v := range slice {
dslice = append(dslice, a(v))
}
return
}
// SliceRand returns random one from slice.
func SliceRand(a []interface{}) (b interface{}) {
randnum := rand.Intn(len(a))
b = a[randnum]
return
}
// SliceSum sums all values in int64 slice.
func SliceSum(intslice []int64) (sum int64) {
for _, v := range intslice {
sum += v
}
return
}
// SliceFilter generates a new slice after filter function.
func SliceFilter(slice []interface{}, a filtertype) (ftslice []interface{}) {
for _, v := range slice {
if a(v) {
ftslice = append(ftslice, v)
}
}
return
}
// SliceDiff returns diff slice of slice1 - slice2.
func SliceDiff(slice1, slice2 []interface{}) (diffslice []interface{}) {
for _, v := range slice1 {
if !InSliceIface(v, slice2) {
diffslice = append(diffslice, v)
}
}
return
}
// SliceIntersect returns slice that are present in all the slice1 and slice2.
func SliceIntersect(slice1, slice2 []interface{}) (diffslice []interface{}) {
for _, v := range slice1 {
if InSliceIface(v, slice2) {
diffslice = append(diffslice, v)
}
}
return
}
// SliceChunk separates one slice to some sized slice.
func SliceChunk(slice []interface{}, size int) (chunkslice [][]interface{}) {
if size >= len(slice) {
chunkslice = append(chunkslice, slice)
return
}
end := size
for i := 0; i <= (len(slice) - size); i += size {
chunkslice = append(chunkslice, slice[i:end])
end += size
}
return
}
// SliceRange generates a new slice from begin to end with step duration of int64 number.
func SliceRange(start, end, step int64) (intslice []int64) {
for i := start; i <= end; i += step {
intslice = append(intslice, i)
}
return
}
// SlicePad prepends size number of val into slice.
func SlicePad(slice []interface{}, size int, val interface{}) []interface{} {
if size <= len(slice) {
return slice
}
for i := 0; i < (size - len(slice)); i++ {
slice = append(slice, val)
}
return slice
}
// SliceUnique cleans repeated values in slice.
func SliceUnique(slice []interface{}) (uniqueslice []interface{}) {
for _, v := range slice {
if !InSliceIface(v, uniqueslice) {
uniqueslice = append(uniqueslice, v)
}
}
return
}
// SliceShuffle shuffles a slice.
func SliceShuffle(slice []interface{}) []interface{} {
for i := 0; i < len(slice); i++ {
a := rand.Intn(len(slice))
b := rand.Intn(len(slice))
slice[a], slice[b] = slice[b], slice[a]
}
return slice
}

View File

@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Derek Parker
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,134 +0,0 @@
package config
import (
"fmt"
"io/ioutil"
"os"
"os/user"
"path"
yaml "gopkg.in/yaml.v2"
)
const (
configDir string = ".dlv"
configFile string = "config.yml"
)
// Describes a rule for substitution of path to source code file.
type SubstitutePathRule struct {
// Directory path will be substituted if it matches `From`.
From string
// Path to which substitution is performed.
To string
}
// Slice of source code path substitution rules.
type SubstitutePathRules []SubstitutePathRule
// Config defines all configuration options available to be set through the config file.
type Config struct {
// Commands aliases.
Aliases map[string][]string
// Source code path substitution rules.
SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
}
// LoadConfig attempts to populate a Config object from the config.yml file.
func LoadConfig() *Config {
err := createConfigPath()
if err != nil {
fmt.Printf("Could not create config directory: %v.", err)
return nil
}
fullConfigFile, err := GetConfigFilePath(configFile)
if err != nil {
fmt.Printf("Unable to get config file path: %v.", err)
return nil
}
f, err := os.Open(fullConfigFile)
if err != nil {
createDefaultConfig(fullConfigFile)
return nil
}
defer func() {
err := f.Close()
if err != nil {
fmt.Printf("Closing config file failed: %v.", err)
}
}()
data, err := ioutil.ReadAll(f)
if err != nil {
fmt.Printf("Unable to read config data: %v.", err)
return nil
}
var c Config
err = yaml.Unmarshal(data, &c)
if err != nil {
fmt.Printf("Unable to decode config file: %v.", err)
return nil
}
return &c
}
func createDefaultConfig(path string) {
f, err := os.Create(path)
if err != nil {
fmt.Printf("Unable to create config file: %v.", err)
return
}
defer func() {
err := f.Close()
if err != nil {
fmt.Printf("Closing config file failed: %v.", err)
}
}()
err = writeDefaultConfig(f)
if err != nil {
fmt.Printf("Unable to write default configuration: %v.", err)
}
}
func writeDefaultConfig(f *os.File) error {
_, err := f.WriteString(
`# Configuration file for the delve debugger.
# This is the default configuration file. Available options are provided, but disabled.
# Delete the leading hash mark to enable an item.
# Provided aliases will be added to the default aliases for a given command.
aliases:
# command: ["alias1", "alias2"]
# Define sources path substitution rules. Can be used to rewrite a source path stored
# in program's debug information, if the sources were moved to a different place
# between compilation and debugging.
# Note that substitution rules will not be used for paths passed to "break" and "trace"
# commands.
substitute-path:
# - {from: path, to: path}
`)
return err
}
// createConfigPath creates the directory structure at which all config files are saved.
func createConfigPath() error {
path, err := GetConfigFilePath("")
if err != nil {
return err
}
return os.MkdirAll(path, 0700)
}
// GetConfigFilePath gets the full path to the given config file name.
func GetConfigFilePath(file string) (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
return path.Join(usr.HomeDir, configDir, file), nil
}

View File

@ -1,95 +0,0 @@
package frame
import (
"encoding/binary"
"fmt"
"sort"
)
// Represents a Common Information Entry in
// the Dwarf .debug_frame section.
type CommonInformationEntry struct {
Length uint32
CIE_id uint32
Version uint8
Augmentation string
CodeAlignmentFactor uint64
DataAlignmentFactor int64
ReturnAddressRegister uint64
InitialInstructions []byte
}
// Represents a Frame Descriptor Entry in the
// Dwarf .debug_frame section.
type FrameDescriptionEntry struct {
Length uint32
CIE *CommonInformationEntry
Instructions []byte
begin, end uint64
order binary.ByteOrder
}
// Returns whether or not the given address is within the
// bounds of this frame.
func (fde *FrameDescriptionEntry) Cover(addr uint64) bool {
if (addr - fde.begin) < fde.end {
return true
}
return false
}
// Address of first location for this frame.
func (fde *FrameDescriptionEntry) Begin() uint64 {
return fde.begin
}
// Address of last location for this frame.
func (fde *FrameDescriptionEntry) End() uint64 {
return fde.begin + fde.end
}
// Set up frame for the given PC.
func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
return executeDwarfProgramUntilPC(fde, pc)
}
// Return the offset from the current SP that the return address is stored at.
func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) (frameOffset, returnAddressOffset int64) {
frame := fde.EstablishFrame(pc)
return frame.cfa.offset, frame.regs[fde.CIE.ReturnAddressRegister].offset
}
type FrameDescriptionEntries []*FrameDescriptionEntry
func NewFrameIndex() FrameDescriptionEntries {
return make(FrameDescriptionEntries, 0, 1000)
}
type NoFDEForPCError struct {
PC uint64
}
func (err *NoFDEForPCError) Error() string {
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
}
// Returns the Frame Description Entry for the given PC.
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
idx := sort.Search(len(fdes), func(i int) bool {
if fdes[i].Cover(pc) {
return true
}
if fdes[i].LessThan(pc) {
return false
}
return true
})
if idx == len(fdes) {
return nil, &NoFDEForPCError{pc}
}
return fdes[idx], nil
}
func (frame *FrameDescriptionEntry) LessThan(pc uint64) bool {
return frame.End() <= pc
}

View File

@ -1,164 +0,0 @@
package frame
// Operation opcodes
const (
DW_OP_addr = 0x03
DW_OP_const1s = 0x09
)
const (
DW_OP_const2u = 0x0a
DW_OP_const2s = 0x0b
DW_OP_const4u = iota
DW_OP_const4s
DW_OP_const8u
DW_OP_const8s
DW_OP_constu
DW_OP_consts
DW_OP_dup
DW_OP_drop
DW_OP_over
DW_OP_pick
DW_OP_swap
DW_OP_rot
DW_OP_xderef
DW_OP_abs
DW_OP_and
DW_OP_div
DW_OP_minus
DW_OP_mod
DW_OP_mul
DW_OP_neg
DW_OP_not
DW_OP_or
DW_OP_plus
DW_OP_plus_uconst
DW_OP_shl
DW_OP_shr
DW_OP_shra
DW_OP_xor
DW_OP_skip
DW_OP_bra
DW_OP_eq
DW_OP_ge
DW_OP_gt
DW_OP_le
DW_OP_lt
DW_OP_ne
)
const (
DW_OP_lit0 = 0x30
DW_OP_lit1 = 0x31
DW_OP_lit2 = iota
DW_OP_lit3
DW_OP_lit4
DW_OP_lit5
DW_OP_lit6
DW_OP_lit7
DW_OP_lit8
DW_OP_lit9
DW_OP_lit10
DW_OP_lit11
DW_OP_lit12
DW_OP_lit13
DW_OP_lit14
DW_OP_lit15
DW_OP_lit16
DW_OP_lit17
DW_OP_lit18
DW_OP_lit19
DW_OP_lit20
DW_OP_lit21
DW_OP_lit22
DW_OP_lit23
DW_OP_lit24
DW_OP_lit25
DW_OP_lit26
DW_OP_lit27
DW_OP_lit28
DW_OP_lit29
DW_OP_lit30
DW_OP_lit31
DW_OP_reg0
DW_OP_reg1
DW_OP_reg2
DW_OP_reg3
DW_OP_reg4
DW_OP_reg5
DW_OP_reg6
DW_OP_reg7
DW_OP_reg8
DW_OP_reg9
DW_OP_reg10
DW_OP_reg11
DW_OP_reg12
DW_OP_reg13
DW_OP_reg14
DW_OP_reg15
DW_OP_reg16
DW_OP_reg17
DW_OP_reg18
DW_OP_reg19
DW_OP_reg20
DW_OP_reg21
DW_OP_reg22
DW_OP_reg23
DW_OP_reg24
DW_OP_reg25
DW_OP_reg26
DW_OP_reg27
DW_OP_reg28
DW_OP_reg29
DW_OP_reg30
DW_OP_reg31
DW_OP_breg0
DW_OP_breg1
DW_OP_breg2
DW_OP_breg3
DW_OP_breg4
DW_OP_breg5
DW_OP_breg6
DW_OP_breg7
DW_OP_breg8
DW_OP_breg9
DW_OP_breg10
DW_OP_breg11
DW_OP_breg12
DW_OP_breg13
DW_OP_breg14
DW_OP_breg15
DW_OP_breg16
DW_OP_breg17
DW_OP_breg18
DW_OP_breg19
DW_OP_breg20
DW_OP_breg21
DW_OP_breg22
DW_OP_breg23
DW_OP_breg24
DW_OP_breg25
DW_OP_breg26
DW_OP_breg27
DW_OP_breg28
DW_OP_breg29
DW_OP_breg30
DW_OP_breg31
DW_OP_regx
DW_OP_fbreg
DW_OP_bregx
DW_OP_piece
DW_OP_deref_size
DW_OP_xderef_size
DW_OP_nop
DW_OP_push_object_address
DW_OP_call2
DW_OP_call4
DW_OP_call_ref
DW_OP_form_tls_address
DW_OP_call_frame_cfa
DW_OP_bit_piece
DW_OP_lo_user = 0xe0
DW_OP_hi_user = 0xff
)

View File

@ -1,125 +0,0 @@
// Package frame contains data structures and
// related functions for parsing and searching
// through Dwarf .debug_frame data.
package frame
import (
"bytes"
"encoding/binary"
"github.com/derekparker/delve/dwarf/util"
)
type parsefunc func(*parseContext) parsefunc
type parseContext struct {
buf *bytes.Buffer
entries FrameDescriptionEntries
common *CommonInformationEntry
frame *FrameDescriptionEntry
length uint32
}
// Parse takes in data (a byte slice) and returns a slice of
// commonInformationEntry structures. Each commonInformationEntry
// has a slice of frameDescriptionEntry structures.
func Parse(data []byte, order binary.ByteOrder) FrameDescriptionEntries {
var (
buf = bytes.NewBuffer(data)
pctx = &parseContext{buf: buf, entries: NewFrameIndex()}
)
for fn := parselength; buf.Len() != 0; {
fn = fn(pctx)
}
for i := range pctx.entries {
pctx.entries[i].order = order
}
return pctx.entries
}
func cieEntry(data []byte) bool {
return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
}
func parselength(ctx *parseContext) parsefunc {
var data = ctx.buf.Next(8)
ctx.length = binary.LittleEndian.Uint32(data[:4]) - 4 // take off the length of the CIE id / CIE pointer.
if cieEntry(data[4:]) {
ctx.common = &CommonInformationEntry{Length: ctx.length}
return parseCIE
}
ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: ctx.common}
return parseFDE
}
func parseFDE(ctx *parseContext) parsefunc {
r := ctx.buf.Next(int(ctx.length))
ctx.frame.begin = binary.LittleEndian.Uint64(r[:8])
ctx.frame.end = binary.LittleEndian.Uint64(r[8:16])
// Insert into the tree after setting address range begin
// otherwise compares won't work.
ctx.entries = append(ctx.entries, ctx.frame)
// The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer
// cursor to length.
ctx.frame.Instructions = r[16:]
ctx.length = 0
return parselength
}
func parseCIE(ctx *parseContext) parsefunc {
data := ctx.buf.Next(int(ctx.length))
buf := bytes.NewBuffer(data)
// parse version
ctx.common.Version = data[0]
// parse augmentation
ctx.common.Augmentation, _ = util.ParseString(buf)
// parse code alignment factor
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
// parse data alignment factor
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
// parse return address register
ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
// parse initial instructions
// The rest of this entry consists of the instructions
// so we can just grab all of the data from the buffer
// cursor to length.
ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
ctx.length = 0
return parselength
}
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
// Trick borrowed from "debug/dwarf".New()
func DwarfEndian(infoSec []byte) binary.ByteOrder {
if len(infoSec) < 6 {
return binary.BigEndian
}
x, y := infoSec[4], infoSec[5]
switch {
case x == 0 && y == 0:
return binary.BigEndian
case x == 0:
return binary.BigEndian
case y == 0:
return binary.LittleEndian
default:
return binary.BigEndian
}
}

View File

@ -1,429 +0,0 @@
package frame
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/derekparker/delve/dwarf/util"
)
type CurrentFrameAddress struct {
register uint64
offset int64
expression []byte
rule byte
}
type DWRule struct {
rule byte
offset int64
newreg uint64
expression []byte
}
type FrameContext struct {
loc uint64
order binary.ByteOrder
address uint64
cfa CurrentFrameAddress
regs map[uint64]DWRule
initialRegs map[uint64]DWRule
prevRegs map[uint64]DWRule
buf *bytes.Buffer
cie *CommonInformationEntry
codeAlignment uint64
dataAlignment int64
}
func (fctx *FrameContext) CFAOffset() int64 {
return fctx.cfa.offset
}
// Instructions used to recreate the table from the .debug_frame data.
const (
DW_CFA_nop = 0x0 // No ops
DW_CFA_set_loc = 0x01 // op1: address
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
DW_CFA_advance_loc2 // op1: 2-byte delta
DW_CFA_advance_loc4 // op1: 4-byte delta
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_restore_extended // op1: ULEB128 register
DW_CFA_undefined // op1: ULEB128 register
DW_CFA_same_value // op1: ULEB128 register
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
DW_CFA_remember_state // No ops
DW_CFA_restore_state // No ops
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
DW_CFA_def_cfa_register // op1: ULEB128 register
DW_CFA_def_cfa_offset // op1: ULEB128 offset
DW_CFA_def_cfa_expression // op1: BLOCK
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 BLOCK
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
DW_CFA_lo_user = 0x1c // op1: BLOCK
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
)
// Rules defined for register values.
const (
rule_undefined = iota
rule_sameval
rule_offset
rule_valoffset
rule_register
rule_expression
rule_valexpression
rule_architectural
)
const low_6_offset = 0x3f
type instruction func(frame *FrameContext)
// // Mapping from DWARF opcode to function.
var fnlookup = map[byte]instruction{
DW_CFA_advance_loc: advanceloc,
DW_CFA_offset: offset,
DW_CFA_restore: restore,
DW_CFA_set_loc: setloc,
DW_CFA_advance_loc1: advanceloc1,
DW_CFA_advance_loc2: advanceloc2,
DW_CFA_advance_loc4: advanceloc4,
DW_CFA_offset_extended: offsetextended,
DW_CFA_restore_extended: restoreextended,
DW_CFA_undefined: undefined,
DW_CFA_same_value: samevalue,
DW_CFA_register: register,
DW_CFA_remember_state: rememberstate,
DW_CFA_restore_state: restorestate,
DW_CFA_def_cfa: defcfa,
DW_CFA_def_cfa_register: defcfaregister,
DW_CFA_def_cfa_offset: defcfaoffset,
DW_CFA_def_cfa_expression: defcfaexpression,
DW_CFA_expression: expression,
DW_CFA_offset_extended_sf: offsetextendedsf,
DW_CFA_def_cfa_sf: defcfasf,
DW_CFA_def_cfa_offset_sf: defcfaoffsetsf,
DW_CFA_val_offset: valoffset,
DW_CFA_val_offset_sf: valoffsetsf,
DW_CFA_val_expression: valexpression,
DW_CFA_lo_user: louser,
DW_CFA_hi_user: hiuser,
}
func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
initialInstructions := make([]byte, len(cie.InitialInstructions))
copy(initialInstructions, cie.InitialInstructions)
frame := &FrameContext{
cie: cie,
regs: make(map[uint64]DWRule),
initialRegs: make(map[uint64]DWRule),
prevRegs: make(map[uint64]DWRule),
codeAlignment: cie.CodeAlignmentFactor,
dataAlignment: cie.DataAlignmentFactor,
buf: bytes.NewBuffer(initialInstructions),
}
frame.ExecuteDwarfProgram()
return frame
}
// Unwind the stack to find the return address register.
func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameContext {
frame := executeCIEInstructions(fde.CIE)
frame.order = fde.order
frame.loc = fde.Begin()
frame.address = pc
fdeInstructions := make([]byte, len(fde.Instructions))
copy(fdeInstructions, fde.Instructions)
frame.ExecuteUntilPC(fdeInstructions)
return frame
}
func (frame *FrameContext) ExecuteDwarfProgram() {
for frame.buf.Len() > 0 {
executeDwarfInstruction(frame)
}
}
// Execute dwarf instructions.
func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
frame.buf.Truncate(0)
frame.buf.Write(instructions)
// We only need to execute the instructions until
// ctx.loc > ctx.addess (which is the address we
// are currently at in the traced process).
for frame.address >= frame.loc && frame.buf.Len() > 0 {
executeDwarfInstruction(frame)
}
}
func executeDwarfInstruction(frame *FrameContext) {
instruction, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read from instruction buffer")
}
if instruction == DW_CFA_nop {
return
}
fn := lookupFunc(instruction, frame.buf)
fn(frame)
}
func lookupFunc(instruction byte, buf *bytes.Buffer) instruction {
const high_2_bits = 0xc0
var restore bool
// Special case the 3 opcodes that have their argument encoded in the opcode itself.
switch instruction & high_2_bits {
case DW_CFA_advance_loc:
instruction = DW_CFA_advance_loc
restore = true
case DW_CFA_offset:
instruction = DW_CFA_offset
restore = true
case DW_CFA_restore:
instruction = DW_CFA_restore
restore = true
}
if restore {
// Restore the last byte as it actually contains the argument for the opcode.
err := buf.UnreadByte()
if err != nil {
panic("Could not unread byte")
}
}
fn, ok := fnlookup[instruction]
if !ok {
panic(fmt.Sprintf("Encountered an unexpected DWARF CFA opcode: %#v", instruction))
}
return fn
}
func advanceloc(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read byte")
}
delta := b & low_6_offset
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc1(frame *FrameContext) {
delta, err := frame.buf.ReadByte()
if err != nil {
panic("Could not read byte")
}
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc2(frame *FrameContext) {
var delta uint16
binary.Read(frame.buf, frame.order, &delta)
frame.loc += uint64(delta) * frame.codeAlignment
}
func advanceloc4(frame *FrameContext) {
var delta uint32
binary.Read(frame.buf, frame.order, &delta)
frame.loc += uint64(delta) * frame.codeAlignment
}
func offset(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic(err)
}
var (
reg = b & low_6_offset
offset, _ = util.DecodeULEB128(frame.buf)
)
frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
}
func restore(frame *FrameContext) {
b, err := frame.buf.ReadByte()
if err != nil {
panic(err)
}
reg := uint64(b & low_6_offset)
oldrule, ok := frame.initialRegs[reg]
if ok {
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
} else {
frame.regs[reg] = DWRule{rule: rule_undefined}
}
}
func setloc(frame *FrameContext) {
var loc uint64
binary.Read(frame.buf, frame.order, &loc)
frame.loc = loc
}
func offsetextended(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
offset, _ = util.DecodeULEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
}
func undefined(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
frame.regs[reg] = DWRule{rule: rule_undefined}
}
func samevalue(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
frame.regs[reg] = DWRule{rule: rule_sameval}
}
func register(frame *FrameContext) {
reg1, _ := util.DecodeULEB128(frame.buf)
reg2, _ := util.DecodeULEB128(frame.buf)
frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register}
}
func rememberstate(frame *FrameContext) {
frame.prevRegs = frame.regs
}
func restorestate(frame *FrameContext) {
frame.regs = frame.prevRegs
}
func restoreextended(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
oldrule, ok := frame.initialRegs[reg]
if ok {
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
} else {
frame.regs[reg] = DWRule{rule: rule_undefined}
}
}
func defcfa(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
offset, _ := util.DecodeULEB128(frame.buf)
frame.cfa.register = reg
frame.cfa.offset = int64(offset)
}
func defcfaregister(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
frame.cfa.register = reg
}
func defcfaoffset(frame *FrameContext) {
offset, _ := util.DecodeULEB128(frame.buf)
frame.cfa.offset = int64(offset)
}
func defcfasf(frame *FrameContext) {
reg, _ := util.DecodeULEB128(frame.buf)
offset, _ := util.DecodeSLEB128(frame.buf)
frame.cfa.register = reg
frame.cfa.offset = offset * frame.dataAlignment
}
func defcfaoffsetsf(frame *FrameContext) {
offset, _ := util.DecodeSLEB128(frame.buf)
offset *= frame.dataAlignment
frame.cfa.offset = offset
}
func defcfaexpression(frame *FrameContext) {
var (
l, _ = util.DecodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.cfa.expression = expr
frame.cfa.rule = rule_expression
}
func expression(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
l, _ = util.DecodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.regs[reg] = DWRule{rule: rule_expression, expression: expr}
}
func offsetextendedsf(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
offset, _ = util.DecodeSLEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset}
}
func valoffset(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
offset, _ = util.DecodeULEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset}
}
func valoffsetsf(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
offset, _ = util.DecodeSLEB128(frame.buf)
)
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset}
}
func valexpression(frame *FrameContext) {
var (
reg, _ = util.DecodeULEB128(frame.buf)
l, _ = util.DecodeULEB128(frame.buf)
expr = frame.buf.Next(int(l))
)
frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr}
}
func louser(frame *FrameContext) {
frame.buf.Next(1)
}
func hiuser(frame *FrameContext) {
frame.buf.Next(1)
}

View File

@ -1,122 +0,0 @@
package line
import (
"bytes"
"encoding/binary"
"github.com/derekparker/delve/dwarf/util"
)
type DebugLinePrologue struct {
UnitLength uint32
Version uint16
Length uint32
MinInstrLength uint8
InitialIsStmt uint8
LineBase int8
LineRange uint8
OpcodeBase uint8
StdOpLengths []uint8
}
type DebugLineInfo struct {
Prologue *DebugLinePrologue
IncludeDirs []string
FileNames []*FileEntry
Instructions []byte
Lookup map[string]*FileEntry
}
type FileEntry struct {
Name string
DirIdx uint64
LastModTime uint64
Length uint64
}
type DebugLines []*DebugLineInfo
func (d *DebugLines) GetLineInfo(name string) *DebugLineInfo {
// Find in which table file exists and return it.
for _, l := range *d {
if _, ok := l.Lookup[name]; ok {
return l
}
}
return nil
}
func Parse(data []byte) DebugLines {
var (
lines = make(DebugLines, 0)
buf = bytes.NewBuffer(data)
)
// We have to parse multiple file name tables here.
for buf.Len() > 0 {
dbl := new(DebugLineInfo)
dbl.Lookup = make(map[string]*FileEntry)
parseDebugLinePrologue(dbl, buf)
parseIncludeDirs(dbl, buf)
parseFileEntries(dbl, buf)
// Instructions size calculation breakdown:
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
// - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
lines = append(lines, dbl)
}
return lines
}
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
p := new(DebugLinePrologue)
p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
p.Version = binary.LittleEndian.Uint16(buf.Next(2))
p.Length = binary.LittleEndian.Uint32(buf.Next(4))
p.MinInstrLength = uint8(buf.Next(1)[0])
p.InitialIsStmt = uint8(buf.Next(1)[0])
p.LineBase = int8(buf.Next(1)[0])
p.LineRange = uint8(buf.Next(1)[0])
p.OpcodeBase = uint8(buf.Next(1)[0])
p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
dbl.Prologue = p
}
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
for {
str, _ := util.ParseString(buf)
if str == "" {
break
}
info.IncludeDirs = append(info.IncludeDirs, str)
}
}
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
for {
entry := new(FileEntry)
name, _ := util.ParseString(buf)
if name == "" {
break
}
entry.Name = name
entry.DirIdx, _ = util.DecodeULEB128(buf)
entry.LastModTime, _ = util.DecodeULEB128(buf)
entry.Length, _ = util.DecodeULEB128(buf)
info.FileNames = append(info.FileNames, entry)
info.Lookup[name] = entry
}
}

View File

@ -1,252 +0,0 @@
package line
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/derekparker/delve/dwarf/util"
)
type Location struct {
File string
Line int
Address uint64
Delta int
}
type StateMachine struct {
dbl *DebugLineInfo
file string
line int
address uint64
column uint
isStmt bool
basicBlock bool
endSeq bool
lastWasStandard bool
lastDelta int
}
type opcodefn func(*StateMachine, *bytes.Buffer)
// Special opcodes
const (
DW_LNS_copy = 1
DW_LNS_advance_pc = 2
DW_LNS_advance_line = 3
DW_LNS_set_file = 4
DW_LNS_set_column = 5
DW_LNS_negate_stmt = 6
DW_LNS_set_basic_block = 7
DW_LNS_const_add_pc = 8
DW_LNS_fixed_advance_pc = 9
)
// Extended opcodes
const (
DW_LINE_end_sequence = 1
DW_LINE_set_address = 2
DW_LINE_define_file = 3
)
var standardopcodes = map[byte]opcodefn{
DW_LNS_copy: copyfn,
DW_LNS_advance_pc: advancepc,
DW_LNS_advance_line: advanceline,
DW_LNS_set_file: setfile,
DW_LNS_set_column: setcolumn,
DW_LNS_negate_stmt: negatestmt,
DW_LNS_set_basic_block: setbasicblock,
DW_LNS_const_add_pc: constaddpc,
DW_LNS_fixed_advance_pc: fixedadvancepc,
}
var extendedopcodes = map[byte]opcodefn{
DW_LINE_end_sequence: endsequence,
DW_LINE_set_address: setaddress,
DW_LINE_define_file: definefile,
}
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
return &StateMachine{dbl: dbl, file: dbl.FileNames[0].Name, line: 1}
}
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
// could be split amongst 2 PCs.
func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
var (
foundFile bool
lastAddr uint64
lineInfo = dbl.GetLineInfo(f)
sm = newStateMachine(lineInfo)
buf = bytes.NewBuffer(lineInfo.Instructions)
)
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if foundFile && sm.file != f {
return
}
if sm.line == l && sm.file == f && sm.address != lastAddr {
foundFile = true
pcs = append(pcs, sm.address)
line := sm.line
// Keep going until we're on a different line. We only care about
// when a line comes back around (i.e. for loop) so get to next line,
// and try to find the line we care about again.
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if line < sm.line {
break
}
}
}
}
return
}
var NoSourceError = errors.New("no source available")
func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint64, error) {
lineInfo := dbl.GetLineInfo(filename)
if lineInfo == nil {
return nil, NoSourceError
}
var (
pcs []uint64
lastaddr uint64
sm = newStateMachine(lineInfo)
buf = bytes.NewBuffer(lineInfo.Instructions)
)
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
findAndExecOpcode(sm, buf, b)
if sm.address > end {
break
}
if sm.address >= begin && sm.address > lastaddr {
lastaddr = sm.address
pcs = append(pcs, sm.address)
}
}
return pcs, nil
}
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
switch {
case b == 0:
execExtendedOpcode(sm, b, buf)
case b < sm.dbl.Prologue.OpcodeBase:
execStandardOpcode(sm, b, buf)
default:
execSpecialOpcode(sm, b)
}
}
func execSpecialOpcode(sm *StateMachine, instr byte) {
var (
opcode = uint8(instr)
decoded = opcode - sm.dbl.Prologue.OpcodeBase
)
if sm.dbl.Prologue.InitialIsStmt == uint8(1) {
sm.isStmt = true
}
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
sm.line += sm.lastDelta
sm.address += uint64(decoded / sm.dbl.Prologue.LineRange)
sm.basicBlock = false
sm.lastWasStandard = false
}
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
_, _ = util.DecodeULEB128(buf)
b, _ := buf.ReadByte()
fn, ok := extendedopcodes[b]
if !ok {
panic(fmt.Sprintf("Encountered unknown extended opcode %#v\n", b))
}
sm.lastWasStandard = false
fn(sm, buf)
}
func execStandardOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
fn, ok := standardopcodes[instr]
if !ok {
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
}
sm.lastWasStandard = true
fn(sm, buf)
}
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
sm.basicBlock = false
}
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
addr, _ := util.DecodeULEB128(buf)
sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
}
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
line, _ := util.DecodeSLEB128(buf)
sm.line += int(line)
sm.lastDelta = int(line)
}
func setfile(sm *StateMachine, buf *bytes.Buffer) {
i, _ := util.DecodeULEB128(buf)
sm.file = sm.dbl.FileNames[i-1].Name
}
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
c, _ := util.DecodeULEB128(buf)
sm.column = uint(c)
}
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
sm.isStmt = !sm.isStmt
}
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
sm.basicBlock = true
}
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
sm.address += (255 / uint64(sm.dbl.Prologue.LineRange))
}
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
var operand uint16
binary.Read(buf, binary.LittleEndian, &operand)
sm.address += uint64(operand)
}
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
sm.endSeq = true
}
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
var addr uint64
binary.Read(buf, binary.LittleEndian, &addr)
sm.address = addr
}
func definefile(sm *StateMachine, buf *bytes.Buffer) {
var (
_, _ = util.ParseString(buf)
_, _ = util.DecodeULEB128(buf)
_, _ = util.DecodeULEB128(buf)
_, _ = util.DecodeULEB128(buf)
)
// Don't do anything here yet.
}

View File

@ -1,84 +0,0 @@
package op
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"github.com/derekparker/delve/dwarf/util"
)
const (
DW_OP_addr = 0x3
DW_OP_call_frame_cfa = 0x9c
DW_OP_plus = 0x22
DW_OP_consts = 0x11
DW_OP_plus_uconsts = 0x23
)
type stackfn func(*bytes.Buffer, []int64, int64) ([]int64, error)
var oplut = map[byte]stackfn{
DW_OP_call_frame_cfa: callframecfa,
DW_OP_plus: plus,
DW_OP_consts: consts,
DW_OP_addr: addr,
DW_OP_plus_uconsts: plusuconsts,
}
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
stack := make([]int64, 0, 3)
buf := bytes.NewBuffer(instructions)
for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() {
fn, ok := oplut[opcode]
if !ok {
return 0, fmt.Errorf("invalid instruction %#v", opcode)
}
stack, err = fn(buf, stack, cfa)
if err != nil {
return 0, err
}
}
if len(stack) == 0 {
return 0, errors.New("empty OP stack")
}
return stack[len(stack)-1], nil
}
func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
if cfa == 0 {
return stack, fmt.Errorf("Could not retrieve CFA for current PC")
}
return append(stack, int64(cfa)), nil
}
func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
}
func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
var (
slen = len(stack)
digits = stack[slen-2 : slen]
st = stack[:slen-2]
)
return append(st, digits[0]+digits[1]), nil
}
func plusuconsts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
slen := len(stack)
num, _ := util.DecodeULEB128(buf)
stack[slen-1] = stack[slen-1] + int64(num)
return stack, nil
}
func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
num, _ := util.DecodeSLEB128(buf)
return append(stack, num), nil
}

View File

@ -1,345 +0,0 @@
package reader
import (
"errors"
"fmt"
"golang.org/x/debug/dwarf"
"github.com/derekparker/delve/dwarf/op"
)
type Reader struct {
*dwarf.Reader
depth int
}
// New returns a reader for the specified dwarf data.
func New(data *dwarf.Data) *Reader {
return &Reader{data.Reader(), 0}
}
// Seek moves the reader to an arbitrary offset.
func (reader *Reader) Seek(off dwarf.Offset) {
reader.depth = 0
reader.Reader.Seek(off)
}
// SeekToEntry moves the reader to an arbitrary entry.
func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error {
reader.Seek(entry.Offset)
// Consume the current entry so .Next works as intended
_, err := reader.Next()
return err
}
// SeekToFunctionEntry moves the reader to the function that includes the
// specified program counter.
func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
reader.Seek(0)
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Tag != dwarf.TagSubprogram {
continue
}
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
if !ok {
continue
}
highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64)
if !ok {
continue
}
if lowpc <= pc && highpc > pc {
return entry, nil
}
}
return nil, fmt.Errorf("unable to find function context")
}
// Returns the address for the named entry.
func (reader *Reader) AddrFor(name string) (uint64, error) {
entry, err := reader.FindEntryNamed(name, false)
if err != nil {
return 0, err
}
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
return 0, fmt.Errorf("type assertion failed")
}
addr, err := op.ExecuteStackProgram(0, instructions)
if err != nil {
return 0, err
}
return uint64(addr), nil
}
// Returns the address for the named struct member. Expects the reader to be at the parent entry
// or one of the parents children, thus does not seek to parent by itself.
func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) {
for {
entry, err := reader.NextMemberVariable()
if err != nil {
return 0, err
}
if entry == nil {
return 0, fmt.Errorf("nil entry for member named %s", member)
}
name, ok := entry.Val(dwarf.AttrName).(string)
if !ok || name != member {
continue
}
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
if !ok {
continue
}
addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
return uint64(addr), err
}
}
var TypeNotFoundErr = errors.New("no type entry found, use 'types' for a list of valid types")
// SeekToType moves the reader to the type specified by the entry,
// optionally resolving typedefs and pointer types. If the reader is set
// to a struct type the NextMemberVariable call can be used to walk all member data.
func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) {
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
return nil, fmt.Errorf("entry does not have a type attribute")
}
// Seek to the first type offset
reader.Seek(offset)
// Walk the types to the base
for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() {
if err != nil {
return nil, err
}
if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs {
return typeEntry, nil
}
if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes {
return typeEntry, nil
}
offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
return typeEntry, nil
}
reader.Seek(offset)
}
return nil, TypeNotFoundErr
}
func (reader *Reader) NextType() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
switch entry.Tag {
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
return entry, nil
}
}
return nil, nil
}
// SeekToTypeNamed moves the reader to the type specified by the name.
// If the reader is set to a struct type the NextMemberVariable call
// can be used to walk all member data.
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
// Walk the types to the base
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
if err != nil {
return nil, err
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok {
continue
}
if n == name {
return entry, nil
}
}
return nil, TypeNotFoundErr
}
// Finds the entry for 'name'.
func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) {
depth := 1
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Children {
depth++
}
if entry.Tag == 0 {
depth--
if depth <= 0 {
return nil, fmt.Errorf("could not find symbol value for %s", name)
}
}
if member {
if entry.Tag != dwarf.TagMember {
continue
}
} else {
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
continue
}
}
n, ok := entry.Val(dwarf.AttrName).(string)
if !ok || n != name {
continue
}
return entry, nil
}
return nil, fmt.Errorf("could not find symbol value for %s", name)
}
func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) {
entry, err := reader.FindEntryNamed(name, member)
if err != nil {
return nil, err
}
var attr dwarf.Attr
if member {
attr = dwarf.AttrDataMemberLoc
} else {
attr = dwarf.AttrLocation
}
instr, ok := entry.Val(attr).([]byte)
if !ok {
return nil, errors.New("invalid typecast for Dwarf instructions")
}
return instr, nil
}
func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
if entry.Tag == dwarf.TagMember {
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
if !ok {
return nil, fmt.Errorf("member data has no data member location attribute")
}
// clone slice to prevent stomping on the dwarf data
return append([]byte{}, instructions...), nil
}
// non-member
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
return nil, fmt.Errorf("entry has no location attribute")
}
// clone slice to prevent stomping on the dwarf data
return append([]byte{}, instructions...), nil
}
// NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry.
func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
// All scope variables will be at the same depth
reader.SkipChildren()
// End of the current depth
if entry.Tag == 0 {
break
}
if entry.Tag == dwarf.TagVariable || entry.Tag == dwarf.TagFormalParameter {
return entry, nil
}
}
// No more items
return nil, nil
}
// NextMememberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
// All member variables will be at the same depth
reader.SkipChildren()
// End of the current depth
if entry.Tag == 0 {
break
}
if entry.Tag == dwarf.TagMember {
return entry, nil
}
}
// No more items
return nil, nil
}
// NextPackageVariable moves the reader to the next debug entry that describes a package variable.
// Any TagVariable entry that is not inside a sub prgram entry and is marked external is considered a package variable.
func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Tag == dwarf.TagVariable {
ext, ok := entry.Val(dwarf.AttrExternal).(bool)
if ok && ext {
return entry, nil
}
}
// Ignore everything inside sub programs
if entry.Tag == dwarf.TagSubprogram {
reader.SkipChildren()
}
}
// No more items
return nil, nil
}
func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil {
return nil, err
}
if entry.Tag == dwarf.TagCompileUnit {
return entry, nil
}
}
return nil, nil
}

View File

@ -1,81 +0,0 @@
package util
import "bytes"
// DecodeULEB128 decodes an unsigned Little Endian Base 128
// represented number.
func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
var (
result uint64
shift uint64
length uint32
)
if buf.Len() == 0 {
return 0, 0
}
for {
b, err := buf.ReadByte()
if err != nil {
panic("Could not parse ULEB128 value")
}
length++
result |= uint64((uint(b) & 0x7f) << shift)
// If high order bit is 1.
if b&0x80 == 0 {
break
}
shift += 7
}
return result, length
}
// DecodeSLEB128 decodes a signed Little Endian Base 128
// represented number.
func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
var (
b byte
err error
result int64
shift uint64
length uint32
)
if buf.Len() == 0 {
return 0, 0
}
for {
b, err = buf.ReadByte()
if err != nil {
panic("Could not parse SLEB128 value")
}
length++
result |= int64((int64(b) & 0x7f) << shift)
shift += 7
if b&0x80 == 0 {
break
}
}
if (shift < 8*uint64(length)) && (b&0x40 > 0) {
result |= -(1 << shift)
}
return result, length
}
func ParseString(data *bytes.Buffer) (string, uint32) {
str, err := data.ReadString(0x0)
if err != nil {
panic("Could not parse string")
}
return str[:len(str)-1], uint32(len(str))
}

View File

@ -1,79 +0,0 @@
package proc
import "runtime"
// Arch defines an interface for representing a
// CPU architecture.
type Arch interface {
SetGStructOffset(ver GoVersion, iscgo bool)
PtrSize() int
BreakpointInstruction() []byte
BreakpointSize() int
GStructOffset() uint64
}
// AMD64 represents the AMD64 CPU architecture.
type AMD64 struct {
ptrSize int
breakInstruction []byte
breakInstructionLen int
gStructOffset uint64
hardwareBreakpointUsage []bool
}
// AMD64Arch returns an initialized AMD64
// struct.
func AMD64Arch() *AMD64 {
var breakInstr = []byte{0xCC}
return &AMD64{
ptrSize: 8,
breakInstruction: breakInstr,
breakInstructionLen: len(breakInstr),
hardwareBreakpointUsage: make([]bool, 4),
}
}
// SetGStructOffset sets the offset of the G struct on the AMD64
// arch struct. The offset is dependent on the Go compiler Version
// and whether or not the target program was externally linked.
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
switch runtime.GOOS {
case "darwin":
a.gStructOffset = 0x8a0
case "linux":
a.gStructOffset = 0xfffffffffffffff0
if isextld || ver.AfterOrEqual(GoVersion{1, 5, -1, 2, 0}) || ver.IsDevel() {
a.gStructOffset += 8
}
case "windows":
// Use ArbitraryUserPointer (0x28) as pointer to pointer
// to G struct per:
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
a.gStructOffset = 0x28
}
}
// PtrSize returns the size of a pointer
// on this architecture.
func (a *AMD64) PtrSize() int {
return a.ptrSize
}
// BreakpointInstruction returns the Breakpoint
// instruction for this architecture.
func (a *AMD64) BreakpointInstruction() []byte {
return a.breakInstruction
}
// BreakpointSize returns the size of the
// breakpoint instruction on this architecture.
func (a *AMD64) BreakpointSize() int {
return a.breakInstructionLen
}
// GStructOffset returns the offset of the G
// struct in thread local storage.
func (a *AMD64) GStructOffset() uint64 {
return a.gStructOffset
}

View File

@ -1,163 +0,0 @@
package proc
import (
"errors"
"fmt"
"go/ast"
"go/constant"
"reflect"
)
// Breakpoint represents a breakpoint. Stores information on the break
// point including the byte of data that originally was stored at that
// address.
type Breakpoint struct {
// File & line information for printing.
FunctionName string
File string
Line int
Addr uint64 // Address breakpoint is set for.
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
Name string // User defined name of the breakpoint
ID int // Monotonically increasing ID.
Kind BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
// Breakpoint information
Tracepoint bool // Tracepoint flag
Goroutine bool // Retrieve goroutine information
Stacktrace int // Number of stack frames to retrieve
Variables []string // Variables to evaluate
LoadArgs *LoadConfig
LoadLocals *LoadConfig
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
TotalHitCount uint64 // Number of times a breakpoint has been reached
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
// will also check if the caller is runtime.gopanic or if the return
// address is in the DeferReturns array.
// Next uses NextDeferBreakpoints for the breakpoint it sets on the
// deferred function, DeferReturns is populated with the
// addresses of calls to runtime.deferreturn in the current
// function. This insures that the breakpoint on the deferred
// function only triggers on panic or on the defer call to
// the function, not when the function is called directly
DeferReturns []uint64
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
Cond ast.Expr
}
// Breakpoint Kind determines the behavior of delve when the
// breakpoint is reached.
type BreakpointKind int
const (
// UserBreakpoint is a user set breakpoint
UserBreakpoint BreakpointKind = iota
// NextBreakpoint is a breakpoint set by Next, Continue
// will stop on it and delete it
NextBreakpoint
// NextDeferBreakpoint is a breakpoint set by Next on the
// first deferred function. In addition to checking their condition
// breakpoints of this kind will also check that the function has been
// called by runtime.gopanic or through runtime.deferreturn.
NextDeferBreakpoint
// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
// Continue will set a new breakpoint (of NextBreakpoint kind) on the
// destination of CALL, delete this breakpoint and then continue again
StepBreakpoint
)
func (bp *Breakpoint) String() string {
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
}
// Clear this breakpoint appropriately depending on whether it is a
// hardware or software breakpoint.
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
return nil, fmt.Errorf("could not clear breakpoint %s", err)
}
return bp, nil
}
// BreakpointExistsError is returned when trying to set a breakpoint at
// an address that already has a breakpoint set for it.
type BreakpointExistsError struct {
file string
line int
addr uint64
}
func (bpe BreakpointExistsError) Error() string {
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
}
// InvalidAddressError represents the result of
// attempting to set a breakpoint at an invalid address.
type InvalidAddressError struct {
address uint64
}
func (iae InvalidAddressError) Error() string {
return fmt.Sprintf("Invalid address %#v\n", iae.address)
}
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
return err
}
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
if bp.Cond == nil {
return true, nil
}
if bp.Kind == NextDeferBreakpoint {
frames, err := thread.Stacktrace(2)
if err == nil {
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
isdeferreturn := false
if len(frames) >= 1 {
for _, pc := range bp.DeferReturns {
if frames[0].Ret == pc {
isdeferreturn = true
break
}
}
}
if !ispanic && !isdeferreturn {
return false, nil
}
}
}
scope, err := thread.Scope()
if err != nil {
return true, err
}
v, err := scope.evalAST(bp.Cond)
if err != nil {
return true, fmt.Errorf("error evaluating expression: %v", err)
}
if v.Unreadable != nil {
return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
}
if v.Kind != reflect.Bool {
return true, errors.New("condition expression not boolean")
}
return constant.BoolVal(v.Value), nil
}
// Internal returns true for breakpoints not set directly by the user.
func (bp *Breakpoint) Internal() bool {
return bp.Kind != UserBreakpoint
}
// NoBreakpointError is returned when trying to
// clear a breakpoint that does not exist.
type NoBreakpointError struct {
addr uint64
}
func (nbp NoBreakpointError) Error() string {
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
}

View File

@ -1,67 +0,0 @@
package proc
type AsmInstruction struct {
Loc Location
DestLoc *Location
Bytes []byte
Breakpoint bool
AtPC bool
Inst *ArchInst
}
type AssemblyFlavour int
const (
GNUFlavour = AssemblyFlavour(iota)
IntelFlavour
)
// Disassemble disassembles target memory between startPC and endPC
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) ([]AsmInstruction, error) {
if thread.dbp.exited {
return nil, &ProcessExitedError{}
}
mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC))
if err != nil {
return nil, err
}
r := make([]AsmInstruction, 0, len(mem)/15)
pc := startPC
var curpc uint64
var regs Registers
if currentGoroutine {
regs, _ = thread.Registers(false)
if regs != nil {
curpc = regs.PC()
}
}
for len(mem) > 0 {
bp, atbp := thread.dbp.Breakpoints[pc]
if atbp {
for i := range bp.OriginalData {
mem[i] = bp.OriginalData[i]
}
}
file, line, fn := thread.dbp.PCToLine(pc)
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
inst, err := asmDecode(mem, pc)
if err == nil {
atpc := currentGoroutine && (curpc == pc)
destloc := thread.resolveCallArg(inst, atpc, regs)
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
pc += uint64(inst.Size())
mem = mem[inst.Size():]
} else {
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
pc++
mem = mem[1:]
}
}
return r, nil
}

View File

@ -1,183 +0,0 @@
package proc
import (
"debug/gosym"
"encoding/binary"
"rsc.io/x86/x86asm"
)
var maxInstructionLength uint64 = 15
type ArchInst x86asm.Inst
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
inst, err := x86asm.Decode(mem, 64)
if err != nil {
return nil, err
}
patchPCRel(pc, &inst)
r := ArchInst(inst)
return &r, nil
}
func (inst *ArchInst) Size() int {
return inst.Len
}
// converts PC relative arguments to absolute addresses
func patchPCRel(pc uint64, inst *x86asm.Inst) {
for i := range inst.Args {
rel, isrel := inst.Args[i].(x86asm.Rel)
if isrel {
inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
}
}
return
}
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
if inst.Inst == nil {
return "?"
}
var text string
switch flavour {
case GNUFlavour:
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
case IntelFlavour:
fallthrough
default:
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
}
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
text += " " + inst.DestLoc.Fn.Name
}
return text
}
func (inst *AsmInstruction) IsCall() bool {
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
}
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
return nil
}
var pc uint64
var err error
switch arg := inst.Args[0].(type) {
case x86asm.Imm:
pc = uint64(arg)
case x86asm.Reg:
if !currentGoroutine || regs == nil {
return nil
}
pc, err = regs.Get(int(arg))
if err != nil {
return nil
}
case x86asm.Mem:
if !currentGoroutine || regs == nil {
return nil
}
if arg.Segment != 0 {
return nil
}
regs, err := thread.Registers(false)
if err != nil {
return nil
}
base, err1 := regs.Get(int(arg.Base))
index, err2 := regs.Get(int(arg.Index))
if err1 != nil || err2 != nil {
return nil
}
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
//TODO: should this always be 64 bits instead of inst.MemBytes?
pcbytes, err := thread.readMemory(addr, inst.MemBytes)
if err != nil {
return nil
}
pc = binary.LittleEndian.Uint64(pcbytes)
default:
return nil
}
file, line, fn := thread.dbp.PCToLine(pc)
if fn == nil {
return nil
}
return &Location{PC: pc, File: file, Line: line, Fn: fn}
}
type instrseq []x86asm.Op
// Possible stacksplit prologues are inserted by stacksplit in
// $GOROOT/src/cmd/internal/obj/x86/obj6.go.
// The stacksplit prologue will always begin with loading curg in CX, this
// instruction is added by load_g_cx in the same file and is either 1 or 2
// MOVs.
var prologues []instrseq
func init() {
var tinyStacksplit = instrseq{x86asm.CMP, x86asm.JBE}
var smallStacksplit = instrseq{x86asm.LEA, x86asm.CMP, x86asm.JBE}
var bigStacksplit = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JE, x86asm.LEA, x86asm.SUB, x86asm.CMP, x86asm.JBE}
var unixGetG = instrseq{x86asm.MOV}
var windowsGetG = instrseq{x86asm.MOV, x86asm.MOV}
prologues = make([]instrseq, 0, 2*3)
for _, getG := range []instrseq{unixGetG, windowsGetG} {
for _, stacksplit := range []instrseq{tinyStacksplit, smallStacksplit, bigStacksplit} {
prologue := make(instrseq, 0, len(getG)+len(stacksplit))
prologue = append(prologue, getG...)
prologue = append(prologue, stacksplit...)
prologues = append(prologues, prologue)
}
}
}
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
text, err := dbp.CurrentThread.Disassemble(fn.Entry, fn.End, false)
if err != nil {
return fn.Entry, err
}
if len(text) <= 0 {
return fn.Entry, nil
}
for _, prologue := range prologues {
if len(prologue) >= len(text) {
continue
}
if checkPrologue(text, prologue) {
r := &text[len(prologue)]
if sameline {
if r.Loc.Line != text[0].Loc.Line {
return fn.Entry, nil
}
}
return r.Loc.PC, nil
}
}
return fn.Entry, nil
}
func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
line := s[0].Loc.Line
for i, op := range prologuePattern {
if s[i].Inst.Op != op || s[i].Loc.Line != line {
return false
}
}
return true
}

View File

@ -1,9 +0,0 @@
// Package proc is a low-level package that provides methods to manipulate
// the process we are debugging.
//
// proc implements all core functionality including:
// * creating / attaching to a process
// * process manipulation (step, next, continue, halt)
// * methods to explore the memory of the process
//
package proc

File diff suppressed because it is too large Load Diff

View File

@ -1,283 +0,0 @@
#ifndef _exc_user_
#define _exc_user_
/* Module exc */
#include <string.h>
#include <mach/ndr.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/notify.h>
#include <mach/mach_types.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/port.h>
/* BEGIN VOUCHER CODE */
#ifndef KERNEL
#if defined(__has_include)
#if __has_include(<mach/mig_voucher_support.h>)
#ifndef USING_VOUCHERS
#define USING_VOUCHERS
#endif
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
#define __VOUCHER_FORWARD_TYPE_DECLS__
#ifdef __cplusplus
extern "C" {
#endif
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
#ifdef __cplusplus
}
#endif
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
#endif // __has_include(<mach/mach_voucher_types.h>)
#endif // __has_include
#endif // !KERNEL
/* END VOUCHER CODE */
#ifdef AUTOTEST
#ifndef FUNCTION_PTR_T
#define FUNCTION_PTR_T
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
typedef struct {
char *name;
function_ptr_t function;
} function_table_entry;
typedef function_table_entry *function_table_t;
#endif /* FUNCTION_PTR_T */
#endif /* AUTOTEST */
#ifndef exc_MSG_COUNT
#define exc_MSG_COUNT 3
#endif /* exc_MSG_COUNT */
#include <mach/std_types.h>
#include <mach/mig.h>
#include <mach/mig.h>
#include <mach/mach_types.h>
#ifdef __BeforeMigUserHeader
__BeforeMigUserHeader
#endif /* __BeforeMigUserHeader */
#include <sys/cdefs.h>
__BEGIN_DECLS
/* Routine exception_raise */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt
);
/* Routine exception_raise_state */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
/* Routine exception_raise_state_identity */
#ifdef mig_external
mig_external
#else
extern
#endif /* mig_external */
kern_return_t exception_raise_state_identity
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
);
__END_DECLS
/********************** Caution **************************/
/* The following data types should be used to calculate */
/* maximum message sizes only. The actual message may be */
/* smaller, and the position of the arguments within the */
/* message layout may vary from what is presented here. */
/* For example, if any of the arguments are variable- */
/* sized, and less than the maximum is sent, the data */
/* will be packed tight in the actual message to reduce */
/* the presence of holes. */
/********************** Caution **************************/
/* typedefs for all requests */
#ifndef __Request__exc_subsystem__defined
#define __Request__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
} __Request__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} __Request__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} __Request__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Request__exc_subsystem__defined */
/* union of all requests */
#ifndef __RequestUnion__exc_subsystem__defined
#define __RequestUnion__exc_subsystem__defined
union __RequestUnion__exc_subsystem {
__Request__exception_raise_t Request_exception_raise;
__Request__exception_raise_state_t Request_exception_raise_state;
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
/* typedefs for all replies */
#ifndef __Reply__exc_subsystem__defined
#define __Reply__exc_subsystem__defined
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply__exception_raise_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply__exception_raise_state_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply__exception_raise_state_identity_t;
#ifdef __MigPackStructs
#pragma pack()
#endif
#endif /* !__Reply__exc_subsystem__defined */
/* union of all replies */
#ifndef __ReplyUnion__exc_subsystem__defined
#define __ReplyUnion__exc_subsystem__defined
union __ReplyUnion__exc_subsystem {
__Reply__exception_raise_t Reply_exception_raise;
__Reply__exception_raise_state_t Reply_exception_raise_state;
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
};
#endif /* !__RequestUnion__exc_subsystem__defined */
#ifndef subsystem_to_name_map_exc
#define subsystem_to_name_map_exc \
{ "exception_raise", 2401 },\
{ "exception_raise_state", 2402 },\
{ "exception_raise_state_identity", 2403 }
#endif
#ifdef __AfterMigUserHeader
__AfterMigUserHeader
#endif /* __AfterMigUserHeader */
#endif /* _exc_user_ */

View File

@ -1,768 +0,0 @@
/*
* IDENTIFICATION:
* stub generated Sun Feb 22 20:54:31 2015
* with a MiG generated by bootstrap_cmds-91
* OPTIONS:
*/
#define __MIG_check__Reply__exc_subsystem__ 1
#include "exc.h"
#ifndef mig_internal
#define mig_internal static __inline__
#endif /* mig_internal */
#ifndef mig_external
#define mig_external
#endif /* mig_external */
#if !defined(__MigTypeCheck) && defined(TypeCheck)
#define __MigTypeCheck TypeCheck /* Legacy setting */
#endif /* !defined(__MigTypeCheck) */
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
#endif /* !defined(__MigKernelSpecificCode) */
#ifndef LimitCheck
#define LimitCheck 0
#endif /* LimitCheck */
#ifndef min
#define min(a,b) ( ((a) < (b))? (a): (b) )
#endif /* min */
#if !defined(_WALIGN_)
#define _WALIGN_(x) (((x) + 3) & ~3)
#endif /* !defined(_WALIGN_) */
#if !defined(_WALIGNSZ_)
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
#endif /* !defined(_WALIGNSZ_) */
#ifndef UseStaticTemplates
#define UseStaticTemplates 0
#endif /* UseStaticTemplates */
#ifndef __MachMsgErrorWithTimeout
#define __MachMsgErrorWithTimeout(_R_) { \
switch (_R_) { \
case MACH_SEND_INVALID_DATA: \
case MACH_SEND_INVALID_DEST: \
case MACH_SEND_INVALID_HEADER: \
mig_put_reply_port(InP->Head.msgh_reply_port); \
break; \
case MACH_SEND_TIMED_OUT: \
case MACH_RCV_TIMED_OUT: \
default: \
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
} \
}
#endif /* __MachMsgErrorWithTimeout */
#ifndef __MachMsgErrorWithoutTimeout
#define __MachMsgErrorWithoutTimeout(_R_) { \
switch (_R_) { \
case MACH_SEND_INVALID_DATA: \
case MACH_SEND_INVALID_DEST: \
case MACH_SEND_INVALID_HEADER: \
mig_put_reply_port(InP->Head.msgh_reply_port); \
break; \
default: \
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
} \
}
#endif /* __MachMsgErrorWithoutTimeout */
#ifndef __DeclareSendRpc
#define __DeclareSendRpc(_NUM_, _NAME_)
#endif /* __DeclareSendRpc */
#ifndef __BeforeSendRpc
#define __BeforeSendRpc(_NUM_, _NAME_)
#endif /* __BeforeSendRpc */
#ifndef __AfterSendRpc
#define __AfterSendRpc(_NUM_, _NAME_)
#endif /* __AfterSendRpc */
#ifndef __DeclareSendSimple
#define __DeclareSendSimple(_NUM_, _NAME_)
#endif /* __DeclareSendSimple */
#ifndef __BeforeSendSimple
#define __BeforeSendSimple(_NUM_, _NAME_)
#endif /* __BeforeSendSimple */
#ifndef __AfterSendSimple
#define __AfterSendSimple(_NUM_, _NAME_)
#endif /* __AfterSendSimple */
#define msgh_request_port msgh_remote_port
#define msgh_reply_port msgh_local_port
#if ( __MigTypeCheck )
#if __MIG_check__Reply__exc_subsystem__
#if !defined(__MIG_check__Reply__exception_raise_t__defined)
#define __MIG_check__Reply__exception_raise_t__defined
mig_internal kern_return_t __MIG_check__Reply__exception_raise_t(__Reply__exception_raise_t *Out0P)
{
typedef __Reply__exception_raise_t __Reply;
if (Out0P->Head.msgh_id != 2501) {
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
{ return MIG_SERVER_DIED; }
else
{ return MIG_REPLY_MISMATCH; }
}
#if __MigTypeCheck
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
{
return Out0P->RetCode;
}
}
#endif /* !defined(__MIG_check__Reply__exception_raise_t__defined) */
#endif /* __MIG_check__Reply__exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine exception_raise */
mig_external kern_return_t exception_raise
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt
)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
} Request;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
mach_msg_trailer_t trailer;
} Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} __Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
mach_msg_return_t msg_result;
unsigned int msgh_size;
#ifdef __MIG_check__Reply__exception_raise_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Reply__exception_raise_t__defined */
__DeclareSendRpc(2401, "exception_raise")
#if UseStaticTemplates
const static mach_msg_port_descriptor_t threadTemplate = {
/* name = */ MACH_PORT_NULL,
/* pad1 = */ 0,
/* pad2 = */ 0,
/* disp = */ 19,
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
#if UseStaticTemplates
const static mach_msg_port_descriptor_t taskTemplate = {
/* name = */ MACH_PORT_NULL,
/* pad1 = */ 0,
/* pad2 = */ 0,
/* disp = */ 19,
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
InP->msgh_body.msgh_descriptor_count = 2;
#if UseStaticTemplates
InP->thread = threadTemplate;
InP->thread.name = thread;
#else /* UseStaticTemplates */
InP->thread.name = thread;
InP->thread.disposition = 19;
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
#endif /* UseStaticTemplates */
#if UseStaticTemplates
InP->task = taskTemplate;
InP->task.name = task;
#else /* UseStaticTemplates */
InP->task.name = task;
InP->task.disposition = 19;
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
#endif /* UseStaticTemplates */
InP->NDR = NDR_record;
InP->exception = exception;
if (codeCnt > 2) {
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
InP->codeCnt = codeCnt;
msgh_size = (mach_msg_size_t)(sizeof(Request) - 8) + ((4 * codeCnt));
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
/* msgh_size passed as argument */
InP->Head.msgh_request_port = exception_port;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 2401;
/* BEGIN VOUCHER CODE */
#ifdef USING_VOUCHERS
if (voucher_mach_msg_set != NULL) {
voucher_mach_msg_set(&InP->Head);
}
#endif // USING_VOUCHERS
/* END VOUCHER CODE */
__BeforeSendRpc(2401, "exception_raise")
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
__AfterSendRpc(2401, "exception_raise")
if (msg_result != MACH_MSG_SUCCESS) {
__MachMsgErrorWithoutTimeout(msg_result);
{ return msg_result; }
}
#if defined(__MIG_check__Reply__exception_raise_t__defined)
check_result = __MIG_check__Reply__exception_raise_t((__Reply__exception_raise_t *)Out0P);
if (check_result != MACH_MSG_SUCCESS)
{ return check_result; }
#endif /* defined(__MIG_check__Reply__exception_raise_t__defined) */
return KERN_SUCCESS;
}
#if ( __MigTypeCheck )
#if __MIG_check__Reply__exc_subsystem__
#if !defined(__MIG_check__Reply__exception_raise_state_t__defined)
#define __MIG_check__Reply__exception_raise_state_t__defined
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_t(__Reply__exception_raise_state_t *Out0P)
{
typedef __Reply__exception_raise_state_t __Reply;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
if (Out0P->Head.msgh_id != 2502) {
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
{ return MIG_SERVER_DIED; }
else
{ return MIG_REPLY_MISMATCH; }
}
#if __MigTypeCheck
msgh_size = Out0P->Head.msgh_size;
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
Out0P->RetCode == KERN_SUCCESS)))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
if (Out0P->RetCode != KERN_SUCCESS) {
return ((mig_reply_error_t *)Out0P)->RetCode;
}
#if __MigTypeCheck
if ( Out0P->new_stateCnt > 224 )
return MIG_TYPE_ERROR;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Reply__exception_raise_state_t__defined) */
#endif /* __MIG_check__Reply__exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine exception_raise_state */
mig_external kern_return_t exception_raise_state
(
mach_port_t exception_port,
exception_type_t exception,
const exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
const thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} Request;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
mach_msg_trailer_t trailer;
} Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
mach_msg_return_t msg_result;
unsigned int msgh_size;
unsigned int msgh_size_delta;
#ifdef __MIG_check__Reply__exception_raise_state_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Reply__exception_raise_state_t__defined */
__DeclareSendRpc(2402, "exception_raise_state")
InP->NDR = NDR_record;
InP->exception = exception;
if (codeCnt > 2) {
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
InP->codeCnt = codeCnt;
msgh_size_delta = (4 * codeCnt);
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
InP->flavor = *flavor;
if (old_stateCnt > 224) {
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
InP->old_stateCnt = old_stateCnt;
msgh_size += (4 * old_stateCnt);
InP = &Mess.In;
InP->Head.msgh_bits =
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
/* msgh_size passed as argument */
InP->Head.msgh_request_port = exception_port;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 2402;
/* BEGIN VOUCHER CODE */
#ifdef USING_VOUCHERS
if (voucher_mach_msg_set != NULL) {
voucher_mach_msg_set(&InP->Head);
}
#endif // USING_VOUCHERS
/* END VOUCHER CODE */
__BeforeSendRpc(2402, "exception_raise_state")
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
__AfterSendRpc(2402, "exception_raise_state")
if (msg_result != MACH_MSG_SUCCESS) {
__MachMsgErrorWithoutTimeout(msg_result);
{ return msg_result; }
}
#if defined(__MIG_check__Reply__exception_raise_state_t__defined)
check_result = __MIG_check__Reply__exception_raise_state_t((__Reply__exception_raise_state_t *)Out0P);
if (check_result != MACH_MSG_SUCCESS)
{ return check_result; }
#endif /* defined(__MIG_check__Reply__exception_raise_state_t__defined) */
*flavor = Out0P->flavor;
if (Out0P->new_stateCnt > 224) {
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
*new_stateCnt = Out0P->new_stateCnt;
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
*new_stateCnt = Out0P->new_stateCnt;
return KERN_SUCCESS;
}
#if ( __MigTypeCheck )
#if __MIG_check__Reply__exc_subsystem__
#if !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
#define __MIG_check__Reply__exception_raise_state_identity_t__defined
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_identity_t(__Reply__exception_raise_state_identity_t *Out0P)
{
typedef __Reply__exception_raise_state_identity_t __Reply;
#if __MigTypeCheck
unsigned int msgh_size;
#endif /* __MigTypeCheck */
if (Out0P->Head.msgh_id != 2503) {
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
{ return MIG_SERVER_DIED; }
else
{ return MIG_REPLY_MISMATCH; }
}
#if __MigTypeCheck
msgh_size = Out0P->Head.msgh_size;
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
Out0P->RetCode == KERN_SUCCESS)))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
if (Out0P->RetCode != KERN_SUCCESS) {
return ((mig_reply_error_t *)Out0P)->RetCode;
}
#if __MigTypeCheck
if ( Out0P->new_stateCnt > 224 )
return MIG_TYPE_ERROR;
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
{ return MIG_TYPE_ERROR ; }
#endif /* __MigTypeCheck */
return MACH_MSG_SUCCESS;
}
#endif /* !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
#endif /* __MIG_check__Reply__exc_subsystem__ */
#endif /* ( __MigTypeCheck ) */
/* Routine exception_raise_state_identity */
mig_external kern_return_t exception_raise_state_identity
(
mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code,
mach_msg_type_number_t codeCnt,
int *flavor,
thread_state_t old_state,
mach_msg_type_number_t old_stateCnt,
thread_state_t new_state,
mach_msg_type_number_t *new_stateCnt
)
{
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
integer_t code[2];
int flavor;
mach_msg_type_number_t old_stateCnt;
natural_t old_state[224];
} Request;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
mach_msg_trailer_t trailer;
} Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
#ifdef __MigPackStructs
#pragma pack(4)
#endif
typedef struct {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
int flavor;
mach_msg_type_number_t new_stateCnt;
natural_t new_state[224];
} __Reply;
#ifdef __MigPackStructs
#pragma pack()
#endif
/*
* typedef struct {
* mach_msg_header_t Head;
* NDR_record_t NDR;
* kern_return_t RetCode;
* } mig_reply_error_t;
*/
union {
Request In;
Reply Out;
} Mess;
Request *InP = &Mess.In;
Reply *Out0P = &Mess.Out;
mach_msg_return_t msg_result;
unsigned int msgh_size;
unsigned int msgh_size_delta;
#ifdef __MIG_check__Reply__exception_raise_state_identity_t__defined
kern_return_t check_result;
#endif /* __MIG_check__Reply__exception_raise_state_identity_t__defined */
__DeclareSendRpc(2403, "exception_raise_state_identity")
#if UseStaticTemplates
const static mach_msg_port_descriptor_t threadTemplate = {
/* name = */ MACH_PORT_NULL,
/* pad1 = */ 0,
/* pad2 = */ 0,
/* disp = */ 19,
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
#if UseStaticTemplates
const static mach_msg_port_descriptor_t taskTemplate = {
/* name = */ MACH_PORT_NULL,
/* pad1 = */ 0,
/* pad2 = */ 0,
/* disp = */ 19,
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
};
#endif /* UseStaticTemplates */
InP->msgh_body.msgh_descriptor_count = 2;
#if UseStaticTemplates
InP->thread = threadTemplate;
InP->thread.name = thread;
#else /* UseStaticTemplates */
InP->thread.name = thread;
InP->thread.disposition = 19;
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
#endif /* UseStaticTemplates */
#if UseStaticTemplates
InP->task = taskTemplate;
InP->task.name = task;
#else /* UseStaticTemplates */
InP->task.name = task;
InP->task.disposition = 19;
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
#endif /* UseStaticTemplates */
InP->NDR = NDR_record;
InP->exception = exception;
if (codeCnt > 2) {
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
InP->codeCnt = codeCnt;
msgh_size_delta = (4 * codeCnt);
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
InP->flavor = *flavor;
if (old_stateCnt > 224) {
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
InP->old_stateCnt = old_stateCnt;
msgh_size += (4 * old_stateCnt);
InP = &Mess.In;
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
/* msgh_size passed as argument */
InP->Head.msgh_request_port = exception_port;
InP->Head.msgh_reply_port = mig_get_reply_port();
InP->Head.msgh_id = 2403;
/* BEGIN VOUCHER CODE */
#ifdef USING_VOUCHERS
if (voucher_mach_msg_set != NULL) {
voucher_mach_msg_set(&InP->Head);
}
#endif // USING_VOUCHERS
/* END VOUCHER CODE */
__BeforeSendRpc(2403, "exception_raise_state_identity")
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
__AfterSendRpc(2403, "exception_raise_state_identity")
if (msg_result != MACH_MSG_SUCCESS) {
__MachMsgErrorWithoutTimeout(msg_result);
{ return msg_result; }
}
#if defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
check_result = __MIG_check__Reply__exception_raise_state_identity_t((__Reply__exception_raise_state_identity_t *)Out0P);
if (check_result != MACH_MSG_SUCCESS)
{ return check_result; }
#endif /* defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
*flavor = Out0P->flavor;
if (Out0P->new_stateCnt > 224) {
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
*new_stateCnt = Out0P->new_stateCnt;
{ return MIG_ARRAY_TOO_LARGE; }
}
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
*new_stateCnt = Out0P->new_stateCnt;
return KERN_SUCCESS;
}

View File

@ -1,112 +0,0 @@
#include "exec_darwin.h"
#include "stdio.h"
extern char** environ;
int
close_exec_pipe(int fd[2]) {
if (pipe(fd) < 0) return -1;
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
return 0;
}
int
fork_exec(char *argv0, char **argv, int size,
char *wd,
task_t *task,
mach_port_t *port_set,
mach_port_t *exception_port,
mach_port_t *notification_port)
{
// Since we're using mach exceptions instead of signals,
// we need to coordinate between parent and child via pipes
// to ensure that the parent has set the exception ports on
// the child task before it execs.
int fd[2];
if (close_exec_pipe(fd) < 0) return -1;
// Create another pipe to signal the parent on exec.
int efd[2];
if (close_exec_pipe(efd) < 0) return -1;
kern_return_t kret;
pid_t pid = fork();
if (pid > 0) {
// In parent.
close(fd[0]);
close(efd[1]);
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
if (kret != KERN_SUCCESS) return -1;
char msg = 'c';
write(fd[1], &msg, 1);
close(fd[1]);
char w;
size_t n = read(efd[0], &w, 1);
close(efd[0]);
if (n != 0) {
// Child died, reap it.
waitpid(pid, NULL, 0);
return -1;
}
return pid;
}
// Fork succeeded, we are in the child.
int pret, cret;
char sig;
close(fd[1]);
read(fd[0], &sig, 1);
close(fd[0]);
// Create a new process group.
if (setpgid(0, 0) < 0) {
perror("setpgid");
exit(1);
}
// Set errno to zero before a call to ptrace.
// It is documented that ptrace can return -1 even
// for successful calls.
errno = 0;
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
// Change working directory if wd is not empty.
if (wd && wd[0]) {
errno = 0;
cret = chdir(wd);
if (cret != 0 && errno != 0) {
char *error_msg;
asprintf(&error_msg, "%s '%s'", "chdir", wd);
perror(error_msg);
exit(1);
}
}
errno = 0;
pret = ptrace(PT_SIGEXC, 0, 0, 0);
if (pret != 0 && errno != 0) {
perror("ptrace");
exit(1);
}
sleep(1);
// Create the child process.
execve(argv0, argv, environ);
// We should never reach here, but if we did something went wrong.
// Write a message to parent to alert that exec failed.
char msg = 'd';
write(efd[1], &msg, 1);
close(efd[1]);
exit(1);
}

View File

@ -1,10 +0,0 @@
#include "proc_darwin.h"
#include <unistd.h>
#include <sys/ptrace.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
int
fork_exec(char *, char **, int, char *, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);

Some files were not shown because too many files have changed in this diff Show More