Search code examples
stringimportincludegoconstants

Is it possible to include an external file as a string constant in go?


I always wished it was possible to do something like this in C++:

const std::string fragmentShader = "
#include "shader.frag"
";

Obviously that doesn't work, and there is no way to do it in C++. But it is possible in go? i.e.

const fragmentShader string = `
<insert contents of shader.frag at compile-time>
`

The motivation should be obvious!


Solution

  • This is not possible in pure Go.¹ You could however write a program that reads a file and creates a Go file from it, such as this:

    package main
    
    import "flag"
    import "os"
    import "fmt"
    import "bufio"
    import "io"
    
    var (
        packageName = flag.String("p", "main", "package name")
        outFile     = flag.String("o", "-", "output file. Defaults to stdout")
        varName     = flag.String("v", "file", "variable name")
    )
    
    const (
        header  = "package %s\n\nvar %s = [...]byte{\n"
        trailer = "}\n"
    )
    
    func main() {
    
        flag.Parse()
    
        if len(flag.Args()) != 1 {
            fmt.Fprintln(os.Stderr, "Please provide exactly one file name")
            os.Exit(1)
        }
    
        var inF, outF *os.File
    
        if *outFile == "-" {
            outF = os.Stdout
        } else {
            var err error
            outF, err = os.Create(*outFile)
            if err != nil {
                fmt.Fprintf(os.Stderr, "Cannot create %s: %v\n", *outFile, err)
                os.Exit(1)
            }
        }
    
        inF, err := os.Open(flag.Args()[0])
        if err != nil {
            fmt.Fprintf(os.Stderr, "Cannot open %s: %v\n", flag.Args()[0], err)
            os.Exit(1)
        }
    
        in, out := bufio.NewReader(inF), bufio.NewWriter(outF)
    
        fmt.Fprintf(out, header, *packageName, *varName)
    
        buf := make([]byte, 16)
    
        var n int
    
        for n, err = io.ReadFull(in, buf); n > 0; n, err = io.ReadFull(in, buf) {
            out.WriteRune('\t')
    
            for i := 0; i < n-1; i++ {
                fmt.Fprintf(out, "%#02x, ", buf[i])
            }
    
            fmt.Fprintf(out, "%#02x,\n", buf[n-1])
        }
    
        out.WriteString(trailer)
    
        out.Flush()
    
        if err != io.EOF {
            fmt.Fprintf(os.Stderr, "An error occured while reading from %s: %v\n", flag.Args()[0], err)
            os.Exit(1)
        }
    }
    

    ¹ This is now supported. You can use the embed mechanism as explained in my other answer.