package main import ( "errors" "flag" "fmt" "os" "path/filepath" "runtime" "strconv" "strings" "sync" "text/template" "time" "git.milar.in/milarin/channel" ) // globals var ( OutputFileTmpl = template.New("output-file") ModulePath string ProjectName string // meta data for executables VersionTag string CommitHash string BuildTime string Runner channel.Runner MaxConfigStringLength int ) // command line arguments default values var ( DefaultOutputDir = "output" DefaultOutputFile = "{{.Name}}-{{.OS}}-{{.Arch}}{{.Ext}}" DefaultOS = "" DefaultArch = "" DefaultSilent = false DefaultNoCompress = false DefaultNumThreads = runtime.NumCPU() DefaultKeepDebugFlags = false ) // command line arguments var ( OutputDir = flag.String("o", DefaultOutputDir, "output directory") OutputFile = flag.String("f", DefaultOutputFile, "go template for filenames") OS = flag.String("os", DefaultOS, "comma-separated list of operating systems to compile for (empty for all)") Arch = flag.String("arch", DefaultArch, "comma-separated list of architectures to compile for (empty for all)") Silent = flag.Bool("s", DefaultSilent, "silent mode (no output)") NoCompress = flag.Bool("c", DefaultNoCompress, "dont compress any executables") NumThreads = flag.Int("t", DefaultNumThreads, "amount of threads (0 = infinite)") KeepDebugFlags = flag.Bool("d", DefaultKeepDebugFlags, "keep debug flags") SaveConfigFile = flag.Bool("saveconfig", false, "save config file with current configuration and exit") IgnoreConfigFile = flag.Bool("ignoreconfig", false, "dont read any config file") FindConfigFile = flag.Bool("findconfig", false, "print config file path and exit") ShowVersion = flag.Bool("v", false, "show version and exit") ) func main() { Init() GatherMetaData() ch := make(chan *CompileReport, len(CompileConfigs)) wg := new(sync.WaitGroup) doneCh := make(chan struct{}) go Watch(ch, doneCh) Runner = GetRunner() start := time.Now() for _, cfg := range CompileConfigs { cfg := cfg wg.Add(1) go Runner.Run(func() { Compile(cfg, ch, wg) }) } wg.Wait() close(ch) end := time.Now() <-doneCh // wait for Watch routine if !*Silent { fmt.Printf("compilation took %s (using %s threads)\n", end.Sub(start).Truncate(time.Second/100), GetThreadCountString()) } } func DetermineProjectName() error { data, err := os.ReadFile(filepath.Join(ModulePath, "go.mod")) if err != nil { return err } for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, "module ") { fullName := strings.TrimPrefix(line, "module ") parts := strings.Split(fullName, "/") ProjectName = parts[len(parts)-1] return nil } } return errors.New("malformed syntax in go.mod file") } func GetRunner() channel.Runner { if *NumThreads <= 0 { return channel.NewUnlimitedRunner() } else { return channel.NewLimitedRunner(*NumThreads) } } func GetThreadCountString() string { if *NumThreads == 0 { return "infinitely many" } return strconv.Itoa(*NumThreads) } func CheckDirWritable(dir string) error { path := filepath.Join(*OutputDir, ".gocc") file, err := os.Create(path) if err != nil { return err } file.Close() os.Remove(path) return nil }