libgo: update to Go 1.11

Reviewed-on: https://go-review.googlesource.com/136435

gotools/:
	* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
	make sure it is writable.
	(check-go-tools): Likewise.
	(check-vet): Copy internal/objabi to check-vet-dir.
	* Makefile.in: Rebuild.

From-SVN: r264546
This commit is contained in:
Ian Lance Taylor 2018-09-24 21:46:21 +00:00 committed by Ian Lance Taylor
parent 779d8a5ad0
commit dd931d9b48
1516 changed files with 83937 additions and 19003 deletions

View File

@ -1,4 +1,4 @@
92a14213215fd93df7240fa9d376a1213b1d5a74
7b25b4dff4778fc4d6b5d6e10594814146b3e5dd
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.

View File

@ -8,13 +8,15 @@
package main
import "unsafe"
var bug = false
var minus1 = -1
var five = 5
var big int64 = 10 | 1<<32
var big int64 = 10 | 1<<40
type block [1<<19]byte
type block [1 << 19]byte
var g1 []block
@ -48,9 +50,10 @@ func bigcap() {
g1 = make([]block, 10, big)
}
type cblock [1<<16-1]byte
type cblock [1<<16 - 1]byte
var g4 chan cblock
func badchancap() {
g4 = make(chan cblock, minus1)
}
@ -60,7 +63,8 @@ func bigchancap() {
}
func overflowchan() {
g4 = make(chan cblock, 1<<30)
const ptrSize = unsafe.Sizeof(uintptr(0))
g4 = make(chan cblock, 1<<(30*(ptrSize/4)))
}
func main() {

View File

@ -1,6 +1,6 @@
// run
// Copyright 2013 The Go Authors. All rights reserved.
// Copyright 2013 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.
@ -15,22 +15,31 @@ type T []int
func main() {
n := -1
shouldPanic("len out of range", func() {_ = make(T, n)})
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
shouldPanic("len out of range", func() { _ = make(T, n) })
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
var t *byte
if unsafe.Sizeof(t) == 8 {
n = 1<<20
n <<= 20
shouldPanic("len out of range", func() {_ = make(T, n)})
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
n <<= 20
shouldPanic("len out of range", func() {_ = make(T, n)})
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
var n2 int64 = 1 << 50
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
n2 = 1<<63 - 1
shouldPanic("len out of range", func() { _ = make(T, int(n2)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int(n2)) })
} else {
n = 1<<31 - 1
shouldPanic("len out of range", func() {_ = make(T, n)})
shouldPanic("cap out of range", func() {_ = make(T, 0, n)})
shouldPanic("len out of range", func() { _ = make(T, n) })
shouldPanic("cap out of range", func() { _ = make(T, 0, n) })
shouldPanic("len out of range", func() { _ = make(T, int64(n)) })
shouldPanic("cap out of range", func() { _ = make(T, 0, int64(n)) })
}
// Test make in append panics since the gc compiler optimizes makes in appends.
shouldPanic("len out of range", func() { _ = append(T{}, make(T, n)...) })
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, n)...) })
shouldPanic("len out of range", func() { _ = append(T{}, make(T, int64(n))...) })
shouldPanic("cap out of range", func() { _ = append(T{}, make(T, 0, int64(n))...) })
}
func shouldPanic(str string, f func()) {
@ -44,6 +53,6 @@ func shouldPanic(str string, f func()) {
panic("got panic " + s + ", want " + str)
}
}()
f()
}

View File

@ -1,3 +1,11 @@
2018-09-24 Ian Lance Taylor <iant@golang.org>
* Makefile.am (mostlyclean-local): Run chmod on check-go-dir to
make sure it is writable.
(check-go-tools): Likewise.
(check-vet): Copy internal/objabi to check-vet-dir.
* Makefile.in: Rebuild.
2018-05-09 Ian Lance Taylor <iant@golang.org>
* Makefile.am (check-go-tool): Don't copy zstdpkglist.go.

View File

