<commit_before>// Copyright 2018 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"

	"github.com/fatedier/frp/pkg/auth"
	"github.com/fatedier/frp/pkg/config"
	"github.com/fatedier/frp/pkg/util/log"
	"github.com/fatedier/frp/pkg/util/util"
	"github.com/fatedier/frp/pkg/util/version"
	"github.com/fatedier/frp/server"

	"github.com/spf13/cobra"
)

const (
	CfgFileTypeIni = iota
	CfgFileTypeCmd
)

var (
	cfgFile     string
	showVersion bool

	bindAddr          string
	bindPort          int
	bindUDPPort       int
	kcpBindPort       int
	proxyBindAddr     string
	vhostHTTPPort     int
	vhostHTTPSPort    int
	vhostHTTPTimeout  int64
	dashboardAddr     string
	dashboardPort     int
	dashboardUser     string
	dashboardPwd      string
	enablePrometheus  bool
	assetsDir         string
	logFile           string
	logLevel          string
	logMaxDays        int64
	disableLogColor   bool
	token             string
	subDomainHost     string
	tcpMux            bool
	allowPorts        string
	maxPoolCount      int64
	maxPortsPerClient int64
	tlsOnly           bool
)

func init() {
	rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file of frps")
	rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps")

	rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "0.0.0.0", "bind address")
	rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
	rootCmd.PersistentFlags().IntVarP(&bindUDPPort, "bind_udp_port", "", 0, "bind udp port")
	rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
	rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
	rootCmd.PersistentFlags().IntVarP(&vhostHTTPPort, "vhost_http_port", "", 0, "vhost http port")
	rootCmd.PersistentFlags().IntVarP(&vhostHTTPSPort, "vhost_https_port", "", 0, "vhost https port")
	rootCmd.PersistentFlags().Int64VarP(&vhostHTTPTimeout, "vhost_http_timeout", "", 60, "vhost http response header timeout")
	rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address")
	rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port")
	rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user")
	rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password")
	rootCmd.PersistentFlags().BoolVarP(&enablePrometheus, "enable_prometheus", "", false, "enable prometheus dashboard")
	rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
	rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
	rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log max days")
	rootCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")

	rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
	rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
	rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
	rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
	rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only")
}

var rootCmd = &cobra.Command{
	Use:   "frps",
	Short: "frps is the server of frp (https://github.com/fatedier/frp)",
	RunE: func(cmd *cobra.Command, args []string) error {
		if showVersion {
			fmt.Println(version.Full())
			return nil
		}

		var cfg config.ServerCommonConf
		var err error
		if cfgFile != "" {
			var content string
			content, err = config.GetRenderedConfFromFile(cfgFile)
			if err != nil {
				return err
			}
			cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
		} else {
			cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "")
		}
		if err != nil {
			return err
		}

		err = runServer(cfg)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		return nil
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func parseServerCommonCfg(fileType int, content string) (cfg config.ServerCommonConf, err error) {
	if fileType == CfgFileTypeIni {
		cfg, err = parseServerCommonCfgFromIni(content)
	} else if fileType == CfgFileTypeCmd {
		cfg, err = parseServerCommonCfgFromCmd()
	}
	if err != nil {
		return
	}

	err = cfg.Check()
	if err != nil {
		return
	}
	return
}

func parseServerCommonCfgFromIni(content string) (config.ServerCommonConf, error) {
	cfg, err := config.UnmarshalServerConfFromIni(content)
	if err != nil {
		return config.ServerCommonConf{}, err
	}
	return cfg, nil
}

func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
	cfg = config.GetDefaultServerConf()

	cfg.BindAddr = bindAddr
	cfg.BindPort = bindPort
	cfg.BindUDPPort = bindUDPPort
	cfg.KCPBindPort = kcpBindPort
	cfg.ProxyBindAddr = proxyBindAddr
	cfg.VhostHTTPPort = vhostHTTPPort
	cfg.VhostHTTPSPort = vhostHTTPSPort
	cfg.VhostHTTPTimeout = vhostHTTPTimeout
	cfg.DashboardAddr = dashboardAddr
	cfg.DashboardPort = dashboardPort
	cfg.DashboardUser = dashboardUser
	cfg.DashboardPwd = dashboardPwd
	cfg.EnablePrometheus = enablePrometheus
	cfg.LogFile = logFile
	cfg.LogLevel = logLevel
	cfg.LogMaxDays = logMaxDays
	cfg.SubDomainHost = subDomainHost
	cfg.TLSOnly = tlsOnly

	// Only token authentication is supported in cmd mode
	cfg.ServerConfig = auth.GetDefaultServerConf()
	cfg.Token = token
	if len(allowPorts) > 0 {
		// e.g. 1000-2000,2001,2002,3000-4000
		ports, errRet := util.ParseRangeNumbers(allowPorts)
		if errRet != nil {
			err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
			return
		}

		for _, port := range ports {
			cfg.AllowPorts[int(port)] = struct{}{}
		}
	}
	cfg.MaxPortsPerClient = maxPortsPerClient

	if logFile == "console" {
		cfg.LogWay = "console"
	} else {
		cfg.LogWay = "file"
	}
	cfg.DisableLogColor = disableLogColor
	return
}

func runServer(cfg config.ServerCommonConf) (err error) {
	log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)
	svr, err := server.NewService(cfg)
	if err != nil {
		return err
	}
	log.Info("start frps success")
	svr.Run()
	return
}
<commit_msg>tweak logs: (#2100)<commit_after>// Copyright 2018 fatedier, fatedier@gmail.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"

	"github.com/fatedier/frp/pkg/auth"
	"github.com/fatedier/frp/pkg/config"
	"github.com/fatedier/frp/pkg/util/log"
	"github.com/fatedier/frp/pkg/util/util"
	"github.com/fatedier/frp/pkg/util/version"
	"github.com/fatedier/frp/server"

	"github.com/spf13/cobra"
)

const (
	CfgFileTypeIni = iota
	CfgFileTypeCmd
)

var (
	cfgFile     string
	showVersion bool

	bindAddr          string
	bindPort          int
	bindUDPPort       int
	kcpBindPort       int
	proxyBindAddr     string
	vhostHTTPPort     int
	vhostHTTPSPort    int
	vhostHTTPTimeout  int64
	dashboardAddr     string
	dashboardPort     int
	dashboardUser     string
	dashboardPwd      string
	enablePrometheus  bool
	assetsDir         string
	logFile           string
	logLevel          string
	logMaxDays        int64
	disableLogColor   bool
	token             string
	subDomainHost     string
	tcpMux            bool
	allowPorts        string
	maxPoolCount      int64
	maxPortsPerClient int64
	tlsOnly           bool
)

func init() {
	rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "", "config file of frps")
	rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frps")

	rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "0.0.0.0", "bind address")
	rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
	rootCmd.PersistentFlags().IntVarP(&bindUDPPort, "bind_udp_port", "", 0, "bind udp port")
	rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
	rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
	rootCmd.PersistentFlags().IntVarP(&vhostHTTPPort, "vhost_http_port", "", 0, "vhost http port")
	rootCmd.PersistentFlags().IntVarP(&vhostHTTPSPort, "vhost_https_port", "", 0, "vhost https port")
	rootCmd.PersistentFlags().Int64VarP(&vhostHTTPTimeout, "vhost_http_timeout", "", 60, "vhost http response header timeout")
	rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address")
	rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port")
	rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user")
	rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password")
	rootCmd.PersistentFlags().BoolVarP(&enablePrometheus, "enable_prometheus", "", false, "enable prometheus dashboard")
	rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
	rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
	rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log max days")
	rootCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")

	rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
	rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
	rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
	rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
	rootCmd.PersistentFlags().BoolVarP(&tlsOnly, "tls_only", "", false, "frps tls only")
}

var rootCmd = &cobra.Command{
	Use:   "frps",
	Short: "frps is the server of frp (https://github.com/fatedier/frp)",
	RunE: func(cmd *cobra.Command, args []string) error {
		if showVersion {
			fmt.Println(version.Full())
			return nil
		}

		var cfg config.ServerCommonConf
		var err error
		if cfgFile != "" {
			log.Info("frps uses config file: %s", cfgFile)
			var content string
			content, err = config.GetRenderedConfFromFile(cfgFile)
			if err != nil {
				return err
			}
			cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
		} else {
			log.Info("frps uses command line arguments for config")
			cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "")
		}
		if err != nil {
			return err
		}

		err = runServer(cfg)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		return nil
	},
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		os.Exit(1)
	}
}

func parseServerCommonCfg(fileType int, content string) (cfg config.ServerCommonConf, err error) {
	if fileType == CfgFileTypeIni {
		cfg, err = parseServerCommonCfgFromIni(content)
	} else if fileType == CfgFileTypeCmd {
		cfg, err = parseServerCommonCfgFromCmd()
	}
	if err != nil {
		return
	}

	err = cfg.Check()
	if err != nil {
		return
	}
	return
}

func parseServerCommonCfgFromIni(content string) (config.ServerCommonConf, error) {
	cfg, err := config.UnmarshalServerConfFromIni(content)
	if err != nil {
		return config.ServerCommonConf{}, err
	}
	return cfg, nil
}

func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
	cfg = config.GetDefaultServerConf()

	cfg.BindAddr = bindAddr
	cfg.BindPort = bindPort
	cfg.BindUDPPort = bindUDPPort
	cfg.KCPBindPort = kcpBindPort
	cfg.ProxyBindAddr = proxyBindAddr
	cfg.VhostHTTPPort = vhostHTTPPort
	cfg.VhostHTTPSPort = vhostHTTPSPort
	cfg.VhostHTTPTimeout = vhostHTTPTimeout
	cfg.DashboardAddr = dashboardAddr
	cfg.DashboardPort = dashboardPort
	cfg.DashboardUser = dashboardUser
	cfg.DashboardPwd = dashboardPwd
	cfg.EnablePrometheus = enablePrometheus
	cfg.LogFile = logFile
	cfg.LogLevel = logLevel
	cfg.LogMaxDays = logMaxDays
	cfg.SubDomainHost = subDomainHost
	cfg.TLSOnly = tlsOnly

	// Only token authentication is supported in cmd mode
	cfg.ServerConfig = auth.GetDefaultServerConf()
	cfg.Token = token
	if len(allowPorts) > 0 {
		// e.g. 1000-2000,2001,2002,3000-4000
		ports, errRet := util.ParseRangeNumbers(allowPorts)
		if errRet != nil {
			err = fmt.Errorf("Parse conf error: allow_ports: %v", errRet)
			return
		}

		for _, port := range ports {
			cfg.AllowPorts[int(port)] = struct{}{}
		}
	}
	cfg.MaxPortsPerClient = maxPortsPerClient

	if logFile == "console" {
		cfg.LogWay = "console"
	} else {
		cfg.LogWay = "file"
	}
	cfg.DisableLogColor = disableLogColor
	return
}

func runServer(cfg config.ServerCommonConf) (err error) {
	log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)
	svr, err := server.NewService(cfg)
	if err != nil {
		return err
	}
	log.Info("frps started successfully")
	svr.Run()
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/constabulary/kang"
)

func check(err error) {
	if err != nil {
		fatal(err)
		os.Exit(1)
	}
}

