gocc/main.go

144 lines
3.2 KiB
Go

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
}