@ -123,6 +123,7 @@ MOSTLYCLEANFILES = \
*.sent
mostlyclean-local:
chmod -R u+w check-go-dir
rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir
if NATIVE
@ -228,6 +229,7 @@ ECHO_ENV = PATH=`echo $(abs_builddir):$${PATH} | sed 's,::*,:,g;s,^:*,,;s,:*$$,,
# check-go-tool runs `go test cmd/go` in our environment.
check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
chmod -R u+w check-go-dir
rm -rf check-go-dir cmd_go-testlog
$(MKDIR_P) check-go-dir/src/cmd/go
cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/
@ -297,8 +299,10 @@ check-carchive-test: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check
# check-vet runs `go test cmd/vet` in our environment.
check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
rm -rf check-vet-dir cmd_vet-testlog
$(MKDIR_P) check-vet-dir/src/cmd
$(MKDIR_P) check-vet-dir/src/cmd/internal
cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
@abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog

View File

@ -637,8 +637,8 @@ distclean-generic:
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
@NATIVE_FALSE@uninstall-local:
@NATIVE_FALSE@install-exec-local:
@NATIVE_FALSE@uninstall-local:
clean: clean-am
clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \
@ -744,6 +744,7 @@ s-zdefaultcc: Makefile
$(STAMP) $@
mostlyclean-local:
chmod -R u+w check-go-dir
rm -rf check-go-dir check-runtime-dir cgo-test-dir carchive-test-dir
@NATIVE_TRUE@go$(EXEEXT): $(go_cmd_go_files) $(LIBGOTOOL) $(LIBGODEP)
@ -807,6 +808,7 @@ mostlyclean-local:
# check-go-tool runs `go test cmd/go` in our environment.
@NATIVE_TRUE@check-go-tool: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
@NATIVE_TRUE@ chmod -R u+w check-go-dir
@NATIVE_TRUE@ rm -rf check-go-dir cmd_go-testlog
@NATIVE_TRUE@ $(MKDIR_P) check-go-dir/src/cmd/go
@NATIVE_TRUE@ cp $(cmdsrcdir)/go/*.go check-go-dir/src/cmd/go/
@ -876,8 +878,10 @@ mostlyclean-local:
# check-vet runs `go test cmd/vet` in our environment.
@NATIVE_TRUE@check-vet: go$(EXEEXT) $(noinst_PROGRAMS) check-head check-gccgo check-gcc
@NATIVE_TRUE@ rm -rf check-vet-dir cmd_vet-testlog
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd
@NATIVE_TRUE@ $(MKDIR_P) check-vet-dir/src/cmd/internal
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/vet check-vet-dir/src/cmd/
@NATIVE_TRUE@ cp -r $(cmdsrcdir)/internal/objabi check-vet-dir/src/cmd/internal
@NATIVE_TRUE@ cp $(libgodir)/objabi.go check-vet-dir/src/cmd/internal/objabi/
@NATIVE_TRUE@ @abs_libgodir=`cd $(libgodir) && $(PWD_COMMAND)`; \
@NATIVE_TRUE@ abs_checkdir=`cd check-vet-dir && $(PWD_COMMAND)`; \
@NATIVE_TRUE@ echo "cd check-vet-dir/src/cmd/vet && $(ECHO_ENV) GOPATH=$${abs_checkdir} $(abs_builddir)/go$(EXEEXT) test -test.short -test.timeout=$(GOTOOLS_TEST_TIMEOUT)s -test.v" > cmd_vet-testlog

View File

@ -1,4 +1,4 @@
fe8a0d12b14108cbe2408b417afcaab722b0727c
41e62b8c49d21659b48a95216e3062032285250f
The first line of this file holds the git revision number of the
last merge done from the master library sources.

View File

@ -541,6 +541,7 @@ s-objabi: Makefile
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp
echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp
echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
@ -608,7 +609,7 @@ noinst_DATA += zdefaultcc.go
zstdpkglist.go: s-zstdpkglist; @true
s-zstdpkglist: Makefile
rm -f zstdpkglist.go.tmp
echo 'package build' > zstdpkglist.go.tmp
echo 'package goroot' > zstdpkglist.go.tmp
echo "" >> zstdpkglist.go.tmp
echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp
echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp
@ -707,6 +708,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
libgo_go_objs = \
$(addsuffix .lo,$(PACKAGES)) \
bytes/index.lo \
internal/bytealg/bytealg.lo \
reflect/makefunc_ffi_c.lo \
strings/index.lo \
$(syscall_lib_clone_lo) \
@ -718,7 +720,8 @@ libgo_go_objs = \
log/syslog/syslog_c.lo \
$(os_lib_inotify_lo) \
runtime/internal/atomic_c.lo \
sync/atomic_c.lo
sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo
libgo_ldflags = \
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
@ -960,8 +963,8 @@ runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
extra_go_files_runtime_internal_sys = version.go
runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys)
extra_go_files_go_build = zstdpkglist.go
go/build.lo.dep: $(extra_go_files_go_build)
extra_go_files_internal_goroot = zstdpkglist.go
internal/goroot.lo.dep: $(extra_go_files_internal_goroot)
extra_go_files_go_types = gccgosizes.go
go/types.lo.dep: $(extra_go_files_go_types)
@ -976,6 +979,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
@ -990,6 +1003,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc
strings/index.lo: go/strings/indexbyte.c runtime.inc
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
@$(MKDIR_P) internal/bytealg
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
# Use a C function with a fixed number of arguments to call a C
# varargs function.
@ -1024,6 +1040,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
@$(MKDIR_P) syscall
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
# internal/cpu needs some C code.
internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
@$(MKDIR_P) internal/cpu
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
# Solaris 11.4 changed the type of fields in struct stat.
# Use a build tag, based on a configure check, to cope.
if LIBGO_IS_SOLARIS

View File

@ -174,11 +174,12 @@ libgotool_a_OBJECTS = $(am_libgotool_a_OBJECTS)
LTLIBRARIES = $(toolexeclib_LTLIBRARIES)
@LIBGO_IS_LINUX_TRUE@am__DEPENDENCIES_1 = syscall/clone_linux.lo
am__DEPENDENCIES_2 = $(addsuffix .lo,$(PACKAGES)) bytes/index.lo \
reflect/makefunc_ffi_c.lo strings/index.lo \
$(am__DEPENDENCIES_1) syscall/errno.lo syscall/signame.lo \
syscall/wait.lo $(golang_org_x_net_lif_lo) \
internal/bytealg/bytealg.lo reflect/makefunc_ffi_c.lo \
strings/index.lo $(am__DEPENDENCIES_1) syscall/errno.lo \
syscall/signame.lo syscall/wait.lo $(golang_org_x_net_lif_lo) \
$(golang_org_x_net_route_lo) log/syslog/syslog_c.lo \
runtime/internal/atomic_c.lo sync/atomic_c.lo
runtime/internal/atomic_c.lo sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo
am__DEPENDENCIES_3 =
am__DEPENDENCIES_4 = $(am__DEPENDENCIES_2) \
../libbacktrace/libbacktrace.la $(am__DEPENDENCIES_3) \
@ -824,6 +825,7 @@ PACKAGES = $(shell cat $(srcdir)/libgo-packages.txt)
libgo_go_objs = \
$(addsuffix .lo,$(PACKAGES)) \
bytes/index.lo \
internal/bytealg/bytealg.lo \
reflect/makefunc_ffi_c.lo \
strings/index.lo \
$(syscall_lib_clone_lo) \
@ -835,7 +837,8 @@ libgo_go_objs = \
log/syslog/syslog_c.lo \
$(os_lib_inotify_lo) \
runtime/internal/atomic_c.lo \
sync/atomic_c.lo
sync/atomic_c.lo \
internal/cpu/cpu_gccgo.lo
libgo_ldflags = \
-version-info $(libtool_VERSION) $(PTHREAD_CFLAGS) $(AM_LDFLAGS)
@ -999,7 +1002,7 @@ runtime_internal_sys_lo_check_GOCFLAGS = -fgo-compiling-runtime
# Also use -fno-inline to get better results from the memory profiler.
runtime_pprof_check_GOCFLAGS = -static-libgo -fno-inline
extra_go_files_runtime_internal_sys = version.go
extra_go_files_go_build = zstdpkglist.go
extra_go_files_internal_goroot = zstdpkglist.go
extra_go_files_go_types = gccgosizes.go
extra_go_files_cmd_internal_objabi = objabi.go
extra_go_files_cmd_go_internal_cfg = zdefaultcc.go
@ -1007,6 +1010,16 @@ extra_check_libs_cmd_go_internal_cache = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_generate = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_get = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_load = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_imports = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modconv = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfetch_codehost = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modfile = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_modload = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_module = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_mvs = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_search = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_web2 = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_go_internal_work = $(abs_builddir)/libgotool.a
extra_check_libs_cmd_vet_internal_cfg = $(abs_builddir)/libgotool.a
@HAVE_STAT_TIMESPEC_FALSE@@LIBGO_IS_SOLARIS_TRUE@matchargs_os =
@ -2755,6 +2768,7 @@ s-objabi: Makefile
echo 'const defaultGO386 = `sse2`' >> objabi.go.tmp
echo 'const defaultGOARM = `5`' >> objabi.go.tmp
echo 'const defaultGOMIPS = `hardfloat`' >> objabi.go.tmp
echo 'const defaultGOMIPS64 = `hardfloat`' >> objabi.go.tmp
echo 'const defaultGOOS = runtime.GOOS' >> objabi.go.tmp
echo 'const defaultGOARCH = runtime.GOARCH' >> objabi.go.tmp
echo 'const defaultGO_EXTLINK_ENABLED = ``' >> objabi.go.tmp
@ -2816,7 +2830,7 @@ s-runtime-inc: runtime.lo Makefile
zstdpkglist.go: s-zstdpkglist; @true
s-zstdpkglist: Makefile
rm -f zstdpkglist.go.tmp
echo 'package build' > zstdpkglist.go.tmp
echo 'package goroot' > zstdpkglist.go.tmp
echo "" >> zstdpkglist.go.tmp
echo 'var stdpkg = map[string]bool{' >> zstdpkglist.go.tmp
echo $(libgo_go_objs) 'unsafe.lo' 'runtime/cgo.lo' | sed 's|[a-z0-9_/]*_c\.lo||g' | sed 's|\([a-z0-9_/]*\)\.lo|"\1": true,|g' >> zstdpkglist.go.tmp
@ -2943,7 +2957,7 @@ $(foreach package,$(GOTOOL_PACKAGES),$(eval $(call PACKAGE_template,$(package)))
runtime.lo.dep: $(extra_go_files_runtime)
syscall.lo.dep: $(extra_go_files_syscall)
runtime/internal/sys.lo.dep: $(extra_go_files_runtime_internal_sys)
go/build.lo.dep: $(extra_go_files_go_build)
internal/goroot.lo.dep: $(extra_go_files_internal_goroot)
go/types.lo.dep: $(extra_go_files_go_types)
cmd/internal/objabi.lo.dep: $(extra_go_files_cmd_internal_objabi)
cmd/go/internal/cfg.lo.dep: $(extra_go_files_cmd_go_internal_cfg)
@ -2958,6 +2972,9 @@ bytes/index.lo: go/bytes/indexbyte.c runtime.inc
strings/index.lo: go/strings/indexbyte.c runtime.inc
@$(MKDIR_P) strings
$(LTCOMPILE) -c -o $@ $(srcdir)/go/strings/indexbyte.c
internal/bytealg/bytealg.lo: go/internal/bytealg/bytealg.c runtime.inc
@$(MKDIR_P) internal/bytealg
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/bytealg/bytealg.c
# Use a C function with a fixed number of arguments to call a C
# varargs function.
@ -2992,6 +3009,11 @@ syscall/wait.lo: go/syscall/wait.c runtime.inc
@$(MKDIR_P) syscall
$(LTCOMPILE) -c -o $@ $(srcdir)/go/syscall/wait.c
# internal/cpu needs some C code.
internal/cpu/cpu_gccgo.lo: go/internal/cpu/cpu_gccgo.c runtime.inc
@$(MKDIR_P) internal/cpu
$(LTCOMPILE) -c -o $@ $(srcdir)/go/internal/cpu/cpu_gccgo.c
# Build golang_org/x/net/route only on BSD systems.
@LIBGO_IS_BSD_TRUE@$(eval $(call PACKAGE_template,golang_org/x/net/route))

View File

@ -1 +1 @@
go1.10.3
go1.11

View File

@ -3,9 +3,23 @@ archive/zip
bufio
bytes
cmd/go/internal/cache
cmd/go/internal/dirhash
cmd/go/internal/generate
cmd/go/internal/get
cmd/go/internal/imports
cmd/go/internal/load
cmd/go/internal/modconv
cmd/go/internal/modfetch
cmd/go/internal/modfetch/codehost
cmd/go/internal/modfile
cmd/go/internal/modload
cmd/go/internal/module
cmd/go/internal/mvs
cmd/go/internal/par
cmd/go/internal/search
cmd/go/internal/semver
cmd/go/internal/txtar
cmd/go/internal/web2
cmd/go/internal/work
cmd/internal/buildid
cmd/internal/edit
@ -29,6 +43,7 @@ crypto/dsa
crypto/ecdsa
crypto/elliptic
crypto/hmac
crypto/internal/subtle
crypto/md5
crypto/rand
crypto/rc4
@ -76,11 +91,15 @@ go/printer
go/scanner
go/token
go/types
golang_org/x/crypto/internal/chacha20
golang_org/x/crypto/chacha20poly1305
golang_org/x/crypto/chacha20poly1305/internal/chacha20
golang_org/x/crypto/cryptobyte
golang_org/x/crypto/curve25519
golang_org/x/crypto/poly1305
golang_org/x/net/dns/dnsmessage
golang_org/x/net/http/httpguts
golang_org/x/net/http/httpproxy
golang_org/x/net/http2/hpack
golang_org/x/net/idna
golang_org/x/net/lex/httplex
@ -98,6 +117,7 @@ image/draw
image/jpeg
image/png
index/suffixarray
internal/cpu
internal/poll
internal/singleflight
internal/trace

View File

@ -56,7 +56,7 @@ func (he headerError) Error() string {
const (
// Type '0' indicates a regular file.
TypeReg = '0'
TypeRegA = '\x00' // For legacy support; use TypeReg instead
TypeRegA = '\x00' // Deprecated: Use TypeReg instead.
// Type '1' to '6' are header-only flags and may not have a data body.
TypeLink = '1' // Hard link
@ -138,7 +138,10 @@ var basicKeys = map[string]bool{
// should do so by creating a new Header and copying the fields
// that they are interested in preserving.
type Header struct {
Typeflag byte // Type of header entry (should be TypeReg for most files)
// Typeflag is the type of header entry.
// The zero value is automatically promoted to either TypeReg or TypeDir
// depending on the presence of a trailing slash in Name.
Typeflag byte
Name string // Name of file entry
Linkname string // Target name of link (valid for TypeLink or TypeSymlink)
@ -184,7 +187,7 @@ type Header struct {
// The key and value should be non-empty UTF-8 strings.
//
// When Writer.WriteHeader is called, PAX records derived from the
// the other fields in Header take precedence over PAXRecords.
// other fields in Header take precedence over PAXRecords.
PAXRecords map[string]string
// Format specifies the format of the tar header.

View File

@ -94,7 +94,7 @@ const (
// application can only parse GNU formatted archives.
//
// Reference:
// http://www.gnu.org/software/tar/manual/html_node/Standard.html
// https://www.gnu.org/software/tar/manual/html_node/Standard.html
FormatGNU
// Schily's tar format, which is incompatible with USTAR.

View File

@ -64,7 +64,6 @@ func (tr *Reader) next() (*Header, error) {
// normally be visible to the outside. As such, this loop iterates through
// one or more "header files" until it finds a "normal file".
format := FormatUSTAR | FormatPAX | FormatGNU
loop:
for {
// Discard the remainder of the file and any padding.
if err := discard(tr.r, tr.curr.PhysicalRemaining()); err != nil {
@ -102,7 +101,7 @@ loop:
Format: format,
}, nil
}
continue loop // This is a meta header affecting the next header
continue // This is a meta header affecting the next header
case TypeGNULongName, TypeGNULongLink:
format.mayOnlyBe(FormatGNU)
realname, err := ioutil.ReadAll(tr)
@ -117,7 +116,7 @@ loop:
case TypeGNULongLink:
gnuLongLink = p.parseString(realname)
}
continue loop // This is a meta header affecting the next header
continue // This is a meta header affecting the next header
default:
// The old GNU sparse format is handled here since it is technically
// just a regular file with additional attributes.
@ -131,8 +130,12 @@ loop:
if gnuLongLink != "" {
hdr.Linkname = gnuLongLink
}
if hdr.Typeflag == TypeRegA && strings.HasSuffix(hdr.Name, "/") {
hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories
if hdr.Typeflag == TypeRegA {
if strings.HasSuffix(hdr.Name, "/") {
hdr.Typeflag = TypeDir // Legacy archives use trailing slash for directories
} else {
hdr.Typeflag = TypeReg
}
}
// The extended headers may have updated the size.
@ -200,7 +203,7 @@ func (tr *Reader) handleSparseFile(hdr *Header, rawHdr *block) error {
// readGNUSparsePAXHeaders checks the PAX headers for GNU sparse headers.
// If they are found, then this function reads the sparse map and returns it.
// This assumes that 0.0 headers have already been converted to 0.1 headers
// by the the PAX header parsing logic.
// by the PAX header parsing logic.
func (tr *Reader) readGNUSparsePAXHeaders(hdr *Header) (sparseDatas, error) {
// Identify the version of GNU headers.
var is1x0 bool

View File

@ -189,7 +189,7 @@ func TestReader(t *testing.T) {
Gid: 5000,
Size: 5,
ModTime: time.Unix(1244593104, 0),
Typeflag: '\x00',
Typeflag: '0',
}, {
Name: "small2.txt",
Mode: 0444,
@ -197,7 +197,7 @@ func TestReader(t *testing.T) {
Gid: 5000,
Size: 11,
ModTime: time.Unix(1244593104, 0),
Typeflag: '\x00',
Typeflag: '0',
}},
}, {
file: "testdata/pax.tar",
@ -378,9 +378,9 @@ func TestReader(t *testing.T) {
"security.selinux": "unconfined_u:object_r:default_t:s0\x00",
},
PAXRecords: map[string]string{
"mtime": "1386065770.449252304",
"atime": "1389782991.41987522",
"ctime": "1386065770.449252304",
"mtime": "1386065770.449252304",
"atime": "1389782991.41987522",
"ctime": "1386065770.449252304",
"SCHILY.xattr.security.selinux": "unconfined_u:object_r:default_t:s0\x00",
},
Format: FormatPAX,
@ -534,9 +534,10 @@ func TestReader(t *testing.T) {
// a buggy pre-Go1.8 tar.Writer.
file: "testdata/invalid-go17.tar",
headers: []*Header{{
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
Uid: 010000000,
ModTime: time.Unix(0, 0),
Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/foo",
Uid: 010000000,
ModTime: time.Unix(0, 0),
Typeflag: '0',
}},
}, {
// USTAR archive with a regular entry with non-zero device numbers.

View File

@ -306,6 +306,7 @@ func TestRoundTrip(t *testing.T) {
ModTime: time.Now().Round(time.Second),
PAXRecords: map[string]string{"uid": "2097152"},
Format: FormatPAX,
Typeflag: TypeReg,
}
if err := tw.WriteHeader(hdr); err != nil {
t.Fatalf("tw.WriteHeader: %v", err)

Binary file not shown.

Binary file not shown.

View File

@ -5,7 +5,6 @@
package tar
import (
"bytes"
"fmt"
"io"
"path"
@ -71,6 +70,16 @@ func (tw *Writer) WriteHeader(hdr *Header) error {
}
tw.hdr = *hdr // Shallow copy of Header
// Avoid usage of the legacy TypeRegA flag, and automatically promote
// it to use TypeReg or TypeDir.
if tw.hdr.Typeflag == TypeRegA {
if strings.HasSuffix(tw.hdr.Name, "/") {
tw.hdr.Typeflag = TypeDir
} else {
tw.hdr.Typeflag = TypeReg
}
}
// Round ModTime and ignore AccessTime and ChangeTime unless
// the format is explicitly chosen.
// This ensures nominal usage of WriteHeader (without specifying the format)
@ -166,7 +175,7 @@ func (tw *Writer) writePAXHeader(hdr *Header, paxHdrs map[string]string) error {
sort.Strings(keys)
// Write each record to a buffer.
var buf bytes.Buffer
var buf strings.Builder
for _, k := range keys {
rec, err := formatPAXRecord(k, paxHdrs[k])
if err != nil {

View File

@ -461,6 +461,15 @@ func TestWriter(t *testing.T) {
testHeader{Header{Name: strings.Repeat("123456789/", 30)}, nil},
testClose{nil},
},
}, {
// Automatically promote zero value of Typeflag depending on the name.
file: "testdata/file-and-dir.tar",
tests: []testFnc{
testHeader{Header{Name: "small.txt", Size: 5}, nil},
testWrite{"Kilts", 5, nil},
testHeader{Header{Name: "dir/"}, nil},
testClose{nil},
},
}}
equalError := func(x, y error) bool {
@ -809,8 +818,8 @@ func TestValidTypeflagWithPAXHeader(t *testing.T) {
if err != nil {
t.Fatalf("Failed to read header: %s", err)
}
if header.Typeflag != 0 {
t.Fatalf("Typeflag should've been 0, found %d", header.Typeflag)
if header.Typeflag != TypeReg {
t.Fatalf("Typeflag should've been %d, found %d", TypeReg, header.Typeflag)
}
}
}

View File

@ -81,8 +81,17 @@ const (
// See the zip spec for details.
type FileHeader struct {
// Name is the name of the file.
// It must be a relative path, not start with a drive letter (e.g. C:),
// and must use forward slashes instead of back slashes.
//
// It must be a relative path, not start with a drive letter (such as "C:"),
// and must use forward slashes instead of back slashes. A trailing slash
// indicates that this file is a directory and should have no data.
//
// When reading zip files, the Name field is populated from
// the zip file directly and is not validated for correctness.
// It is the caller's responsibility to sanitize it as
// appropriate, including canonicalizing slash directions,
// validating that paths are relative, and preventing path
// traversal through filenames ("../../../").
Name string
// Comment is any arbitrary user-defined string shorter than 64KiB.
@ -201,7 +210,7 @@ func timeZone(offset time.Duration) *time.Location {
// msDosTimeToTime converts an MS-DOS date and time into a time.Time.
// The resolution is 2s.
// See: http://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
// See: https://msdn.microsoft.com/en-us/library/ms724247(v=VS.85).aspx
func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
return time.Date(
// date bits 0-4: day of month; 5-8: month; 9-15: years since 1980
@ -221,7 +230,7 @@ func msDosTimeToTime(dosDate, dosTime uint16) time.Time {
// timeToMsDosTime converts a time.Time to an MS-DOS date and time.
// The resolution is 2s.
// See: http://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
// See: https://msdn.microsoft.com/en-us/library/ms724274(v=VS.85).aspx
func timeToMsDosTime(t time.Time) (fDate uint16, fTime uint16) {
fDate = uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9)
fTime = uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11)

View File

@ -11,6 +11,7 @@ import (
"hash"
"hash/crc32"
"io"
"strings"
"unicode/utf8"
)
@ -71,7 +72,7 @@ func (w *Writer) SetComment(comment string) error {
}
// Close finishes writing the zip file by writing the central directory.
// It does not (and cannot) close the underlying writer.
// It does not close the underlying writer.
func (w *Writer) Close() error {
if w.last != nil && !w.last.closed {
if err := w.last.close(); err != nil {
@ -209,7 +210,8 @@ func (w *Writer) Close() error {
// The file contents will be compressed using the Deflate method.
// The name must be a relative path: it must not start with a drive
// letter (e.g. C:) or leading slash, and only forward slashes are
// allowed.
// allowed. To create a directory instead of a file, add a trailing
// slash to the name.
// The file's contents must be written to the io.Writer before the next
// call to Create, CreateHeader, or Close.
func (w *Writer) Create(name string) (io.Writer, error) {
@ -261,8 +263,6 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
return nil, errors.New("archive/zip: invalid duplicate FileHeader")
}
fh.Flags |= 0x8 // we will write a data descriptor
// The ZIP format has a sad state of affairs regarding character encoding.
// Officially, the name and comment fields are supposed to be encoded
// in CP-437 (which is mostly compatible with ASCII), unless the UTF-8
@ -319,35 +319,58 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
fh.Extra = append(fh.Extra, mbuf[:]...)
}
fw := &fileWriter{
zipw: w.cw,
compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(),
}
comp := w.compressor(fh.Method)
if comp == nil {
return nil, ErrAlgorithm
}
var err error
fw.comp, err = comp(fw.compCount)
if err != nil {
return nil, err
}
fw.rawCount = &countWriter{w: fw.comp}
var (
ow io.Writer
fw *fileWriter
)
h := &header{
FileHeader: fh,
offset: uint64(w.cw.count),
}
w.dir = append(w.dir, h)
fw.header = h
if strings.HasSuffix(fh.Name, "/") {
// Set the compression method to Store to ensure data length is truly zero,
// which the writeHeader method always encodes for the size fields.
// This is necessary as most compression formats have non-zero lengths
// even when compressing an empty string.
fh.Method = Store
fh.Flags &^= 0x8 // we will not write a data descriptor
// Explicitly clear sizes as they have no meaning for directories.
fh.CompressedSize = 0
fh.CompressedSize64 = 0
fh.UncompressedSize = 0
fh.UncompressedSize64 = 0
ow = dirWriter{}
} else {
fh.Flags |= 0x8 // we will write a data descriptor
fw = &fileWriter{
zipw: w.cw,
compCount: &countWriter{w: w.cw},
crc32: crc32.NewIEEE(),
}
comp := w.compressor(fh.Method)
if comp == nil {
return nil, ErrAlgorithm
}
var err error
fw.comp, err = comp(fw.compCount)
if err != nil {
return nil, err
}
fw.rawCount = &countWriter{w: fw.comp}
fw.header = h
ow = fw
}
w.dir = append(w.dir, h)
if err := writeHeader(w.cw, fh); err != nil {
return nil, err
}
// If we're creating a directory, fw is nil.
w.last = fw
return fw, nil
return ow, nil
}
func writeHeader(w io.Writer, h *FileHeader) error {
@ -400,6 +423,15 @@ func (w *Writer) compressor(method uint16) Compressor {
return comp
}
type dirWriter struct{}
func (dirWriter) Write(b []byte) (int, error) {
if len(b) == 0 {
return 0, nil
}
return 0, errors.New("zip: write to directory")
}
type fileWriter struct {
*header
zipw io.Writer

View File

@ -6,6 +6,7 @@ package zip
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
@ -299,6 +300,59 @@ func TestWriterFlush(t *testing.T) {
}
}
func TestWriterDir(t *testing.T) {
w := NewWriter(ioutil.Discard)
dw, err := w.Create("dir/")
if err != nil {
t.Fatal(err)
}
if _, err := dw.Write(nil); err != nil {
t.Errorf("Write(nil) to directory: got %v, want nil", err)
}
if _, err := dw.Write([]byte("hello")); err == nil {
t.Error(`Write("hello") to directory: got nil error, want non-nil`)
}
}
func TestWriterDirAttributes(t *testing.T) {
var buf bytes.Buffer
w := NewWriter(&buf)
if _, err := w.CreateHeader(&FileHeader{
Name: "dir/",
Method: Deflate,
CompressedSize64: 1234,
UncompressedSize64: 5678,
}); err != nil {
t.Fatal(err)
}
if err := w.Close(); err != nil {
t.Fatal(err)
}
b := buf.Bytes()
var sig [4]byte
binary.LittleEndian.PutUint32(sig[:], uint32(fileHeaderSignature))
idx := bytes.Index(b, sig[:])
if idx == -1 {
t.Fatal("file header not found")
}
b = b[idx:]
if !bytes.Equal(b[6:10], []byte{0, 0, 0, 0}) { // FileHeader.Flags: 0, FileHeader.Method: 0
t.Errorf("unexpected method and flags: %v", b[6:10])
}
if !bytes.Equal(b[14:26], make([]byte, 12)) { // FileHeader.{CRC32,CompressSize,UncompressedSize} all zero.
t.Errorf("unexpected crc, compress and uncompressed size to be 0 was: %v", b[14:26])
}
binary.LittleEndian.PutUint32(sig[:], uint32(dataDescriptorSignature))
if bytes.Index(b, sig[:]) != -1 {
t.Error("there should be no data descriptor")
}
}
func testCreate(t *testing.T, w *Writer, wt *WriteTest) {
header := &FileHeader{
Name: wt.Name,

View File

@ -15,6 +15,7 @@ import (
"internal/testenv"
"io"
"io/ioutil"
"runtime"
"sort"
"strings"
"testing"
@ -140,14 +141,7 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) {
rp = &r.buf[len(r.buf)-1]
// Fast path, if p is entirely the same byte repeated.
if lastByte := rp.b; len(p) > 0 && p[0] == lastByte {
all := true
for _, b := range p {
if b != lastByte {
all = false
break
}
}
if all {
if bytes.Count(p, []byte{lastByte}) == len(p) {
rp.n += int64(len(p))
return len(p), nil
}
@ -165,6 +159,25 @@ func (r *rleBuffer) Write(p []byte) (n int, err error) {
return len(p), nil
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func memset(a []byte, b byte) {
if len(a) == 0 {
return
}
// Double, until we reach power of 2 >= len(a), same as bytes.Repeat,
// but without allocation.
a[0] = b
for i, l := 1, len(a); i < l; i *= 2 {
copy(a[i:], a[:i])
}
}
func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
if len(p) == 0 {
return
@ -176,16 +189,13 @@ func (r *rleBuffer) ReadAt(p []byte, off int64) (n int, err error) {
parts := r.buf[skipParts:]
if len(parts) > 0 {
skipBytes := off - parts[0].off
for len(parts) > 0 {
part := parts[0]
for i := skipBytes; i < part.n; i++ {
if n == len(p) {
return
}
p[n] = part.b
n++
for _, part := range parts {
repeat := min(int(part.n-skipBytes), len(p)-n)
memset(p[n:n+repeat], part.b)
n += repeat
if n == len(p) {
return
}
parts = parts[1:]
skipBytes = 0
}
}
@ -452,6 +462,9 @@ func suffixIsZip64(t *testing.T, zip sizedReaderAt) bool {
// Zip64 is required if the total size of the records is uint32max.
func TestZip64LargeDirectory(t *testing.T) {
if runtime.GOARCH == "wasm" {
t.Skip("too slow on wasm")
}
if testing.Short() {
t.Skip("skipping in short mode")
}

View File

@ -462,6 +462,8 @@ func (b *Reader) ReadString(delim byte) (string, error) {
// WriteTo implements io.WriterTo.
// This may make multiple calls to the Read method of the underlying Reader.
// If the underlying reader supports the WriteTo method,
// this calls the underlying WriteTo without buffering.
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
n, err = b.writeBuf(w)
if err != nil {
@ -684,7 +686,9 @@ func (b *Writer) WriteString(s string) (int, error) {
return nn, nil
}
// ReadFrom implements io.ReaderFrom.
// ReadFrom implements io.ReaderFrom. If the underlying writer
// supports the ReadFrom method, and b has no buffered data yet,
// this calls the underlying ReadFrom without buffering.
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
if b.Buffered() == 0 {
if w, ok := b.wr.(io.ReaderFrom); ok {

View File

@ -45,14 +45,19 @@ type Scanner struct {
// input. The arguments are an initial substring of the remaining unprocessed
// data and a flag, atEOF, that reports whether the Reader has no more data
// to give. The return values are the number of bytes to advance the input
// and the next token to return to the user, plus an error, if any. If the
// data does not yet hold a complete token, for instance if it has no newline
// while scanning lines, SplitFunc can return (0, nil, nil) to signal the
// Scanner to read more data into the slice and try again with a longer slice
// starting at the same point in the input.
// and the next token to return to the user, if any, plus an error, if any.
//
// If the returned error is non-nil, scanning stops and the error
// is returned to the client.
// Scanning stops if the function returns an error, in which case some of
// the input may be discarded.
//
// Otherwise, the Scanner advances the input. If the token is not nil,
// the Scanner returns it to the user. If the token is nil, the
// Scanner reads more data and continues scanning; if there is no more
// data--if atEOF was true--the Scanner returns. If the data does not
// yet hold a complete token, for instance if it has no newline while
// scanning lines, a SplitFunc can return (0, nil, nil) to signal the
// Scanner to read more data into the slice and try again with a
// longer slice starting at the same point in the input.
//
// The function is never called with an empty data slice unless atEOF
// is true. If atEOF is true, however, data may be non-empty and,

View File

@ -202,6 +202,7 @@ func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
b.lastRead = opInvalid
for {
i := b.grow(MinRead)
b.buf = b.buf[:i]
m, e := r.Read(b.buf[i:cap(b.buf)])
if m < 0 {
panic(errNegativeRead)

View File

@ -269,6 +269,39 @@ func TestReadFrom(t *testing.T) {
}
}
type panicReader struct{ panic bool }
func (r panicReader) Read(p []byte) (int, error) {
if r.panic {
panic(nil)
}
return 0, io.EOF
}
// Make sure that an empty Buffer remains empty when
// it is "grown" before a Read that panics
func TestReadFromPanicReader(t *testing.T) {
// First verify non-panic behaviour
var buf Buffer
i, err := buf.ReadFrom(panicReader{})
if err != nil {
t.Fatal(err)
}
if i != 0 {
t.Fatalf("unexpected return from bytes.ReadFrom (1): got: %d, want %d", i, 0)
}
check(t, "TestReadFromPanicReader (1)", &buf, "")
// Confirm that when Reader panics, the emtpy buffer remains empty
var buf2 Buffer
defer func() {
recover()
check(t, "TestReadFromPanicReader (2)", &buf2, "")
}()
buf2.ReadFrom(panicReader{panic: true})
}
func TestReadFromNegativeReader(t *testing.T) {
var b Buffer
defer func() {

View File

@ -7,6 +7,7 @@
package bytes
import (
"internal/bytealg"
"unicode"
"unicode/utf8"
)
@ -46,12 +47,16 @@ func explode(s []byte, n int) [][]byte {
return a[0:na]
}
// countGeneric actually implements Count
func countGeneric(s, sep []byte) int {
// Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
func Count(s, sep []byte) int {
// special case
if len(sep) == 0 {
return utf8.RuneCount(s) + 1
}
if len(sep) == 1 {
return bytealg.Count(s, sep[0])
}
n := 0
for {
i := Index(s, sep)
@ -800,9 +805,9 @@ func EqualFold(s, t []byte) bool {
tr, sr = sr, tr
}
// Fast check for ASCII.
if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
// ASCII, and sr is upper case. tr must be lower case.
if tr == sr+'a'-'A' {
if tr < utf8.RuneSelf {
// ASCII only, sr/tr must be upper/lower case
if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
continue
}
return false
@ -824,6 +829,92 @@ func EqualFold(s, t []byte) bool {
return len(s) == len(t)
}
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return 0
case n == 1:
return IndexByte(s, sep[0])
case n == len(s):
if Equal(sep, s) {
return 0
}
return -1
case n > len(s):
return -1
case n <= bytealg.MaxLen:
// Use brute force when s and sep both are small
if len(s) <= bytealg.MaxBruteForce {
return bytealg.Index(s, sep)
}
c := sep[0]
i := 0
t := s[:len(s)-n+1]
fails := 0
for i < len(t) {
if t[i] != c {
// IndexByte is faster than bytealg.Index, so use it as long as
// we're not getting lots of false positives.
o := IndexByte(t[i:], c)
if o < 0 {
return -1
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
fails++
i++
// Switch to bytealg.Index when IndexByte produces too many false positives.
if fails > bytealg.Cutover(i) {
r := bytealg.Index(s[i:], sep)
if r >= 0 {
return r + i
}
return -1
}
}
return -1
}
c := sep[0]
i := 0
fails := 0
t := s[:len(s)-n+1]
for i < len(t) {
if t[i] != c {
o := IndexByte(t[i:], c)
if o < 0 {
break
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
i++
fails++
if fails >= 4+i>>4 && i < len(t) {
// Give up on IndexByte, it isn't skipping ahead
// far enough to be better than Rabin-Karp.
// Experiments (using IndexPeriodic) suggest
// the cutover is about 16 byte skips.
// TODO: if large prefixes of sep are matching
// we should cutover at even larger average skips,
// because Equal becomes that much more expensive.
// This code does not take that effect into account.
j := indexRabinKarp(s[i:], sep)
if j < 0 {
return -1
}
return i + j
}
}
return -1
}
func indexRabinKarp(s, sep []byte) int {
// Rabin-Karp search
hashsep, pow := hashStr(sep)

View File

@ -1,90 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package bytes
import "internal/cpu"
//go:noescape
// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
// indexShortStr requires 2 <= len(c) <= shortStringLen
func indexShortStr(s, c []byte) int // ../runtime/asm_amd64.s
func countByte(s []byte, c byte) int // ../runtime/asm_amd64.s
var shortStringLen int
func init() {
if cpu.X86.HasAVX2 {
shortStringLen = 63
} else {
shortStringLen = 31
}
}
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return 0
case n == 1:
return IndexByte(s, sep[0])
case n == len(s):
if Equal(sep, s) {
return 0
}
return -1
case n > len(s):
return -1
case n <= shortStringLen:
// Use brute force when s and sep both are small
if len(s) <= 64 {
return indexShortStr(s, sep)
}
c := sep[0]
i := 0
t := s[:len(s)-n+1]
fails := 0
for i < len(t) {
if t[i] != c {
// IndexByte skips 16/32 bytes per iteration,
// so it's faster than indexShortStr.
o := IndexByte(t[i:], c)
if o < 0 {
return -1
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
fails++
i++
// Switch to indexShortStr when IndexByte produces too many false positives.
// Too many means more that 1 error per 8 characters.
// Allow some errors in the beginning.
if fails > (i+16)/8 {
r := indexShortStr(s[i:], sep)
if r >= 0 {
return r + i
}
return -1
}
}
return -1
}
return indexRabinKarp(s, sep)
}
// Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
func Count(s, sep []byte) int {
if len(sep) == 1 && cpu.X86.HasPOPCNT {
return countByte(s, sep[0])
}
return countGeneric(s, sep)
}

View File

@ -1,70 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package bytes
func countByte(s []byte, c byte) int // bytes_arm64.s
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return 0
case n == 1:
return IndexByte(s, sep[0])
case n == len(s):
if Equal(sep, s) {
return 0
}
return -1
case n > len(s):
return -1
}
c := sep[0]
i := 0
fails := 0
t := s[:len(s)-n+1]
for i < len(t) {
if t[i] != c {
o := IndexByte(t[i:], c)
if o < 0 {
break
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
i++
fails++
if fails >= 4+i>>4 && i < len(t) {
// Give up on IndexByte, it isn't skipping ahead
// far enough to be better than Rabin-Karp.
// Experiments (using IndexPeriodic) suggest
// the cutover is about 16 byte skips.
// TODO: if large prefixes of sep are matching
// we should cutover at even larger average skips,
// because Equal becomes that much more expensive.
// This code does not take that effect into account.
j := indexRabinKarp(s[i:], sep)
if j < 0 {
return -1
}
return i + j
}
}
return -1
}
// Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
func Count(s, sep []byte) int {
if len(sep) == 1 {
return countByte(s, sep[0])
}
return countGeneric(s, sep)
}

View File

@ -6,19 +6,19 @@ package bytes
//go:noescape
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
func IndexByte(s []byte, c byte) int // ../runtime/asm_$GOARCH.s
// IndexByte returns the index of the first instance of c in b, or -1 if c is not present in b.
func IndexByte(b []byte, c byte) int // in internal/bytealg
//go:noescape
// Equal returns a boolean reporting whether a and b
// are the same length and contain the same bytes.
// A nil argument is equivalent to an empty slice.
func Equal(a, b []byte) bool // ../runtime/asm_$GOARCH.s
func Equal(a, b []byte) bool // in internal/bytealg
//go:noescape
// 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.
// A nil argument is equivalent to an empty slice.
func Compare(a, b []byte) int // ../runtime/noasm.go or ../runtime/asm_{386,amd64}.s
func Compare(a, b []byte) int // in internal/bytealg

View File

@ -1,65 +0,0 @@
// 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.
// -build !amd64,!s390x,!arm64
package bytes
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return 0
case n == 1:
return IndexByte(s, sep[0])
case n == len(s):
if Equal(sep, s) {
return 0
}
return -1
case n > len(s):
return -1
}
c := sep[0]
i := 0
fails := 0
t := s[:len(s)-n+1]
for i < len(t) {
if t[i] != c {
o := IndexByte(t[i:], c)
if o < 0 {
break
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
i++
fails++
if fails >= 4+i>>4 && i < len(t) {
// Give up on IndexByte, it isn't skipping ahead
// far enough to be better than Rabin-Karp.
// Experiments (using IndexPeriodic) suggest
// the cutover is about 16 byte skips.
// TODO: if large prefixes of sep are matching
// we should cutover at even larger average skips,
// because Equal becomes that much more expensive.
// This code does not take that effect into account.
j := indexRabinKarp(s[i:], sep)
if j < 0 {
return -1
}
return i + j
}
}
return -1
}
// Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
func Count(s, sep []byte) int {
return countGeneric(s, sep)
}

View File

@ -1,88 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build ignore
package bytes
//go:noescape
// indexShortStr returns the index of the first instance of sep in s,
// or -1 if sep is not present in s.
// indexShortStr requires 2 <= len(sep) <= shortStringLen
func indexShortStr(s, c []byte) int // ../runtime/asm_s390x.s
// supportsVX reports whether the vector facility is available.
// indexShortStr must not be called if the vector facility is not
// available.
func supportsVX() bool // ../runtime/asm_s390x.s
var shortStringLen = -1
func init() {
if supportsVX() {
shortStringLen = 64
}
}
// Index returns the index of the first instance of sep in s, or -1 if sep is not present in s.
func Index(s, sep []byte) int {
n := len(sep)
switch {
case n == 0:
return 0
case n == 1:
return IndexByte(s, sep[0])
case n == len(s):
if Equal(sep, s) {
return 0
}
return -1
case n > len(s):
return -1
case n <= shortStringLen:
// Use brute force when s and sep both are small
if len(s) <= 64 {
return indexShortStr(s, sep)
}
c := sep[0]
i := 0
t := s[:len(s)-n+1]
fails := 0
for i < len(t) {
if t[i] != c {
// IndexByte skips 16/32 bytes per iteration,
// so it's faster than indexShortStr.
o := IndexByte(t[i:], c)
if o < 0 {
return -1
}
i += o
}
if Equal(s[i:i+n], sep) {
return i
}
fails++
i++
// Switch to indexShortStr when IndexByte produces too many false positives.
// Too many means more that 1 error per 8 characters.
// Allow some errors in the beginning.
if fails > (i+16)/8 {
r := indexShortStr(s[i:], sep)
if r >= 0 {
return r + i
}
return -1
}
}
return -1
}
return indexRabinKarp(s, sep)
}
// Count counts the number of non-overlapping instances of sep in s.
// If sep is an empty slice, Count returns 1 + the number of UTF-8-encoded code points in s.
func Count(s, sep []byte) int {
return countGeneric(s, sep)
}

View File

@ -415,10 +415,6 @@ func TestCountByte(t *testing.T) {
if p != j+1 {
t.Errorf("TestCountByte.Count(%q, 100) = %d", b[i:i+window], p)
}
pGeneric := CountGeneric(b[i:i+window], []byte{100})
if pGeneric != j+1 {
t.Errorf("TestCountByte.CountGeneric(%q, 100) = %d", b[i:i+window], p)
}
}
}
@ -466,10 +462,6 @@ func TestCountByteNoMatch(t *testing.T) {
if p != 0 {
t.Errorf("TestCountByteNoMatch(%q, 0) = %d", b[i:i+window], p)
}
pGeneric := CountGeneric(b[i:i+window], []byte{0})
if pGeneric != 0 {
t.Errorf("TestCountByteNoMatch.CountGeneric(%q, 100) = %d", b[i:i+window], p)
}
for j := 0; j < window; j++ {
b[i+j] = byte(0)
}

View File

@ -6,6 +6,7 @@ package bytes_test
import (
. "bytes"
"internal/testenv"
"testing"
)
@ -58,10 +59,20 @@ func TestCompareIdenticalSlice(t *testing.T) {
}
func TestCompareBytes(t *testing.T) {
n := 128
lengths := make([]int, 0) // lengths to test in ascending order
for i := 0; i <= 128; i++ {
lengths = append(lengths, i)
}
lengths = append(lengths, 256, 512, 1024, 1333, 4095, 4096, 4097)
if !testing.Short() || testenv.Builder() != "" {
lengths = append(lengths, 65535, 65536, 65537, 99999)
}
n := lengths[len(lengths)-1]
a := make([]byte, n+1)
b := make([]byte, n+1)
for len := 0; len < 128; len++ {
for _, len := range lengths {
// randomish but deterministic data. No 0 or 255.
for i := 0; i < len; i++ {
a[i] = byte(1 + 31*i%254)

View File

@ -7,4 +7,3 @@ package bytes
// Export func for testing
var IndexBytePortable = indexBytePortable
var EqualPortable = equalPortable
var CountGeneric = countGeneric

View File

@ -22,6 +22,21 @@ func usage() {
var wflag = flag.Bool("w", false, "write build ID")
// taken from cmd/go/internal/work/buildid.go
func hashToString(h [32]byte) string {
const b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
const chunks = 5
var dst [chunks * 4]byte
for i := 0; i < chunks; i++ {
v := uint32(h[3*i])<<16 | uint32(h[3*i+1])<<8 | uint32(h[3*i+2])
dst[4*i+0] = b64[(v>>18)&0x3F]
dst[4*i+1] = b64[(v>>12)&0x3F]
dst[4*i+2] = b64[(v>>6)&0x3F]
dst[4*i+3] = b64[v&0x3F]
}
return string(dst[:])
}
func main() {
log.SetPrefix("buildid: ")
log.SetFlags(0)
@ -41,6 +56,8 @@ func main() {
return
}
// Keep in sync with src/cmd/go/internal/work/buildid.go:updateBuildID
f, err := os.Open(file)
if err != nil {
log.Fatal(err)
@ -51,14 +68,14 @@ func main() {
}
f.Close()
tail := id
if i := strings.LastIndex(id, "."); i >= 0 {
tail = tail[i+1:]
newID := id[:strings.LastIndex(id, "/")] + "/" + hashToString(hash)
if len(newID) != len(id) {
log.Fatalf("%s: build ID length mismatch %q vs %q", file, id, newID)
}
if len(tail) != len(hash)*2 {
log.Fatalf("%s: cannot find %d-byte hash in id %s", file, len(hash), id)
if len(matches) == 0 {
return
}
newID := id[:len(id)-len(tail)] + fmt.Sprintf("%x", hash)
f, err = os.OpenFile(file, os.O_WRONLY, 0)
if err != nil {

View File

@ -95,7 +95,7 @@ func (f *File) ParseGo(name string, src []byte) {
}
}
if !sawC {
error_(token.NoPos, `cannot find import "C"`)
error_(ast1.Package, `cannot find import "C"`)
}
// In ast2, strip the import "C" line.
@ -356,6 +356,7 @@ func (f *File) walk(x interface{}, context astContext, visit func(*File, interfa
case *ast.BadExpr:
case *ast.Ident:
case *ast.Ellipsis:
f.walk(&n.Elt, ctxType, visit)
case *ast.BasicLit:
case *ast.FuncLit:
f.walk(n.Type, ctxType, visit)

View File

@ -64,6 +64,11 @@ a full argument: to allow -mfoo=bar, use CGO_CFLAGS_ALLOW='-mfoo.*',
not just CGO_CFLAGS_ALLOW='-mfoo'. Similarly named variables control
the allowed CPPFLAGS, CXXFLAGS, FFLAGS, and LDFLAGS.
Also for security reasons, only a limited set of characters are
permitted, notably alphanumeric characters and a few symbols, such as
'.', that will not be interpreted in unexpected ways. Attempts to use
forbidden characters will get a "malformed #cgo argument" error.
When building, the CGO_CFLAGS, CGO_CPPFLAGS, CGO_CXXFLAGS, CGO_FFLAGS and
CGO_LDFLAGS environment variables are added to the flags derived from
these directives. Package-specific flags should be set using the
@ -99,17 +104,24 @@ compiled with the C compiler. Any .cc, .cpp, or .cxx files will be
compiled with the C++ compiler. Any .f, .F, .for or .f90 files will be
compiled with the fortran compiler. Any .h, .hh, .hpp, or .hxx files will
not be compiled separately, but, if these header files are changed,
the C and C++ files will be recompiled. The default C and C++
compilers may be changed by the CC and CXX environment variables,
respectively; those environment variables may include command line
options.
the package (including its non-Go source files) will be recompiled.
Note that changes to files in other directories do not cause the package
to be recompiled, so all non-Go source code for the package should be
stored in the package directory, not in subdirectories.
The default C and C++ compilers may be changed by the CC and CXX
environment variables, respectively; those environment variables
may include command line options.
The cgo tool is enabled by default for native builds on systems where
it is expected to work. It is disabled by default when
cross-compiling. You can control this by setting the CGO_ENABLED
environment variable when running the go tool: set it to 1 to enable
the use of cgo, and to 0 to disable it. The go tool will set the
build constraint "cgo" if cgo is enabled.
build constraint "cgo" if cgo is enabled. The special import "C"
implies the "cgo" build constraint, as though the file also said
"// +build cgo". Therefore, if cgo is disabled, files that import
"C" will not be built by the go tool. (For more about build constraints
see https://golang.org/pkg/go/build/#hdr-Build_Constraints).
When cross-compiling, you must specify a C cross-compiler for cgo to
use. You can do this by setting the generic CC_FOR_TARGET or the
@ -219,6 +231,26 @@ C compilers are aware of this calling convention and adjust
the call accordingly, but Go cannot. In Go, you must pass
the pointer to the first element explicitly: C.f(&C.x[0]).
Calling variadic C functions is not supported. It is possible to
circumvent this by using a C function wrapper. For example:
package main
// #include <stdio.h>
// #include <stdlib.h>
//
// static void myprint(char* s) {
// printf("%s\n", s);
// }
import "C"
import "unsafe"
func main() {
cs := C.CString("Hello from stdio")
C.myprint(cs)
C.free(unsafe.Pointer(cs))
}
A few special functions convert between Go and C types
by making copies of the data. In pseudo-Go definitions:
@ -348,6 +380,14 @@ and of course there is nothing stopping the C code from doing anything
it likes. However, programs that break these rules are likely to fail
in unexpected and unpredictable ways.
Note: the current implementation has a bug. While Go code is permitted
to write nil or a C pointer (but not a Go pointer) to C memory, the
current implementation may sometimes cause a runtime error if the
contents of the C memory appear to be a Go pointer. Therefore, avoid
passing uninitialized C memory to Go code if the Go code is going to
store pointer values in it. Zero out the memory in C before passing it
to Go.
Special cases
A few special C types which would normally be represented by a pointer

View File

@ -183,9 +183,29 @@ func (p *Package) Translate(f *File) {
cref.Name.C = cname(cref.Name.Go)
}
p.loadDefines(f)
needType := p.guessKinds(f)
if len(needType) > 0 {
p.loadDWARF(f, needType)
p.typedefs = map[string]bool{}
p.typedefList = nil
numTypedefs := -1
for len(p.typedefs) > numTypedefs {
numTypedefs = len(p.typedefs)
// Also ask about any typedefs we've seen so far.
for _, a := range p.typedefList {
f.Name[a] = &Name{
Go: a,
C: a,
}
}
needType := p.guessKinds(f)
if len(needType) > 0 {
p.loadDWARF(f, needType)
}
// In godefs mode we're OK with the typedefs, which
// will presumably also be defined in the file, we
// don't want to resolve them to their base types.
if *godefs {
break
}
}
if p.rewriteCalls(f) {
// Add `import _cgo_unsafe "unsafe"` after the package statement.
@ -570,6 +590,7 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
fatalf("malformed __cgo__ name: %s", name)
}
types[i] = t.Type
p.recordTypedefs(t.Type)
}
if e.Tag != dwarf.TagCompileUnit {
r.SkipChildren()
@ -605,7 +626,25 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
}
}
case "fconst":
if i < len(floats) {
if i >= len(floats) {
break
}
switch base(types[i]).(type) {
case *dwarf.IntType, *dwarf.UintType:
// This has an integer type so it's
// not really a floating point
// constant. This can happen when the
// C compiler complains about using
// the value as an integer constant,
// but not as a general constant.
// Treat this as a variable of the
// appropriate type, not a constant,
// to get C-style type handling,
// avoiding the problem that C permits
// uint64(-1) but Go does not.
// See issue 26066.
n.Kind = "var"
default:
n.Const = fmt.Sprintf("%f", floats[i])
}
case "sconst":
@ -618,6 +657,47 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
}
}
// recordTypedefs remembers in p.typedefs all the typedefs used in dtypes and its children.
func (p *Package) recordTypedefs(dtype dwarf.Type) {
p.recordTypedefs1(dtype, map[dwarf.Type]bool{})
}
func (p *Package) recordTypedefs1(dtype dwarf.Type, visited map[dwarf.Type]bool) {
if dtype == nil {
return
}
if visited[dtype] {
return
}
visited[dtype] = true
switch dt := dtype.(type) {
case *dwarf.TypedefType:
if strings.HasPrefix(dt.Name, "__builtin") {
// Don't look inside builtin types. There be dragons.
return
}
if !p.typedefs[dt.Name] {
p.typedefs[dt.Name] = true
p.typedefList = append(p.typedefList, dt.Name)
p.recordTypedefs1(dt.Type, visited)
}
case *dwarf.PtrType:
p.recordTypedefs1(dt.Type, visited)
case *dwarf.ArrayType:
p.recordTypedefs1(dt.Type, visited)
case *dwarf.QualType:
p.recordTypedefs1(dt.Type, visited)
case *dwarf.FuncType:
p.recordTypedefs1(dt.ReturnType, visited)
for _, a := range dt.ParamType {
p.recordTypedefs1(a, visited)
}
case *dwarf.StructType:
for _, f := range dt.Field {
p.recordTypedefs1(f.Type, visited)
}
}
}
// mangleName does name mangling to translate names
// from the original Go source files to the names
// used in the final Go files generated by cgo.
@ -1373,7 +1453,7 @@ func (p *Package) gccDebug(stdin []byte, nnames int) (d *dwarf.Data, ints []int6
if len(data) <= strlen {
fatalf("invalid string literal")
}
strs[n] = string(data[:strlen])
strs[n] = data[:strlen]
}
}
@ -1754,6 +1834,7 @@ type typeConv struct {
// Map from types to incomplete pointers to those types.
ptrs map[dwarf.Type][]*Type
// Keys of ptrs in insertion order (deterministic worklist)
// ptrKeys contains exactly the keys in ptrs.
ptrKeys []dwarf.Type
// Type names X for which there exists an XGetTypeID function with type func() CFTypeID.
@ -1896,14 +1977,15 @@ func (c *typeConv) FinishType(pos token.Pos) {
for len(c.ptrKeys) > 0 {
dtype := c.ptrKeys[0]
c.ptrKeys = c.ptrKeys[1:]
ptrs := c.ptrs[dtype]
delete(c.ptrs, dtype)
// Note Type might invalidate c.ptrs[dtype].
t := c.Type(dtype, pos)
for _, ptr := range c.ptrs[dtype] {
for _, ptr := range ptrs {
ptr.Go.(*ast.StarExpr).X = t.Go
ptr.C.Set("%s*", t.C)
}
c.ptrs[dtype] = nil // retain the map key
}
}
@ -2180,6 +2262,10 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type {
s := *sub
s.Go = c.uintptr
sub = &s
// Make sure we update any previously computed type.
if oldType := typedef[name.Name]; oldType != nil {
oldType.Go = sub.Go
}
}
t.Go = name
if unionWithPointer[sub.Go] {
@ -2341,7 +2427,7 @@ func (c *typeConv) FuncArg(dtype dwarf.Type, pos token.Pos) *Type {
}
// ...or the typedef is one in which we expect bad pointers.
// It will be a uintptr instead of *X.
if c.badPointerTypedef(dt) {
if c.baseBadPointerTypedef(dt) {
break
}
@ -2693,6 +2779,19 @@ func (c *typeConv) badPointerTypedef(dt *dwarf.TypedefType) bool {
return false
}
// baseBadPointerTypedef reports whether the base of a chain of typedefs is a bad typedef
// as badPointerTypedef reports.
func (c *typeConv) baseBadPointerTypedef(dt *dwarf.TypedefType) bool {
for {
if t, ok := dt.Type.(*dwarf.TypedefType); ok {
dt = t
continue
}
break
}
return c.badPointerTypedef(dt)
}
func (c *typeConv) badCFType(dt *dwarf.TypedefType) bool {
// The real bad types are CFNumberRef and CFDateRef.
// Sometimes non-pointers are stored in these types.
@ -2781,13 +2880,31 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
}
}
// Check that the typedef is:
// struct _jobject;
// typedef struct _jobject *jobject;
// Check that the typedef is either:
// 1:
// struct _jobject;
// typedef struct _jobject *jobject;
// 2: (in NDK16 in C++)
// class _jobject {};
// typedef _jobject* jobject;
// 3: (in NDK16 in C)
// typedef void* jobject;
if ptr, ok := w.Type.(*dwarf.PtrType); ok {
if str, ok := ptr.Type.(*dwarf.StructType); ok {
if str.StructName == "_jobject" && str.Kind == "struct" && len(str.Field) == 0 && str.Incomplete {
return true
switch v := ptr.Type.(type) {
case *dwarf.VoidType:
return true
case *dwarf.StructType:
if v.StructName == "_jobject" && len(v.Field) == 0 {
switch v.Kind {
case "struct":
if v.Incomplete {
return true
}
case "class":
if !v.Incomplete {
return true
}
}
}
}
}
@ -2796,7 +2913,7 @@ func (c *typeConv) badJNI(dt *dwarf.TypedefType) bool {
}
// jniTypes maps from JNI types that we want to be uintptrs, to the underlying type to which
// they are mapped. The base "jobject" maps to the empty string.
// they are mapped. The base "jobject" maps to the empty string.
var jniTypes = map[string]string{
"jobject": "",
"jclass": "jobject",

View File

@ -19,7 +19,7 @@ import (
func (p *Package) godefs(f *File, srcfile string) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
fmt.Fprintf(&buf, "// Code generated by cmd/cgo -godefs; DO NOT EDIT.\n")
fmt.Fprintf(&buf, "// %s %s\n", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
fmt.Fprintf(&buf, "\n")

View File

@ -17,6 +17,7 @@ import (
"go/ast"
"go/printer"
"go/token"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -42,9 +43,11 @@ type Package struct {
Name map[string]*Name // accumulated Name from Files
ExpFunc []*ExpFunc // accumulated ExpFunc from Files
Decl []ast.Decl
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
GoFiles []string // list of Go files
GccFiles []string // list of gcc output files
Preamble string // collected preamble for _cgo_export.h
typedefs map[string]bool // type names that appear in the types of the objects we're interested in
typedefList []string
}
// A File collects information about a single Go input file.
@ -278,6 +281,9 @@ func main() {
if arg == "-fsanitize=thread" {
tsanProlog = yesTsanProlog
}
if arg == "-fsanitize=memory" {
msanProlog = yesMsanProlog
}
}
p := newPackage(args[:i])
@ -297,6 +303,7 @@ func main() {
// concern is other cgo wrappers for the same functions.
// Use the beginning of the md5 of the input to disambiguate.
h := md5.New()
io.WriteString(h, *importPath)
fs := make([]*File, len(goFiles))
for i, input := range goFiles {
if *srcDir != "" {
@ -410,6 +417,14 @@ func (p *Package) Record(f *File) {
for k, v := range f.Name {
if p.Name[k] == nil {
p.Name[k] = v
} else if p.incompleteTypedef(p.Name[k].Type) {
p.Name[k] = v
} else if p.incompleteTypedef(v.Type) {
// Nothing to do.
} else if _, ok := nameToC[k]; ok {
// Names we predefine may appear inconsistent
// if some files typedef them and some don't.
// Issue 26743.
} else if !reflect.DeepEqual(p.Name[k], v) {
error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
}
@ -422,3 +437,9 @@ func (p *Package) Record(f *File) {
}
p.Decl = append(p.Decl, f.AST.Decls...)
}
// incompleteTypedef reports whether t appears to be an incomplete
// typedef definition.
func (p *Package) incompleteTypedef(t *Type) bool {
return t == nil || (t.Size == 0 && t.Align == -1)
}

View File

@ -17,6 +17,7 @@ import (
"io"
"os"
"path/filepath"
"regexp"
"sort"
"strings"
)
@ -78,7 +79,7 @@ func (p *Package) writeDefs() {
// Write second Go output: definitions of _C_xxx.
// In a separate file so that the import of "unsafe" does not
// pollute the original file.
fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo2, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName)
fmt.Fprintf(fgo2, "import \"unsafe\"\n\n")
if !*gccgo && *importRuntimeCgo {
@ -277,10 +278,7 @@ func dynimport(obj string) {
}
}
}
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from ELF file %s: %v", obj, err)
}
sym, _ := f.ImportedSymbols()
for _, s := range sym {
targ := s.Name
if s.Version != "" {
@ -288,10 +286,7 @@ func dynimport(obj string) {
}
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
}
lib, _ := f.ImportedLibraries()
for _, l := range lib {
fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
}
@ -299,20 +294,14 @@ func dynimport(obj string) {
}
if f, err := macho.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err)
}
sym, _ := f.ImportedSymbols()
for _, s := range sym {
if len(s) > 0 && s[0] == '_' {
s = s[1:]
}
fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
}
lib, err := f.ImportedLibraries()
if err != nil {
fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
}
lib, _ := f.ImportedLibraries()
for _, l := range lib {
fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
}
@ -320,10 +309,7 @@ func dynimport(obj string) {
}
if f, err := pe.Open(obj); err == nil {
sym, err := f.ImportedSymbols()
if err != nil {
fatalf("cannot load imported symbols from PE file %s: %v", obj, err)
}
sym, _ := f.ImportedSymbols()
for _, s := range sym {
ss := strings.Split(s, ":")
name := strings.Split(ss[0], "@")[0]
@ -559,8 +545,8 @@ func (p *Package) writeOutput(f *File, srcfile string) {
p.GccFiles = append(p.GccFiles, base+".cgo2.c")
// Write Go output: Go input with rewrites of C.xxx to _C_xxx.
fmt.Fprintf(fgo1, "// Created by cgo - DO NOT EDIT\n\n")
fmt.Fprintf(fgo1, "//line %s:1\n", srcfile)
fmt.Fprintf(fgo1, "// Code generated by cmd/cgo; DO NOT EDIT.\n\n")
fmt.Fprintf(fgo1, "//line %s:1:1\n", srcfile)
fgo1.Write(f.Edit.Bytes())
// While we process the vars and funcs, also write gcc output.
@ -569,6 +555,7 @@ func (p *Package) writeOutput(f *File, srcfile string) {
fmt.Fprintf(fgcc, "%s\n", f.Preamble)
fmt.Fprintf(fgcc, "%s\n", gccProlog)
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
fmt.Fprintf(fgcc, "%s\n", msanProlog)
for _, key := range nameKeys(f.Name) {
n := f.Name[key]
@ -632,14 +619,14 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// We're trying to write a gcc struct that matches gc's layout.
// Use packed attribute to force no padding in this struct in case
// gcc has different packing requirements.
fmt.Fprintf(fgcc, "\t%s %v *a = v;\n", ctype, p.packedAttribute())
fmt.Fprintf(fgcc, "\t%s %v *_cgo_a = v;\n", ctype, p.packedAttribute())
if n.FuncType.Result != nil {
// Save the stack top for use below.
fmt.Fprintf(fgcc, "\tchar *stktop = _cgo_topofstack();\n")
fmt.Fprintf(fgcc, "\tchar *_cgo_stktop = _cgo_topofstack();\n")
}
tr := n.FuncType.Result
if tr != nil {
fmt.Fprintf(fgcc, "\t__typeof__(a->r) r;\n")
fmt.Fprintf(fgcc, "\t__typeof__(_cgo_a->r) _cgo_r;\n")
}
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
if n.AddError {
@ -647,9 +634,9 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
}
fmt.Fprintf(fgcc, "\t")
if tr != nil {
fmt.Fprintf(fgcc, "r = ")
fmt.Fprintf(fgcc, "_cgo_r = ")
if c := tr.C.String(); c[len(c)-1] == '*' {
fmt.Fprint(fgcc, "(__typeof__(a->r)) ")
fmt.Fprint(fgcc, "(__typeof__(_cgo_a->r)) ")
}
}
if n.Kind == "macro" {
@ -660,7 +647,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
if i > 0 {
fmt.Fprintf(fgcc, ", ")
}
fmt.Fprintf(fgcc, "a->p%d", i)
fmt.Fprintf(fgcc, "_cgo_a->p%d", i)
}
fmt.Fprintf(fgcc, ");\n")
}
@ -671,9 +658,19 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
if n.FuncType.Result != nil {
// The cgo call may have caused a stack copy (via a callback).
// Adjust the return value pointer appropriately.
fmt.Fprintf(fgcc, "\ta = (void*)((char*)a + (_cgo_topofstack() - stktop));\n")
fmt.Fprintf(fgcc, "\t_cgo_a = (void*)((char*)_cgo_a + (_cgo_topofstack() - _cgo_stktop));\n")
// Save the return value.
fmt.Fprintf(fgcc, "\ta->r = r;\n")
fmt.Fprintf(fgcc, "\t_cgo_a->r = _cgo_r;\n")
// The return value is on the Go stack. If we are using msan,
// and if the C value is partially or completely uninitialized,
// the assignment will mark the Go stack as uninitialized.
// The Go compiler does not update msan for changes to the
// stack. It is possible that the stack will remain
// uninitialized, and then later be used in a way that is
// visible to msan, possibly leading to a false positive.
// Mark the stack space as written, to avoid this problem.
// See issue 26209.
fmt.Fprintf(fgcc, "\t_cgo_msan_write(&_cgo_a->r, sizeof(_cgo_a->r));\n")
}
if n.AddError {
fmt.Fprintf(fgcc, "\treturn _cgo_errno;\n")
@ -708,12 +705,12 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
fmt.Fprintf(fgcc, ")\n")
fmt.Fprintf(fgcc, "{\n")
if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "\t%s r;\n", t.C.String())
fmt.Fprintf(fgcc, "\t%s _cgo_r;\n", t.C.String())
}
fmt.Fprintf(fgcc, "\t_cgo_tsan_acquire();\n")
fmt.Fprintf(fgcc, "\t")
if t := n.FuncType.Result; t != nil {
fmt.Fprintf(fgcc, "r = ")
fmt.Fprintf(fgcc, "_cgo_r = ")
// Cast to void* to avoid warnings due to omitted qualifiers.
if c := t.C.String(); c[len(c)-1] == '*' {
fmt.Fprintf(fgcc, "(void*)")
@ -739,7 +736,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
if c := t.C.String(); c[len(c)-1] == '*' {
fmt.Fprintf(fgcc, "(void*)")
}
fmt.Fprintf(fgcc, "r;\n")
fmt.Fprintf(fgcc, "_cgo_r;\n")
}
fmt.Fprintf(fgcc, "}\n")
fmt.Fprintf(fgcc, "\n")
@ -748,7 +745,7 @@ func (p *Package) writeGccgoOutputFunc(fgcc *os.File, n *Name) {
// packedAttribute returns host compiler struct attribute that will be
// used to match gc's struct layout. For example, on 386 Windows,
// gcc wants to 8-align int64s, but gc does not.
// Use __gcc_struct__ to work around http://gcc.gnu.org/PR52991 on x86,
// Use __gcc_struct__ to work around https://gcc.gnu.org/PR52991 on x86,
// and https://golang.org/issue/5603.
func (p *Package) packedAttribute() string {
s := "__attribute__((__packed__"
@ -763,7 +760,7 @@ func (p *Package) packedAttribute() string {
func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
p.writeExportHeader(fgcch)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
fmt.Fprintf(fgcc, "#include <stdlib.h>\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n\n")
@ -772,6 +769,7 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) {
fmt.Fprintf(fgcc, "extern void _cgo_release_context(__SIZE_TYPE__);\n\n")
fmt.Fprintf(fgcc, "extern char* _cgo_topofstack(void);")
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
fmt.Fprintf(fgcc, "%s\n", msanProlog)
for _, exp := range p.ExpFunc {
fn := exp.Func
@ -1004,11 +1002,12 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
p.writeExportHeader(fgcch)
fmt.Fprintf(fgcc, "/* Created by cgo - DO NOT EDIT. */\n")
fmt.Fprintf(fgcc, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
fmt.Fprintf(fgcc, "#include \"_cgo_export.h\"\n")
fmt.Fprintf(fgcc, "%s\n", gccgoExportFileProlog)
fmt.Fprintf(fgcc, "%s\n", tsanProlog)
fmt.Fprintf(fgcc, "%s\n", msanProlog)
for _, exp := range p.ExpFunc {
fn := exp.Func
@ -1170,7 +1169,7 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) {
// writeExportHeader writes out the start of the _cgo_export.h file.
func (p *Package) writeExportHeader(fgcch io.Writer) {
fmt.Fprintf(fgcch, "/* Created by \"go tool cgo\" - DO NOT EDIT. */\n\n")
fmt.Fprintf(fgcch, "/* Code generated by cmd/cgo; DO NOT EDIT. */\n\n")
pkg := *importPath
if pkg == "" {
pkg = p.PackagePath
@ -1178,8 +1177,15 @@ func (p *Package) writeExportHeader(fgcch io.Writer) {
fmt.Fprintf(fgcch, "/* package %s */\n\n", pkg)
fmt.Fprintf(fgcch, "%s\n", builtinExportProlog)
// Remove absolute paths from #line comments in the preamble.
// They aren't useful for people using the header file,
// and they mean that the header files change based on the
// exact location of GOPATH.
re := regexp.MustCompile(`(?m)^(#line\s+[0-9]+\s+")[^"]*[/\\]([^"]*")`)
preamble := re.ReplaceAllString(p.Preamble, "$1$2")
fmt.Fprintf(fgcch, "/* Start of preamble from import \"C\" comments. */\n\n")
fmt.Fprintf(fgcch, "%s\n", p.Preamble)
fmt.Fprintf(fgcch, "%s\n", preamble)
fmt.Fprintf(fgcch, "\n/* End of preamble from import \"C\" comments. */\n\n")
fmt.Fprintf(fgcch, "%s\n", p.gccExportHeaderProlog())
@ -1414,6 +1420,25 @@ static void _cgo_tsan_release() {
// Set to yesTsanProlog if we see -fsanitize=thread in the flags for gcc.
var tsanProlog = noTsanProlog
// noMsanProlog is a prologue defining an MSAN function in C.
// This is used when not compiling with -fsanitize=memory.
const noMsanProlog = `
#define _cgo_msan_write(addr, sz)
`
// yesMsanProlog is a prologue defining an MSAN function in C.
// This is used when compiling with -fsanitize=memory.
// See the comment above where _cgo_msan_write is called.
const yesMsanProlog = `
extern void __msan_unpoison(const volatile void *, size_t);
#define _cgo_msan_write(addr, sz) __msan_unpoison((addr), (sz))
`
// msanProlog is set to yesMsanProlog if we see -fsanitize=memory in the flags
// for the C compiler.
var msanProlog = noMsanProlog
const builtinProlog = `
#line 1 "cgo-builtin-prolog"
#include <stddef.h> /* for ptrdiff_t and size_t below */

View File

@ -59,6 +59,8 @@ func run(stdin []byte, argv []string) (stdout, stderr []byte, ok bool) {
var bout, berr bytes.Buffer
p.Stdout = &bout
p.Stderr = &berr
// Disable escape codes in clang error messages.
p.Env = append(os.Environ(), "TERM=dumb")
err := p.Run()
if _, ok := err.(*exec.ExitError); err != nil && !ok {
fatalf("%s", err)
@ -97,6 +99,8 @@ func error_(pos token.Pos, msg string, args ...interface{}) {
nerrors++
if pos.IsValid() {
fmt.Fprintf(os.Stderr, "%s: ", fset.Position(pos).String())
} else {
fmt.Fprintf(os.Stderr, "cgo: ")
}
fmt.Fprintf(os.Stderr, msg, args...)
fmt.Fprintf(os.Stderr, "\n")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,6 @@ import (
"os/exec"
"path/filepath"
"strings"
"syscall"
"testing"
)
@ -57,15 +56,6 @@ func TestAbsolutePath(t *testing.T) {
}
}
func isWindowsXP(t *testing.T) bool {
v, err := syscall.GetVersion()
if err != nil {
t.Fatalf("GetVersion failed: %v", err)
}
major := byte(v)
return major < 6
}
func runIcacls(t *testing.T, args ...string) string {
t.Helper()
out, err := exec.Command("icacls", args...).CombinedOutput()
@ -89,10 +79,6 @@ func runGetACL(t *testing.T, path string) string {
// has discretionary access control list (DACL) set as if the file
// was created in the destination directory.
func TestACL(t *testing.T) {
if isWindowsXP(t) {
t.Skip("Windows XP does not have powershell command")
}
tmpdir, err := ioutil.TempDir("", "TestACL")
if err != nil {
t.Fatal(err)
@ -111,7 +97,7 @@ func TestACL(t *testing.T) {
// will make all files created in TestACL/tmp have different
// security attributes to the files created in TestACL.
runIcacls(t, newtmpdir,
"/grant", "guest:(oi)(ci)f", // add Guest user to have full access
"/grant", "*S-1-5-32-546:(oi)(ci)f", // add Guests group to have full access
)
src := filepath.Join(tmpdir, "main.go")

View File

@ -45,25 +45,43 @@ type Command struct {
// CustomFlags indicates that the command will do its own
// flag parsing.
CustomFlags bool
// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'go help'.
// Note that subcommands are in general best avoided.
Commands []*Command
}
// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'go help'.
var Commands []*Command
var Go = &Command{
UsageLine: "go",
Long: `Go is a tool for managing Go source code.`,
// Commands initialized in package main
}
// Name returns the command's name: the first word in the usage line.
func (c *Command) Name() string {
// LongName returns the command's long name: all the words in the usage line between "go" and a flag or argument,
func (c *Command) LongName() string {
name := c.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
if i := strings.Index(name, " ["); i >= 0 {
name = name[:i]
}
if name == "go" {
return ""
}
return strings.TrimPrefix(name, "go ")
}
// Name returns the command's short name: the last word in the usage line before a flag or argument.
func (c *Command) Name() string {
name := c.LongName()
if i := strings.LastIndex(name, " "); i >= 0 {
name = name[i+1:]
}
return name
}
func (c *Command) Usage() {
fmt.Fprintf(os.Stderr, "usage: %s\n", c.UsageLine)
fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.Name())
fmt.Fprintf(os.Stderr, "Run 'go help %s' for details.\n", c.LongName())
os.Exit(2)
}

View File

@ -0,0 +1,152 @@
// Copyright 2018 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 base
import (
"flag"
"fmt"
"os"
"runtime"
"strings"
"cmd/go/internal/cfg"
)
var (
goflags []string // cached $GOFLAGS list; can be -x or --x form
knownFlag = make(map[string]bool) // flags allowed to appear in $GOFLAGS; no leading dashes
)
// AddKnownFlag adds name to the list of known flags for use in $GOFLAGS.
func AddKnownFlag(name string) {
knownFlag[name] = true
}
// GOFLAGS returns the flags from $GOFLAGS.
// The list can be assumed to contain one string per flag,
// with each string either beginning with -name or --name.
func GOFLAGS() []string {
InitGOFLAGS()
return goflags
}
// InitGOFLAGS initializes the goflags list from $GOFLAGS.
// If goflags is already initialized, it does nothing.
func InitGOFLAGS() {
if goflags != nil { // already initialized
return
}
// Build list of all flags for all commands.
// If no command has that flag, then we report the problem.
// This catches typos while still letting users record flags in GOFLAGS
// that only apply to a subset of go commands.
// Commands using CustomFlags can report their flag names
// by calling AddKnownFlag instead.
var walkFlags func(*Command)
walkFlags = func(cmd *Command) {
for _, sub := range cmd.Commands {
walkFlags(sub)
}
cmd.Flag.VisitAll(func(f *flag.Flag) {
knownFlag[f.Name] = true
})
}
walkFlags(Go)
// Ignore bad flag in go env and go bug, because
// they are what people reach for when debugging
// a problem, and maybe they're debugging GOFLAGS.
// (Both will show the GOFLAGS setting if let succeed.)
hideErrors := cfg.CmdName == "env" || cfg.CmdName == "bug"
goflags = strings.Fields(os.Getenv("GOFLAGS"))
if goflags == nil {
goflags = []string{} // avoid work on later InitGOFLAGS call
}
// Each of the words returned by strings.Fields must be its own flag.
// To set flag arguments use -x=value instead of -x value.
// For boolean flags, -x is fine instead of -x=true.
for _, f := range goflags {
// Check that every flag looks like -x --x -x=value or --x=value.
if !strings.HasPrefix(f, "-") || f == "-" || f == "--" || strings.HasPrefix(f, "---") || strings.HasPrefix(f, "-=") || strings.HasPrefix(f, "--=") {
if hideErrors {
continue
}
Fatalf("go: parsing $GOFLAGS: non-flag %q", f)
}
name := f[1:]
if name[0] == '-' {
name = name[1:]
}
if i := strings.Index(name, "="); i >= 0 {
name = name[:i]
}
if !knownFlag[name] {
if hideErrors {
continue
}
Fatalf("go: parsing $GOFLAGS: unknown flag -%s", name)
}
}
}
// boolFlag is the optional interface for flag.Value known to the flag package.
// (It is not clear why package flag does not export this interface.)
type boolFlag interface {
flag.Value
IsBoolFlag() bool
}
// SetFromGOFLAGS sets the flags in the given flag set using settings in $GOFLAGS.
func SetFromGOFLAGS(flags flag.FlagSet) {
InitGOFLAGS()
// This loop is similar to flag.Parse except that it ignores
// unknown flags found in goflags, so that setting, say, GOFLAGS=-ldflags=-w
// does not break commands that don't have a -ldflags.
// It also adjusts the output to be clear that the reported problem is from $GOFLAGS.
where := "$GOFLAGS"
if runtime.GOOS == "windows" {
where = "%GOFLAGS%"
}
for _, goflag := range goflags {
name, value, hasValue := goflag, "", false
if i := strings.Index(goflag, "="); i >= 0 {
name, value, hasValue = goflag[:i], goflag[i+1:], true
}
if strings.HasPrefix(name, "--") {
name = name[1:]
}
f := flags.Lookup(name[1:])
if f == nil {
continue
}
if fb, ok := f.Value.(boolFlag); ok && fb.IsBoolFlag() {
if hasValue {
if err := fb.Set(value); err != nil {
fmt.Fprintf(flags.Output(), "go: invalid boolean value %q for flag %s (from %s): %v\n", value, name, where, err)
flags.Usage()
}
} else {
if err := fb.Set("true"); err != nil {
fmt.Fprintf(flags.Output(), "go: invalid boolean flag %s (from %s): %v\n", name, where, err)
flags.Usage()
}
}
} else {
if !hasValue {
fmt.Fprintf(flags.Output(), "go: flag needs an argument: %s (from %s)\n", name, where)
flags.Usage()
}
if err := f.Value.Set(value); err != nil {
fmt.Fprintf(flags.Output(), "go: invalid value %q for flag %s (from %s): %v\n", value, name, where, err)
flags.Usage()
}
}
}
}

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris
// +build aix darwin dragonfly freebsd js linux nacl netbsd openbsd solaris
package base

View File

@ -25,7 +25,7 @@ import (
var CmdBug = &base.Command{
Run: runBug,
UsageLine: "bug",
UsageLine: "go bug",
Short: "start a bug report",
Long: `
Bug opens the default browser and starts a new bug report.
@ -38,6 +38,9 @@ func init() {
}
func runBug(cmd *base.Command, args []string) {
if len(args) > 0 {
base.Fatalf("go bug: bug takes no arguments")
}
var buf bytes.Buffer
buf.WriteString(bugHeader)
inspectGoVersion(&buf)

View File

@ -189,6 +189,21 @@ func (c *Cache) get(id ActionID) (Entry, error) {
return Entry{buf, size, time.Unix(0, tm)}, nil
}
// GetFile looks up the action ID in the cache and returns
// the name of the corresponding data file.
func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
entry, err = c.Get(id)
if err != nil {
return "", Entry{}, err
}
file = c.OutputFile(entry.OutputID)
info, err := os.Stat(file)
if err != nil || info.Size() != entry.Size {
return "", Entry{}, errMissing
}
return file, entry, nil
}
// GetBytes looks up the action ID in the cache and returns
// the corresponding output bytes.
// GetBytes should only be used for data that can be expected to fit in memory.

View File

@ -35,12 +35,14 @@ See golang.org to learn more about Go.
// initDefaultCache does the work of finding the default cache
// the first time Default is called.
func initDefaultCache() {
dir := DefaultDir()
dir, showWarnings := defaultDir()
if dir == "off" {
return
}
if err := os.MkdirAll(dir, 0777); err != nil {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
if showWarnings {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
}
return
}
if _, err := os.Stat(filepath.Join(dir, "README")); err != nil {
@ -50,7 +52,9 @@ func initDefaultCache() {
c, err := Open(dir)
if err != nil {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
if showWarnings {
fmt.Fprintf(os.Stderr, "go: disabling cache (%s) due to initialization failure: %s\n", dir, err)
}
return
}
defaultCache = c
@ -59,14 +63,24 @@ func initDefaultCache() {
// DefaultDir returns the effective GOCACHE setting.
// It returns "off" if the cache is disabled.
func DefaultDir() string {
dir, _ := defaultDir()
return dir
}
// defaultDir returns the effective GOCACHE setting.
// It returns "off" if the cache is disabled.
// The second return value reports whether warnings should
// be shown if the cache fails to initialize.
func defaultDir() (string, bool) {
dir := os.Getenv("GOCACHE")
if dir != "" {
return dir
return dir, true
}
// Compute default location.
// TODO(rsc): This code belongs somewhere else,
// like maybe ioutil.CacheDir or os.CacheDir.
showWarnings := true
switch runtime.GOOS {
case "windows":
dir = os.Getenv("LocalAppData")
@ -76,20 +90,20 @@ func DefaultDir() string {
dir = os.Getenv("AppData")
}
if dir == "" {
return "off"
return "off", true
}
case "darwin":
dir = os.Getenv("HOME")
if dir == "" {
return "off"
return "off", true
}
dir += "/Library/Caches"
case "plan9":
dir = os.Getenv("home")
if dir == "" {
return "off"
return "off", true
}
// Plan 9 has no established per-user cache directory,
// but $home/lib/xyz is the usual equivalent of $HOME/.xyz on Unix.
@ -101,10 +115,15 @@ func DefaultDir() string {
if dir == "" {
dir = os.Getenv("HOME")
if dir == "" {
return "off"
return "off", true
}
if dir == "/" {
// probably docker run with -u flag
// https://golang.org/issue/26280
showWarnings = false
}
dir += "/.cache"
}
}
return filepath.Join(dir, "go-build")
return filepath.Join(dir, "go-build"), showWarnings
}

View File

@ -0,0 +1,67 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !windows,!darwin,!plan9
package cache
import (
"os"
"strings"
"testing"
)
func TestDefaultDir(t *testing.T) {
goCacheDir := "/tmp/test-go-cache"
xdgCacheDir := "/tmp/test-xdg-cache"
homeDir := "/tmp/test-home"
// undo env changes when finished
defer func(GOCACHE, XDG_CACHE_HOME, HOME string) {
os.Setenv("GOCACHE", GOCACHE)
os.Setenv("XDG_CACHE_HOME", XDG_CACHE_HOME)
os.Setenv("HOME", HOME)
}(os.Getenv("GOCACHE"), os.Getenv("XDG_CACHE_HOME"), os.Getenv("HOME"))
os.Setenv("GOCACHE", goCacheDir)
os.Setenv("XDG_CACHE_HOME", xdgCacheDir)
os.Setenv("HOME", homeDir)
dir, showWarnings := defaultDir()
if dir != goCacheDir {
t.Errorf("Cache DefaultDir %q should be $GOCACHE %q", dir, goCacheDir)
}
if !showWarnings {
t.Error("Warnings should be shown when $GOCACHE is set")
}
os.Unsetenv("GOCACHE")
dir, showWarnings = defaultDir()
if !strings.HasPrefix(dir, xdgCacheDir+"/") {
t.Errorf("Cache DefaultDir %q should be under $XDG_CACHE_HOME %q when $GOCACHE is unset", dir, xdgCacheDir)
}
if !showWarnings {
t.Error("Warnings should be shown when $XDG_CACHE_HOME is set")
}
os.Unsetenv("XDG_CACHE_HOME")
dir, showWarnings = defaultDir()
if !strings.HasPrefix(dir, homeDir+"/.cache/") {
t.Errorf("Cache DefaultDir %q should be under $HOME/.cache %q when $GOCACHE and $XDG_CACHE_HOME are unset", dir, homeDir+"/.cache")
}
if !showWarnings {
t.Error("Warnings should be shown when $HOME is not /")
}
os.Unsetenv("HOME")
if dir, _ := defaultDir(); dir != "off" {
t.Error("Cache not disabled when $GOCACHE, $XDG_CACHE_HOME, and $HOME are unset")
}
os.Setenv("HOME", "/")
if _, showWarnings := defaultDir(); showWarnings {
// https://golang.org/issue/26280
t.Error("Cache initalization warnings should be squelched when $GOCACHE and $XDG_CACHE_HOME are unset and $HOME is /")
}
}

View File

@ -20,7 +20,8 @@ import (
var (
BuildA bool // -a flag
BuildBuildmode string // -buildmode flag
BuildContext = build.Default
BuildContext = defaultContext()
BuildMod string // -mod flag
BuildI bool // -i flag
BuildLinkshared bool // -linkshared flag
BuildMSan bool // -msan flag
@ -42,6 +43,12 @@ var (
DebugActiongraph string // -debug-actiongraph flag (undocumented, unstable)
)
func defaultContext() build.Context {
ctxt := build.Default
ctxt.JoinPath = filepath.Join // back door to say "do not use go command"
return ctxt
}
func init() {
BuildToolchainCompiler = func() string { return "missing-compiler" }
BuildToolchainLinker = func() string { return "missing-linker" }
@ -67,6 +74,16 @@ var (
Goos = BuildContext.GOOS
ExeSuffix string
Gopath = filepath.SplitList(BuildContext.GOPATH)
// ModulesEnabled specifies whether the go command is running
// in module-aware mode (as opposed to GOPATH mode).
// It is equal to modload.Enabled, but not all packages can import modload.
ModulesEnabled bool
// GoModInGOPATH records whether we've found a go.mod in GOPATH/src
// in GO111MODULE=auto mode. In that case, we don't use modules
// but people might expect us to, so 'go get' warns.
GoModInGOPATH string
)
func init() {
@ -84,9 +101,10 @@ var (
GOROOT_FINAL = findGOROOT_FINAL()
// Used in envcmd.MkEnv and build ID computations.
GOARM = fmt.Sprint(objabi.GOARM)
GO386 = objabi.GO386
GOMIPS = objabi.GOMIPS
GOARM = fmt.Sprint(objabi.GOARM)
GO386 = objabi.GO386
GOMIPS = objabi.GOMIPS
GOMIPS64 = objabi.GOMIPS64
)
// Update build context to use our computed GOROOT.
@ -102,6 +120,16 @@ func init() {
}
}
// There is a copy of findGOROOT, isSameDir, and isGOROOT in
// x/tools/cmd/godoc/goroot.go.
// Try to keep them in sync for now.
// findGOROOT returns the GOROOT value, using either an explicitly
// provided environment variable, a GOROOT that contains the current
// os.Executable value, or else the GOROOT that the binary was built
// with from runtime.GOROOT().
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func findGOROOT() string {
if env := os.Getenv("GOROOT"); env != "" {
return filepath.Clean(env)
@ -161,6 +189,8 @@ func isSameDir(dir1, dir2 string) bool {
// It does this by looking for the path/pkg/tool directory,
// which is necessary for useful operation of the cmd/go tool,
// and is not typically present in a GOPATH.
//
// There is a copy of this code in x/tools/cmd/godoc/goroot.go.
func isGOROOT(path string) bool {
stat, err := os.Stat(filepath.Join(path, "pkg", "tool"))
if err != nil {

View File

@ -17,11 +17,13 @@ import (
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/work"
)
var CmdClean = &base.Command{
UsageLine: "clean [-i] [-r] [-n] [-x] [-cache] [-testcache] [build flags] [packages]",
UsageLine: "go clean [clean flags] [build flags] [packages]",
Short: "remove object files and cached files",
Long: `
Clean removes object files from package source directories.
@ -65,6 +67,10 @@ The -cache flag causes clean to remove the entire go build cache.
The -testcache flag causes clean to expire all test results in the
go build cache.
The -modcache flag causes clean to remove the entire module
download cache, including unpacked source code of versioned
dependencies.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
@ -75,6 +81,7 @@ var (
cleanI bool // clean -i flag
cleanR bool // clean -r flag
cleanCache bool // clean -cache flag
cleanModcache bool // clean -modcache flag
cleanTestcache bool // clean -testcache flag
)
@ -85,6 +92,7 @@ func init() {
CmdClean.Flag.BoolVar(&cleanI, "i", false, "")
CmdClean.Flag.BoolVar(&cleanR, "r", false, "")
CmdClean.Flag.BoolVar(&cleanCache, "cache", false, "")
CmdClean.Flag.BoolVar(&cleanModcache, "modcache", false, "")
CmdClean.Flag.BoolVar(&cleanTestcache, "testcache", false, "")
// -n and -x are important enough to be
@ -95,8 +103,13 @@ func init() {
}
func runClean(cmd *base.Command, args []string) {
for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg)
if len(args) == 0 && modload.Failed() {
// Don't try to clean current directory,
// which will cause modload to base.Fatalf.
} else {
for _, pkg := range load.PackagesAndErrors(args) {
clean(pkg)
}
}
if cleanCache {
@ -138,6 +151,29 @@ func runClean(cmd *base.Command, args []string) {
}
}
}
if cleanModcache {
if modfetch.PkgMod == "" {
base.Fatalf("go clean -modcache: no module cache")
}
if err := removeAll(modfetch.PkgMod); err != nil {
base.Errorf("go clean -modcache: %v", err)
}
}
}
func removeAll(dir string) error {
// Module cache has 0555 directories; make them writable in order to remove content.
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // ignore errors walking in file system
}
if info.IsDir() {
os.Chmod(path, 0777)
}
return nil
})
return os.RemoveAll(dir)
}
var cleaned = map[*load.Package]bool{}

View File

@ -69,6 +69,14 @@ func SyntaxError(cmd, msg string) {
os.Exit(2)
}
// AddKnownFlags registers the flags in defns with base.AddKnownFlag.
func AddKnownFlags(cmd string, defns []*Defn) {
for _, f := range defns {
base.AddKnownFlag(f.Name)
base.AddKnownFlag(cmd + "." + f.Name)
}
}
// Parse sees if argument i is present in the definitions and if so,
// returns its definition, value, and whether it consumed an extra word.
// If the flag begins (cmd+".") it is ignored for the purpose of this function.
@ -121,3 +129,31 @@ func Parse(cmd string, defns []*Defn, args []string, i int) (f *Defn, value stri
f = nil
return
}
// FindGOFLAGS extracts and returns the flags matching defns from GOFLAGS.
// Ideally the caller would mention that the flags were from GOFLAGS
// when reporting errors, but that's too hard for now.
func FindGOFLAGS(defns []*Defn) []string {
var flags []string
for _, flag := range base.GOFLAGS() {
// Flags returned by base.GOFLAGS are well-formed, one of:
// -x
// --x
// -x=value
// --x=value
if strings.HasPrefix(flag, "--") {
flag = flag[1:]
}
name := flag[1:]
if i := strings.Index(name, "="); i >= 0 {
name = name[:i]
}
for _, f := range defns {
if name == f.Name {
flags = append(flags, flag)
break
}
}
}
return flags
}

View File

@ -0,0 +1,103 @@
// Copyright 2018 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 dirhash defines hashes over directory trees.
package dirhash
import (
"archive/zip"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strings"
)
var DefaultHash = Hash1
type Hash func(files []string, open func(string) (io.ReadCloser, error)) (string, error)
func Hash1(files []string, open func(string) (io.ReadCloser, error)) (string, error) {
h := sha256.New()
files = append([]string(nil), files...)
sort.Strings(files)
for _, file := range files {
if strings.Contains(file, "\n") {
return "", errors.New("filenames with newlines are not supported")
}
r, err := open(file)
if err != nil {
return "", err
}
hf := sha256.New()
_, err = io.Copy(hf, r)
r.Close()
if err != nil {
return "", err
}
fmt.Fprintf(h, "%x %s\n", hf.Sum(nil), file)
}
return "h1:" + base64.StdEncoding.EncodeToString(h.Sum(nil)), nil
}
func HashDir(dir, prefix string, hash Hash) (string, error) {
files, err := DirFiles(dir, prefix)
if err != nil {
return "", err
}
osOpen := func(name string) (io.ReadCloser, error) {
return os.Open(filepath.Join(dir, strings.TrimPrefix(name, prefix)))
}
return hash(files, osOpen)
}
func DirFiles(dir, prefix string) ([]string, error) {
var files []string
dir = filepath.Clean(dir)
err := filepath.Walk(dir, func(file string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
rel := file
if dir != "." {
rel = file[len(dir)+1:]
}
f := filepath.Join(prefix, rel)
files = append(files, filepath.ToSlash(f))
return nil
})
if err != nil {
return nil, err
}
return files, nil
}
func HashZip(zipfile string, hash Hash) (string, error) {
z, err := zip.OpenReader(zipfile)
if err != nil {
return "", err
}
defer z.Close()
var files []string
zfiles := make(map[string]*zip.File)
for _, file := range z.File {
files = append(files, file.Name)
zfiles[file.Name] = file
}
zipOpen := func(name string) (io.ReadCloser, error) {
f := zfiles[name]
if f == nil {
return nil, fmt.Errorf("file %q not found in zip", name) // should never happen
}
return f.Open()
}
return hash(files, zipOpen)
}

View File

@ -0,0 +1,135 @@
// Copyright 2018 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 dirhash
import (
"archive/zip"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
)
func h(s string) string {
return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))
}
func htop(k string, s string) string {
sum := sha256.Sum256([]byte(s))
return k + ":" + base64.StdEncoding.EncodeToString(sum[:])
}
func TestHash1(t *testing.T) {
files := []string{"xyz", "abc"}
open := func(name string) (io.ReadCloser, error) {
return ioutil.NopCloser(strings.NewReader("data for " + name)), nil
}
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "abc", h("data for xyz"), "xyz"))
out, err := Hash1(files, open)
if err != nil {
t.Fatal(err)
}
if out != want {
t.Errorf("Hash1(...) = %s, want %s", out, want)
}
_, err = Hash1([]string{"xyz", "a\nbc"}, open)
if err == nil {
t.Error("Hash1: expected error on newline in filenames")
}
}
func TestHashDir(t *testing.T) {
dir, err := ioutil.TempDir("", "dirhash-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
t.Fatal(err)
}
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
out, err := HashDir(dir, "prefix", Hash1)
if err != nil {
t.Fatalf("HashDir: %v", err)
}
if out != want {
t.Errorf("HashDir(...) = %s, want %s", out, want)
}
}
func TestHashZip(t *testing.T) {
f, err := ioutil.TempFile("", "dirhash-test-")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
defer f.Close()
z := zip.NewWriter(f)
w, err := z.Create("prefix/xyz")
if err != nil {
t.Fatal(err)
}
w.Write([]byte("data for xyz"))
w, err = z.Create("prefix/abc")
if err != nil {
t.Fatal(err)
}
w.Write([]byte("data for abc"))
if err := z.Close(); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
want := htop("h1", fmt.Sprintf("%s %s\n%s %s\n", h("data for abc"), "prefix/abc", h("data for xyz"), "prefix/xyz"))
out, err := HashZip(f.Name(), Hash1)
if err != nil {
t.Fatalf("HashDir: %v", err)
}
if out != want {
t.Errorf("HashDir(...) = %s, want %s", out, want)
}
}
func TestDirFiles(t *testing.T) {
dir, err := ioutil.TempDir("", "dirfiles-test-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
if err := ioutil.WriteFile(filepath.Join(dir, "xyz"), []byte("data for xyz"), 0666); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(dir, "abc"), []byte("data for abc"), 0666); err != nil {
t.Fatal(err)
}
if err := os.Mkdir(filepath.Join(dir, "subdir"), 0777); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(filepath.Join(dir, "subdir", "xyz"), []byte("data for subdir xyz"), 0666); err != nil {
t.Fatal(err)
}
prefix := "foo/bar@v2.3.4"
out, err := DirFiles(dir, prefix)
if err != nil {
t.Fatalf("DirFiles: %v", err)
}
for _, file := range out {
if !strings.HasPrefix(file, prefix) {
t.Errorf("Dir file = %s, want prefix %s", file, prefix)
}
}
}

View File

@ -12,7 +12,7 @@ import (
var CmdDoc = &base.Command{
Run: runDoc,
UsageLine: "doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
UsageLine: "go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
CustomFlags: true,
Short: "show documentation for package or symbol",
Long: `

View File

@ -9,6 +9,7 @@ import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
@ -16,11 +17,12 @@ import (
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
)
var CmdEnv = &base.Command{
UsageLine: "env [-json] [var ...]",
UsageLine: "go env [-json] [var ...]",
Short: "print Go environment information",
Long: `
Env prints Go environment information.
@ -52,17 +54,16 @@ func MkEnv() []cfg.EnvVar {
{Name: "GOBIN", Value: cfg.GOBIN},
{Name: "GOCACHE", Value: cache.DefaultDir()},
{Name: "GOEXE", Value: cfg.ExeSuffix},
{Name: "GOFLAGS", Value: os.Getenv("GOFLAGS")},
{Name: "GOHOSTARCH", Value: runtime.GOARCH},
{Name: "GOHOSTOS", Value: runtime.GOOS},
{Name: "GOOS", Value: cfg.Goos},
{Name: "GOPATH", Value: cfg.BuildContext.GOPATH},
{Name: "GOPROXY", Value: os.Getenv("GOPROXY")},
{Name: "GORACE", Value: os.Getenv("GORACE")},
{Name: "GOROOT", Value: cfg.GOROOT},
{Name: "GOTMPDIR", Value: os.Getenv("GOTMPDIR")},
{Name: "GOTOOLDIR", Value: base.ToolDir},
// disable escape codes in clang errors
{Name: "TERM", Value: "dumb"},
}
if work.GccgoBin != "" {
@ -78,6 +79,8 @@ func MkEnv() []cfg.EnvVar {
env = append(env, cfg.EnvVar{Name: "GO386", Value: cfg.GO386})
case "mips", "mipsle":
env = append(env, cfg.EnvVar{Name: "GOMIPS", Value: cfg.GOMIPS})
case "mips64", "mips64le":
env = append(env, cfg.EnvVar{Name: "GOMIPS64", Value: cfg.GOMIPS64})
}
cc := cfg.DefaultCC(cfg.Goos, cfg.Goarch)
@ -111,6 +114,18 @@ func findEnv(env []cfg.EnvVar, name string) string {
// ExtraEnvVars returns environment variables that should not leak into child processes.
func ExtraEnvVars() []cfg.EnvVar {
gomod := ""
if modload.Init(); modload.ModRoot != "" {
gomod = filepath.Join(modload.ModRoot, "go.mod")
}
return []cfg.EnvVar{
{Name: "GOMOD", Value: gomod},
}
}
// ExtraEnvVarsCostly returns environment variables that should not leak into child processes
// but are costly to evaluate.
func ExtraEnvVarsCostly() []cfg.EnvVar {
var b work.Builder
b.Init()
cppflags, cflags, cxxflags, fflags, ldflags, err := b.CFlags(&load.Package{})
@ -120,6 +135,7 @@ func ExtraEnvVars() []cfg.EnvVar {
return nil
}
cmd := b.GccCmd(".", "")
return []cfg.EnvVar{
// Note: Update the switch in runEnv below when adding to this list.
{Name: "CGO_CFLAGS", Value: strings.Join(cflags, " ")},
@ -134,13 +150,14 @@ func ExtraEnvVars() []cfg.EnvVar {
func runEnv(cmd *base.Command, args []string) {
env := cfg.CmdEnv
env = append(env, ExtraEnvVars()...)
// Do we need to call ExtraEnvVars, which is a bit expensive?
// Do we need to call ExtraEnvVarsCostly, which is a bit expensive?
// Only if we're listing all environment variables ("go env")
// or the variables being requested are in the extra list.
needExtra := true
needCostly := true
if len(args) > 0 {
needExtra = false
needCostly = false
for _, arg := range args {
switch arg {
case "CGO_CFLAGS",
@ -150,12 +167,12 @@ func runEnv(cmd *base.Command, args []string) {
"CGO_LDFLAGS",
"PKG_CONFIG",
"GOGCCFLAGS":
needExtra = true
needCostly = true
}
}
}
if needExtra {
env = append(env, ExtraEnvVars()...)
if needCostly {
env = append(env, ExtraEnvVarsCostly()...)
}
if len(args) > 0 {

View File

@ -9,12 +9,15 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/str"
"fmt"
"os"
)
var CmdFix = &base.Command{
Run: runFix,
UsageLine: "fix [packages]",
UsageLine: "go fix [packages]",
Short: "update packages to use new APIs",
Long: `
Fix runs the Go fix command on the packages named by the import paths.
@ -29,7 +32,15 @@ See also: go fmt, go vet.
}
func runFix(cmd *base.Command, args []string) {
printed := false
for _, pkg := range load.Packages(args) {
if modload.Enabled() && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not fixing packages in dependency modules\n")
printed = true
}
continue
}
// Use pkg.gofiles instead of pkg.Dir so that
// the command only applies to this package,
// not to packages in subdirectories.

View File

@ -6,6 +6,7 @@
package fmtcmd
import (
"fmt"
"os"
"path/filepath"
"runtime"
@ -15,6 +16,7 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/str"
)
@ -24,7 +26,7 @@ func init() {
var CmdFmt = &base.Command{
Run: runFmt,
UsageLine: "fmt [-n] [-x] [packages]",
UsageLine: "go fmt [-n] [-x] [packages]",
Short: "gofmt (reformat) package sources",
Long: `
Fmt runs the command 'gofmt -l -w' on the packages named
@ -43,6 +45,7 @@ See also: go fix, go vet.
}
func runFmt(cmd *base.Command, args []string) {
printed := false
gofmt := gofmtPath()
procs := runtime.GOMAXPROCS(0)
var wg sync.WaitGroup
@ -57,6 +60,13 @@ func runFmt(cmd *base.Command, args []string) {
}()
}
for _, pkg := range load.PackagesAndErrors(args) {
if modload.Enabled() && pkg.Module != nil && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not formatting packages in dependency modules\n")
printed = true
}
continue
}
if pkg.Error != nil {
if strings.HasPrefix(pkg.Error.Err, "build constraints exclude all Go files") {
// Skip this error, as we will format

View File

@ -21,12 +21,13 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/work"
)
var CmdGenerate = &base.Command{
Run: runGenerate,
UsageLine: "generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]",
UsageLine: "go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]",
Short: "generate Go files by processing source",
Long: `
Generate runs commands described by directives within existing
@ -47,6 +48,12 @@ that can be run locally. It must either be in the shell path
(gofmt), a fully qualified path (/usr/you/bin/mytool), or a
command alias, described below.
To convey to humans and machine tools that code is generated,
generated source should have a line early in the file that
matches the following regular expression (in Go syntax):
^// Code generated .* DO NOT EDIT\.$
Note that go generate does not parse the file, so lines that look
like directives in comments or multiline strings will be treated
as directives.
@ -152,9 +159,28 @@ func runGenerate(cmd *base.Command, args []string) {
}
}
// Even if the arguments are .go files, this loop suffices.
printed := false
for _, pkg := range load.Packages(args) {
if modload.Enabled() && !pkg.Module.Main {
if !printed {
fmt.Fprintf(os.Stderr, "go: not generating in packages in dependency modules\n")
printed = true
}
continue
}
pkgName := pkg.Name
for _, file := range pkg.InternalGoFiles() {
if !generate(pkg.Name, file) {
if !generate(pkgName, file) {
break
}
}
pkgName += "_test"
for _, file := range pkg.InternalXGoFiles() {
if !generate(pkgName, file) {
break
}
}

View File

@ -28,7 +28,7 @@ func charsetReader(charset string, input io.Reader) (io.Reader, error) {
// parseMetaGoImports returns meta imports from the HTML in r.
// Parsing ends at the end of the <head> section or the beginning of the <body>.
func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
func parseMetaGoImports(r io.Reader, mod ModuleMode) (imports []metaImport, err error) {
d := xml.NewDecoder(r)
d.CharsetReader = charsetReader
d.Strict = false
@ -39,13 +39,13 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
if err == io.EOF || len(imports) > 0 {
err = nil
}
return
break
}
if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
return
break
}
if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
return
break
}
e, ok := t.(xml.StartElement)
if !ok || !strings.EqualFold(e.Name.Local, "meta") {
@ -55,13 +55,6 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
continue
}
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
// Ignore VCS type "mod", which is new Go modules.
// This code is for old go get and must ignore the new mod lines.
// Otherwise matchGoImport will complain about two
// different metaImport lines for the same Prefix.
if f[1] == "mod" {
continue
}
imports = append(imports, metaImport{
Prefix: f[0],
VCS: f[1],
@ -69,6 +62,27 @@ func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
})
}
}
// Extract mod entries if we are paying attention to them.
var list []metaImport
var have map[string]bool
if mod == PreferMod {
have = make(map[string]bool)
for _, m := range imports {
if m.VCS == "mod" {
have[m.Prefix] = true
list = append(list, m)
}
}
}
// Append non-mod entries, ignoring those superseded by a mod entry.
for _, m := range imports {
if m.VCS != "mod" && !have[m.Prefix] {
list = append(list, m)
}
}
return list, nil
}
// attrValue returns the attribute value for the case-insensitive key

View File

@ -16,13 +16,14 @@ import (
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/search"
"cmd/go/internal/str"
"cmd/go/internal/web"
"cmd/go/internal/work"
)
var CmdGet = &base.Command{
UsageLine: "get [-d] [-f] [-fix] [-insecure] [-t] [-u] [-v] [build flags] [packages]",
UsageLine: "go get [-d] [-f] [-t] [-u] [-v] [-fix] [-insecure] [build flags] [packages]",
Short: "download and install packages and dependencies",
Long: `
Get downloads the packages named by the import paths, along with their
@ -73,23 +74,56 @@ For more about specifying packages, see 'go help packages'.
For more about how 'go get' finds source code to
download, see 'go help importpath'.
This text describes the behavior of get when using GOPATH
to manage source code and dependencies.
If instead the go command is running in module-aware mode,
the details of get's flags and effects change, as does 'go help get'.
See 'go help modules' and 'go help module-get'.
See also: go build, go install, go clean.
`,
}
var getD = CmdGet.Flag.Bool("d", false, "")
var getF = CmdGet.Flag.Bool("f", false, "")
var getT = CmdGet.Flag.Bool("t", false, "")
var getU = CmdGet.Flag.Bool("u", false, "")
var getFix = CmdGet.Flag.Bool("fix", false, "")
var getInsecure = CmdGet.Flag.Bool("insecure", false, "")
var HelpGopathGet = &base.Command{
UsageLine: "gopath-get",
Short: "legacy GOPATH go get",
Long: `
The 'go get' command changes behavior depending on whether the
go command is running in module-aware mode or legacy GOPATH mode.
This help text, accessible as 'go help gopath-get' even in module-aware mode,
describes 'go get' as it operates in legacy GOPATH mode.
Usage: ` + CmdGet.UsageLine + `
` + CmdGet.Long,
}
var (
getD = CmdGet.Flag.Bool("d", false, "")
getF = CmdGet.Flag.Bool("f", false, "")
getT = CmdGet.Flag.Bool("t", false, "")
getU = CmdGet.Flag.Bool("u", false, "")
getFix = CmdGet.Flag.Bool("fix", false, "")
Insecure bool
)
func init() {
work.AddBuildFlags(CmdGet)
CmdGet.Run = runGet // break init loop
CmdGet.Flag.BoolVar(&Insecure, "insecure", Insecure, "")
}
func runGet(cmd *base.Command, args []string) {
if cfg.ModulesEnabled {
// Should not happen: main.go should install the separate module-enabled get code.
base.Fatalf("go get: modules not implemented")
}
if cfg.GoModInGOPATH != "" {
// Warn about not using modules with GO111MODULE=auto when go.mod exists.
// To silence the warning, users can set GO111MODULE=off.
fmt.Fprintf(os.Stderr, "go get: warning: modules disabled by GO111MODULE=auto in GOPATH/src;\n\tignoring %s;\n\tsee 'go help modules'\n", base.ShortPath(cfg.GoModInGOPATH))
}
work.BuildInit()
if *getF && !*getU {
@ -129,9 +163,8 @@ func runGet(cmd *base.Command, args []string) {
if *getT {
mode |= load.GetTestDeps
}
args = downloadPaths(args)
for _, arg := range args {
download(arg, nil, &stk, mode)
for _, pkg := range downloadPaths(args) {
download(pkg, nil, &stk, mode)
}
base.ExitIfErrors()
@ -150,8 +183,7 @@ func runGet(cmd *base.Command, args []string) {
// This leads to duplicated loads of the standard packages.
load.ClearCmdCache()
args = load.ImportPaths(args)
load.PackagesForBuild(args)
pkgs := load.PackagesForBuild(args)
// Phase 3. Install.
if *getD {
@ -161,7 +193,7 @@ func runGet(cmd *base.Command, args []string) {
return
}
work.InstallPackages(args, true)
work.InstallPackages(args, pkgs)
}
// downloadPaths prepares the list of paths to pass to download.
@ -169,28 +201,21 @@ func runGet(cmd *base.Command, args []string) {
// for a particular pattern, downloadPaths leaves it in the result list,
// in the hope that we can figure out the repository from the
// initial ...-free prefix.
func downloadPaths(args []string) []string {
args = load.ImportPathsNoDotExpansion(args)
var out []string
for _, a := range args {
if strings.Contains(a, "...") {
var expand []string
// Use matchPackagesInFS to avoid printing
// warnings. They will be printed by the
// eventual call to importPaths instead.
if build.IsLocalImport(a) {
expand = load.MatchPackagesInFS(a)
} else {
expand = load.MatchPackages(a)
}
if len(expand) > 0 {
out = append(out, expand...)
continue
}
func downloadPaths(patterns []string) []string {
for _, arg := range patterns {
if strings.Contains(arg, "@") {
base.Fatalf("go: cannot use path@version syntax in GOPATH mode")
}
out = append(out, a)
}
return out
var pkgs []string
for _, m := range search.ImportPathsQuiet(patterns) {
if len(m.Pkgs) == 0 && strings.Contains(m.Pattern, "...") {
pkgs = append(pkgs, m.Pattern)
} else {
pkgs = append(pkgs, m.Pkgs...)
}
}
return pkgs
}
// downloadCache records the import paths we have already
@ -215,7 +240,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
}
load1 := func(path string, mode int) *load.Package {
if parent == nil {
return load.LoadPackage(path, stk)
return load.LoadPackageNoFlags(path, stk)
}
return load.LoadImport(path, parent.Dir, parent, stk, nil, mode|load.ResolveModule)
}
@ -271,9 +296,9 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
// for p has been replaced in the package cache.
if wildcardOkay && strings.Contains(arg, "...") {
if build.IsLocalImport(arg) {
args = load.MatchPackagesInFS(arg)
args = search.MatchPackagesInFS(arg).Pkgs
} else {
args = load.MatchPackages(arg)
args = search.MatchPackages(arg).Pkgs
}
isWildcard = true
}
@ -304,7 +329,7 @@ func download(arg string, parent *load.Package, stk *load.ImportStack, mode int)
base.Run(cfg.BuildToolexec, str.StringList(base.Tool("fix"), files))
// The imports might have changed, so reload again.
p = load.ReloadPackage(arg, stk)
p = load.ReloadPackageNoFlags(arg, stk)
if p.Error != nil {
base.Errorf("%s", p.Error)
return
@ -369,10 +394,11 @@ func downloadPackage(p *load.Package) error {
vcs *vcsCmd
repo, rootPath string
err error
blindRepo bool // set if the repo has unusual configuration
)
security := web.Secure
if *getInsecure {
if Insecure {
security = web.Insecure
}
@ -389,20 +415,22 @@ func downloadPackage(p *load.Package) error {
dir := filepath.Join(p.Internal.Build.SrcRoot, filepath.FromSlash(rootPath))
remote, err := vcs.remoteRepo(vcs, dir)
if err != nil {
return err
// Proceed anyway. The package is present; we likely just don't understand
// the repo configuration (e.g. unusual remote protocol).
blindRepo = true
}
repo = remote
if !*getF {
if rr, err := repoRootForImportPath(p.ImportPath, security); err == nil {
repo := rr.repo
if !*getF && err == nil {
if rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security); err == nil {
repo := rr.Repo
if rr.vcs.resolveRepo != nil {
resolved, err := rr.vcs.resolveRepo(rr.vcs, dir, repo)
if err == nil {
repo = resolved
}
}
if remote != repo && rr.isCustom {
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.root, repo, dir, remote)
if remote != repo && rr.IsCustom {
return fmt.Errorf("%s is a custom import path for %s, but %s is checked out from %s", rr.Root, repo, dir, remote)
}
}
}
@ -410,13 +438,13 @@ func downloadPackage(p *load.Package) error {
} else {
// Analyze the import path to determine the version control system,
// repository, and the import path for the root of the repository.
rr, err := repoRootForImportPath(p.ImportPath, security)
rr, err := RepoRootForImportPath(p.ImportPath, IgnoreMod, security)
if err != nil {
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 {
if !blindRepo && !vcs.isSecure(repo) && !Insecure {
return fmt.Errorf("cannot download, %v uses insecure protocol", repo)
}

View File

@ -33,15 +33,18 @@ func TestFoldDup(t *testing.T) {
var parseMetaGoImportsTests = []struct {
in string
mod ModuleMode
out []metaImport
}{
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
IgnoreMod,
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
},
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
<meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`,
IgnoreMod,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
{"baz/quux", "git", "http://github.com/rsc/baz/quux"},
@ -50,6 +53,7 @@ var parseMetaGoImportsTests = []struct {
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
IgnoreMod,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
@ -57,35 +61,65 @@ var parseMetaGoImportsTests = []struct {
{
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
IgnoreMod,
[]metaImport{
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
},
},
{
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
PreferMod,
[]metaImport{
{"foo/bar", "mod", "http://github.com/rsc/baz/quux"},
},
},
{
`<head>
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
</head>`,
IgnoreMod,
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
},
{
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
<body>`,
IgnoreMod,
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
},
{
`<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
IgnoreMod,
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
},
{
// XML doesn't like <div style=position:relative>.
`<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`,
IgnoreMod,
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
},
{
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
`,
IgnoreMod,
[]metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}},
},
{
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
`,
PreferMod,
[]metaImport{
{"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"},
{"myitcv.io", "git", "https://github.com/myitcv/x"},
},
},
}
func TestParseMetaGoImports(t *testing.T) {
for i, tt := range parseMetaGoImportsTests {
out, err := parseMetaGoImports(strings.NewReader(tt.in))
out, err := parseMetaGoImports(strings.NewReader(tt.in), tt.mod)
if err != nil {
t.Errorf("test#%d: %v", i, err)
continue

View File

@ -5,7 +5,6 @@
package get
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@ -428,19 +427,18 @@ func (v *vcsCmd) run1(dir string, cmdline string, keyval []string, verbose bool)
fmt.Printf("cd %s\n", dir)
fmt.Printf("%s %s\n", v.cmd, strings.Join(args, " "))
}
var buf bytes.Buffer
cmd.Stdout = &buf
cmd.Stderr = &buf
err = cmd.Run()
out := buf.Bytes()
out, err := cmd.Output()
if err != nil {
if verbose || cfg.BuildV {
fmt.Fprintf(os.Stderr, "# cd %s; %s %s\n", dir, v.cmd, strings.Join(args, " "))
os.Stderr.Write(out)
if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
os.Stderr.Write(ee.Stderr)
} else {
fmt.Fprintf(os.Stderr, err.Error())
}
}
return out, err
}
return out, nil
return out, err
}
// ping pings to determine scheme to use.
@ -624,27 +622,29 @@ func checkNestedVCS(vcs *vcsCmd, dir, srcRoot string) error {
return nil
}
// repoRoot represents a version control system, a repo, and a root of
// where to put it on disk.
type repoRoot struct {
vcs *vcsCmd
// RepoRoot describes the repository root for a tree of source code.
type RepoRoot struct {
Repo string // repository URL, including scheme
Root string // import path corresponding to root of repo
IsCustom bool // defined by served <meta> tags (as opposed to hard-coded pattern)
VCS string // vcs type ("mod", "git", ...)
// repo is the repository URL, including scheme
repo string
// root is the import path corresponding to the root of the
// repository
root string
// isCustom is true for custom import paths (those defined by HTML meta tags)
isCustom bool
vcs *vcsCmd // internal: vcs command access
}
var httpPrefixRE = regexp.MustCompile(`^https?:`)
// repoRootForImportPath analyzes importPath to determine the
// ModuleMode specifies whether to prefer modules when looking up code sources.
type ModuleMode int
const (
IgnoreMod ModuleMode = iota
PreferMod
)
// RepoRootForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoRoot, error) {
func RepoRootForImportPath(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
rr, err := repoRootFromVCSPaths(importPath, "", security, vcsPaths)
if err == errUnknownSite {
// If there are wildcards, look up the thing before the wildcard,
@ -654,7 +654,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR
if i := strings.Index(lookup, "/.../"); i >= 0 {
lookup = lookup[:i]
}
rr, err = repoRootForImportDynamic(lookup, security)
rr, err = repoRootForImportDynamic(lookup, mod, security)
if err != nil {
err = fmt.Errorf("unrecognized import path %q (%v)", importPath, err)
}
@ -667,7 +667,7 @@ func repoRootForImportPath(importPath string, security web.SecurityMode) (*repoR
}
}
if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.root, "...") {
if err == nil && strings.Contains(importPath, "...") && strings.Contains(rr.Root, "...") {
// Do not allow wildcards in the repo root.
rr = nil
err = fmt.Errorf("cannot expand ... in %q", importPath)
@ -680,7 +680,7 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping")
// repoRootFromVCSPaths attempts to map importPath to a repoRoot
// using the mappings defined in vcsPaths.
// If scheme is non-empty, that scheme is forced.
func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*repoRoot, error) {
func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode, vcsPaths []*vcsPath) (*RepoRoot, error) {
// A common error is to use https://packagepath because that's what
// hg and git require. Diagnose this helpfully.
if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil {
@ -733,28 +733,32 @@ func repoRootFromVCSPaths(importPath, scheme string, security web.SecurityMode,
if security == web.Secure && !vcs.isSecureScheme(scheme) {
continue
}
if vcs.ping(scheme, match["repo"]) == nil {
if vcs.pingCmd != "" && vcs.ping(scheme, match["repo"]) == nil {
match["repo"] = scheme + "://" + match["repo"]
break
goto Found
}
}
// No scheme found. Fall back to the first one.
match["repo"] = vcs.scheme[0] + "://" + match["repo"]
Found:
}
}
rr := &repoRoot{
rr := &RepoRoot{
Repo: match["repo"],
Root: match["root"],
VCS: vcs.cmd,
vcs: vcs,
repo: match["repo"],
root: match["root"],
}
return rr, nil
}
return nil, errUnknownSite
}
// repoRootForImportDynamic finds a *repoRoot for a custom domain that's not
// repoRootForImportDynamic finds a *RepoRoot for a custom domain that's not
// statically known by repoRootForImportPathStatic.
//
// This handles custom import paths like "name.tld/pkg/foo" or just "name.tld".
func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*repoRoot, error) {
func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.SecurityMode) (*RepoRoot, error) {
slash := strings.Index(importPath, "/")
if slash < 0 {
slash = len(importPath)
@ -772,7 +776,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
return nil, fmt.Errorf(msg, err)
}
defer body.Close()
imports, err := parseMetaGoImports(body)
imports, err := parseMetaGoImports(body, mod)
if err != nil {
return nil, fmt.Errorf("parsing %s: %v", importPath, err)
}
@ -799,7 +803,7 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
}
urlStr0 := urlStr
var imports []metaImport
urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, security)
urlStr, imports, err = metaImportsForPrefix(mmi.Prefix, mod, security)
if err != nil {
return nil, err
}
@ -809,48 +813,34 @@ func repoRootForImportDynamic(importPath string, security web.SecurityMode) (*re
}
}
if err := validateRepoRootScheme(mmi.RepoRoot); err != nil {
if err := validateRepoRoot(mmi.RepoRoot); err != nil {
return nil, fmt.Errorf("%s: invalid repo root %q: %v", urlStr, mmi.RepoRoot, err)
}
rr := &repoRoot{
vcs: vcsByCmd(mmi.VCS),
repo: mmi.RepoRoot,
root: mmi.Prefix,
isCustom: true,
}
if rr.vcs == nil {
vcs := vcsByCmd(mmi.VCS)
if vcs == nil && mmi.VCS != "mod" {
return nil, fmt.Errorf("%s: unknown vcs %q", urlStr, mmi.VCS)
}
rr := &RepoRoot{
Repo: mmi.RepoRoot,
Root: mmi.Prefix,
IsCustom: true,
VCS: mmi.VCS,
vcs: vcs,
}
return rr, nil
}
// validateRepoRootScheme returns an error if repoRoot does not seem
// to have a valid URL scheme. At this point we permit things that
// aren't valid URLs, although later, if not using -insecure, we will
// restrict repoRoots to be valid URLs. This is only because we've
// historically permitted them, and people may depend on that.
func validateRepoRootScheme(repoRoot string) error {
end := strings.Index(repoRoot, "://")
if end <= 0 {
// validateRepoRoot returns an error if repoRoot does not seem to be
// a valid URL with scheme.
func validateRepoRoot(repoRoot string) error {
url, err := url.Parse(repoRoot)
if err != nil {
return err
}
if url.Scheme == "" {
return errors.New("no scheme")
}
// RFC 3986 section 3.1.
for i := 0; i < end; i++ {
c := repoRoot[i]
switch {
case 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z':
// OK.
case '0' <= c && c <= '9' || c == '+' || c == '-' || c == '.':
// OK except at start.
if i == 0 {
return errors.New("invalid scheme")
}
default:
return errors.New("invalid scheme")
}
}
return nil
}
@ -868,7 +858,7 @@ var (
// It is an error if no imports are found.
// urlStr will still be valid if err != nil.
// The returned urlStr will be of the form "https://golang.org/x/tools?go-get=1"
func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
func metaImportsForPrefix(importPrefix string, mod ModuleMode, security web.SecurityMode) (urlStr string, imports []metaImport, err error) {
setCache := func(res fetchResult) (fetchResult, error) {
fetchCacheMu.Lock()
defer fetchCacheMu.Unlock()
@ -888,7 +878,7 @@ func metaImportsForPrefix(importPrefix string, security web.SecurityMode) (urlSt
if err != nil {
return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("fetch %s: %v", urlStr, err)})
}
imports, err := parseMetaGoImports(body)
imports, err := parseMetaGoImports(body, mod)
if err != nil {
return setCache(fetchResult{urlStr: urlStr, err: fmt.Errorf("parsing %s: %v", urlStr, err)})
}
@ -956,7 +946,13 @@ func matchGoImport(imports []metaImport, importPath string) (metaImport, error)
continue
}
if match != -1 {
if match >= 0 {
if imports[match].VCS == "mod" && im.VCS != "mod" {
// All the mod entries precede all the non-mod entries.
// We have a mod entry and don't care about the rest,
// matching or not.
break
}
return metaImport{}, fmt.Errorf("multiple meta tags match import path %q", importPath)
}
match = i
@ -1001,7 +997,7 @@ var vcsPaths = []*vcsPath{
// IBM DevOps Services (JazzHub)
{
prefix: "hub.jazz.net/git/",
re: `^(?P<root>hub.jazz.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
re: `^(?P<root>hub\.jazz\.net/git/[a-z0-9]+/[A-Za-z0-9_.\-]+)(/[A-Za-z0-9_.\-]+)*$`,
vcs: "git",
repo: "https://{root}",
check: noVCSSuffix,
@ -1010,7 +1006,7 @@ var vcsPaths = []*vcsPath{
// Git at Apache
{
prefix: "git.apache.org/",
re: `^(?P<root>git.apache.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`,
re: `^(?P<root>git\.apache\.org/[a-z0-9_.\-]+\.git)(/[A-Za-z0-9_.\-]+)*$`,
vcs: "git",
repo: "https://{root}",
},

View File

@ -16,49 +16,53 @@ import (
"cmd/go/internal/web"
)
// Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath.
// Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath.
// TODO(cmang): Add tests for SVN and BZR.
func TestRepoRootForImportPath(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
tests := []struct {
path string
want *repoRoot
want *RepoRoot
}{
{
"github.com/golang/groupcache",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://github.com/golang/groupcache",
Repo: "https://github.com/golang/groupcache",
},
},
// Unicode letters in directories (issue 18660).
{
"github.com/user/unicode/испытание",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://github.com/user/unicode",
Repo: "https://github.com/user/unicode",
},
},
// IBM DevOps Services tests
{
"hub.jazz.net/git/user1/pkgname",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user1/pkgname",
Repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
{
"hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user1/pkgname",
Repo: "https://hub.jazz.net/git/user1/pkgname",
},
},
{
"hub.jazz.net",
nil,
},
{
"hubajazz.net",
nil,
},
{
"hub2.jazz.net",
nil,
@ -87,9 +91,9 @@ func TestRepoRootForImportPath(t *testing.T) {
},
{
"hub.jazz.net/git/user/pkg.name",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://hub.jazz.net/git/user/pkg.name",
Repo: "https://hub.jazz.net/git/user/pkg.name",
},
},
// User names cannot have uppercase letters
@ -100,9 +104,9 @@ func TestRepoRootForImportPath(t *testing.T) {
// OpenStack tests
{
"git.openstack.org/openstack/swift",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://git.openstack.org/openstack/swift",
Repo: "https://git.openstack.org/openstack/swift",
},
},
// Trailing .git is less preferred but included for
@ -110,16 +114,16 @@ func TestRepoRootForImportPath(t *testing.T) {
// be compilable on both old and new go
{
"git.openstack.org/openstack/swift.git",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://git.openstack.org/openstack/swift.git",
Repo: "https://git.openstack.org/openstack/swift.git",
},
},
{
"git.openstack.org/openstack/swift/go/hummingbird",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://git.openstack.org/openstack/swift",
Repo: "https://git.openstack.org/openstack/swift",
},
},
{
@ -140,25 +144,29 @@ func TestRepoRootForImportPath(t *testing.T) {
"git.apache.org/package-name/path/to/lib",
nil,
},
{
"gitbapache.org",
nil,
},
{
"git.apache.org/package-name.git",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://git.apache.org/package-name.git",
Repo: "https://git.apache.org/package-name.git",
},
},
{
"git.apache.org/package-name_2.x.git/path/to/lib",
&repoRoot{
&RepoRoot{
vcs: vcsGit,
repo: "https://git.apache.org/package-name_2.x.git",
Repo: "https://git.apache.org/package-name_2.x.git",
},
},
{
"chiselapp.com/user/kyle/repository/fossilgg",
&repoRoot{
&RepoRoot{
vcs: vcsFossil,
repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
Repo: "https://chiselapp.com/user/kyle/repository/fossilgg",
},
},
{
@ -173,21 +181,21 @@ func TestRepoRootForImportPath(t *testing.T) {
}
for _, test := range tests {
got, err := repoRootForImportPath(test.path, web.Secure)
got, err := RepoRootForImportPath(test.path, IgnoreMod, web.Secure)
want := test.want
if want == nil {
if err == nil {
t.Errorf("repoRootForImportPath(%q): Error expected but not received", test.path)
t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path)
}
continue
}
if err != nil {
t.Errorf("repoRootForImportPath(%q): %v", test.path, err)
t.Errorf("RepoRootForImportPath(%q): %v", test.path, err)
continue
}
if got.vcs.name != want.vcs.name || got.repo != want.repo {
t.Errorf("repoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.repo, want.vcs, want.repo)
if got.vcs.name != want.vcs.name || got.Repo != want.Repo {
t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.vcs, got.Repo, want.vcs, want.Repo)
}
}
}
@ -219,18 +227,18 @@ func TestFromDir(t *testing.T) {
f.Close()
}
want := repoRoot{
want := RepoRoot{
vcs: vcs,
root: path.Join("example.com", vcs.name),
Root: path.Join("example.com", vcs.name),
}
var got repoRoot
got.vcs, got.root, err = vcsFromDir(dir, tempDir)
var got RepoRoot
got.vcs, got.Root, err = vcsFromDir(dir, tempDir)
if err != nil {
t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err)
continue
}
if got.vcs.name != want.vcs.name || got.root != want.root {
t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.root, want.vcs, want.root)
if got.vcs.name != want.vcs.name || got.Root != want.Root {
t.Errorf("FromDir(%q, %q) = VCS(%s) Root(%s), want VCS(%s) Root(%s)", dir, tempDir, got.vcs, got.Root, want.vcs, want.Root)
}
}
}
@ -393,6 +401,22 @@ func TestMatchGoImport(t *testing.T) {
path: "different.example.com/user/foo",
err: errors.New("meta tags do not match import path"),
},
{
imports: []metaImport{
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
},
path: "myitcv.io/blah2/foo",
mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
},
{
imports: []metaImport{
{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"},
{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
},
path: "myitcv.io/other",
mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
},
}
for _, test := range tests {
@ -409,45 +433,46 @@ func TestMatchGoImport(t *testing.T) {
}
}
func TestValidateRepoRootScheme(t *testing.T) {
func TestValidateRepoRoot(t *testing.T) {
tests := []struct {
root string
err string
ok bool
}{
{
root: "",
err: "no scheme",
ok: false,
},
{
root: "http://",
err: "",
ok: true,
},
{
root: "a://",
err: "",
root: "git+ssh://",
ok: true,
},
{
root: "a#://",
err: "invalid scheme",
root: "http#://",
ok: false,
},
{
root: "-config",
ok: false,
},
{
root: "-config://",
err: "invalid scheme",
ok: false,
},
}
for _, test := range tests {
err := validateRepoRootScheme(test.root)
if err == nil {
if test.err != "" {
t.Errorf("validateRepoRootScheme(%q) = nil, want %q", test.root, test.err)
err := validateRepoRoot(test.root)
ok := err == nil
if ok != test.ok {
want := "error"
if test.ok {
want = "nil"
}
} else if test.err == "" {
if err != nil {
t.Errorf("validateRepoRootScheme(%q) = %q, want nil", test.root, test.err)
}
} else if err.Error() != test.err {
t.Errorf("validateRepoRootScheme(%q) = %q, want %q", test.root, err, test.err)
t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want)
}
}
}

View File

@ -21,82 +21,95 @@ import (
// Help implements the 'help' command.
func Help(args []string) {
if len(args) == 0 {
PrintUsage(os.Stdout)
// not exit 2: succeeded at 'go help'.
return
}
if len(args) != 1 {
fmt.Fprintf(os.Stderr, "usage: go help command\n\nToo many arguments given.\n")
os.Exit(2) // failed at 'go help'
}
arg := args[0]
// 'go help documentation' generates doc.go.
if arg == "documentation" {
if len(args) == 1 && args[0] == "documentation" {
fmt.Println("// Copyright 2011 The Go Authors. All rights reserved.")
fmt.Println("// Use of this source code is governed by a BSD-style")
fmt.Println("// license that can be found in the LICENSE file.")
fmt.Println()
fmt.Println("// DO NOT EDIT THIS FILE. GENERATED BY mkalldocs.sh.")
fmt.Println("// Code generated by mkalldocs.sh; DO NOT EDIT.")
fmt.Println("// Edit the documentation in other files and rerun mkalldocs.sh to generate this one.")
fmt.Println()
buf := new(bytes.Buffer)
PrintUsage(buf)
PrintUsage(buf, base.Go)
usage := &base.Command{Long: buf.String()}
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, append([]*base.Command{usage}, base.Commands...))
cmds := []*base.Command{usage}
for _, cmd := range base.Go.Commands {
if cmd.UsageLine == "gopath-get" {
// Avoid duplication of the "get" documentation.
continue
}
cmds = append(cmds, cmd)
cmds = append(cmds, cmd.Commands...)
}
tmpl(&commentWriter{W: os.Stdout}, documentationTemplate, cmds)
fmt.Println("package main")
return
}
for _, cmd := range base.Commands {
if cmd.Name() == arg {
tmpl(os.Stdout, helpTemplate, cmd)
// not exit 2: succeeded at 'go help cmd'.
return
cmd := base.Go
Args:
for i, arg := range args {
for _, sub := range cmd.Commands {
if sub.Name() == arg {
cmd = sub
continue Args
}
}
// helpSuccess is the help command using as many args as possible that would succeed.
helpSuccess := "go help"
if i > 0 {
helpSuccess = " " + strings.Join(args[:i], " ")
}
fmt.Fprintf(os.Stderr, "go help %s: unknown help topic. Run '%s'.\n", strings.Join(args, " "), helpSuccess)
os.Exit(2) // failed at 'go help cmd'
}
fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'go help'.\n", arg)
os.Exit(2) // failed at 'go help cmd'
if len(cmd.Commands) > 0 {
PrintUsage(os.Stdout, cmd)
} else {
tmpl(os.Stdout, helpTemplate, cmd)
}
// not exit 2: succeeded at 'go help cmd'.
return
}
var usageTemplate = `Go is a tool for managing Go source code.
var usageTemplate = `{{.Long | trim}}
Usage:
go command [arguments]
{{.UsageLine}} <command> [arguments]
The commands are:
{{range .}}{{if .Runnable}}
{{range .Commands}}{{if or (.Runnable) .Commands}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "go help [command]" for more information about a command.
Use "go help{{with .LongName}} {{.}}{{end}} <command>" for more information about a command.
{{if eq (.UsageLine) "go"}}
Additional help topics:
{{range .}}{{if not .Runnable}}
{{range .Commands}}{{if and (not .Runnable) (not .Commands)}}
{{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
Use "go help [topic]" for more information about that topic.
Use "go help{{with .LongName}} {{.}}{{end}} <topic>" for more information about that topic.
{{end}}
`
var helpTemplate = `{{if .Runnable}}usage: go {{.UsageLine}}
var helpTemplate = `{{if .Runnable}}usage: {{.UsageLine}}
{{end}}{{.Long | trim}}
`
var documentationTemplate = `{{range .}}{{if .Short}}{{.Short | capitalize}}
{{end}}{{if .Runnable}}Usage:
{{end}}{{if .Commands}}` + usageTemplate + `{{else}}{{if .Runnable}}Usage:
go {{.UsageLine}}
{{.UsageLine}}
{{end}}{{.Long | trim}}
{{end}}`
{{end}}{{end}}`
// commentWriter writes a Go comment to the underlying io.Writer,
// using line comment form (//).
@ -171,8 +184,8 @@ func capitalize(s string) string {
return string(unicode.ToTitle(r)) + s[n:]
}
func PrintUsage(w io.Writer) {
func PrintUsage(w io.Writer, cmd *base.Command) {
bw := bufio.NewWriter(w)
tmpl(bw, usageTemplate, base.Commands)
tmpl(bw, usageTemplate, cmd)
bw.Flush()
}

View File

@ -30,7 +30,7 @@ the C or C++ compiler, respectively, to use.
var HelpPackages = &base.Command{
UsageLine: "packages",
Short: "package lists",
Short: "package lists and patterns",
Long: `
Many commands apply to a set of packages:
@ -54,9 +54,11 @@ for packages to be built with the go tool:
- "main" denotes the top-level package in a stand-alone executable.
- "all" expands to all package directories found in all the GOPATH
- "all" expands to all packages found in all the GOPATH
trees. For example, 'go list all' lists all the packages on the local
system.
system. When using modules, "all" expands to all packages in
the main module and their dependencies, including dependencies
needed by tests of any of those.
- "std" is like all but expands to just the packages in the standard
Go library.
@ -193,6 +195,7 @@ using the named version control system, and then the path inside
that repository. The supported version control systems are:
Bazaar .bzr
Fossil .fossil
Git .git
Mercurial .hg
Subversion .svn
@ -236,7 +239,7 @@ The meta tag should appear as early in the file as possible.
In particular, it should appear before any raw JavaScript or CSS,
to avoid confusing the go command's restricted parser.
The vcs is one of "git", "hg", "svn", etc,
The vcs is one of "bzr", "fossil", "git", "hg", "svn".
The repo-root is the root of the version control system
containing a scheme and not containing a .vcs qualifier.
@ -258,12 +261,22 @@ the go tool will verify that https://example.org/?go-get=1 contains the
same meta tag and then git clone https://code.org/r/p/exproj into
GOPATH/src/example.org.
New downloaded packages are written to the first directory listed in the GOPATH
environment variable (For more details see: 'go help gopath').
When using GOPATH, downloaded packages are written to the first directory
listed in the GOPATH environment variable.
(See 'go help gopath-get' and 'go help gopath'.)
The go command attempts to download the version of the
package appropriate for the Go release being used.
Run 'go help get' for more.
When using modules, downloaded packages are stored in the module cache.
(See 'go help modules-get' and 'go help goproxy'.)
When using modules, an additional variant of the go-import meta tag is
recognized and is preferred over those listing version control systems.
That variant uses "mod" as the vcs in the content value, as in:
<meta name="go-import" content="example.org mod https://code.org/moduleproxy">
This tag means to fetch modules with paths beginning with example.org
from the module proxy available at the URL https://code.org/moduleproxy.
See 'go help goproxy' for details about the proxy protocol.
Import path checking
@ -286,6 +299,9 @@ Import path checking is disabled for code found within vendor trees.
This makes it possible to copy code into alternate locations in vendor trees
without needing to update import comments.
Import path checking is also disabled when using modules.
Import path comments are obsoleted by the go.mod file's module statement.
See https://golang.org/s/go14customimport for details.
`,
}
@ -358,6 +374,12 @@ in the list.
See https://golang.org/doc/code.html for an example.
GOPATH and Modules
When using modules, GOPATH is no longer used for resolving imports.
However, it is still used to store downloaded source code (in GOPATH/pkg/mod)
and compiled commands (in GOPATH/bin).
Internal Directories
Code in or below a directory named "internal" is importable only
@ -461,11 +483,21 @@ General-purpose environment variables:
Examples are amd64, 386, arm, ppc64.
GOBIN
The directory where 'go install' will install a command.
GOCACHE
The directory where the go command will store cached
information for reuse in future builds.
GOFLAGS
A space-separated list of -flag=value settings to apply
to go commands by default, when the given flag is known by
the current command. Flags listed on the command-line
are applied after this list and therefore override it.
GOOS
The operating system for which to compile code.
Examples are linux, darwin, windows, netbsd.
GOPATH
For more details see: 'go help gopath'.
GOPROXY
URL of Go module proxy. See 'go help goproxy'.
GORACE
Options for the race detector.
See https://golang.org/doc/articles/race_detector.html.
@ -474,9 +506,6 @@ General-purpose environment variables:
GOTMPDIR
The directory where the go command will write
temporary source files, packages, and binaries.
GOCACHE
The directory where the go command will store
cached information for reuse in future builds.
Environment variables for use with cgo:
@ -523,6 +552,9 @@ Architecture-specific environment variables:
GOMIPS
For GOARCH=mips{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
GOMIPS64
For GOARCH=mips64{,le}, whether to use floating point instructions.
Valid values are hardfloat (default), softfloat.
Special-purpose environment variables:
@ -542,6 +574,20 @@ Special-purpose environment variables:
Defined by Git. A colon-separated list of schemes that are allowed to be used
with git fetch/clone. If set, any scheme not explicitly mentioned will be
considered insecure by 'go get'.
Additional information available from 'go env' but not read from the environment:
GOEXE
The executable file name suffix (".exe" on Windows, "" on other systems).
GOHOSTARCH
The architecture (GOARCH) of the Go toolchain binaries.
GOHOSTOS
The operating system (GOOS) of the Go toolchain binaries.
GOMOD
The absolute path to the go.mod of the main module,
or the empty string if not using modules.
GOTOOLDIR
The directory where the go tools (compile, cover, doc, etc...) are installed.
`,
}
@ -651,6 +697,7 @@ The default location for cache data is a subdirectory named go-build
in the standard user cache directory for the current operating system.
Setting the GOCACHE environment variable overrides this default,
and running 'go env GOCACHE' prints the current cache directory.
You can set the variable to 'off' to disable the cache.
The go command periodically deletes cached data that has not been
used recently. Running 'go clean -cache' deletes all cached data.

View File

@ -0,0 +1,211 @@
// Copyright 2018 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.
// Copied from Go distribution src/go/build/build.go, syslist.go
package imports
import (
"bytes"
"strings"
"unicode"
)
var slashslash = []byte("//")
// ShouldBuild reports whether it is okay to use this file,
// The rule is that in the file's leading run of // comments
// and blank lines, which must be followed by a blank line
// (to avoid including a Go package clause doc comment),
// lines beginning with '// +build' are taken as build directives.
//
// The file is accepted only if each such line lists something
// matching the file. For example:
//
// // +build windows linux
//
// marks the file as applicable only on Windows and Linux.
//
// If tags["*"] is true, then ShouldBuild will consider every
// build tag except "ignore" to be both true and false for
// the purpose of satisfying build tags, in order to estimate
// (conservatively) whether a file could ever possibly be used
// in any build.
//
func ShouldBuild(content []byte, tags map[string]bool) bool {
// Pass 1. Identify leading run of // comments and blank lines,
// which must be followed by a blank line.
end := 0
p := content
for len(p) > 0 {
line := p
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, p = line[:i], p[i+1:]
} else {
p = p[len(p):]
}
line = bytes.TrimSpace(line)
if len(line) == 0 { // Blank line
end = len(content) - len(p)
continue
}
if !bytes.HasPrefix(line, slashslash) { // Not comment line
break
}
}
content = content[:end]
// Pass 2. Process each line in the run.
p = content
allok := true
for len(p) > 0 {
line := p
if i := bytes.IndexByte(line, '\n'); i >= 0 {
line, p = line[:i], p[i+1:]
} else {
p = p[len(p):]
}
line = bytes.TrimSpace(line)
if !bytes.HasPrefix(line, slashslash) {
continue
}
line = bytes.TrimSpace(line[len(slashslash):])
if len(line) > 0 && line[0] == '+' {
// Looks like a comment +line.
f := strings.Fields(string(line))
if f[0] == "+build" {
ok := false
for _, tok := range f[1:] {
if matchTags(tok, tags) {
ok = true
}
}
if !ok {
allok = false
}
}
}
}
return allok
}
// matchTags reports whether the name is one of:
//
// tag (if tags[tag] is true)
// !tag (if tags[tag] is false)
// a comma-separated list of any of these
//
func matchTags(name string, tags map[string]bool) bool {
if name == "" {
return false
}
if i := strings.Index(name, ","); i >= 0 {
// comma-separated list
ok1 := matchTags(name[:i], tags)
ok2 := matchTags(name[i+1:], tags)
return ok1 && ok2
}
if strings.HasPrefix(name, "!!") { // bad syntax, reject always
return false
}
if strings.HasPrefix(name, "!") { // negation
return len(name) > 1 && matchTag(name[1:], tags, false)
}
return matchTag(name, tags, true)
}
// matchTag reports whether the tag name is valid and satisfied by tags[name]==want.
func matchTag(name string, tags map[string]bool, want bool) bool {
// Tags must be letters, digits, underscores or dots.
// Unlike in Go identifiers, all digits are fine (e.g., "386").
for _, c := range name {
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
return false
}
}
if tags["*"] && name != "" && name != "ignore" {
// Special case for gathering all possible imports:
// if we put * in the tags map then all tags
// except "ignore" are considered both present and not
// (so we return true no matter how 'want' is set).
return true
}
have := tags[name]
if name == "linux" {
have = have || tags["android"]
}
return have == want
}
// MatchFile returns false if the name contains a $GOOS or $GOARCH
// suffix which does not match the current system.
// The recognized name formats are:
//
// name_$(GOOS).*
// name_$(GOARCH).*
// name_$(GOOS)_$(GOARCH).*
// name_$(GOOS)_test.*
// name_$(GOARCH)_test.*
// name_$(GOOS)_$(GOARCH)_test.*
//
// An exception: if GOOS=android, then files with GOOS=linux are also matched.
//
// If tags["*"] is true, then MatchFile will consider all possible
// GOOS and GOARCH to be available and will consequently
// always return true.
func MatchFile(name string, tags map[string]bool) bool {
if tags["*"] {
return true
}
if dot := strings.Index(name, "."); dot != -1 {
name = name[:dot]
}
// Before Go 1.4, a file called "linux.go" would be equivalent to having a
// build tag "linux" in that file. For Go 1.4 and beyond, we require this
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
// systems, such as android, to arrive without breaking existing code with
// innocuous source code in "android.go". The easiest fix: cut everything
// in the name before the initial _.
i := strings.Index(name, "_")
if i < 0 {
return true
}
name = name[i:] // ignore everything before first _
l := strings.Split(name, "_")
if n := len(l); n > 0 && l[n-1] == "test" {
l = l[:n-1]
}
n := len(l)
if n >= 2 && KnownOS[l[n-2]] && KnownArch[l[n-1]] {
return tags[l[n-2]] && tags[l[n-1]]
}
if n >= 1 && KnownOS[l[n-1]] {
return tags[l[n-1]]
}
if n >= 1 && KnownArch[l[n-1]] {
return tags[l[n-1]]
}
return true
}
var KnownOS = make(map[string]bool)
var KnownArch = make(map[string]bool)
func init() {
for _, v := range strings.Fields(goosList) {
KnownOS[v] = true
}
for _, v := range strings.Fields(goarchList) {
KnownArch[v] = true
}
}
const goosList = "android darwin dragonfly freebsd js linux nacl netbsd openbsd plan9 solaris windows zos "
const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc riscv riscv64 s390 s390x sparc sparc64 wasm "

View File

@ -0,0 +1,249 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied from Go distribution src/go/build/read.go.
package imports
import (
"bufio"
"errors"
"io"
"unicode/utf8"
)
type importReader struct {
b *bufio.Reader
buf []byte
peek byte
err error
eof bool
nerr int
}
func isIdent(c byte) bool {
return 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c >= utf8.RuneSelf
}
var (
errSyntax = errors.New("syntax error")
errNUL = errors.New("unexpected NUL in input")
)
// syntaxError records a syntax error, but only if an I/O error has not already been recorded.
func (r *importReader) syntaxError() {
if r.err == nil {
r.err = errSyntax
}
}
// readByte reads the next byte from the input, saves it in buf, and returns it.
// If an error occurs, readByte records the error in r.err and returns 0.
func (r *importReader) readByte() byte {
c, err := r.b.ReadByte()
if err == nil {
r.buf = append(r.buf, c)
if c == 0 {
err = errNUL
}
}
if err != nil {
if err == io.EOF {
r.eof = true
} else if r.err == nil {
r.err = err
}
c = 0
}
return c
}
// peekByte returns the next byte from the input reader but does not advance beyond it.
// If skipSpace is set, peekByte skips leading spaces and comments.
func (r *importReader) peekByte(skipSpace bool) byte {
if r.err != nil {
if r.nerr++; r.nerr > 10000 {
panic("go/build: import reader looping")
}
return 0
}
// Use r.peek as first input byte.
// Don't just return r.peek here: it might have been left by peekByte(false)
// and this might be peekByte(true).
c := r.peek
if c == 0 {
c = r.readByte()
}
for r.err == nil && !r.eof {
if skipSpace {
// For the purposes of this reader, semicolons are never necessary to
// understand the input and are treated as spaces.
switch c {
case ' ', '\f', '\t', '\r', '\n', ';':
c = r.readByte()
continue
case '/':
c = r.readByte()
if c == '/' {
for c != '\n' && r.err == nil && !r.eof {
c = r.readByte()
}
} else if c == '*' {
var c1 byte
for (c != '*' || c1 != '/') && r.err == nil {
if r.eof {
r.syntaxError()
}
c, c1 = c1, r.readByte()
}
} else {
r.syntaxError()
}
c = r.readByte()
continue
}
}
break
}
r.peek = c
return r.peek
}
// nextByte is like peekByte but advances beyond the returned byte.
func (r *importReader) nextByte(skipSpace bool) byte {
c := r.peekByte(skipSpace)
r.peek = 0
return c
}
// readKeyword reads the given keyword from the input.
// If the keyword is not present, readKeyword records a syntax error.
func (r *importReader) readKeyword(kw string) {
r.peekByte(true)
for i := 0; i < len(kw); i++ {
if r.nextByte(false) != kw[i] {
r.syntaxError()
return
}
}
if isIdent(r.peekByte(false)) {
r.syntaxError()
}
}
// readIdent reads an identifier from the input.
// If an identifier is not present, readIdent records a syntax error.
func (r *importReader) readIdent() {
c := r.peekByte(true)
if !isIdent(c) {
r.syntaxError()
return
}
for isIdent(r.peekByte(false)) {
r.peek = 0
}
}
// readString reads a quoted string literal from the input.
// If an identifier is not present, readString records a syntax error.
func (r *importReader) readString(save *[]string) {
switch r.nextByte(true) {
case '`':
start := len(r.buf) - 1
for r.err == nil {
if r.nextByte(false) == '`' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof {
r.syntaxError()
}
}
case '"':
start := len(r.buf) - 1
for r.err == nil {
c := r.nextByte(false)
if c == '"' {
if save != nil {
*save = append(*save, string(r.buf[start:]))
}
break
}
if r.eof || c == '\n' {
r.syntaxError()
}
if c == '\\' {
r.nextByte(false)
}
}
default:
r.syntaxError()
}
}
// readImport reads an import clause - optional identifier followed by quoted string -
// from the input.
func (r *importReader) readImport(imports *[]string) {
c := r.peekByte(true)
if c == '.' {
r.peek = 0
} else if isIdent(c) {
r.readIdent()
}
r.readString(imports)
}
// ReadComments is like ioutil.ReadAll, except that it only reads the leading
// block of comments in the file.
func ReadComments(f io.Reader) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.peekByte(true)
if r.err == nil && !r.eof {
// Didn't reach EOF, so must have found a non-space byte. Remove it.
r.buf = r.buf[:len(r.buf)-1]
}
return r.buf, r.err
}
// ReadImports is like ioutil.ReadAll, except that it expects a Go file as input
// and stops reading the input once the imports have completed.
func ReadImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.readKeyword("package")
r.readIdent()
for r.peekByte(true) == 'i' {
r.readKeyword("import")
if r.peekByte(true) == '(' {
r.nextByte(false)
for r.peekByte(true) != ')' && r.err == nil {
r.readImport(imports)
}
r.nextByte(false)
} else {
r.readImport(imports)
}
}
// If we stopped successfully before EOF, we read a byte that told us we were done.
// Return all but that last byte, which would cause a syntax error if we let it through.
if r.err == nil && !r.eof {
return r.buf[:len(r.buf)-1], nil
}
// If we stopped for a syntax error, consume the whole file so that
// we are sure we don't change the errors that go/parser returns.
if r.err == errSyntax && !reportSyntaxError {
r.err = nil
for r.err == nil && !r.eof {
r.readByte()
}
}
return r.buf, r.err
}

View File

@ -0,0 +1,228 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Copied from Go distribution src/go/build/read.go.
package imports
import (
"io"
"strings"
"testing"
)
const quote = "`"
type readTest struct {
// Test input contains where readImports should stop.
in string
err string
}
var readImportsTests = []readTest{
{
`package p`,
"",
},
{
`package p; import "x"`,
"",
},
{
`package p; import . "x"`,
"",
},
{
`package p; import "x";var x = 1`,
"",
},
{
`package p
// comment
import "x"
import _ "x"
import a "x"
/* comment */
import (
"x" /* comment */
_ "x"
a "x" // comment
` + quote + `x` + quote + `
_ /*comment*/ ` + quote + `x` + quote + `
a ` + quote + `x` + quote + `
)
import (
)
import ()
import()import()import()
import();import();import()
var x = 1
`,
"",
},
}
var readCommentsTests = []readTest{
{
`package p`,
"",
},
{
`package p; import "x"`,
"",
},
{
`package p; import . "x"`,
"",
},
{
`// foo
/* bar */
/* quux */ // baz
/*/ zot */
// asdf
Hello, world`,
"",
},
}
func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, error)) {
for i, tt := range tests {
var in, testOut string
j := strings.Index(tt.in, "")
if j < 0 {
in = tt.in
testOut = tt.in
} else {
in = tt.in[:j] + tt.in[j+len(""):]
testOut = tt.in[:j]
}
r := strings.NewReader(in)
buf, err := read(r)
if err != nil {
if tt.err == "" {
t.Errorf("#%d: err=%q, expected success (%q)", i, err, string(buf))
continue
}
if !strings.Contains(err.Error(), tt.err) {
t.Errorf("#%d: err=%q, expected %q", i, err, tt.err)
continue
}
continue
}
if err == nil && tt.err != "" {
t.Errorf("#%d: success, expected %q", i, tt.err)
continue
}
out := string(buf)
if out != testOut {
t.Errorf("#%d: wrong output:\nhave %q\nwant %q\n", i, out, testOut)
}
}
}
func TestReadImports(t *testing.T) {
testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
}
func TestReadComments(t *testing.T) {
testRead(t, readCommentsTests, ReadComments)
}
var readFailuresTests = []readTest{
{
`package`,
"syntax error",
},
{
"package p\n\x00\nimport `math`\n",
"unexpected NUL in input",
},
{
`package p; import`,
"syntax error",
},
{
`package p; import "`,
"syntax error",
},
{
"package p; import ` \n\n",
"syntax error",
},
{
`package p; import "x`,
"syntax error",
},
{
`package p; import _`,
"syntax error",
},
{
`package p; import _ "`,
"syntax error",
},
{
`package p; import _ "x`,
"syntax error",
},
{
`package p; import .`,
"syntax error",
},
{
`package p; import . "`,
"syntax error",
},
{
`package p; import . "x`,
"syntax error",
},
{
`package p; import (`,
"syntax error",
},
{
`package p; import ("`,
"syntax error",
},
{
`package p; import ("x`,
"syntax error",
},
{
`package p; import ("x"`,
"syntax error",
},
}
func TestReadFailures(t *testing.T) {
// Errors should be reported (true arg to readImports).
testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return ReadImports(r, true, nil) })
}
func TestReadFailuresIgnored(t *testing.T) {
// Syntax errors should not be reported (false arg to readImports).
// Instead, entire file should be the output and no error.
// Convert tests not to return syntax errors.
tests := make([]readTest, len(readFailuresTests))
copy(tests, readFailuresTests)
for i := range tests {
tt := &tests[i]
if !strings.Contains(tt.err, "NUL") {
tt.err = ""
}
}
testRead(t, tests, func(r io.Reader) ([]byte, error) { return ReadImports(r, false, nil) })
}

View File

@ -0,0 +1,96 @@
// Copyright 2018 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 imports
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
)
func ScanDir(dir string, tags map[string]bool) ([]string, []string, error) {
infos, err := ioutil.ReadDir(dir)
if err != nil {
return nil, nil, err
}
var files []string
for _, info := range infos {
name := info.Name()
if info.Mode().IsRegular() && !strings.HasPrefix(name, "_") && strings.HasSuffix(name, ".go") && MatchFile(name, tags) {
files = append(files, filepath.Join(dir, name))
}
}
return scanFiles(files, tags, false)
}
func ScanFiles(files []string, tags map[string]bool) ([]string, []string, error) {
return scanFiles(files, tags, true)
}
func scanFiles(files []string, tags map[string]bool, explicitFiles bool) ([]string, []string, error) {
imports := make(map[string]bool)
testImports := make(map[string]bool)
numFiles := 0
Files:
for _, name := range files {
r, err := os.Open(name)
if err != nil {
return nil, nil, err
}
var list []string
data, err := ReadImports(r, false, &list)
r.Close()
if err != nil {
return nil, nil, fmt.Errorf("reading %s: %v", name, err)
}
// import "C" is implicit requirement of cgo tag.
// When listing files on the command line (explicitFiles=true)
// we do not apply build tag filtering but we still do apply
// cgo filtering, so no explicitFiles check here.
// Why? Because we always have, and it's not worth breaking
// that behavior now.
for _, path := range list {
if path == `"C"` && !tags["cgo"] && !tags["*"] {
continue Files
}
}
if !explicitFiles && !ShouldBuild(data, tags) {
continue
}
numFiles++
m := imports
if strings.HasSuffix(name, "_test.go") {
m = testImports
}
for _, p := range list {
q, err := strconv.Unquote(p)
if err != nil {
continue
}
m[q] = true
}
}
if numFiles == 0 {
return nil, nil, ErrNoGo
}
return keys(imports), keys(testImports), nil
}
var ErrNoGo = fmt.Errorf("no Go source files")
func keys(m map[string]bool) []string {
var list []string
for k := range m {
list = append(list, k)
}
sort.Strings(list)
return list
}

View File

@ -0,0 +1,67 @@
// Copyright 2018 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 imports
import (
"internal/testenv"
"path/filepath"
"reflect"
"runtime"
"testing"
)
func TestScan(t *testing.T) {
testenv.MustHaveGoBuild(t)
imports, testImports, err := ScanDir(filepath.Join(runtime.GOROOT(), "src/encoding/json"), Tags())
if err != nil {
t.Fatal(err)
}
foundBase64 := false
for _, p := range imports {
if p == "encoding/base64" {
foundBase64 = true
}
if p == "encoding/binary" {
// A dependency but not an import
t.Errorf("json reported as importing encoding/binary but does not")
}
if p == "net/http" {
// A test import but not an import
t.Errorf("json reported as importing encoding/binary but does not")
}
}
if !foundBase64 {
t.Errorf("json missing import encoding/base64 (%q)", imports)
}
foundHTTP := false
for _, p := range testImports {
if p == "net/http" {
foundHTTP = true
}
if p == "unicode/utf16" {
// A package import but not a test import
t.Errorf("json reported as test-importing unicode/utf16 but does not")
}
}
if !foundHTTP {
t.Errorf("json missing test import net/http (%q)", testImports)
}
}
func TestScanStar(t *testing.T) {
testenv.MustHaveGoBuild(t)
imports, _, err := ScanDir("testdata/import1", map[string]bool{"*": true})
if err != nil {
t.Fatal(err)
}
want := []string{"import1", "import2", "import3", "import4"}
if !reflect.DeepEqual(imports, want) {
t.Errorf("ScanDir testdata/import1:\nhave %v\nwant %v", imports, want)
}
}

View File

@ -0,0 +1,34 @@
// Copyright 2018 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 imports
import "cmd/go/internal/cfg"
var tags map[string]bool
func Tags() map[string]bool {
if tags == nil {
tags = loadTags()
}
return tags
}
func loadTags() map[string]bool {
tags := map[string]bool{
cfg.BuildContext.GOOS: true,
cfg.BuildContext.GOARCH: true,
cfg.BuildContext.Compiler: true,
}
if cfg.BuildContext.CgoEnabled {
tags["cgo"] = true
}
for _, tag := range cfg.BuildContext.BuildTags {
tags[tag] = true
}
for _, tag := range cfg.BuildContext.ReleaseTags {
tags[tag] = true
}
return tags
}

View File

@ -0,0 +1,3 @@
package x
import "import1"

View File

@ -0,0 +1,9 @@
// +build blahblh
// +build linux
// +build !linux
// +build windows
// +build darwin
package x
import "import4"

View File

@ -0,0 +1,3 @@
package xxxx
import "import3"

View File

@ -0,0 +1,3 @@
package x
import "import2"

View File

@ -7,23 +7,33 @@ package list
import (
"bufio"
"bytes"
"encoding/json"
"io"
"os"
"sort"
"strings"
"text/template"
"cmd/go/internal/base"
"cmd/go/internal/cache"
"cmd/go/internal/cfg"
"cmd/go/internal/load"
"cmd/go/internal/modload"
"cmd/go/internal/str"
"cmd/go/internal/work"
)
var CmdList = &base.Command{
UsageLine: "list [-e] [-f format] [-json] [build flags] [packages]",
Short: "list packages",
// Note: -f -json -m are listed explicitly because they are the most common list flags.
// Do not send CLs removing them because they're covered by [list flags].
UsageLine: "go list [-f format] [-json] [-m] [list flags] [build flags] [packages]",
Short: "list packages or modules",
Long: `
List lists the packages named by the import paths, one per line.
List lists the named packages, one per line.
The most commonly-used flags are -f and -json, which control the form
of the output printed for each package. Other list flags, documented below,
control more specific details.
The default output shows the package import path:
@ -33,40 +43,46 @@ The default output shows the package import path:
golang.org/x/net/html
The -f flag specifies an alternate format for the list, using the
syntax of package template. The default output is equivalent to -f
'{{.ImportPath}}'. The struct being passed to the template is:
syntax of package template. The default output is equivalent
to -f '{{.ImportPath}}'. The struct being passed to the template is:
type Package struct {
Dir string // directory containing package sources
ImportPath string // import path of package in dir
ImportComment string // path in import comment on package statement
Name string // package name
Doc string // package documentation string
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?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?
StaleReason string // explanation for Stale==true
Root string // Go root or Go path dir containing this package
ConflictDir string // this directory shadows Dir in $GOPATH
BinaryOnly bool // binary-only package: cannot be recompiled from sources
Dir string // directory containing package sources
ImportPath string // import path of package in dir
ImportComment string // path in import comment on package statement
Name string // package name
Doc string // package documentation string
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?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?
StaleReason string // explanation for Stale==true
Root string // Go root or Go path dir containing this package
ConflictDir string // this directory shadows Dir in $GOPATH
BinaryOnly bool // binary-only package: cannot be recompiled from sources
ForTest string // package is only for use in named test
Export string // file containing export data (when using -export)
Module *Module // info about package's containing module, if any (can be nil)
Match []string // command-line patterns matching this package
DepOnly bool // package is only a dependency, not explicitly listed
// Source files
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go sources files that import "C"
IgnoredGoFiles []string // .go sources ignored due to build constraints
CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files
FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive
TestGoFiles []string // _test.go files in package
XTestGoFiles []string // _test.go files outside package
GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)
CgoFiles []string // .go source files that import "C"
CompiledGoFiles []string // .go files presented to compiler (when using -compiled)
IgnoredGoFiles []string // .go source files ignored due to build constraints
CFiles []string // .c source files
CXXFiles []string // .cc, .cxx and .cpp source files
MFiles []string // .m source files
HFiles []string // .h, .hh, .hpp and .hxx source files
FFiles []string // .f, .F, .for and .f90 Fortran source files
SFiles []string // .s source files
SwigFiles []string // .swig files
SwigCXXFiles []string // .swigcxx files
SysoFiles []string // .syso object files to add to archive
TestGoFiles []string // _test.go files in package
XTestGoFiles []string // _test.go files outside package
// Cgo directives
CgoCFLAGS []string // cgo: flags for C compiler
@ -77,10 +93,11 @@ syntax of package template. The default output is equivalent to -f
CgoPkgConfig []string // cgo: pkg-config names
// Dependency information
Imports []string // import paths used by this package
Deps []string // all (recursively) imported dependencies
TestImports []string // imports from TestGoFiles
XTestImports []string // imports from XTestGoFiles
Imports []string // import paths used by this package
ImportMap map[string]string // map from source import to ImportPath (identity entries omitted)
Deps []string // all (recursively) imported dependencies
TestImports []string // imports from TestGoFiles
XTestImports []string // imports from XTestGoFiles
// Error information
Incomplete bool // this package or a dependency has an error
@ -92,7 +109,7 @@ Packages stored in vendor directories report an ImportPath that includes the
path to the vendor directory (for example, "d/vendor/p" instead of "p"),
so that the ImportPath uniquely identifies a given copy of a package.
The Imports, Deps, TestImports, and XTestImports lists also contain these
expanded imports paths. See golang.org/s/go15vendor for more about vendoring.
expanded import paths. See golang.org/s/go15vendor for more about vendoring.
The error information, if any, is
@ -102,22 +119,25 @@ The error information, if any, is
Err string // the error itself
}
The module information is a Module struct, defined in the discussion
of list -m below.
The template function "join" calls strings.Join.
The template function "context" returns the build context, defined as:
type Context struct {
GOARCH string // target architecture
GOOS string // target operating system
GOROOT string // Go root
GOPATH string // Go path
CgoEnabled bool // whether cgo can be used
UseAllFiles bool // use files regardless of +build lines, file names
Compiler string // compiler to assume when computing target paths
BuildTags []string // build constraints to match in +build lines
ReleaseTags []string // releases the current release is compatible with
InstallSuffix string // suffix to use in the name of the install dir
}
type Context struct {
GOARCH string // target architecture
GOOS string // target operating system
GOROOT string // Go root
GOPATH string // Go path
CgoEnabled bool // whether cgo can be used
UseAllFiles bool // use files regardless of +build lines, file names
Compiler string // compiler to assume when computing target paths
BuildTags []string // build constraints to match in +build lines
ReleaseTags []string // releases the current release is compatible with
InstallSuffix string // suffix to use in the name of the install dir
}
For more information about the meaning of these fields see the documentation
for the go/build package's Context type.
@ -125,6 +145,18 @@ for the go/build package's Context type.
The -json flag causes the package data to be printed in JSON format
instead of using the template format.
The -compiled flag causes list to set CompiledGoFiles to the Go source
files presented to the compiler. Typically this means that it repeats
the files listed in GoFiles and then also adds the Go code generated
by processing CgoFiles and SwigFiles. The Imports list contains the
union of all imports from both GoFiles and CompiledGoFiles.
The -deps flag causes list to iterate over not just the named packages
but also all their dependencies. It visits them in a depth-first post-order
traversal, so that a package is listed only after all its dependencies.
Packages not explicitly listed on the command line will have the DepOnly
field set to true.
The -e flag changes the handling of erroneous packages, those that
cannot be found or are malformed. By default, the list command
prints an error to standard error for each erroneous package and
@ -135,9 +167,120 @@ printing. Erroneous packages will have a non-empty ImportPath and
a non-nil Error field; other information may or may not be missing
(zeroed).
The -export flag causes list to set the Export field to the name of a
file containing up-to-date export information for the given package.
The -find flag causes list to identify the named packages but not
resolve their dependencies: the Imports and Deps lists will be empty.
The -test flag causes list to report not only the named packages
but also their test binaries (for packages with tests), to convey to
source code analysis tools exactly how test binaries are constructed.
The reported import path for a test binary is the import path of
the package followed by a ".test" suffix, as in "math/rand.test".
When building a test, it is sometimes necessary to rebuild certain
dependencies specially for that test (most commonly the tested
package itself). The reported import path of a package recompiled
for a particular test binary is followed by a space and the name of
the test binary in brackets, as in "math/rand [math/rand.test]"
or "regexp [sort.test]". The ForTest field is also set to the name
of the package being tested ("math/rand" or "sort" in the previous
examples).
The Dir, Target, Shlib, Root, ConflictDir, and Export file paths
are all absolute paths.
By default, the lists GoFiles, CgoFiles, and so on hold names of files in Dir
(that is, paths relative to Dir, not absolute paths).
The generated files added when using the -compiled and -test flags
are absolute paths referring to cached copies of generated Go source files.
Although they are Go source files, the paths may not end in ".go".
The -m flag causes list to list modules instead of packages.
When listing modules, the -f flag still specifies a format template
applied to a Go struct, but now a Module struct:
type Module struct {
Path string // module path
Version string // module version
Versions []string // available module versions (with -versions)
Replace *Module // replaced by this module
Time *time.Time // time version was created
Update *Module // available update, if any (with -u)
Main bool // is this the main module?
Indirect bool // is this module only an indirect dependency of main module?
Dir string // directory holding files for this module, if any
GoMod string // path to go.mod file for this module, if any
Error *ModuleError // error loading module
}
type ModuleError struct {
Err string // the error itself
}
The default output is to print the module path and then
information about the version and replacement if any.
For example, 'go list -m all' might print:
my/main/module
golang.org/x/text v0.3.0 => /tmp/text
rsc.io/pdf v0.1.1
The Module struct has a String method that formats this
line of output, so that the default format is equivalent
to -f '{{.String}}'.
Note that when a module has been replaced, its Replace field
describes the replacement module, and its Dir field is set to
the replacement's source code, if present. (That is, if Replace
is non-nil, then Dir is set to Replace.Dir, with no access to
the replaced source code.)
The -u flag adds information about available upgrades.
When the latest version of a given module is newer than
the current one, list -u sets the Module's Update field
to information about the newer module.
The Module's String method indicates an available upgrade by
formatting the newer version in brackets after the current version.
For example, 'go list -m -u all' might print:
my/main/module
golang.org/x/text v0.3.0 [v0.4.0] => /tmp/text
rsc.io/pdf v0.1.1 [v0.1.2]
(For tools, 'go list -m -u -json all' may be more convenient to parse.)
The -versions flag causes list to set the Module's Versions field
to a list of all known versions of that module, ordered according
to semantic versioning, earliest to latest. The flag also changes
the default output format to display the module path followed by the
space-separated version list.
The arguments to list -m are interpreted as a list of modules, not packages.
The main module is the module containing the current directory.
The active modules are the main module and its dependencies.
With no arguments, list -m shows the main module.
With arguments, list -m shows the modules specified by the arguments.
Any of the active modules can be specified by its module path.
The special pattern "all" specifies all the active modules, first the main
module and then dependencies sorted by module path.
A pattern containing "..." specifies the active modules whose
module paths match the pattern.
A query of the form path@version specifies the result of that query,
which is not limited to active modules.
See 'go help modules' for more about module queries.
The template function "module" takes a single string argument
that must be a module path or query and returns the specified
module as a Module struct. If an error occurs, the result will
be a Module struct with a non-nil Error field.
For more about build flags, see 'go help build'.
For more about specifying packages, see 'go help packages'.
For more about modules, see 'go help modules'.
`,
}
@ -146,20 +289,43 @@ func init() {
work.AddBuildFlags(CmdList)
}
var listE = CmdList.Flag.Bool("e", false, "")
var listFmt = CmdList.Flag.String("f", "{{.ImportPath}}", "")
var listJson = CmdList.Flag.Bool("json", false, "")
var (
listCompiled = CmdList.Flag.Bool("compiled", false, "")
listDeps = CmdList.Flag.Bool("deps", false, "")
listE = CmdList.Flag.Bool("e", false, "")
listExport = CmdList.Flag.Bool("export", false, "")
listFmt = CmdList.Flag.String("f", "", "")
listFind = CmdList.Flag.Bool("find", false, "")
listJson = CmdList.Flag.Bool("json", false, "")
listM = CmdList.Flag.Bool("m", false, "")
listU = CmdList.Flag.Bool("u", false, "")
listTest = CmdList.Flag.Bool("test", false, "")
listVersions = CmdList.Flag.Bool("versions", false, "")
)
var nl = []byte{'\n'}
func runList(cmd *base.Command, args []string) {
modload.LoadTests = *listTest
work.BuildInit()
out := newTrackingWriter(os.Stdout)
defer out.w.Flush()
var do func(*load.PackagePublic)
if *listFmt == "" {
if *listM {
*listFmt = "{{.String}}"
if *listVersions {
*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
}
} else {
*listFmt = "{{.ImportPath}}"
}
}
var do func(interface{})
if *listJson {
do = func(p *load.PackagePublic) {
b, err := json.MarshalIndent(p, "", "\t")
do = func(x interface{}) {
b, err := json.MarshalIndent(x, "", "\t")
if err != nil {
out.Flush()
base.Fatalf("%s", err)
@ -178,13 +344,14 @@ func runList(cmd *base.Command, args []string) {
fm := template.FuncMap{
"join": strings.Join,
"context": context,
"module": modload.ModuleInfo,
}
tmpl, err := template.New("main").Funcs(fm).Parse(*listFmt)
if err != nil {
base.Fatalf("%s", err)
}
do = func(p *load.PackagePublic) {
if err := tmpl.Execute(out, p); err != nil {
do = func(x interface{}) {
if err := tmpl.Execute(out, x); err != nil {
out.Flush()
base.Fatalf("%s", err)
}
@ -194,6 +361,62 @@ func runList(cmd *base.Command, args []string) {
}
}
if *listM {
// Module mode.
if *listCompiled {
base.Fatalf("go list -compiled cannot be used with -m")
}
if *listDeps {
// TODO(rsc): Could make this mean something with -m.
base.Fatalf("go list -deps cannot be used with -m")
}
if *listExport {
base.Fatalf("go list -export cannot be used with -m")
}
if *listFind {
base.Fatalf("go list -find cannot be used with -m")
}
if *listTest {
base.Fatalf("go list -test cannot be used with -m")
}
if modload.Init(); !modload.Enabled() {
base.Fatalf("go list -m: not using modules")
}
modload.LoadBuildList()
mods := modload.ListModules(args, *listU, *listVersions)
if !*listE {
for _, m := range mods {
if m.Error != nil {
base.Errorf("go list -m %s: %v", m.Path, m.Error.Err)
}
}
base.ExitIfErrors()
}
for _, m := range mods {
do(m)
}
return
}
// Package mode (not -m).
if *listU {
base.Fatalf("go list -u can only be used with -m")
}
if *listVersions {
base.Fatalf("go list -versions can only be used with -m")
}
// These pairings make no sense.
if *listFind && *listDeps {
base.Fatalf("go list -deps cannot be used with -find")
}
if *listFind && *listTest {
base.Fatalf("go list -test cannot be used with -find")
}
load.IgnoreImports = *listFind
var pkgs []*load.Package
if *listE {
pkgs = load.PackagesAndErrors(args)
@ -201,27 +424,178 @@ func runList(cmd *base.Command, args []string) {
pkgs = load.Packages(args)
}
// Estimate whether staleness information is needed,
// since it's a little bit of work to compute.
if cache.Default() == nil {
// These flags return file names pointing into the build cache,
// so the build cache must exist.
if *listCompiled {
base.Fatalf("go list -compiled requires build cache")
}
if *listExport {
base.Fatalf("go list -export requires build cache")
}
if *listTest {
base.Fatalf("go list -test requires build cache")
}
}
if *listTest {
c := cache.Default()
// Add test binaries to packages to be listed.
for _, p := range pkgs {
if p.Error != nil {
continue
}
if len(p.TestGoFiles)+len(p.XTestGoFiles) > 0 {
pmain, ptest, pxtest, err := load.GetTestPackagesFor(p, nil)
if err != nil {
if *listE {
pkgs = append(pkgs, &load.Package{
PackagePublic: load.PackagePublic{
ImportPath: p.ImportPath + ".test",
Error: &load.PackageError{Err: err.Error()},
},
})
continue
}
base.Errorf("can't load test package: %s", err)
continue
}
pkgs = append(pkgs, pmain)
if ptest != nil {
pkgs = append(pkgs, ptest)
}
if pxtest != nil {
pkgs = append(pkgs, pxtest)
}
data := *pmain.Internal.TestmainGo
h := cache.NewHash("testmain")
h.Write([]byte("testmain\n"))
h.Write(data)
out, _, err := c.Put(h.Sum(), bytes.NewReader(data))
if err != nil {
base.Fatalf("%s", err)
}
pmain.GoFiles[0] = c.OutputFile(out)
}
}
}
// Remember which packages are named on the command line.
cmdline := make(map[*load.Package]bool)
for _, p := range pkgs {
cmdline[p] = true
}
if *listDeps {
// Note: This changes the order of the listed packages
// from "as written on the command line" to
// "a depth-first post-order traversal".
// (The dependency exploration order for a given node
// is alphabetical, same as listed in .Deps.)
// Note that -deps is applied after -test,
// so that you only get descriptions of tests for the things named
// explicitly on the command line, not for all dependencies.
pkgs = load.PackageList(pkgs)
}
// Do we need to run a build to gather information?
needStale := *listJson || strings.Contains(*listFmt, ".Stale")
if needStale {
if needStale || *listExport || *listCompiled {
var b work.Builder
b.Init()
b.ComputeStaleOnly = true
b.IsCmdList = true
b.NeedExport = *listExport
b.NeedCompiledGoFiles = *listCompiled
a := &work.Action{}
// TODO: Use pkgsFilter?
for _, p := range pkgs {
a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
if len(p.GoFiles)+len(p.CgoFiles) > 0 {
a.Deps = append(a.Deps, b.AutoAction(work.ModeInstall, work.ModeInstall, p))
}
}
b.Do(a)
}
for _, pkg := range pkgs {
for _, p := range pkgs {
// Show vendor-expanded paths in listing
pkg.TestImports = pkg.Resolve(pkg.TestImports)
pkg.XTestImports = pkg.Resolve(pkg.XTestImports)
p.TestImports = p.Resolve(p.TestImports)
p.XTestImports = p.Resolve(p.XTestImports)
p.DepOnly = !cmdline[p]
do(&pkg.PackagePublic)
if *listCompiled {
p.Imports = str.StringList(p.Imports, p.Internal.CompiledImports)
}
}
if *listTest {
all := pkgs
if !*listDeps {
all = load.PackageList(pkgs)
}
// Update import paths to distinguish the real package p
// from p recompiled for q.test.
// This must happen only once the build code is done
// looking at import paths, because it will get very confused
// if it sees these.
old := make(map[string]string)
for _, p := range all {
if p.ForTest != "" {
new := p.ImportPath + " [" + p.ForTest + ".test]"
old[new] = p.ImportPath
p.ImportPath = new
}
p.DepOnly = !cmdline[p]
}
// Update import path lists to use new strings.
m := make(map[string]string)
for _, p := range all {
for _, p1 := range p.Internal.Imports {
if p1.ForTest != "" {
m[old[p1.ImportPath]] = p1.ImportPath
}
}
for i, old := range p.Imports {
if new := m[old]; new != "" {
p.Imports[i] = new
}
}
for old := range m {
delete(m, old)
}
}
// Recompute deps lists using new strings, from the leaves up.
for _, p := range all {
deps := make(map[string]bool)
for _, p1 := range p.Internal.Imports {
deps[p1.ImportPath] = true
for _, d := range p1.Deps {
deps[d] = true
}
}
p.Deps = make([]string, 0, len(deps))
for d := range deps {
p.Deps = append(p.Deps, d)
}
sort.Strings(p.Deps)
}
}
// Record non-identity import mappings in p.ImportMap.
for _, p := range pkgs {
for i, srcPath := range p.Internal.RawImports {
path := p.Imports[i]
if path != srcPath {
if p.ImportMap == nil {
p.ImportMap = make(map[string]string)
}
p.ImportMap[srcPath] = path
}
}
}
for _, p := range pkgs {
do(&p.PackagePublic)
}
}

View File

@ -91,31 +91,3 @@ func (f *PerPackageFlag) For(p *Package) []string {
}
return flags
}
var cmdlineMatchers []func(*Package) bool
// SetCmdlinePatterns records the set of patterns given on the command line,
// for use by the PerPackageFlags.
func SetCmdlinePatterns(args []string) {
setCmdlinePatterns(args, base.Cwd)
}
func setCmdlinePatterns(args []string, cwd string) {
if len(args) == 0 {
args = []string{"."}
}
cmdlineMatchers = nil // allow reset for testing
for _, arg := range args {
cmdlineMatchers = append(cmdlineMatchers, MatchPackage(arg, cwd))
}
}
// isCmdlinePkg reports whether p is a package listed on the command line.
func isCmdlinePkg(p *Package) bool {
for _, m := range cmdlineMatchers {
if m(p) {
return true
}
}
return false
}

View File

@ -32,22 +32,6 @@ func hasSubdir(root, dir string) (rel string, ok bool) {
return filepath.ToSlash(dir[len(root):]), true
}
// hasPathPrefix reports whether the path s begins with the
// elements in prefix.
func hasPathPrefix(s, prefix string) bool {
switch {
default:
return false
case len(s) == len(prefix):
return s == prefix
case len(s) > len(prefix):
if prefix != "" && prefix[len(prefix)-1] == '/' {
return strings.HasPrefix(s, prefix)
}
return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
}
}
// expandPath returns the symlink-expanded form of path.
func expandPath(p string) string {
x, err := filepath.EvalSymlinks(p)

File diff suppressed because it is too large Load Diff

View File

@ -5,271 +5,16 @@
package load
import (
"cmd/go/internal/cfg"
"fmt"
"go/build"
"log"
"os"
"path"
"path/filepath"
"regexp"
"strings"
"cmd/go/internal/search"
)
// allPackages returns all the packages that can be found
// under the $GOPATH directories and $GOROOT matching pattern.
// The pattern is either "all" (all packages), "std" (standard packages),
// "cmd" (standard commands), or a path including "...".
func allPackages(pattern string) []string {
pkgs := MatchPackages(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
// allPackagesInFS is like allPackages but is passed a pattern
// beginning ./ or ../, meaning it should scan the tree rooted
// at the given directory. There are ... in the pattern too.
func allPackagesInFS(pattern string) []string {
pkgs := MatchPackagesInFS(pattern)
if len(pkgs) == 0 {
fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
}
return pkgs
}
// MatchPackages returns a list of package paths matching pattern
// (see go help packages for pattern syntax).
func MatchPackages(pattern string) []string {
match := func(string) bool { return true }
treeCanMatch := func(string) bool { return true }
if !IsMetaPackage(pattern) {
match = matchPattern(pattern)
treeCanMatch = treeCanMatchPattern(pattern)
}
have := map[string]bool{
"builtin": true, // ignore pseudo-package that exists only for documentation
}
if !cfg.BuildContext.CgoEnabled {
have["runtime/cgo"] = true // ignore during walk
}
var pkgs []string
for _, src := range cfg.BuildContext.SrcDirs() {
if (pattern == "std" || pattern == "cmd") && src != cfg.GOROOTsrc {
continue
}
src = filepath.Clean(src) + string(filepath.Separator)
root := src
if pattern == "cmd" {
root += "cmd" + string(filepath.Separator)
}
filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil || path == src {
return nil
}
want := true
// Avoid .foo, _foo, and testdata directory trees.
_, elem := filepath.Split(path)
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
want = false
}
name := filepath.ToSlash(path[len(src):])
if pattern == "std" && (!isStandardImportPath(name) || name == "cmd") {
// The name "std" is only the standard library.
// If the name is cmd, it's the root of the command tree.
want = false
}
if !treeCanMatch(name) {
want = false
}
if !fi.IsDir() {
if fi.Mode()&os.ModeSymlink != 0 && want {
if target, err := os.Stat(path); err == nil && target.IsDir() {
fmt.Fprintf(os.Stderr, "warning: ignoring symlink %s\n", path)
}
}
return nil
}
if !want {
return filepath.SkipDir
}
if have[name] {
return nil
}
have[name] = true
if !match(name) {
return nil
}
pkg, err := cfg.BuildContext.ImportDir(path, 0)
if err != nil {
if _, noGo := err.(*build.NoGoError); noGo {
return nil
}
}
// If we are expanding "cmd", skip main
// packages under cmd/vendor. At least as of
// March, 2017, there is one there for the
// vendored pprof tool.
if pattern == "cmd" && strings.HasPrefix(pkg.ImportPath, "cmd/vendor") && pkg.Name == "main" {
return nil
}
pkgs = append(pkgs, name)
return nil
})
}
return pkgs
}
// MatchPackagesInFS returns a list of package paths matching pattern,
// which must begin with ./ or ../
// (see go help packages for pattern syntax).
func MatchPackagesInFS(pattern string) []string {
// Find directory to begin the scan.
// Could be smarter but this one optimization
// is enough for now, since ... is usually at the
// end of a path.
i := strings.Index(pattern, "...")
dir, _ := path.Split(pattern[:i])
// pattern begins with ./ or ../.
// path.Clean will discard the ./ but not the ../.
// We need to preserve the ./ for pattern matching
// and in the returned import paths.
prefix := ""
if strings.HasPrefix(pattern, "./") {
prefix = "./"
}
match := matchPattern(pattern)
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
if err != nil || !fi.IsDir() {
return nil
}
if path == dir {
// filepath.Walk starts at dir and recurses. For the recursive case,
// the path is the result of filepath.Join, which calls filepath.Clean.
// The initial case is not Cleaned, though, so we do this explicitly.
//
// This converts a path like "./io/" to "io". Without this step, running
// "cd $GOROOT/src; go list ./io/..." would incorrectly skip the io
// package, because prepending the prefix "./" to the unclean path would
// result in "././io", and match("././io") returns false.
path = filepath.Clean(path)
}
// Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
_, elem := filepath.Split(path)
dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
return filepath.SkipDir
}
name := prefix + filepath.ToSlash(path)
if !match(name) {
return nil
}
// We keep the directory if we can import it, or if we can't import it
// due to invalid Go source files. This means that directories containing
// parse errors will be built (and fail) instead of being silently skipped
// as not matching the pattern. Go 1.5 and earlier skipped, but that
// behavior means people miss serious mistakes.
// See golang.org/issue/11407.
if p, err := cfg.BuildContext.ImportDir(path, 0); err != nil && (p == nil || len(p.InvalidGoFiles) == 0) {
if _, noGo := err.(*build.NoGoError); !noGo {
log.Print(err)
}
return nil
}
pkgs = append(pkgs, name)
return nil
})
return pkgs
}
// treeCanMatchPattern(pattern)(name) reports whether
// name or children of name can possibly match pattern.
// Pattern is the same limited glob accepted by matchPattern.
func treeCanMatchPattern(pattern string) func(name string) bool {
wildCard := false
if i := strings.Index(pattern, "..."); i >= 0 {
wildCard = true
pattern = pattern[:i]
}
return func(name string) bool {
return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
wildCard && strings.HasPrefix(name, pattern)
}
}
// matchPattern(pattern)(name) reports whether
// name matches pattern. Pattern is a limited glob
// pattern in which '...' means 'any string' and there
// is no other special syntax.
// Unfortunately, there are two special cases. Quoting "go help packages":
//
// First, /... at the end of the pattern can match an empty string,
// so that net/... matches both net and packages in its subdirectories, like net/http.
// Second, any slash-separted pattern element containing a wildcard never
// participates in a match of the "vendor" element in the path of a vendored
// package, so that ./... does not match packages in subdirectories of
// ./vendor or ./mycode/vendor, but ./vendor/... and ./mycode/vendor/... do.
// Note, however, that a directory named vendor that itself contains code
// is not a vendored package: cmd/vendor would be a command named vendor,
// and the pattern cmd/... matches it.
func matchPattern(pattern string) func(name string) bool {
// Convert pattern to regular expression.
// The strategy for the trailing /... is to nest it in an explicit ? expression.
// The strategy for the vendor exclusion is to change the unmatchable
// vendor strings to a disallowed code point (vendorChar) and to use
// "(anything but that codepoint)*" as the implementation of the ... wildcard.
// This is a bit complicated but the obvious alternative,
// namely a hand-written search like in most shell glob matchers,
// is too easy to make accidentally exponential.
// Using package regexp guarantees linear-time matching.
const vendorChar = "\x00"
if strings.Contains(pattern, vendorChar) {
return func(name string) bool { return false }
}
re := regexp.QuoteMeta(pattern)
re = replaceVendor(re, vendorChar)
switch {
case strings.HasSuffix(re, `/`+vendorChar+`/\.\.\.`):
re = strings.TrimSuffix(re, `/`+vendorChar+`/\.\.\.`) + `(/vendor|/` + vendorChar + `/\.\.\.)`
case re == vendorChar+`/\.\.\.`:
re = `(/vendor|/` + vendorChar + `/\.\.\.)`
case strings.HasSuffix(re, `/\.\.\.`):
re = strings.TrimSuffix(re, `/\.\.\.`) + `(/\.\.\.)?`
}
re = strings.Replace(re, `\.\.\.`, `[^`+vendorChar+`]*`, -1)
reg := regexp.MustCompile(`^` + re + `$`)
return func(name string) bool {
if strings.Contains(name, vendorChar) {
return false
}
return reg.MatchString(replaceVendor(name, vendorChar))
}
}
// MatchPackage(pattern, cwd)(p) reports whether package p matches pattern in the working directory cwd.
func MatchPackage(pattern, cwd string) func(*Package) bool {
switch {
case strings.HasPrefix(pattern, "./") || strings.HasPrefix(pattern, "../") || pattern == "." || pattern == "..":
case search.IsRelativePath(pattern):
// Split pattern into leading pattern-free directory path
// (including all . and .. elements) and the final pattern.
var dir string
@ -284,7 +29,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
if pattern == "" {
return func(p *Package) bool { return p.Dir == dir }
}
matchPath := matchPattern(pattern)
matchPath := search.MatchPattern(pattern)
return func(p *Package) bool {
// Compute relative path to dir and see if it matches the pattern.
rel, err := filepath.Rel(dir, p.Dir)
@ -305,81 +50,7 @@ func MatchPackage(pattern, cwd string) func(*Package) bool {
case pattern == "cmd":
return func(p *Package) bool { return p.Standard && strings.HasPrefix(p.ImportPath, "cmd/") }
default:
matchPath := matchPattern(pattern)
matchPath := search.MatchPattern(pattern)
return func(p *Package) bool { return matchPath(p.ImportPath) }
}
}
// replaceVendor returns the result of replacing
// non-trailing vendor path elements in x with repl.
func replaceVendor(x, repl string) string {
if !strings.Contains(x, "vendor") {
return x
}
elem := strings.Split(x, "/")
for i := 0; i < len(elem)-1; i++ {
if elem[i] == "vendor" {
elem[i] = repl
}
}
return strings.Join(elem, "/")
}
// ImportPaths returns the import paths to use for the given command line.
func ImportPaths(args []string) []string {
args = ImportPathsNoDotExpansion(args)
var out []string
for _, a := range args {
if strings.Contains(a, "...") {
if build.IsLocalImport(a) {
out = append(out, allPackagesInFS(a)...)
} else {
out = append(out, allPackages(a)...)
}
continue
}
out = append(out, a)
}
return out
}
// ImportPathsNoDotExpansion returns the import paths to use for the given
// command line, but it does no ... expansion.
func ImportPathsNoDotExpansion(args []string) []string {
if cmdlineMatchers == nil {
SetCmdlinePatterns(args)
}
if len(args) == 0 {
return []string{"."}
}
var out []string
for _, a := range args {
// Arguments are supposed to be import paths, but
// as a courtesy to Windows developers, rewrite \ to /
// in command-line arguments. Handles .\... and so on.
if filepath.Separator == '\\' {
a = strings.Replace(a, `\`, `/`, -1)
}
// Put argument in canonical form, but preserve leading ./.
if strings.HasPrefix(a, "./") {
a = "./" + path.Clean(a)
if a == "./." {
a = "."
}
} else {
a = path.Clean(a)
}
if IsMetaPackage(a) {
out = append(out, allPackages(a)...)
continue
}
out = append(out, a)
}
return out
}
// IsMetaPackage checks if name is a reserved package name that expands to multiple packages.
func IsMetaPackage(name string) bool {
return name == "std" || name == "cmd" || name == "all"
}

View File

@ -0,0 +1,654 @@
// Copyright 2018 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 load
import (
"bytes"
"cmd/go/internal/base"
"cmd/go/internal/str"
"errors"
"fmt"
"go/ast"
"go/build"
"go/doc"
"go/parser"
"go/token"
"path/filepath"
"sort"
"strings"
"text/template"
"unicode"
"unicode/utf8"
)
var TestMainDeps = []string{
// Dependencies for testmain.
"os",
"testing",
"testing/internal/testdeps",
}
type TestCover struct {
Mode string
Local bool
Pkgs []*Package
Paths []string
Vars []coverInfo
DeclVars func(*Package, ...string) map[string]*CoverVar
}
// TestPackagesFor returns three packages:
// - ptest, the package p compiled with added "package p" test files.
// - pxtest, the result of compiling any "package p_test" (external) test files.
// - pmain, the package main corresponding to the test binary (running tests in ptest and pxtest).
//
// If the package has no "package p_test" test files, pxtest will be nil.
// If the non-test compilation of package p can be reused
// (for example, if there are no "package p" test files and
// package p need not be instrumented for coverage or any other reason),
// then the returned ptest == p.
//
// The caller is expected to have checked that len(p.TestGoFiles)+len(p.XTestGoFiles) > 0,
// or else there's no point in any of this.
func GetTestPackagesFor(p *Package, cover *TestCover) (pmain, ptest, pxtest *Package, err error) {
var imports, ximports []*Package
var stk ImportStack
stk.Push(p.ImportPath + " (test)")
rawTestImports := str.StringList(p.TestImports)
for i, path := range p.TestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.TestImportPos[path], ResolveImport)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
if len(p1.DepsErrors) > 0 {
err := p1.DepsErrors[0]
err.Pos = "" // show full import stack
return nil, nil, nil, err
}
if str.Contains(p1.Deps, p.ImportPath) || p1.ImportPath == p.ImportPath {
// Same error that loadPackage returns (via reusePackage) in pkg.go.
// Can't change that code, because that code is only for loading the
// non-test copy of a package.
err := &PackageError{
ImportStack: testImportStack(stk[0], p1, p.ImportPath),
Err: "import cycle not allowed in test",
IsImportCycle: true,
}
return nil, nil, nil, err
}
p.TestImports[i] = p1.ImportPath
imports = append(imports, p1)
}
stk.Pop()
stk.Push(p.ImportPath + "_test")
pxtestNeedsPtest := false
rawXTestImports := str.StringList(p.XTestImports)
for i, path := range p.XTestImports {
p1 := LoadImport(path, p.Dir, p, &stk, p.Internal.Build.XTestImportPos[path], ResolveImport)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
if len(p1.DepsErrors) > 0 {
err := p1.DepsErrors[0]
err.Pos = "" // show full import stack
return nil, nil, nil, err
}
if p1.ImportPath == p.ImportPath {
pxtestNeedsPtest = true
} else {
ximports = append(ximports, p1)
}
p.XTestImports[i] = p1.ImportPath
}
stk.Pop()
// Test package.
if len(p.TestGoFiles) > 0 || p.Name == "main" || cover != nil && cover.Local {
ptest = new(Package)
*ptest = *p
ptest.ForTest = p.ImportPath
ptest.GoFiles = nil
ptest.GoFiles = append(ptest.GoFiles, p.GoFiles...)
ptest.GoFiles = append(ptest.GoFiles, p.TestGoFiles...)
ptest.Target = ""
// Note: The preparation of the vet config requires that common
// indexes in ptest.Imports and ptest.Internal.RawImports
// all line up (but RawImports can be shorter than the others).
// That is, for 0 ≤ i < len(RawImports),
// RawImports[i] is the import string in the program text, and
// Imports[i] is the expanded import string (vendoring applied or relative path expanded away).
// Any implicitly added imports appear in Imports and Internal.Imports
// but not RawImports (because they were not in the source code).
// We insert TestImports, imports, and rawTestImports at the start of
// these lists to preserve the alignment.
// Note that p.Internal.Imports may not be aligned with p.Imports/p.Internal.RawImports,
// but we insert at the beginning there too just for consistency.
ptest.Imports = str.StringList(p.TestImports, p.Imports)
ptest.Internal.Imports = append(imports, p.Internal.Imports...)
ptest.Internal.RawImports = str.StringList(rawTestImports, p.Internal.RawImports)
ptest.Internal.ForceLibrary = true
ptest.Internal.Build = new(build.Package)
*ptest.Internal.Build = *p.Internal.Build
m := map[string][]token.Position{}
for k, v := range p.Internal.Build.ImportPos {
m[k] = append(m[k], v...)
}
for k, v := range p.Internal.Build.TestImportPos {
m[k] = append(m[k], v...)
}
ptest.Internal.Build.ImportPos = m
} else {
ptest = p
}
// External test package.
if len(p.XTestGoFiles) > 0 {
pxtest = &Package{
PackagePublic: PackagePublic{
Name: p.Name + "_test",
ImportPath: p.ImportPath + "_test",
Root: p.Root,
Dir: p.Dir,
GoFiles: p.XTestGoFiles,
Imports: p.XTestImports,
ForTest: p.ImportPath,
},
Internal: PackageInternal{
LocalPrefix: p.Internal.LocalPrefix,
Build: &build.Package{
ImportPos: p.Internal.Build.XTestImportPos,
},
Imports: ximports,
RawImports: rawXTestImports,
Asmflags: p.Internal.Asmflags,
Gcflags: p.Internal.Gcflags,
Ldflags: p.Internal.Ldflags,
Gccgoflags: p.Internal.Gccgoflags,
},
}
if pxtestNeedsPtest {
pxtest.Internal.Imports = append(pxtest.Internal.Imports, ptest)
}
}
// Build main package.
pmain = &Package{
PackagePublic: PackagePublic{
Name: "main",
Dir: p.Dir,
GoFiles: []string{"_testmain.go"},
ImportPath: p.ImportPath + ".test",
Root: p.Root,
Imports: str.StringList(TestMainDeps),
},
Internal: PackageInternal{
Build: &build.Package{Name: "main"},
Asmflags: p.Internal.Asmflags,
Gcflags: p.Internal.Gcflags,
Ldflags: p.Internal.Ldflags,
Gccgoflags: p.Internal.Gccgoflags,
},
}
// The generated main also imports testing, regexp, and os.
// Also the linker introduces implicit dependencies reported by LinkerDeps.
stk.Push("testmain")
deps := TestMainDeps // cap==len, so safe for append
for _, d := range LinkerDeps(p) {
deps = append(deps, d)
}
for _, dep := range deps {
if dep == ptest.ImportPath {
pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
} else {
p1 := LoadImport(dep, "", nil, &stk, nil, 0)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
}
}
stk.Pop()
if cover != nil && cover.Pkgs != nil {
// Add imports, but avoid duplicates.
seen := map[*Package]bool{p: true, ptest: true}
for _, p1 := range pmain.Internal.Imports {
seen[p1] = true
}
for _, p1 := range cover.Pkgs {
if !seen[p1] {
seen[p1] = true
pmain.Internal.Imports = append(pmain.Internal.Imports, p1)
}
}
}
// Do initial scan for metadata needed for writing _testmain.go
// Use that metadata to update the list of imports for package main.
// The list of imports is used by recompileForTest and by the loop
// afterward that gathers t.Cover information.
t, err := loadTestFuncs(ptest)
if err != nil {
return nil, nil, nil, err
}
t.Cover = cover
if len(ptest.GoFiles)+len(ptest.CgoFiles) > 0 {
pmain.Internal.Imports = append(pmain.Internal.Imports, ptest)
pmain.Imports = append(pmain.Imports, ptest.ImportPath)
t.ImportTest = true
}
if pxtest != nil {
pmain.Internal.Imports = append(pmain.Internal.Imports, pxtest)
pmain.Imports = append(pmain.Imports, pxtest.ImportPath)
t.ImportXtest = true
}
// Sort and dedup pmain.Imports.
// Only matters for go list -test output.
sort.Strings(pmain.Imports)
w := 0
for _, path := range pmain.Imports {
if w == 0 || path != pmain.Imports[w-1] {
pmain.Imports[w] = path
w++
}
}
pmain.Imports = pmain.Imports[:w]
pmain.Internal.RawImports = str.StringList(pmain.Imports)
if ptest != p {
// We have made modifications to the package p being tested
// and are rebuilding p (as ptest).
// Arrange to rebuild all packages q such that
// the test depends on q and q depends on p.
// This makes sure that q sees the modifications to p.
// Strictly speaking, the rebuild is only necessary if the
// modifications to p change its export metadata, but
// determining that is a bit tricky, so we rebuild always.
recompileForTest(pmain, p, ptest, pxtest)
}
// Should we apply coverage analysis locally,
// only for this package and only for this test?
// Yes, if -cover is on but -coverpkg has not specified
// a list of packages for global coverage.
if cover != nil && cover.Local {
ptest.Internal.CoverMode = cover.Mode
var coverFiles []string
coverFiles = append(coverFiles, ptest.GoFiles...)
coverFiles = append(coverFiles, ptest.CgoFiles...)
ptest.Internal.CoverVars = cover.DeclVars(ptest, coverFiles...)
}
for _, cp := range pmain.Internal.Imports {
if len(cp.Internal.CoverVars) > 0 {
t.Cover.Vars = append(t.Cover.Vars, coverInfo{cp, cp.Internal.CoverVars})
}
}
data, err := formatTestmain(t)
if err != nil {
return nil, nil, nil, err
}
pmain.Internal.TestmainGo = &data
return pmain, ptest, pxtest, nil
}
func testImportStack(top string, p *Package, target string) []string {
stk := []string{top, p.ImportPath}
Search:
for p.ImportPath != target {
for _, p1 := range p.Internal.Imports {
if p1.ImportPath == target || str.Contains(p1.Deps, target) {
stk = append(stk, p1.ImportPath)
p = p1
continue Search
}
}
// Can't happen, but in case it does...
stk = append(stk, "<lost path to cycle>")
break
}
return stk
}
func recompileForTest(pmain, preal, ptest, pxtest *Package) {
// The "test copy" of preal is ptest.
// For each package that depends on preal, make a "test copy"
// that depends on ptest. And so on, up the dependency tree.
testCopy := map[*Package]*Package{preal: ptest}
for _, p := range PackageList([]*Package{pmain}) {
if p == preal {
continue
}
// Copy on write.
didSplit := p == pmain || p == pxtest
split := func() {
if didSplit {
return
}
didSplit = true
if testCopy[p] != nil {
panic("recompileForTest loop")
}
p1 := new(Package)
testCopy[p] = p1
*p1 = *p
p1.ForTest = preal.ImportPath
p1.Internal.Imports = make([]*Package, len(p.Internal.Imports))
copy(p1.Internal.Imports, p.Internal.Imports)
p1.Imports = make([]string, len(p.Imports))
copy(p1.Imports, p.Imports)
p = p1
p.Target = ""
}
// Update p.Internal.Imports to use test copies.
for i, imp := range p.Internal.Imports {
if p1 := testCopy[imp]; p1 != nil && p1 != imp {
split()
p.Internal.Imports[i] = p1
}
}
}
}
// isTestFunc tells whether fn has the type of a testing function. arg
// specifies the parameter type we look for: B, M or T.
func isTestFunc(fn *ast.FuncDecl, arg string) bool {
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
fn.Type.Params.List == nil ||
len(fn.Type.Params.List) != 1 ||
len(fn.Type.Params.List[0].Names) > 1 {
return false
}
ptr, ok := fn.Type.Params.List[0].Type.(*ast.StarExpr)
if !ok {
return false
}
// We can't easily check that the type is *testing.M
// because we don't know how testing has been imported,
// but at least check that it's *M or *something.M.
// Same applies for B and T.
if name, ok := ptr.X.(*ast.Ident); ok && name.Name == arg {
return true
}
if sel, ok := ptr.X.(*ast.SelectorExpr); ok && sel.Sel.Name == arg {
return true
}
return false
}
// isTest tells whether name looks like a test (or benchmark, according to prefix).
// It is a Test (say) if there is a character after Test that is not a lower-case letter.
// We don't want TesticularCancer.
func isTest(name, prefix string) bool {
if !strings.HasPrefix(name, prefix) {
return false
}
if len(name) == len(prefix) { // "Test" is ok
return true
}
rune, _ := utf8.DecodeRuneInString(name[len(prefix):])
return !unicode.IsLower(rune)
}
type coverInfo struct {
Package *Package
Vars map[string]*CoverVar
}
// loadTestFuncs returns the testFuncs describing the tests that will be run.
func loadTestFuncs(ptest *Package) (*testFuncs, error) {
t := &testFuncs{
Package: ptest,
}
for _, file := range ptest.TestGoFiles {
if err := t.load(filepath.Join(ptest.Dir, file), "_test", &t.ImportTest, &t.NeedTest); err != nil {
return nil, err
}
}
for _, file := range ptest.XTestGoFiles {
if err := t.load(filepath.Join(ptest.Dir, file), "_xtest", &t.ImportXtest, &t.NeedXtest); err != nil {
return nil, err
}
}
return t, nil
}
// formatTestmain returns the content of the _testmain.go file for t.
func formatTestmain(t *testFuncs) ([]byte, error) {
var buf bytes.Buffer
if err := testmainTmpl.Execute(&buf, t); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
type testFuncs struct {
Tests []testFunc
Benchmarks []testFunc
Examples []testFunc
TestMain *testFunc
Package *Package
ImportTest bool
NeedTest bool
ImportXtest bool
NeedXtest bool
Cover *TestCover
}
// ImportPath returns the import path of the package being tested, if it is within GOPATH.
// This is printed by the testing package when running benchmarks.
func (t *testFuncs) ImportPath() string {
pkg := t.Package.ImportPath
if strings.HasPrefix(pkg, "_/") {
return ""
}
if pkg == "command-line-arguments" {
return ""
}
return pkg
}
// Covered returns a string describing which packages are being tested for coverage.
// If the covered package is the same as the tested package, it returns the empty string.
// Otherwise it is a comma-separated human-readable list of packages beginning with
// " in", ready for use in the coverage message.
func (t *testFuncs) Covered() string {
if t.Cover == nil || t.Cover.Paths == nil {
return ""
}
return " in " + strings.Join(t.Cover.Paths, ", ")
}
// Tested returns the name of the package being tested.
func (t *testFuncs) Tested() string {
return t.Package.Name
}
type testFunc struct {
Package string // imported package name (_test or _xtest)
Name string // function name
Output string // output, for examples
Unordered bool // output is allowed to be unordered.
}
var testFileSet = token.NewFileSet()
func (t *testFuncs) load(filename, pkg string, doImport, seen *bool) error {
f, err := parser.ParseFile(testFileSet, filename, nil, parser.ParseComments)
if err != nil {
return base.ExpandScanner(err)
}
for _, d := range f.Decls {
n, ok := d.(*ast.FuncDecl)
if !ok {
continue
}
if n.Recv != nil {
continue
}
name := n.Name.String()
switch {
case name == "TestMain":
if isTestFunc(n, "T") {
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
*doImport, *seen = true, true
continue
}
err := checkTestFunc(n, "M")
if err != nil {
return err
}
if t.TestMain != nil {
return errors.New("multiple definitions of TestMain")
}
t.TestMain = &testFunc{pkg, name, "", false}
*doImport, *seen = true, true
case isTest(name, "Test"):
err := checkTestFunc(n, "T")
if err != nil {
return err
}
t.Tests = append(t.Tests, testFunc{pkg, name, "", false})
*doImport, *seen = true, true
case isTest(name, "Benchmark"):
err := checkTestFunc(n, "B")
if err != nil {
return err
}
t.Benchmarks = append(t.Benchmarks, testFunc{pkg, name, "", false})
*doImport, *seen = true, true
}
}
ex := doc.Examples(f)
sort.Slice(ex, func(i, j int) bool { return ex[i].Order < ex[j].Order })
for _, e := range ex {
*doImport = true // import test file whether executed or not
if e.Output == "" && !e.EmptyOutput {
// Don't run examples with no output.
continue
}
t.Examples = append(t.Examples, testFunc{pkg, "Example" + e.Name, e.Output, e.Unordered})
*seen = true
}
return nil
}
func checkTestFunc(fn *ast.FuncDecl, arg string) error {
if !isTestFunc(fn, arg) {
name := fn.Name.String()
pos := testFileSet.Position(fn.Pos())
return fmt.Errorf("%s: wrong signature for %s, must be: func %s(%s *testing.%s)", pos, name, name, strings.ToLower(arg), arg)
}
return nil
}
var testmainTmpl = template.Must(template.New("main").Parse(`
package main
import (
{{if not .TestMain}}
"os"
{{end}}
"testing"
"testing/internal/testdeps"
{{if .ImportTest}}
{{if .NeedTest}}_test{{else}}_{{end}} {{.Package.ImportPath | printf "%q"}}
{{end}}
{{if .ImportXtest}}
{{if .NeedXtest}}_xtest{{else}}_{{end}} {{.Package.ImportPath | printf "%s_test" | printf "%q"}}
{{end}}
{{if .Cover}}
{{range $i, $p := .Cover.Vars}}
_cover{{$i}} {{$p.Package.ImportPath | printf "%q"}}
{{end}}
{{end}}
)
var tests = []testing.InternalTest{
{{range .Tests}}
{"{{.Name}}", {{.Package}}.{{.Name}}},
{{end}}
}
var benchmarks = []testing.InternalBenchmark{
{{range .Benchmarks}}
{"{{.Name}}", {{.Package}}.{{.Name}}},
{{end}}
}
var examples = []testing.InternalExample{
{{range .Examples}}
{"{{.Name}}", {{.Package}}.{{.Name}}, {{.Output | printf "%q"}}, {{.Unordered}}},
{{end}}
}
func init() {
testdeps.ImportPath = {{.ImportPath | printf "%q"}}
}
{{if .Cover}}
// Only updated by init functions, so no need for atomicity.
var (
coverCounters = make(map[string][]uint32)
coverBlocks = make(map[string][]testing.CoverBlock)
)
func init() {
{{range $i, $p := .Cover.Vars}}
{{range $file, $cover := $p.Vars}}
coverRegisterFile({{printf "%q" $cover.File}}, _cover{{$i}}.{{$cover.Var}}.Count[:], _cover{{$i}}.{{$cover.Var}}.Pos[:], _cover{{$i}}.{{$cover.Var}}.NumStmt[:])
{{end}}
{{end}}
}
func coverRegisterFile(fileName string, counter []uint32, pos []uint32, numStmts []uint16) {
if 3*len(counter) != len(pos) || len(counter) != len(numStmts) {
panic("coverage: mismatched sizes")
}
if coverCounters[fileName] != nil {
// Already registered.
return
}
coverCounters[fileName] = counter
block := make([]testing.CoverBlock, len(counter))
for i := range counter {
block[i] = testing.CoverBlock{
Line0: pos[3*i+0],
Col0: uint16(pos[3*i+2]),
Line1: pos[3*i+1],
Col1: uint16(pos[3*i+2]>>16),
Stmts: numStmts[i],
}
}
coverBlocks[fileName] = block
}
{{end}}
func main() {
{{if .Cover}}
testing.RegisterCover(testing.Cover{
Mode: {{printf "%q" .Cover.Mode}},
Counters: coverCounters,
Blocks: coverBlocks,
CoveredPackages: {{printf "%q" .Covered}},
})
{{end}}
m := testing.MainStart(testdeps.TestDeps{}, tests, benchmarks, examples)
{{with .TestMain}}
{{.Package}}.{{.Name}}(m)
{{else}}
os.Exit(m.Run())
{{end}}
}
`))

View File

@ -0,0 +1,133 @@
// Copyright 2018 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 modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/module"
"cmd/go/internal/par"
"encoding/json"
"os"
)
var cmdDownload = &base.Command{
UsageLine: "go mod download [-dir] [-json] [modules]",
Short: "download modules to local cache",
Long: `
Download downloads the named modules, which can be module patterns selecting
dependencies of the main module or module queries of the form path@version.
With no arguments, download applies to all dependencies of the main module.
The go command will automatically download modules as needed during ordinary
execution. The "go mod download" command is useful mainly for pre-filling
the local cache or to compute the answers for a Go module proxy.
By default, download reports errors to standard error but is otherwise silent.
The -json flag causes download to print a sequence of JSON objects
to standard output, describing each downloaded module (or failure),
corresponding to this Go struct:
type Module struct {
Path string // module path
Version string // module version
Error string // error loading module
Info string // absolute path to cached .info file
GoMod string // absolute path to cached .mod file
Zip string // absolute path to cached .zip file
Dir string // absolute path to cached source root directory
Sum string // checksum for path, version (as in go.sum)
GoModSum string // checksum for go.mod (as in go.sum)
}
See 'go help modules' for more about module queries.
`,
}
var downloadJSON = cmdDownload.Flag.Bool("json", false, "")
func init() {
cmdDownload.Run = runDownload // break init cycle
}
type moduleJSON struct {
Path string `json:",omitempty"`
Version string `json:",omitempty"`
Error string `json:",omitempty"`
Info string `json:",omitempty"`
GoMod string `json:",omitempty"`
Zip string `json:",omitempty"`
Dir string `json:",omitempty"`
Sum string `json:",omitempty"`
GoModSum string `json:",omitempty"`
}
func runDownload(cmd *base.Command, args []string) {
if len(args) == 0 {
args = []string{"all"}
}
var mods []*moduleJSON
var work par.Work
listU := false
listVersions := false
for _, info := range modload.ListModules(args, listU, listVersions) {
if info.Replace != nil {
info = info.Replace
}
if info.Version == "" {
continue
}
m := &moduleJSON{
Path: info.Path,
Version: info.Version,
}
mods = append(mods, m)
work.Add(m)
}
work.Do(10, func(item interface{}) {
m := item.(*moduleJSON)
var err error
m.Info, err = modfetch.InfoFile(m.Path, m.Version)
if err != nil {
m.Error = err.Error()
return
}
m.GoMod, err = modfetch.GoModFile(m.Path, m.Version)
if err != nil {
m.Error = err.Error()
return
}
m.GoModSum, err = modfetch.GoModSum(m.Path, m.Version)
if err != nil {
m.Error = err.Error()
return
}
mod := module.Version{Path: m.Path, Version: m.Version}
m.Zip, err = modfetch.DownloadZip(mod)
if err != nil {
m.Error = err.Error()
return
}
m.Sum = modfetch.Sum(mod)
m.Dir, err = modfetch.Download(mod)
if err != nil {
m.Error = err.Error()
return
}
})
if *downloadJSON {
for _, m := range mods {
b, err := json.MarshalIndent(m, "", "\t")
if err != nil {
base.Fatalf("%v", err)
}
os.Stdout.Write(append(b, '\n'))
}
}
}

View File

@ -0,0 +1,382 @@
// Copyright 2018 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.
// go mod edit
package modcmd
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/modfile"
"cmd/go/internal/modload"
"cmd/go/internal/module"
)
var cmdEdit = &base.Command{
UsageLine: "go mod edit [editing flags] [go.mod]",
Short: "edit go.mod from tools or scripts",
Long: `
Edit provides a command-line interface for editing go.mod,
for use primarily by tools or scripts. It reads only go.mod;
it does not look up information about the modules involved.
By default, edit reads and writes the go.mod file of the main module,
but a different target file can be specified after the editing flags.
The editing flags specify a sequence of editing operations.
The -fmt flag reformats the go.mod file without making other changes.
This reformatting is also implied by any other modifications that use or
rewrite the go.mod file. The only time this flag is needed is if no other
flags are specified, as in 'go mod edit -fmt'.
The -module flag changes the module's path (the go.mod file's module line).
The -require=path@version and -droprequire=path flags
add and drop a requirement on the given module path and version.
Note that -require overrides any existing requirements on path.
These flags are mainly for tools that understand the module graph.
Users should prefer 'go get path@version' or 'go get path@none',
which make other go.mod adjustments as needed to satisfy
constraints imposed by other modules.
The -exclude=path@version and -dropexclude=path@version flags
add and drop an exclusion for the given module path and version.
Note that -exclude=path@version is a no-op if that exclusion already exists.
The -replace=old[@v]=new[@v] and -dropreplace=old[@v] flags
add and drop a replacement of the given module path and version pair.
If the @v in old@v is omitted, the replacement applies to all versions
with the old module path. If the @v in new@v is omitted, the new path
should be a local module root directory, not a module path.
Note that -replace overrides any existing replacements for old[@v].
The -require, -droprequire, -exclude, -dropexclude, -replace,
and -dropreplace editing flags may be repeated, and the changes
are applied in the order given.
The -print flag prints the final go.mod in its text format instead of
writing it back to go.mod.
The -json flag prints the final go.mod file in JSON format instead of
writing it back to go.mod. The JSON output corresponds to these Go types:
type Module struct {
Path string
Version string
}
type GoMod struct {
Module Module
Require []Require
Exclude []Module
Replace []Replace
}
type Require struct {
Path string
Version string
Indirect bool
}
type Replace struct {
Old Module
New Module
}
Note that this only describes the go.mod file itself, not other modules
referred to indirectly. For the full set of modules available to a build,
use 'go list -m -json all'.
For example, a tool can obtain the go.mod as a data structure by
parsing the output of 'go mod edit -json' and can then make changes
by invoking 'go mod edit' with -require, -exclude, and so on.
`,
}
var (
editFmt = cmdEdit.Flag.Bool("fmt", false, "")
// editGo = cmdEdit.Flag.String("go", "", "")
editJSON = cmdEdit.Flag.Bool("json", false, "")
editPrint = cmdEdit.Flag.Bool("print", false, "")
editModule = cmdEdit.Flag.String("module", "", "")
edits []func(*modfile.File) // edits specified in flags
)
type flagFunc func(string)
func (f flagFunc) String() string { return "" }
func (f flagFunc) Set(s string) error { f(s); return nil }
func init() {
cmdEdit.Run = runEdit // break init cycle
cmdEdit.Flag.Var(flagFunc(flagRequire), "require", "")
cmdEdit.Flag.Var(flagFunc(flagDropRequire), "droprequire", "")
cmdEdit.Flag.Var(flagFunc(flagExclude), "exclude", "")
cmdEdit.Flag.Var(flagFunc(flagDropReplace), "dropreplace", "")
cmdEdit.Flag.Var(flagFunc(flagReplace), "replace", "")
cmdEdit.Flag.Var(flagFunc(flagDropExclude), "dropexclude", "")
base.AddBuildFlagsNX(&cmdEdit.Flag)
}
func runEdit(cmd *base.Command, args []string) {
anyFlags :=
*editModule != "" ||
*editJSON ||
*editPrint ||
*editFmt ||
len(edits) > 0
if !anyFlags {
base.Fatalf("go mod edit: no flags specified (see 'go help mod edit').")
}
if *editJSON && *editPrint {
base.Fatalf("go mod edit: cannot use both -json and -print")
}
if len(args) > 1 {
base.Fatalf("go mod edit: too many arguments")
}
var gomod string
if len(args) == 1 {
gomod = args[0]
} else {
modload.MustInit()
gomod = filepath.Join(modload.ModRoot, "go.mod")
}
if *editModule != "" {
if err := module.CheckPath(*editModule); err != nil {
base.Fatalf("go mod: invalid -module: %v", err)
}
}
// TODO(rsc): Implement -go= once we start advertising it.
data, err := ioutil.ReadFile(gomod)
if err != nil {
base.Fatalf("go: %v", err)
}
modFile, err := modfile.Parse(gomod, data, nil)
if err != nil {
base.Fatalf("go: errors parsing %s:\n%s", base.ShortPath(gomod), err)
}
if *editModule != "" {
modFile.AddModuleStmt(modload.CmdModModule)
}
if len(edits) > 0 {
for _, edit := range edits {
edit(modFile)
}
}
modFile.SortBlocks()
modFile.Cleanup() // clean file after edits
if *editJSON {
editPrintJSON(modFile)
return
}
data, err = modFile.Format()
if err != nil {
base.Fatalf("go: %v", err)
}
if *editPrint {
os.Stdout.Write(data)
return
}
if err := ioutil.WriteFile(gomod, data, 0666); err != nil {
base.Fatalf("go: %v", err)
}
}
// parsePathVersion parses -flag=arg expecting arg to be path@version.
func parsePathVersion(flag, arg string) (path, version string) {
i := strings.Index(arg, "@")
if i < 0 {
base.Fatalf("go mod: -%s=%s: need path@version", flag, arg)
}
path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
if err := module.CheckPath(path); err != nil {
base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
}
// We don't call modfile.CheckPathVersion, because that insists
// on versions being in semver form, but here we want to allow
// versions like "master" or "1234abcdef", which the go command will resolve
// the next time it runs (or during -fix).
// Even so, we need to make sure the version is a valid token.
if modfile.MustQuote(version) {
base.Fatalf("go mod: -%s=%s: invalid version %q", flag, arg, version)
}
return path, version
}
// parsePath parses -flag=arg expecting arg to be path (not path@version).
func parsePath(flag, arg string) (path string) {
if strings.Contains(arg, "@") {
base.Fatalf("go mod: -%s=%s: need just path, not path@version", flag, arg)
}
path = arg
if err := module.CheckPath(path); err != nil {
base.Fatalf("go mod: -%s=%s: invalid path: %v", flag, arg, err)
}
return path
}
// parsePathVersionOptional parses path[@version], using adj to
// describe any errors.
func parsePathVersionOptional(adj, arg string, allowDirPath bool) (path, version string, err error) {
if i := strings.Index(arg, "@"); i < 0 {
path = arg
} else {
path, version = strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
}
if err := module.CheckPath(path); err != nil {
if !allowDirPath || !modfile.IsDirectoryPath(path) {
return path, version, fmt.Errorf("invalid %s path: %v", adj, err)
}
}
if path != arg && modfile.MustQuote(version) {
return path, version, fmt.Errorf("invalid %s version: %q", adj, version)
}
return path, version, nil
}
// flagRequire implements the -require flag.
func flagRequire(arg string) {
path, version := parsePathVersion("require", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.AddRequire(path, version); err != nil {
base.Fatalf("go mod: -require=%s: %v", arg, err)
}
})
}
// flagDropRequire implements the -droprequire flag.
func flagDropRequire(arg string) {
path := parsePath("droprequire", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.DropRequire(path); err != nil {
base.Fatalf("go mod: -droprequire=%s: %v", arg, err)
}
})
}
// flagExclude implements the -exclude flag.
func flagExclude(arg string) {
path, version := parsePathVersion("exclude", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.AddExclude(path, version); err != nil {
base.Fatalf("go mod: -exclude=%s: %v", arg, err)
}
})
}
// flagDropExclude implements the -dropexclude flag.
func flagDropExclude(arg string) {
path, version := parsePathVersion("dropexclude", arg)
edits = append(edits, func(f *modfile.File) {
if err := f.DropExclude(path, version); err != nil {
base.Fatalf("go mod: -dropexclude=%s: %v", arg, err)
}
})
}
// flagReplace implements the -replace flag.
func flagReplace(arg string) {
var i int
if i = strings.Index(arg, "="); i < 0 {
base.Fatalf("go mod: -replace=%s: need old[@v]=new[@w] (missing =)", arg)
}
old, new := strings.TrimSpace(arg[:i]), strings.TrimSpace(arg[i+1:])
if strings.HasPrefix(new, ">") {
base.Fatalf("go mod: -replace=%s: separator between old and new is =, not =>", arg)
}
oldPath, oldVersion, err := parsePathVersionOptional("old", old, false)
if err != nil {
base.Fatalf("go mod: -replace=%s: %v", arg, err)
}
newPath, newVersion, err := parsePathVersionOptional("new", new, true)
if err != nil {
base.Fatalf("go mod: -replace=%s: %v", arg, err)
}
if newPath == new && !modfile.IsDirectoryPath(new) {
base.Fatalf("go mod: -replace=%s: unversioned new path must be local directory", arg)
}
edits = append(edits, func(f *modfile.File) {
if err := f.AddReplace(oldPath, oldVersion, newPath, newVersion); err != nil {
base.Fatalf("go mod: -replace=%s: %v", arg, err)
}
})
}
// flagDropReplace implements the -dropreplace flag.
func flagDropReplace(arg string) {
path, version, err := parsePathVersionOptional("old", arg, true)
if err != nil {
base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
}
edits = append(edits, func(f *modfile.File) {
if err := f.DropReplace(path, version); err != nil {
base.Fatalf("go mod: -dropreplace=%s: %v", arg, err)
}
})
}
// fileJSON is the -json output data structure.
type fileJSON struct {
Module module.Version
Require []requireJSON
Exclude []module.Version
Replace []replaceJSON
}
type requireJSON struct {
Path string
Version string `json:",omitempty"`
Indirect bool `json:",omitempty"`
}
type replaceJSON struct {
Old module.Version
New module.Version
}
// editPrintJSON prints the -json output.
func editPrintJSON(modFile *modfile.File) {
var f fileJSON
f.Module = modFile.Module.Mod
for _, r := range modFile.Require {
f.Require = append(f.Require, requireJSON{Path: r.Mod.Path, Version: r.Mod.Version, Indirect: r.Indirect})
}
for _, x := range modFile.Exclude {
f.Exclude = append(f.Exclude, x.Mod)
}
for _, r := range modFile.Replace {
f.Replace = append(f.Replace, replaceJSON{r.Old, r.New})
}
data, err := json.MarshalIndent(&f, "", "\t")
if err != nil {
base.Fatalf("go: internal error: %v", err)
}
data = append(data, '\n')
os.Stdout.Write(data)
}

View File

@ -0,0 +1,73 @@
// Copyright 2018 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.
// go mod graph
package modcmd
import (
"bufio"
"os"
"sort"
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/module"
"cmd/go/internal/par"
)
var cmdGraph = &base.Command{
UsageLine: "go mod graph",
Short: "print module requirement graph",
Long: `
Graph prints the module requirement graph (with replacements applied)
in text form. Each line in the output has two space-separated fields: a module
and one of its requirements. Each module is identified as a string of the form
path@version, except for the main module, which has no @version suffix.
`,
Run: runGraph,
}
func runGraph(cmd *base.Command, args []string) {
if len(args) > 0 {
base.Fatalf("go mod graph: graph takes no arguments")
}
modload.LoadBuildList()
reqs := modload.MinReqs()
format := func(m module.Version) string {
if m.Version == "" {
return m.Path
}
return m.Path + "@" + m.Version
}
// Note: using par.Work only to manage work queue.
// No parallelism here, so no locking.
var out []string
var deps int // index in out where deps start
var work par.Work
work.Add(modload.Target)
work.Do(1, func(item interface{}) {
m := item.(module.Version)
list, _ := reqs.Required(m)
for _, r := range list {
work.Add(r)
out = append(out, format(m)+" "+format(r)+"\n")
}
if m == modload.Target {
deps = len(out)
}
})
sort.Slice(out[deps:], func(i, j int) bool {
return out[deps+i][0] < out[deps+j][0]
})
w := bufio.NewWriter(os.Stdout)
for _, line := range out {
w.WriteString(line)
}
w.Flush()
}

View File

@ -0,0 +1,41 @@
// Copyright 2018 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.
// go mod init
package modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"os"
)
var cmdInit = &base.Command{
UsageLine: "go mod init [module]",
Short: "initialize new module in current directory",
Long: `
Init initializes and writes a new go.mod to the current directory,
in effect creating a new module rooted at the current directory.
The file go.mod must not already exist.
If possible, init will guess the module path from import comments
(see 'go help importpath') or from version control configuration.
To override this guess, supply the module path as an argument.
`,
Run: runInit,
}
func runInit(cmd *base.Command, args []string) {
modload.CmdModInit = true
if len(args) > 1 {
base.Fatalf("go mod init: too many arguments")
}
if len(args) == 1 {
modload.CmdModModule = args[0]
}
if _, err := os.Stat("go.mod"); err == nil {
base.Fatalf("go mod init: go.mod already exists")
}
modload.InitMod() // does all the hard work
}

View File

@ -0,0 +1,31 @@
// Copyright 2018 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 modcmd implements the ``go mod'' command.
package modcmd
import "cmd/go/internal/base"
var CmdMod = &base.Command{
UsageLine: "go mod",
Short: "module maintenance",
Long: `Go mod provides access to operations on modules.
Note that support for modules is built into all the go commands,
not just 'go mod'. For example, day-to-day adding, removing, upgrading,
and downgrading of dependencies should be done using 'go get'.
See 'go help modules' for an overview of module functionality.
`,
Commands: []*base.Command{
cmdDownload,
cmdEdit,
cmdGraph,
cmdInit,
cmdTidy,
cmdVendor,
cmdVerify,
cmdWhy,
},
}

View File

@ -0,0 +1,90 @@
// Copyright 2018 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.
// go mod tidy
package modcmd
import (
"fmt"
"os"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/module"
)
var cmdTidy = &base.Command{
UsageLine: "go mod tidy [-v]",
Short: "add missing and remove unused modules",
Long: `
Tidy makes sure go.mod matches the source code in the module.
It adds any missing modules necessary to build the current module's
packages and dependencies, and it removes unused modules that
don't provide any relevant packages. It also adds any missing entries
to go.sum and removes any unnecessary ones.
The -v flag causes tidy to print information about removed modules
to standard error.
`,
}
func init() {
cmdTidy.Run = runTidy // break init cycle
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
}
func runTidy(cmd *base.Command, args []string) {
if len(args) > 0 {
base.Fatalf("go mod tidy: no arguments allowed")
}
// LoadALL adds missing modules.
// Remove unused modules.
used := make(map[module.Version]bool)
for _, pkg := range modload.LoadALL() {
used[modload.PackageModule(pkg)] = true
}
used[modload.Target] = true // note: LoadALL initializes Target
inGoMod := make(map[string]bool)
for _, r := range modload.ModFile().Require {
inGoMod[r.Mod.Path] = true
}
var keep []module.Version
for _, m := range modload.BuildList() {
if used[m] {
keep = append(keep, m)
} else if cfg.BuildV && inGoMod[m.Path] {
fmt.Fprintf(os.Stderr, "unused %s\n", m.Path)
}
}
modload.SetBuildList(keep)
modTidyGoSum() // updates memory copy; WriteGoMod on next line flushes it out
modload.WriteGoMod()
}
// modTidyGoSum resets the go.sum file content
// to be exactly what's needed for the current go.mod.
func modTidyGoSum() {
// Assuming go.sum already has at least enough from the successful load,
// we only have to tell modfetch what needs keeping.
reqs := modload.Reqs()
keep := make(map[module.Version]bool)
var walk func(module.Version)
walk = func(m module.Version) {
keep[m] = true
list, _ := reqs.Required(m)
for _, r := range list {
if !keep[r] {
walk(r)
}
}
}
walk(modload.Target)
modfetch.TrimGoSum(keep)
}

View File

@ -0,0 +1,200 @@
// Copyright 2018 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 modcmd
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"cmd/go/internal/base"
"cmd/go/internal/cfg"
"cmd/go/internal/modload"
"cmd/go/internal/module"
)
var cmdVendor = &base.Command{
UsageLine: "go mod vendor [-v]",
Short: "make vendored copy of dependencies",
Long: `
Vendor resets the main module's vendor directory to include all packages
needed to build and test all the main module's packages.
It does not include test code for vendored packages.
The -v flag causes vendor to print the names of vendored
modules and packages to standard error.
`,
Run: runVendor,
}
func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
}
func runVendor(cmd *base.Command, args []string) {
if len(args) != 0 {
base.Fatalf("go mod vendor: vendor takes no arguments")
}
pkgs := modload.LoadVendor()
vdir := filepath.Join(modload.ModRoot, "vendor")
if err := os.RemoveAll(vdir); err != nil {
base.Fatalf("go vendor: %v", err)
}
modpkgs := make(map[module.Version][]string)
for _, pkg := range pkgs {
m := modload.PackageModule(pkg)
if m == modload.Target {
continue
}
modpkgs[m] = append(modpkgs[m], pkg)
}
var buf bytes.Buffer
for _, m := range modload.BuildList()[1:] {
if pkgs := modpkgs[m]; len(pkgs) > 0 {
repl := ""
if r := modload.Replacement(m); r.Path != "" {
repl = " => " + r.Path
if r.Version != "" {
repl += " " + r.Version
}
}
fmt.Fprintf(&buf, "# %s %s%s\n", m.Path, m.Version, repl)
if cfg.BuildV {
fmt.Fprintf(os.Stderr, "# %s %s%s\n", m.Path, m.Version, repl)
}
for _, pkg := range pkgs {
fmt.Fprintf(&buf, "%s\n", pkg)
if cfg.BuildV {
fmt.Fprintf(os.Stderr, "%s\n", pkg)
}
vendorPkg(vdir, pkg)
}
}
}
if buf.Len() == 0 {
fmt.Fprintf(os.Stderr, "go: no dependencies to vendor\n")
return
}
if err := ioutil.WriteFile(filepath.Join(vdir, "modules.txt"), buf.Bytes(), 0666); err != nil {
base.Fatalf("go vendor: %v", err)
}
}
func vendorPkg(vdir, pkg string) {
realPath := modload.ImportMap(pkg)
if realPath != pkg && modload.ImportMap(realPath) != "" {
fmt.Fprintf(os.Stderr, "warning: %s imported as both %s and %s; making two copies.\n", realPath, realPath, pkg)
}
dst := filepath.Join(vdir, pkg)
src := modload.PackageDir(realPath)
if src == "" {
fmt.Fprintf(os.Stderr, "internal error: no pkg for %s -> %s\n", pkg, realPath)
}
copyDir(dst, src, matchNonTest)
if m := modload.PackageModule(realPath); m.Path != "" {
copyMetadata(m.Path, realPath, dst, src)
}
}
type metakey struct {
modPath string
dst string
}
var copiedMetadata = make(map[metakey]bool)
// copyMetadata copies metadata files from parents of src to parents of dst,
// stopping after processing the src parent for modPath.
func copyMetadata(modPath, pkg, dst, src string) {
for parent := 0; ; parent++ {
if copiedMetadata[metakey{modPath, dst}] {
break
}
copiedMetadata[metakey{modPath, dst}] = true
if parent > 0 {
copyDir(dst, src, matchMetadata)
}
if modPath == pkg {
break
}
pkg = filepath.Dir(pkg)
dst = filepath.Dir(dst)
src = filepath.Dir(src)
}
}
// metaPrefixes is the list of metadata file prefixes.
// Vendoring copies metadata files from parents of copied directories.
// Note that this list could be arbitrarily extended, and it is longer
// in other tools (such as godep or dep). By using this limited set of
// prefixes and also insisting on capitalized file names, we are trying
// to nudge people toward more agreement on the naming
// and also trying to avoid false positives.
var metaPrefixes = []string{
"AUTHORS",
"CONTRIBUTORS",
"COPYLEFT",
"COPYING",
"COPYRIGHT",
"LEGAL",
"LICENSE",
"NOTICE",
"PATENTS",
}
// matchMetadata reports whether info is a metadata file.
func matchMetadata(info os.FileInfo) bool {
name := info.Name()
for _, p := range metaPrefixes {
if strings.HasPrefix(name, p) {
return true
}
}
return false
}
// matchNonTest reports whether info is any non-test file (including non-Go files).
func matchNonTest(info os.FileInfo) bool {
return !strings.HasSuffix(info.Name(), "_test.go")
}
// copyDir copies all regular files satisfying match(info) from src to dst.
func copyDir(dst, src string, match func(os.FileInfo) bool) {
files, err := ioutil.ReadDir(src)
if err != nil {
base.Fatalf("go vendor: %v", err)
}
if err := os.MkdirAll(dst, 0777); err != nil {
base.Fatalf("go vendor: %v", err)
}
for _, file := range files {
if file.IsDir() || !file.Mode().IsRegular() || !match(file) {
continue
}
r, err := os.Open(filepath.Join(src, file.Name()))
if err != nil {
base.Fatalf("go vendor: %v", err)
}
w, err := os.Create(filepath.Join(dst, file.Name()))
if err != nil {
base.Fatalf("go vendor: %v", err)
}
if _, err := io.Copy(w, r); err != nil {
base.Fatalf("go vendor: %v", err)
}
r.Close()
if err := w.Close(); err != nil {
base.Fatalf("go vendor: %v", err)
}
}
}

View File

@ -0,0 +1,96 @@
// Copyright 2018 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 modcmd
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"cmd/go/internal/base"
"cmd/go/internal/dirhash"
"cmd/go/internal/modfetch"
"cmd/go/internal/modload"
"cmd/go/internal/module"
)
var cmdVerify = &base.Command{
UsageLine: "go mod verify",
Short: "verify dependencies have expected content",
Long: `
Verify checks that the dependencies of the current module,
which are stored in a local downloaded source cache, have not been
modified since being downloaded. If all the modules are unmodified,
verify prints "all modules verified." Otherwise it reports which
modules have been changed and causes 'go mod' to exit with a
non-zero status.
`,
Run: runVerify,
}
func runVerify(cmd *base.Command, args []string) {
if len(args) != 0 {
// NOTE(rsc): Could take a module pattern.
base.Fatalf("go mod verify: verify takes no arguments")
}
ok := true
for _, mod := range modload.LoadBuildList()[1:] {
ok = verifyMod(mod) && ok
}
if ok {
fmt.Printf("all modules verified\n")
}
}
func verifyMod(mod module.Version) bool {
ok := true
zip, zipErr := modfetch.CachePath(mod, "zip")
if zipErr == nil {
_, zipErr = os.Stat(zip)
}
dir, dirErr := modfetch.DownloadDir(mod)
if dirErr == nil {
_, dirErr = os.Stat(dir)
}
data, err := ioutil.ReadFile(zip + "hash")
if err != nil {
if zipErr != nil && os.IsNotExist(zipErr) && dirErr != nil && os.IsNotExist(dirErr) {
// Nothing downloaded yet. Nothing to verify.
return true
}
base.Errorf("%s %s: missing ziphash: %v", mod.Path, mod.Version, err)
return false
}
h := string(bytes.TrimSpace(data))
if zipErr != nil && os.IsNotExist(zipErr) {
// ok
} else {
hZ, err := dirhash.HashZip(zip, dirhash.DefaultHash)
if err != nil {
base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
return false
} else if hZ != h {
base.Errorf("%s %s: zip has been modified (%v)", mod.Path, mod.Version, zip)
ok = false
}
}
if dirErr != nil && os.IsNotExist(dirErr) {
// ok
} else {
hD, err := dirhash.HashDir(dir, mod.Path+"@"+mod.Version, dirhash.DefaultHash)
if err != nil {
base.Errorf("%s %s: %v", mod.Path, mod.Version, err)
return false
}
if hD != h {
base.Errorf("%s %s: dir has been modified (%v)", mod.Path, mod.Version, dir)
ok = false
}
}
return ok
}

View File

@ -0,0 +1,121 @@
// Copyright 2018 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 modcmd
import (
"cmd/go/internal/base"
"cmd/go/internal/modload"
"cmd/go/internal/module"
"fmt"
"strings"
)
var cmdWhy = &base.Command{
UsageLine: "go mod why [-m] [-vendor] packages...",
Short: "explain why packages or modules are needed",
Long: `
Why shows a shortest path in the import graph from the main module to
each of the listed packages. If the -m flag is given, why treats the
arguments as a list of modules and finds a path to any package in each
of the modules.
By default, why queries the graph of packages matched by "go list all",
which includes tests for reachable packages. The -vendor flag causes why
to exclude tests of dependencies.
The output is a sequence of stanzas, one for each package or module
name on the command line, separated by blank lines. Each stanza begins
with a comment line "# package" or "# module" giving the target
package or module. Subsequent lines give a path through the import
graph, one package per line. If the package or module is not
referenced from the main module, the stanza will display a single
parenthesized note indicating that fact.
For example:
$ go mod why golang.org/x/text/language golang.org/x/text/encoding
# golang.org/x/text/language
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language
# golang.org/x/text/encoding
(main module does not need package golang.org/x/text/encoding)
$
`,
}
var (
whyM = cmdWhy.Flag.Bool("m", false, "")
whyVendor = cmdWhy.Flag.Bool("vendor", false, "")
)
func init() {
cmdWhy.Run = runWhy // break init cycle
}
func runWhy(cmd *base.Command, args []string) {
loadALL := modload.LoadALL
if *whyVendor {
loadALL = modload.LoadVendor
}
if *whyM {
listU := false
listVersions := false
for _, arg := range args {
if strings.Contains(arg, "@") {
base.Fatalf("go mod why: module query not allowed")
}
}
mods := modload.ListModules(args, listU, listVersions)
byModule := make(map[module.Version][]string)
for _, path := range loadALL() {
m := modload.PackageModule(path)
if m.Path != "" {
byModule[m] = append(byModule[m], path)
}
}
sep := ""
for _, m := range mods {
best := ""
bestDepth := 1000000000
for _, path := range byModule[module.Version{Path: m.Path, Version: m.Version}] {
d := modload.WhyDepth(path)
if d > 0 && d < bestDepth {
best = path
bestDepth = d
}
}
why := modload.Why(best)
if why == "" {
vendoring := ""
if *whyVendor {
vendoring = " to vendor"
}
why = "(main module does not need" + vendoring + " module " + m.Path + ")\n"
}
fmt.Printf("%s# %s\n%s", sep, m.Path, why)
sep = "\n"
}
} else {
matches := modload.ImportPaths(args) // resolve to packages
loadALL() // rebuild graph, from main module (not from named packages)
sep := ""
for _, m := range matches {
for _, path := range m.Pkgs {
why := modload.Why(path)
if why == "" {
vendoring := ""
if *whyVendor {
vendoring = " to vendor"
}
why = "(main module does not need" + vendoring + " package " + path + ")\n"
}
fmt.Printf("%s# %s\n%s", sep, path, why)
sep = "\n"
}
}
}
}

View File

@ -0,0 +1,90 @@
// Copyright 2018 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 modconv
import (
"fmt"
"os"
"sort"
"strings"
"sync"
"cmd/go/internal/base"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfile"
"cmd/go/internal/module"
"cmd/go/internal/par"
"cmd/go/internal/semver"
)
// ConvertLegacyConfig converts legacy config to modfile.
// The file argument is slash-delimited.
func ConvertLegacyConfig(f *modfile.File, file string, data []byte) error {
i := strings.LastIndex(file, "/")
j := -2
if i >= 0 {
j = strings.LastIndex(file[:i], "/")
}
convert := Converters[file[i+1:]]
if convert == nil && j != -2 {
convert = Converters[file[j+1:]]
}
if convert == nil {
return fmt.Errorf("unknown legacy config file %s", file)
}
mf, err := convert(file, data)
if err != nil {
return fmt.Errorf("parsing %s: %v", file, err)
}
// Convert requirements block, which may use raw SHA1 hashes as versions,
// to valid semver requirement list, respecting major versions.
var work par.Work
for _, r := range mf.Require {
m := r.Mod
if m.Path == "" {
continue
}
work.Add(r.Mod)
}
var (
mu sync.Mutex
need = make(map[string]string)
)
work.Do(10, func(item interface{}) {
r := item.(module.Version)
repo, info, err := modfetch.ImportRepoRev(r.Path, r.Version)
if err != nil {
fmt.Fprintf(os.Stderr, "go: converting %s: stat %s@%s: %v\n", base.ShortPath(file), r.Path, r.Version, err)
return
}
mu.Lock()
path := repo.ModulePath()
// Don't use semver.Max here; need to preserve +incompatible suffix.
if v, ok := need[path]; !ok || semver.Compare(v, info.Version) < 0 {
need[path] = info.Version
}
mu.Unlock()
})
var paths []string
for path := range need {
paths = append(paths, path)
}
sort.Strings(paths)
for _, path := range paths {
f.AddNewRequire(path, need[path], false)
}
for _, r := range mf.Replace {
err := f.AddReplace(r.Old.Path, r.Old.Version, r.New.Path, r.New.Version)
if err != nil {
return fmt.Errorf("add replace: %v", err)
}
}
f.Cleanup()
return nil
}

View File

@ -0,0 +1,186 @@
// Copyright 2018 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 modconv
import (
"bytes"
"fmt"
"internal/testenv"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"cmd/go/internal/cfg"
"cmd/go/internal/modfetch"
"cmd/go/internal/modfetch/codehost"
"cmd/go/internal/modfile"
"cmd/go/internal/module"
)
func TestMain(m *testing.M) {
os.Exit(testMain(m))
}
func testMain(m *testing.M) int {
if _, err := exec.LookPath("git"); err != nil {
fmt.Fprintln(os.Stderr, "skipping because git binary not found")
fmt.Println("PASS")
return 0
}
dir, err := ioutil.TempDir("", "modconv-test-")
if err != nil {
log.Fatal(err)
}
defer os.RemoveAll(dir)
modfetch.PkgMod = filepath.Join(dir, "pkg/mod")
codehost.WorkRoot = filepath.Join(dir, "codework")
return m.Run()
}
func TestConvertLegacyConfig(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
if testing.Verbose() {
old := cfg.BuildX
defer func() {
cfg.BuildX = old
}()
cfg.BuildX = true
}
var tests = []struct {
path string
vers string
gomod string
}{
/*
Different versions of git seem to find or not find
github.com/Masterminds/semver's a93e51b5a57e,
which is an unmerged pull request.
We'd rather not provide access to unmerged pull requests,
so the line is removed from the golden file here,
but some git commands still find it somehow.
{
// Gopkg.lock parsing.
"github.com/golang/dep", "v0.4.0",
`module github.com/golang/dep
require (
github.com/Masterminds/vcs v1.11.1
github.com/armon/go-radix v0.0.0-20160115234725-4239b77079c7
github.com/boltdb/bolt v1.3.1
github.com/go-yaml/yaml v0.0.0-20170407172122-cd8b52f8269e
github.com/golang/protobuf v0.0.0-20170901042739-5afd06f9d81a
github.com/jmank88/nuts v0.3.0
github.com/nightlyone/lockfile v0.0.0-20170707060451-e83dc5e7bba0
github.com/pelletier/go-toml v0.0.0-20171218135716-b8b5e7696574
github.com/pkg/errors v0.8.0
github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353
golang.org/x/net v0.0.0-20170828231752-66aacef3dd8a
golang.org/x/sync v0.0.0-20170517211232-f52d1811a629
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea
)`,
},
*/
// TODO: https://github.com/docker/distribution uses vendor.conf
{
// Godeps.json parsing.
// TODO: Should v2.0.0 work here too?
"github.com/docker/distribution", "v0.0.0-20150410205453-85de3967aa93",
`module github.com/docker/distribution
require (
github.com/AdRoll/goamz v0.0.0-20150130162828-d3664b76d905
github.com/MSOpenTech/azure-sdk-for-go v0.0.0-20150323223030-d90753bcad2e
github.com/Sirupsen/logrus v0.7.3
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b
github.com/bugsnag/panicwrap v0.0.0-20141110184334-e5f9854865b9
github.com/codegangsta/cli v0.0.0-20150131031259-6086d7927ec3
github.com/docker/docker v0.0.0-20150204013315-165ea5c158cf
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1
github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7
github.com/gorilla/context v0.0.0-20140604161150-14f550f51af5
github.com/gorilla/handlers v0.0.0-20140825150757-0e84b7d810c1
github.com/gorilla/mux v0.0.0-20140926153814-e444e69cbd2e
github.com/jlhawn/go-crypto v0.0.0-20150401213827-cd738dde20f0
github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43
github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50
github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f
golang.org/x/net v0.0.0-20150202051010-1dfe7915deaf
gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789
gopkg.in/yaml.v2 v2.0.0-20150116202057-bef53efd0c76
)`,
},
{
// golang.org/issue/24585 - confusion about v2.0.0 tag in legacy non-v2 module
"github.com/fishy/gcsbucket", "v0.0.0-20150410205453-618d60fe84e0",
`module github.com/fishy/gcsbucket
require (
cloud.google.com/go v0.18.0
github.com/fishy/fsdb v0.0.0-20180217030800-5527ded01371
github.com/golang/protobuf v1.0.0
github.com/googleapis/gax-go v2.0.0+incompatible
golang.org/x/net v0.0.0-20180216171745-136a25c244d3
golang.org/x/oauth2 v0.0.0-20180207181906-543e37812f10
golang.org/x/text v0.0.0-20180208041248-4e4a3210bb54
google.golang.org/api v0.0.0-20180217000815-c7a403bb5fe1
google.golang.org/appengine v1.0.0
google.golang.org/genproto v0.0.0-20180206005123-2b5a72b8730b
google.golang.org/grpc v1.10.0
)`,
},
}
for _, tt := range tests {
t.Run(strings.Replace(tt.path, "/", "_", -1)+"_"+tt.vers, func(t *testing.T) {
f, err := modfile.Parse("golden", []byte(tt.gomod), nil)
if err != nil {
t.Fatal(err)
}
want, err := f.Format()
if err != nil {
t.Fatal(err)
}
dir, err := modfetch.Download(module.Version{Path: tt.path, Version: tt.vers})
if err != nil {
t.Fatal(err)
}
for name := range Converters {
file := filepath.Join(dir, name)
data, err := ioutil.ReadFile(file)
if err == nil {
f := new(modfile.File)
f.AddModuleStmt(tt.path)
if err := ConvertLegacyConfig(f, filepath.ToSlash(file), data); err != nil {
t.Fatal(err)
}
out, err := f.Format()
if err != nil {
t.Fatalf("format after conversion: %v", err)
}
if !bytes.Equal(out, want) {
t.Fatalf("final go.mod:\n%s\n\nwant:\n%s", out, want)
}
return
}
}
t.Fatalf("no converter found for %s@%s", tt.path, tt.vers)
})
}
}

View File

@ -0,0 +1,74 @@
// Copyright 2018 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 modconv
import (
"fmt"
"strconv"
"strings"
"cmd/go/internal/modfile"
"cmd/go/internal/module"
"cmd/go/internal/semver"
)
func ParseGopkgLock(file string, data []byte) (*modfile.File, error) {
mf := new(modfile.File)
var list []module.Version
var r *module.Version
for lineno, line := range strings.Split(string(data), "\n") {
lineno++
if i := strings.Index(line, "#"); i >= 0 {
line = line[:i]
}
line = strings.TrimSpace(line)
if line == "[[projects]]" {
list = append(list, module.Version{})
r = &list[len(list)-1]
continue
}
if strings.HasPrefix(line, "[") {
r = nil
continue
}
if r == nil {
continue
}
i := strings.Index(line, "=")
if i < 0 {
continue
}
key := strings.TrimSpace(line[:i])
val := strings.TrimSpace(line[i+1:])
if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
q, err := strconv.Unquote(val) // Go unquoting, but close enough for now
if err != nil {
return nil, fmt.Errorf("%s:%d: invalid quoted string: %v", file, lineno, err)
}
val = q
}
switch key {
case "name":
r.Path = val
case "revision", "version":
// Note: key "version" should take priority over "revision",
// and it does, because dep writes toml keys in alphabetical order,
// so we see version (if present) second.
if key == "version" {
if !semver.IsValid(val) || semver.Canonical(val) != val {
break
}
}
r.Version = val
}
}
for _, r := range list {
if r.Path == "" || r.Version == "" {
return nil, fmt.Errorf("%s: empty [[projects]] stanza (%s)", file, r.Path)
}
mf.Require = append(mf.Require, &modfile.Require{Mod: r})
}
return mf, nil
}

Some files were not shown because too many files have changed in this diff Show More