diff --git a/compiler/compiler.go b/compiler/compiler.go index 7841cd656..f211fa8d3 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -8,6 +8,7 @@ import ( "go/constant" "go/token" "go/types" + "log" "math/bits" "path/filepath" "sort" @@ -693,6 +694,7 @@ func (c *compilerContext) getDIFile(filename string) llvm.Metadata { // createPackage builds the LLVM IR for all types, methods, and global variables // in the given package. func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) { + println("called create package:", pkg.Pkg.Name()) // Sort by position, so that the order of the functions in the IR matches // the order of functions in the source file. This is useful for testing, // for example. @@ -758,6 +760,13 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package case *ssa.Global: // Global variable. info := c.getGlobalInfo(member) + if strings.TrimSpace(info.embeds) != "" { + embeds, err := parseGoEmbed(info.embeds) + if err != nil { + log.Fatal(err) + } + fmt.Printf("file embed found at %v: %v\n", info.linkName, embeds) + } global := c.getGlobal(member) if !info.extern { global.SetInitializer(llvm.ConstNull(global.Type().ElementType())) @@ -766,6 +775,7 @@ func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package global.SetSection(info.section) } } + } } } diff --git a/compiler/embed.go b/compiler/embed.go new file mode 100644 index 000000000..2915af5ff --- /dev/null +++ b/compiler/embed.go @@ -0,0 +1,70 @@ +package compiler + +import ( + "fmt" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// parseGoEmbed parses the text following "//go:embed" to extract the glob patterns. +// It accepts unquoted space-separated patterns as well as double-quoted and back-quoted Go strings. +// Copied from https://github.com/golang/go/blob/219fe9d547d09d3de1b024c6c8b8314dd0bf12e4/src/cmd/compile/internal/noder/noder.go#L1717-L1776 +func parseGoEmbed(args string) ([]string, error) { + var list []string + for args = strings.TrimSpace(args); args != ""; args = strings.TrimSpace(args) { + var path string + Switch: + switch args[0] { + default: + i := len(args) + for j, c := range args { + if unicode.IsSpace(c) { + i = j + break + } + } + path = args[:i] + args = args[i:] + + case '`': + i := strings.Index(args[1:], "`") + if i < 0 { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + path = args[1 : 1+i] + args = args[1+i+1:] + + case '"': + i := 1 + for ; i < len(args); i++ { + if args[i] == '\\' { + i++ + continue + } + if args[i] == '"' { + q, err := strconv.Unquote(args[:i+1]) + if err != nil { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args[:i+1]) + } + path = q + args = args[i+1:] + break Switch + } + } + if i >= len(args) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + + if args != "" { + r, _ := utf8.DecodeRuneInString(args) + if !unicode.IsSpace(r) { + return nil, fmt.Errorf("invalid quoted string in //go:embed: %s", args) + } + } + list = append(list, path) + } + return list, nil +} diff --git a/compiler/symbol.go b/compiler/symbol.go index 49ccfa20d..efffd45b2 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -331,6 +331,7 @@ type globalInfo struct { extern bool // go:extern align int // go:align section string // go:section + embeds string // go:embed } // loadASTComments loads comments on globals from the AST, for use later in the @@ -448,6 +449,9 @@ func (info *globalInfo) parsePragmas(doc *ast.CommentGroup) { if len(parts) == 2 { info.section = parts[1] } + case "//go:embed": + raw := strings.TrimPrefix(comment.Text, "//go:embed") + info.embeds += " " + raw } } } diff --git a/go.mod b/go.mod index 4820d7747..f8f1e2668 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/tinygo-org/tinygo -go 1.13 +go 1.16 require ( github.com/blakesmith/ar v0.0.0-20150311145944-8bd4349a67f2 diff --git a/src/examples/embed/embed.go b/src/examples/embed/embed.go new file mode 100644 index 000000000..453bb46ef --- /dev/null +++ b/src/examples/embed/embed.go @@ -0,0 +1,21 @@ +package main + +import ( + "embed" + "log" +) + +//go:embed file1.txt file2.txt +//go:embed file3.txt +var files embed.FS + +func main() { + println(msg) + contents, err := files.ReadDir(".") + if err != nil { + log.Fatal(err) + } + for _, c := range contents { + println("file:", c.Name()) + } +} diff --git a/src/examples/embed/file1.txt b/src/examples/embed/file1.txt new file mode 100644 index 000000000..6c8db5df2 --- /dev/null +++ b/src/examples/embed/file1.txt @@ -0,0 +1 @@ +file 1 \ No newline at end of file diff --git a/src/examples/embed/file2.txt b/src/examples/embed/file2.txt new file mode 100644 index 000000000..dd4128ed9 --- /dev/null +++ b/src/examples/embed/file2.txt @@ -0,0 +1 @@ +file 2 \ No newline at end of file diff --git a/src/examples/embed/file3.txt b/src/examples/embed/file3.txt new file mode 100644 index 000000000..fe1b1a429 --- /dev/null +++ b/src/examples/embed/file3.txt @@ -0,0 +1 @@ +file 3 \ No newline at end of file diff --git a/src/examples/embed/message.txt b/src/examples/embed/message.txt new file mode 100644 index 000000000..bc7774a7b --- /dev/null +++ b/src/examples/embed/message.txt @@ -0,0 +1 @@ +hello world! \ No newline at end of file diff --git a/src/examples/embed/test.go b/src/examples/embed/test.go new file mode 100644 index 000000000..3093dd794 --- /dev/null +++ b/src/examples/embed/test.go @@ -0,0 +1,6 @@ +package main + +import _ "embed" + +//go:embed message.txt +var msg string