2014-05-24 22:44:28 -05:00
|
|
|
// 24 may 2014
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2014-05-25 00:13:16 -05:00
|
|
|
"fmt"
|
2014-05-24 22:44:28 -05:00
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
"go/token"
|
|
|
|
"go/ast"
|
|
|
|
"go/parser"
|
2014-05-25 00:28:59 -05:00
|
|
|
"sort"
|
2014-05-25 02:13:49 -05:00
|
|
|
"io/ioutil"
|
|
|
|
"path/filepath"
|
|
|
|
"os/exec"
|
2014-05-24 22:44:28 -05:00
|
|
|
)
|
|
|
|
|
2014-05-25 00:13:16 -05:00
|
|
|
func getPackage(path string) (pkg *ast.Package) {
|
|
|
|
fileset := token.NewFileSet() // parser.ParseDir() actually writes to this; not sure why it doesn't return one instead
|
2014-05-24 22:44:28 -05:00
|
|
|
filter := func(i os.FileInfo) bool {
|
|
|
|
return strings.HasSuffix(i.Name(), "_windows.go")
|
|
|
|
}
|
2014-05-25 00:13:16 -05:00
|
|
|
pkgs, err := parser.ParseDir(fileset, path, filter, parser.AllErrors)
|
2014-05-24 22:44:28 -05:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
if len(pkgs) != 1 {
|
|
|
|
panic("more than one package found")
|
|
|
|
}
|
|
|
|
for k, _ := range pkgs { // get the sole key
|
|
|
|
pkg = pkgs[k]
|
|
|
|
}
|
2014-05-25 00:13:16 -05:00
|
|
|
return pkg
|
2014-05-24 23:31:40 -05:00
|
|
|
}
|
2014-05-24 22:44:28 -05:00
|
|
|
|
2014-05-24 23:31:40 -05:00
|
|
|
type walker struct {
|
|
|
|
desired func(string) bool
|
|
|
|
}
|
|
|
|
|
2014-05-24 23:40:22 -05:00
|
|
|
var known = map[string]string{}
|
|
|
|
var unknown = map[string]struct{}{}
|
|
|
|
|
2014-05-24 23:31:40 -05:00
|
|
|
func (w *walker) Visit(node ast.Node) ast.Visitor {
|
|
|
|
if n, ok := node.(*ast.Ident); ok {
|
|
|
|
if w.desired(n.Name) {
|
|
|
|
if n.Obj != nil {
|
2014-05-24 23:40:22 -05:00
|
|
|
delete(unknown, n.Name)
|
|
|
|
kind := n.Obj.Kind.String()
|
|
|
|
if known[n.Name] != "" && known[n.Name] != kind {
|
|
|
|
panic(n.Name + "(" + kind + ") already known to be a " + known[n.Name])
|
|
|
|
}
|
|
|
|
known[n.Name] = kind
|
|
|
|
} else if _, ok := known[n.Name]; !ok { // only if not known
|
|
|
|
unknown[n.Name] = struct{}{}
|
2014-05-24 22:44:28 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-24 23:31:40 -05:00
|
|
|
return w
|
|
|
|
}
|
|
|
|
|
2014-05-25 00:13:16 -05:00
|
|
|
func gatherNames(pkg *ast.Package) {
|
2014-05-24 23:31:40 -05:00
|
|
|
desired := func(name string) bool {
|
|
|
|
if strings.HasPrefix(name, "_") && len(name) > 1 {
|
|
|
|
return !strings.ContainsAny(name,
|
|
|
|
"abcdefghijklmnopqrstuvwxyz")
|
2014-05-24 22:44:28 -05:00
|
|
|
}
|
2014-05-24 23:31:40 -05:00
|
|
|
return false
|
2014-05-24 22:44:28 -05:00
|
|
|
}
|
|
|
|
for _, f := range pkg.Files {
|
2014-05-24 23:31:40 -05:00
|
|
|
for _, d := range f.Decls {
|
|
|
|
ast.Walk(&walker{desired}, d)
|
|
|
|
}
|
2014-05-24 22:44:28 -05:00
|
|
|
}
|
|
|
|
}
|
2014-05-25 00:13:16 -05:00
|
|
|
|
2014-05-25 00:28:59 -05:00
|
|
|
func preamble(pkg string) string {
|
|
|
|
return "// autogenerated by windowsconstgen; do not edit\n" +
|
|
|
|
"package " + pkg + "\n"
|
|
|
|
}
|
|
|
|
|
2014-05-25 00:13:16 -05:00
|
|
|
func main() {
|
2014-05-25 02:13:49 -05:00
|
|
|
if len(os.Args) != 3 {
|
|
|
|
panic("usage: " + os.Args[0] + " path goarch")
|
2014-05-25 00:13:16 -05:00
|
|
|
}
|
2014-05-25 02:13:49 -05:00
|
|
|
pkgpath := os.Args[1]
|
|
|
|
targetarch := os.Args[2]
|
2014-05-25 00:13:16 -05:00
|
|
|
|
2014-05-25 02:13:49 -05:00
|
|
|
pkg := getPackage(pkgpath)
|
2014-05-25 00:13:16 -05:00
|
|
|
gatherNames(pkg)
|
|
|
|
|
|
|
|
if len(unknown) > 0 {
|
|
|
|
s := "error: the following are still unknown!"
|
|
|
|
for k, _ := range unknown {
|
|
|
|
s += "\n" + k
|
|
|
|
}
|
|
|
|
panic(s)
|
|
|
|
}
|
|
|
|
|
2014-05-25 00:28:59 -05:00
|
|
|
// keep sorted for git
|
|
|
|
consts := make([]string, 0, len(known))
|
2014-05-25 00:13:16 -05:00
|
|
|
for ident, kind := range known {
|
2014-05-25 00:28:59 -05:00
|
|
|
if kind == "const" || kind == "var" {
|
|
|
|
consts = append(consts, ident)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sort.Strings(consts)
|
|
|
|
|
2014-05-25 02:15:49 -05:00
|
|
|
// thanks to james4k in irc.freenode.net/#go-nuts
|
2014-05-25 02:13:49 -05:00
|
|
|
tmpdir, err := ioutil.TempDir("", "windowsconstgen")
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
genoutname := filepath.Join(tmpdir, "gen.go")
|
|
|
|
f, err := os.Create(genoutname)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(f, "%s" +
|
2014-05-25 00:28:59 -05:00
|
|
|
"import \"fmt\"\n" +
|
|
|
|
"// #include <windows.h>\n" +
|
2014-05-25 02:13:49 -05:00
|
|
|
"// #include <commctrl.h>\n" +
|
2014-05-25 00:28:59 -05:00
|
|
|
"import \"C\"\n" +
|
|
|
|
"func main() {\n" +
|
|
|
|
" fmt.Println(%q)\n",
|
|
|
|
preamble("main"), preamble("ui"))
|
|
|
|
for _, ident := range consts {
|
2014-05-25 02:13:49 -05:00
|
|
|
fmt.Fprintf(f, " fmt.Println(\"const %s =\", C.%s)\n", ident, ident[1:])
|
2014-05-25 00:13:16 -05:00
|
|
|
}
|
2014-05-25 02:13:49 -05:00
|
|
|
fmt.Fprintf(f, "}\n")
|
|
|
|
f.Close()
|
|
|
|
|
|
|
|
cmd := exec.Command("go", "run", genoutname)
|
|
|
|
f, err = os.Create(filepath.Join(pkgpath, "zconstants_windows_" + targetarch + ".go"))
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
cmd.Stdout = f
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
cmd.Env = append(cmd.Env, os.Environ()...) // otherwise $PATH doesn't get carried over and things mysteriously fail
|
|
|
|
cmd.Env = append(cmd.Env,
|
|
|
|
"GOOS=windows",
|
|
|
|
"GOARCH=" + targetarch,
|
|
|
|
"CGO_ENABLED=1") // needed as it's not set by default in cross-compiles
|
|
|
|
err = cmd.Run()
|
|
|
|
if err != nil {
|
|
|
|
// TODO find a way to get the exit code
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO remove the temporary directory
|
2014-05-25 00:13:16 -05:00
|
|
|
}
|