chromedp/cmd/chromedp-gen/main.go

256 lines
5.5 KiB
Go

// chromedp-gen is a tool to generate the low-level Chrome Debugging Protocol
// implementation types used by chromedp, based off Chrome's protocol.json.
//
// Please see README.md for more information on using this tool.
package main
//go:generate qtc -dir templates -ext qtpl
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"sort"
"sync"
"github.com/knq/chromedp/cmd/chromedp-gen/fixup"
"github.com/knq/chromedp/cmd/chromedp-gen/gen"
"github.com/knq/chromedp/cmd/chromedp-gen/internal"
)
func main() {
var err error
// parse flags
err = internal.FlagSet.Parse(os.Args[1:])
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// load protocol data
buf, err := ioutil.ReadFile(*internal.FlagFile)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// unmarshal protocol info
var protoInfo internal.ProtocolInfo
err = json.Unmarshal(buf, &protoInfo)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// remove existing directory
if !*internal.FlagNoRemove {
err = os.RemoveAll(out())
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
// determine what to process
pkgs := []string{""}
var processed []*internal.Domain
for _, d := range protoInfo.Domains {
// skip if not processing
if (!*internal.FlagDep && d.Deprecated.Bool()) || (!*internal.FlagExp && d.Experimental.Bool()) {
// extra info
var extra []string
if d.Deprecated.Bool() {
extra = append(extra, "deprecated")
}
if d.Experimental.Bool() {
extra = append(extra, "experimental")
}
log.Printf("skipping domain %s (%s) %v", d, d.PackageName(), extra)
continue
}
// will process
pkgs = append(pkgs, d.PackageName())
processed = append(processed, d)
// cleanup types, events, commands
cleanup(d)
}
// fixup
fixup.FixDomains(processed)
// generate
files := gen.GenerateDomains(processed)
// write
err = write(files)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// goimports
err = goimports(files)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
// easyjson
err = easyjson(pkgs)
if err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
log.Printf("done.")
}
// cleanupTypes removes deprecated types.
func cleanupTypes(n string, dtyp string, types []*internal.Type) []*internal.Type {
var ret []*internal.Type
for _, t := range types {
typ := dtyp + "." + t.IDorName()
if !*internal.FlagDep && t.Deprecated.Bool() {
log.Printf("skipping %s %s [deprecated]", n, typ)
continue
}
if !*internal.FlagRedirect && string(t.Redirect) != "" {
log.Printf("skipping %s %s [redirect:%s]", n, typ, t.Redirect)
continue
}
if t.Properties != nil {
t.Properties = cleanupTypes(n+" property", typ, t.Properties)
}
if t.Parameters != nil {
t.Parameters = cleanupTypes(n+" param", typ, t.Parameters)
}
if t.Returns != nil {
t.Returns = cleanupTypes(n+" return param", typ, t.Returns)
}
ret = append(ret, t)
}
return ret
}
// cleanup removes deprecated types, events, and commands from the domain.
func cleanup(d *internal.Domain) {
d.Types = cleanupTypes("type", d.String(), d.Types)
d.Events = cleanupTypes("event", d.String(), d.Events)
d.Commands = cleanupTypes("command", d.String(), d.Commands)
}
// write writes all the file buffers.
func write(fileBuffers map[string]*bytes.Buffer) error {
var err error
out := out() + "/"
for n, buf := range fileBuffers {
// add out path
n = out + n
// create directory
dir := filepath.Dir(n)
err = os.MkdirAll(dir, 0755)
if err != nil {
return err
}
// write file
err = ioutil.WriteFile(n, buf.Bytes(), 0644)
if err != nil {
return err
}
}
return nil
}
// goimports formats all the output file buffers on disk using goimports.
func goimports(fileBuffers map[string]*bytes.Buffer) error {
log.Printf("running goimports")
var keys []string
for k := range fileBuffers {
keys = append(keys, k)
}
sort.Strings(keys)
var wg sync.WaitGroup
for _, n := range keys {
wg.Add(1)
go func(wg *sync.WaitGroup, n string) {
defer wg.Done()
buf, err := exec.Command("goimports", "-w", out()+"/"+n).CombinedOutput()
if err != nil {
log.Fatalf("error: could not format %s, got:\n%s", n, string(buf))
}
}(&wg, n)
}
wg.Wait()
return nil
}
// easyjson runs easy json on the list of packages.
func easyjson(pkgs []string) error {
p := []string{"-pkg", "-all", "-output_filename", "easyjson.go"}
log.Printf("running easyjson (stubs)")
// generate easyjson stubs
var wg sync.WaitGroup
for _, n := range pkgs {
wg.Add(1)
go func(wg *sync.WaitGroup, n string) {
defer wg.Done()
cmd := exec.Command("easyjson", append(p, "-stubs")...)
cmd.Dir = out() + "/" + n
buf, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("could not generate easyjson stubs for %s, got:\n%s", cmd.Dir, string(buf))
}
}(&wg, n)
}
wg.Wait()
log.Printf("running easyjson")
// generate actual easyjson types
for _, n := range pkgs {
wg.Add(1)
go func(wg *sync.WaitGroup, n string) {
defer wg.Done()
cmd := exec.Command("easyjson", p...)
cmd.Dir = out() + "/" + n
buf, err := cmd.CombinedOutput()
if err != nil {
log.Fatalf("could not easyjson %s, got:\n%s", cmd.Dir, string(buf))
}
}(&wg, n)
}
wg.Wait()
return nil
}
// out returns the output path of the passed package flag.
func out() string {
return os.Getenv("GOPATH") + "/src/" + *internal.FlagPkg
}