diff --git a/Gopkg.lock b/Gopkg.lock
index a6d4f970e..0e5c0b487 100644
--- a/Gopkg.lock
+++ b/Gopkg.lock
@@ -118,6 +118,15 @@
packages = ["."]
revision = "0b12d6b5"
+[[projects]]
+ name = "github.com/kisielk/gotool"
+ packages = [
+ ".",
+ "internal/load"
+ ]
+ revision = "80517062f582ea3340cd4baf70e86d539ae7d84d"
+ version = "v1.0.0"
+
[[projects]]
branch = "master"
name = "github.com/lib/pq"
@@ -154,6 +163,12 @@
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
+[[projects]]
+ branch = "master"
+ name = "github.com/rogpeppe/godeps"
+ packages = ["build"]
+ revision = "404a7e748cd352bb0d7449dedc645546eebbfc6e"
+
[[projects]]
name = "github.com/rs/xid"
packages = ["."]
@@ -195,6 +210,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
- inputs-digest = "5251c9b92d6c52a0dcefa0d8d2388deaa287802827c76bacdfba819e90706707"
+ inputs-digest = "3ec44a399b5e6d46c6601a3e6c85d875044a948e8cc1de3e8b2e3951198f986d"
solver-name = "gps-cdcl"
solver-version = 1
diff --git a/Gopkg.toml b/Gopkg.toml
index a7c8d1596..dc5aad505 100644
--- a/Gopkg.toml
+++ b/Gopkg.toml
@@ -48,3 +48,7 @@
[[constraint]]
branch = "master"
name = "github.com/lib/pq"
+
+[[constraint]]
+ branch = "master"
+ name = "github.com/rogpeppe/godeps"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 000000000..d7f105139
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/README.md b/README.md
index f12227016..a35ac3379 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@ baur will implement:
- detect if build artifacts for an application version already exist or if it's
need to be build
-Baur makes certain Assumptions:
+baur makes certain Assumptions:
- the baur repository is part of a git repository,
- an application directory only contains one application,
- an application can be build by running a single command,
@@ -25,7 +25,7 @@ Baur makes certain Assumptions:
To build baur run `make`.
## Dependencies
-- The git command lines tools are used to retrieve informations in a baur
+- The git command lines tools are used to retrieve information in a baur
repository. The tools must be installed and be in one of the paths of the
`$PATH` environment variable.
@@ -95,6 +95,20 @@ The docker image is specified by it's manifest digest.
The manifest digest for a docker image can be retrieved with
`docker images --digests` or `docker inspect`
+#### `[Build.Input.GolangSources]`
+Allows to add Golang applications as inputs.
+The `paths` parameters take a list of paths to directories relative to the
+application directory.
+In every directory `.go` files are discovered and the files they depend on.
+Imports in the `.go` files are evaluated, resolved to files and tracked as build
+inputs.
+Go test files and imports belong to the Golang stdlib are ignored.
+
+To be able to resolve the imports either the `GOPATH` environment variable must
+be set correctly or alternatively the `go_path` parameter in the config section
+must be set the `GOPATH`. The `go_path` expects a path relative to the
+application directory.
+
### Build Outputs
Build outputs are the results that are produced by a build. They can be
described in the `[Build.Output]` section.
diff --git a/app.go b/app.go
index 11774584d..92f6a09ec 100644
--- a/app.go
+++ b/app.go
@@ -46,10 +46,15 @@ func replaceGitCommitVar(in string, r *Repository) (string, error) {
func (a *App) setInputsFromCfg(r *Repository, cfg *cfg.App) error {
sliceLen := len(cfg.Build.Input.Files.Paths) + len(cfg.Build.Input.DockerImage)
+
if len(cfg.Build.Input.GitFiles.Paths) > 0 {
sliceLen++
}
+ if len(cfg.Build.Input.GolangSources.Paths) > 0 {
+ sliceLen++
+ }
+
a.BuildInputPaths = make([]BuildInputPathResolver, 0, sliceLen)
for _, p := range cfg.Build.Input.Files.Paths {
@@ -65,6 +70,17 @@ func (a *App) setInputsFromCfg(r *Repository, cfg *cfg.App) error {
a.BuildInputPaths = append(a.BuildInputPaths, &DockerImageRef{Repository: d.Repository, Digest: d.Digest})
}
+ if len(cfg.Build.Input.GolangSources.Paths) > 0 {
+ var gopath string
+
+ if len(cfg.Build.Input.GolangSources.GoPath) > 0 {
+ gopath = filepath.Join(a.Path, cfg.Build.Input.GolangSources.GoPath)
+ }
+
+ a.BuildInputPaths = append(a.BuildInputPaths,
+ NewGoSrcDirs(r.Path, a.RelPath, gopath, cfg.Build.Input.GolangSources.Paths))
+ }
+
return nil
}
diff --git a/cfg/app.go b/cfg/app.go
index 8280ea9b4..45b2ca8ff 100644
--- a/cfg/app.go
+++ b/cfg/app.go
@@ -26,9 +26,16 @@ type Build struct {
// BuildInput contains information about inputs (sources, compiler, docker
// images) for an build
type BuildInput struct {
- Files FileInputs `comment:"file paths, e.g: source files, the used compiler binary "`
- GitFiles GitFileInputs `comment:"If the baur repository is part of a git repository, this option can be used to specify source files tracked by git."`
- DockerImage []*DockerImageInput `comment:"docker images that are used to build the application or affect in other ways the produces artifact"`
+ Files FileInputs `comment:"file paths, e.g: source files, the used compiler binary "`
+ GitFiles GitFileInputs `comment:"If the baur repository is part of a git repository, this option can be used to specify source files tracked by git."`
+ GolangSources GolangSources `comment:"Directories containing Golang applications, all source files to build the application are located and added as inputs (excluding stdlib and test files)"`
+ DockerImage []*DockerImageInput `comment:"docker images that are used to build the application or affect in other ways the produces artifact"`
+}
+
+// GolangSources specifies inputs for Golang Applications
+type GolangSources struct {
+ Paths []string `toml:"paths" comment:"paths to directories containing Golang source files" commented:"true"`
+ GoPath string `toml:"go_path" comment:"specifies the GOPATH, that is used for source file discovery, if not set or empty the current GOPATH is used. The go_path is relative to the application directory." commented:"true"`
}
// DockerImageInput specifies a docker image as build source
@@ -100,6 +107,10 @@ func ExampleApp(name string) *App {
Digest: "sha256:b1589cc882898e1e726994bbf9827953156b94d423dae8c89b56614ec298684e",
},
},
+ GolangSources: GolangSources{
+ Paths: []string{"."},
+ GoPath: "../",
+ },
},
Output: BuildOutput{
File: []*FileOutput{
@@ -236,6 +247,10 @@ func (b *BuildInput) Validate() error {
return errors.Wrap(err, "[Build.Input.Files] section contains errors")
}
+ if err := b.GolangSources.Validate(); err != nil {
+ return errors.Wrap(err, "[Build.Input.Files] section contains errors")
+ }
+
// TODO: add validation for gitfiles section
for _, d := range b.DockerImage {
@@ -247,6 +262,17 @@ func (b *BuildInput) Validate() error {
return nil
}
+// Validate validates the GolangSources section
+func (g *GolangSources) Validate() error {
+ for _, p := range g.Paths {
+ if len(p) == 0 {
+ return errors.New("a path can not be empty")
+ }
+ }
+
+ return nil
+}
+
// Validate validates the BuildOutput section
func (b *BuildOutput) Validate() error {
for _, f := range b.File {
diff --git a/golang/golang/golang.go b/golang/golang/golang.go
new file mode 100644
index 000000000..c301c1206
--- /dev/null
+++ b/golang/golang/golang.go
@@ -0,0 +1,140 @@
+// Package golang determines all Go Source files that are imported by a Go-Files
+// in a directory.
+// Most of the code is based on a slightly modified version of https://github.com/rogpeppe/showdeps
+package golang
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/kisielk/gotool"
+ "github.com/pkg/errors"
+ "github.com/rogpeppe/godeps/build"
+)
+
+// SrcFiles returns the Go source files in the passed directories plus all
+// source files of the imported packages.
+// Testfiles and stdlib dependencies are ignored.
+func SrcFiles(gopath string, dirs ...string) ([]string, error) {
+ var allFiles []string
+ ctx := build.Default
+
+ if len(gopath) > 0 {
+ ctx.GOPATH = gopath
+ }
+
+ for _, d := range dirs {
+ files, err := resolve(ctx, d)
+ if err != nil {
+ return nil, err
+ }
+
+ allFiles = append(allFiles, files...)
+ }
+
+ return allFiles, nil
+}
+
+func resolve(ctx build.Context, path string) ([]string, error) {
+ recur := true
+ pkgs := []string{"./..."}
+
+ if err := os.Chdir(path); err != nil {
+ return nil, errors.Wrapf(err, "changing cwd to %q failed", path)
+ }
+
+ pkgs = gotool.ImportPaths(pkgs)
+
+ rootPkgs := make(map[string]bool)
+ for _, pkg := range pkgs {
+ p, err := ctx.Import(pkg, path, build.FindOnly)
+ if err != nil {
+ return nil, errors.Wrapf(err, "cannot find %q", pkg)
+ }
+
+ rootPkgs[p.ImportPath] = true
+ }
+
+ allPkgs := make(map[string][]string)
+ for pkg := range rootPkgs {
+ if err := findImports(ctx, pkg, path, recur, allPkgs, rootPkgs); err != nil {
+ return nil, errors.Wrapf(err, "cannot find imports from %q", pkg)
+ }
+ }
+
+ files := make([]string, 0, len(allPkgs))
+ for pkgName := range allPkgs {
+ pkg, err := ctx.Import(pkgName, path, 0)
+ if err != nil {
+ return nil, errors.Wrapf(err, "determining imports from %q failed", pkg)
+ }
+
+ gofiles := absFilePaths(pkg, pkg.GoFiles)
+ cgofiles := absFilePaths(pkg, pkg.CgoFiles)
+
+ files = append(files, gofiles...)
+ files = append(files, cgofiles...)
+ }
+
+ return files, nil
+}
+
+func absFilePaths(pkg *build.Package, fs []string) []string {
+ res := make([]string, 0, len(fs))
+
+ for _, f := range fs {
+ res = append(res, filepath.Join(pkg.Dir, f))
+ }
+
+ return res
+}
+
+func isStdlib(pkg string) bool {
+ return !strings.Contains(strings.SplitN(pkg, "/", 2)[0], ".")
+}
+
+// findImports recursively adds all imported packages by the given
+// package (packageName) to the allPkgs map.
+func findImports(ctx build.Context, packageName, dir string, recur bool, allPkgs map[string][]string, rootPkgs map[string]bool) error {
+ if packageName == "C" {
+ return nil
+ }
+
+ pkg, err := ctx.Import(packageName, dir, 0)
+ if err != nil {
+ return errors.Wrapf(err, "cannot find %q", packageName)
+ }
+
+ // Iterate through the imports in sorted order so that we provide
+ // deterministic results.
+ for _, name := range imports(pkg, rootPkgs[pkg.ImportPath]) {
+ if isStdlib(name) {
+ continue
+ }
+
+ _, alreadyDone := allPkgs[name]
+ allPkgs[name] = append(allPkgs[name], pkg.ImportPath)
+ if recur && !alreadyDone {
+ if err := findImports(ctx, name, pkg.Dir, recur, allPkgs, rootPkgs); err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func imports(pkg *build.Package, isRoot bool) []string {
+ var res []string
+
+ for _, s := range pkg.Imports {
+ if isStdlib(s) {
+ continue
+ }
+
+ res = append(res, s)
+ }
+
+ return res
+}
diff --git a/golang/golang/golang.old b/golang/golang/golang.old
new file mode 100644
index 000000000..91d640763
--- /dev/null
+++ b/golang/golang/golang.old
@@ -0,0 +1,99 @@
+package main
+
+import (
+ "fmt"
+ "go/build"
+ "sort"
+
+ "github.com/kisielk/gotool"
+)
+
+func addPackages(m map[string]bool, ss []string) {
+ for _, s := range ss {
+ m[s] = true
+ }
+}
+
+func imports(pkg *build.Package, isRoot bool) map[string]bool {
+ const noTestDeps = true
+
+ imps := make(map[string]bool)
+ addPackages(imps, pkg.Imports)
+ if isRoot && !noTestDeps {
+ addPackages(imps, pkg.TestImports)
+ addPackages(imps, pkg.XTestImports)
+ }
+ return imps
+}
+
+func sorted(m map[string]bool) []string {
+ s := make([]string, 0, len(m))
+ for x := range m {
+ s = append(s, x)
+ }
+ sort.Strings(s)
+ return s
+}
+
+// findImports recursively adds all imported packages by the given
+// package (packageName) to the allPkgs map.
+// Source: showdeps (https://github.com/rogpeppe/showdeps)
+func findImports(packageName, dir string, recur bool, allPkgs map[string][]string, rootPkgs map[string]bool) error {
+ fmt.Printf("pkg: %s\n", packageName)
+ if packageName == "C" {
+ return nil
+ }
+ pkg, err := build.Default.Import(packageName, dir, 0)
+ if err != nil {
+ return nil
+ }
+ allPkgs[pkg.ImportPath] = allPkgs[pkg.ImportPath] // ensure the package has an entry.
+ // Iterate through the imports in sorted order so that we provide
+ // deterministic results.
+ //for _, name := range sorted(imports(pkg, rootPkgs[pkg.ImportPath])) {
+ for _, name := range sorted(imports(pkg, rootPkgs[pkg.ImportPath])) {
+ _, alreadyDone := allPkgs[name]
+ allPkgs[name] = append(allPkgs[name], pkg.ImportPath)
+ if recur && !alreadyDone {
+ if err := findImports(name, pkg.Dir, recur, allPkgs, rootPkgs); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func GetSourceFiles(path string) error {
+ const allGoPkgs = "./..."
+
+ rootPkgs := gotool.ImportPaths([]string{allGoPkgs})
+ rootPkgs := make(map[string]bool)
+ for _, pkg := range rootPkgs {
+ rp, err := build.Default.Import(pkg, path, build.FindOnly) // replace "." with app dir
+ if err != nil {
+ return err
+ }
+
+ /*
+ p, err := build.Default.Import(rp.ImportPath, path, 0)
+ if err != nil {
+ return err
+ }
+ fmt.Println(p.GoFiles)
+ */
+ }
+
+ allPkgs := make(map[string][]string)
+ for _, pkg := range rootPkgs {
+ if err := findImports(pkg, path, false, allPkgs, rootPkgs); err != nil {
+ fatalf("cannot find imports from %q: %v", pkg, err)
+ }
+ }
+ fmt.Println(pkg)
+
+ return nil
+}
+
+func main() {
+ fmt.Println(GetSourceFiles("."))
+}
diff --git a/gosrcdir.go b/gosrcdir.go
new file mode 100644
index 000000000..e032a2e86
--- /dev/null
+++ b/gosrcdir.go
@@ -0,0 +1,76 @@
+package baur
+
+import (
+ "fmt"
+ "path/filepath"
+ "strings"
+
+ "github.com/pkg/errors"
+ "github.com/simplesurance/baur/fs"
+ "github.com/simplesurance/baur/golang/golang"
+ "github.com/simplesurance/baur/log"
+)
+
+// GoSrcDirs resolves Golang source files in directories to files including
+// resolving all imports to files
+type GoSrcDirs struct {
+ repositoryRootPath string
+ relAppPath string
+ paths []string
+ gopath string
+}
+
+// NewGoSrcDirs returns a GoSrcDirs
+func NewGoSrcDirs(repositoryRootPath, relAppPath, gopath string, paths []string) *GoSrcDirs {
+ return &GoSrcDirs{
+ repositoryRootPath: repositoryRootPath,
+ relAppPath: relAppPath,
+ paths: paths,
+ gopath: gopath,
+ }
+}
+
+// Resolve returns list of Go src files
+func (g *GoSrcDirs) Resolve() ([]BuildInput, error) {
+ baseDir := filepath.Join(g.repositoryRootPath, g.relAppPath)
+ fullpaths := make([]string, 0, len(g.paths))
+
+ for _, p := range g.paths {
+ absPath := filepath.Join(baseDir, p)
+
+ isDir, err := fs.IsDir(absPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if !isDir {
+ return nil, fmt.Errorf("%q is not a directory", p)
+ }
+
+ fullpaths = append(fullpaths, absPath)
+ }
+
+ log.Debugf("resolving go src files, GOPATH=%q, srcdirs=%q\n", g.gopath, fullpaths)
+
+ absSrcPaths, err := golang.SrcFiles(g.gopath, fullpaths...)
+ if err != nil {
+ return nil, err
+ }
+
+ res := make([]BuildInput, 0, len(absSrcPaths))
+ for _, p := range absSrcPaths {
+ relPath, err := filepath.Rel(g.repositoryRootPath, p)
+ if err != nil {
+ return nil, errors.Wrapf(err, "converting %q to relpath with basedir %q failed", p, g.repositoryRootPath)
+ }
+
+ res = append(res, NewFile(g.repositoryRootPath, relPath))
+ }
+
+ return res, nil
+}
+
+// String returns the path
+func (g *GoSrcDirs) String() string {
+ return strings.Join(g.paths, ", ")
+}
diff --git a/vendor/github.com/kisielk/gotool/.travis.yml b/vendor/github.com/kisielk/gotool/.travis.yml
new file mode 100644
index 000000000..d1784e1e2
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/.travis.yml
@@ -0,0 +1,23 @@
+sudo: false
+language: go
+go:
+ - 1.2
+ - 1.3
+ - 1.4
+ - 1.5
+ - 1.6
+ - 1.7
+ - 1.8
+ - 1.9
+ - master
+matrix:
+ allow_failures:
+ - go: master
+ fast_finish: true
+install:
+ - # Skip.
+script:
+ - go get -t -v ./...
+ - diff -u <(echo -n) <(gofmt -d .)
+ - go tool vet .
+ - go test -v -race ./...
diff --git a/vendor/github.com/kisielk/gotool/LEGAL b/vendor/github.com/kisielk/gotool/LEGAL
new file mode 100644
index 000000000..72b859cd6
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/LEGAL
@@ -0,0 +1,32 @@
+All the files in this distribution are covered under either the MIT
+license (see the file LICENSE) except some files mentioned below.
+
+match.go, match_test.go:
+
+ Copyright (c) 2009 The Go Authors. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Google Inc. nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/kisielk/gotool/LICENSE b/vendor/github.com/kisielk/gotool/LICENSE
new file mode 100644
index 000000000..1cbf651e2
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2013 Kamil Kisiel
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/kisielk/gotool/README.md b/vendor/github.com/kisielk/gotool/README.md
new file mode 100644
index 000000000..6e4e92b2f
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/README.md
@@ -0,0 +1,6 @@
+gotool
+======
+[](https://godoc.org/github.com/kisielk/gotool)
+[](https://travis-ci.org/kisielk/gotool)
+
+Package gotool contains utility functions used to implement the standard "cmd/go" tool, provided as a convenience to developers who want to write tools with similar semantics.
diff --git a/vendor/github.com/kisielk/gotool/go.mod b/vendor/github.com/kisielk/gotool/go.mod
new file mode 100644
index 000000000..503b37c6f
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/go.mod
@@ -0,0 +1 @@
+module "github.com/kisielk/gotool"
diff --git a/vendor/github.com/kisielk/gotool/go13.go b/vendor/github.com/kisielk/gotool/go13.go
new file mode 100644
index 000000000..2dd9b3fdf
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/go13.go
@@ -0,0 +1,15 @@
+// +build !go1.4
+
+package gotool
+
+import (
+ "go/build"
+ "path/filepath"
+ "runtime"
+)
+
+var gorootSrc = filepath.Join(runtime.GOROOT(), "src", "pkg")
+
+func shouldIgnoreImport(p *build.Package) bool {
+ return true
+}
diff --git a/vendor/github.com/kisielk/gotool/go14-15.go b/vendor/github.com/kisielk/gotool/go14-15.go
new file mode 100644
index 000000000..aa99a3227
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/go14-15.go
@@ -0,0 +1,15 @@
+// +build go1.4,!go1.6
+
+package gotool
+
+import (
+ "go/build"
+ "path/filepath"
+ "runtime"
+)
+
+var gorootSrc = filepath.Join(runtime.GOROOT(), "src")
+
+func shouldIgnoreImport(p *build.Package) bool {
+ return true
+}
diff --git a/vendor/github.com/kisielk/gotool/go16-18.go b/vendor/github.com/kisielk/gotool/go16-18.go
new file mode 100644
index 000000000..f25cec14a
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/go16-18.go
@@ -0,0 +1,15 @@
+// +build go1.6,!go1.9
+
+package gotool
+
+import (
+ "go/build"
+ "path/filepath"
+ "runtime"
+)
+
+var gorootSrc = filepath.Join(runtime.GOROOT(), "src")
+
+func shouldIgnoreImport(p *build.Package) bool {
+ return p == nil || len(p.InvalidGoFiles) == 0
+}
diff --git a/vendor/github.com/kisielk/gotool/internal/load/path.go b/vendor/github.com/kisielk/gotool/internal/load/path.go
new file mode 100644
index 000000000..74e15b9d3
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/internal/load/path.go
@@ -0,0 +1,27 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.9
+
+package load
+
+import (
+ "strings"
+)
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
diff --git a/vendor/github.com/kisielk/gotool/internal/load/pkg.go b/vendor/github.com/kisielk/gotool/internal/load/pkg.go
new file mode 100644
index 000000000..b937ede75
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/internal/load/pkg.go
@@ -0,0 +1,25 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.9
+
+// Package load loads packages.
+package load
+
+import (
+ "strings"
+)
+
+// isStandardImportPath reports whether $GOROOT/src/path should be considered
+// part of the standard distribution. For historical reasons we allow people to add
+// their own code to $GOROOT instead of using $GOPATH, but we assume that
+// code will start with a domain name (dot in the first element).
+func isStandardImportPath(path string) bool {
+ i := strings.Index(path, "/")
+ if i < 0 {
+ i = len(path)
+ }
+ elem := path[:i]
+ return !strings.Contains(elem, ".")
+}
diff --git a/vendor/github.com/kisielk/gotool/internal/load/search.go b/vendor/github.com/kisielk/gotool/internal/load/search.go
new file mode 100644
index 000000000..17ed62dda
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/internal/load/search.go
@@ -0,0 +1,354 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.9
+
+package load
+
+import (
+ "fmt"
+ "go/build"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// Context specifies values for operation of ImportPaths that would
+// otherwise come from cmd/go/internal/cfg package.
+//
+// This is a construct added for gotool purposes and doesn't have
+// an equivalent upstream in cmd/go.
+type Context struct {
+ // BuildContext is the build context to use.
+ BuildContext build.Context
+
+ // GOROOTsrc is the location of the src directory in GOROOT.
+ // At this time, it's used only in MatchPackages to skip
+ // GOOROOT/src entry from BuildContext.SrcDirs output.
+ GOROOTsrc string
+}
+
+// allPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func (c *Context) allPackages(pattern string) []string {
+ pkgs := c.MatchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// allPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func (c *Context) allPackagesInFS(pattern string) []string {
+ pkgs := c.MatchPackagesInFS(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// MatchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func (c *Context) MatchPackages(pattern string) []string {
+ match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
+ if !IsMetaPackage(pattern) {
+ match = matchPattern(pattern)
+ treeCanMatch = treeCanMatchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !c.BuildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ for _, src := range c.BuildContext.SrcDirs() {
+ if (pattern == "std" || pattern == "cmd") && src != c.GOROOTsrc {
+ continue
+ }
+ src = filepath.Clean(src) + string(filepath.Separator)
+ root := src
+ if pattern == "cmd" {
+ root += "cmd" + string(filepath.Separator)
+ }
+ filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || path == src {
+ return nil
+ }
+
+ want := true
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ want = false
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
+ // The name "std" is only the standard library.
+ // If the name is cmd, it's the root of the command tree.
+ want = false
+ }
+ if !treeCanMatch(name) {
+ want = false
+ }
+
+ if !fi.IsDir() {
+ if fi.Mode()&os.ModeSymlink != 0 && want {
+ if target, err := os.Stat(path); err == nil && target.IsDir() {
+ fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
+ }
+ }
+ return nil
+ }
+ if !want {
+ return filepath.SkipDir
+ }
+
+ if have[name] {
+ return nil
+ }
+ have[name] = true
+ if !match(name) {
+ return nil
+ }
+ pkg, err := c.BuildContext.ImportDir(path, 0)
+ if err != nil {
+ if _, noGo := err.(*build.NoGoError); noGo {
+ return nil
+ }
+ }
+
+ // If we are expanding "cmd", skip main
+ // packages under cmd/vendor. At least as of
+ // March, 2017, there is one there for the
+ // vendored pprof tool.
+ if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
+ return nil
+ }
+
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ }
+ return pkgs
+}
+
+// MatchPackagesInFS returns a list of package paths matching pattern,
+// which must begin with ./ or ../
+// (see go help packages for pattern syntax).
+func (c *Context) MatchPackagesInFS(pattern string) []string {
+ // Find directory to begin the scan.
+ // Could be smarter but this one optimization
+ // is enough for now, since ... is usually at the
+ // end of a path.
+ i := strings.Index(pattern, "...")
+ dir, _ := path.Split(pattern[:i])
+
+ // pattern begins with ./ or ../.
+ // path.Clean will discard the ./ but not the ../.
+ // We need to preserve the ./ for pattern matching
+ // and in the returned import paths.
+ prefix := ""
+ if strings.HasPrefix(pattern, "./") {
+ prefix = "./"
+ }
+ match := matchPattern(pattern)
+
+ var pkgs []string
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() {
+ return nil
+ }
+ if path == dir {
+ // filepath.Walk starts at dir and recurses. For the recursive case,
+ // the path is the result of filepath.Join, which calls filepath.Clean.
+ // The initial case is not Cleaned, though, so we do this explicitly.
+ //
+ // This converts a path like "./io/" to "io". Without this step, running
+ // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
+ // package, because prepending the prefix "./" to the unclean path would
+ // result in "././io", and match("././io") returns false.
+ path = filepath.Clean(path)
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
+ _, elem := filepath.Split(path)
+ dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+ if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := prefix + filepath.ToSlash(path)
+ if !match(name) {
+ return nil
+ }
+
+ // We keep the directory if we can import it, or if we can't import it
+ // due to invalid Go source files. This means that directories containing
+ // parse errors will be built (and fail) instead of being silently skipped
+ // as not matching the pattern. Go 1.5 and earlier skipped, but that
+ // behavior means people miss serious mistakes.
+ // See golang.org/issue/11407.
+ if p, err := c.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
+ return nil
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ return pkgs
+}
+
+// treeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func treeCanMatchPattern(pattern string) func(name string) bool {
+ wildCard := false
+ if i := strings.Index(pattern, "..."); i >= 0 {
+ wildCard = true
+ pattern = pattern[:i]
+ }
+ return func(name string) bool {
+ return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+ wildCard && strings.HasPrefix(name, pattern)
+ }
+}
+
+// matchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+// Unfortunately, there are two special cases. Quoting "go help packages":
+//
+// First, /... at the end of the pattern can match an empty string,
+// so that net/... matches both net and packages in its subdirectories, like net/http.
+// Second, any slash-separted pattern element containing a wildcard never
+// participates in a match of the "vendor" element in the path of a vendored
+// package, so that ./... does not match packages in subdirectories of
+// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
+// Note, however, that a directory named vendor that itself contains code
+// is not a vendored package: cmd/vendor would be a command named vendor,
+// and the pattern cmd/... matches it.
+func matchPattern(pattern string) func(name string) bool {
+ // Convert pattern to regular expression.
+ // The strategy for the trailing /... is to nest it in an explicit ? expression.
+ // The strategy for the vendor exclusion is to change the unmatchable
+ // vendor strings to a disallowed code point (vendorChar) and to use
+ // "(anything but that codepoint)*" as the implementation of the ... wildcard.
+ // This is a bit complicated but the obvious alternative,
+ // namely a hand-written search like in most shell glob matchers,
+ // is too easy to make accidentally exponential.
+ // Using package regexp guarantees linear-time matching.
+
+ const vendorChar = "\x00"
+
+ if strings.Contains(pattern, vendorChar) {
+ return func(name string) bool { return false }
+ }
+
+ re := regexp.QuoteMeta(pattern)
+ re = replaceVendor(re, vendorChar)
+ switch {
+ case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
+ re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case re == vendorChar+`/\.\.\.`:
+ re = `(/vendor|/` + vendorChar + `/\.\.\.)`
+ case strings.HasSuffix(re, `/\.\.\.`):
+ re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
+ }
+ re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
+
+ reg := regexp.MustCompile(`^` + re + `$`)
+
+ return func(name string) bool {
+ if strings.Contains(name, vendorChar) {
+ return false
+ }
+ return reg.MatchString(replaceVendor(name, vendorChar))
+ }
+}
+
+// replaceVendor returns the result of replacing
+// non-trailing vendor path elements in x with repl.
+func replaceVendor(x, repl string) string {
+ if !strings.Contains(x, "vendor") {
+ return x
+ }
+ elem := strings.Split(x, "/")
+ for i := 0; i < len(elem)-1; i++ {
+ if elem[i] == "vendor" {
+ elem[i] = repl
+ }
+ }
+ return strings.Join(elem, "/")
+}
+
+// ImportPaths returns the import paths to use for the given command line.
+func (c *Context) ImportPaths(args []string) []string {
+ args = c.ImportPathsNoDotExpansion(args)
+ var out []string
+ for _, a := range args {
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, c.allPackagesInFS(a)...)
+ } else {
+ out = append(out, c.allPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// ImportPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+func (c *Context) ImportPathsNoDotExpansion(args []string) []string {
+ if len(args) == 0 {
+ return []string{"."}
+ }
+ var out []string
+ for _, a := range args {
+ // Arguments are supposed to be import paths, but
+ // as a courtesy to Windows developers, rewrite \ to /
+ // in command-line arguments. Handles .\... and so on.
+ if filepath.Separator == '\\' {
+ a = strings.Replace(a, `\`, `/`, -1)
+ }
+
+ // Put argument in canonical form, but preserve leading ./.
+ if strings.HasPrefix(a, "./") {
+ a = "./" + path.Clean(a)
+ if a == "./." {
+ a = "."
+ }
+ } else {
+ a = path.Clean(a)
+ }
+ if IsMetaPackage(a) {
+ out = append(out, c.allPackages(a)...)
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func IsMetaPackage(name string) bool {
+ return name == "std" || name == "cmd" || name == "all"
+}
diff --git a/vendor/github.com/kisielk/gotool/match.go b/vendor/github.com/kisielk/gotool/match.go
new file mode 100644
index 000000000..4dbdbff47
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/match.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2009 The Go Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// +build go1.9
+
+package gotool
+
+import (
+ "path/filepath"
+
+ "github.com/kisielk/gotool/internal/load"
+)
+
+// importPaths returns the import paths to use for the given command line.
+func (c *Context) importPaths(args []string) []string {
+ lctx := load.Context{
+ BuildContext: c.BuildContext,
+ GOROOTsrc: c.joinPath(c.BuildContext.GOROOT, "src"),
+ }
+ return lctx.ImportPaths(args)
+}
+
+// joinPath calls c.BuildContext.JoinPath (if not nil) or else filepath.Join.
+//
+// It's a copy of the unexported build.Context.joinPath helper.
+func (c *Context) joinPath(elem ...string) string {
+ if f := c.BuildContext.JoinPath; f != nil {
+ return f(elem...)
+ }
+ return filepath.Join(elem...)
+}
diff --git a/vendor/github.com/kisielk/gotool/match18.go b/vendor/github.com/kisielk/gotool/match18.go
new file mode 100644
index 000000000..6d6b1368c
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/match18.go
@@ -0,0 +1,317 @@
+// Copyright (c) 2009 The Go Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// +build !go1.9
+
+package gotool
+
+import (
+ "fmt"
+ "go/build"
+ "log"
+ "os"
+ "path"
+ "path/filepath"
+ "regexp"
+ "strings"
+)
+
+// This file contains code from the Go distribution.
+
+// matchPattern(pattern)(name) reports whether
+// name matches pattern. Pattern is a limited glob
+// pattern in which '...' means 'any string' and there
+// is no other special syntax.
+func matchPattern(pattern string) func(name string) bool {
+ re := regexp.QuoteMeta(pattern)
+ re = strings.Replace(re, `\.\.\.`, `.*`, -1)
+ // Special case: foo/... matches foo too.
+ if strings.HasSuffix(re, `/.*`) {
+ re = re[:len(re)-len(`/.*`)] + `(/.*)?`
+ }
+ reg := regexp.MustCompile(`^` + re + `$`)
+ return reg.MatchString
+}
+
+// matchPackages returns a list of package paths matching pattern
+// (see go help packages for pattern syntax).
+func (c *Context) matchPackages(pattern string) []string {
+ match := func(string) bool { return true }
+ treeCanMatch := func(string) bool { return true }
+ if !isMetaPackage(pattern) {
+ match = matchPattern(pattern)
+ treeCanMatch = treeCanMatchPattern(pattern)
+ }
+
+ have := map[string]bool{
+ "builtin": true, // ignore pseudo-package that exists only for documentation
+ }
+ if !c.BuildContext.CgoEnabled {
+ have["runtime/cgo"] = true // ignore during walk
+ }
+ var pkgs []string
+
+ for _, src := range c.BuildContext.SrcDirs() {
+ if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
+ continue
+ }
+ src = filepath.Clean(src) + string(filepath.Separator)
+ root := src
+ if pattern == "cmd" {
+ root += "cmd" + string(filepath.Separator)
+ }
+ filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == src {
+ return nil
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees.
+ _, elem := filepath.Split(path)
+ if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
+ // The name "std" is only the standard library.
+ // If the name is cmd, it's the root of the command tree.
+ return filepath.SkipDir
+ }
+ if !treeCanMatch(name) {
+ return filepath.SkipDir
+ }
+ if have[name] {
+ return nil
+ }
+ have[name] = true
+ if !match(name) {
+ return nil
+ }
+ _, err = c.BuildContext.ImportDir(path, 0)
+ if err != nil {
+ if _, noGo := err.(*build.NoGoError); noGo {
+ return nil
+ }
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ }
+ return pkgs
+}
+
+// importPathsNoDotExpansion returns the import paths to use for the given
+// command line, but it does no ... expansion.
+func (c *Context) importPathsNoDotExpansion(args []string) []string {
+ if len(args) == 0 {
+ return []string{"."}
+ }
+ var out []string
+ for _, a := range args {
+ // Arguments are supposed to be import paths, but
+ // as a courtesy to Windows developers, rewrite \ to /
+ // in command-line arguments. Handles .\... and so on.
+ if filepath.Separator == '\\' {
+ a = strings.Replace(a, `\`, `/`, -1)
+ }
+
+ // Put argument in canonical form, but preserve leading ./.
+ if strings.HasPrefix(a, "./") {
+ a = "./" + path.Clean(a)
+ if a == "./." {
+ a = "."
+ }
+ } else {
+ a = path.Clean(a)
+ }
+ if isMetaPackage(a) {
+ out = append(out, c.allPackages(a)...)
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// importPaths returns the import paths to use for the given command line.
+func (c *Context) importPaths(args []string) []string {
+ args = c.importPathsNoDotExpansion(args)
+ var out []string
+ for _, a := range args {
+ if strings.Contains(a, "...") {
+ if build.IsLocalImport(a) {
+ out = append(out, c.allPackagesInFS(a)...)
+ } else {
+ out = append(out, c.allPackages(a)...)
+ }
+ continue
+ }
+ out = append(out, a)
+ }
+ return out
+}
+
+// allPackages returns all the packages that can be found
+// under the $GOPATH directories and $GOROOT matching pattern.
+// The pattern is either "all" (all packages), "std" (standard packages),
+// "cmd" (standard commands), or a path including "...".
+func (c *Context) allPackages(pattern string) []string {
+ pkgs := c.matchPackages(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// allPackagesInFS is like allPackages but is passed a pattern
+// beginning ./ or ../, meaning it should scan the tree rooted
+// at the given directory. There are ... in the pattern too.
+func (c *Context) allPackagesInFS(pattern string) []string {
+ pkgs := c.matchPackagesInFS(pattern)
+ if len(pkgs) == 0 {
+ fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
+ }
+ return pkgs
+}
+
+// matchPackagesInFS returns a list of package paths matching pattern,
+// which must begin with ./ or ../
+// (see go help packages for pattern syntax).
+func (c *Context) matchPackagesInFS(pattern string) []string {
+ // Find directory to begin the scan.
+ // Could be smarter but this one optimization
+ // is enough for now, since ... is usually at the
+ // end of a path.
+ i := strings.Index(pattern, "...")
+ dir, _ := path.Split(pattern[:i])
+
+ // pattern begins with ./ or ../.
+ // path.Clean will discard the ./ but not the ../.
+ // We need to preserve the ./ for pattern matching
+ // and in the returned import paths.
+ prefix := ""
+ if strings.HasPrefix(pattern, "./") {
+ prefix = "./"
+ }
+ match := matchPattern(pattern)
+
+ var pkgs []string
+ filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() {
+ return nil
+ }
+ if path == dir {
+ // filepath.Walk starts at dir and recurses. For the recursive case,
+ // the path is the result of filepath.Join, which calls filepath.Clean.
+ // The initial case is not Cleaned, though, so we do this explicitly.
+ //
+ // This converts a path like "./io/" to "io". Without this step, running
+ // "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
+ // package, because prepending the prefix "./" to the unclean path would
+ // result in "././io", and match("././io") returns false.
+ path = filepath.Clean(path)
+ }
+
+ // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
+ _, elem := filepath.Split(path)
+ dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
+ if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := prefix + filepath.ToSlash(path)
+ if !match(name) {
+ return nil
+ }
+
+ // We keep the directory if we can import it, or if we can't import it
+ // due to invalid Go source files. This means that directories containing
+ // parse errors will be built (and fail) instead of being silently skipped
+ // as not matching the pattern. Go 1.5 and earlier skipped, but that
+ // behavior means people miss serious mistakes.
+ // See golang.org/issue/11407.
+ if p, err := c.BuildContext.ImportDir(path, 0); err != nil && shouldIgnoreImport(p) {
+ if _, noGo := err.(*build.NoGoError); !noGo {
+ log.Print(err)
+ }
+ return nil
+ }
+ pkgs = append(pkgs, name)
+ return nil
+ })
+ return pkgs
+}
+
+// isMetaPackage checks if name is a reserved package name that expands to multiple packages.
+func isMetaPackage(name string) bool {
+ return name == "std" || name == "cmd" || name == "all"
+}
+
+// isStandardImportPath reports whether $GOROOT/src/path should be considered
+// part of the standard distribution. For historical reasons we allow people to add
+// their own code to $GOROOT instead of using $GOPATH, but we assume that
+// code will start with a domain name (dot in the first element).
+func isStandardImportPath(path string) bool {
+ i := strings.Index(path, "/")
+ if i < 0 {
+ i = len(path)
+ }
+ elem := path[:i]
+ return !strings.Contains(elem, ".")
+}
+
+// hasPathPrefix reports whether the path s begins with the
+// elements in prefix.
+func hasPathPrefix(s, prefix string) bool {
+ switch {
+ default:
+ return false
+ case len(s) == len(prefix):
+ return s == prefix
+ case len(s) > len(prefix):
+ if prefix != "" && prefix[len(prefix)-1] == '/' {
+ return strings.HasPrefix(s, prefix)
+ }
+ return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
+ }
+}
+
+// treeCanMatchPattern(pattern)(name) reports whether
+// name or children of name can possibly match pattern.
+// Pattern is the same limited glob accepted by matchPattern.
+func treeCanMatchPattern(pattern string) func(name string) bool {
+ wildCard := false
+ if i := strings.Index(pattern, "..."); i >= 0 {
+ wildCard = true
+ pattern = pattern[:i]
+ }
+ return func(name string) bool {
+ return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
+ wildCard && strings.HasPrefix(name, pattern)
+ }
+}
diff --git a/vendor/github.com/kisielk/gotool/tool.go b/vendor/github.com/kisielk/gotool/tool.go
new file mode 100644
index 000000000..c7409e11e
--- /dev/null
+++ b/vendor/github.com/kisielk/gotool/tool.go
@@ -0,0 +1,48 @@
+// Package gotool contains utility functions used to implement the standard
+// "cmd/go" tool, provided as a convenience to developers who want to write
+// tools with similar semantics.
+package gotool
+
+import "go/build"
+
+// Export functions here to make it easier to keep the implementations up to date with upstream.
+
+// DefaultContext is the default context that uses build.Default.
+var DefaultContext = Context{
+ BuildContext: build.Default,
+}
+
+// A Context specifies the supporting context.
+type Context struct {
+ // BuildContext is the build.Context that is used when computing import paths.
+ BuildContext build.Context
+}
+
+// ImportPaths returns the import paths to use for the given command line.
+//
+// The path "all" is expanded to all packages in $GOPATH and $GOROOT.
+// The path "std" is expanded to all packages in the Go standard library.
+// The path "cmd" is expanded to all Go standard commands.
+// The string "..." is treated as a wildcard within a path.
+// When matching recursively, directories are ignored if they are prefixed with
+// a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata".
+// Relative import paths are not converted to full import paths.
+// If args is empty, a single element "." is returned.
+func (c *Context) ImportPaths(args []string) []string {
+ return c.importPaths(args)
+}
+
+// ImportPaths returns the import paths to use for the given command line
+// using default context.
+//
+// The path "all" is expanded to all packages in $GOPATH and $GOROOT.
+// The path "std" is expanded to all packages in the Go standard library.
+// The path "cmd" is expanded to all Go standard commands.
+// The string "..." is treated as a wildcard within a path.
+// When matching recursively, directories are ignored if they are prefixed with
+// a dot or an underscore (such as ".foo" or "_foo"), or are named "testdata".
+// Relative import paths are not converted to full import paths.
+// If args is empty, a single element "." is returned.
+func ImportPaths(args []string) []string {
+ return DefaultContext.importPaths(args)
+}
diff --git a/vendor/github.com/rogpeppe/godeps/build/build.go b/vendor/github.com/rogpeppe/godeps/build/build.go
new file mode 100644
index 000000000..8a65079be
--- /dev/null
+++ b/vendor/github.com/rogpeppe/godeps/build/build.go
@@ -0,0 +1,1603 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package build
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/ast"
+ "go/doc"
+ "go/parser"
+ "go/token"
+ "io"
+ "io/ioutil"
+ "log"
+ "os"
+ pathpkg "path"
+ "path/filepath"
+ "runtime"
+ "sort"
+ "strconv"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A Context specifies the supporting context for a build.
+type Context struct {
+ GOARCH string // target architecture
+ GOOS string // target operating system
+ GOROOT string // Go root
+ GOPATH string // Go path
+ CgoEnabled bool // whether cgo can be used
+ UseAllFiles bool // use files regardless of +build lines, file names
+ Compiler string // compiler to assume when computing target paths
+
+ // MatchTag, if specified, is called to determine whether a given
+ // tag should be considered to match for the purposes of importing.
+ // If neg is false, it should report whether the tag matches,
+ // otherwise it should report whether the tag does not match.
+ // A MatchTag that always returns true is equivalent to
+ // specifying UseAllFiles=true.
+ //
+ // If MatchTag is nil, Context.DefaultMatchTag will be called to
+ // to decide if a tag matches.
+ MatchTag func(tag string, neg bool) bool
+
+ // The build and release tags specify build constraints
+ // that should be considered satisfied when processing +build lines.
+ // Clients creating a new context may customize BuildTags, which
+ // defaults to empty, but it is usually an error to customize ReleaseTags,
+ // which defaults to the list of Go releases the current release is compatible with.
+ // In addition to the BuildTags and ReleaseTags, build constraints
+ // consider the values of GOARCH and GOOS as satisfied tags.
+ //
+ // Both these fields are considered only by DefaultMatchTag and
+ // will be ignored if MatchTag is non-nil.
+ BuildTags []string
+ ReleaseTags []string
+
+ // The install suffix specifies a suffix to use in the name of the installation
+ // directory. By default it is empty, but custom builds that need to keep
+ // their outputs separate can set InstallSuffix to do so. For example, when
+ // using the race detector, the go command uses InstallSuffix = "race", so
+ // that on a Linux/386 system, packages are written to a directory named
+ // "linux_386_race" instead of the usual "linux_386".
+ InstallSuffix string
+
+ // By default, Import uses the operating system's file system calls
+ // to read directories and files. To read from other sources,
+ // callers can set the following functions. They all have default
+ // behaviors that use the local file system, so clients need only set
+ // the functions whose behaviors they wish to change.
+
+ // JoinPath joins the sequence of path fragments into a single path.
+ // If JoinPath is nil, Import uses filepath.Join.
+ JoinPath func(elem ...string) string
+
+ // SplitPathList splits the path list into a slice of individual paths.
+ // If SplitPathList is nil, Import uses filepath.SplitList.
+ SplitPathList func(list string) []string
+
+ // IsAbsPath reports whether path is an absolute path.
+ // If IsAbsPath is nil, Import uses filepath.IsAbs.
+ IsAbsPath func(path string) bool
+
+ // IsDir reports whether the path names a directory.
+ // If IsDir is nil, Import calls os.Stat and uses the result's IsDir method.
+ IsDir func(path string) bool
+
+ // HasSubdir reports whether dir is a subdirectory of
+ // (perhaps multiple levels below) root.
+ // If so, HasSubdir sets rel to a slash-separated path that
+ // can be joined to root to produce a path equivalent to dir.
+ // If HasSubdir is nil, Import uses an implementation built on
+ // filepath.EvalSymlinks.
+ HasSubdir func(root, dir string) (rel string, ok bool)
+
+ // ReadDir returns a slice of os.FileInfo, sorted by Name,
+ // describing the content of the named directory.
+ // If ReadDir is nil, Import uses ioutil.ReadDir.
+ ReadDir func(dir string) ([]os.FileInfo, error)
+
+ // OpenFile opens a file (not a directory) for reading.
+ // If OpenFile is nil, Import uses os.Open.
+ OpenFile func(path string) (io.ReadCloser, error)
+}
+
+// joinPath calls ctxt.JoinPath (if not nil) or else filepath.Join.
+func (ctxt *Context) joinPath(elem ...string) string {
+ if f := ctxt.JoinPath; f != nil {
+ return f(elem...)
+ }
+ return filepath.Join(elem...)
+}
+
+// splitPathList calls ctxt.SplitPathList (if not nil) or else filepath.SplitList.
+func (ctxt *Context) splitPathList(s string) []string {
+ if f := ctxt.SplitPathList; f != nil {
+ return f(s)
+ }
+ return filepath.SplitList(s)
+}
+
+// isAbsPath calls ctxt.IsAbsPath (if not nil) or else filepath.IsAbs.
+func (ctxt *Context) isAbsPath(path string) bool {
+ if f := ctxt.IsAbsPath; f != nil {
+ return f(path)
+ }
+ return filepath.IsAbs(path)
+}
+
+// isDir calls ctxt.IsDir (if not nil) or else uses os.Stat.
+func (ctxt *Context) isDir(path string) bool {
+ if f := ctxt.IsDir; f != nil {
+ return f(path)
+ }
+ fi, err := os.Stat(path)
+ return err == nil && fi.IsDir()
+}
+
+// hasSubdir calls ctxt.HasSubdir (if not nil) or else uses
+// the local file system to answer the question.
+func (ctxt *Context) hasSubdir(root, dir string) (rel string, ok bool) {
+ if f := ctxt.HasSubdir; f != nil {
+ return f(root, dir)
+ }
+
+ // Try using paths we received.
+ if rel, ok = hasSubdir(root, dir); ok {
+ return
+ }
+
+ // Try expanding symlinks and comparing
+ // expanded against unexpanded and
+ // expanded against expanded.
+ rootSym, _ := filepath.EvalSymlinks(root)
+ dirSym, _ := filepath.EvalSymlinks(dir)
+
+ if rel, ok = hasSubdir(rootSym, dir); ok {
+ return
+ }
+ if rel, ok = hasSubdir(root, dirSym); ok {
+ return
+ }
+ return hasSubdir(rootSym, dirSym)
+}
+
+func hasSubdir(root, dir string) (rel string, ok bool) {
+ const sep = string(filepath.Separator)
+ root = filepath.Clean(root)
+ if !strings.HasSuffix(root, sep) {
+ root += sep
+ }
+ dir = filepath.Clean(dir)
+ if !strings.HasPrefix(dir, root) {
+ return "", false
+ }
+ return filepath.ToSlash(dir[len(root):]), true
+}
+
+// readDir calls ctxt.ReadDir (if not nil) or else ioutil.ReadDir.
+func (ctxt *Context) readDir(path string) ([]os.FileInfo, error) {
+ if f := ctxt.ReadDir; f != nil {
+ return f(path)
+ }
+ return ioutil.ReadDir(path)
+}
+
+// openFile calls ctxt.OpenFile (if not nil) or else os.Open.
+func (ctxt *Context) openFile(path string) (io.ReadCloser, error) {
+ if fn := ctxt.OpenFile; fn != nil {
+ return fn(path)
+ }
+
+ f, err := os.Open(path)
+ if err != nil {
+ return nil, err // nil interface
+ }
+ return f, nil
+}
+
+// isFile determines whether path is a file by trying to open it.
+// It reuses openFile instead of adding another function to the
+// list in Context.
+func (ctxt *Context) isFile(path string) bool {
+ f, err := ctxt.openFile(path)
+ if err != nil {
+ return false
+ }
+ f.Close()
+ return true
+}
+
+// gopath returns the list of Go path directories.
+func (ctxt *Context) gopath() []string {
+ var all []string
+ for _, p := range ctxt.splitPathList(ctxt.GOPATH) {
+ if p == "" || p == ctxt.GOROOT {
+ // Empty paths are uninteresting.
+ // If the path is the GOROOT, ignore it.
+ // People sometimes set GOPATH=$GOROOT.
+ // Do not get confused by this common mistake.
+ continue
+ }
+ if strings.HasPrefix(p, "~") {
+ // Path segments starting with ~ on Unix are almost always
+ // users who have incorrectly quoted ~ while setting GOPATH,
+ // preventing it from expanding to $HOME.
+ // The situation is made more confusing by the fact that
+ // bash allows quoted ~ in $PATH (most shells do not).
+ // Do not get confused by this, and do not try to use the path.
+ // It does not exist, and printing errors about it confuses
+ // those users even more, because they think "sure ~ exists!".
+ // The go command diagnoses this situation and prints a
+ // useful error.
+ // On Windows, ~ is used in short names, such as c:\progra~1
+ // for c:\program files.
+ continue
+ }
+ all = append(all, p)
+ }
+ return all
+}
+
+// SrcDirs returns a list of package source root directories.
+// It draws from the current Go root and Go path but omits directories
+// that do not exist.
+func (ctxt *Context) SrcDirs() []string {
+ var all []string
+ if ctxt.GOROOT != "" {
+ dir := ctxt.joinPath(ctxt.GOROOT, "src")
+ if ctxt.isDir(dir) {
+ all = append(all, dir)
+ }
+ }
+ for _, p := range ctxt.gopath() {
+ dir := ctxt.joinPath(p, "src")
+ if ctxt.isDir(dir) {
+ all = append(all, dir)
+ }
+ }
+ return all
+}
+
+// Default is the default Context for builds.
+// It uses the GOARCH, GOOS, GOROOT, and GOPATH environment variables
+// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
+var Default Context = defaultContext()
+
+func defaultGOPATH() string {
+ env := "HOME"
+ if runtime.GOOS == "windows" {
+ env = "USERPROFILE"
+ } else if runtime.GOOS == "plan9" {
+ env = "home"
+ }
+ if home := os.Getenv(env); home != "" {
+ def := filepath.Join(home, "go")
+ if def == runtime.GOROOT() {
+ // Don't set the default GOPATH to GOROOT,
+ // as that will trigger warnings from the go tool.
+ return ""
+ }
+ return def
+ }
+ return ""
+}
+
+func defaultContext() Context {
+ var c Context
+
+ c.GOARCH = envOr("GOARCH", runtime.GOARCH)
+ c.GOOS = envOr("GOOS", runtime.GOOS)
+ c.GOROOT = pathpkg.Clean(runtime.GOROOT())
+ c.GOPATH = envOr("GOPATH", defaultGOPATH())
+ c.Compiler = runtime.Compiler
+
+ // Each major Go release in the Go 1.x series should add a tag here.
+ // Old tags should not be removed. That is, the go1.x tag is present
+ // in all releases >= Go 1.x. Code that requires Go 1.x or later should
+ // say "+build go1.x", and code that should only be built before Go 1.x
+ // (perhaps it is the stub to use in that case) should say "+build !go1.x".
+ c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5", "go1.6", "go1.7", "go1.8"}
+
+ env := os.Getenv("CGO_ENABLED")
+ if env == "" {
+ env = defaultCGO_ENABLED
+ }
+ switch env {
+ case "1":
+ c.CgoEnabled = true
+ case "0":
+ c.CgoEnabled = false
+ default:
+ // cgo must be explicitly enabled for cross compilation builds
+ if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
+ c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+ break
+ }
+ c.CgoEnabled = false
+ }
+
+ return c
+}
+
+func envOr(name, def string) string {
+ s := os.Getenv(name)
+ if s == "" {
+ return def
+ }
+ return s
+}
+
+// An ImportMode controls the behavior of the Import method.
+type ImportMode uint
+
+const (
+ // If FindOnly is set, Import stops after locating the directory
+ // that should contain the sources for a package. It does not
+ // read any files in the directory.
+ FindOnly ImportMode = 1 << iota
+
+ // If AllowBinary is set, Import can be satisfied by a compiled
+ // package object without corresponding sources.
+ //
+ // Deprecated:
+ // The supported way to create a compiled-only package is to
+ // write source code containing a //go:binary-only-package comment at
+ // the top of the file. Such a package will be recognized
+ // regardless of this flag setting (because it has source code)
+ // and will have BinaryOnly set to true in the returned Package.
+ AllowBinary
+
+ // If ImportComment is set, parse import comments on package statements.
+ // Import returns an error if it finds a comment it cannot understand
+ // or finds conflicting comments in multiple source files.
+ // See golang.org/s/go14customimport for more information.
+ ImportComment
+
+ // By default, Import searches vendor directories
+ // that apply in the given source directory before searching
+ // the GOROOT and GOPATH roots.
+ // If an Import finds and returns a package using a vendor
+ // directory, the resulting ImportPath is the complete path
+ // to the package, including the path elements leading up
+ // to and including "vendor".
+ // For example, if Import("y", "x/subdir", 0) finds
+ // "x/vendor/y", the returned package's ImportPath is "x/vendor/y",
+ // not plain "y".
+ // See golang.org/s/go15vendor for more information.
+ //
+ // Setting IgnoreVendor ignores vendor directories.
+ //
+ // In contrast to the package's ImportPath,
+ // the returned package's Imports, TestImports, and XTestImports
+ // are always the exact import paths from the source files:
+ // Import makes no attempt to resolve or check those paths.
+ IgnoreVendor
+)
+
+// A Package describes the Go package found in a directory.
+type Package struct {
+ Dir string // directory containing package sources
+ Name string // package name
+ ImportComment string // path in import comment on package statement
+ Doc string // documentation synopsis
+ ImportPath string // import path of package ("" if unknown)
+ Root string // root of Go tree where this package lives
+ SrcRoot string // package source root directory ("" if unknown)
+ PkgRoot string // package install root directory ("" if unknown)
+ PkgTargetRoot string // architecture dependent install root directory ("" if unknown)
+ BinDir string // command install directory ("" if unknown)
+ Goroot bool // package found in Go root
+ PkgObj string // installed .a file
+ AllTags []string // tags that can influence file selection in this directory
+ ConflictDir string // this directory shadows Dir in $GOPATH
+ BinaryOnly bool // cannot be rebuilt from source (has //go:binary-only-package comment)
+
+ // Source files
+ GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
+ CgoFiles []string // .go source files that import "C"
+ IgnoredGoFiles []string // .go source files ignored for this build
+ InvalidGoFiles []string // .go source files with detected problems (parse error, wrong package name, and so on)
+ CFiles []string // .c source files
+ CXXFiles []string // .cc, .cpp and .cxx source files
+ MFiles []string // .m (Objective-C) source files
+ HFiles []string // .h, .hh, .hpp and .hxx source files
+ FFiles []string // .f, .F, .for and .f90 Fortran source files
+ SFiles []string // .s source files
+ SwigFiles []string // .swig files
+ SwigCXXFiles []string // .swigcxx files
+ SysoFiles []string // .syso system object files to add to archive
+
+ // Cgo directives
+ CgoCFLAGS []string // Cgo CFLAGS directives
+ CgoCPPFLAGS []string // Cgo CPPFLAGS directives
+ CgoCXXFLAGS []string // Cgo CXXFLAGS directives
+ CgoFFLAGS []string // Cgo FFLAGS directives
+ CgoLDFLAGS []string // Cgo LDFLAGS directives
+ CgoPkgConfig []string // Cgo pkg-config directives
+
+ // Dependency information
+ Imports []string // import paths from GoFiles, CgoFiles
+ ImportPos map[string][]token.Position // line information for Imports
+
+ // Test information
+ TestGoFiles []string // _test.go files in package
+ TestImports []string // import paths from TestGoFiles
+ TestImportPos map[string][]token.Position // line information for TestImports
+ XTestGoFiles []string // _test.go files outside package
+ XTestImports []string // import paths from XTestGoFiles
+ XTestImportPos map[string][]token.Position // line information for XTestImports
+}
+
+// IsCommand reports whether the package is considered a
+// command to be installed (not just a library).
+// Packages named "main" are treated as commands.
+func (p *Package) IsCommand() bool {
+ return p.Name == "main"
+}
+
+// ImportDir is like Import but processes the Go package found in
+// the named directory.
+func (ctxt *Context) ImportDir(dir string, mode ImportMode) (*Package, error) {
+ return ctxt.Import(".", dir, mode)
+}
+
+// NoGoError is the error used by Import to describe a directory
+// containing no buildable Go source files. (It may still contain
+// test files, files hidden by build tags, and so on.)
+type NoGoError struct {
+ Dir string
+ Ignored bool // whether any Go files were ignored due to build tags
+}
+
+func (e *NoGoError) Error() string {
+ msg := "no buildable Go source files in " + e.Dir
+ if e.Ignored {
+ msg += " (.go files ignored due to build tags)"
+ }
+ return msg
+}
+
+// MultiplePackageError describes a directory containing
+// multiple buildable Go source files for multiple packages.
+type MultiplePackageError struct {
+ Dir string // directory containing files
+ Packages []string // package names found
+ Files []string // corresponding files: Files[i] declares package Packages[i]
+}
+
+func (e *MultiplePackageError) Error() string {
+ // Error string limited to two entries for compatibility.
+ return fmt.Sprintf("found packages %s (%s) and %s (%s) in %s", e.Packages[0], e.Files[0], e.Packages[1], e.Files[1], e.Dir)
+}
+
+func nameExt(name string) string {
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ return ""
+ }
+ return name[i:]
+}
+
+// Import returns details about the Go package named by the import path,
+// interpreting local import paths relative to the srcDir directory.
+// If the path is a local import path naming a package that can be imported
+// using a standard import path, the returned package will set p.ImportPath
+// to that path.
+//
+// In the directory containing the package, .go, .c, .h, and .s files are
+// considered part of the package except for:
+//
+// - .go files in package documentation
+// - files starting with _ or . (likely editor temporary files)
+// - files with build constraints not satisfied by the context
+//
+// If an error occurs, Import returns a non-nil error and a non-nil
+// *Package containing partial information.
+//
+func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Package, error) {
+ p := &Package{
+ ImportPath: path,
+ }
+ if path == "" {
+ return p, fmt.Errorf("import %q: invalid import path", path)
+ }
+
+ var pkgtargetroot string
+ var pkga string
+ var pkgerr error
+ suffix := ""
+ if ctxt.InstallSuffix != "" {
+ suffix = "_" + ctxt.InstallSuffix
+ }
+ switch ctxt.Compiler {
+ case "gccgo":
+ pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+ case "gc":
+ pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+ default:
+ // Save error for end of function.
+ pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
+ }
+ setPkga := func() {
+ switch ctxt.Compiler {
+ case "gccgo":
+ dir, elem := pathpkg.Split(p.ImportPath)
+ pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
+ case "gc":
+ pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
+ }
+ }
+ setPkga()
+
+ binaryOnly := false
+ if IsLocalImport(path) {
+ pkga = "" // local imports have no installed path
+ if srcDir == "" {
+ return p, fmt.Errorf("import %q: import relative to unknown directory", path)
+ }
+ if !ctxt.isAbsPath(path) {
+ p.Dir = ctxt.joinPath(srcDir, path)
+ }
+ // Determine canonical import path, if any.
+ // Exclude results where the import path would include /testdata/.
+ inTestdata := func(sub string) bool {
+ return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
+ }
+ if ctxt.GOROOT != "" {
+ root := ctxt.joinPath(ctxt.GOROOT, "src")
+ if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
+ p.Goroot = true
+ p.ImportPath = sub
+ p.Root = ctxt.GOROOT
+ goto Found
+ }
+ }
+ all := ctxt.gopath()
+ for i, root := range all {
+ rootsrc := ctxt.joinPath(root, "src")
+ if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
+ // We found a potential import path for dir,
+ // but check that using it wouldn't find something
+ // else first.
+ if ctxt.GOROOT != "" {
+ if dir := ctxt.joinPath(ctxt.GOROOT, "src", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
+ goto Found
+ }
+ }
+ for _, earlyRoot := range all[:i] {
+ if dir := ctxt.joinPath(earlyRoot, "src", sub); ctxt.isDir(dir) {
+ p.ConflictDir = dir
+ goto Found
+ }
+ }
+
+ // sub would not name some other directory instead of this one.
+ // Record it.
+ p.ImportPath = sub
+ p.Root = root
+ goto Found
+ }
+ }
+ // It's okay that we didn't find a root containing dir.
+ // Keep going with the information we have.
+ } else {
+ if strings.HasPrefix(path, "/") {
+ return p, fmt.Errorf("import %q: cannot import absolute path", path)
+ }
+
+ // tried records the location of unsuccessful package lookups
+ var tried struct {
+ vendor []string
+ goroot string
+ gopath []string
+ }
+ gopath := ctxt.gopath()
+
+ // Vendor directories get first chance to satisfy import.
+ if mode&IgnoreVendor == 0 && srcDir != "" {
+ searchVendor := func(root string, isGoroot bool) bool {
+ sub, ok := ctxt.hasSubdir(root, srcDir)
+ if !ok || !strings.HasPrefix(sub, "src/") || strings.Contains(sub, "/testdata/") {
+ return false
+ }
+ for {
+ vendor := ctxt.joinPath(root, sub, "vendor")
+ if ctxt.isDir(vendor) {
+ dir := ctxt.joinPath(vendor, path)
+ if ctxt.isDir(dir) && hasGoFiles(ctxt, dir) {
+ p.Dir = dir
+ p.ImportPath = strings.TrimPrefix(pathpkg.Join(sub, "vendor", path), "src/")
+ p.Goroot = isGoroot
+ p.Root = root
+ setPkga() // p.ImportPath changed
+ return true
+ }
+ tried.vendor = append(tried.vendor, dir)
+ }
+ i := strings.LastIndex(sub, "/")
+ if i < 0 {
+ break
+ }
+ sub = sub[:i]
+ }
+ return false
+ }
+ if searchVendor(ctxt.GOROOT, true) {
+ goto Found
+ }
+ for _, root := range gopath {
+ if searchVendor(root, false) {
+ goto Found
+ }
+ }
+ }
+
+ // Determine directory from import path.
+ if ctxt.GOROOT != "" {
+ dir := ctxt.joinPath(ctxt.GOROOT, "src", path)
+ isDir := ctxt.isDir(dir)
+ binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(ctxt.GOROOT, pkga))
+ if isDir || binaryOnly {
+ p.Dir = dir
+ p.Goroot = true
+ p.Root = ctxt.GOROOT
+ goto Found
+ }
+ tried.goroot = dir
+ }
+ for _, root := range gopath {
+ dir := ctxt.joinPath(root, "src", path)
+ isDir := ctxt.isDir(dir)
+ binaryOnly = !isDir && mode&AllowBinary != 0 && pkga != "" && ctxt.isFile(ctxt.joinPath(root, pkga))
+ if isDir || binaryOnly {
+ p.Dir = dir
+ p.Root = root
+ goto Found
+ }
+ tried.gopath = append(tried.gopath, dir)
+ }
+
+ // package was not found
+ var paths []string
+ format := "\t%s (vendor tree)"
+ for _, dir := range tried.vendor {
+ paths = append(paths, fmt.Sprintf(format, dir))
+ format = "\t%s"
+ }
+ if tried.goroot != "" {
+ paths = append(paths, fmt.Sprintf("\t%s (from $GOROOT)", tried.goroot))
+ } else {
+ paths = append(paths, "\t($GOROOT not set)")
+ }
+ format = "\t%s (from $GOPATH)"
+ for _, dir := range tried.gopath {
+ paths = append(paths, fmt.Sprintf(format, dir))
+ format = "\t%s"
+ }
+ if len(tried.gopath) == 0 {
+ paths = append(paths, "\t($GOPATH not set. For more details see: 'go help gopath')")
+ }
+ return p, fmt.Errorf("cannot find package %q in any of:\n%s", path, strings.Join(paths, "\n"))
+ }
+
+Found:
+ if p.Root != "" {
+ p.SrcRoot = ctxt.joinPath(p.Root, "src")
+ p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
+ p.BinDir = ctxt.joinPath(p.Root, "bin")
+ if pkga != "" {
+ p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
+ p.PkgObj = ctxt.joinPath(p.Root, pkga)
+ }
+ }
+
+ if mode&FindOnly != 0 {
+ return p, pkgerr
+ }
+ if binaryOnly && (mode&AllowBinary) != 0 {
+ return p, pkgerr
+ }
+
+ dirs, err := ctxt.readDir(p.Dir)
+ if err != nil {
+ return p, err
+ }
+
+ var badGoError error
+ var Sfiles []string // files with ".S" (capital S)
+ var firstFile, firstCommentFile string
+ imported := make(map[string][]token.Position)
+ testImported := make(map[string][]token.Position)
+ xTestImported := make(map[string][]token.Position)
+ allTags := make(map[string]bool)
+ fset := token.NewFileSet()
+ for _, d := range dirs {
+ if d.IsDir() {
+ continue
+ }
+
+ name := d.Name()
+ ext := nameExt(name)
+
+ badFile := func(err error) {
+ if badGoError == nil {
+ badGoError = err
+ }
+ p.InvalidGoFiles = append(p.InvalidGoFiles, name)
+ }
+
+ match, data, filename, err := ctxt.matchFile(p.Dir, name, true, allTags, &p.BinaryOnly)
+ if err != nil {
+ badFile(err)
+ continue
+ }
+ if !match {
+ if ext == ".go" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
+ continue
+ }
+
+ // Going to save the file. For non-Go files, can stop here.
+ switch ext {
+ case ".c":
+ p.CFiles = append(p.CFiles, name)
+ continue
+ case ".cc", ".cpp", ".cxx":
+ p.CXXFiles = append(p.CXXFiles, name)
+ continue
+ case ".m":
+ p.MFiles = append(p.MFiles, name)
+ continue
+ case ".h", ".hh", ".hpp", ".hxx":
+ p.HFiles = append(p.HFiles, name)
+ continue
+ case ".f", ".F", ".for", ".f90":
+ p.FFiles = append(p.FFiles, name)
+ continue
+ case ".s":
+ p.SFiles = append(p.SFiles, name)
+ continue
+ case ".S":
+ Sfiles = append(Sfiles, name)
+ continue
+ case ".swig":
+ p.SwigFiles = append(p.SwigFiles, name)
+ continue
+ case ".swigcxx":
+ p.SwigCXXFiles = append(p.SwigCXXFiles, name)
+ continue
+ case ".syso":
+ // binary objects to add to package archive
+ // Likely of the form foo_windows.syso, but
+ // the name was vetted above with goodOSArchFile.
+ p.SysoFiles = append(p.SysoFiles, name)
+ continue
+ }
+
+ pf, err := parser.ParseFile(fset, filename, data, parser.ImportsOnly|parser.ParseComments)
+ if err != nil {
+ badFile(err)
+ continue
+ }
+
+ pkg := pf.Name.Name
+ if pkg == "documentation" {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ continue
+ }
+
+ isTest := strings.HasSuffix(name, "_test.go")
+ isXTest := false
+ if isTest && strings.HasSuffix(pkg, "_test") {
+ isXTest = true
+ pkg = pkg[:len(pkg)-len("_test")]
+ }
+
+ if p.Name == "" {
+ p.Name = pkg
+ firstFile = name
+ } else if pkg != p.Name {
+ badFile(&MultiplePackageError{
+ Dir: p.Dir,
+ Packages: []string{p.Name, pkg},
+ Files: []string{firstFile, name},
+ })
+ p.InvalidGoFiles = append(p.InvalidGoFiles, name)
+ }
+ if pf.Doc != nil && p.Doc == "" {
+ p.Doc = doc.Synopsis(pf.Doc.Text())
+ }
+
+ if mode&ImportComment != 0 {
+ qcom, line := findImportComment(data)
+ if line != 0 {
+ com, err := strconv.Unquote(qcom)
+ if err != nil {
+ badFile(fmt.Errorf("%s:%d: cannot parse import comment", filename, line))
+ } else if p.ImportComment == "" {
+ p.ImportComment = com
+ firstCommentFile = name
+ } else if p.ImportComment != com {
+ badFile(fmt.Errorf("found import comments %q (%s) and %q (%s) in %s", p.ImportComment, firstCommentFile, com, name, p.Dir))
+ }
+ }
+ }
+
+ // Record imports and information about cgo.
+ isCgo := false
+ for _, decl := range pf.Decls {
+ d, ok := decl.(*ast.GenDecl)
+ if !ok {
+ continue
+ }
+ for _, dspec := range d.Specs {
+ spec, ok := dspec.(*ast.ImportSpec)
+ if !ok {
+ continue
+ }
+ quoted := spec.Path.Value
+ path, err := strconv.Unquote(quoted)
+ if err != nil {
+ log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted)
+ }
+ if isXTest {
+ xTestImported[path] = append(xTestImported[path], fset.Position(spec.Pos()))
+ } else if isTest {
+ testImported[path] = append(testImported[path], fset.Position(spec.Pos()))
+ } else {
+ imported[path] = append(imported[path], fset.Position(spec.Pos()))
+ }
+ if path == "C" {
+ if isTest {
+ badFile(fmt.Errorf("use of cgo in test %s not supported", filename))
+ } else {
+ cg := spec.Doc
+ if cg == nil && len(d.Specs) == 1 {
+ cg = d.Doc
+ }
+ if cg != nil {
+ if err := ctxt.saveCgo(filename, p, cg); err != nil {
+ badFile(err)
+ }
+ }
+ isCgo = true
+ }
+ }
+ }
+ }
+ if isCgo {
+ allTags["cgo"] = true
+ if ctxt.CgoEnabled {
+ p.CgoFiles = append(p.CgoFiles, name)
+ } else {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
+ }
+ } else if isXTest {
+ p.XTestGoFiles = append(p.XTestGoFiles, name)
+ } else if isTest {
+ p.TestGoFiles = append(p.TestGoFiles, name)
+ } else {
+ p.GoFiles = append(p.GoFiles, name)
+ }
+ }
+ if badGoError != nil {
+ return p, badGoError
+ }
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
+ return p, &NoGoError{Dir: p.Dir, Ignored: len(p.IgnoredGoFiles) > 0}
+ }
+
+ for tag := range allTags {
+ p.AllTags = append(p.AllTags, tag)
+ }
+ sort.Strings(p.AllTags)
+
+ p.Imports, p.ImportPos = cleanImports(imported)
+ p.TestImports, p.TestImportPos = cleanImports(testImported)
+ p.XTestImports, p.XTestImportPos = cleanImports(xTestImported)
+
+ // add the .S files only if we are using cgo
+ // (which means gcc will compile them).
+ // The standard assemblers expect .s files.
+ if len(p.CgoFiles) > 0 {
+ p.SFiles = append(p.SFiles, Sfiles...)
+ sort.Strings(p.SFiles)
+ }
+
+ return p, pkgerr
+}
+
+// hasGoFiles reports whether dir contains any files with names ending in .go.
+// For a vendor check we must exclude directories that contain no .go files.
+// Otherwise it is not possible to vendor just a/b/c and still import the
+// non-vendored a/b. See golang.org/issue/13832.
+func hasGoFiles(ctxt *Context, dir string) bool {
+ ents, _ := ctxt.readDir(dir)
+ for _, ent := range ents {
+ if !ent.IsDir() && strings.HasSuffix(ent.Name(), ".go") {
+ return true
+ }
+ }
+ return false
+}
+
+func findImportComment(data []byte) (s string, line int) {
+ // expect keyword package
+ word, data := parseWord(data)
+ if string(word) != "package" {
+ return "", 0
+ }
+
+ // expect package name
+ _, data = parseWord(data)
+
+ // now ready for import comment, a // or /* */ comment
+ // beginning and ending on the current line.
+ for len(data) > 0 && (data[0] == ' ' || data[0] == '\t' || data[0] == '\r') {
+ data = data[1:]
+ }
+
+ var comment []byte
+ switch {
+ case bytes.HasPrefix(data, slashSlash):
+ i := bytes.Index(data, newline)
+ if i < 0 {
+ i = len(data)
+ }
+ comment = data[2:i]
+ case bytes.HasPrefix(data, slashStar):
+ data = data[2:]
+ i := bytes.Index(data, starSlash)
+ if i < 0 {
+ // malformed comment
+ return "", 0
+ }
+ comment = data[:i]
+ if bytes.Contains(comment, newline) {
+ return "", 0
+ }
+ }
+ comment = bytes.TrimSpace(comment)
+
+ // split comment into `import`, `"pkg"`
+ word, arg := parseWord(comment)
+ if string(word) != "import" {
+ return "", 0
+ }
+
+ line = 1 + bytes.Count(data[:cap(data)-cap(arg)], newline)
+ return strings.TrimSpace(string(arg)), line
+}
+
+var (
+ slashSlash = []byte("//")
+ slashStar = []byte("/*")
+ starSlash = []byte("*/")
+ newline = []byte("\n")
+)
+
+// skipSpaceOrComment returns data with any leading spaces or comments removed.
+func skipSpaceOrComment(data []byte) []byte {
+ for len(data) > 0 {
+ switch data[0] {
+ case ' ', '\t', '\r', '\n':
+ data = data[1:]
+ continue
+ case '/':
+ if bytes.HasPrefix(data, slashSlash) {
+ i := bytes.Index(data, newline)
+ if i < 0 {
+ return nil
+ }
+ data = data[i+1:]
+ continue
+ }
+ if bytes.HasPrefix(data, slashStar) {
+ data = data[2:]
+ i := bytes.Index(data, starSlash)
+ if i < 0 {
+ return nil
+ }
+ data = data[i+2:]
+ continue
+ }
+ }
+ break
+ }
+ return data
+}
+
+// parseWord skips any leading spaces or comments in data
+// and then parses the beginning of data as an identifier or keyword,
+// returning that word and what remains after the word.
+func parseWord(data []byte) (word, rest []byte) {
+ data = skipSpaceOrComment(data)
+
+ // Parse past leading word characters.
+ rest = data
+ for {
+ r, size := utf8.DecodeRune(rest)
+ if unicode.IsLetter(r) || '0' <= r && r <= '9' || r == '_' {
+ rest = rest[size:]
+ continue
+ }
+ break
+ }
+
+ word = data[:len(data)-len(rest)]
+ if len(word) == 0 {
+ return nil, nil
+ }
+
+ return word, rest
+}
+
+// MatchFile reports whether the file with the given name in the given directory
+// matches the context and would be included in a Package created by ImportDir
+// of that directory.
+//
+// MatchFile considers the name of the file and may use ctxt.OpenFile to
+// read some or all of the file's content.
+func (ctxt *Context) MatchFile(dir, name string) (match bool, err error) {
+ match, _, _, err = ctxt.matchFile(dir, name, false, nil, nil)
+ return
+}
+
+// matchFile determines whether the file with the given name in the given directory
+// should be included in the package being constructed.
+// It returns the data read from the file.
+// If returnImports is true and name denotes a Go program, matchFile reads
+// until the end of the imports (and returns that data) even though it only
+// considers text until the first non-comment.
+// If allTags is non-nil, matchFile records any encountered build tag
+// by setting allTags[tag] = true.
+func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map[string]bool, binaryOnly *bool) (match bool, data []byte, filename string, err error) {
+ if strings.HasPrefix(name, "_") ||
+ strings.HasPrefix(name, ".") {
+ return
+ }
+
+ i := strings.LastIndex(name, ".")
+ if i < 0 {
+ i = len(name)
+ }
+ ext := name[i:]
+
+ if !ctxt.goodOSArchFile(name, allTags) && !ctxt.UseAllFiles {
+ return
+ }
+
+ switch ext {
+ case ".go", ".c", ".cc", ".cxx", ".cpp", ".m", ".s", ".h", ".hh", ".hpp", ".hxx", ".f", ".F", ".f90", ".S", ".swig", ".swigcxx":
+ // tentatively okay - read to make sure
+ case ".syso":
+ // binary, no reading
+ match = true
+ return
+ default:
+ // skip
+ return
+ }
+
+ filename = ctxt.joinPath(dir, name)
+ f, err := ctxt.openFile(filename)
+ if err != nil {
+ return
+ }
+
+ if strings.HasSuffix(filename, ".go") {
+ data, err = readImports(f, false, nil)
+ if strings.HasSuffix(filename, "_test.go") {
+ binaryOnly = nil // ignore //go:binary-only-package comments in _test.go files
+ }
+ } else {
+ binaryOnly = nil // ignore //go:binary-only-package comments in non-Go sources
+ data, err = readComments(f)
+ }
+ f.Close()
+ if err != nil {
+ err = fmt.Errorf("read %s: %v", filename, err)
+ return
+ }
+
+ // Look for +build comments to accept or reject the file.
+ var sawBinaryOnly bool
+ if !ctxt.shouldBuild(data, allTags, &sawBinaryOnly) && !ctxt.UseAllFiles {
+ return
+ }
+
+ if binaryOnly != nil && sawBinaryOnly {
+ *binaryOnly = true
+ }
+ match = true
+ return
+}
+
+func cleanImports(m map[string][]token.Position) ([]string, map[string][]token.Position) {
+ all := make([]string, 0, len(m))
+ for path := range m {
+ all = append(all, path)
+ }
+ sort.Strings(all)
+ return all, m
+}
+
+// Import is shorthand for Default.Import.
+func Import(path, srcDir string, mode ImportMode) (*Package, error) {
+ return Default.Import(path, srcDir, mode)
+}
+
+// ImportDir is shorthand for Default.ImportDir.
+func ImportDir(dir string, mode ImportMode) (*Package, error) {
+ return Default.ImportDir(dir, mode)
+}
+
+var slashslash = []byte("//")
+
+// Special comment denoting a binary-only package.
+// See https://golang.org/design/2775-binary-only-packages
+// for more about the design of binary-only packages.
+var binaryOnlyComment = []byte("//go:binary-only-package")
+
+// shouldBuild reports whether it is okay to use this file,
+// The rule is that in the file's leading run of // comments
+// and blank lines, which must be followed by a blank line
+// (to avoid including a Go package clause doc comment),
+// lines beginning with '// +build' are taken as build directives.
+//
+// The file is accepted only if each such line lists something
+// matching the file. For example:
+//
+// // +build windows linux
+//
+// marks the file as applicable only on Windows and Linux.
+//
+// If shouldBuild finds a //go:binary-only-package comment in the file,
+// it sets *binaryOnly to true. Otherwise it does not change *binaryOnly.
+//
+func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool, binaryOnly *bool) bool {
+ sawBinaryOnly := false
+
+ // Pass 1. Identify leading run of // comments and blank lines,
+ // which must be followed by a blank line.
+ end := 0
+ p := content
+ 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)
+ if len(line) == 0 { // Blank line
+ end = len(content) - len(p)
+ continue
+ }
+ if !bytes.HasPrefix(line, slashslash) { // Not comment line
+ break
+ }
+ }
+ content = content[:end]
+
+ // Pass 2. Process each line in the run.
+ p = content
+ allok := true
+ 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)
+ if bytes.HasPrefix(line, slashslash) {
+ if bytes.Equal(line, binaryOnlyComment) {
+ sawBinaryOnly = true
+ }
+ line = bytes.TrimSpace(line[len(slashslash):])
+ if len(line) > 0 && line[0] == '+' {
+ // Looks like a comment +line.
+ f := strings.Fields(string(line))
+ if f[0] == "+build" {
+ ok := false
+ for _, tok := range f[1:] {
+ if ctxt.match(tok, allTags) {
+ ok = true
+ }
+ }
+ if !ok {
+ allok = false
+ }
+ }
+ }
+ }
+ }
+
+ if binaryOnly != nil && sawBinaryOnly {
+ *binaryOnly = true
+ }
+
+ return allok
+}
+
+// saveCgo saves the information from the #cgo lines in the import "C" comment.
+// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
+// that affect the way cgo's C code is built.
+func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
+ text := cg.Text()
+ for _, line := range strings.Split(text, "\n") {
+ orig := line
+
+ // Line is
+ // #cgo [GOOS/GOARCH...] LDFLAGS: stuff
+ //
+ line = strings.TrimSpace(line)
+ if len(line) < 5 || line[:4] != "#cgo" || (line[4] != ' ' && line[4] != '\t') {
+ continue
+ }
+
+ // Split at colon.
+ line = strings.TrimSpace(line[4:])
+ i := strings.Index(line, ":")
+ if i < 0 {
+ return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+ }
+ line, argstr := line[:i], line[i+1:]
+
+ // Parse GOOS/GOARCH stuff.
+ f := strings.Fields(line)
+ if len(f) < 1 {
+ return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+ }
+
+ cond, verb := f[:len(f)-1], f[len(f)-1]
+ if len(cond) > 0 {
+ ok := false
+ for _, c := range cond {
+ if ctxt.match(c, nil) {
+ ok = true
+ break
+ }
+ }
+ if !ok {
+ continue
+ }
+ }
+
+ args, err := splitQuoted(argstr)
+ if err != nil {
+ return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
+ }
+ var ok bool
+ for i, arg := range args {
+ if arg, ok = expandSrcDir(arg, di.Dir); !ok {
+ return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
+ }
+ args[i] = arg
+ }
+
+ switch verb {
+ case "CFLAGS":
+ di.CgoCFLAGS = append(di.CgoCFLAGS, args...)
+ case "CPPFLAGS":
+ di.CgoCPPFLAGS = append(di.CgoCPPFLAGS, args...)
+ case "CXXFLAGS":
+ di.CgoCXXFLAGS = append(di.CgoCXXFLAGS, args...)
+ case "FFLAGS":
+ di.CgoFFLAGS = append(di.CgoFFLAGS, args...)
+ case "LDFLAGS":
+ di.CgoLDFLAGS = append(di.CgoLDFLAGS, args...)
+ case "pkg-config":
+ di.CgoPkgConfig = append(di.CgoPkgConfig, args...)
+ default:
+ return fmt.Errorf("%s: invalid #cgo verb: %s", filename, orig)
+ }
+ }
+ return nil
+}
+
+// expandSrcDir expands any occurrence of ${SRCDIR}, making sure
+// the result is safe for the shell.
+func expandSrcDir(str string, srcdir string) (string, bool) {
+ // "\" delimited paths cause safeCgoName to fail
+ // so convert native paths with a different delimiter
+ // to "/" before starting (eg: on windows).
+ srcdir = filepath.ToSlash(srcdir)
+
+ // Spaces are tolerated in ${SRCDIR}, but not anywhere else.
+ chunks := strings.Split(str, "${SRCDIR}")
+ if len(chunks) < 2 {
+ return str, safeCgoName(str, false)
+ }
+ ok := true
+ for _, chunk := range chunks {
+ ok = ok && (chunk == "" || safeCgoName(chunk, false))
+ }
+ ok = ok && (srcdir == "" || safeCgoName(srcdir, true))
+ res := strings.Join(chunks, srcdir)
+ return res, ok && res != ""
+}
+
+// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
+// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
+// See golang.org/issue/6038.
+// The @ is for OS X. See golang.org/issue/13720.
+// The % is for Jenkins. See golang.org/issue/16959.
+const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$@%"
+const safeSpaces = " "
+
+var safeBytes = []byte(safeSpaces + safeString)
+
+func safeCgoName(s string, spaces bool) bool {
+ if s == "" {
+ return false
+ }
+ safe := safeBytes
+ if !spaces {
+ safe = safe[len(safeSpaces):]
+ }
+ for i := 0; i < len(s); i++ {
+ if c := s[i]; c < utf8.RuneSelf && bytes.IndexByte(safe, c) < 0 {
+ return false
+ }
+ }
+ return true
+}
+
+// splitQuoted splits the string s around each instance of one or more consecutive
+// white space characters while taking into account quotes and escaping, and
+// returns an array of substrings of s or an empty list if s contains only white space.
+// Single quotes and double quotes are recognized to prevent splitting within the
+// quoted region, and are removed from the resulting substrings. If a quote in s
+// isn't closed err will be set and r will have the unclosed argument as the
+// last element. The backslash is used for escaping.
+//
+// For example, the following string:
+//
+// a b:"c d" 'e''f' "g\""
+//
+// Would be parsed as:
+//
+// []string{"a", "b:c d", "ef", `g"`}
+//
+func splitQuoted(s string) (r []string, err error) {
+ var args []string
+ arg := make([]rune, len(s))
+ escaped := false
+ quoted := false
+ quote := '\x00'
+ i := 0
+ for _, rune := range s {
+ switch {
+ case escaped:
+ escaped = false
+ case rune == '\\':
+ escaped = true
+ continue
+ case quote != '\x00':
+ if rune == quote {
+ quote = '\x00'
+ continue
+ }
+ case rune == '"' || rune == '\'':
+ quoted = true
+ quote = rune
+ continue
+ case unicode.IsSpace(rune):
+ if quoted || i > 0 {
+ quoted = false
+ args = append(args, string(arg[:i]))
+ i = 0
+ }
+ continue
+ }
+ arg[i] = rune
+ i++
+ }
+ if quoted || i > 0 {
+ args = append(args, string(arg[:i]))
+ }
+ if quote != 0 {
+ err = errors.New("unclosed quote")
+ } else if escaped {
+ err = errors.New("unfinished escaping")
+ }
+ return args, err
+}
+
+// match reports whether the name is one of:
+//
+// $GOOS
+// $GOARCH
+// cgo (if cgo is enabled)
+// !cgo (if cgo is disabled)
+// ctxt.Compiler
+// !ctxt.Compiler
+// tag (if ctxt.MatchTag(name) returns true)
+// !tag (if ctxt.MatchTag(name) returns false)
+// a comma-separated list of any of these
+//
+func (ctxt *Context) match(name string, allTags map[string]bool) bool {
+ if name == "" {
+ if allTags != nil {
+ allTags[name] = true
+ }
+ return false
+ }
+ if i := strings.Index(name, ","); i >= 0 {
+ // comma-separated list
+ ok1 := ctxt.match(name[:i], allTags)
+ ok2 := ctxt.match(name[i+1:], allTags)
+ return ok1 && ok2
+ }
+ if strings.HasPrefix(name, "!!") { // bad syntax, reject always
+ return false
+ }
+ neg := false
+ if strings.HasPrefix(name, "!") {
+ if len(name) <= 1 {
+ return false
+ }
+ neg = true
+ name = name[1:]
+ }
+
+ if allTags != nil {
+ allTags[name] = true
+ }
+
+ // Tags must be letters, digits, underscores or dots.
+ // Unlike in Go identifiers, all digits are fine (e.g., "386").
+ for _, c := range name {
+ if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
+ return false
+ }
+ }
+
+ return ctxt.matchTag(name, neg)
+}
+
+// matchTag implements ctxt.MatchTag by calling that
+// field if not nil, and otherwise using ctxt.DefaultMatchTag.
+func (ctxt *Context) matchTag(name string, neg bool) bool {
+ if ctxt.MatchTag != nil {
+ return ctxt.MatchTag(name, neg)
+ }
+ return ctxt.DefaultMatchTag(name) != neg
+}
+
+// DefaultMatchTag reports whether the given name matches the given
+// tag with respect to the context. It is used to provide the default implementation
+// of ctxt.MatchTag used if that field is nil.
+func (ctxt *Context) DefaultMatchTag(name string) bool {
+ // special tags
+ if ctxt.CgoEnabled && name == "cgo" {
+ return true
+ }
+ if name == ctxt.GOOS || name == ctxt.GOARCH || name == ctxt.Compiler {
+ return true
+ }
+ if ctxt.GOOS == "android" && name == "linux" {
+ return true
+ }
+ // other tags
+ for _, tag := range ctxt.BuildTags {
+ if tag == name {
+ return true
+ }
+ }
+ for _, tag := range ctxt.ReleaseTags {
+ if tag == name {
+ return true
+ }
+ }
+ return false
+}
+
+// goodOSArchFile returns false if the name contains a $GOOS or $GOARCH
+// suffix which does not match the current system.
+// The recognized name formats are:
+//
+// name_$(GOOS).*
+// name_$(GOARCH).*
+// name_$(GOOS)_$(GOARCH).*
+// name_$(GOOS)_test.*
+// name_$(GOARCH)_test.*
+// name_$(GOOS)_$(GOARCH)_test.*
+//
+// An exception: if GOOS=android, then files with GOOS=linux are also matched.
+func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
+ if dot := strings.Index(name, "."); dot != -1 {
+ name = name[:dot]
+ }
+
+ // Before Go 1.4, a file called "linux.go" would be equivalent to having a
+ // build tag "linux" in that file. For Go 1.4 and beyond, we require this
+ // auto-tagging to apply only to files with a non-empty prefix, so
+ // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
+ // systems, such as android, to arrive without breaking existing code with
+ // innocuous source code in "android.go". The easiest fix: cut everything
+ // in the name before the initial _.
+ i := strings.Index(name, "_")
+ if i < 0 {
+ return true
+ }
+ name = name[i:] // ignore everything before first _
+
+ l := strings.Split(name, "_")
+ if n := len(l); n > 0 && l[n-1] == "test" {
+ l = l[:n-1]
+ }
+ n := len(l)
+ if n >= 2 && knownOS[l[n-2]] && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-2]] = true
+ allTags[l[n-1]] = true
+ }
+ return ctxt.matchTag(l[n-1], false) && ctxt.matchTag(l[n-2], false)
+ }
+ if n >= 1 && knownOS[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
+ return ctxt.matchTag(l[n-1], false)
+ }
+ if n >= 1 && knownArch[l[n-1]] {
+ if allTags != nil {
+ allTags[l[n-1]] = true
+ }
+ return ctxt.matchTag(l[n-1], false)
+ }
+ return true
+}
+
+var knownOS = make(map[string]bool)
+var knownArch = make(map[string]bool)
+
+func init() {
+ for _, v := range strings.Fields(goosList) {
+ knownOS[v] = true
+ }
+ for _, v := range strings.Fields(goarchList) {
+ knownArch[v] = true
+ }
+}
+
+// KnownOS reports whether the argument is the name of known OS.
+func KnownOS(os string) bool {
+ return knownOS[os]
+}
+
+// KnownArch reports whether the argument is the name of a known architecture.
+func KnownArch(arch string) bool {
+ return knownArch[arch]
+}
+
+// ToolDir is the directory containing build tools.
+var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
+
+// IsLocalImport reports whether the import path is
+// a local import path, like ".", "..", "./foo", or "../foo".
+func IsLocalImport(path string) bool {
+ return path == "." || path == ".." ||
+ strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
+}
+
+// ArchChar returns "?" and an error.
+// In earlier versions of Go, the returned string was used to derive
+// the compiler and linker tool names, the default object file suffix,
+// and the default linker output name. As of Go 1.5, those strings
+// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
+func ArchChar(goarch string) (string, error) {
+ return "?", errors.New("architecture letter no longer used")
+}
diff --git a/vendor/github.com/rogpeppe/godeps/build/doc.go b/vendor/github.com/rogpeppe/godeps/build/doc.go
new file mode 100644
index 000000000..979d0477d
--- /dev/null
+++ b/vendor/github.com/rogpeppe/godeps/build/doc.go
@@ -0,0 +1,165 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package build gathers information about Go packages.
+//
+// Go Path
+//
+// The Go path is a list of directory trees containing Go source code.
+// It is consulted to resolve imports that cannot be found in the standard
+// Go tree. The default path is the value of the GOPATH environment
+// variable, interpreted as a path list appropriate to the operating system
+// (on Unix, the variable is a colon-separated string;
+// on Windows, a semicolon-separated string;
+// on Plan 9, a list).
+//
+// Each directory listed in the Go path must have a prescribed structure:
+//
+// The src/ directory holds source code. The path below 'src' determines
+// the import path or executable name.
+//
+// The pkg/ directory holds installed package objects.
+// As in the Go tree, each target operating system and
+// architecture pair has its own subdirectory of pkg
+// (pkg/GOOS_GOARCH).
+//
+// If DIR is a directory listed in the Go path, a package with
+// source in DIR/src/foo/bar can be imported as "foo/bar" and
+// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
+// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
+//
+// The bin/ directory holds compiled commands.
+// Each command is named for its source directory, but only
+// using the final element, not the entire path. That is, the
+// command with source in DIR/src/foo/quux is installed into
+// DIR/bin/quux, not DIR/bin/foo/quux. The foo/ is stripped
+// so that you can add DIR/bin to your PATH to get at the
+// installed commands.
+//
+// Here's an example directory layout:
+//
+// GOPATH=/home/user/gocode
+//
+// /home/user/gocode/
+// src/
+// foo/
+// bar/ (go code in package bar)
+// x.go
+// quux/ (go code in package main)
+// y.go
+// bin/
+// quux (installed command)
+// pkg/
+// linux_amd64/
+// foo/
+// bar.a (installed package object)
+//
+// Build Constraints
+//
+// A build constraint, also known as a build tag, is a line comment that begins
+//
+// // +build
+//
+// that lists the conditions under which a file should be included in the package.
+// Constraints may appear in any kind of source file (not just Go), but
+// they must appear near the top of the file, preceded
+// only by blank lines and other line comments. These rules mean that in Go
+// files a build constraint must appear before the package clause.
+//
+// To distinguish build constraints from package documentation, a series of
+// build constraints must be followed by a blank line.
+//
+// A build constraint is evaluated as the OR of space-separated options;
+// each option evaluates as the AND of its comma-separated terms;
+// and each term is an alphanumeric word or, preceded by !, its negation.
+// That is, the build constraint:
+//
+// // +build linux,386 darwin,!cgo
+//
+// corresponds to the boolean formula:
+//
+// (linux AND 386) OR (darwin AND (NOT cgo))
+//
+// A file may have multiple build constraints. The overall constraint is the AND
+// of the individual constraints. That is, the build constraints:
+//
+// // +build linux darwin
+// // +build 386
+//
+// corresponds to the boolean formula:
+//
+// (linux OR darwin) AND 386
+//
+// During a particular build, the following words are satisfied:
+//
+// - the target operating system, as spelled by runtime.GOOS
+// - the target architecture, as spelled by runtime.GOARCH
+// - the compiler being used, either "gc" or "gccgo"
+// - "cgo", if ctxt.CgoEnabled is true
+// - "go1.1", from Go version 1.1 onward
+// - "go1.2", from Go version 1.2 onward
+// - "go1.3", from Go version 1.3 onward
+// - "go1.4", from Go version 1.4 onward
+// - "go1.5", from Go version 1.5 onward
+// - "go1.6", from Go version 1.6 onward
+// - "go1.7", from Go version 1.7 onward
+// - "go1.8", from Go version 1.8 onward
+// - any additional words listed in ctxt.BuildTags
+//
+// If a file's name, after stripping the extension and a possible _test suffix,
+// matches any of the following patterns:
+// *_GOOS
+// *_GOARCH
+// *_GOOS_GOARCH
+// (example: source_windows_amd64.go) where GOOS and GOARCH represent
+// any known operating system and architecture values respectively, then
+// the file is considered to have an implicit build constraint requiring
+// those terms (in addition to any explicit constraints in the file).
+//
+// To keep a file from being considered for the build:
+//
+// // +build ignore
+//
+// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
+//
+// To build a file only when using cgo, and only on Linux and OS X:
+//
+// // +build linux,cgo darwin,cgo
+//
+// Such a file is usually paired with another file implementing the
+// default functionality for other systems, which in this case would
+// carry the constraint:
+//
+// // +build !linux,!darwin !cgo
+//
+// Naming a file dns_windows.go will cause it to be included only when
+// building the package for Windows; similarly, math_386.s will be included
+// only when building the package for 32-bit x86.
+//
+// Using GOOS=android matches build tags and files as for GOOS=linux
+// in addition to android tags and files.
+//
+// Binary-Only Packages
+//
+// It is possible to distribute packages in binary form without including the
+// source code used for compiling the package. To do this, the package must
+// be distributed with a source file not excluded by build constraints and
+// containing a "//go:binary-only-package" comment.
+// Like a build constraint, this comment must appear near the top of the file,
+// preceded only by blank lines and other line comments and with a blank line
+// following the comment, to separate it from the package documentation.
+// Unlike build constraints, this comment is only recognized in non-test
+// Go source files.
+//
+// The minimal source code for a binary-only package is therefore:
+//
+// //go:binary-only-package
+//
+// package mypkg
+//
+// The source code may include additional Go code. That code is never compiled
+// but will be processed by tools like godoc and might be useful as end-user
+// documentation.
+//
+package build
diff --git a/vendor/github.com/rogpeppe/godeps/build/read.go b/vendor/github.com/rogpeppe/godeps/build/read.go
new file mode 100644
index 000000000..29b8cdc78
--- /dev/null
+++ b/vendor/github.com/rogpeppe/godeps/build/read.go
@@ -0,0 +1,247 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package build
+
+import (
+ "bufio"
+ "errors"
+ "io"
+ "unicode/utf8"
+)
+
+type importReader struct {
+ b *bufio.Reader
+ buf []byte
+ peek byte
+ err error
+ eof bool
+ nerr int
+}
+
+func isIdent(c byte) bool {
+ return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
+}
+
+var (
+ errSyntax = errors.New("syntax error")
+ errNUL = errors.New("unexpected NUL in input")
+)
+
+// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
+func (r *importReader) syntaxError() {
+ if r.err == nil {
+ r.err = errSyntax
+ }
+}
+
+// readByte reads the next byte from the input, saves it in buf, and returns it.
+// If an error occurs, readByte records the error in r.err and returns 0.
+func (r *importReader) readByte() byte {
+ c, err := r.b.ReadByte()
+ if err == nil {
+ r.buf = append(r.buf, c)
+ if c == 0 {
+ err = errNUL
+ }
+ }
+ if err != nil {
+ if err == io.EOF {
+ r.eof = true
+ } else if r.err == nil {
+ r.err = err
+ }
+ c = 0
+ }
+ return c
+}
+
+// peekByte returns the next byte from the input reader but does not advance beyond it.
+// If skipSpace is set, peekByte skips leading spaces and comments.
+func (r *importReader) peekByte(skipSpace bool) byte {
+ if r.err != nil {
+ if r.nerr++; r.nerr > 10000 {
+ panic("go/build: import reader looping")
+ }
+ return 0
+ }
+
+ // Use r.peek as first input byte.
+ // Don't just return r.peek here: it might have been left by peekByte(false)
+ // and this might be peekByte(true).
+ c := r.peek
+ if c == 0 {
+ c = r.readByte()
+ }
+ for r.err == nil && !r.eof {
+ if skipSpace {
+ // For the purposes of this reader, semicolons are never necessary to
+ // understand the input and are treated as spaces.
+ switch c {
+ case ' ', '\f', '\t', '\r', '\n', ';':
+ c = r.readByte()
+ continue
+
+ case '/':
+ c = r.readByte()
+ if c == '/' {
+ for c != '\n' && r.err == nil && !r.eof {
+ c = r.readByte()
+ }
+ } else if c == '*' {
+ var c1 byte
+ for (c != '*' || c1 != '/') && r.err == nil {
+ if r.eof {
+ r.syntaxError()
+ }
+ c, c1 = c1, r.readByte()
+ }
+ } else {
+ r.syntaxError()
+ }
+ c = r.readByte()
+ continue
+ }
+ }
+ break
+ }
+ r.peek = c
+ return r.peek
+}
+
+// nextByte is like peekByte but advances beyond the returned byte.
+func (r *importReader) nextByte(skipSpace bool) byte {
+ c := r.peekByte(skipSpace)
+ r.peek = 0
+ return c
+}
+
+// readKeyword reads the given keyword from the input.
+// If the keyword is not present, readKeyword records a syntax error.
+func (r *importReader) readKeyword(kw string) {
+ r.peekByte(true)
+ for i := 0; i < len(kw); i++ {
+ if r.nextByte(false) != kw[i] {
+ r.syntaxError()
+ return
+ }
+ }
+ if isIdent(r.peekByte(false)) {
+ r.syntaxError()
+ }
+}
+
+// readIdent reads an identifier from the input.
+// If an identifier is not present, readIdent records a syntax error.
+func (r *importReader) readIdent() {
+ c := r.peekByte(true)
+ if !isIdent(c) {
+ r.syntaxError()
+ return
+ }
+ for isIdent(r.peekByte(false)) {
+ r.peek = 0
+ }
+}
+
+// readString reads a quoted string literal from the input.
+// If an identifier is not present, readString records a syntax error.
+func (r *importReader) readString(save *[]string) {
+ switch r.nextByte(true) {
+ case '`':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ if r.nextByte(false) == '`' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof {
+ r.syntaxError()
+ }
+ }
+ case '"':
+ start := len(r.buf) - 1
+ for r.err == nil {
+ c := r.nextByte(false)
+ if c == '"' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
+ break
+ }
+ if r.eof || c == '\n' {
+ r.syntaxError()
+ }
+ if c == '\\' {
+ r.nextByte(false)
+ }
+ }
+ default:
+ r.syntaxError()
+ }
+}
+
+// readImport reads an import clause - optional identifier followed by quoted string -
+// from the input.
+func (r *importReader) readImport(imports *[]string) {
+ c := r.peekByte(true)
+ if c == '.' {
+ r.peek = 0
+ } else if isIdent(c) {
+ r.readIdent()
+ }
+ r.readString(imports)
+}
+
+// readComments is like ioutil.ReadAll, except that it only reads the leading
+// block of comments in the file.
+func readComments(f io.Reader) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+ r.peekByte(true)
+ if r.err == nil && !r.eof {
+ // Didn't reach EOF, so must have found a non-space byte. Remove it.
+ r.buf = r.buf[:len(r.buf)-1]
+ }
+ return r.buf, r.err
+}
+
+// readImports is like ioutil.ReadAll, except that it expects a Go file as input
+// and stops reading the input once the imports have completed.
+func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
+ r := &importReader{b: bufio.NewReader(f)}
+
+ r.readKeyword("package")
+ r.readIdent()
+ for r.peekByte(true) == 'i' {
+ r.readKeyword("import")
+ if r.peekByte(true) == '(' {
+ r.nextByte(false)
+ for r.peekByte(true) != ')' && r.err == nil {
+ r.readImport(imports)
+ }
+ r.nextByte(false)
+ } else {
+ r.readImport(imports)
+ }
+ }
+
+ // If we stopped successfully before EOF, we read a byte that told us we were done.
+ // Return all but that last byte, which would cause a syntax error if we let it through.
+ if r.err == nil && !r.eof {
+ return r.buf[:len(r.buf)-1], nil
+ }
+
+ // If we stopped for a syntax error, consume the whole file so that
+ // we are sure we don't change the errors that go/parser returns.
+ if r.err == errSyntax && !reportSyntaxError {
+ r.err = nil
+ for r.err == nil && !r.eof {
+ r.readByte()
+ }
+ }
+
+ return r.buf, r.err
+}
diff --git a/vendor/github.com/rogpeppe/godeps/build/syslist.go b/vendor/github.com/rogpeppe/godeps/build/syslist.go
new file mode 100644
index 000000000..73fdbe6c8
--- /dev/null
+++ b/vendor/github.com/rogpeppe/godeps/build/syslist.go
@@ -0,0 +1,8 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package build
+
+const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows zos "
+const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc s390 s390x sparc sparc64 "
diff --git a/vendor/github.com/rogpeppe/godeps/build/zcgo.go b/vendor/github.com/rogpeppe/godeps/build/zcgo.go
new file mode 100644
index 000000000..b588b76d5
--- /dev/null
+++ b/vendor/github.com/rogpeppe/godeps/build/zcgo.go
@@ -0,0 +1,35 @@
+// auto generated by go tool dist
+
+package build
+
+const defaultCGO_ENABLED = ""
+
+var cgoEnabled = map[string]bool{
+ "android/386": true,
+ "android/amd64": true,
+ "android/arm": true,
+ "android/arm64": true,
+ "darwin/386": true,
+ "darwin/amd64": true,
+ "darwin/arm": true,
+ "darwin/arm64": true,
+ "dragonfly/amd64": true,
+ "freebsd/386": true,
+ "freebsd/amd64": true,
+ "linux/386": true,
+ "linux/amd64": true,
+ "linux/arm": true,
+ "linux/arm64": true,
+ "linux/mips64": true,
+ "linux/mips64le": true,
+ "linux/ppc64le": true,
+ "linux/s390x": true,
+ "netbsd/386": true,
+ "netbsd/amd64": true,
+ "netbsd/arm": true,
+ "openbsd/386": true,
+ "openbsd/amd64": true,
+ "solaris/amd64": true,
+ "windows/386": true,
+ "windows/amd64": true,
+}