Parse build tags and scan a package based on those
diff --git a/dependency/scan.go b/dependency/scan.go
index 5b99858..1b5ac65 100644
--- a/dependency/scan.go
+++ b/dependency/scan.go
@@ -1,7 +1,12 @@
 package dependency
 
 import (
+	"bytes"
+	"io"
+	"os"
+	"path/filepath"
 	"strings"
+	"text/scanner"
 
 	"github.com/Masterminds/glide/msg"
 	"github.com/Masterminds/glide/util"
@@ -32,40 +37,242 @@
 // of ignore. This is a bit of a hack. It causes UseAllFiles to have errors.
 func IterativeScan(path string) ([]string, error) {
 
+	// TODO(mattfarina): Add support for release tags.
+
+	tgs, _ := readBuildTags(path)
+	// Handle the case of scanning with no tags
+	tgs = append(tgs, "")
+
 	var pkgs []string
-	for _, o := range osList {
-		for _, a := range archList {
-			b, err := util.GetBuildContext()
-			if err != nil {
-				return []string{}, err
+	for _, tt := range tgs {
+
+		// split the tag combination to look at permutations.
+		ts := strings.Split(tt, ",")
+		var ttgs []string
+		var arch string
+		var ops string
+		for _, ttt := range ts {
+			dirty := false
+			if strings.HasPrefix(ttt, "!") {
+				dirty = true
+				ttt = strings.TrimPrefix(ttt, "!")
 			}
-
-			// Make sure use all files is off
-			b.UseAllFiles = false
-
-			// Set the OS and Arch for this pass
-			b.GOARCH = a
-			b.GOOS = o
-
-			pk, err := b.ImportDir(path, 0)
-			if err != nil {
-				msg.Debug("Problem parsing package at %s for %s %s", path, o, a)
-				return []string{}, err
+			if isSupportedOs(ttt) {
+				if dirty {
+					ops = getOsValue(ttt)
+				} else {
+					ops = ttt
+				}
+			} else if isSupportedArch(ttt) {
+				if dirty {
+					arch = getArchValue(ttt)
+				} else {
+					arch = ttt
+				}
+			} else {
+				if !dirty {
+					ttgs = append(ttgs, ttt)
+				}
 			}
+		}
 
-			for _, dep := range pk.Imports {
-				found := false
-				for _, p := range pkgs {
-					if p == dep {
-						found = true
-					}
+		// Handle the case where there are no tags but we need to iterate
+		// on something.
+		if len(ttgs) == 0 {
+			ttgs = append(ttgs, "")
+		}
+
+		b, err := util.GetBuildContext()
+		if err != nil {
+			return []string{}, err
+		}
+
+		// Make sure use all files is off
+		b.UseAllFiles = false
+
+		// Set the OS and Arch for this pass
+		b.GOARCH = arch
+		b.GOOS = ops
+		b.BuildTags = ttgs
+		msg.Debug("Scanning with Arch(%s), OS(%s), and Build Tags(%v)", arch, ops, ttgs)
+
+		pk, err := b.ImportDir(path, 0)
+		if err != nil && strings.HasPrefix(err.Error(), "no buildable Go source files in") {
+			continue
+		} else if err != nil {
+			msg.Debug("Problem parsing package at %s for %s %s", path, ops, arch)
+			return []string{}, err
+		}
+
+		for _, dep := range pk.Imports {
+			found := false
+			for _, p := range pkgs {
+				if p == dep {
+					found = true
 				}
-				if !found {
-					pkgs = append(pkgs, dep)
-				}
+			}
+			if !found {
+				pkgs = append(pkgs, dep)
 			}
 		}
 	}
 
 	return pkgs, nil
 }
+
+func readBuildTags(p string) ([]string, error) {
+	_, err := os.Stat(p)
+	if err != nil {
+		return []string{}, err
+	}
+
+	d, err := os.Open(p)
+	if err != nil {
+		return []string{}, err
+	}
+
+	objects, err := d.Readdir(-1)
+	if err != nil {
+		return []string{}, err
+	}
+
+	var tags []string
+	for _, obj := range objects {
+
+		// only process Go files
+		if strings.HasSuffix(obj.Name(), ".go") {
+			fp := filepath.Join(p, obj.Name())
+
+			co, err := readGoContents(fp)
+			if err != nil {
+				return []string{}, err
+			}
+
+			// Only look at places where we had a code comment.
+			if len(co) > 0 {
+				t := findTags(co)
+				for _, tg := range t {
+					found := false
+					for _, tt := range tags {
+						if tt == tg {
+							found = true
+						}
+					}
+					if !found {
+						tags = append(tags, tg)
+					}
+				}
+			}
+		}
+	}
+
+	return tags, nil
+}
+
+// Read contents of a Go file up to the package declaration. This can be used
+// to find the the build tags.
+func readGoContents(fp string) ([]byte, error) {
+	f, err := os.Open(fp)
+	defer f.Close()
+	if err != nil {
+		return []byte{}, err
+	}
+
+	var s scanner.Scanner
+	s.Init(f)
+	var tok rune
+	var pos scanner.Position
+	for tok != scanner.EOF {
+		tok = s.Scan()
+
+		// Getting the token text will skip comments by default.
+		tt := s.TokenText()
+		// build tags will not be after the package declaration.
+		if tt == "package" {
+			pos = s.Position
+			break
+		}
+	}
+
+	buf := bytes.NewBufferString("")
+	f.Seek(0, 0)
+	_, err = io.CopyN(buf, f, int64(pos.Offset))
+	if err != nil {
+		return []byte{}, err
+	}
+
+	return buf.Bytes(), nil
+}
+
+// From a byte slice of a Go file find the tags.
+func findTags(co []byte) []string {
+	p := co
+	var tgs []string
+	for len(p) > 0 {
+		line := p
+		if i := bytes.IndexByte(line, '\n'); i >= 0 {
+			line, p = line[:i], p[i+1:]
+		} else {
+			p = p[len(p):]
+		}
+		line = bytes.TrimSpace(line)
+		// Only look at comment lines that are well formed in the Go style
+		if bytes.HasPrefix(line, []byte("//")) {
+			line = bytes.TrimSpace(line[len([]byte("//")):])
+			if len(line) > 0 && line[0] == '+' {
+				f := strings.Fields(string(line))
+
+				// We've found a +build tag line.
+				if f[0] == "+build" {
+					for _, tg := range f[1:] {
+						tgs = append(tgs, tg)
+					}
+				}
+			}
+		}
+	}
+
+	return tgs
+}
+
+// Get an OS value that's not the one passed in.
+func getOsValue(n string) string {
+	for _, o := range osList {
+		if o != n {
+			return o
+		}
+	}
+
+	return n
+}
+
+func isSupportedOs(n string) bool {
+	for _, o := range osList {
+		if o == n {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Get an Arch value that's not the one passed in.
+func getArchValue(n string) string {
+	for _, o := range archList {
+		if o != n {
+			return o
+		}
+	}
+
+	return n
+}
+
+func isSupportedArch(n string) bool {
+	for _, o := range archList {
+		if o == n {
+			return true
+		}
+	}
+
+	return false
+}