Commit af146490 by Ian Lance Taylor

runtime: Remove now unnecessary pad field from ParFor.

    
    It is not needed due to the removal of the ctx field.
    
    Reviewed-on: https://go-review.googlesource.com/16525

From-SVN: r229616
parent 725e1be3

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

16f69a4007a1903da4055a496882b514e05f45f3 4b6b496579225cdd897130f6d6fd18ecb100bf99
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
883bc6ed0ea815293fe6309d66f967ea60630e87 bb03defe933c89fee44be675d7aa0fbd893ced30
The first line of this file holds the git revision number of the The first line of this file holds the git revision number of the
last merge done from the master library sources. last merge done from the master library sources.
go1.4.2 go1.5
\ No newline at end of file \ No newline at end of file
...@@ -139,8 +139,8 @@ func (fi headerFileInfo) Mode() (mode os.FileMode) { ...@@ -139,8 +139,8 @@ func (fi headerFileInfo) Mode() (mode os.FileMode) {
} }
switch fi.h.Typeflag { switch fi.h.Typeflag {
case TypeLink, TypeSymlink: case TypeSymlink:
// hard link, symbolic link // symbolic link
mode |= os.ModeSymlink mode |= os.ModeSymlink
case TypeChar: case TypeChar:
// character device node // character device node
...@@ -249,6 +249,30 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) { ...@@ -249,6 +249,30 @@ func FileInfoHeader(fi os.FileInfo, link string) (*Header, error) {
if fm&os.ModeSticky != 0 { if fm&os.ModeSticky != 0 {
h.Mode |= c_ISVTX h.Mode |= c_ISVTX
} }
// If possible, populate additional fields from OS-specific
// FileInfo fields.
if sys, ok := fi.Sys().(*Header); ok {
// This FileInfo came from a Header (not the OS). Use the
// original Header to populate all remaining fields.
h.Uid = sys.Uid
h.Gid = sys.Gid
h.Uname = sys.Uname
h.Gname = sys.Gname
h.AccessTime = sys.AccessTime
h.ChangeTime = sys.ChangeTime
if sys.Xattrs != nil {
h.Xattrs = make(map[string]string)
for k, v := range sys.Xattrs {
h.Xattrs[k] = v
}
}
if sys.Typeflag == TypeLink {
// hard link
h.Typeflag = TypeLink
h.Size = 0
h.Linkname = sys.Linkname
}
}
if sysStat != nil { if sysStat != nil {
return h, sysStat(fi, h) return h, sysStat(fi, h)
} }
......
...@@ -85,6 +85,8 @@ const ( ...@@ -85,6 +85,8 @@ const (
func NewReader(r io.Reader) *Reader { return &Reader{r: r} } func NewReader(r io.Reader) *Reader { return &Reader{r: r} }
// Next advances to the next entry in the tar archive. // Next advances to the next entry in the tar archive.
//
// io.EOF is returned at the end of the input.
func (tr *Reader) Next() (*Header, error) { func (tr *Reader) Next() (*Header, error) {
var hdr *Header var hdr *Header
if tr.err == nil { if tr.err == nil {
...@@ -108,7 +110,13 @@ func (tr *Reader) Next() (*Header, error) { ...@@ -108,7 +110,13 @@ func (tr *Reader) Next() (*Header, error) {
// We actually read the whole file, // We actually read the whole file,
// but this skips alignment padding // but this skips alignment padding
tr.skipUnread() tr.skipUnread()
if tr.err != nil {
return nil, tr.err
}
hdr = tr.readHeader() hdr = tr.readHeader()
if hdr == nil {
return nil, tr.err
}
mergePAX(hdr, headers) mergePAX(hdr, headers)
// Check for a PAX format sparse file // Check for a PAX format sparse file
...@@ -331,7 +339,7 @@ func parsePAX(r io.Reader) (map[string]string, error) { ...@@ -331,7 +339,7 @@ func parsePAX(r io.Reader) (map[string]string, error) {
} }
// Parse the first token as a decimal integer. // Parse the first token as a decimal integer.
n, err := strconv.ParseInt(string(buf[:sp]), 10, 0) n, err := strconv.ParseInt(string(buf[:sp]), 10, 0)
if err != nil { if err != nil || n < 5 || int64(len(buf)) < n {
return nil, ErrHeader return nil, ErrHeader
} }
// Extract everything between the decimal and the n -1 on the // Extract everything between the decimal and the n -1 on the
...@@ -461,6 +469,10 @@ func (tr *Reader) readHeader() *Header { ...@@ -461,6 +469,10 @@ func (tr *Reader) readHeader() *Header {
hdr.Uid = int(tr.octal(s.next(8))) hdr.Uid = int(tr.octal(s.next(8)))
hdr.Gid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8)))
hdr.Size = tr.octal(s.next(12)) hdr.Size = tr.octal(s.next(12))
if hdr.Size < 0 {
tr.err = ErrHeader
return nil
}
hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0)
s.next(8) // chksum s.next(8) // chksum
hdr.Typeflag = s.next(1)[0] hdr.Typeflag = s.next(1)[0]
...@@ -785,6 +797,9 @@ func (sfr *sparseFileReader) Read(b []byte) (n int, err error) { ...@@ -785,6 +797,9 @@ func (sfr *sparseFileReader) Read(b []byte) (n int, err error) {
// Otherwise, we're at the end of the file // Otherwise, we're at the end of the file
return 0, io.EOF return 0, io.EOF
} }
if sfr.tot < sfr.sp[0].offset {
return 0, io.ErrUnexpectedEOF
}
if sfr.pos < sfr.sp[0].offset { if sfr.pos < sfr.sp[0].offset {
// We're in a hole // We're in a hole
n = sfr.readHole(b, sfr.sp[0].offset) n = sfr.readHole(b, sfr.sp[0].offset)
......
...@@ -462,10 +462,15 @@ func TestParsePAXHeader(t *testing.T) { ...@@ -462,10 +462,15 @@ func TestParsePAXHeader(t *testing.T) {
t.Error("Buffer wasn't consumed") t.Error("Buffer wasn't consumed")
} }
} }
badHeader := bytes.NewReader([]byte("3 somelongkey=")) badHeaderTests := [][]byte{
if _, err := parsePAX(badHeader); err != ErrHeader { []byte("3 somelongkey=\n"),
[]byte("50 tooshort=\n"),
}
for _, test := range badHeaderTests {
if _, err := parsePAX(bytes.NewReader(test)); err != ErrHeader {
t.Fatal("Unexpected success when parsing bad header") t.Fatal("Unexpected success when parsing bad header")
} }
}
} }
func TestParsePAXTime(t *testing.T) { func TestParsePAXTime(t *testing.T) {
...@@ -741,3 +746,53 @@ func TestUninitializedRead(t *testing.T) { ...@@ -741,3 +746,53 @@ func TestUninitializedRead(t *testing.T) {
} }
} }
// Negative header size should not cause panic.
// Issues 10959 and 10960.
func TestNegativeHdrSize(t *testing.T) {
f, err := os.Open("testdata/neg-size.tar")
if err != nil {
t.Fatal(err)
}
defer f.Close()
r := NewReader(f)
_, err = r.Next()
if err != ErrHeader {
t.Error("want ErrHeader, got", err)
}
io.Copy(ioutil.Discard, r)
}
// This used to hang in (*sparseFileReader).readHole due to missing
// verification of sparse offsets against file size.
func TestIssue10968(t *testing.T) {
f, err := os.Open("testdata/issue10968.tar")
if err != nil {
t.Fatal(err)
}
defer f.Close()
r := NewReader(f)
_, err = r.Next()
if err != nil {
t.Fatal(err)
}
_, err = io.Copy(ioutil.Discard, r)
if err != io.ErrUnexpectedEOF {
t.Fatalf("expected %q, got %q", io.ErrUnexpectedEOF, err)
}
}
// Do not panic if there are errors in header blocks after the pax header.
// Issue 11169
func TestIssue11169(t *testing.T) {
f, err := os.Open("testdata/issue11169.tar")
if err != nil {
t.Fatal(err)
}
defer f.Close()
r := NewReader(f)
_, err = r.Next()
if err == nil {
t.Fatal("Unexpected success")
}
}
...@@ -147,17 +147,6 @@ func TestHeaderRoundTrip(t *testing.T) { ...@@ -147,17 +147,6 @@ func TestHeaderRoundTrip(t *testing.T) {
}, },
fm: 0644, fm: 0644,
}, },
// hard link.
{
h: &Header{
Name: "hard.txt",
Mode: 0644 | c_ISLNK,
Size: 0,
ModTime: time.Unix(1360600916, 0),
Typeflag: TypeLink,
},
fm: 0644 | os.ModeSymlink,
},
// symbolic link. // symbolic link.
{ {
h: &Header{ h: &Header{
...@@ -246,6 +235,33 @@ func TestHeaderRoundTrip(t *testing.T) { ...@@ -246,6 +235,33 @@ func TestHeaderRoundTrip(t *testing.T) {
}, },
fm: 0600 | os.ModeSticky, fm: 0600 | os.ModeSticky,
}, },
// hard link.
{
h: &Header{
Name: "hard.txt",
Mode: 0644 | c_ISREG,
Size: 0,
Linkname: "file.txt",
ModTime: time.Unix(1360600916, 0),
Typeflag: TypeLink,
},
fm: 0644,
},
// More information.
{
h: &Header{
Name: "info.txt",
Mode: 0600 | c_ISREG,
Size: 0,
Uid: 1000,
Gid: 1000,
ModTime: time.Unix(1360602540, 0),
Uname: "slartibartfast",
Gname: "users",
Typeflag: TypeReg,
},
fm: 0600,
},
} }
for i, g := range golden { for i, g := range golden {
...@@ -268,12 +284,37 @@ func TestHeaderRoundTrip(t *testing.T) { ...@@ -268,12 +284,37 @@ func TestHeaderRoundTrip(t *testing.T) {
if got, want := h2.Size, g.h.Size; got != want { if got, want := h2.Size, g.h.Size; got != want {
t.Errorf("i=%d: Size: got %v, want %v", i, got, want) t.Errorf("i=%d: Size: got %v, want %v", i, got, want)
} }
if got, want := h2.Uid, g.h.Uid; got != want {
t.Errorf("i=%d: Uid: got %d, want %d", i, got, want)
}
if got, want := h2.Gid, g.h.Gid; got != want {
t.Errorf("i=%d: Gid: got %d, want %d", i, got, want)
}
if got, want := h2.Uname, g.h.Uname; got != want {
t.Errorf("i=%d: Uname: got %q, want %q", i, got, want)
}
if got, want := h2.Gname, g.h.Gname; got != want {
t.Errorf("i=%d: Gname: got %q, want %q", i, got, want)
}
if got, want := h2.Linkname, g.h.Linkname; got != want {
t.Errorf("i=%d: Linkname: got %v, want %v", i, got, want)
}
if got, want := h2.Typeflag, g.h.Typeflag; got != want {
t.Logf("%#v %#v", g.h, fi.Sys())
t.Errorf("i=%d: Typeflag: got %q, want %q", i, got, want)
}
if got, want := h2.Mode, g.h.Mode; got != want { if got, want := h2.Mode, g.h.Mode; got != want {
t.Errorf("i=%d: Mode: got %o, want %o", i, got, want) t.Errorf("i=%d: Mode: got %o, want %o", i, got, want)
} }
if got, want := fi.Mode(), g.fm; got != want { if got, want := fi.Mode(), g.fm; got != want {
t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want) t.Errorf("i=%d: fi.Mode: got %o, want %o", i, got, want)
} }
if got, want := h2.AccessTime, g.h.AccessTime; got != want {
t.Errorf("i=%d: AccessTime: got %v, want %v", i, got, want)
}
if got, want := h2.ChangeTime, g.h.ChangeTime; got != want {
t.Errorf("i=%d: ChangeTime: got %v, want %v", i, got, want)
}
if got, want := h2.ModTime, g.h.ModTime; got != want { if got, want := h2.ModTime, g.h.ModTime; got != want {
t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want) t.Errorf("i=%d: ModTime: got %v, want %v", i, got, want)
} }
......
...@@ -355,7 +355,7 @@ func paxHeader(msg string) string { ...@@ -355,7 +355,7 @@ func paxHeader(msg string) string {
// hdr.Size bytes are written after WriteHeader. // hdr.Size bytes are written after WriteHeader.
func (tw *Writer) Write(b []byte) (n int, err error) { func (tw *Writer) Write(b []byte) (n int, err error) {
if tw.closed { if tw.closed {
err = ErrWriteTooLong err = ErrWriteAfterClose
return return
} }
overwrite := false overwrite := false
......
...@@ -147,6 +147,44 @@ var writerTests = []*writerTest{ ...@@ -147,6 +147,44 @@ var writerTests = []*writerTest{
}, },
}, },
}, },
// This file was produced using gnu tar 1.26
// echo "Slartibartfast" > file.txt
// ln file.txt hard.txt
// tar -b 1 --format=ustar -c -f hardlink.tar file.txt hard.txt
{
file: "testdata/hardlink.tar",
entries: []*writerTestEntry{
{
header: &Header{
Name: "file.txt",
Mode: 0644,
Uid: 1000,
Gid: 100,
Size: 15,
ModTime: time.Unix(1425484303, 0),
Typeflag: '0',
Uname: "vbatts",
Gname: "users",
},
contents: "Slartibartfast\n",
},
{
header: &Header{
Name: "hard.txt",
Mode: 0644,
Uid: 1000,
Gid: 100,
Size: 0,
ModTime: time.Unix(1425484303, 0),
Typeflag: '1',
Linkname: "file.txt",
Uname: "vbatts",
Gname: "users",
},
// no contents
},
},
},
} }
// Render byte array in a two-character hexadecimal string, spaced for easy visual inspection. // Render byte array in a two-character hexadecimal string, spaced for easy visual inspection.
...@@ -489,3 +527,20 @@ func TestValidTypeflagWithPAXHeader(t *testing.T) { ...@@ -489,3 +527,20 @@ func TestValidTypeflagWithPAXHeader(t *testing.T) {
} }
} }
} }
func TestWriteAfterClose(t *testing.T) {
var buffer bytes.Buffer
tw := NewWriter(&buffer)
hdr := &Header{
Name: "small.txt",
Size: 5,
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatalf("Failed to write header: %s", err)
}
tw.Close()
if _, err := tw.Write([]byte("Kilts")); err != ErrWriteAfterClose {
t.Fatalf("Write: got %v; want ErrWriteAfterClose", err)
}
}
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"bufio" "bufio"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"hash" "hash"
"hash/crc32" "hash/crc32"
"io" "io"
...@@ -77,6 +78,9 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { ...@@ -77,6 +78,9 @@ func (z *Reader) init(r io.ReaderAt, size int64) error {
if err != nil { if err != nil {
return err return err
} }
if end.directoryRecords > uint64(size)/fileHeaderLen {
return fmt.Errorf("archive/zip: TOC declares impossible %d files in %d byte zip", end.directoryRecords, size)
}
z.r = r z.r = r
z.File = make([]*File, 0, end.directoryRecords) z.File = make([]*File, 0, end.directoryRecords)
z.Comment = end.comment z.Comment = end.comment
...@@ -146,13 +150,19 @@ func (f *File) Open() (rc io.ReadCloser, err error) { ...@@ -146,13 +150,19 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
if f.hasDataDescriptor() { if f.hasDataDescriptor() {
desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen)
} }
rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil} rc = &checksumReader{
rc: rc,
hash: crc32.NewIEEE(),
f: f,
desr: desr,
}
return return
} }
type checksumReader struct { type checksumReader struct {
rc io.ReadCloser rc io.ReadCloser
hash hash.Hash32 hash hash.Hash32
nread uint64 // number of bytes read so far
f *File f *File
desr io.Reader // if non-nil, where to read the data descriptor desr io.Reader // if non-nil, where to read the data descriptor
err error // sticky error err error // sticky error
...@@ -164,13 +174,21 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { ...@@ -164,13 +174,21 @@ func (r *checksumReader) Read(b []byte) (n int, err error) {
} }
n, err = r.rc.Read(b) n, err = r.rc.Read(b)
r.hash.Write(b[:n]) r.hash.Write(b[:n])
r.nread += uint64(n)
if err == nil { if err == nil {
return return
} }
if err == io.EOF { if err == io.EOF {
if r.nread != r.f.UncompressedSize64 {
return 0, io.ErrUnexpectedEOF
}
if r.desr != nil { if r.desr != nil {
if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { if err1 := readDataDescriptor(r.desr, r.f); err1 != nil {
if err1 == io.EOF {
err = io.ErrUnexpectedEOF
} else {
err = err1 err = err1
}
} else if r.hash.Sum32() != r.f.CRC32 { } else if r.hash.Sum32() != r.f.CRC32 {
err = ErrChecksum err = ErrChecksum
} }
......
...@@ -531,3 +531,77 @@ func TestIssue8186(t *testing.T) { ...@@ -531,3 +531,77 @@ func TestIssue8186(t *testing.T) {
} }
} }
} }
// Verify we return ErrUnexpectedEOF when length is short.
func TestIssue10957(t *testing.T) {
data := []byte("PK\x03\x040000000PK\x01\x0200000" +
"0000000000000000000\x00" +
"\x00\x00\x00\x00\x00000000000000PK\x01" +
"\x020000000000000000000" +
"00000\v\x00\x00\x00\x00\x00000000000" +
"00000000000000PK\x01\x0200" +
"00000000000000000000" +
"00\v\x00\x00\x00\x00\x00000000000000" +
"00000000000PK\x01\x020000<" +
"0\x00\x0000000000000000\v\x00\v" +
"\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" +
"00000000PK\x01\x0200000000" +
"0000000000000000\v\x00\x00\x00" +
"\x00\x0000PK\x05\x06000000\x05\x000000" +
"\v\x00\x00\x00\x00\x00")
z, err := NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
t.Fatal(err)
}
for i, f := range z.File {
r, err := f.Open()
if err != nil {
continue
}
if f.UncompressedSize64 < 1e6 {
n, err := io.Copy(ioutil.Discard, r)
if i == 3 && err != io.ErrUnexpectedEOF {
t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err)
}
if err == nil && uint64(n) != f.UncompressedSize64 {
t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64)
}
}
r.Close()
}
}
// Verify the number of files is sane.
func TestIssue10956(t *testing.T) {
data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
"0000PK\x05\x06000000000000" +
"0000\v\x00000\x00\x00\x00\x00\x00\x00\x000")
_, err := NewReader(bytes.NewReader(data), int64(len(data)))
const want = "TOC declares impossible 3472328296227680304 files in 57 byte"
if err == nil && !strings.Contains(err.Error(), want) {
t.Errorf("error = %v; want %q", err, want)
}
}
// Verify we return ErrUnexpectedEOF when reading truncated data descriptor.
func TestIssue11146(t *testing.T) {
data := []byte("PK\x03\x040000000000000000" +
"000000\x01\x00\x00\x000\x01\x00\x00\xff\xff0000" +
"0000000000000000PK\x01\x02" +
"0000\b0\b\x00000000000000" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000000PK\x05\x06\x00\x00" +
"\x00\x0000\x01\x0000008\x00\x00\x00\x00\x00")
z, err := NewReader(bytes.NewReader(data), int64(len(data)))
if err != nil {
t.Fatal(err)
}
r, err := z.File[0].Open()
if err != nil {
t.Fatal(err)
}
_, err = ioutil.ReadAll(r)
if err != io.ErrUnexpectedEOF {
t.Errorf("File[0] error = %v; want io.ErrUnexpectedEOF", err)
}
r.Close()
}
...@@ -81,8 +81,8 @@ type FileHeader struct { ...@@ -81,8 +81,8 @@ type FileHeader struct {
ModifiedTime uint16 // MS-DOS time ModifiedTime uint16 // MS-DOS time
ModifiedDate uint16 // MS-DOS date ModifiedDate uint16 // MS-DOS date
CRC32 uint32 CRC32 uint32
CompressedSize uint32 // deprecated; use CompressedSize64 CompressedSize uint32 // Deprecated: Use CompressedSize64 instead.
UncompressedSize uint32 // deprecated; use UncompressedSize64 UncompressedSize uint32 // Deprecated: Use UncompressedSize64 instead.
CompressedSize64 uint64 CompressedSize64 uint64
UncompressedSize64 uint64 UncompressedSize64 uint64
Extra []byte Extra []byte
...@@ -233,7 +233,7 @@ func (h *FileHeader) SetMode(mode os.FileMode) { ...@@ -233,7 +233,7 @@ func (h *FileHeader) SetMode(mode os.FileMode) {
} }
} }
// isZip64 returns true if the file size exceeds the 32 bit limit // isZip64 reports whether the file size exceeds the 32 bit limit
func (fh *FileHeader) isZip64() bool { func (fh *FileHeader) isZip64() bool {
return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max return fh.CompressedSize64 > uint32max || fh.UncompressedSize64 > uint32max
} }
......
...@@ -34,6 +34,17 @@ func NewWriter(w io.Writer) *Writer { ...@@ -34,6 +34,17 @@ func NewWriter(w io.Writer) *Writer {
return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}} return &Writer{cw: &countWriter{w: bufio.NewWriter(w)}}
} }
// SetOffset sets the offset of the beginning of the zip data within the
// underlying writer. It should be used when the zip data is appended to an
// existing file, such as a binary executable.
// It must be called before any data is written.
func (w *Writer) SetOffset(n int64) {
if w.cw.count != 0 {
panic("zip: SetOffset called after data was written")
}
w.cw.count = n
}
// Flush flushes any buffered data to the underlying writer. // Flush flushes any buffered data to the underlying writer.
// Calling Flush is not normally necessary; calling Close is sufficient. // Calling Flush is not normally necessary; calling Close is sufficient.
func (w *Writer) Flush() error { func (w *Writer) Flush() error {
...@@ -122,7 +133,7 @@ func (w *Writer) Close() error { ...@@ -122,7 +133,7 @@ func (w *Writer) Close() error {
// zip64 end of central directory record // zip64 end of central directory record
b.uint32(directory64EndSignature) b.uint32(directory64EndSignature)
b.uint64(directory64EndLen) b.uint64(directory64EndLen - 12) // length minus signature (uint32) and length fields (uint64)
b.uint16(zipVersion45) // version made by b.uint16(zipVersion45) // version made by
b.uint16(zipVersion45) // version needed to extract b.uint16(zipVersion45) // version needed to extract
b.uint32(0) // number of this disk b.uint32(0) // number of this disk
...@@ -184,14 +195,20 @@ func (w *Writer) Create(name string) (io.Writer, error) { ...@@ -184,14 +195,20 @@ func (w *Writer) Create(name string) (io.Writer, error) {
// CreateHeader adds a file to the zip file using the provided FileHeader // CreateHeader adds a file to the zip file using the provided FileHeader
// for the file metadata. // for the file metadata.
// It returns a Writer to which the file contents should be written. // It returns a Writer to which the file contents should be written.
//
// The file's contents must be written to the io.Writer before the next // The file's contents must be written to the io.Writer before the next
// call to Create, CreateHeader, or Close. // call to Create, CreateHeader, or Close. The provided FileHeader fh
// must not be modified after a call to CreateHeader.
func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
if w.last != nil && !w.last.closed { if w.last != nil && !w.last.closed {
if err := w.last.close(); err != nil { if err := w.last.close(); err != nil {
return nil, err return nil, err
} }
} }
if len(w.dir) > 0 && w.dir[len(w.dir)-1].FileHeader == fh {
// See https://golang.org/issue/11144 confusion.
return nil, errors.New("archive/zip: invalid duplicate FileHeader")
}
fh.Flags |= 0x8 // we will write a data descriptor fh.Flags |= 0x8 // we will write a data descriptor
......
...@@ -87,6 +87,41 @@ func TestWriter(t *testing.T) { ...@@ -87,6 +87,41 @@ func TestWriter(t *testing.T) {
} }
} }
func TestWriterOffset(t *testing.T) {
largeData := make([]byte, 1<<17)
for i := range largeData {
largeData[i] = byte(rand.Int())
}
writeTests[1].Data = largeData
defer func() {
writeTests[1].Data = nil
}()
// write a zip file
buf := new(bytes.Buffer)
existingData := []byte{1, 2, 3, 1, 2, 3, 1, 2, 3}
n, _ := buf.Write(existingData)
w := NewWriter(buf)
w.SetOffset(int64(n))
for _, wt := range writeTests {
testCreate(t, w, &wt)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
// read it back
r, err := NewReader(bytes.NewReader(buf.Bytes()), int64(buf.Len()))
if err != nil {
t.Fatal(err)
}
for i, wt := range writeTests {
testReadFile(t, r.File[i], &wt)
}
}
func TestWriterFlush(t *testing.T) { func TestWriterFlush(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
w := NewWriter(struct{ io.Writer }{&buf}) w := NewWriter(struct{ io.Writer }{&buf})
......
...@@ -229,10 +229,11 @@ func TestZip64(t *testing.T) { ...@@ -229,10 +229,11 @@ func TestZip64(t *testing.T) {
t.Skip("slow test; skipping") t.Skip("slow test; skipping")
} }
const size = 1 << 32 // before the "END\n" part const size = 1 << 32 // before the "END\n" part
testZip64(t, size) buf := testZip64(t, size)
testZip64DirectoryRecordLength(buf, t)
} }
func testZip64(t testing.TB, size int64) { func testZip64(t testing.TB, size int64) *rleBuffer {
const chunkSize = 1024 const chunkSize = 1024
chunks := int(size / chunkSize) chunks := int(size / chunkSize)
// write 2^32 bytes plus "END\n" to a zip file // write 2^32 bytes plus "END\n" to a zip file
...@@ -302,6 +303,37 @@ func testZip64(t testing.TB, size int64) { ...@@ -302,6 +303,37 @@ func testZip64(t testing.TB, size int64) {
if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want { if got, want := f0.UncompressedSize64, uint64(size)+uint64(len(end)); got != want {
t.Errorf("UncompressedSize64 %d, want %d", got, want) t.Errorf("UncompressedSize64 %d, want %d", got, want)
} }
return buf
}
// Issue 9857
func testZip64DirectoryRecordLength(buf *rleBuffer, t *testing.T) {
d := make([]byte, 1024)
if _, err := buf.ReadAt(d, buf.Size()-int64(len(d))); err != nil {
t.Fatal("read:", err)
}
sigOff := findSignatureInBlock(d)
dirOff, err := findDirectory64End(buf, buf.Size()-int64(len(d))+int64(sigOff))
if err != nil {
t.Fatal("findDirectory64End:", err)
}
d = make([]byte, directory64EndLen)
if _, err := buf.ReadAt(d, dirOff); err != nil {
t.Fatal("read:", err)
}
b := readBuf(d)
if sig := b.uint32(); sig != directory64EndSignature {
t.Fatalf("Expected directory64EndSignature (%d), got %d", directory64EndSignature, sig)
}
size := b.uint64()
if size != directory64EndLen-12 {
t.Fatalf("Expected length of %d, got %d", directory64EndLen-12, size)
}
} }
func testInvalidHeader(h *FileHeader, t *testing.T) { func testInvalidHeader(h *FileHeader, t *testing.T) {
......
...@@ -144,6 +144,39 @@ func (b *Reader) Peek(n int) ([]byte, error) { ...@@ -144,6 +144,39 @@ func (b *Reader) Peek(n int) ([]byte, error) {
return b.buf[b.r : b.r+n], err return b.buf[b.r : b.r+n], err
} }
// Discard skips the next n bytes, returning the number of bytes discarded.
//
// If Discard skips fewer than n bytes, it also returns an error.
// If 0 <= n <= b.Buffered(), Discard is guaranteed to succeed without
// reading from the underlying io.Reader.
func (b *Reader) Discard(n int) (discarded int, err error) {
if n < 0 {
return 0, ErrNegativeCount
}
if n == 0 {
return
}
remain := n
for {
skip := b.Buffered()
if skip == 0 {
b.fill()
skip = b.Buffered()
}
if skip > remain {
skip = remain
}
b.r += skip
remain -= skip
if remain == 0 {
return n, nil
}
if b.err != nil {
return n - remain, b.readErr()
}
}
}
// Read reads data into p. // Read reads data into p.
// It returns the number of bytes read into p. // It returns the number of bytes read into p.
// It calls Read at most once on the underlying Reader, // It calls Read at most once on the underlying Reader,
...@@ -367,7 +400,6 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) { ...@@ -367,7 +400,6 @@ func (b *Reader) ReadBytes(delim byte) (line []byte, err error) {
// accumulating full buffers. // accumulating full buffers.
var frag []byte var frag []byte
var full [][]byte var full [][]byte
err = nil
for { for {
var e error var e error
......
...@@ -1268,6 +1268,135 @@ func TestWriterReset(t *testing.T) { ...@@ -1268,6 +1268,135 @@ func TestWriterReset(t *testing.T) {
} }
} }
func TestReaderDiscard(t *testing.T) {
tests := []struct {
name string
r io.Reader
bufSize int // 0 means 16
peekSize int
n int // input to Discard
want int // from Discard
wantErr error // from Discard
wantBuffered int
}{
{
name: "normal case",
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
peekSize: 16,
n: 6,
want: 6,
wantBuffered: 10,
},
{
name: "discard causing read",
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
n: 6,
want: 6,
wantBuffered: 10,
},
{
name: "discard all without peek",
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
n: 26,
want: 26,
wantBuffered: 0,
},
{
name: "discard more than end",
r: strings.NewReader("abcdefghijklmnopqrstuvwxyz"),
n: 27,
want: 26,
wantErr: io.EOF,
wantBuffered: 0,
},
// Any error from filling shouldn't show up until we
// get past the valid bytes. Here we return we return 5 valid bytes at the same time
// as an error, but test that we don't see the error from Discard.
{
name: "fill error, discard less",
r: newScriptedReader(func(p []byte) (n int, err error) {
if len(p) < 5 {
panic("unexpected small read")
}
return 5, errors.New("5-then-error")
}),
n: 4,
want: 4,
wantErr: nil,
wantBuffered: 1,
},
{
name: "fill error, discard equal",
r: newScriptedReader(func(p []byte) (n int, err error) {
if len(p) < 5 {
panic("unexpected small read")
}
return 5, errors.New("5-then-error")
}),
n: 5,
want: 5,
wantErr: nil,
wantBuffered: 0,
},
{
name: "fill error, discard more",
r: newScriptedReader(func(p []byte) (n int, err error) {
if len(p) < 5 {
panic("unexpected small read")
}
return 5, errors.New("5-then-error")
}),
n: 6,
want: 5,
wantErr: errors.New("5-then-error"),
wantBuffered: 0,
},
// Discard of 0 shouldn't cause a read:
{
name: "discard zero",
r: newScriptedReader(), // will panic on Read
n: 0,
want: 0,
wantErr: nil,
wantBuffered: 0,
},
{
name: "discard negative",
r: newScriptedReader(), // will panic on Read
n: -1,
want: 0,
wantErr: ErrNegativeCount,
wantBuffered: 0,
},
}
for _, tt := range tests {
br := NewReaderSize(tt.r, tt.bufSize)
if tt.peekSize > 0 {
peekBuf, err := br.Peek(tt.peekSize)
if err != nil {
t.Errorf("%s: Peek(%d): %v", tt.name, tt.peekSize, err)
continue
}
if len(peekBuf) != tt.peekSize {
t.Errorf("%s: len(Peek(%d)) = %v; want %v", tt.name, tt.peekSize, len(peekBuf), tt.peekSize)
continue
}
}
discarded, err := br.Discard(tt.n)
if ge, we := fmt.Sprint(err), fmt.Sprint(tt.wantErr); discarded != tt.want || ge != we {
t.Errorf("%s: Discard(%d) = (%v, %v); want (%v, %v)", tt.name, tt.n, discarded, ge, tt.want, we)
continue
}
if bn := br.Buffered(); bn != tt.wantBuffered {
t.Errorf("%s: after Discard, Buffered = %d; want %d", tt.name, bn, tt.wantBuffered)
}
}
}
// An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have. // An onlyReader only implements io.Reader, no matter what other methods the underlying implementation may have.
type onlyReader struct { type onlyReader struct {
io.Reader io.Reader
...@@ -1278,6 +1407,23 @@ type onlyWriter struct { ...@@ -1278,6 +1407,23 @@ type onlyWriter struct {
io.Writer io.Writer
} }
// A scriptedReader is an io.Reader that executes its steps sequentially.
type scriptedReader []func(p []byte) (n int, err error)
func (sr *scriptedReader) Read(p []byte) (n int, err error) {
if len(*sr) == 0 {
panic("too many Read calls on scripted Reader. No steps remain.")
}
step := (*sr)[0]
*sr = (*sr)[1:]
return step(p)
}
func newScriptedReader(steps ...func(p []byte) (n int, err error)) io.Reader {
sr := scriptedReader(steps)
return &sr
}
func BenchmarkReaderCopyOptimal(b *testing.B) { func BenchmarkReaderCopyOptimal(b *testing.B) {
// Optimal case is where the underlying reader implements io.WriterTo // Optimal case is where the underlying reader implements io.WriterTo
srcBuf := bytes.NewBuffer(make([]byte, 8192)) srcBuf := bytes.NewBuffer(make([]byte, 8192))
......
...@@ -109,7 +109,7 @@ func (s *Scanner) Text() string { ...@@ -109,7 +109,7 @@ func (s *Scanner) Text() string {
// After Scan returns false, the Err method will return any error that // After Scan returns false, the Err method will return any error that
// occurred during scanning, except that if it was io.EOF, Err // occurred during scanning, except that if it was io.EOF, Err
// will return nil. // will return nil.
// Split panics if the split function returns 100 empty tokens without // Scan panics if the split function returns 100 empty tokens without
// advancing the input. This is a common error mode for scanners. // advancing the input. This is a common error mode for scanners.
func (s *Scanner) Scan() bool { func (s *Scanner) Scan() bool {
// Loop until we have a token. // Loop until we have a token.
......
...@@ -236,14 +236,14 @@ func panic(v interface{}) ...@@ -236,14 +236,14 @@ func panic(v interface{})
// panicking. // panicking.
func recover() interface{} func recover() interface{}
// The print built-in function formats its arguments in an implementation- // The print built-in function formats its arguments in an
// specific way and writes the result to standard error. // implementation-specific way and writes the result to standard error.
// Print is useful for bootstrapping and debugging; it is not guaranteed // Print is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language. // to stay in the language.
func print(args ...Type) func print(args ...Type)
// The println built-in function formats its arguments in an implementation- // The println built-in function formats its arguments in an
// specific way and writes the result to standard error. // implementation-specific way and writes the result to standard error.
// Spaces are always added between arguments and a newline is appended. // Spaces are always added between arguments and a newline is appended.
// Println is useful for bootstrapping and debugging; it is not guaranteed // Println is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language. // to stay in the language.
......
...@@ -56,6 +56,10 @@ func (b *Buffer) String() string { ...@@ -56,6 +56,10 @@ func (b *Buffer) String() string {
// b.Len() == len(b.Bytes()). // b.Len() == len(b.Bytes()).
func (b *Buffer) Len() int { return len(b.buf) - b.off } func (b *Buffer) Len() int { return len(b.buf) - b.off }
// Cap returns the capacity of the buffer's underlying byte slice, that is, the
// total space allocated for the buffer's data.
func (b *Buffer) Cap() int { return cap(b.buf) }
// Truncate discards all but the first n unread bytes from the buffer. // Truncate discards all but the first n unread bytes from the buffer.
// It panics if n is negative or greater than the length of the buffer. // It panics if n is negative or greater than the length of the buffer.
func (b *Buffer) Truncate(n int) { func (b *Buffer) Truncate(n int) {
......
...@@ -231,6 +231,23 @@ func TestMixedReadsAndWrites(t *testing.T) { ...@@ -231,6 +231,23 @@ func TestMixedReadsAndWrites(t *testing.T) {
empty(t, "TestMixedReadsAndWrites (2)", &buf, s, make([]byte, buf.Len())) empty(t, "TestMixedReadsAndWrites (2)", &buf, s, make([]byte, buf.Len()))
} }
func TestCapWithPreallocatedSlice(t *testing.T) {
buf := NewBuffer(make([]byte, 10))
n := buf.Cap()
if n != 10 {
t.Errorf("expected 10, got %d", n)
}
}
func TestCapWithSliceAndWrittenData(t *testing.T) {
buf := NewBuffer(make([]byte, 0, 10))
buf.Write([]byte("test"))
n := buf.Cap()
if n != 10 {
t.Errorf("expected 10, got %d", n)
}
}
func TestNil(t *testing.T) { func TestNil(t *testing.T) {
var b *Buffer var b *Buffer
if b.String() != "<nil>" { if b.String() != "<nil>" {
......
...@@ -23,7 +23,7 @@ func equalPortable(a, b []byte) bool { ...@@ -23,7 +23,7 @@ func equalPortable(a, b []byte) bool {
return true return true
} }
// explode splits s into a slice of UTF-8 sequences, one per Unicode character (still slices of bytes), // explode splits s into a slice of UTF-8 sequences, one per Unicode code point (still slices of bytes),
// up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes. // up to a maximum of n byte slices. Invalid UTF-8 sequences are chopped into individual bytes.
func explode(s []byte, n int) [][]byte { func explode(s []byte, n int) [][]byte {
if n <= 0 { if n <= 0 {
...@@ -47,6 +47,7 @@ func explode(s []byte, n int) [][]byte { ...@@ -47,6 +47,7 @@ func explode(s []byte, n int) [][]byte {
} }
// Count counts the number of non-overlapping instances of sep in s. // Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of Unicode code points in s.
func Count(s, sep []byte) int { func Count(s, sep []byte) int {
n := len(sep) n := len(sep)
if n == 0 { if n == 0 {
...@@ -137,6 +138,16 @@ func LastIndex(s, sep []byte) int { ...@@ -137,6 +138,16 @@ func LastIndex(s, sep []byte) int {
return -1 return -1
} }
// LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s.
func LastIndexByte(s []byte, c byte) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == c {
return i
}
}
return -1
}
// IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points. // IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points.
// It returns the byte index of the first occurrence in s of the given rune. // It returns the byte index of the first occurrence in s of the given rune.
// It returns -1 if rune is not present in s. // It returns -1 if rune is not present in s.
...@@ -442,7 +453,7 @@ func isSeparator(r rune) bool { ...@@ -442,7 +453,7 @@ func isSeparator(r rune) bool {
// Title returns a copy of s with all Unicode letters that begin words // Title returns a copy of s with all Unicode letters that begin words
// mapped to their title case. // mapped to their title case.
// //
// BUG: The rule Title uses for word boundaries does not handle Unicode punctuation properly. // BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
func Title(s []byte) []byte { func Title(s []byte) []byte {
// Use a closure here to remember state. // Use a closure here to remember state.
// Hackish but effective. Depends on Map scanning in order and calling // Hackish but effective. Depends on Map scanning in order and calling
......
...@@ -21,4 +21,4 @@ func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s ...@@ -21,4 +21,4 @@ func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
// Compare returns an integer comparing two byte slices lexicographically. // Compare returns an integer comparing two byte slices lexicographically.
// The result will be 0 if a==b, -1 if a < b, and +1 if a > b. // The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
// A nil argument is equivalent to an empty slice. // A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int // ../runtime/noasm_arm.goc or ../runtime/asm_{386,amd64}.s func Compare(a, b []byte) int // ../runtime/noasm.go or ../runtime/asm_{386,amd64}.s
...@@ -265,6 +265,23 @@ func TestIndexByte(t *testing.T) { ...@@ -265,6 +265,23 @@ func TestIndexByte(t *testing.T) {
} }
} }
func TestLastIndexByte(t *testing.T) {
testCases := []BinOpTest{
{"", "q", -1},
{"abcdef", "q", -1},
{"abcdefabcdef", "a", len("abcdef")}, // something in the middle
{"abcdefabcdef", "f", len("abcdefabcde")}, // last byte
{"zabcdefabcdef", "z", 0}, // first byte
{"a☺b☻c☹d", "b", len("a☺")}, // non-ascii
}
for _, test := range testCases {
actual := LastIndexByte([]byte(test.a), test.b[0])
if actual != test.i {
t.Errorf("LastIndexByte(%q,%c) = %v; want %v", test.a, test.b[0], actual, test.i)
}
}
}
// test a larger buffer with different sizes and alignments // test a larger buffer with different sizes and alignments
func TestIndexByteBig(t *testing.T) { func TestIndexByteBig(t *testing.T) {
var n = 1024 var n = 1024
......
...@@ -17,6 +17,8 @@ var compareTests = []struct { ...@@ -17,6 +17,8 @@ var compareTests = []struct {
{[]byte("a"), []byte(""), 1}, {[]byte("a"), []byte(""), 1},
{[]byte(""), []byte("a"), -1}, {[]byte(""), []byte("a"), -1},
{[]byte("abc"), []byte("abc"), 0}, {[]byte("abc"), []byte("abc"), 0},
{[]byte("abd"), []byte("abc"), 1},
{[]byte("abc"), []byte("abd"), -1},
{[]byte("ab"), []byte("abc"), -1}, {[]byte("ab"), []byte("abc"), -1},
{[]byte("abc"), []byte("ab"), 1}, {[]byte("abc"), []byte("ab"), 1},
{[]byte("x"), []byte("ab"), 1}, {[]byte("x"), []byte("ab"), 1},
...@@ -27,6 +29,7 @@ var compareTests = []struct { ...@@ -27,6 +29,7 @@ var compareTests = []struct {
{[]byte("abcdefgh"), []byte("abcdefgh"), 0}, {[]byte("abcdefgh"), []byte("abcdefgh"), 0},
{[]byte("abcdefghi"), []byte("abcdefghi"), 0}, {[]byte("abcdefghi"), []byte("abcdefghi"), 0},
{[]byte("abcdefghi"), []byte("abcdefghj"), -1}, {[]byte("abcdefghi"), []byte("abcdefghj"), -1},
{[]byte("abcdefghj"), []byte("abcdefghi"), 1},
// nil tests // nil tests
{nil, nil, 0}, {nil, nil, 0},
{[]byte(""), nil, 0}, {[]byte(""), nil, 0},
......
...@@ -7,7 +7,3 @@ package bytes ...@@ -7,7 +7,3 @@ package bytes
// Export func for testing // Export func for testing
var IndexBytePortable = indexBytePortable var IndexBytePortable = indexBytePortable
var EqualPortable = equalPortable var EqualPortable = equalPortable
func (b *Buffer) Cap() int {
return cap(b.buf)
}
...@@ -29,6 +29,12 @@ func (r *Reader) Len() int { ...@@ -29,6 +29,12 @@ func (r *Reader) Len() int {
return int(int64(len(r.s)) - r.i) return int(int64(len(r.s)) - r.i)
} }
// Size returns the original length of the underlying byte slice.
// Size is the number of bytes available for reading via ReadAt.
// The returned value is always the same and is not affected by calls
// to any other method.
func (r *Reader) Size() int64 { return int64(len(r.s)) }
func (r *Reader) Read(b []byte) (n int, err error) { func (r *Reader) Read(b []byte) (n int, err error) {
if len(b) == 0 { if len(b) == 0 {
return 0, nil return 0, nil
......
...@@ -244,3 +244,15 @@ func TestReaderCopyNothing(t *testing.T) { ...@@ -244,3 +244,15 @@ func TestReaderCopyNothing(t *testing.T) {
t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut) t.Errorf("behavior differs: with = %#v; without: %#v", with, withOut)
} }
} }
// tests that Len is affected by reads, but Size is not.
func TestReaderLenSize(t *testing.T) {
r := NewReader([]byte("abc"))
io.CopyN(ioutil.Discard, r, 1)
if r.Len() != 2 {
t.Errorf("Len = %d; want 2", r.Len())
}
if r.Size() != 3 {
t.Errorf("Size = %d; want 3", r.Size())
}
}
...@@ -235,9 +235,17 @@ func (f *File) saveExport(x interface{}, context string) { ...@@ -235,9 +235,17 @@ func (f *File) saveExport(x interface{}, context string) {
error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name) error_(c.Pos(), "export comment has wrong name %q, want %q", name, n.Name.Name)
} }
doc := ""
for _, c1 := range n.Doc.List {
if c1 != c {
doc += c1.Text + "\n"
}
}
f.ExpFunc = append(f.ExpFunc, &ExpFunc{ f.ExpFunc = append(f.ExpFunc, &ExpFunc{
Func: n, Func: n,
ExpName: name, ExpName: name,
Doc: doc,
}) })
break break
} }
......
...@@ -154,20 +154,6 @@ func splitQuoted(s string) (r []string, err error) { ...@@ -154,20 +154,6 @@ func splitQuoted(s string) (r []string, err error) {
return args, err return args, err
} }
var safeBytes = []byte(`+-.,/0123456789:=ABCDEFGHIJKLMNOPQRSTUVWXYZ\_abcdefghijklmnopqrstuvwxyz`)
func safeName(s string) bool {
if s == "" {
return false
}
for i := 0; i < len(s); i++ {
if c := s[i]; c < 0x80 && bytes.IndexByte(safeBytes, c) < 0 {
return false
}
}
return true
}
// Translate rewrites f.AST, the original Go input, to remove // Translate rewrites f.AST, the original Go input, to remove
// references to the imported package C, replacing them with // references to the imported package C, replacing them with
// references to the equivalent Go types, functions, and variables. // references to the equivalent Go types, functions, and variables.
...@@ -213,6 +199,10 @@ func (p *Package) loadDefines(f *File) { ...@@ -213,6 +199,10 @@ func (p *Package) loadDefines(f *File) {
val = strings.TrimSpace(line[tabIndex:]) val = strings.TrimSpace(line[tabIndex:])
} }
if key == "__clang__" {
p.GccIsClang = true
}
if n := f.Name[key]; n != nil { if n := f.Name[key]; n != nil {
if *debugDefine { if *debugDefine {
fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val) fmt.Fprintf(os.Stderr, "#define %s %s\n", key, val)
...@@ -582,7 +572,7 @@ func (p *Package) mangleName(n *Name) { ...@@ -582,7 +572,7 @@ func (p *Package) mangleName(n *Name) {
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the // rewriteRef rewrites all the C.xxx references in f.AST to refer to the
// Go equivalents, now that we have figured out the meaning of all // Go equivalents, now that we have figured out the meaning of all
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names // the xxx. In *godefs mode, rewriteRef replaces the names
// with full definitions instead of mangled names. // with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) { func (p *Package) rewriteRef(f *File) {
// Keep a list of all the functions, to remove the ones // Keep a list of all the functions, to remove the ones
...@@ -673,6 +663,13 @@ func (p *Package) rewriteRef(f *File) { ...@@ -673,6 +663,13 @@ func (p *Package) rewriteRef(f *File) {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr} expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
} }
case "selector":
if r.Name.Kind == "var" {
expr = &ast.StarExpr{Star: (*r.Expr).Pos(), X: expr}
} else {
error_(r.Pos(), "only C variables allowed in selector expression", fixGo(r.Name.Go))
}
case "type": case "type":
if r.Name.Kind != "type" { if r.Name.Kind != "type" {
error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go)) error_(r.Pos(), "expression C.%s used as type", fixGo(r.Name.Go))
...@@ -688,7 +685,7 @@ func (p *Package) rewriteRef(f *File) { ...@@ -688,7 +685,7 @@ func (p *Package) rewriteRef(f *File) {
error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go)) error_(r.Pos(), "must call C.%s", fixGo(r.Name.Go))
} }
} }
if *godefs || *cdefs { if *godefs {
// Substitute definition for mangled type name. // Substitute definition for mangled type name.
if id, ok := expr.(*ast.Ident); ok { if id, ok := expr.(*ast.Ident); ok {
if t := typedef[id.Name]; t != nil { if t := typedef[id.Name]; t != nil {
...@@ -746,6 +743,10 @@ func (p *Package) gccMachine() []string { ...@@ -746,6 +743,10 @@ func (p *Package) gccMachine() []string {
return []string{"-m32"} return []string{"-m32"}
case "arm": case "arm":
return []string{"-marm"} // not thumb return []string{"-marm"} // not thumb
case "s390":
return []string{"-m31"}
case "s390x":
return []string{"-m64"}
} }
return nil return nil
} }
...@@ -765,7 +766,7 @@ func (p *Package) gccCmd() []string { ...@@ -765,7 +766,7 @@ func (p *Package) gccCmd() []string {
"-c", // do not link "-c", // do not link
"-xc", // input language is C "-xc", // input language is C
) )
if strings.Contains(c[0], "clang") { if p.GccIsClang {
c = append(c, c = append(c,
"-ferror-limit=0", "-ferror-limit=0",
// Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn) // Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
...@@ -780,7 +781,7 @@ func (p *Package) gccCmd() []string { ...@@ -780,7 +781,7 @@ func (p *Package) gccCmd() []string {
// incorrectly typed unsigned long. We work around that // incorrectly typed unsigned long. We work around that
// by disabling the builtin functions (this is safe as // by disabling the builtin functions (this is safe as
// it won't affect the actual compilation of the C code). // it won't affect the actual compilation of the C code).
// See: http://golang.org/issue/6506. // See: https://golang.org/issue/6506.
"-fno-builtin", "-fno-builtin",
) )
} }
...@@ -992,8 +993,8 @@ func (c *typeConv) Init(ptrSize, intSize int64) { ...@@ -992,8 +993,8 @@ func (c *typeConv) Init(ptrSize, intSize int64) {
c.goVoid = c.Ident("_Ctype_void") c.goVoid = c.Ident("_Ctype_void")
// Normally cgo translates void* to unsafe.Pointer, // Normally cgo translates void* to unsafe.Pointer,
// but for historical reasons -cdefs and -godefs use *byte instead. // but for historical reasons -godefs uses *byte instead.
if *cdefs || *godefs { if *godefs {
c.goVoidPtr = &ast.StarExpr{X: c.byte} c.goVoidPtr = &ast.StarExpr{X: c.byte}
} else { } else {
c.goVoidPtr = c.Ident("unsafe.Pointer") c.goVoidPtr = c.Ident("unsafe.Pointer")
...@@ -1045,7 +1046,7 @@ func (tr *TypeRepr) String() string { ...@@ -1045,7 +1046,7 @@ func (tr *TypeRepr) String() string {
return fmt.Sprintf(tr.Repr, tr.FormatArgs...) return fmt.Sprintf(tr.Repr, tr.FormatArgs...)
} }
// Empty returns true if the result of String would be "". // Empty reports whether the result of String would be "".
func (tr *TypeRepr) Empty() bool { func (tr *TypeRepr) Empty() bool {
return len(tr.Repr) == 0 return len(tr.Repr) == 0
} }
...@@ -1334,8 +1335,8 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -1334,8 +1335,8 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
// If sub.Go.Name is "_Ctype_struct_foo" or "_Ctype_union_foo" or "_Ctype_class_foo", // If sub.Go.Name is "_Ctype_struct_foo" or "_Ctype_union_foo" or "_Ctype_class_foo",
// use that as the Go form for this typedef too, so that the typedef will be interchangeable // use that as the Go form for this typedef too, so that the typedef will be interchangeable
// with the base type. // with the base type.
// In -godefs and -cdefs mode, do this for all typedefs. // In -godefs mode, do this for all typedefs.
if isStructUnionClass(sub.Go) || *godefs || *cdefs { if isStructUnionClass(sub.Go) || *godefs {
t.Go = sub.Go t.Go = sub.Go
if isStructUnionClass(sub.Go) { if isStructUnionClass(sub.Go) {
...@@ -1397,7 +1398,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { ...@@ -1397,7 +1398,7 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
name := c.Ident("_Ctype_" + s) name := c.Ident("_Ctype_" + s)
tt := *t tt := *t
typedef[name.Name] = &tt typedef[name.Name] = &tt
if !*godefs && !*cdefs { if !*godefs {
t.Go = name t.Go = name
} }
} }
...@@ -1543,14 +1544,16 @@ func (c *typeConv) intExpr(n int64) ast.Expr { ...@@ -1543,14 +1544,16 @@ func (c *typeConv) intExpr(n int64) ast.Expr {
} }
// Add padding of given size to fld. // Add padding of given size to fld.
func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { func (c *typeConv) pad(fld []*ast.Field, sizes []int64, size int64) ([]*ast.Field, []int64) {
n := len(fld) n := len(fld)
fld = fld[0 : n+1] fld = fld[0 : n+1]
fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)} fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident("_")}, Type: c.Opaque(size)}
return fld sizes = sizes[0 : n+1]
sizes[n] = size
return fld, sizes
} }
// Struct conversion: return Go and (6g) C syntax for type. // Struct conversion: return Go and (gc) C syntax for type.
func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) {
// Minimum alignment for a struct is 1 byte. // Minimum alignment for a struct is 1 byte.
align = 1 align = 1
...@@ -1558,6 +1561,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1558,6 +1561,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("struct {") buf.WriteString("struct {")
fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field
sizes := make([]int64, 0, 2*len(dt.Field)+1)
off := int64(0) off := int64(0)
// Rename struct fields that happen to be named Go keywords into // Rename struct fields that happen to be named Go keywords into
...@@ -1573,7 +1577,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1573,7 +1577,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
used[f.Name] = true used[f.Name] = true
} }
if !*godefs && !*cdefs { if !*godefs {
for cid, goid := range ident { for cid, goid := range ident {
if token.Lookup(goid).IsKeyword() { if token.Lookup(goid).IsKeyword() {
// Avoid keyword // Avoid keyword
...@@ -1593,19 +1597,19 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1593,19 +1597,19 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
anon := 0 anon := 0
for _, f := range dt.Field { for _, f := range dt.Field {
if f.ByteOffset > off { if f.ByteOffset > off {
fld = c.pad(fld, f.ByteOffset-off) fld, sizes = c.pad(fld, sizes, f.ByteOffset-off)
off = f.ByteOffset off = f.ByteOffset
} }
name := f.Name name := f.Name
ft := f.Type ft := f.Type
// In godefs or cdefs mode, if this field is a C11 // In godefs mode, if this field is a C11
// anonymous union then treat the first field in the // anonymous union then treat the first field in the
// union as the field in the struct. This handles // union as the field in the struct. This handles
// cases like the glibc <sys/resource.h> file; see // cases like the glibc <sys/resource.h> file; see
// issue 6677. // issue 6677.
if *godefs || *cdefs { if *godefs {
if st, ok := f.Type.(*dwarf.StructType); ok && name == "" && st.Kind == "union" && len(st.Field) > 0 && !used[st.Field[0].Name] { if st, ok := f.Type.(*dwarf.StructType); ok && name == "" && st.Kind == "union" && len(st.Field) > 0 && !used[st.Field[0].Name] {
name = st.Field[0].Name name = st.Field[0].Name
ident[name] = name ident[name] = name
...@@ -1635,14 +1639,12 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1635,14 +1639,12 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
talign = size talign = size
} }
if talign > 0 && f.ByteOffset%talign != 0 && !*cdefs { if talign > 0 && f.ByteOffset%talign != 0 {
// Drop misaligned fields, the same way we drop integer bit fields. // Drop misaligned fields, the same way we drop integer bit fields.
// The goal is to make available what can be made available. // The goal is to make available what can be made available.
// Otherwise one bad and unneeded field in an otherwise okay struct // Otherwise one bad and unneeded field in an otherwise okay struct
// makes the whole program not compile. Much of the time these // makes the whole program not compile. Much of the time these
// structs are in system headers that cannot be corrected. // structs are in system headers that cannot be corrected.
// Exception: In -cdefs mode, we use #pragma pack, so misaligned
// fields should still work.
continue continue
} }
n := len(fld) n := len(fld)
...@@ -1653,6 +1655,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1653,6 +1655,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
ident[name] = name ident[name] = name
} }
fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo} fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo}
sizes = sizes[0 : n+1]
sizes[n] = size
off += size off += size
buf.WriteString(t.C.String()) buf.WriteString(t.C.String())
buf.WriteString(" ") buf.WriteString(" ")
...@@ -1663,16 +1667,29 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct ...@@ -1663,16 +1667,29 @@ func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.Struct
} }
} }
if off < dt.ByteSize { if off < dt.ByteSize {
fld = c.pad(fld, dt.ByteSize-off) fld, sizes = c.pad(fld, sizes, dt.ByteSize-off)
off = dt.ByteSize off = dt.ByteSize
} }
// If the last field in a non-zero-sized struct is zero-sized
// the compiler is going to pad it by one (see issue 9401).
// We can't permit that, because then the size of the Go
// struct will not be the same as the size of the C struct.
// Our only option in such a case is to remove the field,
// which means that it can not be referenced from Go.
for off > 0 && sizes[len(sizes)-1] == 0 {
n := len(sizes)
fld = fld[0 : n-1]
sizes = sizes[0 : n-1]
}
if off != dt.ByteSize { if off != dt.ByteSize {
fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize) fatalf("%s: struct size calculation error off=%d bytesize=%d", lineno(pos), off, dt.ByteSize)
} }
buf.WriteString("}") buf.WriteString("}")
csyntax = buf.String() csyntax = buf.String()
if *godefs || *cdefs { if *godefs {
godefsFields(fld) godefsFields(fld)
} }
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}} expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
...@@ -1707,11 +1724,9 @@ func godefsFields(fld []*ast.Field) { ...@@ -1707,11 +1724,9 @@ func godefsFields(fld []*ast.Field) {
n.Name = "Pad_cgo_" + strconv.Itoa(npad) n.Name = "Pad_cgo_" + strconv.Itoa(npad)
npad++ npad++
} }
if !*cdefs {
n.Name = upper(n.Name) n.Name = upper(n.Name)
} }
} }
}
} }
// fieldPrefix returns the prefix that should be removed from all the // fieldPrefix returns the prefix that should be removed from all the
...@@ -1721,9 +1736,6 @@ func godefsFields(fld []*ast.Field) { ...@@ -1721,9 +1736,6 @@ func godefsFields(fld []*ast.Field) {
// package syscall's data structures, we drop a common prefix // package syscall's data structures, we drop a common prefix
// (so sec, usec, which will get turned into Sec, Usec for exporting). // (so sec, usec, which will get turned into Sec, Usec for exporting).
func fieldPrefix(fld []*ast.Field) string { func fieldPrefix(fld []*ast.Field) string {
if *cdefs {
return ""
}
prefix := "" prefix := ""
for _, f := range fld { for _, f := range fld {
for _, n := range f.Names { for _, n := range f.Names {
......
...@@ -114,173 +114,6 @@ func (p *Package) godefs(f *File, srcfile string) string { ...@@ -114,173 +114,6 @@ func (p *Package) godefs(f *File, srcfile string) string {
return buf.String() return buf.String()
} }
// cdefs returns the output for -cdefs mode.
// The easiest way to do this is to translate the godefs Go to C.
func (p *Package) cdefs(f *File, srcfile string) string {
godefsOutput := p.godefs(f, srcfile)
lines := strings.Split(godefsOutput, "\n")
lines[0] = "// Created by cgo -cdefs - DO NOT EDIT"
for i, line := range lines {
lines[i] = strings.TrimSpace(line)
}
var out bytes.Buffer
printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) }
didTypedef := false
for i := 0; i < len(lines); i++ {
line := lines[i]
// Delete
// package x
if strings.HasPrefix(line, "package ") {
continue
}
// Convert
// const (
// A = 1
// B = 2
// )
//
// to
//
// enum {
// A = 1,
// B = 2,
// };
if line == "const (" {
printf("enum {\n")
for i++; i < len(lines) && lines[i] != ")"; i++ {
line = lines[i]
if line != "" {
printf("\t%s,", line)
}
printf("\n")
}
printf("};\n")
continue
}
// Convert
// const A = 1
// to
// enum { A = 1 };
if strings.HasPrefix(line, "const ") {
printf("enum { %s };\n", line[len("const "):])
continue
}
// On first type definition, typedef all the structs
// in case there are dependencies between them.
if !didTypedef && strings.HasPrefix(line, "type ") {
didTypedef = true
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {")
printf("typedef struct %s %s;\n", s, s)
}
}
printf("\n")
printf("#pragma pack on\n")
printf("\n")
}
// Convert
// type T struct {
// X int64
// Y *int32
// Z [4]byte
// }
//
// to
//
// struct T {
// int64 X;
// int32 *Y;
// byte Z[4];
// }
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
if len(lines) > i+1 && lines[i+1] == "}" {
// do not output empty struct
i++
continue
}
s := line[len("type ") : len(line)-len(" struct {")]
printf("struct %s {\n", s)
for i++; i < len(lines) && lines[i] != "}"; i++ {
line := lines[i]
if line != "" {
f := strings.Fields(line)
if len(f) != 2 {
fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line)
nerrors++
continue
}
printf("\t%s;", cdecl(f[0], f[1]))
}
printf("\n")
}
printf("};\n")
continue
}
// Convert
// type T int
// to
// typedef int T;
if strings.HasPrefix(line, "type ") {
f := strings.Fields(line[len("type "):])
if len(f) != 2 {
fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line)
nerrors++
continue
}
printf("typedef\t%s;\n", cdecl(f[0], f[1]))
continue
}
printf("%s\n", line)
}
if didTypedef {
printf("\n")
printf("#pragma pack off\n")
}
return out.String()
}
// cdecl returns the C declaration for the given Go name and type.
// It only handles the specific cases necessary for converting godefs output.
func cdecl(name, typ string) string {
// X *[0]byte -> X *void
if strings.HasPrefix(typ, "*[0]") {
typ = "*void"
}
// X [4]byte -> X[4] byte
for strings.HasPrefix(typ, "[") {
i := strings.Index(typ, "]") + 1
name = name + typ[:i]
typ = typ[i:]
}
// X *byte -> *X byte
for strings.HasPrefix(typ, "*") {
name = "*" + name
typ = typ[1:]
}
// X T -> T X
// Handle the special case: 'unsafe.Pointer' is 'void *'
if typ == "unsafe.Pointer" {
typ = "void"
name = "*" + name
}
return typ + "\t" + name
}
var gofmtBuf bytes.Buffer var gofmtBuf bytes.Buffer
// gofmt returns the gofmt-formatted string for an AST node. // gofmt returns the gofmt-formatted string for an AST node.
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
// TODO(rsc): // TODO(rsc):
// Emit correct line number annotations. // Emit correct line number annotations.
// Make 6g understand the annotations. // Make gc understand the annotations.
package main package main
...@@ -33,6 +33,7 @@ type Package struct { ...@@ -33,6 +33,7 @@ type Package struct {
PtrSize int64 PtrSize int64
IntSize int64 IntSize int64
GccOptions []string GccOptions []string
GccIsClang bool
CgoFlags map[string][]string // #cgo flags (CFLAGS, LDFLAGS) CgoFlags map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
Written map[string]bool Written map[string]bool
Name map[string]*Name // accumulated Name from Files Name map[string]*Name // accumulated Name from Files
...@@ -87,7 +88,7 @@ type Name struct { ...@@ -87,7 +88,7 @@ type Name struct {
Const string // constant definition Const string // constant definition
} }
// IsVar returns true if Kind is either "var" or "fpvar" // IsVar reports whether Kind is either "var" or "fpvar"
func (n *Name) IsVar() bool { func (n *Name) IsVar() bool {
return n.Kind == "var" || n.Kind == "fpvar" return n.Kind == "var" || n.Kind == "fpvar"
} }
...@@ -98,6 +99,7 @@ func (n *Name) IsVar() bool { ...@@ -98,6 +99,7 @@ func (n *Name) IsVar() bool {
type ExpFunc struct { type ExpFunc struct {
Func *ast.FuncDecl Func *ast.FuncDecl
ExpName string // name to use from C ExpName string // name to use from C
Doc string
} }
// A TypeRepr contains the string representation of a type. // A TypeRepr contains the string representation of a type.
...@@ -174,15 +176,18 @@ var cPrefix string ...@@ -174,15 +176,18 @@ var cPrefix string
var fset = token.NewFileSet() var fset = token.NewFileSet()
var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file") var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
var dynout = flag.String("dynout", "", "write -dynobj output to this file") var dynout = flag.String("dynout", "", "write -dynimport output to this file")
var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in dynimport mode") var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")
// These flags are for bootstrapping a new Go implementation, // This flag is for bootstrapping a new Go implementation,
// to generate Go and C headers that match the data layout and // to generate Go types that match the data layout and
// constant values used in the host's C libraries and system calls. // constant values used in the host's C libraries and system calls.
var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output") var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output")
var objDir = flag.String("objdir", "", "object directory") var objDir = flag.String("objdir", "", "object directory")
var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo") var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo") var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
...@@ -208,12 +213,7 @@ func main() { ...@@ -208,12 +213,7 @@ func main() {
return return
} }
if *godefs && *cdefs { if *godefs {
fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n")
os.Exit(2)
}
if *godefs || *cdefs {
// Generating definitions pulled from header files, // Generating definitions pulled from header files,
// to be checked into Go repositories. // to be checked into Go repositories.
// Line numbers are just noise. // Line numbers are just noise.
...@@ -305,14 +305,12 @@ func main() { ...@@ -305,14 +305,12 @@ func main() {
p.Record(f) p.Record(f)
if *godefs { if *godefs {
os.Stdout.WriteString(p.godefs(f, input)) os.Stdout.WriteString(p.godefs(f, input))
} else if *cdefs {
os.Stdout.WriteString(p.cdefs(f, input))
} else { } else {
p.writeOutput(f, input) p.writeOutput(f, input)
} }
} }
if !*godefs && !*cdefs { if !*godefs {
p.writeDefs() p.writeDefs()
} }
if nerrors > 0 { if nerrors > 0 {
......
...@@ -55,7 +55,7 @@ func error_(pos token.Pos, msg string, args ...interface{}) { ...@@ -55,7 +55,7 @@ func error_(pos token.Pos, msg string, args ...interface{}) {
fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "\n")
} }
// isName returns true if s is a valid C identifier // isName reports whether s is a valid C identifier
func isName(s string) bool { func isName(s string) bool {
for i, v := range s { for i, v := range s {
if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') { if v != '_' && (v < 'A' || v > 'Z') && (v < 'a' || v > 'z') && (v < '0' || v > '9') {
......
...@@ -17,11 +17,19 @@ import ( ...@@ -17,11 +17,19 @@ import (
var errHTTP = errors.New("no http in bootstrap go command") var errHTTP = errors.New("no http in bootstrap go command")
type httpError struct {
statusCode int
}
func (e *httpError) Error() string {
panic("unreachable")
}
func httpGET(url string) ([]byte, error) { func httpGET(url string) ([]byte, error) {
return nil, errHTTP return nil, errHTTP
} }
func httpsOrHTTP(importPath string) (string, io.ReadCloser, error) { func httpsOrHTTP(importPath string, security securityMode) (string, io.ReadCloser, error) {
return "", nil, errHTTP return "", nil, errHTTP
} }
......
...@@ -36,7 +36,6 @@ func mkEnv() []envVar { ...@@ -36,7 +36,6 @@ func mkEnv() []envVar {
env := []envVar{ env := []envVar{
{"GOARCH", goarch}, {"GOARCH", goarch},
{"GOBIN", gobin}, {"GOBIN", gobin},
{"GOCHAR", archChar},
{"GOEXE", exeSuffix}, {"GOEXE", exeSuffix},
{"GOHOSTARCH", runtime.GOARCH}, {"GOHOSTARCH", runtime.GOARCH},
{"GOHOSTOS", runtime.GOOS}, {"GOHOSTOS", runtime.GOOS},
...@@ -45,6 +44,7 @@ func mkEnv() []envVar { ...@@ -45,6 +44,7 @@ func mkEnv() []envVar {
{"GORACE", os.Getenv("GORACE")}, {"GORACE", os.Getenv("GORACE")},
{"GOROOT", goroot}, {"GOROOT", goroot},
{"GOTOOLDIR", toolDir}, {"GOTOOLDIR", toolDir},
{"GO15VENDOREXPERIMENT", os.Getenv("GO15VENDOREXPERIMENT")},
// disable escape codes in clang errors // disable escape codes in clang errors
{"TERM", "dumb"}, {"TERM", "dumb"},
......
...@@ -11,7 +11,7 @@ var cmdFix = &Command{ ...@@ -11,7 +11,7 @@ var cmdFix = &Command{
Long: ` Long: `
Fix runs the Go fix command on the packages named by the import paths. Fix runs the Go fix command on the packages named by the import paths.
For more about fix, see 'godoc fix'. For more about fix, see 'go doc cmd/fix'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
To run fix with specific options, run 'go tool fix'. To run fix with specific options, run 'go tool fix'.
...@@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) { ...@@ -25,6 +25,6 @@ func runFix(cmd *Command, args []string) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
run(stringList(tool("fix"), relPaths(pkg.allgofiles))) run(stringList(buildToolExec, tool("fix"), relPaths(pkg.allgofiles)))
} }
} }
...@@ -4,6 +4,11 @@ ...@@ -4,6 +4,11 @@
package main package main
import (
"os"
"path/filepath"
)
func init() { func init() {
addBuildFlagsNX(cmdFmt) addBuildFlagsNX(cmdFmt)
} }
...@@ -16,7 +21,7 @@ var cmdFmt = &Command{ ...@@ -16,7 +21,7 @@ var cmdFmt = &Command{
Fmt runs the command 'gofmt -l -w' on the packages named Fmt runs the command 'gofmt -l -w' on the packages named
by the import paths. It prints the names of the files that are modified. by the import paths. It prints the names of the files that are modified.
For more about gofmt, see 'godoc gofmt'. For more about gofmt, see 'go doc cmd/gofmt'.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
The -n flag prints commands that would be executed. The -n flag prints commands that would be executed.
...@@ -29,10 +34,31 @@ See also: go fix, go vet. ...@@ -29,10 +34,31 @@ See also: go fix, go vet.
} }
func runFmt(cmd *Command, args []string) { func runFmt(cmd *Command, args []string) {
gofmt := gofmtPath()
for _, pkg := range packages(args) { for _, pkg := range packages(args) {
// Use pkg.gofiles instead of pkg.Dir so that // Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package, // the command only applies to this package,
// not to packages in subdirectories. // not to packages in subdirectories.
run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles))) run(stringList(gofmt, "-l", "-w", relPaths(pkg.allgofiles)))
} }
} }
func gofmtPath() string {
gofmt := "gofmt"
if toolIsWindows {
gofmt += toolWindowsExtension
}
gofmtPath := filepath.Join(gobin, gofmt)
if _, err := os.Stat(gofmtPath); err == nil {
return gofmtPath
}
gofmtPath = filepath.Join(goroot, "bin", gofmt)
if _, err := os.Stat(gofmtPath); err == nil {
return gofmtPath
}
// fallback to looking for gofmt in $PATH
return "gofmt"
}
...@@ -13,11 +13,11 @@ import ( ...@@ -13,11 +13,11 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
) )
var cmdGenerate = &Command{ var cmdGenerate = &Command{
...@@ -62,8 +62,12 @@ Go generate sets several variables when it runs the generator: ...@@ -62,8 +62,12 @@ Go generate sets several variables when it runs the generator:
The execution operating system (linux, windows, etc.) The execution operating system (linux, windows, etc.)
$GOFILE $GOFILE
The base name of the file. The base name of the file.
$GOLINE
The line number of the directive in the source file.
$GOPACKAGE $GOPACKAGE
The name of the package of the file containing the directive. The name of the package of the file containing the directive.
$DOLLAR
A dollar sign.
Other than variable substitution and quoted-string evaluation, no Other than variable substitution and quoted-string evaluation, no
special processing such as "globbing" is performed on the command special processing such as "globbing" is performed on the command
...@@ -106,9 +110,10 @@ The generator is run in the package's source directory. ...@@ -106,9 +110,10 @@ The generator is run in the package's source directory.
Go generate accepts one specific flag: Go generate accepts one specific flag:
-run="" -run=""
TODO: This flag is unimplemented. if non-empty, specifies a regular expression to select
if non-empty, specifies a regular expression to directives whose full original source text (excluding
select directives whose command matches the expression. any trailing spaces and final newline) matches the
expression.
It also accepts the standard build flags -v, -n, and -x. It also accepts the standard build flags -v, -n, and -x.
The -v flag prints the names of packages and files as they are The -v flag prints the names of packages and files as they are
...@@ -120,7 +125,10 @@ For more about specifying packages, see 'go help packages'. ...@@ -120,7 +125,10 @@ For more about specifying packages, see 'go help packages'.
`, `,
} }
var generateRunFlag string // generate -run flag var (
generateRunFlag string // generate -run flag
generateRunRE *regexp.Regexp // compiled expression for -run
)
func init() { func init() {
addBuildFlags(cmdGenerate) addBuildFlags(cmdGenerate)
...@@ -128,6 +136,13 @@ func init() { ...@@ -128,6 +136,13 @@ func init() {
} }
func runGenerate(cmd *Command, args []string) { func runGenerate(cmd *Command, args []string) {
if generateRunFlag != "" {
var err error
generateRunRE, err = regexp.Compile(generateRunFlag)
if err != nil {
log.Fatalf("generate: %s", err)
}
}
// Even if the arguments are .go files, this loop suffices. // Even if the arguments are .go files, this loop suffices.
for _, pkg := range packages(args) { for _, pkg := range packages(args) {
for _, file := range pkg.gofiles { for _, file := range pkg.gofiles {
...@@ -163,7 +178,7 @@ type Generator struct { ...@@ -163,7 +178,7 @@ type Generator struct {
file string // base name of file. file string // base name of file.
pkg string pkg string
commands map[string][]string commands map[string][]string
lineNum int lineNum int // current line number.
} }
// run runs the generators in the current file. // run runs the generators in the current file.
...@@ -221,6 +236,11 @@ func (g *Generator) run() (ok bool) { ...@@ -221,6 +236,11 @@ func (g *Generator) run() (ok bool) {
if !isGoGenerate(buf) { if !isGoGenerate(buf) {
continue continue
} }
if generateRunFlag != "" {
if !generateRunRE.Match(bytes.TrimSpace(buf)) {
continue
}
}
words := g.split(string(buf)) words := g.split(string(buf))
if len(words) == 0 { if len(words) == 0 {
...@@ -306,7 +326,7 @@ Words: ...@@ -306,7 +326,7 @@ Words:
} }
// Substitute environment variables. // Substitute environment variables.
for i, word := range words { for i, word := range words {
words[i] = g.expandEnv(word) words[i] = os.Expand(word, g.expandVar)
} }
return words return words
} }
...@@ -322,38 +342,25 @@ func (g *Generator) errorf(format string, args ...interface{}) { ...@@ -322,38 +342,25 @@ func (g *Generator) errorf(format string, args ...interface{}) {
panic(stop) panic(stop)
} }
// expandEnv expands any $XXX invocations in word. // expandVar expands the $XXX invocation in word. It is called
func (g *Generator) expandEnv(word string) string { // by os.Expand.
if !strings.ContainsRune(word, '$') { func (g *Generator) expandVar(word string) string {
return word switch word {
}
var buf bytes.Buffer
var w int
var r rune
for i := 0; i < len(word); i += w {
r, w = utf8.DecodeRuneInString(word[i:])
if r != '$' {
buf.WriteRune(r)
continue
}
w += g.identLength(word[i+w:])
envVar := word[i+1 : i+w]
var sub string
switch envVar {
case "GOARCH": case "GOARCH":
sub = runtime.GOARCH return buildContext.GOARCH
case "GOOS": case "GOOS":
sub = runtime.GOOS return buildContext.GOOS
case "GOFILE": case "GOFILE":
sub = g.file return g.file
case "GOLINE":
return fmt.Sprint(g.lineNum)
case "GOPACKAGE": case "GOPACKAGE":
sub = g.pkg return g.pkg
case "DOLLAR":
return "$"
default: default:
sub = os.Getenv(envVar) return os.Getenv(word)
}
buf.WriteString(sub)
} }
return buf.String()
} }
// identLength returns the length of the identifier beginning the string. // identLength returns the length of the identifier beginning the string.
...@@ -395,7 +402,7 @@ func (g *Generator) exec(words []string) { ...@@ -395,7 +402,7 @@ func (g *Generator) exec(words []string) {
"GOFILE=" + g.file, "GOFILE=" + g.file,
"GOPACKAGE=" + g.pkg, "GOPACKAGE=" + g.pkg,
} }
cmd.Env = mergeEnvLists(env, os.Environ()) cmd.Env = mergeEnvLists(env, origEnv)
err := cmd.Run() err := cmd.Run()
if err != nil { if err != nil {
g.errorf("running %q: %s", words[0], err) g.errorf("running %q: %s", words[0], err)
......
...@@ -26,6 +26,7 @@ var splitTests = []splitTest{ ...@@ -26,6 +26,7 @@ var splitTests = []splitTest{
{"$GOPACKAGE", []string{"sys"}}, {"$GOPACKAGE", []string{"sys"}},
{"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}}, {"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}},
{"/$XXNOTDEFINED/", []string{"//"}}, {"/$XXNOTDEFINED/", []string{"//"}},
{"/$DOLLAR/", []string{"/$/"}},
{"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}}, {"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}},
} }
......
...@@ -16,7 +16,7 @@ import ( ...@@ -16,7 +16,7 @@ import (
) )
var cmdGet = &Command{ var cmdGet = &Command{
UsageLine: "get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]", UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [build flags] [packages]",
Short: "download and install packages and dependencies", Short: "download and install packages and dependencies",
Long: ` Long: `
Get downloads and installs the packages named by the import paths, Get downloads and installs the packages named by the import paths,
...@@ -33,6 +33,9 @@ of the original. ...@@ -33,6 +33,9 @@ of the original.
The -fix flag instructs get to run the fix tool on the downloaded packages The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code. before resolving dependencies or building the code.
The -insecure flag permits fetching from repositories and resolving
custom domains using insecure schemes such as HTTP. Use with caution.
The -t flag instructs get to also download the packages required to build The -t flag instructs get to also download the packages required to build
the tests for the specified packages. the tests for the specified packages.
...@@ -48,6 +51,10 @@ rule is that if the local installation is running version "go1", get ...@@ -48,6 +51,10 @@ rule is that if the local installation is running version "go1", get
searches for a branch or tag named "go1". If no such version exists it searches for a branch or tag named "go1". If no such version exists it
retrieves the most recent version of the package. retrieves the most recent version of the package.
If the vendoring experiment is enabled (see 'go help gopath'),
then when go get checks out or updates a Git repository,
it also updates any git submodules referenced by the repository.
For more about specifying packages, see 'go help packages'. For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to For more about how 'go get' finds source code to
...@@ -62,6 +69,7 @@ var getF = cmdGet.Flag.Bool("f", false, "") ...@@ -62,6 +69,7 @@ var getF = cmdGet.Flag.Bool("f", false, "")
var getT = cmdGet.Flag.Bool("t", false, "") var getT = cmdGet.Flag.Bool("t", false, "")
var getU = cmdGet.Flag.Bool("u", false, "") var getU = cmdGet.Flag.Bool("u", false, "")
var getFix = cmdGet.Flag.Bool("fix", false, "") var getFix = cmdGet.Flag.Bool("fix", false, "")
var getInsecure = cmdGet.Flag.Bool("insecure", false, "")
func init() { func init() {
addBuildFlags(cmdGet) addBuildFlags(cmdGet)
...@@ -73,10 +81,20 @@ func runGet(cmd *Command, args []string) { ...@@ -73,10 +81,20 @@ func runGet(cmd *Command, args []string) {
fatalf("go get: cannot use -f flag without -u") fatalf("go get: cannot use -f flag without -u")
} }
// Disable any prompting for passwords by Git.
// Only has an effect for 2.3.0 or later, but avoiding
// the prompt in earlier versions is just too hard.
// See golang.org/issue/9341.
os.Setenv("GIT_TERMINAL_PROMPT", "0")
// Phase 1. Download/update. // Phase 1. Download/update.
var stk importStack var stk importStack
mode := 0
if *getT {
mode |= getTestDeps
}
for _, arg := range downloadPaths(args) { for _, arg := range downloadPaths(args) {
download(arg, &stk, *getT) download(arg, nil, &stk, mode)
} }
exitIfErrors() exitIfErrors()
...@@ -92,12 +110,13 @@ func runGet(cmd *Command, args []string) { ...@@ -92,12 +110,13 @@ func runGet(cmd *Command, args []string) {
} }
args = importPaths(args) args = importPaths(args)
packagesForBuild(args)
// Phase 3. Install. // Phase 3. Install.
if *getD { if *getD {
// Download only. // Download only.
// Check delayed until now so that importPaths // Check delayed until now so that importPaths
// has a chance to print errors. // and packagesForBuild have a chance to print errors.
return return
} }
...@@ -148,13 +167,30 @@ var downloadRootCache = map[string]bool{} ...@@ -148,13 +167,30 @@ var downloadRootCache = map[string]bool{}
// download runs the download half of the get command // download runs the download half of the get command
// for the package named by the argument. // for the package named by the argument.
func download(arg string, stk *importStack, getTestDeps bool) { func download(arg string, parent *Package, stk *importStack, mode int) {
p := loadPackage(arg, stk) load := func(path string, mode int) *Package {
if parent == nil {
return loadPackage(path, stk)
}
return loadImport(path, parent.Dir, parent, stk, nil, mode)
}
p := load(arg, mode)
if p.Error != nil && p.Error.hard { if p.Error != nil && p.Error.hard {
errorf("%s", p.Error) errorf("%s", p.Error)
return return
} }
// loadPackage inferred the canonical ImportPath from arg.
// Use that in the following to prevent hysteresis effects
// in e.g. downloadCache and packageCache.
// This allows invocations such as:
// mkdir -p $GOPATH/src/github.com/user
// cd $GOPATH/src/github.com/user
// go get ./foo
// see: golang.org/issue/9767
arg = p.ImportPath
// There's nothing to do if this is a package in the standard library. // There's nothing to do if this is a package in the standard library.
if p.Standard { if p.Standard {
return return
...@@ -163,7 +199,7 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -163,7 +199,7 @@ func download(arg string, stk *importStack, getTestDeps bool) {
// Only process each package once. // Only process each package once.
// (Unless we're fetching test dependencies for this package, // (Unless we're fetching test dependencies for this package,
// in which case we want to process it again.) // in which case we want to process it again.)
if downloadCache[arg] && !getTestDeps { if downloadCache[arg] && mode&getTestDeps == 0 {
return return
} }
downloadCache[arg] = true downloadCache[arg] = true
...@@ -175,7 +211,7 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -175,7 +211,7 @@ func download(arg string, stk *importStack, getTestDeps bool) {
// Download if the package is missing, or update if we're using -u. // Download if the package is missing, or update if we're using -u.
if p.Dir == "" || *getU { if p.Dir == "" || *getU {
// The actual download. // The actual download.
stk.push(p.ImportPath) stk.push(arg)
err := downloadPackage(p) err := downloadPackage(p)
if err != nil { if err != nil {
errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()}) errorf("%s", &PackageError{ImportStack: stk.copy(), Err: err.Error()})
...@@ -183,6 +219,17 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -183,6 +219,17 @@ func download(arg string, stk *importStack, getTestDeps bool) {
return return
} }
// Warn that code.google.com is shutting down. We
// issue the warning here because this is where we
// have the import stack.
if strings.HasPrefix(p.ImportPath, "code.google.com") {
fmt.Fprintf(os.Stderr, "warning: code.google.com is shutting down; import path %v will stop working\n", p.ImportPath)
if len(*stk) > 1 {
fmt.Fprintf(os.Stderr, "warning: package %v\n", strings.Join(*stk, "\n\timports "))
}
}
stk.pop()
args := []string{arg} args := []string{arg}
// If the argument has a wildcard in it, re-evaluate the wildcard. // If the argument has a wildcard in it, re-evaluate the wildcard.
// We delay this until after reloadPackage so that the old entry // We delay this until after reloadPackage so that the old entry
...@@ -208,9 +255,10 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -208,9 +255,10 @@ func download(arg string, stk *importStack, getTestDeps bool) {
pkgs = pkgs[:0] pkgs = pkgs[:0]
for _, arg := range args { for _, arg := range args {
stk.push(arg) // Note: load calls loadPackage or loadImport,
p := loadPackage(arg, stk) // which push arg onto stk already.
stk.pop() // Do not push here too, or else stk will say arg imports arg.
p := load(arg, mode)
if p.Error != nil { if p.Error != nil {
errorf("%s", p.Error) errorf("%s", p.Error)
continue continue
...@@ -223,7 +271,7 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -223,7 +271,7 @@ func download(arg string, stk *importStack, getTestDeps bool) {
// due to wildcard expansion. // due to wildcard expansion.
for _, p := range pkgs { for _, p := range pkgs {
if *getFix { if *getFix {
run(stringList(tool("fix"), relPaths(p.allgofiles))) run(buildToolExec, stringList(tool("fix"), relPaths(p.allgofiles)))
// The imports might have changed, so reload again. // The imports might have changed, so reload again.
p = reloadPackage(arg, stk) p = reloadPackage(arg, stk)
...@@ -240,18 +288,31 @@ func download(arg string, stk *importStack, getTestDeps bool) { ...@@ -240,18 +288,31 @@ func download(arg string, stk *importStack, getTestDeps bool) {
} }
// Process dependencies, now that we know what they are. // Process dependencies, now that we know what they are.
for _, dep := range p.deps { for _, path := range p.Imports {
if path == "C" {
continue
}
// Don't get test dependencies recursively. // Don't get test dependencies recursively.
download(dep.ImportPath, stk, false) // Imports is already vendor-expanded.
download(path, p, stk, 0)
} }
if getTestDeps { if mode&getTestDeps != 0 {
// Process test dependencies when -t is specified. // Process test dependencies when -t is specified.
// (Don't get test dependencies for test dependencies.) // (Don't get test dependencies for test dependencies.)
// We pass useVendor here because p.load does not
// vendor-expand TestImports and XTestImports.
// The call to loadImport inside download needs to do that.
for _, path := range p.TestImports { for _, path := range p.TestImports {
download(path, stk, false) if path == "C" {
continue
}
download(path, p, stk, useVendor)
} }
for _, path := range p.XTestImports { for _, path := range p.XTestImports {
download(path, stk, false) if path == "C" {
continue
}
download(path, p, stk, useVendor)
} }
} }
...@@ -269,6 +330,12 @@ func downloadPackage(p *Package) error { ...@@ -269,6 +330,12 @@ func downloadPackage(p *Package) error {
repo, rootPath string repo, rootPath string
err error err error
) )
security := secure
if *getInsecure {
security = insecure
}
if p.build.SrcRoot != "" { if p.build.SrcRoot != "" {
// Directory exists. Look for checkout along path to src. // Directory exists. Look for checkout along path to src.
vcs, rootPath, err = vcsForDir(p) vcs, rootPath, err = vcsForDir(p)
...@@ -278,10 +345,15 @@ func downloadPackage(p *Package) error { ...@@ -278,10 +345,15 @@ func downloadPackage(p *Package) error {
repo = "<local>" // should be unused; make distinctive repo = "<local>" // should be unused; make distinctive
// Double-check where it came from. // Double-check where it came from.
if *getU && vcs.remoteRepo != nil && !*getF { if *getU && vcs.remoteRepo != nil {
dir := filepath.Join(p.build.SrcRoot, rootPath) dir := filepath.Join(p.build.SrcRoot, rootPath)
if remote, err := vcs.remoteRepo(vcs, dir); err == nil { remote, err := vcs.remoteRepo(vcs, dir)
if rr, err := repoRootForImportPath(p.ImportPath); err == nil { if err != nil {
return err
}
repo = remote
if !*getF {
if rr, err := repoRootForImportPath(p.ImportPath, security); err == nil {
repo := rr.repo repo := rr.repo
if rr.vcs.resolveRepo != nil { if rr.vcs.resolveRepo != nil {
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo) resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
...@@ -289,7 +361,7 @@ func downloadPackage(p *Package) error { ...@@ -289,7 +361,7 @@ func downloadPackage(p *Package) error {
repo = resolved repo = resolved
} }
} }
if remote != repo { if remote != repo && p.ImportComment != "" {
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote) return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
} }
} }
...@@ -298,12 +370,15 @@ func downloadPackage(p *Package) error { ...@@ -298,12 +370,15 @@ func downloadPackage(p *Package) error {
} else { } else {
// Analyze the import path to determine the version control system, // Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository. // repository, and the import path for the root of the repository.
rr, err := repoRootForImportPath(p.ImportPath) rr, err := repoRootForImportPath(p.ImportPath, security)
if err != nil { if err != nil {
return err return err
} }
vcs, repo, rootPath = rr.vcs, rr.repo, rr.root vcs, repo, rootPath = rr.vcs, rr.repo, rr.root
} }
if !vcs.isSecure(repo) && !*getInsecure {
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
}
if p.build.SrcRoot == "" { if p.build.SrcRoot == "" {
// Package not found. Put in first directory of $GOPATH. // Package not found. Put in first directory of $GOPATH.
......
...@@ -18,11 +18,25 @@ import ( ...@@ -18,11 +18,25 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"time"
) )
// httpClient is the default HTTP client, but a variable so it can be // httpClient is the default HTTP client, but a variable so it can be
// changed by tests, without modifying http.DefaultClient. // changed by tests, without modifying http.DefaultClient.
var httpClient = http.DefaultClient var httpClient = http.DefaultClient
var impatientHTTPClient = &http.Client{
Timeout: time.Duration(5 * time.Second),
}
type httpError struct {
status string
statusCode int
url string
}
func (e *httpError) Error() string {
return fmt.Sprintf("%s: %s", e.url, e.status)
}
// httpGET returns the data from an HTTP GET request for the given URL. // httpGET returns the data from an HTTP GET request for the given URL.
func httpGET(url string) ([]byte, error) { func httpGET(url string) ([]byte, error) {
...@@ -32,7 +46,9 @@ func httpGET(url string) ([]byte, error) { ...@@ -32,7 +46,9 @@ func httpGET(url string) ([]byte, error) {
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
return nil, fmt.Errorf("%s: %s", url, resp.Status) err := &httpError{status: resp.Status, statusCode: resp.StatusCode, url: url}
return nil, err
} }
b, err := ioutil.ReadAll(resp.Body) b, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
...@@ -43,7 +59,7 @@ func httpGET(url string) ([]byte, error) { ...@@ -43,7 +59,7 @@ func httpGET(url string) ([]byte, error) {
// httpsOrHTTP returns the body of either the importPath's // httpsOrHTTP returns the body of either the importPath's
// https resource or, if unavailable, the http resource. // https resource or, if unavailable, the http resource.
func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err error) { func httpsOrHTTP(importPath string, security securityMode) (urlStr string, body io.ReadCloser, err error) {
fetch := func(scheme string) (urlStr string, res *http.Response, err error) { fetch := func(scheme string) (urlStr string, res *http.Response, err error) {
u, err := url.Parse(scheme + "://" + importPath) u, err := url.Parse(scheme + "://" + importPath)
if err != nil { if err != nil {
...@@ -54,7 +70,11 @@ func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err erro ...@@ -54,7 +70,11 @@ func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err erro
if buildV { if buildV {
log.Printf("Fetching %s", urlStr) log.Printf("Fetching %s", urlStr)
} }
if security == insecure && scheme == "https" { // fail earlier
res, err = impatientHTTPClient.Get(urlStr)
} else {
res, err = httpClient.Get(urlStr) res, err = httpClient.Get(urlStr)
}
return return
} }
closeBody := func(res *http.Response) { closeBody := func(res *http.Response) {
...@@ -72,8 +92,10 @@ func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err erro ...@@ -72,8 +92,10 @@ func httpsOrHTTP(importPath string) (urlStr string, body io.ReadCloser, err erro
} }
} }
closeBody(res) closeBody(res)
if security == insecure {
urlStr, res, err = fetch("http") urlStr, res, err = fetch("http")
} }
}
if err != nil { if err != nil {
closeBody(res) closeBody(res)
return "", nil, err return "", nil, err
......
...@@ -21,9 +21,10 @@ List lists the packages named by the import paths, one per line. ...@@ -21,9 +21,10 @@ List lists the packages named by the import paths, one per line.
The default output shows the package import path: The default output shows the package import path:
code.google.com/p/google-api-go-client/books/v1 bytes
code.google.com/p/goauth2/oauth encoding/json
code.google.com/p/sqlite github.com/gorilla/mux
golang.org/x/net/html
The -f flag specifies an alternate format for the list, using the The -f flag specifies an alternate format for the list, using the
syntax of package template. The default output is equivalent to -f syntax of package template. The default output is equivalent to -f
...@@ -36,6 +37,7 @@ syntax of package template. The default output is equivalent to -f ...@@ -36,6 +37,7 @@ syntax of package template. The default output is equivalent to -f
Name string // package name Name string // package name
Doc string // package documentation string Doc string // package documentation string
Target string // install path Target string // install path
Shlib string // the shared library that contains this package (only set when -linkshared)
Goroot bool // is this package in the Go root? Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library? Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package? Stale bool // would 'go install' do anything for this package?
...@@ -126,6 +128,7 @@ var listJson = cmdList.Flag.Bool("json", false, "") ...@@ -126,6 +128,7 @@ var listJson = cmdList.Flag.Bool("json", false, "")
var nl = []byte{'\n'} var nl = []byte{'\n'}
func runList(cmd *Command, args []string) { func runList(cmd *Command, args []string) {
buildModeInit()
out := newTrackingWriter(os.Stdout) out := newTrackingWriter(os.Stdout)
defer out.w.Flush() defer out.w.Flush()
...@@ -173,6 +176,10 @@ func runList(cmd *Command, args []string) { ...@@ -173,6 +176,10 @@ func runList(cmd *Command, args []string) {
} }
for _, pkg := range load(args) { for _, pkg := range load(args) {
// Show vendor-expanded paths in listing
pkg.TestImports = pkg.vendored(pkg.TestImports)
pkg.XTestImports = pkg.vendored(pkg.XTestImports)
do(pkg) do(pkg)
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package main package main
import ( import (
"bufio"
"bytes" "bytes"
"flag" "flag"
"fmt" "fmt"
...@@ -76,6 +77,7 @@ func (c *Command) Runnable() bool { ...@@ -76,6 +77,7 @@ func (c *Command) Runnable() bool {
var commands = []*Command{ var commands = []*Command{
cmdBuild, cmdBuild,
cmdClean, cmdClean,
cmdDoc,
cmdEnv, cmdEnv,
cmdFix, cmdFix,
cmdFmt, cmdFmt,
...@@ -90,8 +92,10 @@ var commands = []*Command{ ...@@ -90,8 +92,10 @@ var commands = []*Command{
cmdVet, cmdVet,
helpC, helpC,
helpBuildmode,
helpFileType, helpFileType,
helpGopath, helpGopath,
helpEnvironment,
helpImportPath, helpImportPath,
helpPackages, helpPackages,
helpTestflag, helpTestflag,
...@@ -109,6 +113,8 @@ func setExitStatus(n int) { ...@@ -109,6 +113,8 @@ func setExitStatus(n int) {
exitMu.Unlock() exitMu.Unlock()
} }
var origEnv []string
func main() { func main() {
_ = go11tag _ = go11tag
flag.Usage = usage flag.Usage = usage
...@@ -139,7 +145,7 @@ func main() { ...@@ -139,7 +145,7 @@ func main() {
fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p) fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot start with shell metacharacter '~': %q\n", p)
os.Exit(2) os.Exit(2)
} }
if build.IsLocalImport(p) { if !filepath.IsAbs(p) {
fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p) fmt.Fprintf(os.Stderr, "go: GOPATH entry is relative; must be absolute path: %q.\nRun 'go help gopath' for usage.\n", p)
os.Exit(2) os.Exit(2)
} }
...@@ -151,8 +157,20 @@ func main() { ...@@ -151,8 +157,20 @@ func main() {
os.Exit(2) os.Exit(2)
} }
// Set environment (GOOS, GOARCH, etc) explicitly.
// In theory all the commands we invoke should have
// the same default computation of these as we do,
// but in practice there might be skew
// This makes sure we all agree.
origEnv = os.Environ()
for _, env := range mkEnv() {
if os.Getenv(env.name) != env.value {
os.Setenv(env.name, env.value)
}
}
for _, cmd := range commands { for _, cmd := range commands {
if cmd.Name() == args[0] && cmd.Run != nil { if cmd.Name() == args[0] && cmd.Runnable() {
cmd.Flag.Usage = func() { cmd.Usage() } cmd.Flag.Usage = func() { cmd.Usage() }
if cmd.CustomFlags { if cmd.CustomFlags {
args = args[1:] args = args[1:]
...@@ -200,8 +218,8 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser ...@@ -200,8 +218,8 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
// DO NOT EDIT THIS FILE. GENERATED BY mkdoc.sh. // DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.
// Edit the documentation in other files and rerun mkdoc.sh to generate this one. // Edit the documentation in other files and rerun mkalldocs.sh to generate this one.
/* /*
{{range .}}{{if .Short}}{{.Short | capitalize}} {{range .}}{{if .Short}}{{.Short | capitalize}}
...@@ -217,12 +235,35 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser ...@@ -217,12 +235,35 @@ var documentationTemplate = `// Copyright 2011 The Go Authors. All rights reser
package main package main
` `
// An errWriter wraps a writer, recording whether a write error occurred.
type errWriter struct {
w io.Writer
err error
}
func (w *errWriter) Write(b []byte) (int, error) {
n, err := w.w.Write(b)
if err != nil {
w.err = err
}
return n, err
}
// tmpl executes the given template text on data, writing the result to w. // tmpl executes the given template text on data, writing the result to w.
func tmpl(w io.Writer, text string, data interface{}) { func tmpl(w io.Writer, text string, data interface{}) {
t := template.New("top") t := template.New("top")
t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
template.Must(t.Parse(text)) template.Must(t.Parse(text))
if err := t.Execute(w, data); err != nil { ew := &errWriter{w: w}
err := t.Execute(ew, data)
if ew.err != nil {
// I/O error writing. Ignore write on closed pipe.
if strings.Contains(ew.err.Error(), "pipe") {
os.Exit(1)
}
fatalf("writing output: %v", ew.err)
}
if err != nil {
panic(err) panic(err)
} }
} }
...@@ -236,13 +277,17 @@ func capitalize(s string) string { ...@@ -236,13 +277,17 @@ func capitalize(s string) string {
} }
func printUsage(w io.Writer) { func printUsage(w io.Writer) {
tmpl(w, usageTemplate, commands) bw := bufio.NewWriter(w)
tmpl(bw, usageTemplate, commands)
bw.Flush()
} }
func usage() { func usage() {
// special case "go test -h" // special case "go test -h"
if len(os.Args) > 1 && os.Args[1] == "test" { if len(os.Args) > 1 && os.Args[1] == "test" {
help([]string{"testflag"}) os.Stdout.WriteString(testUsage + "\n\n" +
strings.TrimSpace(testFlag1) + "\n\n" +
strings.TrimSpace(testFlag2) + "\n")
os.Exit(2) os.Exit(2)
} }
printUsage(os.Stderr) printUsage(os.Stderr)
...@@ -308,7 +353,7 @@ func importPathsNoDotExpansion(args []string) []string { ...@@ -308,7 +353,7 @@ func importPathsNoDotExpansion(args []string) []string {
} else { } else {
a = path.Clean(a) a = path.Clean(a)
} }
if a == "all" || a == "std" { if a == "all" || a == "std" || a == "cmd" {
out = append(out, allPackages(a)...) out = append(out, allPackages(a)...)
continue continue
} }
...@@ -401,11 +446,10 @@ func runOut(dir string, cmdargs ...interface{}) []byte { ...@@ -401,11 +446,10 @@ func runOut(dir string, cmdargs ...interface{}) []byte {
// The environment is the current process's environment // The environment is the current process's environment
// but with an updated $PWD, so that an os.Getwd in the // but with an updated $PWD, so that an os.Getwd in the
// child will be faster. // child will be faster.
func envForDir(dir string) []string { func envForDir(dir string, base []string) []string {
env := os.Environ()
// Internally we only use rooted paths, so dir is rooted. // Internally we only use rooted paths, so dir is rooted.
// Even if dir is not rooted, no harm done. // Even if dir is not rooted, no harm done.
return mergeEnvLists([]string{"PWD=" + dir}, env) return mergeEnvLists([]string{"PWD=" + dir}, base)
} }
// mergeEnvLists merges the two environment lists such that // mergeEnvLists merges the two environment lists such that
...@@ -458,6 +502,28 @@ func hasPathPrefix(s, prefix string) bool { ...@@ -458,6 +502,28 @@ func hasPathPrefix(s, prefix string) bool {
} }
} }
// hasFilePathPrefix reports whether the filesystem path s begins with the
// elements in prefix.
func hasFilePathPrefix(s, prefix string) bool {
sv := strings.ToUpper(filepath.VolumeName(s))
pv := strings.ToUpper(filepath.VolumeName(prefix))
s = s[len(sv):]
prefix = prefix[len(pv):]
switch {
default:
return false
case sv != pv:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == filepath.Separator {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == filepath.Separator && s[:len(prefix)] == prefix
}
}
// treeCanMatchPattern(pattern)(name) reports whether // treeCanMatchPattern(pattern)(name) reports whether
// name or children of name can possibly match pattern. // name or children of name can possibly match pattern.
// Pattern is the same limited glob accepted by matchPattern. // Pattern is the same limited glob accepted by matchPattern.
...@@ -475,8 +541,8 @@ func treeCanMatchPattern(pattern string) func(name string) bool { ...@@ -475,8 +541,8 @@ func treeCanMatchPattern(pattern string) func(name string) bool {
// allPackages returns all the packages that can be found // allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern. // under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages) // The pattern is either "all" (all packages), "std" (standard packages),
// or a path including "...". // "cmd" (standard commands), or a path including "...".
func allPackages(pattern string) []string { func allPackages(pattern string) []string {
pkgs := matchPackages(pattern) pkgs := matchPackages(pattern)
if len(pkgs) == 0 { if len(pkgs) == 0 {
...@@ -488,7 +554,7 @@ func allPackages(pattern string) []string { ...@@ -488,7 +554,7 @@ func allPackages(pattern string) []string {
func matchPackages(pattern string) []string { func matchPackages(pattern string) []string {
match := func(string) bool { return true } match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true } treeCanMatch := func(string) bool { return true }
if pattern != "all" && pattern != "std" { if pattern != "all" && pattern != "std" && pattern != "cmd" {
match = matchPattern(pattern) match = matchPattern(pattern)
treeCanMatch = treeCanMatchPattern(pattern) treeCanMatch = treeCanMatchPattern(pattern)
} }
...@@ -501,47 +567,16 @@ func matchPackages(pattern string) []string { ...@@ -501,47 +567,16 @@ func matchPackages(pattern string) []string {
} }
var pkgs []string var pkgs []string
// Commands
cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() || path == cmd {
return nil
}
name := path[len(cmd):]
if !treeCanMatch(name) {
return filepath.SkipDir
}
// Commands are all in cmd/, not in subdirectories.
if strings.Contains(name, string(filepath.Separator)) {
return filepath.SkipDir
}
// We use, e.g., cmd/gofmt as the pseudo import path for gofmt.
name = "cmd/" + name
if have[name] {
return nil
}
have[name] = true
if !match(name) {
return nil
}
_, err = buildContext.ImportDir(path, 0)
if err != nil {
if _, noGo := err.(*build.NoGoError); !noGo {
log.Print(err)
}
return nil
}
pkgs = append(pkgs, name)
return nil
})
for _, src := range buildContext.SrcDirs() { for _, src := range buildContext.SrcDirs() {
if pattern == "std" && src != gorootSrc { if (pattern == "std" || pattern == "cmd") && src != gorootSrc {
continue continue
} }
src = filepath.Clean(src) + string(filepath.Separator) src = filepath.Clean(src) + string(filepath.Separator)
filepath.Walk(src, func(path string, fi os.FileInfo, err error) error { 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 { if err != nil || !fi.IsDir() || path == src {
return nil return nil
} }
...@@ -553,7 +588,10 @@ func matchPackages(pattern string) []string { ...@@ -553,7 +588,10 @@ func matchPackages(pattern string) []string {
} }
name := filepath.ToSlash(path[len(src):]) name := filepath.ToSlash(path[len(src):])
if pattern == "std" && strings.Contains(name, ".") { if pattern == "std" && (strings.Contains(name, ".") || name == "cmd") {
// The name "std" is only the standard library.
// If the name has a dot, assume it's a domain name for go get,
// and if the name is cmd, it's the root of the command tree.
return filepath.SkipDir return filepath.SkipDir
} }
if !treeCanMatch(name) { if !treeCanMatch(name) {
...@@ -659,7 +697,7 @@ func stringList(args ...interface{}) []string { ...@@ -659,7 +697,7 @@ func stringList(args ...interface{}) []string {
case string: case string:
x = append(x, arg) x = append(x, arg)
default: default:
panic("stringList: invalid argument") panic("stringList: invalid argument of type " + fmt.Sprintf("%T", arg))
} }
} }
return x return x
......
// Copyright 2015 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 main
import (
"bytes"
"debug/elf"
"encoding/binary"
"fmt"
"io"
"os"
)
func readAligned4(r io.Reader, sz int32) ([]byte, error) {
full := (sz + 3) &^ 3
data := make([]byte, full)
_, err := io.ReadFull(r, data)
if err != nil {
return nil, err
}
data = data[:sz]
return data, nil
}
func readELFNote(filename, name string, typ int32) ([]byte, error) {
f, err := elf.Open(filename)
if err != nil {
return nil, err
}
for _, sect := range f.Sections {
if sect.Type != elf.SHT_NOTE {
continue
}
r := sect.Open()
for {
var namesize, descsize, noteType int32
err = binary.Read(r, f.ByteOrder, &namesize)
if err != nil {
if err == io.EOF {
break
}
return nil, fmt.Errorf("read namesize failed: %v", err)
}
err = binary.Read(r, f.ByteOrder, &descsize)
if err != nil {
return nil, fmt.Errorf("read descsize failed: %v", err)
}
err = binary.Read(r, f.ByteOrder, &noteType)
if err != nil {
return nil, fmt.Errorf("read type failed: %v", err)
}
noteName, err := readAligned4(r, namesize)
if err != nil {
return nil, fmt.Errorf("read name failed: %v", err)
}
desc, err := readAligned4(r, descsize)
if err != nil {
return nil, fmt.Errorf("read desc failed: %v", err)
}
if name == string(noteName) && typ == noteType {
return desc, nil
}
}
}
return nil, nil
}
var elfGoNote = []byte("Go\x00\x00")
// readELFGoBuildID the Go build ID string from an ELF binary.
// The Go build ID is stored in a note described by an ELF PT_NOTE prog header.
// The caller has already opened filename, to get f, and read the first 4 kB out, in data.
func readELFGoBuildID(filename string, f *os.File, data []byte) (buildid string, err error) {
// Assume the note content is in the first 4 kB, already read.
// Rewrite the ELF header to set shnum to 0, so that we can pass
// the data to elf.NewFile and it will decode the Prog list but not
// try to read the section headers and the string table from disk.
// That's a waste of I/O when all we care about is the Prog list
// and the one ELF note.
switch elf.Class(data[elf.EI_CLASS]) {
case elf.ELFCLASS32:
data[48] = 0
data[49] = 0
case elf.ELFCLASS64:
data[60] = 0
data[61] = 0
}
const elfGoBuildIDTag = 4
ef, err := elf.NewFile(bytes.NewReader(data))
if err != nil {
return "", &os.PathError{Path: filename, Op: "parse", Err: err}
}
for _, p := range ef.Progs {
if p.Type != elf.PT_NOTE || p.Off >= uint64(len(data)) || p.Off+p.Filesz >= uint64(len(data)) || p.Filesz < 16 {
continue
}
note := data[p.Off : p.Off+p.Filesz]
nameSize := ef.ByteOrder.Uint32(note)
valSize := ef.ByteOrder.Uint32(note[4:])
tag := ef.ByteOrder.Uint32(note[8:])
name := note[12:16]
if nameSize != 4 || 16+valSize > uint32(len(note)) || tag != elfGoBuildIDTag || !bytes.Equal(name, elfGoNote) {
continue
}
return string(note[16 : 16+valSize]), nil
}
// No note. Treat as successful but build ID empty.
return "", nil
}
// Copyright 2015 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 main_test
import (
main "cmd/go"
"runtime"
"testing"
)
func TestNoteReading(t *testing.T) {
tg := testgo(t)
defer tg.cleanup()
tg.tempFile("hello.go", `package main; func main() { print("hello, world\n") }`)
const buildID = "TestNoteReading-Build-ID"
tg.run("build", "-ldflags", "-buildid="+buildID, "-o", tg.path("hello.exe"), tg.path("hello.go"))
id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
if err != nil {
t.Fatalf("reading build ID from hello binary: %v", err)
}
if id != buildID {
t.Fatalf("buildID in hello binary = %q, want %q", id, buildID)
}
if runtime.GOOS == "linux" && runtime.GOARCH == "ppc64le" {
t.Skipf("skipping - golang.org/issue/11184")
}
switch runtime.GOOS {
case "plan9":
// no external linking
t.Logf("no external linking - skipping linkmode=external test")
case "solaris":
t.Logf("skipping - golang.org/issue/12178")
default:
tg.run("build", "-ldflags", "-buildid="+buildID+" -linkmode=external", "-o", tg.path("hello.exe"), tg.path("hello.go"))
id, err := main.ReadBuildIDFromBinary(tg.path("hello.exe"))
if err != nil {
t.Fatalf("reading build ID from hello binary (linkmode=external): %v", err)
}
if id != buildID {
t.Fatalf("buildID in hello binary = %q, want %q (linkmode=external)", id, buildID)
}
}
}
...@@ -37,7 +37,8 @@ Run compiles and runs the main package comprising the named Go source files. ...@@ -37,7 +37,8 @@ Run compiles and runs the main package comprising the named Go source files.
A Go source file is defined to be a file ending in a literal ".go" suffix. A Go source file is defined to be a file ending in a literal ".go" suffix.
By default, 'go run' runs the compiled binary directly: 'a.out arguments...'. By default, 'go run' runs the compiled binary directly: 'a.out arguments...'.
If the -exec flag is given, 'go run' invokes the binary using xprog: 'xprog a.out arguments...'. If the -exec flag is given, 'go run' invokes the binary using xprog:
'xprog a.out arguments...'.
If the -exec flag is not given, GOOS or GOARCH is different from the system If the -exec flag is not given, GOOS or GOARCH is different from the system
default, and a program named go_$GOOS_$GOARCH_exec can be found default, and a program named go_$GOOS_$GOARCH_exec can be found
on the current search path, 'go run' invokes the binary using that program, on the current search path, 'go run' invokes the binary using that program,
...@@ -64,6 +65,7 @@ func printStderr(args ...interface{}) (int, error) { ...@@ -64,6 +65,7 @@ func printStderr(args ...interface{}) (int, error) {
func runRun(cmd *Command, args []string) { func runRun(cmd *Command, args []string) {
raceInit() raceInit()
buildModeInit()
var b builder var b builder
b.init() b.init()
b.print = printStderr b.print = printStderr
...@@ -136,6 +138,7 @@ func runStdin(cmdline []string) { ...@@ -136,6 +138,7 @@ func runStdin(cmdline []string) {
cmd.Stdin = os.Stdin cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
cmd.Env = origEnv
startSigHandlers() startSigHandlers()
if err := cmd.Run(); err != nil { if err := cmd.Run(); err != nil {
errorf("%v", err) errorf("%v", err)
......
...@@ -4,6 +4,6 @@ ...@@ -4,6 +4,6 @@
// Test go generate variable substitution. // Test go generate variable substitution.
//go:generate echo $GOARCH $GOFILE $GOPACKAGE xyz$GOPACKAGE/$GOFILE/123 //go:generate echo $GOARCH $GOFILE:$GOLINE ${GOPACKAGE}abc xyz$GOPACKAGE/$GOFILE/123
package p package p
// Copyright 2015 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.
// Test -run flag
//go:generate echo oh yes my man
//go:generate echo no, no, a thousand times no
package p
package q1
import "testing"
import _ "testcycle/q1"
func Test(t *testing.T) {}
package p2
import _ "testdep/p3"
// +build ignore
package ignored
package main
import (
"fmt"
"strings" // really ../vendor/strings
)
func main() {
fmt.Printf("%s\n", strings.Msg)
}
package main
import (
"strings" // really ../vendor/strings
"testing"
)
func TestMsgInternal(t *testing.T) {
if strings.Msg != "hello, world" {
t.Fatal("unexpected msg: %v", strings.Msg)
}
}
package main_test
import (
"strings" // really ../vendor/strings
"testing"
)
func TestMsgExternal(t *testing.T) {
if strings.Msg != "hello, world" {
t.Fatal("unexpected msg: %v", strings.Msg)
}
}
package strings
var Msg = "hello, world"
package invalid
import "vend/x/invalid/vendor/foo"
package x
import _ "p"
import _ "q"
import _ "r"
// +build tagtest
package p
import "fmt"
func g() {
fmt.Printf("%d", 3, 4)
}
package t
import _ "internal/does-not-exist"
package p
import (
_ "q/internal/x"
_ "q/j"
)
package p
import (
_ "q/y"
_ "q/z"
)
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package main package main
import ( import (
"flag"
"fmt" "fmt"
"os" "os"
"strconv" "strconv"
...@@ -16,46 +17,11 @@ import ( ...@@ -16,46 +17,11 @@ import (
// our command line are for us, and some are for 6.out, and // our command line are for us, and some are for 6.out, and
// some are for both. // some are for both.
var usageMessage = `Usage of go test:
-c=false: compile but do not run the test binary
-file=file_test.go: specify file to use for tests;
use multiple times for multiple files
-p=n: build and test up to n packages in parallel
-x=false: print command lines as they are executed
// These flags can be passed with or without a "test." prefix: -v or -test.v.
-bench="": passes -test.bench to test
-benchmem=false: print memory allocation statistics for benchmarks
-benchtime=1s: passes -test.benchtime to test
-cover=false: enable coverage analysis
-covermode="set": specifies mode for coverage analysis
-coverpkg="": comma-separated list of packages for coverage analysis
-coverprofile="": passes -test.coverprofile to test if -cover
-cpu="": passes -test.cpu to test
-cpuprofile="": passes -test.cpuprofile to test
-memprofile="": passes -test.memprofile to test
-memprofilerate=0: passes -test.memprofilerate to test
-blockprofile="": pases -test.blockprofile to test
-blockprofilerate=0: passes -test.blockprofilerate to test
-outputdir=$PWD: passes -test.outputdir to test
-parallel=0: passes -test.parallel to test
-run="": passes -test.run to test
-short=false: passes -test.short to test
-timeout=0: passes -test.timeout to test
-v=false: passes -test.v to test
`
// usage prints a usage message and exits.
func testUsage() {
fmt.Fprint(os.Stderr, usageMessage)
setExitStatus(2)
exit()
}
// testFlagSpec defines a flag we know about. // testFlagSpec defines a flag we know about.
type testFlagSpec struct { type testFlagSpec struct {
name string name string
boolVar *bool boolVar *bool
flagValue flag.Value
passToTest bool // pass to Test passToTest bool // pass to Test
multiOK bool // OK to have multiple instances multiOK bool // OK to have multiple instances
present bool // flag has been seen present bool // flag has been seen
...@@ -65,32 +31,18 @@ type testFlagSpec struct { ...@@ -65,32 +31,18 @@ type testFlagSpec struct {
var testFlagDefn = []*testFlagSpec{ var testFlagDefn = []*testFlagSpec{
// local. // local.
{name: "c", boolVar: &testC}, {name: "c", boolVar: &testC},
{name: "i", boolVar: &buildI},
{name: "o"},
{name: "cover", boolVar: &testCover}, {name: "cover", boolVar: &testCover},
{name: "covermode"},
{name: "coverpkg"}, {name: "coverpkg"},
{name: "o"},
// build flags.
{name: "a", boolVar: &buildA},
{name: "n", boolVar: &buildN},
{name: "p"},
{name: "x", boolVar: &buildX},
{name: "i", boolVar: &buildI},
{name: "work", boolVar: &buildWork},
{name: "ccflags"},
{name: "gcflags"},
{name: "exec"}, {name: "exec"},
{name: "ldflags"},
{name: "gccgoflags"},
{name: "tags"},
{name: "compiler"},
{name: "race", boolVar: &buildRace},
{name: "installsuffix"},
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v. // passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{name: "bench", passToTest: true}, {name: "bench", passToTest: true},
{name: "benchmem", boolVar: new(bool), passToTest: true}, {name: "benchmem", boolVar: new(bool), passToTest: true},
{name: "benchtime", passToTest: true}, {name: "benchtime", passToTest: true},
{name: "covermode"}, {name: "count", passToTest: true},
{name: "coverprofile", passToTest: true}, {name: "coverprofile", passToTest: true},
{name: "cpu", passToTest: true}, {name: "cpu", passToTest: true},
{name: "cpuprofile", passToTest: true}, {name: "cpuprofile", passToTest: true},
...@@ -103,9 +55,26 @@ var testFlagDefn = []*testFlagSpec{ ...@@ -103,9 +55,26 @@ var testFlagDefn = []*testFlagSpec{
{name: "run", passToTest: true}, {name: "run", passToTest: true},
{name: "short", boolVar: new(bool), passToTest: true}, {name: "short", boolVar: new(bool), passToTest: true},
{name: "timeout", passToTest: true}, {name: "timeout", passToTest: true},
{name: "trace", passToTest: true},
{name: "v", boolVar: &testV, passToTest: true}, {name: "v", boolVar: &testV, passToTest: true},
} }
// add build flags to testFlagDefn
func init() {
var cmd Command
addBuildFlags(&cmd)
cmd.Flag.VisitAll(func(f *flag.Flag) {
if f.Name == "v" {
// test overrides the build -v flag
return
}
testFlagDefn = append(testFlagDefn, &testFlagSpec{
name: f.Name,
flagValue: f.Value,
})
})
}
// testFlags processes the command line, grabbing -x and -c, rewriting known flags // testFlags processes the command line, grabbing -x and -c, rewriting known flags
// to have "test" before them, and reading the command line for the 6.out. // to have "test" before them, and reading the command line for the 6.out.
// Unfortunately for us, we need to do our own flag processing because go test // Unfortunately for us, we need to do our own flag processing because go test
...@@ -148,51 +117,32 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -148,51 +117,32 @@ func testFlags(args []string) (packageNames, passToTest []string) {
passToTest = append(passToTest, args[i]) passToTest = append(passToTest, args[i])
continue continue
} }
if f.flagValue != nil {
if err := f.flagValue.Set(value); err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
} else {
// Test-only flags.
// Arguably should be handled by f.flagValue, but aren't.
var err error var err error
switch f.name { switch f.name {
// bool flags. // bool flags.
case "a", "c", "i", "n", "x", "v", "race", "cover", "work": case "c", "i", "v", "cover":
setBoolFlag(f.boolVar, value) setBoolFlag(f.boolVar, value)
case "o": case "o":
testO = value testO = value
testNeedBinary = true testNeedBinary = true
case "p":
setIntFlag(&buildP, value)
case "exec": case "exec":
execCmd, err = splitQuotedFields(value) execCmd, err = splitQuotedFields(value)
if err != nil { if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err) fatalf("invalid flag argument for -%s: %v", f.name, err)
} }
case "ccflags":
buildCcflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "gcflags":
buildGcflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "ldflags":
buildLdflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "gccgoflags":
buildGccgoflags, err = splitQuotedFields(value)
if err != nil {
fatalf("invalid flag argument for -%s: %v", f.name, err)
}
case "tags":
buildContext.BuildTags = strings.Fields(value)
case "compiler":
buildCompiler{}.Set(value)
case "bench": case "bench":
// record that we saw the flag; don't care about the value // record that we saw the flag; don't care about the value
testBench = true testBench = true
case "timeout": case "timeout":
testTimeout = value testTimeout = value
case "blockprofile", "cpuprofile", "memprofile": case "blockprofile", "cpuprofile", "memprofile", "trace":
testProfile = true testProfile = true
testNeedBinary = true testNeedBinary = true
case "coverpkg": case "coverpkg":
...@@ -210,12 +160,13 @@ func testFlags(args []string) (packageNames, passToTest []string) { ...@@ -210,12 +160,13 @@ func testFlags(args []string) (packageNames, passToTest []string) {
case "set", "count", "atomic": case "set", "count", "atomic":
testCoverMode = value testCoverMode = value
default: default:
fatalf("invalid flag argument for -cover: %q", value) fatalf("invalid flag argument for -covermode: %q", value)
} }
testCover = true testCover = true
case "outputdir": case "outputdir":
outputDir = value outputDir = value
} }
}
if extraWord { if extraWord {
i++ i++
} }
...@@ -267,7 +218,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) ...@@ -267,7 +218,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
for _, f = range testFlagDefn { for _, f = range testFlagDefn {
if name == f.name { if name == f.name {
// Booleans are special because they have modes -x, -x=true, -x=false. // Booleans are special because they have modes -x, -x=true, -x=false.
if f.boolVar != nil { if f.boolVar != nil || isBoolFlag(f.flagValue) {
if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
value = "true" value = "true"
} else { } else {
...@@ -294,6 +245,17 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool) ...@@ -294,6 +245,17 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
return return
} }
// isBoolFlag reports whether v is a bool flag.
func isBoolFlag(v flag.Value) bool {
vv, ok := v.(interface {
IsBoolFlag() bool
})
if ok {
return vv.IsBoolFlag()
}
return false
}
// setBoolFlag sets the addressed boolean to the value. // setBoolFlag sets the addressed boolean to the value.
func setBoolFlag(flag *bool, value string) { func setBoolFlag(flag *bool, value string) {
x, err := strconv.ParseBool(value) x, err := strconv.ParseBool(value)
......
...@@ -50,6 +50,9 @@ func tool(toolName string) string { ...@@ -50,6 +50,9 @@ func tool(toolName string) string {
if toolIsWindows { if toolIsWindows {
toolPath += toolWindowsExtension toolPath += toolWindowsExtension
} }
if len(buildToolExec) > 0 {
return toolPath
}
// Give a nice message if there is no tool with that name. // Give a nice message if there is no tool with that name.
if _, err := os.Stat(toolPath); err != nil { if _, err := os.Stat(toolPath); err != nil {
if isInGoToolsRepo(toolName) { if isInGoToolsRepo(toolName) {
...@@ -64,10 +67,6 @@ func tool(toolName string) string { ...@@ -64,10 +67,6 @@ func tool(toolName string) string {
} }
func isInGoToolsRepo(toolName string) bool { func isInGoToolsRepo(toolName string) bool {
switch toolName {
case "cover", "vet":
return true
}
return false return false
} }
...@@ -92,7 +91,11 @@ func runTool(cmd *Command, args []string) { ...@@ -92,7 +91,11 @@ func runTool(cmd *Command, args []string) {
return return
} }
if toolN { if toolN {
fmt.Printf("%s %s\n", toolPath, strings.Join(args[1:], " ")) cmd := toolPath
if len(args) > 1 {
cmd += " " + strings.Join(args[1:], " ")
}
fmt.Printf("%s\n", cmd)
return return
} }
toolCmd := &exec.Cmd{ toolCmd := &exec.Cmd{
...@@ -101,6 +104,8 @@ func runTool(cmd *Command, args []string) { ...@@ -101,6 +104,8 @@ func runTool(cmd *Command, args []string) {
Stdin: os.Stdin, Stdin: os.Stdin,
Stdout: os.Stdout, Stdout: os.Stdout,
Stderr: os.Stderr, Stderr: os.Stderr,
// Set $GOROOT, mainly for go tool dist.
Env: mergeEnvLists([]string{"GOROOT=" + goroot}, os.Environ()),
} }
err := toolCmd.Run() err := toolCmd.Run()
if err != nil { if err != nil {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment