This repository has been archived by the owner on Dec 5, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement skeleton of a file generator. writing file is TBD.
- Loading branch information
Showing
4 changed files
with
381 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright 2015 Huan Du. All rights reserved. | ||
// Use of this source code is governed by a MIT | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"go/ast" | ||
"go/parser" | ||
"go/token" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
type visitor func(node ast.Node) bool | ||
|
||
func (v visitor) Visit(node ast.Node) ast.Visitor { | ||
if v(node) { | ||
return v | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// Generator stores progress of package parser. | ||
type Generator struct { | ||
parsedPkgs map[string]bool | ||
pkgs []string | ||
context *Context | ||
} | ||
|
||
func (g *Generator) Parse() { | ||
for i := 0; i < len(g.pkgs); i++ { | ||
g.parsePkg(g.pkgs[i]) | ||
} | ||
} | ||
|
||
func (g *Generator) parsePkg(pkg string) { | ||
pkgPath := filepath.Join(g.context.GoPackage, pkg) | ||
fset := token.NewFileSet() | ||
pkgs, err := parser.ParseDir(fset, pkgPath, func(info os.FileInfo) bool { | ||
// Filter out all test files. | ||
if strings.HasSuffix(info.Name(), "_test.go") { | ||
return false | ||
} | ||
|
||
return true | ||
}, parser.ParseComments) | ||
|
||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
if _, ok := pkgs["main"]; ok { | ||
delete(pkgs, "main") | ||
} | ||
|
||
if len(pkgs) != 1 { | ||
keys := []string{} | ||
|
||
for k, _ := range pkgs { | ||
keys = append(keys, k) | ||
} | ||
|
||
panic(fmt.Errorf("there must be only one package name in a package. [pkgs:%v]", strings.Join(keys, ", "))) | ||
} | ||
|
||
//goDir := "go" + g.context.Version.Join("_") | ||
//output := filepath.Join(g.context.Output, goDir, pkg) | ||
//importPath := filepath.Join(g.context.ImportPath, goDir, pkg) | ||
|
||
for _, p := range pkgs { | ||
//name := p.Name | ||
files := p.Files | ||
|
||
for _, f := range files { | ||
decls := f.Decls | ||
neededDecls := []ast.Decl{} | ||
|
||
for _, decl := range decls { | ||
// Only type decl is needed. | ||
if genDecl, ok := decl.(*ast.GenDecl); ok && genDecl.Tok == token.TYPE { | ||
neededDecls = append(neededDecls, decl) | ||
specs := genDecl.Specs | ||
|
||
for _, spec := range specs { | ||
ast.Walk(visitor(func(node ast.Node) bool { | ||
if node == nil { | ||
return false | ||
} | ||
|
||
switch n := node.(type) { | ||
case *ast.TypeSpec: | ||
typeName := n.Name.Name | ||
logTracef("Find type. [type:%v]", typeName) | ||
|
||
case *ast.SelectorExpr: | ||
pkgName := n.X.(*ast.Ident).Name | ||
typeName := n.Sel.Name | ||
|
||
if pkgName == "C" { | ||
break | ||
} | ||
|
||
logTracef("Find type. [package-name:%v] [type:%v]", pkgName, typeName) | ||
} | ||
|
||
return true | ||
}), spec) | ||
} | ||
} | ||
} | ||
|
||
f.Decls = neededDecls | ||
} | ||
} | ||
} | ||
|
||
// Generate hacked files for packages. | ||
// Basically, it extracts all types and generates hacked go files. | ||
// | ||
// Panic if it encounters any error. | ||
func GenerateHackedFiles(context *Context, pkgs ...string) { | ||
generator := &Generator{ | ||
parsedPkgs: make(map[string]bool), | ||
pkgs: pkgs, | ||
context: context, | ||
} | ||
generator.Parse() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
// Copyright 2015 Huan Du. All rights reserved. | ||
// Use of this source code is governed by a MIT | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
) | ||
|
||
func logFatalf(format string, args ...interface{}) { | ||
err := fmt.Errorf(format, args...) | ||
panic(err) | ||
} | ||
|
||
func logErrorf(format string, args ...interface{}) { | ||
fmt.Fprintf(os.Stderr, "ERROR: "+format+"\n", args...) | ||
} | ||
|
||
func logTracef(format string, args ...interface{}) { | ||
fmt.Printf("TRACE: "+format+"\n", args...) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright 2015 Huan Du. All rights reserved. | ||
// Use of this source code is governed by a MIT | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"bytes" | ||
"flag" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
) | ||
|
||
var ( | ||
flagGoSrc string | ||
flagOutput string | ||
flagImportPath string | ||
|
||
reImportPath = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*(/[a-zA-Z0-9_]+)*$`) | ||
) | ||
|
||
type Context struct { | ||
GoSrc string // Path to go src root directory. | ||
GoPackage string // Path to package src in go src directory. | ||
Version Version // Version number. | ||
Output string // Path to output directory. | ||
ImportPath string // The prefix of import path for output directory. | ||
} | ||
|
||
func init() { | ||
flag.StringVar(&flagGoSrc, "go-src", "", "Path to go src directory.") | ||
flag.StringVar(&flagOutput, "output", "", "Output path. Default is current directory.") | ||
flag.StringVar(&flagImportPath, "import-path", "", "The prefix of import path for output directory.") | ||
} | ||
|
||
func validateGoSrc(context *Context) { | ||
if flagGoSrc == "" { | ||
logFatalf("Flag -go-src must be set.") | ||
} | ||
|
||
src, err := filepath.Abs(flagGoSrc) | ||
|
||
if err != nil { | ||
logFatalf("Fail to get absolute path of go src. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
info, err := os.Stat(src) | ||
|
||
if err != nil { | ||
logFatalf("Value of flag -go-src must be a valid directory name. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
if !info.Mode().IsDir() { | ||
logFatalf("Value of flag -go-src must be a directory. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
srcsrc := filepath.Join(src, "src") | ||
info, err = os.Stat(srcsrc) | ||
|
||
if err != nil { | ||
logFatalf("Fail to find `src` in go src directory. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
if !info.Mode().IsDir() { | ||
logFatalf("The `src` in go src must be a directory. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
versionPath := filepath.Join(src, "VERSION") | ||
versionFile, err := os.Open(versionPath) | ||
|
||
if err != nil { | ||
logFatalf("Fail to read VERSION in go src directory. Are you sure -go-src points to a valid go src directory? [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
defer versionFile.Close() | ||
versionBuf := &bytes.Buffer{} | ||
versionBuf.ReadFrom(versionFile) | ||
|
||
if versionBuf.Len() == 0 { | ||
logFatalf("VERSION in go src is empty. [go-src:%v]", flagGoSrc) | ||
} | ||
|
||
version, err := ParseVersion(versionBuf.String()) | ||
|
||
if err != nil { | ||
logFatalf("VERSION file content is not valid. [go-src:%v] [err:%v] [content:%v]", flagGoSrc, err, versionBuf.String()) | ||
} | ||
|
||
context.GoSrc = src | ||
context.GoPackage = srcsrc | ||
context.Version = version | ||
} | ||
|
||
func validateOutput(context *Context) { | ||
var err error | ||
|
||
if flagOutput == "" { | ||
flagOutput, err = os.Getwd() | ||
|
||
if err != nil { | ||
logFatalf("Value of flag -output is empty and current directory is not available.") | ||
} | ||
} | ||
|
||
output, err := filepath.Abs(flagOutput) | ||
|
||
if err != nil { | ||
logFatalf("Fail to get absolute path of output. [output:%v]", flagOutput) | ||
} | ||
|
||
info, err := os.Stat(output) | ||
|
||
if err != nil { | ||
logFatalf("Value of flag -output must be a valid directory name. [output:%v]", flagOutput) | ||
} | ||
|
||
if !info.Mode().IsDir() { | ||
logFatalf("Value of flag -output must be a directory. [output:%v]", flagOutput) | ||
} | ||
|
||
context.Output = output | ||
} | ||
|
||
func validateImportPath(context *Context) { | ||
if flagImportPath == "" { | ||
logFatalf("Flag -import-path must be set.") | ||
} | ||
|
||
if !reImportPath.MatchString(flagImportPath) { | ||
logFatalf("Value of flag -import-path must be a valid import path. [import-path:%v]", flagImportPath) | ||
} | ||
|
||
context.ImportPath = flagImportPath | ||
} | ||
|
||
func main() { | ||
defer func() { | ||
if err := recover(); err != nil { | ||
logErrorf("%v", err) | ||
os.Exit(1) | ||
} | ||
}() | ||
|
||
flag.Parse() | ||
|
||
context := &Context{} | ||
validateGoSrc(context) | ||
validateOutput(context) | ||
validateImportPath(context) | ||
|
||
GenerateHackedFiles(context, "runtime") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
// Copyright 2015 Huan Du. All rights reserved. | ||
// Use of this source code is governed by a MIT | ||
// license that can be found in the LICENSE file. | ||
|
||
package main | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
var reVersion = regexp.MustCompile(`^go(\d+)(\.(\d+))+`) | ||
|
||
type Version []string | ||
|
||
// Parse version string like "go1.6.3". | ||
func ParseVersion(version string) (Version, error) { | ||
matches := reVersion.FindStringSubmatch(version) | ||
|
||
if matches == nil { | ||
return nil, fmt.Errorf("invalid version format") | ||
} | ||
|
||
v := Version{} | ||
|
||
for i := 1; i < len(matches); i += 2 { | ||
v = append(v, matches[i]) | ||
} | ||
|
||
return v, nil | ||
} | ||
|
||
// Return a version string like "go1.6.3". | ||
func (v Version) String() string { | ||
return "go" + v.Join(".") | ||
} | ||
|
||
// Join version numbers with sep. | ||
func (v Version) Join(sep string) string { | ||
return strings.Join(([]string)(v), sep) | ||
} | ||
|
||
// Compare two versions. | ||
// Return 1 if a > b. | ||
// Return 0 if two versions equal. | ||
// Return -1 if a < b. | ||
func CompareVersions(a, b Version) int { | ||
if a == nil && b != nil { | ||
return -1 | ||
} | ||
|
||
if a != nil && b == nil { | ||
return 1 | ||
} | ||
|
||
la := len(a) | ||
lb := len(b) | ||
|
||
for i := 0; i < la && i < lb; i++ { | ||
if c := strings.Compare(a[i], b[i]); c != 0 { | ||
return c | ||
} | ||
} | ||
|
||
if la > lb { | ||
return 1 | ||
} else if la < lb { | ||
return -1 | ||
} else { | ||
return 0 | ||
} | ||
} |