package main import ( "bufio" "flag" "fmt" "io" "os" "os/exec" "path/filepath" "strings" ) type repeatedFlag []string func (value *repeatedFlag) String() string { return strings.Join(*value, ",") } func (value *repeatedFlag) Set(input string) error { *value = append(*value, input) return nil } func main() { var clean bool var extraArgs repeatedFlag var manifestPath string var mode string var outDir string var outputPath string var packageDir string var ts bool var wailsPath string flag.BoolVar(&clean, "clean", true, "") flag.Var(&extraArgs, "extra-arg", "") flag.StringVar(&manifestPath, "manifest", "", "") flag.StringVar(&mode, "mode", "", "") flag.StringVar(&outDir, "out-dir", "", "") flag.StringVar(&outputPath, "out", "", "") flag.StringVar(&packageDir, "package-dir", "", "") flag.BoolVar(&ts, "ts", true, "") flag.StringVar(&wailsPath, "wails", "", "") flag.Parse() require(mode != "", "missing --mode") require(outDir != "", "missing --out-dir") require(wailsPath != "", "missing --wails") var err error wailsPath, err = filepath.Abs(wailsPath) must(err) must(resolveGoEnvToAbsolutePath()) switch mode { case "action": require(manifestPath != "", "missing --manifest") require(outputPath != "", "missing --out") must(runActionMode(manifestPath, outputPath, outDir, wailsPath, clean, ts, extraArgs)) case "workspace": require(packageDir != "", "missing --package-dir") must(runWorkspaceMode(packageDir, outDir, wailsPath, clean, ts, extraArgs)) default: panic("unsupported --mode") } } func resolveGoEnvToAbsolutePath() error { goBinary := os.Getenv("GO_BIN") if goBinary == "" || filepath.IsAbs(goBinary) { return nil } absolutePath, err := filepath.Abs(goBinary) if err != nil { return err } return os.Setenv("GO_BIN", absolutePath) } func runActionMode(manifestPath string, outputPath string, outDir string, wailsPath string, clean bool, ts bool, extraArgs []string) error { tempRoot, err := os.MkdirTemp("", "rules-wails-bindings-*") if err != nil { return err } defer os.RemoveAll(tempRoot) workDir := filepath.Join(tempRoot, "work") if err := os.MkdirAll(workDir, 0o755); err != nil { return err } if err := stageManifest(manifestPath, workDir); err != nil { return err } if err := runBindings(workDir, outDir, wailsPath, clean, ts, extraArgs); err != nil { return err } if err := os.RemoveAll(outputPath); err != nil { return err } if err := os.MkdirAll(outputPath, 0o755); err != nil { return err } return copyTree(filepath.Join(workDir, outDir), outputPath) } func runWorkspaceMode(packageDir string, outDir string, wailsPath string, clean bool, ts bool, extraArgs []string) error { return runBindings(packageDir, outDir, wailsPath, clean, ts, extraArgs) } func runBindings(cwd string, outDir string, wailsPath string, clean bool, ts bool, extraArgs []string) error { commandArgs := []string{"generate", "bindings", "-d", outDir} if clean { commandArgs = append(commandArgs, "-clean") } if ts { commandArgs = append(commandArgs, "-ts") } commandArgs = append(commandArgs, extraArgs...) command := exec.Command(wailsPath, commandArgs...) command.Dir = cwd command.Stdout = os.Stdout command.Stderr = os.Stderr return command.Run() } func stageManifest(manifestPath string, destinationRoot string) error { entries, err := readManifest(manifestPath) if err != nil { return err } for _, entry := range entries { destinationPath := filepath.Join(destinationRoot, entry.relativePath) if err := os.MkdirAll(filepath.Dir(destinationPath), 0o755); err != nil { return err } if err := copyFile(entry.sourcePath, destinationPath); err != nil { return err } } return nil } func readManifest(path string) ([]manifestEntry, error) { file, err := os.Open(path) if err != nil { return nil, err } defer file.Close() entries := make([]manifestEntry, 0) scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" { continue } parts := strings.SplitN(line, "\t", 2) if len(parts) != 2 { return nil, fmt.Errorf("invalid manifest line: %s", line) } entries = append(entries, manifestEntry{ sourcePath: parts[0], relativePath: parts[1], }) } return entries, scanner.Err() } func copyTree(sourceRoot string, destinationRoot string) error { return filepath.Walk(sourceRoot, func(path string, info os.FileInfo, err error) error { if err != nil { return err } relativePath, err := filepath.Rel(sourceRoot, path) if err != nil { return err } if relativePath == "." { return nil } destinationPath := filepath.Join(destinationRoot, relativePath) if info.IsDir() { return os.MkdirAll(destinationPath, 0o755) } if err := os.MkdirAll(filepath.Dir(destinationPath), 0o755); err != nil { return err } return copyFile(path, destinationPath) }) } func copyFile(sourcePath string, destinationPath string) error { sourceFile, err := os.Open(sourcePath) if err != nil { return err } defer sourceFile.Close() destinationFile, err := os.Create(destinationPath) if err != nil { return err } defer destinationFile.Close() if _, err := io.Copy(destinationFile, sourceFile); err != nil { return err } return destinationFile.Chmod(0o644) } func must(err error) { if err != nil { panic(err) } } func require(condition bool, message string) { if !condition { panic(message) } } type manifestEntry struct { sourcePath string relativePath string }