diff --git a/Gopkg.lock b/Gopkg.lock index 8d84aa1..978ffcf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,14 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + digest = "1:740f0e477ddda011071f5e076ac9d38a0f6cee3ad3a924ab2acdd45b74a3c05c" + name = "github.com/apex/log" + packages = ["."] + pruneopts = "UT" + revision = "941dea75d3ebfbdd905a5d8b7b232965c5e5c684" + version = "v1.1.0" + [[projects]] digest = "1:b7b063b8125b7aeaf179aa4564ba60c896551d702f7c824ace98cc654fc813a0" name = "github.com/djherbis/times" @@ -103,7 +111,7 @@ [[projects]] branch = "master" - digest = "1:b44d53c23020bcc3a9de9942e92039931c1ea92a8a319ab73dde160933b996ef" + digest = "1:ba2b26c747d7c38e96bb5f601c7a7052cd8a0b2763d374b35059f8b67e5572eb" name = "github.com/moisespsena-go/assetfs" packages = [ ".", @@ -111,7 +119,7 @@ "local", ] pruneopts = "UT" - revision = "4eef90af121f38b228177482de6505c78898e203" + revision = "e7c9e99a64ab3c4a343a224ddd7ad1dfdc1be556" [[projects]] branch = "master" @@ -163,11 +171,11 @@ [[projects]] branch = "master" - digest = "1:4f81684a98dd82ad913484d2d7dc47f1b35a5b51e8651721ce0584312a7e61d6" + digest = "1:548fdd558f15f58efdd5be255ea8532e6149c3cd6518f436770495052f7c62f7" name = "github.com/moisespsena-go/path-helpers" packages = ["."] pruneopts = "UT" - revision = "993d518d4d1dd0215c9dd9c30ca95ff9d4dba248" + revision = "d85da131f3f5152dff99359f3d315bfbf13daf7b" [[projects]] branch = "master" @@ -311,6 +319,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/apex/log", "github.com/djherbis/times", "github.com/dustin/go-humanize", "github.com/go-errors/errors", @@ -332,6 +341,7 @@ "github.com/pkg/errors", "github.com/spf13/cobra", "github.com/spf13/viper", + "gopkg.in/yaml.v2", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/config.go b/config.go index 4a1623b..f75ccf1 100644 --- a/config.go +++ b/config.go @@ -45,7 +45,7 @@ type InputConfig struct { Prefix string - NamePrefix string + NameSpace string WalkFunc func(visited *map[string]bool, recursive bool, cb func(info walker.FileInfo) error) error } @@ -65,10 +65,10 @@ func (i InputConfig) DefaultWalk(visited *map[string]bool, recursive bool, cb wa } func (i InputConfig) prepareCb(cb walker.WalkCallback) walker.WalkCallback { - if i.NamePrefix != "" { + if i.NameSpace != "" { old := cb cb = func(info walker.FileInfo) error { - return old(info.SetNamePrefix(i.NamePrefix)) + return old(info.SetNamePrefix(i.NameSpace)) } } return cb diff --git a/config_many.go b/config_many.go index bb57270..5201077 100644 --- a/config_many.go +++ b/config_many.go @@ -1,12 +1,16 @@ package xbindata import ( + "context" "fmt" "os" "path" "path/filepath" + "strconv" "strings" + "github.com/go-errors/errors" + "github.com/apex/log" "gopkg.in/yaml.v2" @@ -30,9 +34,9 @@ type ( ManyConfigInputSlice []ManyConfigInput ) -func (s ManyConfigInputSlice) Items() (r []InputConfig, err error) { +func (s ManyConfigInputSlice) Items(ctx context.Context) (r []InputConfig, err error) { for j, input := range s { - if c, err := input.Config(); err != nil { + if c, err := input.Config(ContextWithInputKey(ctx, "#"+strconv.Itoa(j))); err != nil { return nil, fmt.Errorf("get config from input #%d (%q) failed: %v", j, input, err) } else { for _, c := range c { @@ -46,13 +50,26 @@ func (s ManyConfigInputSlice) Items() (r []InputConfig, err error) { type ManyConfigInput struct { Path string Prefix string - NamePrefix string `mapstructure:"name_prefix" yaml:"name_prefix"` + Ns string + NameSpace string `mapstructure:"name_space" yaml:"name_space"` Recursive bool Ignore IgnoreSlice IgnoreGlob IgnoreGlobSlice `mapstructure:"ignore_glob" yaml:"ignore_glob"` Pkg string } +func (i *ManyConfigInput) UnmarshalMap(value interface{}) (err error) { + var ns struct{ Ns string } + if err = mapstructure.Decode(value, i); err == nil { + if i.NameSpace == "" { + if err = mapstructure.Decode(value, &ns); err == nil { + i.NameSpace = ns.Ns + } + } + } + return +} + func (i *ManyConfigInput) GetPkg() string { if i.Pkg == "" { pth := i.Path @@ -67,17 +84,34 @@ func (i *ManyConfigInput) GetPkg() string { return i.Pkg } -func (i *ManyConfigInput) Config() (configs []*InputConfig, err error) { +func (i *ManyConfigInput) Config(ctx context.Context) (configs []*InputConfig, err error) { if i.Path == "" { log.Warnf("input path not set", i.Path) return } + if i.Ns != "" { + i.NameSpace = i.Ns + i.Ns = "" + } + + if key := InputKey(ctx, "[%s]:"); key != "" { + defer func() { + if err != nil { + err = errors.New(fmt.Sprint(key, err.Error())) + } + }() + } + if strings.HasPrefix(i.Path, "go:") { i.Pkg = i.Path[3:] _, i.Path = path_helpers.ResolveGoSrcPath(i.Pkg) } + if i.Path, err = i.format(ctx, "path", i.Path); err != nil { + return + } + if _, err = os.Stat(i.Path); err != nil { if os.IsNotExist(err) { log.Warnf("input %q does not exists", i.Path) @@ -86,10 +120,18 @@ func (i *ManyConfigInput) Config() (configs []*InputConfig, err error) { return } - if i.NamePrefix == "_" { - i.NamePrefix = i.GetPkg() - } else if strings.Contains(i.NamePrefix, "$PKG") { - i.NamePrefix = path.Clean(strings.Replace(i.NamePrefix, "$PKG", i.GetPkg(), 1)) + if i.NameSpace != "" { + if i.NameSpace == "_" { + i.NameSpace = i.GetPkg() + } else if strings.Contains(i.NameSpace, "$PKG") { + i.NameSpace = strings.Replace(i.NameSpace, "$PKG", i.GetPkg(), 1) + } + + if i.NameSpace, err = i.format(ctx, "name_space", i.NameSpace); err != nil { + return + } + + i.NameSpace = path.Clean(i.NameSpace) } xbinputFile := filepath.Join(i.Path, ".xbinputs.yml") @@ -106,17 +148,25 @@ func (i *ManyConfigInput) Config() (configs []*InputConfig, err error) { return } + ctx := ContextWithInputKey(ctx, xbinputFile) + func() { defer f.Close() err = yaml.NewDecoder(f).Decode(&xbinput) }() if err != nil { - return nil, fmt.Errorf("decode %q failed: %v", xbinputFile, err) + return } for _, input := range xbinput.Sources { - input.NamePrefix = path.Join(i.NamePrefix, input.NamePrefix) + if input.Ns != "" { + input.NameSpace = input.Ns + input.Ns = "" + } + if input.NameSpace != "" && i.NameSpace != "" { + input.NameSpace = i.NameSpace + "/" + input.NameSpace + } if input.Prefix != "" { if input.Prefix == "_" { @@ -136,49 +186,58 @@ func (i *ManyConfigInput) Config() (configs []*InputConfig, err error) { input.Ignore = append(i.Ignore, input.Ignore...) var cfgs []*InputConfig - if cfgs, err = input.Config(); err != nil { + if cfgs, err = input.Config(ctx); err != nil { return } configs = append(configs, cfgs...) } - } else { - c := &InputConfig{ - Path: i.Path, - Recursive: i.Recursive, - Prefix: i.Prefix, - NamePrefix: i.NamePrefix, - } + return + } - if i.Prefix == "_" { - c.Prefix = i.Path - } + c := &InputConfig{ + Path: i.Path, + Recursive: i.Recursive, + Prefix: i.Prefix, + NameSpace: i.NameSpace, + } - if c.IgnoreGlob, err = i.IgnoreGlob.Items(); err != nil { - return nil, err - } - if c.Ignore, err = i.Ignore.Items(); err != nil { - return nil, err - } + if i.Prefix == "_" { + c.Prefix = i.Path + } - if _, err := os.Stat(filepath.Join(i.Path, ".xbwalk", "main.go")); err == nil { - c.WalkFunc = i.Walked - } + if c.IgnoreGlob, err = i.IgnoreGlob.Items(); err != nil { + return nil, err + } + if c.Ignore, err = i.Ignore.Items(); err != nil { + return nil, err + } - configs = append(configs, c) + walkedPath := filepath.Join(i.Path, ".xbwalk", "main.go") + if _, err := os.Stat(walkedPath); err == nil { + c.WalkFunc = i.Walked } + configs = append(configs, c) return } type ManyConfigCommonDefaultInput struct { - Prefix string - NamePrefix string `mapstructure:"name_prefix" yaml:"name_prefix"` - Recursive bool + Prefix string + NameSpace string `mapstructure:"name_space" yaml:"name_space"` + Recursive bool } func (i *ManyConfigCommonDefaultInput) UnmarshalMap(value interface{}) (err error) { - return mapstructure.Decode(value, i) + var ns struct{ Ns string } + if err = mapstructure.Decode(value, i); err == nil { + if i.NameSpace == "" { + if err = mapstructure.Decode(value, &ns); err == nil { + i.NameSpace = ns.Ns + } + } + } + return } type ManyConfigCommonDefault struct { @@ -238,7 +297,7 @@ func (a *ManyConfigCommon) Validate() (err error) { return nil } -func (a *ManyConfigCommon) Config() (c *Config, err error) { +func (a *ManyConfigCommon) Config(ctx context.Context) (c *Config, err error) { c = NewConfig() c.Package = a.Pkg c.FileSystem = a.Fs @@ -279,8 +338,8 @@ func (a *ManyConfigCommon) Config() (c *Config, err error) { if a.Default.Input.Prefix != "" && input.Prefix == "" { input.Prefix = a.Default.Input.Prefix } - if a.Default.Input.NamePrefix != "" && input.NamePrefix == "" { - input.NamePrefix = a.Default.Input.NamePrefix + if a.Default.Input.NameSpace != "" && input.NameSpace == "" { + input.NameSpace = a.Default.Input.NameSpace } if a.Default.Input.Recursive { input.Recursive = true @@ -288,7 +347,7 @@ func (a *ManyConfigCommon) Config() (c *Config, err error) { a.Inputs[i] = input } - if c.Input, err = a.Inputs.Items(); err != nil { + if c.Input, err = a.Inputs.Items(ctx); err != nil { return nil, err } @@ -343,8 +402,8 @@ func (a *ManyConfigOutlined) Validate() (err error) { return nil } -func (a *ManyConfigOutlined) Config() (c *Config, err error) { - if c, err = a.ManyConfigCommon.Config(); err != nil { +func (a *ManyConfigOutlined) Config(ctx context.Context) (c *Config, err error) { + if c, err = a.ManyConfigCommon.Config(ctx); err != nil { return } c.Outlined = true @@ -368,9 +427,9 @@ func (a *ManyConfigOutlined) UnmarshalMap(value interface{}) (err error) { return } -func (a *ManyConfigOutlined) Translate() (err error) { +func (a *ManyConfigOutlined) Translate(ctx context.Context) (err error) { var c *Config - if c, err = a.Config(); err != nil { + if c, err = a.Config(ctx); err != nil { return } return Translate(c) diff --git a/config_many_input_walked.go b/config_many_input_walked.go index 7cdb64c..362b370 100644 --- a/config_many_input_walked.go +++ b/config_many_input_walked.go @@ -3,16 +3,53 @@ package xbindata import ( "bufio" "fmt" - "github.com/moisespsena-go/xbindata/walker" "os" "os/exec" "path/filepath" + "runtime" + + "github.com/go-errors/errors" + "github.com/moisespsena-go/xbindata/tempfile" + "github.com/moisespsena-go/xbindata/walker" ) func (i ManyConfigInput) Walked(visited *map[string]bool, recursive bool, cb walker.WalkCallback) (err error) { - cmd := exec.Command("go", "run", filepath.Join(i.Path, ".xbwalk", "main.go")) + dir := filepath.Join(i.Path, ".xbwalk") + defer func() { + if err != nil { + err = fmt.Errorf("%s: %v", dir, err) + } + }() + + var suffix, exe string + if runtime.GOOS == "windows" { + suffix = ".exe" + } + + if exe, err = tempfile.TempFile("", "xbindata-walk", suffix); err != nil { + return + } + + cmd := exec.Command("go", "build", "-o", exe, filepath.Join(dir, "main.go")) + cmd.Dir = i.Path + cmd.Env = EnvS(map[string]string{ + "GOOS": runtime.GOOS, + "GOARCH": runtime.GOARCH, + }) + cmd.Stderr = os.Stderr + cmd.Stdout = os.Stdout + + if err = cmd.Run(); err != nil { + err = errors.New("build failed: " + err.Error()) + return + } + + defer os.Remove(exe) + + cmd = exec.Command(exe) cmd.Dir = i.Path cmd.Env = os.Environ() + fr, err := cmd.StdoutPipe() if err != nil { return fmt.Errorf("pipe stdout failed: %v", err) @@ -48,7 +85,9 @@ func (i ManyConfigInput) Walked(visited *map[string]bool, recursive bool, cb wal } }() - cmd.Wait() + if err2 := cmd.Wait(); err == nil && err2 != nil { + err = err2 + } closed = true return err diff --git a/context.go b/context.go new file mode 100644 index 0000000..bdc99a6 --- /dev/null +++ b/context.go @@ -0,0 +1,82 @@ +package xbindata + +import ( + "context" + "fmt" + "os" + "strings" +) + +type configContextKeyType uint8 + +const ( + ContextEnvKey configContextKeyType = iota + ContextInputKey +) + +func ContextWithEnv(ctx context.Context, env map[string]string, noInherits ...bool) context.Context { + if ctx == nil { + ctx = context.Background() + } else { + if len(noInherits) == 0 || !noInherits[0] { + oldEnv := ctx.Value(ContextEnvKey) + if oldEnv != nil { + for key, value := range oldEnv.(map[string]string) { + env[key] = value + } + } + } + } + return context.WithValue(ctx, ContextEnvKey, env) +} + +func ContextEnv(ctx context.Context) map[string]string { + if ctx != nil { + env := ctx.Value(ContextEnvKey) + if env != nil { + return env.(map[string]string) + } + } + return map[string]string{} +} + +func ContextWithInputKey(ctx context.Context, key string) context.Context { + if ctx == nil { + ctx = context.Background() + } + return context.WithValue(ctx, ContextInputKey, key) +} + +func InputKey(ctx context.Context, format ...string) (key string) { + if ctx != nil { + old := ctx.Value(ContextInputKey) + if old != nil { + if len(format) == 0 || format[0] == "" { + return old.(string) + } + return fmt.Sprintf(format[0], old) + } + } + return +} + +func Env(update ...map[string]string) map[string]string { + items := make(map[string]string) + for _, item := range os.Environ() { + kv := strings.Split(item, "=") + items[kv[0]] = kv[1] + } + for _, env := range update { + for k, v := range env { + items[k] = v + } + } + return items +} + +func EnvS(update ...map[string]string) (env []string) { + for k, v := range Env(update...) { + env = append(env, k+"="+v) + } + return +} diff --git a/convert.go b/convert.go index e362834..f6775e2 100644 --- a/convert.go +++ b/convert.go @@ -136,9 +136,13 @@ func Translate(c *Config) error { } } + if c.Hybrid { + c.Tags = append(c.Tags, "!"+tagLocalFs) + } + // Write build tags, if applicable. if len(c.Tags) > 0 { - if _, err := fmt.Fprintf(buf, "// +build %s\n\n", strings.Join(c.Tags, ",")); err != nil { + if _, err := fmt.Fprintf(buf, "\n// +build %s\n\n", strings.Join(c.Tags, "\n// +build ")); err != nil { return err } } @@ -150,7 +154,7 @@ func Translate(c *Config) error { } // Write assets. - if c.Debug || c.Dev { + if !c.Outlined && (c.Debug || c.Dev) { if os.Getenv("GO_BINDATA_TEST") == "true" { // If we don't do this, people running the tests on different // machines get different git diffs. @@ -166,6 +170,12 @@ func Translate(c *Config) error { return err } + if c.Hybrid { + if err = localFs(c); err != nil { + return err + } + } + if c.Outlined { } else { // Write table of contents diff --git a/localfs.go b/localfs.go new file mode 100644 index 0000000..1c02922 --- /dev/null +++ b/localfs.go @@ -0,0 +1,113 @@ +package xbindata + +import ( + "os" + "strings" +) + +const tagLocalFs = "xblocalfs" + +func localFs(c *Config) (err error) { + var ( + f *os.File + pth string + ) + if c.Outlined { + pth = strings.TrimSuffix(c.OutlinedApi, ".go") + "_localfs.go" + } + + if f, err = os.Create(pth); err != nil { + return + } + + defer f.Close() + + if _, err = f.WriteString("// +build " + tagLocalFs + "\n\npackage " + c.Package + "\n"); err != nil { + return + } + var data string + + data += ` +import ( + "os" + "path/filepath" + "strings" + "sync" + + "github.com/moisespsena-go/assetfs" + fsapi "github.com/moisespsena-go/assetfs/assetfsapi" + "github.com/moisespsena-go/path-helpers" + "gopkg.in/yaml.v2" +) + +type inputStruct struct { + Path, Prefix, Ns string +} + +var ( + loaded bool + mu sync.Mutex + fs *assetfs.AssetFileSystem + fsLoadCallbacks []func(fs fsapi.Interface) + + __file__ = path_helpers.GetCalledFile(true) +) + +func OnFsLoad(cb ...func(fs fsapi.Interface)) { + fsLoadCallbacks = append(fsLoadCallbacks, cb...) +} + +func callFsLoadCallbacks() { + for _, f := range fsLoadCallbacks { + f(fs) + } +} + +func Load() { + if loaded { return } + mu.Lock() + defer mu.Unlock() + if loaded { return } + defer func() { loaded = true }() + fs = assetfs.NewAssetFileSystem() + + inputsPath := strings.TrimSuffix(__file__, "_localfs.go") + "_inputs.yml" + f, err := os.Open(inputsPath) + if err != nil { + panic(err) + } + defer f.Close() + + var inputs []inputStruct + if err = yaml.NewDecoder(f).Decode(&inputs); err != nil { + panic(err) + } + + dir := filepath.Dir(__file__) + + for _, input := range inputs { + input.Path = filepath.Join(dir, input.Path) + input.Prefix = filepath.Join(dir, input.Prefix) + if input.Ns == "" { + if err = fs.RegisterPath(input.Path); err != nil { + panic(err) + } + } else { + ns := fs.NameSpaceFS(input.Ns) + if err = ns.RegisterPath(input.Path); err != nil { + panic(err) + } + } + } +} + +func FS() *assetfs.AssetFileSystem { + Load() + return fs +} +` + + _, err = f.WriteString(data) + + return +} diff --git a/log.go b/log.go index 30ab642..69f9cf6 100644 --- a/log.go +++ b/log.go @@ -15,6 +15,11 @@ func logTocAndInputs(dir, base string, c *Config, toc []Asset) (err error) { if wd, err = os.Getwd(); err != nil { return } + if !filepath.IsAbs(dir) { + if dir, err = filepath.Abs(filepath.Join(wd, dir)); err != nil { + return + } + } var ignores = []string{ base + "toc_names.yml", @@ -45,25 +50,28 @@ func logTocAndInputs(dir, base string, c *Config, toc []Asset) (err error) { return } - tmpl := "- { pth: %q, recursive: %v, name_prefix: %q, prefix: %q }\n" + tmpl := "- { path: %q, recursive: %v, ns: %q, prefix: %q }\n" for _, input := range c.Input { relative := input.Path - if filepath.IsAbs(relative) { - relative, err = filepath.Rel(wd, relative) - if err != nil { - return - } + if !filepath.IsAbs(relative) { + relative = filepath.Join(wd, relative) + } + relative, err = filepath.Rel(dir, relative) + if err != nil { + return } prefix := input.Prefix - if filepath.IsAbs(prefix) { - prefix, err = filepath.Rel(wd, prefix) - if err != nil { - return - } + if !filepath.IsAbs(prefix) { + prefix = filepath.Join(wd, prefix) } - if _, err = fmt.Fprintf(f, tmpl, relative, input.Recursive, input.NamePrefix, prefix); err != nil { + prefix, err = filepath.Rel(dir, prefix) + if err != nil { + return + } + + if _, err = fmt.Fprintf(f, tmpl, relative, input.Recursive, input.NameSpace, prefix); err != nil { return } } @@ -99,7 +107,7 @@ func logTocAndInputs(dir, base string, c *Config, toc []Asset) (err error) { } for _, asset := range toc { - relative, err := filepath.Rel(wd, asset.Path) + relative, err := filepath.Rel(dir, asset.Path) if err != nil { return } diff --git a/release.go b/release.go index 763dc19..1613bf5 100644 --- a/release.go +++ b/release.go @@ -89,10 +89,6 @@ func writeReleaseHeader(w io.Writer, c *Config, toc []Asset) error { err = header_compressed_memcopy(w, c, imports...) } } - - if err == nil && c.Hybrid { - _, err = w.Write([]byte("var pkg = path_helpers.GetCalledDir()\n\n")) - } } if err == nil { @@ -214,13 +210,6 @@ func header_compressed_nomemcopy(w io.Writer, c *Config, imports ...string) (err "sync", ) - if c.Hybrid { - imports = append(imports, - "github.com/moisespsena-go/path-helpers", - "github.com/moisespsena-go/assetfs", - ) - } - if err = write_imports(w, c, imports...); err != nil { return } @@ -247,13 +236,6 @@ func header_compressed_memcopy(w io.Writer, c *Config, imports ...string) (err e "sync", ) - if c.Hybrid { - imports = append(imports, - "github.com/moisespsena-go/path-helpers", - "github.com/moisespsena-go/assetfs", - ) - } - if err = write_imports(w, c, imports...); err != nil { return } @@ -280,13 +262,6 @@ func header_uncompressed_nomemcopy(w io.Writer, c *Config, imports ...string) (e "sync", ) - if c.Hybrid { - imports = append(imports, - "github.com/moisespsena-go/path-helpers", - "github.com/moisespsena-go/assetfs", - ) - } - if err = write_imports(w, c, imports...); err != nil { return } @@ -314,13 +289,6 @@ func header_uncompressed_memcopy(w io.Writer, c *Config, imports ...string) (err "sync", ) - if c.Hybrid { - imports = append(imports, - "github.com/moisespsena-go/path-helpers", - "github.com/moisespsena-go/assetfs", - ) - } - return write_imports(w, c, imports...) } @@ -335,13 +303,6 @@ func header_outlined(w io.Writer, c *Config, toc []Asset, imports ...string) (er "errors", ) - if c.Hybrid { - imports = append(imports, - "path/filepath", - "github.com/moisespsena-go/assetfs", - ) - } - if err = write_imports(w, c, imports...); err != nil { return } @@ -404,7 +365,7 @@ func Outlined() (archiv *outlined.Outlined, err error) { return _outlined, nil } -func LoadDefault() {` + preInit + ` +func load() {` + preInit + ` if outlinedPath == "" { pths := []string{` + outlined + `} @@ -450,7 +411,7 @@ func LoadDefault() {` + preInit + ` data += ` } - loaded = true + fs = xbfs.NewFileSystem(&Assets) } ` @@ -481,62 +442,12 @@ func callFsLoadCallbacks() { } } ` - if c.Hybrid { - data += "\nfunc IsLocal() bool {\n" - data += fmt.Sprintf("\tif _, err := os.Stat(%q); err == nil {\n\t\t", c.Input[0].Path) - data += "return true\n\t" - data += "}\n\t" - data += "return false\n}\n\n" - } data += ` func FS() fsapi.Interface { Load() return fs } ` - - data += ` -var DefaultFS fsapi.Interface` - - if c.Outlined { - data += `= xbfs.NewFileSystem(&Assets) -` - } else { - data += "\n" - } - - if c.Hybrid { - data += `var LocalFS = assetfs.NewAssetFileSystem() - -func LoadLocal() { - var inputs = []string{ -` - // Locate all the assets. - for _, input := range c.Input { - data += fmt.Sprintf("\t\t%q,\n", input.Path) - } - data += ` } -` - if c.Outlined { - data += ` localDir := filepath.Join("` + c.OutlinedLocalOutputDir + `", filepath.FromSlash(pkg)) - if _, err := os.Stat(localDir); err == nil { - for i, pth := range inputs { - inputs[i] = filepath.Join(localDir, pth) - } - } else if !os.IsNotExist(err) { - panic(err) - } -` - } - data += ` for _, pth := range inputs { - if err := LocalFS.RegisterPath(pth); err != nil { - panic(err) - } - } -} - -` - } } data += `func Load() { @@ -552,24 +463,8 @@ func LoadLocal() { data += ` defer mu.Unlock() defer func() { loaded = true }() ` - if c.Hybrid { - data += ` if IsLocal() { - LoadLocal() -` - if c.FileSystem { - data += ` fs = LocalFS -` - } - data += ` return - } + data += ` load() ` - } - data += ` LoadDefault() -` - if c.FileSystem { - data += ` fs = DefaultFS -` - } data += `} ` diff --git a/tempfile/tempfile.go b/tempfile/tempfile.go new file mode 100644 index 0000000..b35d213 --- /dev/null +++ b/tempfile/tempfile.go @@ -0,0 +1,66 @@ +package tempfile + +import ( + "os" + "path/filepath" + "strconv" + "sync" + "time" +) + +// Random number state. +// We generate random temporary file names so that there's a good +// chance the file doesn't exist yet - keeps the number of tries in +// TempFile to a minimum. +var rand uint32 +var randmu sync.Mutex + +func reseed() uint32 { + return uint32(time.Now().UnixNano()) +} + +func nextSuffix() string { + randmu.Lock() + r := rand + if r == 0 { + r = reseed() + } + r = r*1664525 + 1013904223 // constants from Numerical Recipes + rand = r + randmu.Unlock() + return strconv.Itoa(int(1e9 + r%1e9))[1:] + "-" + strconv.Itoa(os.Getpid()) +} + +// TempFile creates a new temporary file name in the directory dir with a name +// beginning with prefix and ending with suffix, opens the file for reading and +// writing, and returns the resulting *os.File. If dir is the empty string, +// TempFile uses the default directory for temporary files (see os.TempDir). +// Multiple programs calling TempFile simultaneously will not choose the same +// file. The caller can use f.Name() to find the pathname of the file. It is +// the caller's responsibility to remove the file when no longer needed. +func TempFile(dir, prefix string, suffix string) (pth string, err error) { + if dir == "" { + dir = os.TempDir() + } + + var ( + nconflict int + f *os.File + ) + + for i := 0; i < 10000; i++ { + name := filepath.Join(dir, prefix+nextSuffix()+suffix) + f, err = os.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + if os.IsExist(err) { + if nconflict++; nconflict > 10 { + randmu.Lock() + rand = reseed() + randmu.Unlock() + } + continue + } + f.Close() + return name, nil + } + return +} diff --git a/template.go b/template.go new file mode 100644 index 0000000..5cc6065 --- /dev/null +++ b/template.go @@ -0,0 +1,43 @@ +package xbindata + +import ( + "bytes" + "context" + "errors" + "fmt" + "path" + "path/filepath" + "strings" + "text/template" +) + +func (i *ManyConfigInput) format(ctx context.Context, key, value string, envs ...map[string]string) (v string, err error) { + defer func() { + if err != nil { + err = errors.New(fmt.Sprint("format{"+key+"}:", err.Error())) + } + }() + + env := Env(append([]map[string]string{ContextEnv(ctx)}, envs...)...) + if strings.ContainsRune(value, '{') { + var t *template.Template + if t, err = template.New("").Parse(key); err != nil { + return + } + var buf bytes.Buffer + if err = t.Execute(&buf, map[string]interface{}{ + "pclean": path.Clean, + "pjoin": path.Join, + "fpclean": filepath.Clean, + "fpjoin": filepath.Join, + "Env": env, + "PKG": i.GetPkg(), + }); err != nil { + return + } + v = buf.String() + } else { + v = value + } + return +} diff --git a/xb/cmd/build.go b/xb/cmd/build.go index f748cda..f46a6e4 100644 --- a/xb/cmd/build.go +++ b/xb/cmd/build.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "fmt" "log" "os" @@ -109,10 +110,12 @@ var ( cfg.Outlined, cfg.Embedded = outline, embedded } + ctx := context.Background() + for i, cfg := range cfg.Outlined { log.Println("==== cfg config #"+strconv.Itoa(i)+":", cfg.Pkg, " ====") var c *xbindata.Config - if c, err = cfg.Config(); err != nil { + if c, err = cfg.Config(ctx); err != nil { return fmt.Errorf("cfg #%d [%s]: create config failed: %v", i, cfg.Pkg, err) } if err = xbindata.Translate(c); err != nil { @@ -123,7 +126,7 @@ var ( for i, cfg := range cfg.Embedded { log.Println("==== embeded config #"+strconv.Itoa(i)+":", cfg.Pkg, " ====") var c *xbindata.Config - if c, err = cfg.Config(); err != nil { + if c, err = cfg.Config(ctx); err != nil { return fmt.Errorf("cfg #%d [%s]: create config failed: %v", i, cfg.Pkg, err) } if err = xbindata.Translate(c); err != nil { diff --git a/xb/cmd/buildProgram.go b/xb/cmd/buildProgram.go index a2f1ab1..39496e3 100644 --- a/xb/cmd/buildProgram.go +++ b/xb/cmd/buildProgram.go @@ -15,6 +15,7 @@ package cmd import ( + "context" "io" "log" "os" @@ -153,7 +154,7 @@ Examples: for _, cfg := range cfg.Outlined { if cfg.Program { var c *xbindata.Config - c, err = cfg.Config() + c, err = cfg.Config(context.Background()) if err != nil { return err }