github.com/goplus/igop@v0.25.0/load/embed_go116.go (about) 1 //go:build go1.16 2 // +build go1.16 3 4 /* 5 * Copyright (c) 2022 The GoPlus Authors (goplus.org). All rights reserved. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 package load 21 22 import ( 23 "bytes" 24 "fmt" 25 "go/ast" 26 "go/build" 27 "go/parser" 28 "go/token" 29 "strconv" 30 _ "unsafe" 31 32 "github.com/visualfc/goembed" 33 embedparser "github.com/visualfc/goembed/parser" 34 ) 35 36 func buildIdent(name string) string { 37 return fmt.Sprintf("__igop_embed_%x__", name) 38 } 39 40 var embed_head = `package %v 41 42 import ( 43 "embed" 44 "unsafe" 45 ) 46 47 func __igop_embed_buildFS__(list []struct { 48 name string 49 data string 50 hash [16]byte 51 }) (f embed.FS) { 52 fs := struct { 53 files *[]struct { 54 name string 55 data string 56 hash [16]byte 57 } 58 }{&list} 59 return *(*embed.FS)(unsafe.Pointer(&fs)) 60 } 61 ` 62 63 // Embed check package embed data 64 func Embed(bp *build.Package, fset *token.FileSet, files []*ast.File, test bool, xtest bool) (*ast.File, error) { 65 var pkgName string 66 var err error 67 var ems []*goembed.Embed 68 if xtest { 69 pkgName = bp.Name + "_test" 70 ems, err = goembed.CheckEmbed(bp.XTestEmbedPatternPos, fset, files) 71 if err != nil { 72 return nil, err 73 } 74 } else { 75 pkgName = bp.Name 76 ems, err = goembed.CheckEmbed(bp.EmbedPatternPos, fset, files) 77 if err != nil { 78 return nil, err 79 } 80 if test { 81 tems, err := goembed.CheckEmbed(bp.TestEmbedPatternPos, fset, files) 82 if err != nil { 83 return nil, err 84 } 85 if len(tems) > 0 { 86 ems = append(ems, tems...) 87 } 88 } 89 } 90 if len(ems) == 0 { 91 return nil, nil 92 } 93 r := goembed.NewResolve() 94 for _, v := range ems { 95 fs, err := r.Load(bp.Dir, fset, v) 96 if err != nil { 97 return nil, err 98 } 99 switch v.Kind { 100 case goembed.EmbedMaybeAlias: 101 // value = Type(data) 102 // valid alias string or []byte type used by types.check 103 v.Spec.Values = []ast.Expr{ 104 &ast.CallExpr{ 105 Fun: v.Spec.Type, 106 Args: []ast.Expr{ 107 &ast.Ident{Name: buildIdent(fs[0].Name), 108 NamePos: v.Spec.Names[0].NamePos}, 109 }, 110 }} 111 case goembed.EmbedBytes: 112 // value = []byte(data) 113 v.Spec.Values = []ast.Expr{ 114 &ast.CallExpr{ 115 Fun: v.Spec.Type, 116 Args: []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))}, 117 }} 118 case goembed.EmbedString: 119 // value = data 120 v.Spec.Values = []ast.Expr{ast.NewIdent(buildIdent(fs[0].Name))} 121 case goembed.EmbedFiles: 122 // value = __igop_embed_buildFS__([]struct{name string; data string; hash [16]byte}{...}) 123 fs = goembed.BuildFS(fs) 124 elts := make([]ast.Expr, len(fs), len(fs)) 125 for i, f := range fs { 126 if len(f.Data) == 0 { 127 elts[i] = &ast.CompositeLit{ 128 Elts: []ast.Expr{ 129 &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, 130 &ast.BasicLit{Kind: token.STRING, Value: `""`}, 131 &ast.CompositeLit{ 132 Type: &ast.ArrayType{ 133 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 134 Elt: ast.NewIdent("byte"), 135 }, 136 }, 137 }, 138 } 139 } else { 140 var hash [16]ast.Expr 141 for j, v := range f.Hash { 142 hash[j] = &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(int(v))} 143 } 144 elts[i] = &ast.CompositeLit{ 145 Elts: []ast.Expr{ 146 &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(f.Name)}, 147 ast.NewIdent(buildIdent(f.Name)), 148 &ast.CompositeLit{ 149 Type: &ast.ArrayType{ 150 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 151 Elt: ast.NewIdent("byte"), 152 }, 153 Elts: hash[:], 154 }, 155 }, 156 } 157 } 158 } 159 call := &ast.CallExpr{ 160 Fun: ast.NewIdent("__igop_embed_buildFS__"), 161 Args: []ast.Expr{ 162 &ast.CompositeLit{ 163 Type: &ast.ArrayType{ 164 Elt: &ast.StructType{ 165 Fields: &ast.FieldList{ 166 List: []*ast.Field{ 167 &ast.Field{ 168 Names: []*ast.Ident{ast.NewIdent("name")}, 169 Type: ast.NewIdent("string"), 170 }, 171 &ast.Field{ 172 Names: []*ast.Ident{ast.NewIdent("data")}, 173 Type: ast.NewIdent("string"), 174 }, 175 &ast.Field{ 176 Names: []*ast.Ident{ast.NewIdent("hash")}, 177 Type: &ast.ArrayType{ 178 Len: &ast.BasicLit{Kind: token.INT, Value: "16"}, 179 Elt: ast.NewIdent("byte"), 180 }, 181 }, 182 }, 183 }, 184 }, 185 }, 186 Elts: elts, 187 }, 188 }, 189 } 190 v.Spec.Values = []ast.Expr{call} 191 } 192 } 193 var buf bytes.Buffer 194 fmt.Fprintf(&buf, embed_head, pkgName) 195 buf.WriteString("\nconst (\n") 196 for _, f := range r.Files() { 197 if len(f.Data) == 0 { 198 fmt.Fprintf(&buf, "\t%v = \"\"\n", buildIdent(f.Name)) 199 } else { 200 fmt.Fprintf(&buf, "\t%v = \"%v\"\n", buildIdent(f.Name), goembed.BytesToHex(f.Data)) 201 } 202 } 203 buf.WriteString(")\n\n") 204 return parser.ParseFile(fset, "_igop_embed_data.go", buf.Bytes(), parser.ParseComments) 205 } 206 207 func EmbedFiles(pkgName string, dir string, fset *token.FileSet, files []*ast.File) (*ast.File, error) { 208 embed, err := embedparser.ParseEmbed(fset, files) 209 if err != nil { 210 return nil, err 211 } 212 if embed == nil { 213 return nil, nil 214 } 215 bp := &build.Package{ 216 Name: pkgName, 217 Dir: dir, 218 EmbedPatterns: embed.Patterns, 219 EmbedPatternPos: embed.PatternPos, 220 } 221 return Embed(bp, fset, files, false, false) 222 }