func fatal(arg interface{}, args ...interface{}) {
	fmt.Fprint(os.Stderr, "fatal: ", arg)
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func main() {
	flag.Parse()
	f, err := findkangfile(cwd())
	check(err)

	f, err = filepath.Abs(f)
	check(err)

	fmt.Println("Using", f)

	kf, err := ParseFile(f)
	check(err)

	prefix, ok := kf["project"]["prefix"]
	if prefix == "" || !ok {
		fatal("project prefix missing from .kangfile")
	}

	workdir, err := ioutil.TempDir("", "kang")
	check(err)

	rootdir := filepath.Dir(f)
	pkgdir := filepath.Join(rootdir, ".kang", "pkg")

	ctx := &kang.Context{
		GOOS:    runtime.GOOS,
		GOARCH:  runtime.GOARCH,
		Workdir: workdir,
		Pkgdir:  pkgdir,
		Bindir:  rootdir,
	}

	action := "build"

	switch action {
	case "build":
		srcs := loadSources(prefix, rootdir)
		for _, src := range srcs {
			fmt.Printf("loaded %s (%s)\n", src.ImportPath, src.Name)
		}

		pkgs := transform(ctx, srcs...)
		computeStale(pkgs...)

		targets := make(map[string]func() error)
		fn, err := buildPackages(targets, pkgs...)
		check(err)
		check(fn())
	default:
		fatal("unknown action:", action)
	}
}

func cwd() string {
	wd, err := os.Getwd()
	check(err)
	return wd
}

// transform takes a slice of go/build.Package and returns the
// corresponding slice of kang.Packages.
func transform(ctx *kang.Context, v ...*build.Package) []*kang.Package {
	srcs := make(map[string]*build.Package)
	for _, pkg := range v {
		srcs[pkg.ImportPath] = pkg
	}

	var pkgs []*kang.Package
	seen := make(map[string]bool)

	var walk func(src *build.Package)
	walk = func(src *build.Package) {
		if seen[src.ImportPath] {
			return
		}
		seen[src.ImportPath] = true

		for _, i := range src.Imports {
			if stdlib[i] {
				// skip stdlib package
				continue
			}
			pkg, ok := srcs[i]
			if !ok {
				fatal("transform: pkg ", i, "is not loaded")
			}
			walk(pkg)
		}

		pkgs = append(pkgs, &kang.Package{
			Context:    ctx,
			ImportPath: src.ImportPath,
			Dir:        src.Dir,
			GoFiles:    src.GoFiles,
			Main:       src.Name == "main",
		})
	}
	for _, p := range v {
		walk(p)
	}
	return pkgs
}

// computeStale sets the UpToDate flag on a set of package roots.
func computeStale(roots ...*kang.Package) {
	seen := make(map[*kang.Package]bool)

	var walk func(pkg *kang.Package) bool
	walk = func(pkg *kang.Package) bool {
		if seen[pkg] {
			return pkg.NotStale
		}
		seen[pkg] = true

		for _, i := range pkg.Imports {
			if !walk(i) {
				// a dep is stale so we are stale
				return false
			}
		}

		stale := pkg.IsStale()
		pkg.NotStale = !stale
		return !stale
	}

	for _, root := range roots {
		walk(root)
	}
}

// findkangfile returns the location of the closest .kangfile
// relative to the dir provided. If no .kangfile is found, an
// error is returned.
func findkangfile(dir string) (string, error) {
	orig := dir
	for {
		path := filepath.Join(dir, ".kangfile")
		fi, err := os.Stat(path)
		if err == nil && !fi.IsDir() {
			return path, nil
		}
		if err != nil && !os.IsNotExist(err) {
			check(err)
		}
		d := filepath.Dir(dir)
		if d == dir {
			// got to the root directory without
			return "", fmt.Errorf("could not locate .kangfile in %s", orig)
		}
		dir = d
	}
}

func buildPackages(targets map[string]func() error, pkgs ...*kang.Package) (func() error, error) {
	var deps []func() error
	for _, pkg := range pkgs {
		fn, err := buildPackage(targets, pkg)
		check(err)
		deps = append(deps, fn)
	}
	return func() error {
		for _, fn := range deps {
			if err := fn(); err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func buildPackage(targets map[string]func() error, pkg *kang.Package) (func() error, error) {

	// if this action is already present in the map, return it
	// rather than creating a new action.
	if fn, ok := targets[pkg.ImportPath]; ok {
		return fn, nil
	}

	// step 0. are we stale ?
	// if this package is not stale, then by definition none of its
	// dependencies are stale, so ignore this whole tree.
	if pkg.NotStale {
		return func() error {
			fmt.Println(pkg.ImportPath, "is up to date")
			return nil
		}, nil
	}

	// step 1. build dependencies
	var deps []func() error
	for _, pkg := range pkg.Imports {
		fn, err := buildPackage(targets, pkg)
		if err != nil {
			return nil, err
		}
		deps = append(deps, fn)
	}

	// step 2. build this package
	build := func() error {
		for _, dep := range deps {
			if err := dep(); err != nil {
				return err
			}
		}
		if err := kang.Compile(pkg); err != nil {
			return err
		}
		if !pkg.Main {
			return nil // we're done
		}
		return kang.Link(pkg)
	}

	// record the final action as the action that represents
	// building this package.
	targets[pkg.ImportPath] = build

	return build, nil
}

func loadSources(prefix string, dir string) []*build.Package {
	f, err := os.Open(dir)
	check(err)
	files, err := f.Readdir(-1)
	check(err)
	f.Close()

	var srcs []*build.Package
	for _, fi := range files {
		name := fi.Name()
		if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") || name == "testdata" || name == "vendor" {
			// ignore it
			continue
		}
		if fi.IsDir() {
			srcs = append(srcs, loadSources(path.Join(prefix, name), filepath.Join(dir, name))...)
		}
	}

	pkg, err := build.ImportDir(dir, 0)
	switch err := err.(type) {
	case nil:
		// ImportDir does not know the import path for this package
		// but we know the prefix, so fix it.
		pkg.ImportPath = prefix
		srcs = append(srcs, pkg)
	case (*build.NoGoError):
		// do nothing
	default:
		check(err)
	}

	return srcs
}
<commit_msg>cmd/kang: added dependency resolution from a prepupulated cache<commit_after>package main

import (
	"crypto/sha1"
	"flag"
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/constabulary/kang"
)

func check(err error) {
	if err != nil {
		fatal(err)
		os.Exit(1)
	}
}

func fatal(arg interface{}, args ...interface{}) {
	fmt.Fprint(os.Stderr, "fatal: ", arg)
	fmt.Fprintln(os.Stderr, args...)
	os.Exit(1)
}

func main() {
	flag.Parse()
	f, err := findkangfile(cwd())
	check(err)

	f, err = filepath.Abs(f)
	check(err)

	fmt.Println("Using", f)

	kf, err := ParseFile(f)
	check(err)

	prefix, ok := kf["project"]["prefix"]
	if prefix == "" || !ok {
		fatal("project prefix missing from .kangfile")
	}

	workdir, err := ioutil.TempDir("", "kang")
	check(err)

	rootdir := filepath.Dir(f)
	pkgdir := filepath.Join(rootdir, ".kang", "pkg")

	ctx := &kang.Context{
		GOOS:    runtime.GOOS,
		GOARCH:  runtime.GOARCH,
		Workdir: workdir,
		Pkgdir:  pkgdir,
		Bindir:  rootdir,
	}

	action := "build"

	switch action {
	case "build":
		srcs := loadSources(prefix, rootdir)
		for _, src := range srcs {
			fmt.Printf("loaded %s (%s)\n", src.ImportPath, src.Name)
		}

		srcs = loadDependencies(rootdir, kf, srcs...)

		pkgs := transform(ctx, srcs...)
		computeStale(pkgs...)

		targets := make(map[string]func() error)
		fn, err := buildPackages(targets, pkgs...)
		check(err)
		check(fn())
	default:
		fatal("unknown action:", action)
	}
}

func cwd() string {
	wd, err := os.Getwd()
	check(err)
	return wd
}

// transform takes a slice of go/build.Package and returns the
// corresponding slice of kang.Packages.
func transform(ctx *kang.Context, v ...*build.Package) []*kang.Package {
	srcs := make(map[string]*build.Package)
	for _, pkg := range v {
		srcs[pkg.ImportPath] = pkg
	}

	var pkgs []*kang.Package
	seen := make(map[string]bool)

	var walk func(src *build.Package)
	walk = func(src *build.Package) {
		if seen[src.ImportPath] {
			return
		}
		seen[src.ImportPath] = true

		for _, i := range src.Imports {
			if stdlib[i] {
				// skip stdlib package
				continue
			}
			pkg, ok := srcs[i]
			if !ok {
				fatal("transform: pkg ", i, "is not loaded")
			}
			walk(pkg)
		}

		pkgs = append(pkgs, &kang.Package{
			Context:    ctx,
			ImportPath: src.ImportPath,
			Dir:        src.Dir,
			GoFiles:    src.GoFiles,
			Main:       src.Name == "main",
		})
	}
	for _, p := range v {
		walk(p)
	}
	return pkgs
}

// computeStale sets the UpToDate flag on a set of package roots.
func computeStale(roots ...*kang.Package) {
	seen := make(map[*kang.Package]bool)

	var walk func(pkg *kang.Package) bool
	walk = func(pkg *kang.Package) bool {
		if seen[pkg] {
			return pkg.NotStale
		}
		seen[pkg] = true

		for _, i := range pkg.Imports {
			if !walk(i) {
				// a dep is stale so we are stale
				return false
			}
		}

		stale := pkg.IsStale()
		pkg.NotStale = !stale
		return !stale
	}

	for _, root := range roots {
		walk(root)
	}
}

// findkangfile returns the location of the closest .kangfile
// relative to the dir provided. If no .kangfile is found, an
// error is returned.
func findkangfile(dir string) (string, error) {
	orig := dir
	for {
		path := filepath.Join(dir, ".kangfile")
		fi, err := os.Stat(path)
		if err == nil && !fi.IsDir() {
			return path, nil
		}
		if err != nil && !os.IsNotExist(err) {
			check(err)
		}
		d := filepath.Dir(dir)
		if d == dir {
			// got to the root directory without
			return "", fmt.Errorf("could not locate .kangfile in %s", orig)
		}
		dir = d
	}
}

func buildPackages(targets map[string]func() error, pkgs ...*kang.Package) (func() error, error) {
	var deps []func() error
	for _, pkg := range pkgs {
		fn, err := buildPackage(targets, pkg)
		check(err)
		deps = append(deps, fn)
	}
	return func() error {
		for _, fn := range deps {
			if err := fn(); err != nil {
				return err
			}
		}
		return nil
	}, nil
}

func buildPackage(targets map[string]func() error, pkg *kang.Package) (func() error, error) {

	// if this action is already present in the map, return it
	// rather than creating a new action.
	if fn, ok := targets[pkg.ImportPath]; ok {
		return fn, nil
	}

	// step 0. are we stale ?
	// if this package is not stale, then by definition none of its
	// dependencies are stale, so ignore this whole tree.
	if pkg.NotStale {
		return func() error {
			fmt.Println(pkg.ImportPath, "is up to date")
			return nil
		}, nil
	}

	// step 1. build dependencies
	var deps []func() error
	for _, pkg := range pkg.Imports {
		fn, err := buildPackage(targets, pkg)
		if err != nil {
			return nil, err
		}
		deps = append(deps, fn)
	}

	// step 2. build this package
	build := func() error {
		for _, dep := range deps {
			if err := dep(); err != nil {
				return err
			}
		}
		if err := kang.Compile(pkg); err != nil {
			return err
		}
		if !pkg.Main {
			return nil // we're done
		}
		return kang.Link(pkg)
	}

	// record the final action as the action that represents
	// building this package.
	targets[pkg.ImportPath] = build

	return build, nil
}

func loadSources(prefix string, dir string) []*build.Package {
	f, err := os.Open(dir)
	check(err)
	files, err := f.Readdir(-1)
	check(err)
	f.Close()

	var srcs []*build.Package
	for _, fi := range files {
		name := fi.Name()
		if strings.HasPrefix(name, "_") || strings.HasPrefix(name, ".") || name == "testdata" || name == "vendor" {
			// ignore it
			continue
		}
		if fi.IsDir() {
			srcs = append(srcs, loadSources(path.Join(prefix, name), filepath.Join(dir, name))...)
		}
	}

	pkg, err := build.ImportDir(dir, 0)
	switch err := err.(type) {
	case nil:
		// ImportDir does not know the import path for this package
		// but we know the prefix, so fix it.
		pkg.ImportPath = prefix
		srcs = append(srcs, pkg)
	case (*build.NoGoError):
		// do nothing
	default:
		check(err)
	}

	return srcs
}

func loadDependencies(rootdir string, m map[string]map[string]string, srcs ...*build.Package) []*build.Package {
	load := func(path string) *build.Package { fatal("cannot resolve path ", path); return nil }
	for prefix, d := range m {
		if prefix == "project" {
			// skip kang metadata
			continue
		}
		if version, ok := d["version"]; ok {
			hash := sha1.Sum([]byte(prefix + "version=" + version))
			prefix := prefix
			dir := filepath.Join(rootdir, ".kang", "cache", fmt.Sprintf("%x", hash[0:1]), fmt.Sprintf("%x", hash[1:]))
			next := load
			fmt.Println("registered:", prefix, "@", version)
			load = func(path string) *build.Package {
				if !strings.HasPrefix(path, prefix) {
					return next(path)
				}
				fmt.Println("searching", path, "in", prefix, "@", version)
				dir := filepath.Join(dir, path)
				_, err := os.Stat(dir)
				if os.IsNotExist(err) {
					check(err)
				}
				pkg, err := build.ImportDir(dir, 0)
				switch err := err.(type) {
				case nil:
					// ImportDir does not know the import path for this package
					// but we know the prefix, so fix it.
					pkg.ImportPath = path
					return pkg
				case (*build.NoGoError):
					// do nothing
				default:
					check(err)
				}
				return nil
			}
			continue
		}
		if tag, ok := d["tag"]; ok {
			hash := sha1.Sum([]byte(prefix + "tag=" + tag))
			prefix := prefix
			dir := filepath.Join(rootdir, ".kang", "cache", fmt.Sprintf("%x", hash[0:1]), fmt.Sprintf("%x", hash[1:]))
			next := load
			fmt.Println("registered:", prefix, "@", tag)
			load = func(path string) *build.Package {
				if !strings.HasPrefix(path, prefix) {
					return next(path)
				}
				fmt.Println("searching", path, "in", prefix, "@", tag)
				dir := filepath.Join(dir, path)
				_, err := os.Stat(dir)
				if os.IsNotExist(err) {
					check(err)
				}
				pkg, err := build.ImportDir(dir, 0)
				switch err := err.(type) {
				case nil:
					// ImportDir does not know the import path for this package
					// but we know the prefix, so fix it.
					pkg.ImportPath = path
					return pkg
				case (*build.NoGoError):
					// do nothing
				default:
					check(err)
				}
				return nil
			}
			continue
		}
		fatal("unknoww dependency", d)
	}

	seen := make(map[string]bool)
	var walk func(string)
	walk = func(path string) {
		if stdlib[path] {
			return
		}
		if seen[path] {
			return
		}
		seen[path] = true
		pkg := load(path)
		srcs = append(srcs, pkg)
		for _, i := range pkg.Imports {
			walk(i)
		}
	}
	for _, src := range srcs {
		seen[src.ImportPath] = true
	}
	for _, src := range srcs[:] {
		for _, i := range src.Imports {
			walk(i)
		}
	}
	return srcs
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"text/template"
	"time"

	"github.com/google/kati"
)

const shellDateTimeformat = time.RFC3339

var (
	makefileFlag string
	jobsFlag     int

	loadJSON string
	saveJSON string
	loadGOB  string
	saveGOB  string
	useCache bool

	m2n  bool
	goma bool

	cpuprofile          string
	heapprofile         string
	memstats            string
	traceEventFile      string
	syntaxCheckOnlyFlag bool
	queryFlag           string
	eagerCmdEvalFlag    bool
	generateNinja       bool
	gomaDir             string
	findCachePrunes     string
	findCacheLeafNames  string
	shellDate           string
)

func init() {
	// TODO: Make this default and replace this by -d flag.
	flag.StringVar(&makefileFlag, "f", "", "Use it as a makefile")
	flag.IntVar(&jobsFlag, "j", 1, "Allow N jobs at once.")

	flag.StringVar(&loadGOB, "load", "", "")
	flag.StringVar(&saveGOB, "save", "", "")
	flag.StringVar(&loadJSON, "load_json", "", "")
	flag.StringVar(&saveJSON, "save_json", "", "")
	flag.BoolVar(&useCache, "use_cache", false, "Use cache.")

	flag.BoolVar(&m2n, "m2n", false, "m2n mode")
	flag.BoolVar(&goma, "goma", false, "ensure goma start")

	flag.StringVar(&cpuprofile, "kati_cpuprofile", "", "write cpu profile to `file`")
	flag.StringVar(&heapprofile, "kati_heapprofile", "", "write heap profile to `file`")
	flag.StringVar(&memstats, "kati_memstats", "", "Show memstats with given templates")
	flag.StringVar(&traceEventFile, "kati_trace_event", "", "write trace event to `file`")
	flag.BoolVar(&syntaxCheckOnlyFlag, "c", false, "Syntax check only.")
	flag.StringVar(&queryFlag, "query", "", "Show the target info")
	flag.BoolVar(&eagerCmdEvalFlag, "eager_cmd_eval", false, "Eval commands first.")
	flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
	flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")

	flag.StringVar(&findCachePrunes, "find_cache_prunes", "",
		"space separated prune directories for find cache.")
	flag.StringVar(&findCacheLeafNames, "find_cache_leaf_names", "",
		"space separated leaf names for find cache.")
	flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)

	flag.BoolVar(&kati.LogFlag, "kati_log", false, "Verbose kati specific log")
	flag.BoolVar(&kati.StatsFlag, "kati_stats", false, "Show a bunch of statistics")
	flag.BoolVar(&kati.PeriodicStatsFlag, "kati_periodic_stats", false, "Show a bunch of periodic statistics")
	flag.BoolVar(&kati.EvalStatsFlag, "kati_eval_stats", false, "Show eval statistics")

	flag.BoolVar(&kati.DryRunFlag, "n", false, "Only print the commands that would be executed")

	// TODO: Make this default.
	flag.BoolVar(&kati.UseFindCache, "use_find_cache", false, "Use find cache.")
	flag.BoolVar(&kati.UseWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
	flag.BoolVar(&kati.UseShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
	flag.StringVar(&kati.IgnoreOptionalInclude, "ignore_optional_include", "", "If specified, skip reading -include directives start with the specified path.")
}

func writeHeapProfile() {
	f, err := os.Create(heapprofile)
	if err != nil {
		panic(err)
	}
	pprof.WriteHeapProfile(f)
	f.Close()
}

type memStatsDumper struct {
	*template.Template
}

func (t memStatsDumper) dump() {
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	var buf bytes.Buffer
	err := t.Template.Execute(&buf, ms)
	fmt.Println(buf.String())
	if err != nil {
		panic(err)
	}
}

func load(req kati.LoadReq) (*kati.DepGraph, bool, error) {
	if loadGOB != "" {
		g, err := kati.GOB.Load(loadGOB)
		return g, true, err
	}
	if loadJSON != "" {
		g, err := kati.JSON.Load(loadJSON)
		return g, true, err
	}
	g, err := kati.Load(req)
	return g, false, err
}

func save(g *kati.DepGraph, targets []string) error {
	var err error
	if saveGOB != "" {
		err = kati.GOB.Save(g, saveGOB, targets)
	}
	if saveJSON != "" {
		serr := kati.JSON.Save(g, saveJSON, targets)
		if err == nil {
			err = serr
		}
	}
	return err
}

func m2nsetup() {
	fmt.Println("kati: m2n mode")
	generateNinja = true
	kati.IgnoreOptionalInclude = "out/%.P"
	kati.UseFindCache = true
	if findCachePrunes == "" {
		findCachePrunes = ".git .repo out"
	}
}

func gomasetup() {
	if gomaDir == "" {
		gomaDir = os.ExpandEnv("${HOME}/goma")
	}
	fmt.Printf("kati: setup goma: %s\n", gomaDir)
	cmd := exec.Command(filepath.Join(gomaDir, "goma_ctl.py"), "ensure_start")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Printf("goma failed to start: %v", err)
		os.Exit(1)
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	m2ncmd := false
	if filepath.Base(os.Args[0]) == "m2n" {
		m2nsetup()
		m2ncmd = true
	}
	flag.Parse()
	args := flag.Args()
	if m2n {
		generateNinja = true
		if !m2ncmd {
			m2nsetup()
		}
		if len(args) > 1 {
			fmt.Println("use only first argument as ONE_SHOT_MAKEFILE. ignore rest")
		}
		if len(args) > 0 {
			err := os.Setenv("ONE_SHOT_MAKEFILE", filepath.Join(args[0], "Android.mk"))
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("ONE_SHOT_MAKEFILE=%s\n", os.ExpandEnv("${ONE_SHOT_MAKEFILE}"))
		}
		args = args[:0]
	}
	if goma {
		gomasetup()
	}
	err := katiMain(args)
	if err != nil {
		fmt.Println(err)
		// http://www.gnu.org/software/make/manual/html_node/Running.html
		os.Exit(2)
	}
}

func katiMain(args []string) error {
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			return err
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if heapprofile != "" {
		defer writeHeapProfile()
	}
	defer kati.DumpStats()
	if memstats != "" {
		ms := memStatsDumper{
			Template: template.Must(template.New("memstats").Parse(memstats)),
		}
		ms.dump()
		defer ms.dump()
	}
	if traceEventFile != "" {
		f, err := os.Create(traceEventFile)
		if err != nil {
			panic(err)
		}
		kati.TraceEventStart(f)
		defer kati.TraceEventStop()
	}

	if shellDate != "" {
		if shellDate == "ref" {
			shellDate = shellDateTimeformat[:20] // until Z, drop 07:00
		}
		t, err := time.Parse(shellDateTimeformat, shellDate)
		if err != nil {
			panic(err)
		}
		kati.ShellDateTimestamp = t
	}

	var leafNames []string
	if findCacheLeafNames != "" {
		leafNames = strings.Fields(findCacheLeafNames)
	}
	if findCachePrunes != "" {
		kati.UseFindCache = true
		kati.AndroidFindCacheInit(strings.Fields(findCachePrunes), leafNames)
	}

	req := kati.FromCommandLine(args)
	if makefileFlag != "" {
		req.Makefile = makefileFlag
	}
	req.EnvironmentVars = os.Environ()
	req.UseCache = useCache
	req.EagerEvalCommand = eagerCmdEvalFlag

	g, cached, err := load(req)
	if err != nil {
		return err
	}

	if !cached {
		err = save(g, req.Targets)
		if err != nil {
			return err
		}
	}

	if generateNinja {
		return kati.GenerateNinja(g, gomaDir)
	}

	if syntaxCheckOnlyFlag {
		return nil
	}

	if queryFlag != "" {
		kati.Query(os.Stdout, queryFlag, g)
		return nil
	}

	execOpt := &kati.ExecutorOpt{
		NumJobs: jobsFlag,
	}
	ex, err := kati.NewExecutor(execOpt)
	if err != nil {
		return err
	}
	err = ex.Exec(g)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>kati -m2n also respects GOMA_DIR environment variable.<commit_after>// Copyright 2015 Google Inc. All rights reserved
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"runtime/pprof"
	"strings"
	"text/template"
	"time"

	"github.com/google/kati"
)

const shellDateTimeformat = time.RFC3339

var (
	makefileFlag string
	jobsFlag     int

	loadJSON string
	saveJSON string
	loadGOB  string
	saveGOB  string
	useCache bool

	m2n  bool
	goma bool

	cpuprofile          string
	heapprofile         string
	memstats            string
	traceEventFile      string
	syntaxCheckOnlyFlag bool
	queryFlag           string
	eagerCmdEvalFlag    bool
	generateNinja       bool
	gomaDir             string
	findCachePrunes     string
	findCacheLeafNames  string
	shellDate           string
)

func init() {
	// TODO: Make this default and replace this by -d flag.
	flag.StringVar(&makefileFlag, "f", "", "Use it as a makefile")
	flag.IntVar(&jobsFlag, "j", 1, "Allow N jobs at once.")

	flag.StringVar(&loadGOB, "load", "", "")
	flag.StringVar(&saveGOB, "save", "", "")
	flag.StringVar(&loadJSON, "load_json", "", "")
	flag.StringVar(&saveJSON, "save_json", "", "")
	flag.BoolVar(&useCache, "use_cache", false, "Use cache.")

	flag.BoolVar(&m2n, "m2n", false, "m2n mode")
	flag.BoolVar(&goma, "goma", false, "ensure goma start")

	flag.StringVar(&cpuprofile, "kati_cpuprofile", "", "write cpu profile to `file`")
	flag.StringVar(&heapprofile, "kati_heapprofile", "", "write heap profile to `file`")
	flag.StringVar(&memstats, "kati_memstats", "", "Show memstats with given templates")
	flag.StringVar(&traceEventFile, "kati_trace_event", "", "write trace event to `file`")
	flag.BoolVar(&syntaxCheckOnlyFlag, "c", false, "Syntax check only.")
	flag.StringVar(&queryFlag, "query", "", "Show the target info")
	flag.BoolVar(&eagerCmdEvalFlag, "eager_cmd_eval", false, "Eval commands first.")
	flag.BoolVar(&generateNinja, "ninja", false, "Generate build.ninja.")
	flag.StringVar(&gomaDir, "goma_dir", "", "If specified, use goma to build C/C++ files.")

	flag.StringVar(&findCachePrunes, "find_cache_prunes", "",
		"space separated prune directories for find cache.")
	flag.StringVar(&findCacheLeafNames, "find_cache_leaf_names", "",
		"space separated leaf names for find cache.")
	flag.StringVar(&shellDate, "shell_date", "", "specify $(shell date) time as "+shellDateTimeformat)

	flag.BoolVar(&kati.LogFlag, "kati_log", false, "Verbose kati specific log")
	flag.BoolVar(&kati.StatsFlag, "kati_stats", false, "Show a bunch of statistics")
	flag.BoolVar(&kati.PeriodicStatsFlag, "kati_periodic_stats", false, "Show a bunch of periodic statistics")
	flag.BoolVar(&kati.EvalStatsFlag, "kati_eval_stats", false, "Show eval statistics")

	flag.BoolVar(&kati.DryRunFlag, "n", false, "Only print the commands that would be executed")

	// TODO: Make this default.
	flag.BoolVar(&kati.UseFindCache, "use_find_cache", false, "Use find cache.")
	flag.BoolVar(&kati.UseWildcardCache, "use_wildcard_cache", true, "Use wildcard cache.")
	flag.BoolVar(&kati.UseShellBuiltins, "use_shell_builtins", true, "Use shell builtins")
	flag.StringVar(&kati.IgnoreOptionalInclude, "ignore_optional_include", "", "If specified, skip reading -include directives start with the specified path.")
}

func writeHeapProfile() {
	f, err := os.Create(heapprofile)
	if err != nil {
		panic(err)
	}
	pprof.WriteHeapProfile(f)
	f.Close()
}

type memStatsDumper struct {
	*template.Template
}

func (t memStatsDumper) dump() {
	var ms runtime.MemStats
	runtime.ReadMemStats(&ms)
	var buf bytes.Buffer
	err := t.Template.Execute(&buf, ms)
	fmt.Println(buf.String())
	if err != nil {
		panic(err)
	}
}

func load(req kati.LoadReq) (*kati.DepGraph, bool, error) {
	if loadGOB != "" {
		g, err := kati.GOB.Load(loadGOB)
		return g, true, err
	}
	if loadJSON != "" {
		g, err := kati.JSON.Load(loadJSON)
		return g, true, err
	}
	g, err := kati.Load(req)
	return g, false, err
}

func save(g *kati.DepGraph, targets []string) error {
	var err error
	if saveGOB != "" {
		err = kati.GOB.Save(g, saveGOB, targets)
	}
	if saveJSON != "" {
		serr := kati.JSON.Save(g, saveJSON, targets)
		if err == nil {
			err = serr
		}
	}
	return err
}

func m2nsetup() {
	fmt.Println("kati: m2n mode")
	generateNinja = true
	kati.IgnoreOptionalInclude = "out/%.P"
	kati.UseFindCache = true
	if findCachePrunes == "" {
		findCachePrunes = ".git .repo out"
	}
}

func gomasetup() {
	if gomaDir == "" {
		gomaDir = os.Getenv("GOMA_DIR")
		if gomaDir == "" {
			gomaDir = os.ExpandEnv("${HOME}/goma")
		}
	}
	fmt.Printf("kati: setup goma: %s\n", gomaDir)
	cmd := exec.Command(filepath.Join(gomaDir, "goma_ctl.py"), "ensure_start")
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err := cmd.Run()
	if err != nil {
		fmt.Printf("goma failed to start: %v", err)
		os.Exit(1)
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	m2ncmd := false
	if filepath.Base(os.Args[0]) == "m2n" {
		m2nsetup()
		m2ncmd = true
	}
	flag.Parse()
	args := flag.Args()
	if m2n {
		generateNinja = true
		if !m2ncmd {
			m2nsetup()
		}
		if len(args) > 1 {
			fmt.Println("use only first argument as ONE_SHOT_MAKEFILE. ignore rest")
		}
		if len(args) > 0 {
			err := os.Setenv("ONE_SHOT_MAKEFILE", filepath.Join(args[0], "Android.mk"))
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("ONE_SHOT_MAKEFILE=%s\n", os.ExpandEnv("${ONE_SHOT_MAKEFILE}"))
		}
		args = args[:0]
	}
	if goma {
		gomasetup()
	}
	err := katiMain(args)
	if err != nil {
		fmt.Println(err)
		// http://www.gnu.org/software/make/manual/html_node/Running.html
		os.Exit(2)
	}
}

func katiMain(args []string) error {
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			return err
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}
	if heapprofile != "" {
		defer writeHeapProfile()
	}
	defer kati.DumpStats()
	if memstats != "" {
		ms := memStatsDumper{
			Template: template.Must(template.New("memstats").Parse(memstats)),
		}
		ms.dump()
		defer ms.dump()
	}
	if traceEventFile != "" {
		f, err := os.Create(traceEventFile)
		if err != nil {
			panic(err)
		}
		kati.TraceEventStart(f)
		defer kati.TraceEventStop()
	}

	if shellDate != "" {
		if shellDate == "ref" {
			shellDate = shellDateTimeformat[:20] // until Z, drop 07:00
		}
		t, err := time.Parse(shellDateTimeformat, shellDate)
		if err != nil {
			panic(err)
		}
		kati.ShellDateTimestamp = t
	}

	var leafNames []string
	if findCacheLeafNames != "" {
		leafNames = strings.Fields(findCacheLeafNames)
	}
	if findCachePrunes != "" {
		kati.UseFindCache = true
		kati.AndroidFindCacheInit(strings.Fields(findCachePrunes), leafNames)
	}

	req := kati.FromCommandLine(args)
	if makefileFlag != "" {
		req.Makefile = makefileFlag
	}
	req.EnvironmentVars = os.Environ()
	req.UseCache = useCache
	req.EagerEvalCommand = eagerCmdEvalFlag

	g, cached, err := load(req)
	if err != nil {
		return err
	}

	if !cached {
		err = save(g, req.Targets)
		if err != nil {
			return err
		}
	}

	if generateNinja {
		return kati.GenerateNinja(g, gomaDir)
	}

	if syntaxCheckOnlyFlag {
		return nil
	}

	if queryFlag != "" {
		kati.Query(os.Stdout, queryFlag, g)
		return nil
	}

	execOpt := &kati.ExecutorOpt{
		NumJobs: jobsFlag,
	}
	ex, err := kati.NewExecutor(execOpt)
	if err != nil {
		return err
	}
	err = ex.Exec(g)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

/**
*
* Author: Amir Mofasser <amir.mofasser@gmail.com>
*	https://github.com/amimof
*
*/

import (
	copy "github.com/amimof/lazycopy/copy"
	logger "github.com/amimof/lazycopy/logger"
	"io/ioutil"
	"os"
	"regexp"
	"flag"
	"path"
	"fmt"
	"strings"
)

var (
	prog string
	mroot string
	sroot string
	target string
	unit string
	overwrite bool
	confirm bool
	listmode bool
	loglevel int
)

type Movie struct {
	title string
	year string
	filename string
}

type Serie struct {
	title string
	season string
	episode string
	filename string
}

// Extensions in regexp disabled atm since it prevents us from detecting folders
// var extensions string = "\\.(mkv|MKV|mp4|MP4|m4p|M4P|m4v|M4V|mpg|MPG|mpeg|MPEG|mp2|MP2|mpe|MPE|mpv|MPV|3gp|3GP|nsv|NSV|f4v|F4V|f4p|F4P|f4a|F4A|f4b|F4P|vob|VOB|avi|AVI|mov|MOV|wmv|WMV|asd|ASD|flv|FLV|ogv|OGV|ogg|OGG|qt|QT|yuv|YUV|rm|RM|rmvb|RMVB)"
var extensions string = ""
var spattern []string = []string{"(.*?)S(\\d{1,2})E(\\d{2})(.*)"+extensions,
	"(.*?)s(\\d{1,2})e(\\d{2})(.*)"+extensions,
	"(.*?)\\[?(\\d{1,2})x(\\d{2})\\]?(.*)"+extensions,
	"(.*?)Season.?(\\d{1,2}).*?Episode.?(\\d{1,2})(.*)"+extensions}
var mpattern []string = []string{"(.*?)\\((17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\)(.*)"+extensions,
	"(.*?)\\[(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\](.*)"+extensions,
	"(.*?)\\{(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\}(.*)"+extensions,
	"(.*?)(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])(.*)"+extensions,
	"(.*?)(\\d{3,4}p)(.*)"}
var log *logger.Logger = logger.SetupNew("MAIN")

// Returns true if path exists
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}

// Return true if path is a file
func isFile(path string) bool {
	file, err := os.Stat(path)
	if err == nil && file.IsDir() != true {
		return true
	}
	return false
}

// Checks wether a given filename is considered to be a movie or series
// based on the specified regexp patterns
func isMovie(filename string, pattern []string) (*Movie, error) {
	for index, element := range pattern {
		_ = index
		r, err := regexp.Compile(element)
		if err == nil {
			match := r.MatchString(filename)
			if match {
				result := r.FindStringSubmatch(filename)
				movie := &Movie{strings.Trim(result[1], "."), strings.Trim(result[2], "."), filename}
				return movie, nil
			}
		} else {
			return nil, err
		}
	}
	return nil, nil
}

func isSerie(filename string, pattern []string) (*Serie, error) {
	for index, element := range pattern {
		_ = index
		r, err := regexp.Compile(element)
		if err == nil {
			match := r.MatchString(filename)
			if match {
				result := r.FindStringSubmatch(filename)
				serie := &Serie{strings.Trim(result[1], "."), strings.Trim(result[2], "."), strings.Trim(result[3], "."), filename}
				return serie, nil
			}
		} else {
			return nil, err
		}
	}
	return nil, nil
}

// Converts file size from bytes to kb, mb, gb or tb
func convertFileSize(size int64, unit string) int64 {
	var result int64
	switch unit {
		case "k":
			result = size / 1024
		case "m":
			result = (size / 1024) / 1024
		case "g":
			result = ((size / 1024) / 1024) / 1024
		case "t":
			result = (((size / 1024) / 1024) / 1024) / 1024
		default:
			result = ((size / 1024) / 1024) / 1024
	}
	return result
}

// Confirm prompt. Accept y/n from user. Returns true or false
func confirmCopy(msg string) bool {

	fmt.Println(msg)

	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		log.Error(err)
	}

	// Lowercase response. Remove whitespace
	response = strings.ToLower(response)
	response = strings.Trim(response, " ")

	r := string(response[0])
	if r == "y" {
		return true
	} else if r == "n" {
		return false
	} else {
		fmt.Println("Please type yes or no")
		return confirmCopy(msg)
	}
}

// Copy file from src to dst. if list is true then content of src is not copied
func copyFile(src, dst string, overwrite, list bool) (int64, error) {
	var written int64
	if !listmode {
		written, err = copy.CopyFile(src, dst, overwrite)
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// Copy file from src to dst. if list is true then content of src is not copied
func copyDir(src, dst string, overwrite, list bool) (int64, error) {
	var written int64
	if !listmode {
		written, err = copy.CopyDir(src, dst, overwrite)
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// Main loop
func main() {

	// Read arguments
	flag.StringVar(&mroot, "m", ".", "Directory to your movies")
	flag.StringVar(&sroot, "s", ".", "Directory to your series")
	flag.StringVar(&target, "t", ".", "Target directory. Typically your Downloads folder")
	flag.BoolVar(&overwrite, "o", false, "Overwrite existing files/folders when copying")
	flag.BoolVar(&confirm, "c", false, "Prompt for confirm when overwriting existing files/folders")
	flag.BoolVar(&listmode, "S", false, "List found media. Don't perform any copy")
	flag.IntVar(&loglevel, "v", 0, "Log level. 3=DEBUG, 2=WARN, 1=INFO, 0=DEBUG. (default \"0\")")
	flag.StringVar(&unit, "u", "g", "String representation of unit to use when calculating file sizes. Choices are k, m, g and t")
	flag.Parse()

	// Sets the loglevel.
	// First we need to read from args and convert it to an int
	log.Level.SetLevel(loglevel)
	log.Debug("Log level is", loglevel)

	// Check if movies root exists
	if !exists(mroot) {
		log.Error("Does not exist", mroot)
		os.Exit(1)
	}
	// Check if series root exists
	if !exists(sroot) {
		log.Error("Does not exist", sroot)
		os.Exit(1)
	}
	// Check if movies root is a directory
	if isFile(mroot) {
		log.Error("Is not a directory", mroot)
		os.Exit(1)
	}
	// Check if series root is a directory
	if isFile(sroot) {
		log.Error("Is not a directory", sroot)
		os.Exit(1)
	}
	// Check if target exists
	if !exists(target) {
		log.Error("Does not exist", target)
		os.Exit(1)
	}
	// Check if target is a directory
	if isFile(target) {
		log.Error("Is not a directory", target)
		os.Exit(1)
	}

	// Main
	var mmatches []string
	var smatches []string
	f, err := ioutil.ReadDir(target)
	log.Debug("Overwrite is set to", overwrite)
	log.Debug("Looking in", target)
	var totalWritten int64
	for i, file := range f {
		log.Debug("Checking", file.Name())

		// Check for movies
		movie, errM := isMovie(file.Name(), mpattern)
		if movie != nil {
			if errM == nil {
				log.Debug("==== MOVIE START ====")
				log.Debug("Movie", movie.title, movie.year, movie.filename, i, err)
				mmatches = append(mmatches, file.Name())
				srcf := path.Join(target, file.Name())
				if confirm == true {
					overwrite = confirmCopy("Overwrite? (y/n) " + file.Name())
				}
				var written int64
				if isFile(srcf) {
					dstf := path.Join(mroot, file.Name())
					log.Debug("Dest is", dstf)
					log.Debug("Is file", dstf)
					written, err = copyFile(srcf, path.Join(mroot, file.Name()), overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				} else {
					dstf := path.Join(mroot, file.Name())
					log.Debug("Is dir", dstf)
					written, err = copyDir(srcf, path.Join(mroot, file.Name()), overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				}
			}
			log.Debug("==== MOVIE END ====")
		}

		// Check for series
		serie, errS := isSerie(file.Name(), spattern)
		if serie != nil {
			if errS == nil {
				log.Debug("==== MOVIE START ====")
				log.Debug("Serie", serie.title, serie.season, serie.episode, serie.filename)
				var written int64
				smatches = append(smatches, file.Name())
				if confirm == true {
					overwrite = confirmCopy("Overwrite? (y/n) " + file.Name())
				}
				srcf := path.Join(target, file.Name())
				dstFolder := path.Join(sroot, serie.title, "Season"+serie.season)
				if !exists(dstFolder) {
						log.Debug("Dest does not exist, creating", dstFolder)
						err = os.MkdirAll(dstFolder, 0700)
						if err != nil {
							log.Error("Couldn't create", dstFolder, err)
						}
				}
				dstf := path.Join(dstFolder, file.Name())
				log.Debug("Dest is", dstf)
				if isFile(srcf) {
					log.Debug("Is file", srcf)
					written, err = copyFile(srcf, dstf, overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				} else {
					log.Debug("Is dir", srcf)
					written, err = copyDir(srcf, dstf, overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				}
			}
			log.Debug("==== SERIE END ====")
		}
	}

	log.Debug("Movies matched in dir is", len(mmatches))
	log.Debug("Series matched in dir is", len(smatches))
	fmt.Println("Copied", convertFileSize(totalWritten, unit), unit)

}
<commit_msg>List mode (-l) correctly prints out matched media<commit_after>package main

/**
*
* Author: Amir Mofasser <amir.mofasser@gmail.com>
*	https://github.com/amimof
*
*/

import (
	copy "github.com/amimof/lazycopy/copy"
	logger "github.com/amimof/lazycopy/logger"
	"io/ioutil"
	"os"
	"regexp"
	"flag"
	"path"
	"fmt"
	"strings"
)

var (
	prog string
	mroot string
	sroot string
	target string
	unit string
	overwrite bool
	confirm bool
	listmode bool
	loglevel int
)

type Movie struct {
	title string
	year string
	filename string
}

type Serie struct {
	title string
	season string
	episode string
	filename string
}

// Extensions in regexp disabled atm since it prevents us from detecting folders
// var extensions string = "\\.(mkv|MKV|mp4|MP4|m4p|M4P|m4v|M4V|mpg|MPG|mpeg|MPEG|mp2|MP2|mpe|MPE|mpv|MPV|3gp|3GP|nsv|NSV|f4v|F4V|f4p|F4P|f4a|F4A|f4b|F4P|vob|VOB|avi|AVI|mov|MOV|wmv|WMV|asd|ASD|flv|FLV|ogv|OGV|ogg|OGG|qt|QT|yuv|YUV|rm|RM|rmvb|RMVB)"
var extensions string = ""
var spattern []string = []string{"(.*?)S(\\d{1,2})E(\\d{2})(.*)"+extensions,
	"(.*?)s(\\d{1,2})e(\\d{2})(.*)"+extensions,
	"(.*?)\\[?(\\d{1,2})x(\\d{2})\\]?(.*)"+extensions,
	"(.*?)Season.?(\\d{1,2}).*?Episode.?(\\d{1,2})(.*)"+extensions}
var mpattern []string = []string{"(.*?)\\((17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\)(.*)"+extensions,
	"(.*?)\\[(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\](.*)"+extensions,
	"(.*?)\\{(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])\\}(.*)"+extensions,
	"(.*?)(17[0-9][0-9]|180[0-9]|181[0-9]|18[2-9]\\d|19\\d\\d|2\\d{3}|30[0-3]\\d|304[0-8])(.*)"+extensions,
	"(.*?)(\\d{3,4}p)(.*)"}
var log *logger.Logger = logger.SetupNew("MAIN")

// Returns true if path exists
func exists(path string) bool {
	_, err := os.Stat(path)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return true
}

// Return true if path is a file
func isFile(path string) bool {
	file, err := os.Stat(path)
	if err == nil && file.IsDir() != true {
		return true
	}
	return false
}

// Checks wether a given filename is considered to be a movie or series
// based on the specified regexp patterns
func isMovie(filename string, pattern []string) (*Movie, error) {
	for index, element := range pattern {
		_ = index
		r, err := regexp.Compile(element)
		if err == nil {
			match := r.MatchString(filename)
			if match {
				result := r.FindStringSubmatch(filename)
				movie := &Movie{strings.Trim(result[1], "."), strings.Trim(result[2], "."), filename}
				return movie, nil
			}
		} else {
			return nil, err
		}
	}
	return nil, nil
}

func isSerie(filename string, pattern []string) (*Serie, error) {
	for index, element := range pattern {
		_ = index
		r, err := regexp.Compile(element)
		if err == nil {
			match := r.MatchString(filename)
			if match {
				result := r.FindStringSubmatch(filename)
				serie := &Serie{strings.Trim(result[1], "."), strings.Trim(result[2], "."), strings.Trim(result[3], "."), filename}
				return serie, nil
			}
		} else {
			return nil, err
		}
	}
	return nil, nil
}

// Converts file size from bytes to kb, mb, gb or tb
func convertFileSize(size int64, unit string) int64 {
	var result int64
	switch unit {
		case "k":
			result = size / 1024
		case "m":
			result = (size / 1024) / 1024
		case "g":
			result = ((size / 1024) / 1024) / 1024
		case "t":
			result = (((size / 1024) / 1024) / 1024) / 1024
		default:
			result = ((size / 1024) / 1024) / 1024
	}
	return result
}

// Confirm prompt. Accept y/n from user. Returns true or false
func confirmCopy(msg string) bool {

	fmt.Println(msg)

	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		log.Error(err)
	}

	// Lowercase response. Remove whitespace
	response = strings.ToLower(response)
	response = strings.Trim(response, " ")

	r := string(response[0])
	if r == "y" {
		return true
	} else if r == "n" {
		return false
	} else {
		fmt.Println("Please type yes or no")
		return confirmCopy(msg)
	}
}

// Copy file from src to dst. if list is true then content of src is not copied
func copyFile(src, dst string, overwrite bool) (int64, error) {
	var written int64
	if !listmode {
		written, err := copy.CopyFile(src, dst, overwrite)
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// Copy file from src to dst. if list is true then content of src is not copied
func copyDir(src, dst string, overwrite bool) (int64, error) {
	var written int64
	if !listmode {
		written, err := copy.CopyDir(src, dst, overwrite)
		if err != nil {
			return written, err
		}
	}
	return written, nil
}

// Main loop
func main() {

	// Read arguments
	flag.StringVar(&mroot, "m", ".", "Directory to your movies")
	flag.StringVar(&sroot, "s", ".", "Directory to your series")
	flag.StringVar(&target, "t", ".", "Target directory. Typically your Downloads folder")
	flag.BoolVar(&overwrite, "o", false, "Overwrite existing files/folders when copying")
	flag.BoolVar(&confirm, "c", false, "Prompt for confirm when overwriting existing files/folders")
	flag.BoolVar(&listmode, "l", false, "List found media. Don't perform any copy")
	flag.IntVar(&loglevel, "v", 0, "Log level. 3=DEBUG, 2=WARN, 1=INFO, 0=DEBUG. (default \"0\")")
	flag.StringVar(&unit, "u", "g", "String representation of unit to use when calculating file sizes. Choices are k, m, g and t")
	flag.Parse()

	// Sets the loglevel.
	// First we need to read from args and convert it to an int
	log.Level.SetLevel(loglevel)
	log.Debug("Log level is", loglevel)

	// Check if movies root exists
	if !exists(mroot) {
		log.Error("Does not exist", mroot)
		os.Exit(1)
	}
	// Check if series root exists
	if !exists(sroot) {
		log.Error("Does not exist", sroot)
		os.Exit(1)
	}
	// Check if movies root is a directory
	if isFile(mroot) {
		log.Error("Is not a directory", mroot)
		os.Exit(1)
	}
	// Check if series root is a directory
	if isFile(sroot) {
		log.Error("Is not a directory", sroot)
		os.Exit(1)
	}
	// Check if target exists
	if !exists(target) {
		log.Error("Does not exist", target)
		os.Exit(1)
	}
	// Check if target is a directory
	if isFile(target) {
		log.Error("Is not a directory", target)
		os.Exit(1)
	}

	// Main
	var mmatches []string
	var smatches []string
	f, err := ioutil.ReadDir(target)
	log.Debug("Overwrite is set to", overwrite)
	log.Debug("List mode is set to", listmode)
	log.Debug("Looking in", target)
	var totalWritten int64
	for i, file := range f {
		log.Debug("Checking", file.Name())

		// Check for movies
		movie, errM := isMovie(file.Name(), mpattern)
		if movie != nil {
			if errM == nil {
				if listmode {
					fmt.Println("Matches movie:", file.Name())
				}
				log.Debug("==== MOVIE START ====")
				log.Debug("Movie", movie.title, movie.year, movie.filename, i, err)
				mmatches = append(mmatches, file.Name())
				srcf := path.Join(target, file.Name())
				if confirm == true {
					overwrite = confirmCopy("Overwrite? (y/n) " + file.Name())
				}
				var written int64
				if isFile(srcf) {
					dstf := path.Join(mroot, file.Name())
					log.Debug("Dest is", dstf)
					log.Debug("Is file", dstf)
					written, err = copyFile(srcf, path.Join(mroot, file.Name()), overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				} else {
					dstf := path.Join(mroot, file.Name())
					log.Debug("Is dir", dstf)
					written, err = copyDir(srcf, path.Join(mroot, file.Name()), overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				}
			}
			log.Debug("==== MOVIE END ====")
		}

		// Check for series
		serie, errS := isSerie(file.Name(), spattern)
		if serie != nil {
			if errS == nil {
				if listmode {
					fmt.Println("Matches serie:", file.Name())
				}
				log.Debug("==== MOVIE START ====")
				log.Debug("Serie", serie.title, serie.season, serie.episode, serie.filename)
				var written int64
				smatches = append(smatches, file.Name())
				if confirm == true {
					overwrite = confirmCopy("Overwrite? (y/n) " + file.Name())
				}
				srcf := path.Join(target, file.Name())
				dstFolder := path.Join(sroot, serie.title, "Season"+serie.season)
				if !exists(dstFolder) {
						log.Debug("Dest does not exist, creating", dstFolder)
						err = os.MkdirAll(dstFolder, 0700)
						if err != nil {
							log.Error("Couldn't create", dstFolder, err)
						}
				}
				dstf := path.Join(dstFolder, file.Name())
				log.Debug("Dest is", dstf)
				if isFile(srcf) {
					log.Debug("Is file", srcf)
					written, err = copyFile(srcf, dstf, overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				} else {
					log.Debug("Is dir", srcf)
					written, err = copyDir(srcf, dstf, overwrite)
					if err != nil {
						log.Error("Can't copy", file.Name(), err)
					}
					totalWritten = totalWritten + written
				}
			}
			log.Debug("==== SERIE END ====")
		}
	}

	log.Debug("Movies matched in dir is", len(mmatches))
	log.Debug("Series matched in dir is", len(smatches))
	fmt.Println("Copied", convertFileSize(totalWritten, unit), unit)

}
<|endoftext|>


<commit_before>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 */
package main

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/ui/qt/webengine"
	"github.com/obscuren/qml"
)

const (
	ClientIdentifier = "Mist"
	Version          = "0.9.0"
)

var (
	gitCommit       string // set via linker flag
	nodeNameVersion string

	app           = utils.NewApp(Version, "the ether browser")
	assetPathFlag = cli.StringFlag{
		Name:  "asset_path",
		Usage: "absolute path to GUI assets directory",
		Value: common.DefaultAssetPath(),
	}
	rpcCorsFlag = utils.RPCCORSDomainFlag
)

func init() {
	// Mist-specific default
	if len(rpcCorsFlag.Value) == 0 {
		rpcCorsFlag.Value = "http://localhost"
	}
	if gitCommit == "" {
		nodeNameVersion = Version
	} else {
		nodeNameVersion = Version + "-" + gitCommit[:8]
	}

	app.Action = run
	app.Flags = []cli.Flag{
		assetPathFlag,
		rpcCorsFlag,

		utils.BootnodesFlag,
		utils.DataDirFlag,
		utils.ListenPortFlag,
		utils.LogFileFlag,
		utils.LogLevelFlag,
		utils.MaxPeersFlag,
		utils.MinerThreadsFlag,
		utils.NATFlag,
		utils.NodeKeyFileFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
		utils.JSpathFlag,
		utils.ProtocolVersionFlag,
		utils.BlockchainVersionFlag,
		utils.NetworkIdFlag,
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// This is a bit of a cheat, but ey!
	os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")

	var interrupted = false
	utils.RegisterInterrupt(func(os.Signal) {
		interrupted = true
	})
	utils.HandleInterrupt()

	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, "Error: ", err)
	}

	// we need to run the interrupt callbacks in case gui is closed
	// this skips if we got here by actual interrupt stopping the GUI
	if !interrupted {
		utils.RunInterruptCallbacks(os.Interrupt)
	}
	logger.Flush()
}

func run(ctx *cli.Context) {
	tstart := time.Now()

	// TODO: show qml popup instead of exiting if initialization fails.
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v", err)
	}
	utils.StartRPC(ethereum, ctx)
	go utils.StartEthereum(ethereum)
	fmt.Println("initializing eth stack took", time.Since(tstart))

	// Open the window
	qml.Run(func() error {
		webengine.Initialize()
		gui := NewWindow(ethereum)
		utils.RegisterInterrupt(func(os.Signal) { gui.Stop() })
		// gui blocks the main thread
		gui.Start(ctx.GlobalString(assetPathFlag.Name), ctx.GlobalString(utils.JSpathFlag.Name))
		return nil
	})
}
<commit_msg>cmd/mist: always enable whisper<commit_after>/*
	This file is part of go-ethereum

	go-ethereum is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	go-ethereum is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with go-ethereum.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
 * @authors
 * 	Jeffrey Wilcke <i@jev.io>
 */
package main

import (
	"fmt"
	"os"
	"runtime"
	"time"

	"github.com/codegangsta/cli"
	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/eth"
	"github.com/ethereum/go-ethereum/logger"
	"github.com/ethereum/go-ethereum/ui/qt/webengine"
	"github.com/obscuren/qml"
)

const (
	ClientIdentifier = "Mist"
	Version          = "0.9.0"
)

var (
	gitCommit       string // set via linker flag
	nodeNameVersion string

	app           = utils.NewApp(Version, "the ether browser")
	assetPathFlag = cli.StringFlag{
		Name:  "asset_path",
		Usage: "absolute path to GUI assets directory",
		Value: common.DefaultAssetPath(),
	}
	rpcCorsFlag = utils.RPCCORSDomainFlag
)

func init() {
	// Mist-specific default
	if len(rpcCorsFlag.Value) == 0 {
		rpcCorsFlag.Value = "http://localhost"
	}
	if gitCommit == "" {
		nodeNameVersion = Version
	} else {
		nodeNameVersion = Version + "-" + gitCommit[:8]
	}

	app.Action = run
	app.Flags = []cli.Flag{
		assetPathFlag,
		rpcCorsFlag,

		utils.BootnodesFlag,
		utils.DataDirFlag,
		utils.ListenPortFlag,
		utils.LogFileFlag,
		utils.LogLevelFlag,
		utils.MaxPeersFlag,
		utils.MinerThreadsFlag,
		utils.NATFlag,
		utils.NodeKeyFileFlag,
		utils.RPCListenAddrFlag,
		utils.RPCPortFlag,
		utils.JSpathFlag,
		utils.ProtocolVersionFlag,
		utils.BlockchainVersionFlag,
		utils.NetworkIdFlag,
	}
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// This is a bit of a cheat, but ey!
	os.Setenv("QTWEBKIT_INSPECTOR_SERVER", "127.0.0.1:99999")

	var interrupted = false
	utils.RegisterInterrupt(func(os.Signal) {
		interrupted = true
	})
	utils.HandleInterrupt()

	if err := app.Run(os.Args); err != nil {
		fmt.Fprintln(os.Stderr, "Error: ", err)
	}

	// we need to run the interrupt callbacks in case gui is closed
	// this skips if we got here by actual interrupt stopping the GUI
	if !interrupted {
		utils.RunInterruptCallbacks(os.Interrupt)
	}
	logger.Flush()
}

func run(ctx *cli.Context) {
	tstart := time.Now()

	// TODO: show qml popup instead of exiting if initialization fails.
	cfg := utils.MakeEthConfig(ClientIdentifier, nodeNameVersion, ctx)
	cfg.Shh = true
	ethereum, err := eth.New(cfg)
	if err != nil {
		utils.Fatalf("%v", err)
	}
	utils.StartRPC(ethereum, ctx)
	go utils.StartEthereum(ethereum)
	fmt.Println("initializing eth stack took", time.Since(tstart))

	// Open the window
	qml.Run(func() error {
		webengine.Initialize()
		gui := NewWindow(ethereum)
		utils.RegisterInterrupt(func(os.Signal) { gui.Stop() })
		// gui blocks the main thread
		gui.Start(ctx.GlobalString(assetPathFlag.Name), ctx.GlobalString(utils.JSpathFlag.Name))
		return nil
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/moby/tool/src/moby"
	log "github.com/sirupsen/logrus"
)

var (
	defaultLogFormatter = &log.TextFormatter{}

	// Version is the human-readable version
	Version = "unknown"

	// GitCommit hash, set at compile time
	GitCommit = "unknown"
)

// infoFormatter overrides the default format for Info() log events to
// provide an easier to read output
type infoFormatter struct {
}

func (f *infoFormatter) Format(entry *log.Entry) ([]byte, error) {
	if entry.Level == log.InfoLevel {
		return append([]byte(entry.Message), '\n'), nil
	}
	return defaultLogFormatter.Format(entry)
}

func version() {
	fmt.Printf("%s version %s\n", filepath.Base(os.Args[0]), Version)
	fmt.Printf("commit: %s\n", GitCommit)
	os.Exit(0)
}

const mobyDefaultDir string = ".moby"

func defaultMobyConfigDir() string {
	home := homeDir()
	return filepath.Join(home, mobyDefaultDir)
}

func main() {
	flag.Usage = func() {
		fmt.Printf("USAGE: %s [options] COMMAND\n\n", filepath.Base(os.Args[0]))
		fmt.Printf("Commands:\n")
		fmt.Printf("  build       Build a Moby image from a YAML file\n")
		fmt.Printf("  version     Print version information\n")
		fmt.Printf("  help        Print this message\n")
		fmt.Printf("\n")
		fmt.Printf("Run '%s COMMAND --help' for more information on the command\n", filepath.Base(os.Args[0]))
		fmt.Printf("\n")
		fmt.Printf("Options:\n")
		flag.PrintDefaults()
	}
	flagQuiet := flag.Bool("q", false, "Quiet execution")
	flagVerbose := flag.Bool("v", false, "Verbose execution")

	// config and cache directory
	flagConfigDir := flag.String("config", defaultMobyConfigDir(), "Configuration directory")

	// Set up logging
	log.SetFormatter(new(infoFormatter))
	log.SetLevel(log.InfoLevel)

	flag.Parse()
	if *flagQuiet && *flagVerbose {
		fmt.Printf("Can't set quiet and verbose flag at the same time\n")
		os.Exit(1)
	}
	if *flagQuiet {
		log.SetLevel(log.ErrorLevel)
	}
	if *flagVerbose {
		// Switch back to the standard formatter
		log.SetFormatter(defaultLogFormatter)
		log.SetLevel(log.DebugLevel)
	}

	args := flag.Args()
	if len(args) < 1 {
		fmt.Printf("Please specify a command.\n\n")
		flag.Usage()
		os.Exit(1)
	}

	mobyDir := *flagConfigDir
	err := os.MkdirAll(mobyDir, 0755)
	if err != nil {
		log.Fatalf("Could not create config directory [%s]: %v", mobyDir, err)
	}

	err = os.MkdirAll(filepath.Join(mobyDir, "tmp"), 0755)
	if err != nil {
		log.Fatalf("Could not create config tmp directory [%s]: %v", filepath.Join(mobyDir, "tmp"), err)
	}
	moby.MobyDir = mobyDir

	switch args[0] {
	case "build":
		build(args[1:])
	case "version":
		version()
	case "help":
		flag.Usage()
	default:
		fmt.Printf("%q is not valid command.\n\n", args[0])
		flag.Usage()
		os.Exit(1)
	}
}
<commit_msg>Add cpu and mem profiling options<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"runtime/pprof"

	"github.com/moby/tool/src/moby"
	log "github.com/sirupsen/logrus"
)

var (
	defaultLogFormatter = &log.TextFormatter{}

	// Version is the human-readable version
	Version = "unknown"

	// GitCommit hash, set at compile time
	GitCommit = "unknown"
)

// infoFormatter overrides the default format for Info() log events to
// provide an easier to read output
type infoFormatter struct {
}

func (f *infoFormatter) Format(entry *log.Entry) ([]byte, error) {
	if entry.Level == log.InfoLevel {
		return append([]byte(entry.Message), '\n'), nil
	}
	return defaultLogFormatter.Format(entry)
}

func version() {
	fmt.Printf("%s version %s\n", filepath.Base(os.Args[0]), Version)
	fmt.Printf("commit: %s\n", GitCommit)
	os.Exit(0)
}

const mobyDefaultDir string = ".moby"

func defaultMobyConfigDir() string {
	home := homeDir()
	return filepath.Join(home, mobyDefaultDir)
}

func main() {
	flag.Usage = func() {
		fmt.Printf("USAGE: %s [options] COMMAND\n\n", filepath.Base(os.Args[0]))
		fmt.Printf("Commands:\n")
		fmt.Printf("  build       Build a Moby image from a YAML file\n")
		fmt.Printf("  version     Print version information\n")
		fmt.Printf("  help        Print this message\n")
		fmt.Printf("\n")
		fmt.Printf("Run '%s COMMAND --help' for more information on the command\n", filepath.Base(os.Args[0]))
		fmt.Printf("\n")
		fmt.Printf("Options:\n")
		flag.PrintDefaults()
	}
	flagQuiet := flag.Bool("q", false, "Quiet execution")
	flagVerbose := flag.Bool("v", false, "Verbose execution")
	flagCPUProfile := flag.String("cpuprofile", "", "write cpu profile to `file`")
	flagMemProfile := flag.String("memprofile", "", "write mem profile to `file`")

	// config and cache directory
	flagConfigDir := flag.String("config", defaultMobyConfigDir(), "Configuration directory")

	// Set up logging
	log.SetFormatter(new(infoFormatter))
	log.SetLevel(log.InfoLevel)

	flag.Parse()
	if *flagQuiet && *flagVerbose {
		fmt.Printf("Can't set quiet and verbose flag at the same time\n")
		os.Exit(1)
	}
	if *flagQuiet {
		log.SetLevel(log.ErrorLevel)
	}
	if *flagVerbose {
		// Switch back to the standard formatter
		log.SetFormatter(defaultLogFormatter)
		log.SetLevel(log.DebugLevel)
	}

	args := flag.Args()
	if len(args) < 1 {
		fmt.Printf("Please specify a command.\n\n")
		flag.Usage()
		os.Exit(1)
	}

	mobyDir := *flagConfigDir
	err := os.MkdirAll(mobyDir, 0755)
	if err != nil {
		log.Fatalf("Could not create config directory [%s]: %v", mobyDir, err)
	}

	err = os.MkdirAll(filepath.Join(mobyDir, "tmp"), 0755)
	if err != nil {
		log.Fatalf("Could not create config tmp directory [%s]: %v", filepath.Join(mobyDir, "tmp"), err)
	}
	moby.MobyDir = mobyDir

	if *flagCPUProfile != "" {
		f, err := os.Create(*flagCPUProfile)
		if err != nil {
			log.Fatal("could not create CPU profile: ", err)
		}
		if err := pprof.StartCPUProfile(f); err != nil {
			log.Fatal("could not start CPU profile: ", err)
		}
		defer pprof.StopCPUProfile()
	}

	switch args[0] {
	case "build":
		build(args[1:])
	case "version":
		version()
	case "help":
		flag.Usage()
	default:
		fmt.Printf("%q is not valid command.\n\n", args[0])
		flag.Usage()
		os.Exit(1)
	}

	if *flagMemProfile != "" {
		f, err := os.Create(*flagMemProfile)
		if err != nil {
			log.Fatal("could not create memory profile: ", err)
		}
		runtime.GC() // get up-to-date statistics
		if err := pprof.WriteHeapProfile(f); err != nil {
			log.Fatal("could not write memory profile: ", err)
		}
		f.Close()
	}
}
<|endoftext|>


<commit_before>package cmd

import "testing"

func TestFindProjectroot(t *testing.T) {
	tests := []struct {
		path   string
		gopath []string
		want   string
		err    error
	}{{
		path: "/home/foo/work/project/src",
		want: "/home/foo/word/project",
	}}

	for _, tt := range tests {
		got, err := FindProjectroot(tt.path, tt.gopath)
		if got != tt.want || err != tt.err {
			t.Errorf("FindProjectroot(%v, %v): want: %v, %v, got %v, %v", tt.path, tt.gopath, tt.want, tt.err, got, err)
		}
	}
}
<commit_msg>Fixed gc/cmd tests<commit_after>package cmd

import "testing"

// disabled, FindProjectRoot uses os.Stat
func testFindProjectroot(t *testing.T) {
	tests := []struct {
		path   string
		gopath []string
		want   string
		err    error
	}{{
		path: "/home/foo/work/project/src",
		want: "/home/foo/work/project",
	}}

	for _, tt := range tests {
		got, err := FindProjectroot(tt.path, tt.gopath)
		if got != tt.want || err != tt.err {
			t.Errorf("FindProjectroot(%v, %v): want: %v, %v, got %v, %v", tt.path, tt.gopath, tt.want, tt.err, got, err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/go-percol"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: percol [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --query=QUERY         pre-input query
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help  bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY   string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query string `long:"query"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			os.Stdout.WriteString(result)
		}
	}()

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		os.Exit(1)
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	go view.Loop()
	go filter.Loop()
	go input.Loop()

	if len(opts.Query) > 0 {
		ctx.ExecQuery(string(string(opts.Query)))
	} else {
		view.Refresh()
	}

	ctx.WaitDone()
	ctx.PrintResult()
}
<commit_msg>fix import path<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/peco"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: percol [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --query=QUERY         pre-input query
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help  bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY   string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query string `long:"query"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var in *os.File

	// receive in from either a file or Stdin
	if len(args) > 0 {
		in, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !peco.IsTty() {
		in = os.Stdin
	}

	ctx := peco.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			os.Stdout.WriteString(result)
		}
	}()

	if err = ctx.ReadBuffer(in); err != nil {
		// Nothing to process, bail out
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		os.Exit(1)
	}

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer termbox.Close()

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()

	go view.Loop()
	go filter.Loop()
	go input.Loop()

	if len(opts.Query) > 0 {
		ctx.ExecQuery(string(string(opts.Query)))
	} else {
		view.Refresh()
	}

	ctx.WaitDone()
	ctx.PrintResult()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/jessevdk/go-flags"
	"github.com/nsf/termbox-go"
	"github.com/peco/peco"
)

var version = "v0.2.3"

func showHelp() {
	const v = `
Usage: peco [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --version             print the version and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
  -b, --buffer-size     number of lines to keep in search buffer
  --null                expect NUL (\0) as separator for target/output (EXPERIMENTAL)
  --initial-index       position of the initial index of the selection (0 base)
  --prompt              specify prompt
`
	os.Stderr.Write([]byte(v))
}

type cmdOptions struct {
	OptHelp          bool   `short:"h" long:"help" description:"show this help message and exit"`
	OptTTY           string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	OptQuery         string `long:"query"`
	OptRcfile        string `long:"rcfile" descriotion:"path to the settings file"`
	OptNoIgnoreCase  bool   `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
	OptVersion       bool   `long:"version" description:"print the version and exit"`
	OptBufferSize    int    `long:"buffer-size" short:"b" description:"number of lines to keep in search buffer"`
	OptEnableNullSep bool   `long:"null" description:"expect NUL (\\0) as separator for target/output"`
	OptInitialIndex  int    `long:"initial-index" description:"position of the initial index of the selection (0 base)"`
	OptPrompt        string `long:"prompt"`
}

// BufferSize returns the specified buffer size. Fulfills peco.CtxOptions
func (o cmdOptions) BufferSize() int {
	return o.OptBufferSize
}

// EnableNullSep returns tru if --null was specified. Fulfills peco.CtxOptions
func (o cmdOptions) EnableNullSep() bool {
	return o.OptEnableNullSep
}

func (o cmdOptions) InitialIndex() int {
	if o.OptInitialIndex >= 0 {
		return o.OptInitialIndex + 1
	}
	return 1
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.OptHelp {
		showHelp()
		return
	}

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	switch {
	case len(args) > 0:
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			fmt.Fprintln(os.Stderr, err)
			return
		}
	case !peco.IsTty(os.Stdin.Fd()):
		in = os.Stdin
	default:
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	ctx := peco.NewCtx(opts)
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, match := range result {
				line := match.Output()
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.OptRcfile == "" {
		file, err := peco.LocateRcfile()
		if err == nil {
			opts.OptRcfile = file
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.OptRcfile != "" {
		err = ctx.ReadConfig(opts.OptRcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if opts.OptNoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	// Try waiting for something available in the source stream
	// before doing any terminal initialization (also done by termbox)
	reader := ctx.NewBufferReader(in)
	ctx.AddWaitGroup(1)
	go reader.Loop()

	// This channel blocks until we receive something from `in`
	<-reader.InputReadyCh()

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	// Windows handle Esc/Alt self
	if runtime.GOOS == "windows" {
		termbox.SetInputMode(termbox.InputEsc | termbox.InputAlt)
	}

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()
	sig := ctx.NewSignalHandler()

	loopers := []interface {
		Loop()
	}{
		view,
		filter,
		input,
		sig,
	}
	for _, looper := range loopers {
		ctx.AddWaitGroup(1)
		go looper.Loop()
	}

	if len(opts.OptQuery) > 0 {
		ctx.SetQuery([]rune(opts.OptQuery))
		ctx.ExecQuery()
	} else {
		view.Refresh()
	}

	if len(opts.OptPrompt) > 0 {
		ctx.SetPrompt([]rune(opts.OptPrompt))
	}

	ctx.WaitDone()

	st = ctx.ExitStatus
}
<commit_msg>Initial matcher<commit_after>package main

import (
	"fmt"
	"os"
	"runtime"

	"github.com/jessevdk/go-flags"
	"github.com/nsf/termbox-go"
	"github.com/peco/peco"
)

var version = "v0.2.3"

func showHelp() {
	const v = `
Usage: peco [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --version             print the version and exit
  --rcfile=RCFILE       path to the settings file
  --query=QUERY         pre-input query
  --no-ignore-case      start in case-sensitive mode
  -b, --buffer-size     number of lines to keep in search buffer
  --null                expect NUL (\0) as separator for target/output (EXPERIMENTAL)
  --initial-index       position of the initial index of the selection (0 base)
  --initial-matcher     specify default matcher
  --prompt              specify prompt
`
	os.Stderr.Write([]byte(v))
}

type cmdOptions struct {
	OptHelp           bool   `short:"h" long:"help" description:"show this help message and exit"`
	OptTTY            string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	OptQuery          string `long:"query"`
	OptRcfile         string `long:"rcfile" descriotion:"path to the settings file"`
	OptNoIgnoreCase   bool   `long:"no-ignore-case" description:"start in case-sensitive-mode" default:"false"`
	OptVersion        bool   `long:"version" description:"print the version and exit"`
	OptBufferSize     int    `long:"buffer-size" short:"b" description:"number of lines to keep in search buffer"`
	OptEnableNullSep  bool   `long:"null" description:"expect NUL (\\0) as separator for target/output"`
	OptInitialIndex   int    `long:"initial-index" description:"position of the initial index of the selection (0 base)"`
	OptInitialMatcher string `long:"initial-matcher" description:"matcher"`
	OptPrompt         string `long:"prompt"`
}

// BufferSize returns the specified buffer size. Fulfills peco.CtxOptions
func (o cmdOptions) BufferSize() int {
	return o.OptBufferSize
}

// EnableNullSep returns tru if --null was specified. Fulfills peco.CtxOptions
func (o cmdOptions) EnableNullSep() bool {
	return o.OptEnableNullSep
}

func (o cmdOptions) InitialIndex() int {
	if o.OptInitialIndex >= 0 {
		return o.OptInitialIndex + 1
	}
	return 1
}

func main() {
	var err error
	var st int

	defer func() { os.Exit(st) }()

	if envvar := os.Getenv("GOMAXPROCS"); envvar == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}

	opts := &cmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse()
	if err != nil {
		showHelp()
		st = 1
		return
	}

	if opts.OptHelp {
		showHelp()
		return
	}

	if opts.OptVersion {
		fmt.Fprintf(os.Stderr, "peco: %s\n", version)
		return
	}

	var in *os.File

	// receive in from either a file or Stdin
	switch {
	case len(args) > 0:
		in, err = os.Open(args[0])
		if err != nil {
			st = 1
			fmt.Fprintln(os.Stderr, err)
			return
		}
	case !peco.IsTty(os.Stdin.Fd()):
		in = os.Stdin
	default:
		fmt.Fprintln(os.Stderr, "You must supply something to work with via filename or stdin")
		st = 1
		return
	}

	ctx := peco.NewCtx(opts)
	defer func() {
		if err := recover(); err != nil {
			st = 1
			fmt.Fprintf(os.Stderr, "Error:\n%s", err)
		}

		if result := ctx.Result(); result != nil {
			for _, match := range result {
				line := match.Output()
				if line[len(line)-1] != '\n' {
					line = line + "\n"
				}
				fmt.Fprint(os.Stdout, line)
			}
		}
	}()

	if opts.OptRcfile == "" {
		file, err := peco.LocateRcfile()
		if err == nil {
			opts.OptRcfile = file
		}
	}

	// Default matcher is IgnoreCase
	ctx.SetCurrentMatcher(peco.IgnoreCaseMatch)

	if opts.OptRcfile != "" {
		err = ctx.ReadConfig(opts.OptRcfile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			st = 1
			return
		}
	}

	if opts.OptNoIgnoreCase {
		ctx.SetCurrentMatcher(peco.CaseSensitiveMatch)
	}

	if len(opts.OptInitialMatcher) > 0 {
		ctx.SetCurrentMatcher(opts.OptInitialMatcher)
	}

	// Try waiting for something available in the source stream
	// before doing any terminal initialization (also done by termbox)
	reader := ctx.NewBufferReader(in)
	ctx.AddWaitGroup(1)
	go reader.Loop()

	// This channel blocks until we receive something from `in`
	<-reader.InputReadyCh()

	err = peco.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer peco.TtyTerm()

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		st = 1
		return
	}
	defer termbox.Close()

	// Windows handle Esc/Alt self
	if runtime.GOOS == "windows" {
		termbox.SetInputMode(termbox.InputEsc | termbox.InputAlt)
	}

	view := ctx.NewView()
	filter := ctx.NewFilter()
	input := ctx.NewInput()
	sig := ctx.NewSignalHandler()

	loopers := []interface {
		Loop()
	}{
		view,
		filter,
		input,
		sig,
	}
	for _, looper := range loopers {
		ctx.AddWaitGroup(1)
		go looper.Loop()
	}

	if len(opts.OptQuery) > 0 {
		ctx.SetQuery([]rune(opts.OptQuery))
		ctx.ExecQuery()
	} else {
		view.Refresh()
	}

	if len(opts.OptPrompt) > 0 {
		ctx.SetPrompt([]rune(opts.OptPrompt))
	}

	ctx.WaitDone()

	st = ctx.ExitStatus
}
<|endoftext|>


<commit_before>package pipe

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"syscall"
	"time"

	"github.com/go-errors/errors"
	"github.com/itchio/butler/butler"
	"github.com/natefinch/npipe"
)

var args = struct {
	command *[]string
	stdin   *string
	stdout  *string
	stderr  *string
}{}

func Register(ctx *butler.Context) {
	cmd := ctx.App.Command("pipe", "Runs a command, redirecting stdin/stdout/stderr to named pipes").Hidden()
	args.command = cmd.Arg("command", "A command to run, with arguments").Strings()
	args.stdin = cmd.Flag("stdin", "A named pipe to read stdin from").String()
	args.stdout = cmd.Flag("stdout", "A named pipe to write stdout to").String()
	args.stderr = cmd.Flag("stderr", "A named pipe to write stderr to").String()
	ctx.Register(cmd, do)
}

func do(ctx *butler.Context) {
	ctx.Must(Do(ctx, *args.command, *args.stdin, *args.stdout, *args.stderr))
}

<commit_msg>remove unused imports<commit_after>package pipe

import (
	"github.com/itchio/butler/butler"
)

var args = struct {
	command *[]string
	stdin   *string
	stdout  *string
	stderr  *string
}{}

func Register(ctx *butler.Context) {
	cmd := ctx.App.Command("pipe", "Runs a command, redirecting stdin/stdout/stderr to named pipes").Hidden()
	args.command = cmd.Arg("command", "A command to run, with arguments").Strings()
	args.stdin = cmd.Flag("stdin", "A named pipe to read stdin from").String()
	args.stdout = cmd.Flag("stdout", "A named pipe to write stdout to").String()
	args.stderr = cmd.Flag("stderr", "A named pipe to write stderr to").String()
	ctx.Register(cmd, do)
}

func do(ctx *butler.Context) {
	ctx.Must(Do(ctx, *args.command, *args.stdin, *args.stdout, *args.stderr))
}
<|endoftext|>


<commit_before>package rcat

import (
	"log"
	"os"
	"time"

	"github.com/ncw/rclone/cmd"
	"github.com/ncw/rclone/fs/operations"
	"github.com/spf13/cobra"
)

func init() {
	cmd.Root.AddCommand(commandDefintion)
}

var commandDefintion = &cobra.Command{
	Use:   "rcat remote:path",
	Short: `Copies standard input to file on remote.`,
	Long: `
rclone rcat reads from standard input (stdin) and copies it to a
single remote file.

    echo "hello world" | rclone rcat remote:path/to/file
    ffmpeg - | rclone rcat --checksum remote:path/to/file

If the remote file already exists, it will be overwritten.

rcat will try to upload small files in a single request, which is
usually more efficient than the streaming/chunked upload endpoints,
which use multiple requests. Exact behaviour depends on the remote.
What is considered a small file may be set through
` + "`--streaming-upload-cutoff`" + `. Uploading only starts after
the cutoff is reached or if the file ends before that. The data
must fit into RAM. The cutoff needs to be small enough to adhere
the limits of your remote, please see there. Generally speaking,
setting this cutoff too high will decrease your performance.

Note that the upload can also not be retried because the data is
not kept around until the upload succeeds. If you need to transfer
a lot of data, you're better off caching locally and then
` + "`rclone move`" + ` it to the destination.`,
	Run: func(command *cobra.Command, args []string) {
		cmd.CheckArgs(1, 1, command, args)

		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) != 0 {
			log.Fatalf("nothing to read from standard input (stdin).")
		}

		fdst, dstFileName := cmd.NewFsDstFile(args)
		cmd.Run(false, false, command, func() error {
			_, err := operations.Rcat(fdst, dstFileName, os.Stdin, time.Now())
			return err
		})
	},
}
<commit_msg>rcat: remove --checksum flag from the docs as it is not usually effective<commit_after>package rcat

import (
	"log"
	"os"
	"time"

	"github.com/ncw/rclone/cmd"
	"github.com/ncw/rclone/fs/operations"
	"github.com/spf13/cobra"
)

func init() {
	cmd.Root.AddCommand(commandDefintion)
}

var commandDefintion = &cobra.Command{
	Use:   "rcat remote:path",
	Short: `Copies standard input to file on remote.`,
	Long: `
rclone rcat reads from standard input (stdin) and copies it to a
single remote file.

    echo "hello world" | rclone rcat remote:path/to/file
    ffmpeg - | rclone rcat remote:path/to/file

If the remote file already exists, it will be overwritten.

rcat will try to upload small files in a single request, which is
usually more efficient than the streaming/chunked upload endpoints,
which use multiple requests. Exact behaviour depends on the remote.
What is considered a small file may be set through
` + "`--streaming-upload-cutoff`" + `. Uploading only starts after
the cutoff is reached or if the file ends before that. The data
must fit into RAM. The cutoff needs to be small enough to adhere
the limits of your remote, please see there. Generally speaking,
setting this cutoff too high will decrease your performance.

Note that the upload can also not be retried because the data is
not kept around until the upload succeeds. If you need to transfer
a lot of data, you're better off caching locally and then
` + "`rclone move`" + ` it to the destination.`,
	Run: func(command *cobra.Command, args []string) {
		cmd.CheckArgs(1, 1, command, args)

		stat, _ := os.Stdin.Stat()
		if (stat.Mode() & os.ModeCharDevice) != 0 {
			log.Fatalf("nothing to read from standard input (stdin).")
		}

		fdst, dstFileName := cmd.NewFsDstFile(args)
		cmd.Run(false, false, command, func() error {
			_, err := operations.Rcat(fdst, dstFileName, os.Stdin, time.Now())
			return err
		})
	},
}
<|endoftext|>