Merge branch 'Release-0.9'
diff --git a/README.md b/README.md
index 6ab8cde..6b1a900 100644
--- a/README.md
+++ b/README.md
@@ -408,13 +408,13 @@
work and lots of extra space in your VCS. There may also be unforeseen errors
([see an example](https://github.com/mattfarina/golang-broken-vendor)).
-#### Q: How do I import settings from GPM, Godep, or gb?
+#### Q: How do I import settings from GPM, Godep, gom or gb?
There are two parts to importing.
-1. If a package you import has configuration for GPM, Godep, or gb Glide will
+1. If a package you import has configuration for GPM, Godep, gom or gb Glide will
recursively install the dependencies automatically.
-2. If you would like to import configuration from GPM, Godep, or gb to Glide see
+2. If you would like to import configuration from GPM, Godep, gom or gb to Glide see
the `glide import` command. For example, you can run `glide import godep` for
Glide to detect the projects Godep configuration and generate a `glide.yaml`
file for you.
diff --git a/action/import_gom.go b/action/import_gom.go
new file mode 100644
index 0000000..87ab616
--- /dev/null
+++ b/action/import_gom.go
@@ -0,0 +1,21 @@
+package action
+
+import (
+ "github.com/Masterminds/glide/gom"
+ "github.com/Masterminds/glide/msg"
+)
+
+// ImportGom imports a Gomfile.
+func ImportGom(dest string) {
+ base := "."
+ config := EnsureConfig()
+ if !gom.Has(base) {
+ msg.Die("No gom data found.")
+ }
+ deps, err := gom.Parse(base)
+ if err != nil {
+ msg.Die("Failed to extract Gomfile: %s", err)
+ }
+ appendImports(deps, config)
+ writeConfigToFileOrStdout(config, dest)
+}
diff --git a/glide.go b/glide.go
index 26dc1b5..6d4bed5 100644
--- a/glide.go
+++ b/glide.go
@@ -307,6 +307,19 @@
action.ImportGB(c.String("file"))
},
},
+ {
+ Name: "gom",
+ Usage: "Import Gomfile and display the would-be yaml file",
+ Flags: []cli.Flag{
+ cli.StringFlag{
+ Name: "file, f",
+ Usage: "Save all of the discovered dependencies to a Glide YAML file.",
+ },
+ },
+ Action: func(c *cli.Context) {
+ action.ImportGom(c.String("file"))
+ },
+ },
},
},
{
diff --git a/gom/gom.go b/gom/gom.go
new file mode 100644
index 0000000..fae091a
--- /dev/null
+++ b/gom/gom.go
@@ -0,0 +1,110 @@
+package gom
+
+import (
+ "errors"
+ "os"
+ "path/filepath"
+
+ "github.com/Masterminds/glide/cfg"
+ "github.com/Masterminds/glide/msg"
+ "github.com/Masterminds/glide/util"
+)
+
+// Has returns true if this dir has a Gomfile.
+func Has(dir string) bool {
+ path := filepath.Join(dir, "Gomfile")
+ fi, err := os.Stat(path)
+ return err == nil && !fi.IsDir()
+}
+
+// Parse parses a Gomfile.
+func Parse(dir string) ([]*cfg.Dependency, error) {
+ path := filepath.Join(dir, "Gomfile")
+ if fi, err := os.Stat(path); err != nil || fi.IsDir() {
+ return []*cfg.Dependency{}, nil
+ }
+
+ msg.Info("Found Gomfile.\n")
+ buf := []*cfg.Dependency{}
+
+ goms, err := parseGomfile(path)
+ if err != nil {
+ return []*cfg.Dependency{}, err
+ }
+
+ for _, gom := range goms {
+ // Do we need to skip this dependency?
+ if val, ok := gom.options["skipdep"]; ok && val.(string) == "true" {
+ continue
+ }
+
+ // Check for custom cloning command
+ if _, ok := gom.options["command"]; ok {
+ return []*cfg.Dependency{}, errors.New("Glide does not support custom Gomfile commands")
+ }
+
+ // Check for groups/environments
+ if val, ok := gom.options["group"]; ok {
+ groups := toStringSlice(val)
+ if !stringsContain(groups, "development") && !stringsContain(groups, "production") {
+ // right now we only support development and production
+ msg.Info("Skipping dependency '%s' because it isn't in the development or production group", gom.name)
+ continue
+ }
+ }
+
+ pkg, sub := util.NormalizeName(gom.name)
+
+ dep := &cfg.Dependency{
+ Name: pkg,
+ }
+
+ if len(sub) > 0 {
+ dep.Subpackages = []string{sub}
+ }
+
+ // Check for a specific revision
+ if val, ok := gom.options["commit"]; ok {
+ dep.Reference = val.(string)
+ }
+ if val, ok := gom.options["tag"]; ok {
+ dep.Reference = val.(string)
+ }
+ if val, ok := gom.options["branch"]; ok {
+ dep.Reference = val.(string)
+ }
+
+ // Parse goos and goarch
+ if val, ok := gom.options["goos"]; ok {
+ dep.Os = toStringSlice(val)
+ }
+ if val, ok := gom.options["goarch"]; ok {
+ dep.Arch = toStringSlice(val)
+ }
+
+ buf = append(buf, dep)
+ }
+
+ return buf, nil
+}
+
+func stringsContain(v []string, key string) bool {
+ for _, s := range v {
+ if s == key {
+ return true
+ }
+ }
+ return false
+}
+
+func toStringSlice(v interface{}) []string {
+ if v, ok := v.(string); ok {
+ return []string{v}
+ }
+
+ if v, ok := v.([]string); ok {
+ return v
+ }
+
+ return []string{}
+}
\ No newline at end of file
diff --git a/gom/parser.go b/gom/parser.go
new file mode 100644
index 0000000..bf54f61
--- /dev/null
+++ b/gom/parser.go
@@ -0,0 +1,130 @@
+package gom
+
+// This is copied + slightly adapted from gom's `gomfile.go` file.
+//
+// gom's license is MIT-style.
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "strings"
+)
+
+var qx = `'[^']*'|"[^"]*"`
+var kx = `:[a-z][a-z0-9_]*`
+var ax = `(?:\s*` + kx + `\s*|,\s*` + kx + `\s*)`
+var re_group = regexp.MustCompile(`\s*group\s+((?:` + kx + `\s*|,\s*` + kx + `\s*)*)\s*do\s*$`)
+var re_end = regexp.MustCompile(`\s*end\s*$`)
+var re_gom = regexp.MustCompile(`^\s*gom\s+(` + qx + `)\s*((?:,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*))*)$`)
+var re_options = regexp.MustCompile(`(,\s*` + kx + `\s*=>\s*(?:` + qx + `|\s*\[\s*` + ax + `*\s*\]\s*)\s*)`)
+
+func unquote(name string) string {
+ name = strings.TrimSpace(name)
+ if len(name) > 2 {
+ if (name[0] == '\'' && name[len(name)-1] == '\'') || (name[0] == '"' && name[len(name)-1] == '"') {
+ return name[1 : len(name)-1]
+ }
+ }
+ return name
+}
+
+func parseOptions(line string, options map[string]interface{}) {
+ ss := re_options.FindAllStringSubmatch(line, -1)
+ re_a := regexp.MustCompile(ax)
+ for _, s := range ss {
+ kvs := strings.SplitN(strings.TrimSpace(s[0])[1:], "=>", 2)
+ kvs[0], kvs[1] = strings.TrimSpace(kvs[0]), strings.TrimSpace(kvs[1])
+ if kvs[1][0] == '[' {
+ as := re_a.FindAllStringSubmatch(kvs[1][1:len(kvs[1])-1], -1)
+ a := []string{}
+ for i := range as {
+ it := strings.TrimSpace(as[i][0])
+ if strings.HasPrefix(it, ",") {
+ it = strings.TrimSpace(it[1:])
+ }
+ if strings.HasPrefix(it, ":") {
+ it = strings.TrimSpace(it[1:])
+ }
+ a = append(a, it)
+ }
+ options[kvs[0][1:]] = a
+ } else {
+ options[kvs[0][1:]] = unquote(kvs[1])
+ }
+ }
+}
+
+type Gom struct {
+ name string
+ options map[string]interface{}
+}
+
+func parseGomfile(filename string) ([]Gom, error) {
+ f, err := os.Open(filename + ".lock")
+ if err != nil {
+ f, err = os.Open(filename)
+ if err != nil {
+ return nil, err
+ }
+ }
+ br := bufio.NewReader(f)
+
+ goms := make([]Gom, 0)
+
+ n := 0
+ skip := 0
+ valid := true
+ var envs []string
+ for {
+ n++
+ lb, _, err := br.ReadLine()
+ if err != nil {
+ if err == io.EOF {
+ return goms, nil
+ }
+ return nil, err
+ }
+ line := strings.TrimSpace(string(lb))
+ if line == "" || strings.HasPrefix(line, "#") {
+ continue
+ }
+
+ name := ""
+ options := make(map[string]interface{})
+ var items []string
+ if re_group.MatchString(line) {
+ envs = strings.Split(re_group.FindStringSubmatch(line)[1], ",")
+ for i := range envs {
+ envs[i] = strings.TrimSpace(envs[i])[1:]
+ }
+ valid = true
+ continue
+ } else if re_end.MatchString(line) {
+ if !valid {
+ skip--
+ if skip < 0 {
+ return nil, fmt.Errorf("Syntax Error at line %d", n)
+ }
+ }
+ valid = false
+ envs = nil
+ continue
+ } else if skip > 0 {
+ continue
+ } else if re_gom.MatchString(line) {
+ items = re_gom.FindStringSubmatch(line)[1:]
+ name = unquote(items[0])
+ parseOptions(items[1], options)
+ } else {
+ return nil, fmt.Errorf("Syntax Error at line %d", n)
+ }
+ if envs != nil {
+ options["group"] = envs
+ }
+ goms = append(goms, Gom{name, options})
+ }
+ return goms, nil
+}
\ No newline at end of file
diff --git a/importer/importer.go b/importer/importer.go
index 51a9ba5..9d5d2ac 100644
--- a/importer/importer.go
+++ b/importer/importer.go
@@ -1,4 +1,4 @@
-// Package importer imports dependency configuration from Glide, Godep, GPM, and GB
+// Package importer imports dependency configuration from Glide, Godep, GPM, GB and gom
package importer
import (
@@ -9,12 +9,13 @@
"github.com/Masterminds/glide/cfg"
"github.com/Masterminds/glide/gb"
"github.com/Masterminds/glide/godep"
+ "github.com/Masterminds/glide/gom"
"github.com/Masterminds/glide/gpm"
)
var i = &DefaultImporter{}
-// Import uses the DefaultImporter to import from Glide, Godep, GPM, and GB.
+// Import uses the DefaultImporter to import from Glide, Godep, GPM, GB and gom.
func Import(path string) (bool, []*cfg.Dependency, error) {
return i.Import(path)
}
@@ -29,10 +30,10 @@
Import(path string) (bool, []*cfg.Dependency, error)
}
-// DefaultImporter imports from Glide, Godep, GPM, and GB.
+// DefaultImporter imports from Glide, Godep, GPM, GB and gom.
type DefaultImporter struct{}
-// Import tries to import configuration from Glide, Godep, GPM, and GB.
+// Import tries to import configuration from Glide, Godep, GPM, GB and gom.
func (d *DefaultImporter) Import(path string) (bool, []*cfg.Dependency, error) {
// Try importing from Glide first.
@@ -77,6 +78,15 @@
return true, deps, nil
}
+ // Try importing from gom
+ if gom.Has(path) {
+ deps, err := gom.Parse(path)
+ if err != nil {
+ return false, []*cfg.Dependency{}, err
+ }
+ return true, deps, nil
+ }
+
// When none are found.
return false, []*cfg.Dependency{}, nil
}