github.com/sc0rp1us/gb@v0.4.1-0.20160319180011-4ba8cf1baa5a/importer/build.go (about)

     1  // Copyright 2011 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package importer
     6  
     7  import (
     8  	"bytes"
     9  	"errors"
    10  	"fmt"
    11  	"go/token"
    12  	"path/filepath"
    13  	"strings"
    14  	"unicode"
    15  )
    16  
    17  // importer is a private interface defined here so public *Importer
    18  // types can be embedded in *Package with exposing them to the caller.
    19  type importer interface {
    20  	matchFile(path string, allTags map[string]bool) (match bool, data []byte, err error)
    21  	match(name string, allTags map[string]bool) bool
    22  }
    23  
    24  // A Package describes the Go package found in a directory.
    25  type Package struct {
    26  	importer               // the importer context that loaded this package
    27  	Dir           string   // directory containing package sources
    28  	Name          string   // package name
    29  	ImportPath    string   // import path of package ("" if unknown)
    30  	Root          string   // root of Go tree where this package lives
    31  	SrcRoot       string   // package source root directory ("" if unknown)
    32  	PkgTargetRoot string   // architecture dependent install root directory ("" if unknown)
    33  	Standard      bool     // package found in GOROOT
    34  	AllTags       []string // tags that can influence file selection in this directory
    35  	ConflictDir   string   // this directory shadows Dir in $GOPATH
    36  
    37  	// Source files
    38  	GoFiles        []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
    39  	CgoFiles       []string // .go source files that import "C"
    40  	IgnoredGoFiles []string // .go source files ignored for this build
    41  	CFiles         []string // .c source files
    42  	CXXFiles       []string // .cc, .cpp and .cxx source files
    43  	MFiles         []string // .m (Objective-C) source files
    44  	HFiles         []string // .h, .hh, .hpp and .hxx source files
    45  	SFiles         []string // .s source files
    46  	SwigFiles      []string // .swig files
    47  	SwigCXXFiles   []string // .swigcxx files
    48  	SysoFiles      []string // .syso system object files to add to archive
    49  
    50  	// Cgo directives
    51  	CgoCFLAGS    []string // Cgo CFLAGS directives
    52  	CgoCPPFLAGS  []string // Cgo CPPFLAGS directives
    53  	CgoCXXFLAGS  []string // Cgo CXXFLAGS directives
    54  	CgoLDFLAGS   []string // Cgo LDFLAGS directives
    55  	CgoPkgConfig []string // Cgo pkg-config directives
    56  
    57  	// Dependency information
    58  	Imports   []string                    // imports from GoFiles, CgoFiles
    59  	ImportPos map[string][]token.Position // line information for Imports
    60  
    61  	// Test information
    62  	TestGoFiles    []string                    // _test.go files in package
    63  	TestImports    []string                    // imports from TestGoFiles
    64  	TestImportPos  map[string][]token.Position // line information for TestImports
    65  	XTestGoFiles   []string                    // _test.go files outside package
    66  	XTestImports   []string                    // imports from XTestGoFiles
    67  	XTestImportPos map[string][]token.Position // line information for XTestImports
    68  }
    69  
    70  // NoGoError is the error used by Import to describe a directory
    71  // containing no buildable Go source files. (It may still contain
    72  // test files, files hidden by build tags, and so on.)
    73  type NoGoError struct {
    74  	Dir string
    75  }
    76  
    77  func (e *NoGoError) Error() string {
    78  	return "no buildable Go source files in " + e.Dir
    79  }
    80  
    81  // MultiplePackageError describes a directory containing
    82  // multiple buildable Go source files for multiple packages.
    83  type MultiplePackageError struct {
    84  	Dir      string   // directory containing files
    85  	Packages []string // package names found
    86  	Files    []string // corresponding files: Files[i] declares package Packages[i]
    87  }
    88  
    89  func (e *MultiplePackageError) Error() string {
    90  	// Error string limited to two entries for compatibility.
    91  	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)
    92  }
    93  
    94  // expandSrcDir expands any occurrence of ${SRCDIR}, making sure
    95  // the result is safe for the shell.
    96  func expandSrcDir(str string, srcdir string) (string, bool) {
    97  	// "\" delimited paths cause safeCgoName to fail
    98  	// so convert native paths with a different delimeter
    99  	// to "/" before starting (eg: on windows).
   100  	srcdir = filepath.ToSlash(srcdir)
   101  
   102  	// Spaces are tolerated in ${SRCDIR}, but not anywhere else.
   103  	chunks := strings.Split(str, "${SRCDIR}")
   104  	if len(chunks) < 2 {
   105  		return str, safeCgoName(str, false)
   106  	}
   107  	ok := true
   108  	for _, chunk := range chunks {
   109  		ok = ok && (chunk == "" || safeCgoName(chunk, false))
   110  	}
   111  	ok = ok && (srcdir == "" || safeCgoName(srcdir, true))
   112  	res := strings.Join(chunks, srcdir)
   113  	return res, ok && res != ""
   114  }
   115  
   116  // NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
   117  // We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
   118  // See golang.org/issue/6038.
   119  const safeString = "+-.,/0123456789=ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz:$"
   120  const safeSpaces = " "
   121  
   122  var safeBytes = []byte(safeSpaces + safeString)
   123  
   124  func safeCgoName(s string, spaces bool) bool {
   125  	if s == "" {
   126  		return false
   127  	}
   128  	safe := safeBytes
   129  	if !spaces {
   130  		safe = safe[len(safeSpaces):]
   131  	}
   132  	for i := 0; i < len(s); i++ {
   133  		if c := s[i]; c < 0x80 && bytes.IndexByte(safe, c) < 0 {
   134  			return false
   135  		}
   136  	}
   137  	return true
   138  }
   139  
   140  // splitQuoted splits the string s around each instance of one or more consecutive
   141  // white space characters while taking into account quotes and escaping, and
   142  // returns an array of substrings of s or an empty list if s contains only white space.
   143  // Single quotes and double quotes are recognized to prevent splitting within the
   144  // quoted region, and are removed from the resulting substrings. If a quote in s
   145  // isn't closed err will be set and r will have the unclosed argument as the
   146  // last element.  The backslash is used for escaping.
   147  //
   148  // For example, the following string:
   149  //
   150  //     a b:"c d" 'e''f'  "g\""
   151  //
   152  // Would be parsed as:
   153  //
   154  //     []string{"a", "b:c d", "ef", `g"`}
   155  //
   156  func splitQuoted(s string) (r []string, err error) {
   157  	var args []string
   158  	arg := make([]rune, len(s))
   159  	escaped := false
   160  	quoted := false
   161  	quote := '\x00'
   162  	i := 0
   163  	for _, rune := range s {
   164  		switch {
   165  		case escaped:
   166  			escaped = false
   167  		case rune == '\\':
   168  			escaped = true
   169  			continue
   170  		case quote != '\x00':
   171  			if rune == quote {
   172  				quote = '\x00'
   173  				continue
   174  			}
   175  		case rune == '"' || rune == '\'':
   176  			quoted = true
   177  			quote = rune
   178  			continue
   179  		case unicode.IsSpace(rune):
   180  			if quoted || i > 0 {
   181  				quoted = false
   182  				args = append(args, string(arg[:i]))
   183  				i = 0
   184  			}
   185  			continue
   186  		}
   187  		arg[i] = rune
   188  		i++
   189  	}
   190  	if quoted || i > 0 {
   191  		args = append(args, string(arg[:i]))
   192  	}
   193  	if quote != 0 {
   194  		err = errors.New("unclosed quote")
   195  	} else if escaped {
   196  		err = errors.New("unfinished escaping")
   197  	}
   198  	return args, err
   199  }