Beispiel #1
0
// Generate generates the source code for type structs, and writes the
// generated.go to the filesystem.
func Generate(ctx context.Context, env *envctx.Env) error {

	wgctx.Add(ctx, "Generate")
	defer wgctx.Done(ctx, "Generate")

	cmd := cmdctx.FromContext(ctx)

	cmd.Printf("Generating types for %s... ", env.Path)

	outputDir := env.Dir
	filename := "generated.go"
	source, err := generate.Structs(ctx, env)
	if err != nil {
		return kerr.Wrap("XFNESBLBTQ", err)
	}

	// We only backup in the system structs and types files because they are
	// the only generated files we ever need to roll back
	backup := env.Path == "kego.io/system"

	if err = save(outputDir, source, filename, backup); err != nil {
		return kerr.Wrap("UONJTTSTWW", err)
	} else {
		cmd.Println("OK.")
	}

	return nil
}
Beispiel #2
0
Datei: main.go Projekt: kego/ke
func main() {
	// This turns off focus reporting mode that will print “^[[O” and “^[[I”
	// when the terminal window gets / loses focus
	// http://superuser.com/questions/931873/o-and-i-appearing-on-iterm2-when-focus-lost
	fmt.Print("\033[?1004l")

	ctx, cancel, err := process.Initialise(context.Background(), nil)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	signal.Notify(c, syscall.SIGTERM)
	go func() {
		<-c
		cancel()
	}()

	if err := process.GenerateAll(ctx, env.Path, map[string]bool{}); err != nil {
		fmt.Println(err.Error())
		wgctx.WaitAndExit(ctx, 1)
	}

	if cmd.Validate {
		err := process.RunValidateCommand(ctx)
		if err != nil {
			if !cmd.Log {
				// in log mode, we have already written the output of the exec'ed ke command,
				// so we don't need to duplicate the error message.
				if v, ok := kerr.Source(err).(validate.ValidationCommandError); ok {
					fmt.Println(v.Description)
					wgctx.WaitAndExit(ctx, 4) // Exit code 4: validation error
				}
				fmt.Println(err.Error())
			}
			wgctx.WaitAndExit(ctx, 1)
		}
	}

	if cmd.Edit {
		if err = server.Start(ctx, cancel); err != nil {
			fmt.Println(err.Error())
			wgctx.WaitAndExit(ctx, 1)
		}
	}

	wgctx.WaitAndExit(ctx, 0)

}
Beispiel #3
0
Datei: server.go Projekt: kego/ke
func serve(ctx context.Context) error {

	wgctx.Add(ctx, "serve")
	defer wgctx.Done(ctx, "serve")

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	// Starting with port zero chooses a random open port
	listner, err := net.Listen("tcp", fmt.Sprintf(":%d", cmd.Port))
	if err != nil {
		return kerr.Wrap("QGLXHWPWQW", err)
	}
	defer listner.Close()

	// Here we get the address we're serving on
	address, ok := listner.Addr().(*net.TCPAddr)
	if !ok {
		return kerr.New("CBLPYVGGUR", "Can't find address (l.Addr() is not *net.TCPAddr)")
	}

	url := fmt.Sprintf("http://localhost:%d/%s/", address.Port, env.Path)

	cmd.Printf("Server now running on %s\n", url)

	// We open the default browser and navigate to the address we're serving
	// from. This will error if the system doesn't have a browser, so we can
	// ignore the error.
	browser.OpenURL(url)

	withCancel(ctx, func() {
		err = http.Serve(listner, nil)
	})
	if err != nil {
		return kerr.Wrap("TUCBTWMRNN", err)
	}

	if cmd.Debug {
		return kerr.New("ATUTBOICGJ", "Connection closed")
	}
	return nil

}
Beispiel #4
0
Datei: run.go Projekt: kego/ke
// buildValidateCommand creates a temporary folder in the package, in which the go source for the
// local command is generated. This command is then compiled.
func buildValidateCommand(ctx context.Context) error {

	wgctx.Add(ctx, "buildValidateCommand")
	defer wgctx.Done(ctx, "buildValidateCommand")

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	validateCommandPath := filepath.Join(env.Dir, validateCommand)

	source, err := generate.ValidateCommand(ctx)
	if err != nil {
		return kerr.Wrap("SPRFABSRWK", err)
	}

	outputDir, err := ioutil.TempDir(env.Dir, "temporary")
	if err != nil {
		return kerr.Wrap("HWOPVXYMCT", err)
	}
	defer os.RemoveAll(outputDir)
	outputName := "generated_cmd.go"
	outputPath := filepath.Join(outputDir, outputName)

	if err = save(outputDir, source, outputName, false); err != nil {
		return kerr.Wrap("FRLCYFOWCJ", err)
	}

	cmd.Print("Building validate command... ")

	combined, stdout, stderr := logger.Logger(cmd.Log)
	exe := exec.Command("go", "build", "-o", validateCommandPath, outputPath)
	exe.Stdout = stdout
	exe.Stderr = stderr

	if err := exe.Run(); err != nil {
		return kerr.Wrap("OEPAEEYKIS", err)
	}
	cmd.Println("OK.")
	cmd.Print(combined.String())

	return nil
}
Beispiel #5
0
Datei: parser.go Projekt: kego/ke
func GoGet(ctx context.Context, path string) error {
	vos := vosctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)
	cmd.Print("Running go get -d ")
	args := []string{"get", "-d"}
	if cmd.Update {
		cmd.Print("-u ")
		args = append(args, "-u")
	}
	cmd.Print(path, "...")
	args = append(args, path)
	exe := exec.Command("go", args...)
	exe.Env = vos.Environ()
	if combined, err := exe.CombinedOutput(); err != nil {
		if !strings.Contains(string(combined), "no buildable Go source files") {
			return kerr.New("NIKCKQAKUI", "%s: %s", err.Error(), combined)
		}
	}
	cmd.Println(" OK.")
	return nil
}
Beispiel #6
0
Datei: server.go Projekt: kego/ke
func Start(ctx context.Context, cancel context.CancelFunc) error {

	wgctx.Add(ctx, "Start")
	defer wgctx.Done(ctx, "Start")

	auth := auther.New()

	cmd := cmdctx.FromContext(ctx)

	fail := make(chan error)

	cmd.Println("Starting editor server... ")

	// This contains the source map that will be persisted between requests
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if strings.HasSuffix(req.URL.Path, "/favicon.ico") {
			w.WriteHeader(404)
			return
		}
		if strings.HasSuffix(req.URL.Path, "/script.js") {
			if err := script(ctx, w, req, false); err != nil {
				fail <- kerr.Wrap("XPVTVKDWHJ", err)
				return
			}
			return
		}
		if strings.HasSuffix(req.URL.Path, "/script.js.map") {
			if err := script(ctx, w, req, true); err != nil {
				fail <- kerr.Wrap("JAIBRHULSI", err)
				return
			}
			return
		}
		if err := root(ctx, w, req, auth); err != nil {
			fail <- kerr.Wrap("QOMJGNOCQF", err)
			return
		}
	})

	if err := rpc.Register(&Server{ctx: ctx, auth: auth}); err != nil {
		return kerr.Wrap("RESBVVGRMH", err)
	}

	http.Handle("/_rpc", websocket.Handler(func(ws *websocket.Conn) {
		ws.PayloadType = websocket.BinaryFrame
		rpc.ServeConn(ws)
	}))

	go func() {
		if err := serve(ctx); err != nil {
			fail <- err
		}
	}()

	done := ctx.Done()
	for {
		select {
		case <-done:
			fmt.Println("Exiting editor server (interupted)... ")
			return nil
		case err, open := <-fail:
			if !open {
				// Channel has been closed, so app should gracefully exit.
				cancel()
				cmd.Println("Exiting editor server (finished)... ")
			} else {
				// Error received, so app should display error.
				// return kerr.New("WKHPTVJBIL", err, "Fail channel receive")
				fmt.Println(err)
			}
			if !cmd.Debug {
				return nil
			}
		}
	}
}
Beispiel #7
0
Datei: parser.go Projekt: kego/ke
func parse(ctx context.Context, path string, queue []string) (*sysctx.SysPackageInfo, error) {

	scache := sysctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	for _, q := range queue {
		if q == path {
			return nil, kerr.New("SCSCFJPPHD", "Circular import %v -> %v", queue, path)
		}
	}

	if _, found := scache.Get("kego.io/json"); !found {
		system.RegisterJsonTypes(ctx)
	}

	hash := &PackageHasher{Path: path, Aliases: map[string]string{}, Types: map[string]uint64{}, Exports: map[string]uint64{}}

	importPackage := func(importPath string, importAlias string) error {
		if _, found := scache.Get(importPath); !found {
			_, err := parse(ctx, importPath, append(queue, path))
			if err != nil {
				return kerr.Wrap("RIARRSCMVE", err)
			}
		}
		hash.Aliases[importAlias] = importPath
		return nil
	}

	packageDirectoryExists := true
	_, err := packages.GetDirFromPackage(ctx, path)
	if err != nil {
		_, ok := kerr.Source(err).(gopackages.NotFoundError)
		if ok {
			packageDirectoryExists = false
		}
	}
	if !packageDirectoryExists || cmd.Update {
		if err := GoGet(ctx, path); err != nil {
			return nil, kerr.Wrap("SBALWXUPKN", err)
		}
	}

	env, err := ScanForEnv(ctx, path)
	if err != nil {
		return nil, kerr.Wrap("GJRHNGGWFD", err)
	}

	// Always scan the system package first if we don't have it already
	if path != "kego.io/system" {
		if err := importPackage("kego.io/system", "system"); err != nil {
			return nil, kerr.Wrap("ORRCDNUPOX", err)
		}
	}

	for aliasName, aliasPath := range env.Aliases {
		if aliasPath == "kego.io/system" || aliasName == "system" {
			return nil, kerr.New("EWMLNJDXKC", "Illegal import %s", aliasName)
		}
		if err := importPackage(aliasPath, aliasName); err != nil {
			return nil, kerr.Wrap("NOVMGYKHHI", err)
		}
	}

	pcache := scache.SetEnv(env)

	cmd.Printf("Parsing %s...", path)

	if err := scanForTypesAndExports(ctx, env, pcache, hash); err != nil {
		return nil, kerr.Wrap("VFUNPHUFHD", err)
	}

	cmd.Println(" OK.")

	h, err := hash.Hash()
	if err != nil {
		return nil, kerr.Wrap("MIODRYNEJQ", err)
	}
	env.Hash = h

	return pcache, nil
}
Beispiel #8
0
Datei: run.go Projekt: kego/ke
func runValidateCommand(ctx context.Context, build bool, repeat bool) (err error) {

	wgctx.Add(ctx, "runValidateCommand")
	defer wgctx.Done(ctx, "runValidateCommand")

	if build {
		if err := buildValidateCommand(ctx); err != nil {
			return kerr.Wrap("FIJHGMEEUM", err)
		}
	}

	env := envctx.FromContext(ctx)
	cmd := cmdctx.FromContext(ctx)

	validateCommandPath := filepath.Join(env.Dir, validateCommand)

	cmd.Print("Running validate command... ")

	combined, stdout, stderr := logger.Logger(cmd.Log)

	hashChanged := false

	exe := exec.Command(validateCommandPath)
	exe.Stdout = stdout
	exe.Stderr = stderr
	if err = exe.Run(); err != nil {

		exiterr, ok := err.(*exec.ExitError)
		if !ok {
			goto Repeat
		}

		// The program has exited with an exit code != 0. This works on both Unix and Windows.
		// Although package syscall is generally platform dependent, WaitStatus is defined for
		// both Unix and Windows and in both cases has an ExitStatus() method with the same
		// signature.
		status, ok := exiterr.Sys().(syscall.WaitStatus)
		if !ok {
			// ke: {"block": {"notest": true}}
			goto Repeat
		}

		switch status.ExitStatus() {
		case 3:
			// Exit status 3 = hash changed
			hashChanged = true
			goto Repeat
		case 4:
			// Exit status 4 = validation error
			return validate.ValidationCommandError{Struct: kerr.New("ETWHPXTUVB", strings.TrimSpace(combined.String()))}
		default:
			// ke: {"block": {"notest": true}}
			goto Repeat
		}
	}
	cmd.Println("OK.")
	return nil

Repeat:
	if repeat {
		if hashChanged {
			cmd.Println("Types have changed since last run. Rebuilding...")
		} else {
			// ke: {"block": {"notest": true}}
			cmd.Println("Command returned an error. Rebuilding...")
		}
		if err := runValidateCommand(ctx, true, false); err != nil {
			return kerr.Wrap("HOHQEISLMI", err)
		}
		return nil
	}
	return kerr.Wrap("DTTHRRJSSF", err)
}