|
| 1 | +// Copyright 2018 PingCAP, Inc. |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// See the License for the specific language governing permissions and |
| 12 | +// limitations under the License. |
| 13 | + |
| 14 | +package main |
| 15 | + |
| 16 | +import ( |
| 17 | + "context" |
| 18 | + "encoding/json" |
| 19 | + "flag" |
| 20 | + "fmt" |
| 21 | + "log" |
| 22 | + "os" |
| 23 | + "os/exec" |
| 24 | + "path" |
| 25 | + "path/filepath" |
| 26 | + "text/template" |
| 27 | + "time" |
| 28 | + |
| 29 | + "github.com/BurntSushi/toml" |
| 30 | +) |
| 31 | + |
| 32 | +var ( |
| 33 | + pkgDir string |
| 34 | + outDir string |
| 35 | +) |
| 36 | + |
| 37 | +const codeTemplate = ` |
| 38 | +package main |
| 39 | +
|
| 40 | +import ( |
| 41 | + "github.com/pingcap/tidb/plugin" |
| 42 | + "github.com/pingcap/tidb/sessionctx/variable" |
| 43 | +) |
| 44 | +
|
| 45 | +func PluginManifest() *plugin.Manifest { |
| 46 | + return plugin.ExportManifest(&plugin.{{.kind}}Manifest{ |
| 47 | + Manifest: plugin.Manifest{ |
| 48 | + Kind: plugin.{{.kind}}, |
| 49 | + Name: "{{.name}}", |
| 50 | + Description: "{{.description}}", |
| 51 | + Version: {{.version}}, |
| 52 | + RequireVersion: map[string]uint16{}, |
| 53 | + License: "{{.license}}", |
| 54 | + BuildTime: "{{.buildTime}}", |
| 55 | + SysVars: map[string]*variable.SysVar{ |
| 56 | + {{range .sysVars}} |
| 57 | + "{{.name}}": { |
| 58 | + Scope: variable.Scope{{.scope}}, |
| 59 | + Name: "{{.name}}", |
| 60 | + Value: "{{.value}}", |
| 61 | + }, |
| 62 | + {{end}} |
| 63 | + }, |
| 64 | + Validate: {{.validate}}, |
| 65 | + OnInit: {{.onInit}}, |
| 66 | + OnShutdown: {{.onShutdown}}, |
| 67 | + }, |
| 68 | + {{range .export}} |
| 69 | + {{.extPoint}}: {{.impl}}, |
| 70 | + {{end}} |
| 71 | + }) |
| 72 | +} |
| 73 | +` |
| 74 | + |
| 75 | +func init() { |
| 76 | + flag.StringVar(&pkgDir, "pkg-dir", "", "plugin package folder path") |
| 77 | + flag.StringVar(&outDir, "out-dir", "", "plugin packaged folder path") |
| 78 | + flag.Usage = usage |
| 79 | +} |
| 80 | + |
| 81 | +func usage() { |
| 82 | + log.Printf("Usage: %s --pkg-dir [plugin source pkg folder] --outDir-dir [outDir-dir]\n", path.Base(os.Args[0])) |
| 83 | + flag.PrintDefaults() |
| 84 | + os.Exit(1) |
| 85 | +} |
| 86 | + |
| 87 | +func main() { |
| 88 | + flag.Parse() |
| 89 | + if pkgDir == "" || outDir == "" { |
| 90 | + flag.Usage() |
| 91 | + } |
| 92 | + var manifest map[string]interface{} |
| 93 | + _, err := toml.DecodeFile(filepath.Join(pkgDir, "manifest.toml"), &manifest) |
| 94 | + if err != nil { |
| 95 | + log.Printf("read pkg %s's manifest failure, %+v\n", pkgDir, err) |
| 96 | + os.Exit(1) |
| 97 | + } |
| 98 | + manifest["buildTime"] = time.Now().String() |
| 99 | + |
| 100 | + pluginName := manifest["name"].(string) |
| 101 | + if pluginName != filepath.Base(pkgDir) { |
| 102 | + log.Printf("plugin package must be same with plugin name in manifest file\n") |
| 103 | + os.Exit(1) |
| 104 | + } |
| 105 | + |
| 106 | + version := manifest["version"].(string) |
| 107 | + tmpl, err := template.New("gen-plugin").Parse(codeTemplate) |
| 108 | + if err != nil { |
| 109 | + log.Printf("generate code failure during parse template, %+v\n", err) |
| 110 | + os.Exit(1) |
| 111 | + } |
| 112 | + |
| 113 | + genFileName := filepath.Join(pkgDir, filepath.Base(pkgDir)+".gen.go") |
| 114 | + genFile, err := os.OpenFile(genFileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) |
| 115 | + if err != nil { |
| 116 | + log.Printf("generate code failure during prepare output file, %+v\n", err) |
| 117 | + os.Exit(1) |
| 118 | + } |
| 119 | + defer func() { |
| 120 | + err1 := os.Remove(genFileName) |
| 121 | + if err1 != nil { |
| 122 | + log.Printf("remove tmp file %s failure, please clean up manually at %v", genFileName, err1) |
| 123 | + } |
| 124 | + }() |
| 125 | + |
| 126 | + err = tmpl.Execute(genFile, manifest) |
| 127 | + if err != nil { |
| 128 | + log.Printf("generate code failure during generating code, %+v\n", err) |
| 129 | + os.Exit(1) |
| 130 | + } |
| 131 | + |
| 132 | + outputFile := filepath.Join(outDir, pluginName+"-"+version+".so") |
| 133 | + pluginPath := `-pluginpath=` + pluginName + "-" + version |
| 134 | + ctx := context.Background() |
| 135 | + buildCmd := exec.CommandContext(ctx, "go", "build", |
| 136 | + "-ldflags", pluginPath, |
| 137 | + "-buildmode=plugin", |
| 138 | + "-o", outputFile, pkgDir) |
| 139 | + buildCmd.Stderr = os.Stderr |
| 140 | + buildCmd.Stdout = os.Stdout |
| 141 | + buildCmd.Env = append(os.Environ(), "GO111MODULE=on") |
| 142 | + err = buildCmd.Run() |
| 143 | + if err != nil { |
| 144 | + log.Printf("compile plugin source code failure, %+v\n", err) |
| 145 | + os.Exit(1) |
| 146 | + } |
| 147 | + fmt.Printf(`Package "%s" as plugin "%s" success.`+"\nManifest:\n", pkgDir, outputFile) |
| 148 | + encoder := json.NewEncoder(os.Stdout) |
| 149 | + encoder.SetIndent(" ", "\t") |
| 150 | + err = encoder.Encode(manifest) |
| 151 | + if err != nil { |
| 152 | + log.Printf("print manifest detail failure, err: %v", err) |
| 153 | + } |
| 154 | +} |
0 commit comments