Skip to content

Instantly share code, notes, and snippets.

@charlires
Created August 21, 2024 14:55
Show Gist options
  • Save charlires/efb6b964cd59045803e6a8cc50eb660e to your computer and use it in GitHub Desktop.
Save charlires/efb6b964cd59045803e6a8cc50eb660e to your computer and use it in GitHub Desktop.
A go script to merge multiple go files in a dir tree into one single file per dir
package main
import (
"bufio"
"flag"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
)
// A go script to merge multiple go files into one single file
// - look down recursively in the provided directory
// - create a merge file per directory the generated file in each directory and name the file as the directory
// - merge all files in a provided directory into the new file
// - if the directory doesn't have go files in it void creating a go file for that directory node
// - remove duplicated package declaration
// - remove duplicated imports
// usage:
// go run merge.go -dir=path/to/root/directory
// go run merge.go -dir=path/to/root/directory -clean
func main() {
dir := flag.String("dir", ".", "Root directory to search for Go files")
clean := flag.Bool("clean", false, "Delete all merged files after merging")
flag.Parse()
var mergedFiles []string
err := filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
mergedFile := mergeFilesInDirectory(path)
if len(mergedFile) > 0 {
mergedFiles = append(mergedFiles, mergedFile...)
}
}
return nil
})
if err != nil {
fmt.Println("Error walking the path:", err)
return
}
fmt.Println("Merge completed for all directories.")
if *clean {
deleteMergedFiles(mergedFiles)
}
}
func mergeFilesInDirectory(dir string) []string {
files, err := os.ReadDir(dir)
if err != nil {
fmt.Println("Error reading directory:", dir, err)
return nil
}
var goFiles []fs.DirEntry
for _, file := range files {
if filepath.Ext(file.Name()) == ".go" && file.Name() != filepath.Base(dir)+".go" {
goFiles = append(goFiles, file)
}
}
if len(goFiles) == 0 {
// No Go files in this directory, so skip creating a merged file
return nil
}
outputFilePath := filepath.Join(dir, filepath.Base(dir)+".go")
outputFile, err := os.Create(outputFilePath)
if err != nil {
fmt.Println("Error creating output file:", err)
return nil
}
defer outputFile.Close()
writer := bufio.NewWriter(outputFile)
defer writer.Flush()
packageDeclared := false
importSet := make(map[string]bool)
var mergedFiles []string
for _, file := range goFiles {
filePath := filepath.Join(dir, file.Name())
mergeFile(filePath, writer, &packageDeclared, importSet)
mergedFiles = append(mergedFiles, filePath)
}
fmt.Println("Files merged successfully into", outputFilePath)
return mergedFiles
}
func mergeFile(filePath string, writer *bufio.Writer, packageDeclared *bool, importSet map[string]bool) {
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Error opening file:", filePath, err)
return
}
defer file.Close()
scanner := bufio.NewScanner(file)
inImportSection := false
for scanner.Scan() {
line := scanner.Text()
trimmedLine := strings.TrimSpace(line)
if strings.HasPrefix(trimmedLine, "package ") && *packageDeclared {
continue
}
if strings.HasPrefix(trimmedLine, "package ") && !*packageDeclared {
writer.WriteString(line + "\n")
*packageDeclared = true
continue
}
if strings.HasPrefix(trimmedLine, "import ") {
inImportSection = true
}
if inImportSection {
if trimmedLine == ")" {
inImportSection = false
}
if !importSet[trimmedLine] {
writer.WriteString(line + "\n")
importSet[trimmedLine] = true
}
continue
}
writer.WriteString(line + "\n")
}
if err := scanner.Err(); err != nil {
fmt.Println("Error reading file:", filePath, err)
}
}
func deleteMergedFiles(mergedFiles []string) {
for _, filePath := range mergedFiles {
err := os.Remove(filePath)
if err != nil {
fmt.Println("Error deleting file:", filePath, err)
} else {
fmt.Println("Deleted merged file:", filePath)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment